** LambdaMOO Database, Format Version 4 **
225
2571
0
8
2
36
35
63
190
196
199
200
#0
System Object

24
199
-1
-1
-1
164
-1
-1
9
do_login_command
199
173
-1
server_started
199
173
-1
init_for_core
199
173
-1
do_out_of_band_command
199
173
-1
user_created user_connected
199
173
-1
user_disconnected user_client_disconnected
199
173
-1
user_reconnected
199
173
-1
checkpoint_finished
199
173
-1
checkpoint_started
199
173
-1
177
builder
login
last_huh
guest_log
last_restart_time
biglist
big_mail_recipient
limbo
registration_db
new_player_log
verb_help
core_help
prog_help
wiz_help
shutdown_task
wiz_utils
site_db
math_utils
set_utils
builtin_function_help
new_prog_log
generic_help
guest
seq_utils
quota_log
you
hacker
generic_db
shutdown_message
shutdown_time
no_one
player_db
player_class
gender_utils
trig_utils
time_utils
editor_help
mail_recipient
mail_agent
mail_editor
note_editor
verb_editor
generic_editor
object_utils
lock_utils
gripe_recipients
dump_interval
list_utils
command_utils
player
wiz
prog
code_utils
help
nothing
failed_match
ambiguous_match
perm_utils
building_utils
string_utils
news
note
container
thing
exit
room
player_start
root_class
recycler
garbage
mail_options
edit_options
display_options
generic_options
maxint
minint
list_options
error
newt_log
toad_log
site_log
housekeeper
feature
pronoun_sub
they
login_watcher
english
language
integration_utils
network
country_db
html
jtext
feature_help
jaddress
misc_options
client_options
building_options
it
nobody
furniture
sittable
I
everything
all
nowhere
admin_group
steering_committee
db_group
unix_group
community_group
tech_group
admin_mail_recipient
registrar
event_dispatcher
prog_group
openable_container
containing_object
utils
biglist_utils
english_utils
boot_log
help_editor
object_help
tester
failed_help
who_options
who_utils
event_handler
eval_options
he
she
e
we
plural_you
either
splat
gender
neuter
egotistical
plural
royal
male
female
Spivak
second
second_plural
singular
none
topic
builder_help
name_utils
guest_help
parse_utils
admin
core_wizard
first_wizard
door
ftp
__core_init_phase
quota_utils
window
server_options
walking_utils
maxfloat
checkpointer
startup
syslog
core_extraction_time
generic_biglist_home
http
module
property_farm
modules
smtp
cord
mcp
193
1
4
199
5
1
10
199
1
1
11
199
1
1
12
199
1
0
913134293
199
5
1
13
199
5
1
14
199
5
1
15
199
5
1
16
199
5
1
17
199
5
1
20
199
5
1
21
199
5
1
22
199
5
1
23
199
5
3
0
199
5
1
24
199
5
1
25
199
5
1
26
199
5
1
27
199
5
1
28
199
5
1
29
199
5
1
30
199
5
1
31
199
1
1
32
199
5
1
33
199
5
1
34
35
1
1
35
199
5
1
79
199
5
2

199
5
0
0
199
5
1
36
199
1
1
37
199
1
1
6
199
5
1
38
199
1
1
26
199
5
1
39
199
5
1
40
199
5
1
41
199
5
1
42
199
5
1
43
199
5
1
44
199
5
1
45
199
5
1
46
199
5
1
47
199
5
1
48
199
5
4
1
1
2
199
5
0
3600
199
5
1
49
199
5
1
50
199
5
1
6
199
5
1
51
199
5
1
52
199
5
1
53
199
5
1
54
199
5
1
-1
199
5
1
-3
199
5
1
-2
199
5
1
137
199
5
1
19
199
5
1
18
199
5
1
151
199
5
1
9
199
5
1
133
199
5
1
5
199
5
1
7
199
5
1
3
199
5
1
70
199
5
1
1
199
5
1
55
199
5
1
56
199
5
1
57
199
5
1
58
199
5
1
59
199
5
1
60
199
5
0
2147483647
199
5
0
-2147483648
199
5
1
61
199
5
1
66
199
5
1
112
199
5
1
62
199
5
1
62
199
5
1
63
199
5
1
65
199
1
1
69
199
1
1
71
199
1
1
85
199
5
1
72
199
1
1
72
199
1
1
74
199
5
1
83
199
5
1
97
199
1
1
105
199
1
1
95
199
1
1
106
199
1
1
117
199
1
1
118
199
1
1
122
199
1
1
129
199
5
1
165
199
5
1
36
199
5
1
81
199
5
1
80
199
5
1
171
199
5
1
149
199
5
1
109
199
5
1
-1
199
5
1
174
199
5
1
147
199
5
1
159
199
5
1
158
199
5
1
176
199
5
1
175
199
5
1
113
199
5
1
114
199
5
1
116
199
5
1
131
199
5
1
8
199
5
1
155
199
5
1
156
199
5
1
13
199
5
1
72
199
5
1
87
199
5
1
184
199
1
1
186
199
5
1
104
199
1
1
134
199
1
1
92
199
5
1
92
199
5
1
142
199
5
1
146
199
5
1
167
199
5
1
168
199
5
1
169
199
5
1
170
199
5
1
172
199
5
1
173
199
5
1
177
199
5
1
64
199
5
1
165
199
5
1
171
199
5
1
71
199
5
1
170
199
5
1
167
199
5
1
168
199
5
1
169
199
5
1
34
199
5
1
172
199
5
1
165
199
5
1
178
199
5
1
192
199
5
1
166
199
1
1
191
199
1
1
181
199
1
1
123
199
1
1
183
199
1
1
199
199
1
1
2
199
5
1
128
199
1
1
180
199
1
0
0
199
1
1
188
199
5
1
193
199
5
1
207
199
1
1
197
199
1
9
1.79769313486231471e+308
199
5
1
189
199
1
1
209
199
5
1
198
199
1
0
913134427
199
1
1
75
199
1
1
89
199
5
1
82
199
1
1
222
199
1
1
138
199
1
1
84
199
1
1
215
199
1
1
157
199
1
5
199
1
4
22
2
cat
2
notebook
2
gopher
2
lock
2
client_utils
2
core_db
2
nntp
2
table
2
projects
2
project
2
closet
2
interior_room
2
openable_closet
2
radio
2
httpd
2
options
2
property_help
2
notebook
2
ooo_utils
2
tutorial
2
out_of_band
2
list_editor
199
1
5
199
4
4
1
2
System Object
199
5
2
The Known Universe aka The, Known, Universe, and #0.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
21079
0
873878439
200
1
#1
root class

152
199
-1
-1
-1
-1
42
-1
81
initialize
199
173
-1
recycle
199
173
-1
set_name
199
173
-1
name
35
173
-1
titlec namec
35
173
-1
set_aliases
199
173
-1
match
35
173
-1
match_object
35
173
-1
set_description
199
173
-1
description
35
173
-1
look_self
35
173
-1
notify
199
173
-1
tell
199
173
-1
tell_lines
35
173
-1
accept
35
173
-1
moveto
199
173
-1
eject
199
173
-1
is_unlocked_for
199
173
-1
huh
199
173
-1
set_message
199
173
-1
do_examine
199
173
-1
examine_key
199
173
-1
examine_names
35
173
-1
examine_desc
35
173
-1
examine_contents
199
173
-1
examine_verbs
199
173
-1
room_announce*_all_but
35
173
-1
init_for_core
199
173
-1
contents objects_for_match
35
173
-1
examine_verb_ok
199
173
-1
is_listening
35
173
-1
obvious_verbs hidden_verbs help_verbs
199
173
-1
examine_owner
35
173
-1
announce*_all_but
35
173
-1
visible
35
173
-1
integrate_room_msg integrate_container_msg
35
173
-1
dname*c iname*c
35
173
-1
where_am_i where_are_you
35
173
-1
integrate_in
35
173
-1
name_for_look_self namec_for_look_self
199
173
-1
look_in
35
173
-1
integrate_player_msg
35
173
-1
default_editing
35
173
-1
title
35
173
-1
name_for_tell_contents namec_for_tell_contents
35
173
-1
tell_lines_suspended
35
173
-1
is_hidden_verb
199
173
-1
verb_sub noun_sub adj_sub
35
173
-1
exitfunc
35
173
-1
editing_for
199
173
-1
event_*
35
173
-1
name_and_number iname_and_number dname_and_number namec_and_number inamec_and_number dnamec_and_number
35
173
-1
add_handler
35
173
-1
_add_handler
35
173
-1
remove_handler
35
173
-1
handlers_event_*
35
173
-1
ps*c po*c pr*c pp*c pq*c psu pou pru ppu pqu
35
173
-1
gender gender_name
35
173
-1
set_number set_visible set_integrate_in
35
173
-1
number
35
173
-1
is_plural are_plural plural
35
173
-1
gender_obj
35
173
-1
you
35
173
-1
modname_d
35
173
-1
modname_i
35
173
-1
modname_c
35
173
-1
modname_p
35
173
-1
base_name
35
173
-1
nominate_for_core
35
173
-1
help_text
35
173
-1
modname_u
35
173
-1
modname_titleize
35
173
-1
empty_message
199
173
-1
empty_message_integrate_room empty_message_integrate_container
35
173
-1
modname_in
35
173
-1
acceptable
35
173
-1
modname_v
35
173
-1
modname_#
35
173
-1
modname_l
199
173
-1
get_messages
199
173
-1
get_message
199
173
-1
14
key
aliases
description
visible
integrate_room_msg
integrate_player_msg
integrate_container_msg
unique
proper
integrate_in
handlers
number
offered
object_size
14
0
0
199
4
4
1
2
root class
199
5
2

199
5
0
1
35
1
2

199
5
2

199
5
2

199
5
0
1
199
5
0
0
199
5
4
1
1
155
35
1
4
0
35
1
0
1
35
1
1
-1
199
1
4
2
0
51502
0
913134427
200
1
#2
Wizard

23
2
70
-1
-1
51
-1
199
1
init_for_core
199
173
-1
0
115
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
4
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
4
3
1
67
1
68
1
160
35
1
5
199
0
5
199
5
5
199
0
5
199
1
5
199
5
5
35
1
5
35
1
5
35
0
5
35
1
5
199
5
5
200
1
5
35
1
5
199
4
5
199
0
5
199
0
5
199
5
5
199
4
5
199
4
5
199
4
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
4
5
199
4
5
199
1
0
-10000
200
0
1
169
199
5
1
70
35
1
0
0
199
0
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
199
1
5
199
1
5
35
1
5
35
1
5
199
5
5
199
5
5
199
1
5
199
1
5
199
5
5
199
0
5
199
5
5
35
1
5
35
1
5
199
5
5
35
1
5
199
0
5
199
0
5
199
0
5
199
1
5
199
1
5
35
1
5
199
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
1
5
199
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
199
1
5
199
1
5
199
0
5
199
1
4
4
0
0
0
2984
0
0
0
0
200
0
5
199
5
5
199
0
5
35
1
5
199
1
5
199
1
5
199
5
5
199
1
5
199
4
4
1
2
Wizard
199
1
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
5
199
1
4
2
0
2984
0
913134427
200
1
#3
generic room

152
199
-1
31
-1
155
46
6
65
confunc
35
173
-1
disfunc
35
173
-1
say
35
93
-2
emote
35
93
-2
announce
35
173
-1
match_exit match_entrance
35
173
-1
add_exit add_entrance
199
173
-1
tell_contents
35
173
-1
@exits
35
13
-1
bless_for_entry
199
173
-1
@entrances
199
9
-1
go
199
29
-1
l*ook
35
93
-2
announce_all
35
173
-1
announce_all_but
35
173
-1
enterfunc
199
173
-1
remove_exit
199
173
-1
remove_entrance
199
173
-1
@add-exit @addexit
199
25
-1
@add-entrance @addentrance
199
25
-1
recycle
199
173
-1
@lastlog
35
29
-1
e east w west s south n north ne northeast nw northwest se southeast sw southwest u up d down
35
13
-1
@eject
199
25
-1
ejection_msg oejection_msg victim_ejection_msg
35
173
-1
accept_for_abode
35
173
-1
@resident*s
199
25
-1
@remove-exit
199
25
-1
@remove-entrance
199
25
-1
moveto
199
173
-1
who_location_msg
35
173
-1
exits entrances
199
173
-1
obvious_exits
35
173
-1
here_huh
199
173
-1
room_announce*_all_but
35
173
-1
examine_commands_ok
35
173
-1
examine_key
199
173
-1
examine_contents
35
173
-1
ok_to_integrate
35
173
-1
look_self
35
173
-1
description
35
173
-1
where_am_i
35
173
-1
housekeeper_msg housekeeper_deposit_msg
35
173
-1
look_in
35
173
-1
namec_for_look_self
35
173
-1
set_integrate_unknown_objects
35
173
-1
here_find_path
35
173
-1
_here_find_path
35
173
-1
dependents dependents_event_*
35
173
-1
reconfunc
35
173
-1
sit
35
13
-1
stand
35
9
-1
topic_msg
35
173
-1
topic
35
89
-2
set_topic_sign
196
173
-1
event_really_disconnected
199
173
-1
match_type_object
35
173
-1
party_location
35
173
-1
init_for_core
199
173
-1
acceptable
199
173
-1
set_integrate_exit_msg
35
173
-1
is_public_location
199
173
-1
objects_for_match
35
173
-1
set_walking_cost
35
173
-1
walking_cost
35
173
-1
21
who_location_msg
free_home
victim_ejection_msg
ejection_msg
oejection_msg
residents
free_entry
entrances
blessed_object
blessed_task
exits
dark
integration_enabled
housekeeper_msg
integrate_sep_msg
housekeeper_deposit_msg
integrate_paragraphs
integrate_unknown_objects
topic_sign
say_msg
walking_cost
37
4
3
1
69
2
do
4
3
2
name
2
thing
0
1
199
5
0
0
199
5
4
7
1
69
2
do
2
You have been expelled from 
4
3
2
dname
2
iobj
0
0
2
 by 
4
3
2
dname
2
player
0
0
2
.
199
5
4
7
1
69
2
do
2
You expel 
4
3
2
dname
2
dobj
0
0
2
 from 
4
3
2
dname
2
iobj
0
0
2
.
199
5
4
10
1
69
2
do
4
3
2
name
2
dobj
4
1
2
dc
2
 
4
3
2
verb
2
dobj
2
is
2
 unceremoniously expelled from 
4
3
2
name
2
iobj
4
1
2
d
2
 by 
4
3
2
name
2
player
4
1
2
d
2
.
199
5
4
0
199
5
0
0
199
5
4
0
199
4
1
-1
199
5
0
1197213733
199
5
4
0
199
4
0
0
199
5
0
1
199
5
4
6
1
69
2
do
4
3
2
dname
2
player
0
1
2
's friends arrive to cart 
4
3
2
pronoun
2
player
2
po
2
 off to bed.
199
5
2
  
199
5
4
6
1
69
2
do
4
3
2
iname
2
player
0
1
2
's friends arrive to drop 
4
3
2
pronoun
2
player
2
po
2
 off, sound asleep.
199
5
4
0
199
5
0
1
35
1
1
-1
196
1
4
8
1
69
2
do
4
3
2
name
2
player
4
1
2
dc
2
 
4
3
2
verb
2
player
2
says
2
, "
4
3
2
string
2
argstr
0
0
2
"
199
5
0
0
35
1
5
199
5
5
199
1
5
199
4
4
1
2
generic room
199
5
5
199
5
0
0
35
1
5
199
5
5
199
5
5
199
5
0
1
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
46045
0
913134427
200
1
#4
generic builder

16
199
3
-1
9
6
52
190
31
@quota
199
25
-1
@create
199
89
-2
@recycle
199
25
-1
@recreate
199
89
13
@dig
199
89
-2
@auditDB
199
89
-2
@count
199
25
-1
@countDB
199
25
-1
@sort-owned*-objects
199
9
-1
@add-owned
199
25
-1
@verify-owned
199
9
-1
@unlock
199
25
-1
@lock
199
89
0
@newmess*age
199
89
-2
@unmess*age
199
89
-2
_messagify
199
173
-1
@kids
199
29
-1
@contents
199
25
-1
@par*ents
199
25
-1
@location*s
199
25
-1
_create
199
173
-1
_recycle
199
173
-1
@audit
199
89
-2
@building-o*ptions @buildingo*ptions
199
89
-2
set_building_option
199
173
-1
building_option
199
173
-1
options_packages
35
173
-1
@recycle!
199
25
-1
@move
199
93
1
@measure
199
89
-2
@grant
199
89
1
2
recreate_enabled
building_options
103
0
1
199
5
4
0
199
5
5
35
1
5
199
0
5
199
5
5
199
0
5
199
1
1
166
199
5
5
35
1
5
35
1
5
35
0
5
35
1
5
199
5
5
200
1
5
35
1
5
199
4
5
199
0
5
199
0
5
199
5
5
199
4
5
199
4
5
199
4
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
4
5
199
4
5
199
1
0
0
200
0
5
199
5
5
35
1
5
199
0
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
199
1
5
199
1
5
35
1
5
35
1
5
199
5
5
199
5
5
199
1
5
199
1
5
199
5
5
199
0
5
199
5
5
35
1
5
35
1
5
199
5
4
0
35
1
5
199
0
5
199
0
5
199
0
5
199
1
5
199
1
5
35
1
5
199
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
1
5
199
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
199
1
5
199
1
5
199
0
5
199
1
5
200
0
5
199
5
5
199
0
5
35
1
5
199
1
5
199
1
5
199
5
5
199
1
5
199
4
4
1
2
generic builder
199
1
2
You see a player who should type '@describe me as ...'.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
35049
0
913134427
200
1
#5
generic thing

152
199
3
-1
104
1
9
194
8
g*et t*ake
199
45
-1
d*rop th*row
199
45
-1
moveto
199
173
-1
take_failed_msg take_succeeded_msg otake_failed_msg otake_succeeded_msg drop_failed_msg drop_succeeded_msg odrop_failed_msg odrop_succeeded_msg
199
173
-1
gi*ve ha*nd
199
109
1
examine_key
199
173
-1
initialize
199
173
-1
help_text
35
173
-1
8
drop_failed_msg
drop_succeeded_msg
odrop_failed_msg
odrop_succeeded_msg
otake_succeeded_msg
otake_failed_msg
take_succeeded_msg
take_failed_msg
22
4
5
1
69
2
do
2
You can't seem to drop 
4
3
2
dname
2
thing
0
0
2
 here.
199
5
4
5
1
69
2
do
2
You drop 
4
3
2
dname
2
thing
0
0
2
.
199
5
4
8
1
69
2
do
4
3
2
verb
2
player
2
tries
2
 to drop 
4
3
2
iname
2
thing
0
0
2
 but 
4
3
2
verb
2
player
2
fails
2
.
199
5
4
6
1
69
2
do
4
3
2
verb
2
player
2
drops
2
 
4
3
2
iname
2
thing
0
0
2
.
199
5
4
6
1
69
2
do
4
3
2
verb
2
player
2
picks
2
 up 
4
3
2
dname
2
thing
0
0
2
.
199
5
2

199
5
4
5
1
69
2
do
2
You take 
4
3
2
dname
2
thing
0
0
2
.
199
5
4
5
1
69
2
do
2
You can't pick up 
4
3
2
dname
2
thing
0
0
2
.
199
5
5
199
4
4
1
2
generic thing
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
8084
0
913134427
200
1
#6
generic player

152
199
3
8
4
155
36
133
234
init_for_core
199
173
-1
confunc
199
173
-1
disfunc
199
173
-1
initialize
199
173
-1
recycle
199
173
-1
my_huh
199
173
-1
last_huh
199
173
-1
my_match_object
199
173
-1
tell_contents
199
173
-1
notify
199
173
-1
notify_lines
199
173
-1
linesplit
199
173
-1
linelen
35
173
-1
@more
199
25
-1
@wrap
35
9
-2
@linelen*gth
35
25
-1
@pagelen*gth
199
25
-1
tell
199
173
-1
gag_p
199
173
-1
set_gaglist set_gaglist_noisy
199
173
-1
@gag
199
89
-2
@listgag @gaglist @gagged
199
13
-1
@ungag
199
29
-1
whodunnit
199
173
-1
@paranoid
199
89
-2
@sw*eep
199
9
-1
wh*isper
199
157
1
receive_page
199
173
-1
page_origin_msg page_echo_msg page_absent_msg
35
173
-1
i inv*entory
199
9
-1
look_self
199
173
-1
home
199
9
-1
@sethome
199
9
-1
g*et take
199
45
-1
@eject
199
89
5
where*is @where*is
199
93
-2
@wizards
199
29
-1
mail_forward mail_notify
199
173
-1
display_message
199
173
-1
parse_message_seq from_msg_seq %from_msg_seq to_msg_seq %to_msg_seq subject_msg_seq body_msg_seq display_seq_headers display_seq_full messages_in_seq list_rmm new_message_num length_num_le length_date_le length_date_gt length_all_msgs exists_num_eq rm_message_seq undo_rmm expunge_rmm renumber
199
173
-1
msg_summary_line
35
173
-1
msg_text
199
173
-1
notify_mail
199
173
-1
__fix
199
173
-1
current_message
199
173
-1
get_current_message
199
173
-1
set_current_message
199
173
-1
make_current_message
199
173
-1
kill_current_message
199
173
-1
current_folder
199
173
-1
set_current_folder
199
173
-1
parse_folder_spec
199
173
-1
parse_mailread_cmd
199
173
-1
@mail
199
93
-2
@read @peek
199
93
-2
@next @prev
199
89
-2
@rmm*ail
199
89
-2
@renumber
199
25
-1
@unrmm*ail
199
89
-2
@send
199
93
-2
@answer @repl*y
199
89
-2
@forward
199
93
-2
@gripe
199
89
-2
@typo @bug @suggest*ion @idea @comment
199
89
-2
@skip @unsub*scribe
199
89
-2
@subscribe
199
89
-2
mail_catch_up
199
173
-1
@rn check_mail_lists @subscribed
199
13
-1
mail_option
199
173
-1
edit_option
199
173
-1
set_mail_option set_edit_option set_misc_option set_client_option set_who_option
199
173
-1
@mailo*ptions @mail-o*ptions @edito*ptions @edit-o*ptions @misco*ptions @misc-o*ptions @cliento*ptions @client-o*ptions @whoo*ptions @who-o*ptions
199
89
-2
set_name
199
173
-1
set_aliases
199
173
-1
@rename*#
199
89
1
@add-alias*# @addalias*# @add_alias*#
199
89
1
@rmalias*# @rm-alias*#
199
89
5
@desc*ribe
199
89
13
@mess*ages
199
25
-1
@notedit
35
25
-1
@password
199
89
-2
@last-c*onnection
199
29
-1
set_gender set_gender_name set_gender_obj
199
173
-1
@gender
199
25
-1
@ex*amine
199
29
-1
ex*amine
199
25
-1
add_feature
35
173
-1
remove_feature
35
173
-1
@add-feature @addfeature
199
25
-1
@remove-feature @rmfeature @rm-feature
199
25
-1
@features
199
93
11
@features
199
25
-1
@memory
35
9
-1
@version
35
9
-1
@uptime
35
9
-1
@quit
199
9
-1
QUIT-IN-ALL-CAPS
199
9
-1
notify(new)
199
169
-1
examine_commands_ok
199
173
-1
is_listening
199
173
-1
@quicksend @qsend
199
89
-2
!*
35
89
-2
'*
35
89
-2
@pronoun_sub @pronoun-sub
199
73
0
@set-note-string @set-note-text @set-note-value
199
25
-1
description
35
173
-1
@nn
199
9
-1
mu*rmur
199
89
-2
look_in
199
173
-1
@option*s
199
89
-2
options_packages
199
173
-1
options_command
199
173
-1
contribute_partial
199
173
-1
contribute_reset
199
173
-1
contribute_link
199
173
-1
receive_vbox
35
173
-1
contribute_title
199
173
-1
moveto
199
173
-1
ok_to_integrate
35
173
-1
visible_of
35
173
-1
@set-jtext-form
199
25
-1
idle_suffix
199
173
-1
modname_v
199
173
-1
@edit*#
199
89
-2
set_focus_object
199
173
-1
email_address
199
173
-1
misc_option client_option who_option
199
173
-1
@forward-me @forwardme
199
89
-2
ownership_quota
199
173
-1
contribute_newline
199
173
-1
set_current_object set_current_object_stack set_authentication_key
199
173
-1
client_notify
199
173
-1
brief
199
173
-1
picknew
35
25
-1
walk go
35
77
1
_find_path
199
173
-1
_move_by_exits
199
173
-1
join
199
29
-1
pick
35
157
4
pop
35
141
4
match_command
35
173
-1
features
199
173
-1
receive_document
199
173
-1
@co @currentobject @current-object
199
25
-1
parse_current_object
35
173
-1
@pusho*bject
199
25
-1
@popo*object
199
9
-1
@swapo*bject
199
9
-1
jdetails
199
25
-1
@@sendmail
199
89
-2
fol*low
35
41
-1
unfol*low
35
89
-2
followers
35
89
-2
following
35
9
-1
my_match_player
199
173
-1
news
199
25
-1
@helpme @911
199
89
-2
@admin*istrators @admins
199
29
-1
parse_message unparse_message
199
173
-1
@url
199
89
-2
@read-all-new*-mail
199
29
-1
send_self_netmail
199
173
-1
@add-help-db
199
25
-1
@rm-help-db
199
25
-1
@unsubscribed
199
9
-1
reconfunc
199
173
-1
@resend
199
89
-2
help ?*
199
93
-2
match_help_object
35
173
-1
@aliases
199
25
-1
contribute_newline_maybe
199
173
-1
contribute_hr
199
173
-1
set_last_player_paged
199
173
-1
visible
35
173
-1
set_number
35
173
-1
you
35
173
-1
page
199
93
-2
is_idle
199
13
-1
start_dozing
199
93
-2
start_idling
199
93
-2
idle reidle
199
93
-2
idle_msg
199
173
-1
dozing_msg
199
93
-2
start_doing
35
173
-1
doing_msg
199
173
-1
doing undoing
199
93
-2
@who
199
93
-2
debug_if_player
199
173
-1
match_type_object
199
173
-1
dependents_event_really_disconnected
199
173
-1
event_really_disconnected
199
173
-1
maybe_really_disconnected
199
173
-1
modname_c
35
173
-1
@desc*ribe
199
25
-1
@netforw*ard
199
89
-2
@register
199
89
13
@register @email-address
199
25
-1
more_string
199
173
-1
desc_idle_msg
199
173
-1
nominate_for_core
199
173
-1
sit
199
137
0
acceptable
199
173
-1
@shout
199
89
-2
set_home
35
173
-1
anyconfunc
199
173
-1
@ch*eck-full
199
89
-2
@add-handler @addhandler
199
25
-1
handlers
199
173
-1
@remove-handler @rmhandler @rm-handler
199
25
-1
@handlers
199
93
11
@handlers
199
25
-1
total_connect_times
199
173
-1
_set_mail_task
199
173
-1
add_handler
35
173
-1
remove_handler
35
173
-1
set_walking_dest set_walking_task
35
173
-1
move_by_exits
35
173
-1
walking_step
35
173
-1
begin_walking
35
173
-1
end_walking
35
173
-1
walkto
35
173
-1
find_path
35
173
-1
stop
35
9
-1
timestamp_string
199
173
-1
@age
199
25
-1
my_match_room
199
173
-1
lose
35
89
-2
display_current_object
199
173
-1
current_object_string
199
173
-1
edit_set_note_value
199
173
-1
edit_sendmail
199
173
-1
+* ++*
199
85
-2
send_forwarded_moomail
199
173
-1
receive_message
199
173
-1
85
features
previous_connection
mail_lists
email_address
last_disconnect_time
help
linetask
linesleft
linebuffer
pagelen
_mail_task
owned_objects
linelen
current_folder
all_connect_places
last_connect_place
dict
messages_going
responsible
lines
page_absent_msg
page_origin_msg
page_echo_msg
mail_notify
mail_forward
edit_options
mail_options
current_message
messages
last_connect_time
ownership_quota
gender
home
password
gaglist
paranoid
spoof_attribution_msg
pronoun_sub_style
last_player_paged
contributed_line
contributed_links
followers
followable
integrate_sep_msg
integration_enabled
jtext_form
focus_object
misc_options
authentication_key
client_options
link_shepherd
walking_task
temp_brief
jaddress_stack
total_started
total_connect_time
started_keeping_total
current_object
current_object_stack
following
walk_randomness
client_features
client_authkey
client_disfuncs
client_debug
remote_emote_prefix_msg
gag_announce_msg
who_options
last_entrance_time
dozing_msg
idle_start_time
idle_msg
idle_string
dozing_string
doing_string
doing_msg
disconnect_task
last_password_time
last_timestamp
size_quota
shout_msg
contributed_document
walking_dest
first_connect_time
out_of_band_session
101
4
2
1
67
1
68
35
1
0
0
199
0
4
0
199
5
2

199
0
0
0
199
1
4
2
1
186
1
106
199
5
4
2
0
0
0
0
35
1
0
0
35
1
4
0
35
0
0
0
35
1
0
0
199
5
4
0
200
1
0
-79
35
1
0
1
199
4
4
0
199
0
2
?
199
0
4
0
199
5
4
0
199
4
4
0
199
4
0
20
199
4
4
6
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
is
2
 not currently logged in.
199
5
2

199
5
4
5
1
69
2
do
2
Your message has been sent to 
4
3
2
dname
2
thing
0
0
2
.
199
5
4
0
199
5
4
0
199
5
4
0
199
5
4
0
199
5
4
2
0
0
0
0
199
4
4
0
199
4
0
0
199
1
0
0
200
0
1
169
199
5
1
70
35
1
2
impossible password to type
199
0
4
0
199
5
0
0
199
5
2
     --
199
5
1
78
199
5
2
Everyman
35
1
2

199
1
4
0
199
1
4
0
35
1
0
1
35
1
2
  
199
5
0
1
199
5
2
linemode
199
1
1
-1
199
1
4
1
4
2
2
fstamp
2
[ $o:$M $p ]
199
5
0
0
199
0
4
0
199
5
1
-1
35
1
0
0
35
1
0
0
199
5
4
0
35
1
0
0
199
0
0
0
199
0
0
0
199
0
4
2
1
0
2
#0
199
1
4
0
199
1
4
0
35
1
4
2
0
1
0
8
199
1
4
0
199
5
2

199
5
4
0
199
5
0
0
199
5
4
5
1
69
2
do
2
(from 
4
3
2
iname
2
location
0
0
2
)
199
5
4
7
1
69
2
do
2
**GAG:  
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
is
2
 gagged by
199
5
4
2
4
2
2
columns
4
4
2
name
2
location
2
idle
2
doing
4
2
2
order
2
idle
199
1
0
768064450
199
1
4
3
1
69
2
do
2
idling
199
5
0
0
199
5
4
3
1
69
2
do
2
real life intrusion
199
5
2

199
5
2

199
5
2

35
1
4
2
1
69
2
do
199
1
0
0
199
1
0
0
199
0
0
830677321
199
1
4
4
0
0
0
0
0
0
0
0
200
0
4
8
1
69
2
do
4
3
2
name
2
player
4
1
2
dc
2
 
4
3
2
verb
2
player
2
shouts
2
, "
4
3
2
string
2
argstr
0
0
2
"
199
5
3
0
199
0
1
-1
35
1
0
2147483647
199
1
1
-1
199
1
5
199
5
5
199
1
5
199
4
4
1
2
generic player
199
1
2
You see a player who should type '@describe me as ...'.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
1
199
1
0
1
199
1
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
240433
0
913134427
200
1
#7
generic exit

152
199
-1
-1
-1
116
128
-1
32
recycle
199
173
-1
leave_msg oleave_msg arrive_msg oarrive_msg nogo_msg onogo_msg no_one_msg
199
173
-1
set_name
199
173
-1
set_aliases
199
173
-1
defaulting_oleave_msg
199
173
-1
moveto
199
173
-1
examine_key
199
173
-1
substitute
199
173
-1
dest_who
199
173
-1
dest_contents
199
173
-1
dest_description
199
173
-1
set_message
199
173
-1
set_description
199
173
-1
invoke
199
173
-1
integrate_in
199
173
-1
direction sub_direction
35
173
-1
description
199
173
-1
sub_who
199
173
-1
move
199
173
-1
sweep_for_followers
199
173
-1
obvious
35
173
-1
defaulting_oarrive_msg
35
173
-1
integrate_room_msg
199
173
-1
dependents_event_move_by_exit_*
199
173
-1
other_side obj_other_side
199
173
-1
set_walking_cost
35
173
-1
walking_cost
35
173
-1
obj_dest obj_source
35
173
-1
moddir_d moddir_i
35
173
-1
moddir_*
35
173
-1
through_msg
199
173
-1
where_are_you
35
173
-1
14
obvious
source
dest
nogo_msg
onogo_msg
arrive_msg
oarrive_msg
oleave_msg
leave_msg
nothing_msg
no_one_msg
prefix_name
through_msg
walking_cost
29
0
1
199
5
1
-1
199
5
1
-1
199
5
0
0
199
5
0
0
199
5
0
0
199
5
0
0
199
5
0
0
199
5
0
0
199
5
2
nothing
199
5
2
no one
199
5
0
1
199
5
4
6
1
69
2
do
4
3
2
name
4
4
2
special
2
thing
2
dest
4
0
4
3
2
d
4
1
2
in
2
c
2
, you see 
4
3
2
name
4
2
2
contents
4
4
2
special
2
thing
2
dest
4
0
4
1
2
i
2
.
199
5
0
1
35
1
5
199
1
5
199
4
4
1
2
generic exit
199
5
2

199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
20636
0
913134427
200
1
#8
generic openable container

152
35
6
133
-1
133
-1
-1
18
look_self
35
173
-1
open
35
45
-1
@lock_for_open @lock-for-open
35
109
0
is_openable_by
35
173
-1
close
35
45
-1
@unlock_for_open @unlock-for-open
35
45
-1
set_opened
35
173
-1
@opacity
35
105
12
set_opaque
35
173
-1
open_msg close_msg
35
173
-1
look_in
35
173
-1
open_fail_msg empty_msg
35
173
-1
can_put_in
35
173
-1
can_get_from
35
173
-1
is_closable_by
35
173
-1
sub_open sub_opened
35
173
-1
modname_open modname_opened
35
173
-1
set_open_key set_automatic
35
173
-1
8
close_msg
open_msg
opaque
dark
open_fail_msg
opened
open_key
automatic
31
4
8
1
69
2
do
4
3
2
name
2
player
4
1
2
dc
2
 
4
3
2
verb
2
player
2
closes
2
 
4
3
2
name
2
thing
4
1
2
d
2
.
35
5
4
8
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
opens
2
 
4
3
2
dname
2
thing
0
0
2
.
35
5
0
1
35
1
0
0
35
1
4
3
1
69
2
do
2
You can't open that.
35
5
0
1
35
1
0
0
35
0
0
1
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
5
5
199
1
5
35
4
4
1
2
generic openable container
35
5
4
29
2
The Generic Container (for programmers)
2

2
In addition to the command verbs described under `help containers'
2
and the _msg properties described in `help container-messages',
2
the following verbs and properties are available for use within programs
2

2
.opened == TRUE iff the container is open
2
.dark   == TRUE iff the contents of the container may be seen
2
.opaque -- describes the correlation between .open and .dark
2
   == 0  container is always !dark
2
   == 1  container is dark iff it is closed
2
   == 2  container is always dark              
2

2
:set_opaque(newvalue)   
2
  changes the .opaque value for the container
2
  => newvalue or E_PERM or E_INVARG
2

2
:set_opened(newvalue)   
2
  opens/closes the container (updates .open and .dark) according to newvalue
2
  => newvalue or E_PERM
2

2
:is_openable_by(player) 
2
 what the :open command uses to test whether the player should be able to open
2
 the container.  By default this refers to .open_key  (set by
2
 @(un)lock_for_open), but the object owner is free to customize this.
2

2
N.B.:  There is no way to directly set .dark; .dark can be changed only by 
2
changing one of .opaque or .opened.  Use :set_opaque(0) and :set_opaque(2)
2
to have .dark change independently of the value of .opened.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
12347
0
913134427
200
1
#9
generic note

152
199
3
-1
5
5
-1
55
19
r*ead
199
45
-1
er*ase
199
45
-1
wr*ite
199
157
4
del*ete rem*ove
199
153
5
encrypt
199
105
0
decrypt
199
41
-1
text get_text plaintext
199
173
-1
is_readable_by
199
173
-1
set_text
199
173
-1
is_writable_by
199
173
-1
mailme
199
41
-1
show
199
105
1
default_editing
199
173
-1
deliver_to
199
173
-1
read-new
199
41
-1
description
199
173
-1
match_type_object
199
173
-1
nominate_for_core
199
173
-1
do_read
199
173
-1
3
writers
encryption_key
text
25
4
0
199
5
0
0
199
4
4
0
199
4
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
4
4
1
2
generic note
199
5
2
There appears to be some writing on the note ...
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
11669
0
913134427
200
1
#10
Login Commands

16
199
-1
-1
-1
1
-1
60
38
?
199
93
-1
h*elp @h*elp
199
93
-1
w*ho @w*ho
199
93
-1
co*nnect @co*nnect safe-c*onnect safec*onnect
199
93
-1
cr*eate @cr*eate
199
93
-1
q*uit @q*uit
199
93
-1
up*time @up*time
199
93
-1
v*ersion @v*ersion
199
93
-1
parse_command
199
173
-1
check_for_shutdown
199
173
-1
check_player_db
199
173
-1
_match_player
199
173
-1
notify
199
173
-1
tell
35
173
-1
player_creation_enabled
199
173
-1
newt_registration_string registration_string
199
173
-1
init_for_core
199
173
-1
blacklisted graylisted redlisted
199
173
-1
blacklist_add graylist_add redlist_add spooflist_add
199
173
-1
blacklist_remove graylist_remove redlist_remove spooflist_remove
199
173
-1
listname
199
173
-1
record_connection
199
173
-1
@register register
199
93
-1
spooflisted
199
173
-1
unescape_for_url
35
173
-1
guest
199
93
-1
bootlisted
199
173
-1
bootlist
199
173
-1
is_newted newt_message_for
199
173
-1
newt_player
199
173
-1
denewt_player
199
173
-1
t*est
199
173
-1
maybe_print_idle
199
173
-1
avg_idle
199
173
-1
i*dle
199
93
-1
notify_lines
199
173
-1
is_tester
199
173
-1
tester_disconnecting
199
173
-1
16
welcome_message
newt_registration_string
registration_string
registration_address
create_enabled
bogus_command
blank_command
graylist
blacklist
redlist
spooflist
bootlist
newted
sitematch_guests
testers
max_guests
30
4
10
2
Welcome to the JHCore database.
2
  Extracted December 8, 1998 (under LambdaMOO 1.8.0r5)
2

2
Type 'connect wizard' to log in.
2

2
You will probably want to change this text, which is stored in $login.welcome_message.
2

2
Before you do, though, please read `help core-copyright' (linked to `help copyright') for the exceedingly broad copyright on JHCore.
2

2
You will also want to read `help getting-started' for some more information about starting a JHCore MOO.
199
5
2
Your character is temporarily hosed.
199
5
2
Character creation is disabled.
199
5
2

199
5
0
0
199
5
2
?
199
1
2
help
199
1
4
2
4
0
4
0
199
0
4
2
4
0
4
0
199
0
4
2
4
0
4
0
199
0
4
2
4
0
4
0
199
0
4
0
199
0
4
0
199
0
0
1
199
5
4
0
199
1
0
4
199
1
5
199
4
4
1
2
Login Commands
199
5
2
This provides everything needed by #0:do_login_command.  See `help $login' on $core_help for details.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
29562
0
913134427
200
1
#11
Player Last_huh Verbs

16
199
-1
-1
-1
1
-1
10
5
@*
199
173
-1
give hand
199
173
-1
get take
199
173
-1
drop throw
199
173
-1
QUIT
199
173
-1
0
14
5
199
4
4
1
2
Player Last_huh Verbs
199
5
2
A repository of last-resort player verbs to be called by $player:last_huh
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
6093
0
913134427
200
1
#12
Guest Log

16
199
-1
-1
-1
1
-1
11
4
enter
199
173
-1
last
199
173
-1
init_for_core
199
173
-1
nominate_for_core
199
173
-1
1
connections
15
4
0
199
0
5
199
4
4
1
2
Guest Log
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2990
0
913134427
200
1
#13
Generic BigList Utilities

144
35
-1
-1
-1
156
-1
72
24
length
35
173
-1
find_nth
35
173
-1
find_ord
35
173
-1
set_nth
35
173
-1
kill
35
173
-1
insert_after insert_before
35
173
-1
extract_range
35
173
-1
delete_range
35
173
-1
keep_range
35
173
-1
insert_last
35
173
-1
start
35
173
-1
next
35
173
-1
_call
199
173
-1
_find_ord
35
173
-1
_skill
35
173
-1
_extract
35
173
-1
_merge
35
173
-1
_smerge
35
173
-1
_split
35
173
-1
_rmerge
35
173
-1
_scrunch
35
173
-1
_listfind_nth
35
173
-1
_insertfirst
35
173
-1
debug
35
173
-1
3
about
maxfanout
help_text
18
4
15
2
Implementation notes
2
--------------------
2
Each biglist is actually a tree (a kind of B-tree, actually).
2
The routines above pass around handles of the form
2

2
    {root_node, size, leftmost_ord}
2

2
where root_node is the (string) name of a property that holds the root of the tree, size is the number of leaves in the tree, and leftmost_ord is the :_ord value of the leftmost element of the list (i.e., the leftmost leaf).
2
Each node property has a value of the form 
2

2
    {height,list of subtrees}.
2

2
where the each of the subtrees is itself a 3-element list as above unless
2
the height is 0, in which case the subtrees are actually biglist elements of the arbitrary form determined by the home object.
2
At every level, each node except the rightmost has between this.maxfanout/2 and this.maxfanout subtrees; the rightmost is allowed to have as few as 1 subtree.
35
5
0
7
35
5
4
1
2
foo
35
5
5
35
5
5
35
4
4
2
2
ghblu
2
biglist_utils
35
1
4
73
2
Generic BigList Utilities
2
-------------------------
2

2
This is a package for maintaining huge persistent (sorted) lists in a format that is less likely to spam the server (which runs into a certain amount of trouble dealing with long ordinary lists --- btw we use `biglist' to refer to the huge data structure we're about to describe and `list' to refer to ordinary MOO lists {...}).  The biglist in question lives on a particular object, to which we will refer in the discussion below as the `home' object, and its various elements appear as leaves of a tree whose nodes are kept in properties of the home object.  It should be noted that the home object does not need to be (and in fact should *not* be) a descendant of this one; this object merely provides utilities for manipulating the properties on the home object that are used in a particular biglist manipulation.  
2

2
All of the utilities below refer to `caller' to locate the home object.  Thus verbs to manipulate a given biglist must be located on or inherited by its home object itself.  The home object needs to define the following verbs
2

2
  :_make(@args)     => new property on home object with value args
2
  :_kill(prop)      delete a given property that was created by :_make
2
  :_get(prop)       => home.prop
2
  :_put(prop,@args) set home.prop = args
2
  :_ord(element)    given something that is of the form of a biglist element
2
                    return the corresponding ordinal (for sorting purposes).
2
                    If you never intend to use :find_ord, then this can be a 
2
                    routine that always returns 0 or some other random value.
2

2
See $big_mail_recipient (Generic Large-Capacity Mail Recipient) for examples.
2

2
Those of the following routines that take a biglist argument are expecting
2
either {} (empty biglist) or some biglist returned by one of the other routines
2

2
  :length(biglist)          => length(biglist) (i.e., number of elements)
2
  :find_nth(biglist,n)      => biglist[n]
2
  :find_ord(biglist,k,comp) => n where n is
2
     the largest such that home:(comp)(k,home:_ord(biglist[n])) is false, or
2
     the smallest such that home:(comp)(k,home:_ord(biglist[n+1])) is true.
2
     Always returns a value between 0 and length(biglist) inclusive.
2
     This assumes biglist to be sorted in order of increasing :_ord values 
2
     with respect to home:(comp)().
2
     Standard situation is :_ord returns a number and comp is a < verb.
2

2
  :start(biglist,s,e)  => {biglist[s..?],@handle} or {}
2
  :next(@handle)       => {biglist[?+1..??],@newhandle} or {}
2
     These two are used for iterating over a range of elements of a biglist
2
     The canonical incantation for doing
2
        for elt in (biglist[first..last])
2
          ...
2
        endfor
2
     is
2
        handle = :start(biglist,first,last);
2
        while(handle)
2
          for elt in (handle[1])
2
            ...
2
          endfor
2
          handle = :next(@listdelete(handle,1));
2
        endwhile
2

2
The following all destructively modify their biglist argument(s) L (and M).
2

2
  :set_nth(L,n,value)  =>  L[n] = value
2
     replaces the indicated element
2

2
  :insert_before(L,M,n) => {@L[1..n-1],@M,@L[n..length(L)]}
2
  :insert_after (L,M,n) => {@L[1..n],  @M,@L[n+1..length(L)]}
2
     takes two distinct biglists, inserts one into the other at the given point
2
     returns the resulting consolidated biglist
2

2
  :extract_range(L,m,n) => {{@L[1..m-1],@L[n+1..]}, L[m..n]} 
2
     breaks the given biglist into two distinct biglists.
2

2
  :delete_range(L,m,n[,leafkiller]) => {@L[1..m-1],@L[n+1..]}
2
  :keep_range  (L,m,n[,leafkiller]) => L[m..n]
2
     like extract_range only we destroy what we don't want.
2

2
  :insertlast(L,value)  => {@L,value}
2
     inserts a new element at the end of biglist.  
2
     If find_ord is to continue to work properly, it is assumed that the 
2
     home:_ord(elt) is greater (comp-wise) than all of the :_ord values
2
     of elements currently in the biglist.
2

2
  :kill(L[,leafkiller]) 
2
     destroys all nodes used by biglist.  
2
     Calls home:leafkiller on each element.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
23372
0
913134427
200
1
#14
Generic Large-Capacity Mail Recipient

144
35
-1
-1
-1
41
17
151
41
_genprop
35
173
-1
_make
199
173
-1
_kill
199
173
-1
_get
35
173
-1
_put
35
173
-1
_ord
35
173
-1
_makemsg
35
173
-1
_killmsg
35
173
-1
_message_num
35
173
-1
_message_date
35
173
-1
_message_hdr
35
173
-1
_message_text
35
173
-1
_lt_msgnum
35
173
-1
_lt_msgdate
35
173
-1
receive_batch
35
173
-1
receive_message
35
173
-1
messages_in_seq
35
173
-1
display_seq_headers
35
173
-1
display_seq_full
35
173
-1
list_rmm
35
173
-1
undo_rmm
35
173
-1
expunge_rmm
35
173
-1
rm_message_seq
35
173
-1
renumber
35
173
-1
length_all_msgs
35
173
-1
length_num_le
35
173
-1
length_date_le
35
173
-1
exists_num_eq
35
173
-1
new_message_num
35
173
-1
from_msg_seq
35
173
-1
%from_msg_seq
35
173
-1
to_msg_seq
35
173
-1
%to_msg_seq
35
173
-1
subject_msg_seq
35
173
-1
body_msg_seq
35
173
-1
date_sort
35
173
-1
_fix_last_msg_date
35
173
-1
__fix
35
173
-1
init_for_core
199
173
-1
length_date_gt
35
173
-1
rmm_for_core
199
173
-1
4
summary_uses_body
_mgr
mowner
_genprop
30
0
0
35
5
1
13
35
5
1
35
35
1
2

35
1
5
35
5
0
0
35
1
5
35
0
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
5
5
35
1
5
35
0
5
35
4
4
1
2
Generic Large-Capacity Mail Recipient
35
1
4
24
2
Generic Large Capacity Mail Recipient
2
-------------------------------------
2
Since any modifications to large lists entail copying the entire list
2
over, operations on ordinary mail recipients having large numbers of
2
messages, that actually change the content of .messages will take
2
inordinately long.  Thus we have this version which makes use of the
2
$biglist package, scattering the messages onto numerous properties so
2
that write operations involving only a few messages will not require
2
recopying of the entire list.
2

2
In nearly all respects it behaves as the ordinary Mail Recipient,
2
except that it is faster for certain kinds of operations.
2

2
Certain unimplemented verbs, like :date_sort(), and :messages()
2
currently return E_VERBNF.
2

2
To convert an existing $mail_recipient-child (call it #MR) into a
2
$big_mail_recipient-child the basic procedure is
2

2
    ;;something.foo= #MR:messages();
2
    @rmm 1-$ from #MR
2
    @unrmm expunge
2
    @chparent #MR to $big_mail_recipient
2
    ;#MR:receive_batch(@something.foo);
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
26190
0
913134427
200
1
#15
Limbo

16
199
-1
35
-1
1
-1
12
7
confunc
199
173
-1
who_location_msg
35
173
-1
contents
199
173
-1
acceptable
199
173
-1
in_name
35
173
-1
is_public_location
35
173
-1
modname_in
35
173
-1
0
14
5
199
4
4
2
2
Limbo
2
body bag
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
1
199
5
0
1
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2592
0
913134427
200
1
#16
Registration Database

0
35
-1
-1
-1
79
-1
97
7
find(old)
35
169
-1
add
35
173
-1
init_for_core
199
173
-1
suspicious_address
199
173
-1
suspicious_userid
199
173
-1
check_address_request
199
173
-1
prohibit_lookup prohibit_modify
35
173
-1
0
21
4
2
1
35
2

35
5
0
0
35
1
0
0
35
1
5
35
5
5
35
5
4
2
0
913134423
0
1439449680
35
1
4
4
2

2

4
0
4
0
35
1
5
35
4
4
1
2
Registration Database
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
7079
0
913134427
200
1
#17
Player-Creation-Log

0
35
42
-1
150
14
-1
-1
3
display_seq_headers
199
173
-1
msg_summary_line
199
173
-1
is_usable_by
199
173
-1
1
autoregistration_player
31
1
111
199
1
0
1
35
5
1
13
35
5
1
35
35
1
5
35
1
5
35
5
5
35
1
5
35
0
0
1
35
5
5
35
5
5
35
5
5
35
5
4
1
1
2
35
1
4
0
35
1
5
35
5
5
35
1
5
35
0
5
35
4
4
3
2
Player-Creation-Log
2
Player_Creation_Log
2
PCL
35
1
2
Log of player creations.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2354
0
913134427
200
1
#18
string utilities

24
199
-1
-1
-1
156
-1
74
85
space
199
173
-1
left
199
173
-1
right
199
173
-1
centre center
199
173
-1
columnize columnise
35
173
-1
from_list
199
173
-1
english_list
199
173
-1
names_of
35
173
-1
from_seconds
199
173
-1
trim
35
173
-1
triml
35
173
-1
trimr
35
173
-1
strip_chars
35
173
-1
strip_all_but
35
173
-1
uppercase lowercase
199
173
-1
capitalize capitalise
199
173
-1
literal_object
35
173
-1
match
199
173
-1
match_str*ing
199
173
-1
match_object
199
173
-1
match_player
35
173
-1
match_player_or_object
199
173
-1
find_prefix
199
173
-1
index_d*elimited
35
173
-1
is_numeric
35
173
-1
ordinal
35
173
-1
group_number
199
173
-1
english_number
199
173
-1
english_ordinal
35
173
-1
english_ones
199
173
-1
english_tens
199
173
-1
subst*itute
35
173
-1
substitute_d*elimited
199
13
-1
_cap_property
199
173
-1
pronoun_sub
199
173
-1
pronoun_sub_secure
35
173
-1
pronoun_quote
35
173
-1
explode
35
173
-1
words
35
173
-1
word_start
35
173
-1
to_value
35
173
-1
prefix_to_value
35
173
-1
_tolist
35
173
-1
_unquote
35
173
-1
_toscalar
35
173
-1
parse_command
199
173
-1
from_value
199
173
-1
print
199
173
-1
print_suspended
35
173
-1
reverse
199
173
-1
char_list
35
173
-1
regexp_quote
35
173
-1
connection_hostname_bsd
35
173
-1
connection_hostname
35
173
-1
from_value_suspended
199
173
-1
first_word
35
173
-1
common
199
173
-1
name_list namec_list dnamec_list inamec_list dname_list iname_list
35
173
-1
_pronoun_sub
199
173
-1
name_and_number_list iname_and_number_list dname_and_number_list
199
173
-1
to_list
199
173
-1
character_to_ascii
199
173
-1
character_to_hex_ascii
199
173
-1
ascii_to_character
199
173
-1
hex_ascii_to_character
199
173
-1
print_truncated
35
173
-1
print_truncated_recursive
35
173
-1
name_and_number nn iname_and_number dname_and_number namec_and_number inamec_and_number dnamec_and_number
35
173
-1
next_index
35
173
-1
match_room
199
173
-1
is_uppercase
35
173
-1
index_all
199
173
-1
columnize_with_headers columnise_with_headers
35
173
-1
columnize_suspended
35
173
-1
nonblank
35
173
-1
is_lowercase
35
173
-1
columnize_no_truncate
35
173
-1
print_with_names
199
173
-1
match_stringlist
199
173
-1
title_list name_for_tell_contents_list name_for_look_self_list titlec_list namec_for_look_self_list namec_for_tell_contents_list name_and_number_list iname_and_number_list dname_and_number_list namec_and_number_list inamec_and_number_list dnamec_and_number_list
35
173
-1
glob_to_regexp
35
173
-1
xglob_to_regexp
35
173
-1
match_all
199
173
-1
explode_match
199
173
-1
incr_alpha
199
173
-1
7
digits
ascii
alphabet
_character_set
_character_set_in_ascii
_character_set_in_hex_ascii
tab
22
2
0123456789
199
5
2
 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
199
5
2
abcdefghijklmnopqrstuvwxyz
199
5
2
	 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
199
5
4
96
0
8
0
32
0
33
0
34
0
35
0
36
0
37
0
38
0
39
0
40
0
41
0
42
0
43
0
44
0
45
0
46
0
47
0
48
0
49
0
50
0
51
0
52
0
53
0
54
0
55
0
56
0
57
0
58
0
59
0
60
0
61
0
62
0
63
0
64
0
65
0
66
0
67
0
68
0
69
0
70
0
71
0
72
0
73
0
74
0
75
0
76
0
77
0
78
0
79
0
80
0
81
0
82
0
83
0
84
0
85
0
86
0
87
0
88
0
89
0
90
0
91
0
92
0
93
0
94
0
95
0
96
0
97
0
98
0
99
0
100
0
101
0
102
0
103
0
104
0
105
0
106
0
107
0
108
0
109
0
110
0
111
0
112
0
113
0
114
0
115
0
116
0
117
0
118
0
119
0
120
0
121
0
122
0
123
0
124
0
125
0
126
199
5
4
96
2
08
2
20
2
21
2
22
2
23
2
24
2
25
2
26
2
27
2
28
2
29
2
2A
2
2B
2
2C
2
2D
2
2E
2
2F
2
30
2
31
2
32
2
33
2
34
2
35
2
36
2
37
2
38
2
39
2
3A
2
3B
2
3C
2
3D
2
3E
2
3F
2
40
2
41
2
42
2
43
2
44
2
45
2
46
2
47
2
48
2
49
2
4A
2
4B
2
4C
2
4D
2
4E
2
4F
2
50
2
51
2
52
2
53
2
54
2
55
2
56
2
57
2
58
2
59
2
5A
2
5B
2
5C
2
5D
2
5E
2
5F
2
60
2
61
2
62
2
63
2
64
2
65
2
66
2
67
2
68
2
69
2
6A
2
6B
2
6C
2
6D
2
6E
2
6F
2
70
2
71
2
72
2
73
2
74
2
75
2
76
2
77
2
78
2
79
2
7A
2
7B
2
7C
2
7D
2
7E
199
5
2
	
199
5
2
millions of
199
5
5
199
4
4
2
2
string
2
utils
199
5
4
126
2
For a complete description of a given verb, do `help $string_utils:verbname'
2

2
    Conversion routines:
2

2
:char_list    (string)                               => {"a", "b", "c"}
2
:to_list      (string)                               => {"a", "foo", "bear"}
2
 (see also :explode, below)
2

2
:from_list    (list [,sep])                          => "foo1foo2foo3"
2
:english_list (str-list[,none-str[,and-str[, sep]]]) => "foo1, foo2, and foo3"
2
:title_list*c (obj-list[,none-str[,and-str[, sep]]]) => "foo1, foo2, and foo3"
2
                                                  or => "Foo1, foo2, and foo3"
2

2
:[i|d]name[c]_and_number                             => "foo (#123)"
2
:[i|d]name[c]_list (obj-list, name-args)             => english list of names
2
                                                        in appropriate style
2
:[i|d]name[c]_and_number_list(obj-list, name-args)   => same, plus " (#123)"
2
:names_of     (obj-list)  => "foo1 (#123)   foo2 (#456)   foo3 (#789)"
2

2
:to_value       (string)                             => {1, value}
2
                                                  or => {0, error message}
2
:prefix_to_value(string)                             => {rest-of-string, value}
2
                                                  or => {0, error message}
2
:end_expression (string)                             => index of expression
2

2
:from_value[_suspended] (value [,quoteflag [,maxlistdepth]])
2
                                                     => "{foo1, foo2, foo3}"
2
:print[_suspended] (value)                           => "{foo1, foo2, foo3}"
2
:print_truncated   (value, length, suffix)           => "{foo1, fo.."
2
:print_with_names  (value)                           => "{foo1, foo2 (#123)}"
2

2
:english_number(42 [,zero])  => "forty-two"
2
:english_ordinal(42)         => "forty-second"
2
:ordinal(42)                 => "42nd"
2
:group_number(42135 [,sep])  => "42,135"
2
:from_seconds (num)          => "an hour" or "3 days" or "15 seconds"
2

2
:glob_to_regexp("foo*b?r")   => "^foo.*b.r$"
2
:xglob_to_regexp("f*ba[rz]") => "^f.*ba[rz]$"
2

2
    Type checking:
2

2
:is_numeric   (string) => return true if string is composed entirely of digits
2

2
    String Matching:
2

2
:match_string(string, pattern, options) => * wildcard matching
2
:find_prefix(prefix, string-list)       => list index of element starting
2
                                           with prefix
2
:match_stringlist(string, string-list)  => index/$ambiguous_match/$failed_match
2
:index_delimited(string,target[, case]) => index of delimited string occurrence
2
:index_all(string, target[, delimited]) => all occurrences of target in string
2
:next_index(str1, str2, prev[, case])   => index, after index `prev'
2

2
    Object Matching (see also $command_utils):
2

2
:literal_object(string)                        => object match independent
2
                                                  of location
2
:match        (string, [obj-list, prop-name]+) => matching object
2
:match_player (string-list[,me-object])        => list of matching players
2
:match_object (string, location)               => default object match
2
:match_player_or_object(@string-list)          => matching object or player
2
:match_room   (string)                         => matching room object
2

2
    Parsing:
2

2
:explode (string,char) -- string => list of words delimited by char
2
 (see also :to_list, above)
2
:words   (string)      -- string => list of words (as with command line parser)
2
:word_start (string)   -- string => list of start-end pairs.
2
:first_word (string)   -- string => {first word, rest of string} or {}
2

2
:parse_command(cmd_line[, player]) -- string => parsed command info
2

2
    Pretty printing:
2

2
:space         (n/string[,filler])     => n spaces
2
:left          (string,width[,filler]) => left justified string in field 
2
:right         (string,width[,filler]) => right justified string in field
2
:center/re     (string,width[,filler]) => centered string in field
2
:columnize/se[_suspended](list,n[,width])
2
                                       => list of strings in n columns
2
:columnize/se_no_truncate(list,n[,linelen])
2
                                       => same, but will not truncate items
2
:columnize/se_with_headers(headers, fields) => columnize, plus headers
2

2
    Substitutions
2

2
:substitute (string, subst_list [,case])  -- general substitutions.
2
:substitute_delimited(...)                -- same, but with word boundaries.
2
:pronoun_sub (string/list[,who[,thing[,location]]])
2
                                          -- pronoun substitutions.
2
:pronoun_sub_secure (string[,who[,thing[,location]]],default)
2
                                          -- substitute and check for names.
2
:pronoun_quote (string/list/subst_list)   -- quoting for pronoun substitutions.
2
:regexp_quote(string)                     -- quoting for regexps.
2

2
    Miscellaneous string munging:
2

2
:trim         (string)       => string with outside whitespace removed.
2
:triml        (string)       => string with leading whitespace removed.
2
:trimr        (string)       => string with trailing whitespace removed.
2
:strip_chars  (string,chars) => string with all chars in `chars' removed.
2
:strip_all_but(string,chars) => string with all chars not in `chars' removed.
2
:capitalize/se(string)       => string with first letter capitalized.
2
:uppercase/lowercase(string) => string with all letters upper or lowercase.
2
:reverse      (string)       => string, backwards.
2

2
:common(string1, string2)    => length of longest common prefix.
2
:is_uppercase (string)       => true iff no lowercase letters in string
2
:is_lowercase (string)       => true iff no uppercase letters in string
2
:nonblank     (string)       => true iff any non-whitespace characters
2

2
:connection_hostname(string) => hostname part of connection identifier.
2

2
    Conversions to and from common string formats:
2

2
:character_to_ascii(char)    => integer ASCII rep of char
2
:character_to_hex_ascii(char) => two-digit hexadecimal string ASCII rep of char
2
:ascii_to_character(num)     => character represented in ASCII by num
2
:hex_ascii_to_character(hex) => ASCII character represented by two-character hexadecimal string hex
2

2
    A few useful properties:
2

2
alphabet                    => "abcdefghijklmnopqrstuvwxyz"
2
tab                         => a single tab character
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
85694
0
913134427
200
1
#19
building utilities

24
199
-1
-1
-1
156
-1
18
9
make_exit
199
173
-1
set_names
199
173
-1
recreate
199
173
-1
parse_names
199
173
-1
object_audit_string
199
173
-1
audit_object_category
199
173
-1
size_string
200
173
-1
do_audit do_prospectus
199
173
-1
do_audit_item
199
173
-1
0
15
5
199
5
5
199
4
4
2
2
building
2
utils
199
5
4
14
2
Verbs useful for building.  For a complete description of a given verb, do `help $building_utils:verbname'.
2

2
make_exit(spec,source,dest[,don't-really-create]) => a new exit
2
          spec is an exit-spec as described in `help @dig'
2

2
set_names(object, spec) - sets name and aliases for an object
2
parse_names(spec) => list of {name, aliases}
2
          in both of these, spec is of the form
2
            <name>[[,:]<alias>,<alias>,...]
2
          (as described in `help @rename')
2

2
recreate(object, newparent) - effectively recycle and recreate object
2
          as a child of newparent
2
transfer_ownership(object, old-owner, new-owner) - just what it sounds like
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
11443
0
913134427
200
1
#20
Verb Help

16
35
-1
-1
-1
30
106
186
6
find_topics
35
173
-1
get_topic
199
173
-1
topic_text
199
173
-1
set_topic_text
199
173
-1
add_topic
35
173
-1
index
35
173
-1
0
15
4
0
35
1
5
35
4
4
1
2
Verb Help
35
5
2
A `help database' that knows about all of the documented verbs.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5063
0
913134427
200
1
#21
Core Utility Help

16
35
-1
-1
-1
30
-1
163
5
find_topics
35
173
-1
get_topic
35
173
-1
topic_text
199
173
-1
set_topic_text
199
173
-1
nominate_for_core
199
173
-1
30
$login
$openable_container
$mail_agent
MR-subscribing
MR-naming
MR-access
$mail_recipient
receiving-mail
mail-format
mail-resolve
sending-mail
mail-system
$player_db
core-index
object-matching
$no_one
$exit
$room
$help
$generic_db
$generic_editor
$generic_help
$generic_options
MR-sequences
MR-reading
MR-writing
MR-searching
$english
$housekeeper
mail-recipient
45
4
80
2
$login
2
1-----
2

2
This object manages command parsing for unconnected players and governs the initiation of an actual connection.  There are verbs pertaining to registration, controlling player creation, and doing site-locks (see `help blacklist' on $wiz_help).
2

2
Commands for unconnected players
2
2-------------------------------
2

2
Recall that for each line that an unconnected player types, the server parses that line into words (the same way normal commands are parsed into a list of words that is then assigned to `args') and then #0:do_login_command is called.
2

2
  :parse_command (@args) => {verb, @args}
2
    given the sequence of arguments that were fed to #0:do_login_command
2
    this returns the name of a verb on $login to be called together with a 
2
    list of arguments to be passed to it.
2

2
By default this just returns args iff args[1] names an actual verb on $login that is +x and has args {"any","none","any"}.  Otherwise, it returns one of
2

2
  .blank_command   -- verb to call if command line is empty
2
  .bogus_command   -- verb to call if command line otherwise unintelligible
2

2
In both cases :parse_command returns a verbname followed by the entire args list passed to it (including the would-be verb at the beginning if any).
2

2
Currently the following verbs are availabe to non-connected players
2

2
  h*elp @h*elp       -- print .welcome_message
2
  ?                  -- print a short list of available commands
2
  w*ho @w*ho         -- print a list of logged in players (excluding wizards)
2
  co*nnect @co*nnect -- connect to an existing player
2
  cr*eate @cr*eate   -- create a new player
2
  up*time @up*time   -- tell how long the server has been running
2
  version @version   -- tell which version of the server is running
2
  q*uit @q*uit       -- logoff
2

2
Adding a new command is fairly straightforward; just create a verb on $login, making sure a previous verb doesn't already match the name you want to give it.  Then give it args of "any" "none "any" and make sure it is +x.  Such a verb should begin with `if (caller != #0) return E_PERM; ...' so as to prevent anyone other from a not-logged-in player from making use of it.
2

2
Customizations
2
2-------------
2

2
  .welcome_message 
2
    -- the message for "help" to print.
2
  .create_enabled 
2
    == 0 => @create prints .registration_string if one tries to use it
2
    == 1 => anyone from a non-blacklisted site (see `help blacklist')
2
            may use @create to make a new player
2

2
  .registration_address
2
    -- an email address for character creation requests
2
  .registration_string  
2
    -- string to print to players to give them information about how to get 
2
       a character created for them, .registration_address is substituted 
2
       for %e, % for %%
2
  .newt_registration_string
2
    -- string to print to @newted players (see `help @newt').
2
       same substitutions as for .registration_string.
2

2
  .sitematch_guests
2
    -- use $country_db to connect guests from ".fr" as "French guest", etc.
2

2
Other verbs
2
2----------
2

2
   :registration_string()      => .registration_string with substitutions
2
   :newt_registration_string() => .newt_registration_string with substitutions
2
   :player_creation_enabled(connection) 
2
       decides whether someone on connection should be allowed to create 
2
       a player.  If you decide this shouldn't depend strictly on the blacklist
2
       and on the value of .create_enabled, here's where the extra code can go.
2
   :check_for_shutdown()
2
       prints a warning message to append to the login banner in the event 
2
       that the server will be going down soon.
2
   :check_player_db()
2
       prints a warning message to append to the login banner in the event 
2
       that $player_db is being reloaded to warn players that their character
2
       names might not be recognized.
2

2
Site locks
2
2---------
2

2
See `help blacklist'.
2

35
5
4
29
2
The Generic Container (for programmers)
2

2
In addition to the command verbs described under `help containers'
2
and the _msg properties described in `help container-messages',
2
the following verbs and properties are available for use within programs
2

2
.opened == TRUE iff the container is open
2
.dark   == TRUE iff the contents of the container may be seen
2
.opaque -- describes the correlation between .open and .dark
2
   == 0  container is always !dark
2
   == 1  container is dark iff it is closed
2
   == 2  container is always dark              
2

2
:set_opaque(newvalue)   
2
  changes the .opaque value for the container
2
  => newvalue or E_PERM or E_INVARG
2

2
:set_opened(newvalue)   
2
  opens/closes the container (updates .open and .dark) according to newvalue
2
  => newvalue or E_PERM
2

2
:is_openable_by(player) 
2
 what the :open command uses to test whether the player should be able to open
2
 the container.  By default this refers to .open_key  (set by
2
 @(un)lock_for_open), but the object owner is free to customize this.
2

2
N.B.:  There is no way to directly set .dark; .dark can be changed only by 
2
changing one of .opaque or .opened.  Use :set_opaque(0) and :set_opaque(2)
2
to have .dark change independently of the value of .opened.
35
5
4
68
2
$mail_agent
2

2
This object contains a two distinct sets of routines:
2

2
  1.  utilities for performing basic mailsystem functions, e.g.,
2
      matching on recipient names, resolving mail forwarding, 
2
      formatting messages, sending messages
2

2
Recipient Matching
2

2
match           - match on a $mail_recipient
2
match_recipient - match on either a $mail_recipient or a player
2
match_failed    - print angry messages to the user for $failed/ambiguous_match
2

2
look_self  - provides a list of available $mail_recipients
2
check_names
2
touch
2
accept
2

2
Message Format
2

2
make_message        - produces a message in the canonical transmission format
2
name                - single recipient     => string for address field
2
name_list           - list of recipients   => string for address field
2
parse_address_field - address field string => object list
2

2
Sending Messages
2

2
send_message  - advertised message sending routine.
2
raw_send      - raw message sending routine 
2
                (only called by $mail_editor:send and this:send_message)
2
resolve_addr  - converts a given list recipients into a list of actual 
2
                recipients and objects to be notified.
2
sends_to      - Does X forward (transitively) to Y
2

2
Mail Options
2

2
option         
2
option_verbose
2

2
  2.  canonical versions of mail_recipient verbs
2

2
Ideally, the verbs to perform operations on a given mail recipient would be located on the recipient itself, except for the fact that these verbs also need to be located on players, which for various reasons, shouldn't be children of $mail_recipient.  Multiple inheritance would solve our problems, but we don't have it yet.  Ergo, both $mail_recipient and $player refer to the following verbs here:
2

2
display_seq_full     print entire text of messages  (@read)
2
display_seq_headers  print headers of messages      (@mail)
2
rm_message_seq       remove messages                (@rmm)
2
undo_rmm             undo last rm_message_seq       (@unrmm)
2
expunge_rmm          flush removed messages         (@unrmm expunge)
2
list_rmm             list removed messages          (@unrmm list)
2
renumber             renumber messages              (@renumber)
2
msg_summary_line     msg header => display_seq_headers/list_rmm summary line
2

2
parse_message_seq    command line msg sequence spec => message sequence
2
new_message_num      => message number of next new message
2
length_all_msgs      => number of messages (total)
2
length_num_le        => number of messages numbered <= some number
2
length_date_le       => number of messages dated <= some date
2
exists_num_eq        => true iff there exists a messsage with the given number
2
from_msg_seq         => message sequence of msgs from given sender(s)
2
to_msg_seq           => message sequence of msgs to given recipient(s)
2
subject_msg_seq      => message sequence of msgs with subjects containing text
2
body_msg_seq         => message sequence of msgs with bodies containing text
2
messages_in_seq      => list of {message number, message} pairs
2

2
messages             == :messages_in_seq(1,:length_all_msgs()+1)   (obsolete)
2

2
The $mail_agent versions of these verbs are set_task_perms(caller_perms()) and perform their operations on caller, which in turn is assumed to have done any necessary security checks.
35
5
4
36
2
Subscribing to mail recipients
2
1-----------------------------
2

2
There are two notions of being "subscribed" to a mailing list/recipient.
2

2
  o  Hard subscribed == being on the recipient's .mail_forward list so that mail sent to this list is forwarded to one's own .messages as well (see `help mail-forwarding').
2
  o  Soft subscribed == keeping track of a current message for this recipient and (optionally) being on the recipient's .mail_notify list.
2

2
Each player has a .current_message property that contains, for each recipient the player cares to keep track of, a current message number and a last read date.
2

2
player:current_message(rcpt)                 (somewhat obsolete)
2
 => player's current message number for rcpt 
2

2
player:get_current_message(rcpt) 
2
 => player's {current message number for rcpt, last-read-date for rcpt}
2

2
player:make_current_message(rcpt)
2
 => adds a current_message entry for rcpt  (NOOP if rcpt == player)
2

2
player:set_current_message(rcpt,n|E_NONE,[,date])
2
 => sets player's current message number for rcpt to n iff n!=E_NONE
2
    updates the last-read-date for rcpt to date iff date > last-read-date
2

2
player:kill_current_message(rcpt)
2
 => removes current-message info for rcpt  (NOOP if rcpt == player)
2

2
On $mail_recipient, .mail_forward and .mail_notify are -c so one needs to use the following verbs to actually modify them.
2

2
    :add_forward(@new_recipients)
2
    :delete_forward(@recpients)
2
    :add_notify(@new_notifiees)
2
    :delete_notify(@notifiees)
2

2
A recipient's owner is, of course, allowed to make arbitrary changes to .mail_forward and .mail_notify.  However, the default versions of these verbs also allow any player to add him/herself to a recipient's .mail_forward or .mail_notify if the recipient is readable (see `help MR-access') by him/her.
2

2
Likewise any player may use the :delete* verbs to delete him/herself from any .mail_forward/.mail_notify list, regardless of his actual access to the list.
35
5
4
15
2
One may always refer to a list by its object number.  In order to refer to it by name, it must be contained in $mail_agent, which holds all mailing lists, i.e., those that you want others to be able to refer to by name.
2

2
The .aliases field holds the names by which one may refer to the list, but only those names not containing spaces actually count for anything.  As with certain other types of objects (e.g., players), set_aliases() needs to be called in order to change the .aliases field.
2

2
$mail_agent:match(name) 
2
    is the canonical way to obtain the objectid of a mailing list 
2
    given the name ("*" is assumed; an initial "*" will be dropped).
2

2
$mail_agent:match_recipient(name) 
2
    is the canonical way to obtain the objectid of a list or player
2
    matching the given name.  An initial "*" indicates that this is 
2
    supposed to be a list.
2

2
$mail_agent:match_failed(objid,name) 
2
    is the mail_recipient counterpart to $command_utils:object_match_failed
35
5
4
35
2
Controlling access to mail recipients
2
1------------------------------------
2

2
:is_writable_by(one) - one may alter/add/remove saved messages
2

2
:is_readable_by(one) - one may read messages.
2

2
:is_usable_by(one)   - one may send to this list
2

2
By default, these verbs refer to the following properties:
2

2
writers   - list of players other than the owner who have full privileges.
2
readers   - if == 1, indicates a public mailing list.
2
            list of additional readers (by default anyone who receives mail 
2
            sent to the list can read the saved messages).
2
moderated - if false, indicates a normal mail recipient everyone can send to.
2
            otherwise this should be a list of approved senders.
2

2
Terminology:
2

2
A mailing list is "public" if everyone can read it.
2

2
A mailing list is "moderated" if not everyone can send to it.
2

2
Note that while being able to write to a recipient implies being able to read from it or send to it, neither of read-ability or send-ability implies the other.
2

2
It is highly recommended that if you are creating custom mail recipients with variable reader/sender lists, i.e., you find you need to write your own :is_readable/usable/writabe_by verbs, you are best off if such verbs are of the form
2

2
  return pass(@args) || << your_test(args[1]) >>
2

2
and have .writers == .readers == {} and .moderated == 1.  This will ensure
2

2
  o  Wizards having write access.  This is necessary in order for :receive_message to work.
2
  o  Writers being able to read and send (the converse being a ludicrous situation.
2
  o  Persons on the mail_forward list of someone with reader access will also have read access (convenient).
35
5
4
17
2
Generic mail recipient
2
1---------------------
2

2
A "mail recipient" is, by definition, an object that can be sent mail.  Mail recipients must either be players or descendants of $mail_recipient.
2

2
One source of confusion is that the terms "mail recipient", "mail folder", "mailing list", and "mail collection" really all refer to the same object.  It so happens that $mail_recipient serve several distinct functions and we tend to use whatever term happens to best match the application under discussion, e.g., it's a "mailing list" if we're playing with its .mail_forward property but it's also a "mail folder" if we're examining the messages that have been saved in it.
2

2
Topics
2
2=====
2

2
MR-access -- controlling read, write and send access to a recipient
2
MR-naming -- naming conventions and how to match on recipient names
2
MR-sequences -- message sequence arguments to $mail_recipient verbs
2
MR-reading -- reading messages/headers on recipients
2
MR-searching -- searching message lists for patterns in certain fields
2
MR-writing -- removing and renumbering messages
2
MR-subscribing -- updating .mail_forward, .mail_notify and the story of .current_message
35
5
4
18
2
Receiving Mail
2
1-------------
2

2
By definition a recipient "receives" a mail message when its :receive_message verb is called with that message as an argument.
2

2
:new_message_num()
2
=> number that will be assigned to the next incoming message.
2

2
By default this returns the maximum of the message numbers appearing in messages or .messages_going, incremented by 1.  If the recipient is a player then the value returned will be 1 higher if it conflicts with the player's current message number for him/herself.
2

2
:receive_message(msg,sender)
2

2
By default this first calls this:new_message_num to obtain a message number to assign to the incoming message and then appends {num,msg} to this.messages.  `sender', the original sender, is supplied in case one wants different action depending on who is sending the message (e.g., mail-gagging).  The return value should be an error or string if :receive_message is considered to have failed in some way.  Otherwise, a number should be returned --- this number is given to any :notify_mail routines that are called and is expected to either be 0 or the number assigned to the incoming message.
2

2
Note that :receive_message can do arbitrary things, including resending the same message to a new destination.  Hacking :receive_message to resend messages is different from using .mail_forward in the following respects
2

2
  o  the resent message is considered to be a distinct message having this object as its "author" --- i.e., the From: line will necessarily be different.
2
  o  since this "forwarding" is invisible to the mailsystem, there is no protection against loops and multiple copies.
35
5
4
35
2
Mail Transmission Format
2
1-----------------------
2

2
There is a standard message format used for transmitting messages.  This is the format that $mail_editor:make_message produces, and that :receive_message verbs on players and $mail_recipients expect to see.  The (currently experimental) @refile and @copy commands also use this format to transfer messages.
2

2
This *transmission* format is distinct from the *storage* format, though, for convenience this same format is often used as well for storing messages in player collections and ordinary $mail_recipient children though, in general, there is no requirement that this be the case.
2

2
A transmitted message is a list in the following form
2

2
   date (number),
2
     the time() value at the time the message was sent.
2
   from (string),
2
     the sending object (address list form)
2
     if this is not a player, an additional header will indicate the 
2
     current ownership of the object.
2
   to  (string),
2
     recipients (address list form) which can either be players 
2
     or $mail_recipient descendents.
2
   subject (string),
2
     subject of the message, or " " if there is no subject,
2
  @additional optional headers (list of strings),
2
     each header has the form "<header-name>: text" where <header-name>: 
2
     is padded out to a width of 10 columns for the convenience of 
2
     :display_message.  Currently "Reply-to: <address list>" is the only 
2
     additional header in use,
2
   "",
2
  @body of message (list of strings)
2

2
Note that the from, to and subject lines do *not* include a header name like "From:", "To:", or "Subject:".  The @'s indicate that the lists in question get spliced in (as usual), thus the entire message is a list whose first element is a number and the rest are strings.
2

2
The address lists that appear in the from and to lines is a string in the form a sequence of object ids, each enclosed in parentheses and preceded by optional text, e.g.,
2

2
  "*Core-DB-Issues (#8175), Rog (#4292), and Haakon (#2)"
2

2
The text is intended to give the current name of each object for the benefit of human readers, but is actually ignored by all header parsing routines.  The convention is that the text is either a player name or a * followed by a mailing list name.
35
5
4
51
2
Resolving mail forwarding and notification
2
1-----------------------------------------
2

2
For each recipient of a given mail message, the following two verbs are called to determine where the message should actually go and who should be notified about it:
2

2
:mail_forward
2
2------------
2

2
:mail_forward([from]) should return either
2

2
  o  a list of objects (either players or $mail_recipients) to which mail for this recipient will be redirected.
2
  o  a string error message to be printed to the player sending the message. If this recipient is one of the original destinations (i.e., not the result of a previous forwarding), no mail is actually sent.
2

2
If :mail_forward returns a nonempty list, the recipient itself will *not* actually receive the mail message unless it is included in the list.  #-1 is allowed to be on the list; it is ignored but does make the list nonempty.  Thus, having :mail_forward() return {#-1} is the canonical way to have arriving mail disappear without being kept or forwarded.
2

2
:mail_notify
2
2-----------
2

2
:mail_notify([from]) should return a list of objects that are to be told about any mail sent to this recipient (whether or not the recipient actually receives it). Said objects must have a :notify_mail verb, but other from that, there is no restriction on what these can be.
2

2
:notify_mail
2
2-----------
2

2
object:notify_mail is called with the arguments (sender,recipients,msgnumbers) where 
2

2
  o  recipients  == list of recipients including object in .mail_notify
2
  o  msgsnumbers == corresponding list of :receive_message return values (or 0 if :receive_message is not actually called, which will be the case if the recipient forwards without keeping)
2

2
When called as part of a mail send, the `from' argument is the immediate predecessor on the forwarding chain.  The default versions of these verbs return the values of .mail_forward and .mail_notify respectively (pronoun_subbing if the value is a string), unless this is a moderated mailing list and `from' is an unapproved sender (see `help MR-access') in which case the following verbs are called instead:
2

2
:moderator_forward
2
2-----------------
2

2
:moderator_forward(from) is what :mail_forward should return for mail coming from unapproved senders. This returns .moderator_forward (pronoun_subbed if a string) by default.
2

2
:moderator_notify
2
2----------------
2

2
:moderator_notify(from) is what :mail_notify should return for mail coming from unapproved senders.  This returns .moderator_notify (pronoun_subbed if a string) by default.
2

2
Since the :mail_forward verbs only see the previous sender in the forwarding chain, if, e.g, B is moderated but A can send to B (i.e., B:mail_forward(A) returns an actual list), then any mail sent to A goes to B even if the original sender isn't normally allowed to send to B directly.
2

2
These verbs should all allow `from' to be omitted in which case they should return as if `from' were a generic approved sender (e.g., wizard).
2

2
It should rarely be necessary to actually modify any of :*_forward/*_notify verbs, since one has a fair amount of control over their behavior via the following properties:
2

2
  o  .mail_forward
2
  o  .mail_notify
2
  o  .moderated          (see `help MR-access')
2
  o  .moderator_forward
2
  o  .moderator_notify
35
5
4
27
2
Sending Mail
2
1-----------
2

2
$mail_agent:send_message(from,recipients,headers,body)
2

2
  from:        sender of the message 
2
               (this must be you or something you own; otherwise => E_PERM)
2
  recipients:  object or list of objects (must all be players or 
2
               $mail_recipient descendants)
2
  headers:     either a string (contents of the Subject: line) 
2
               or a list {subject,replytos} replytos is a list 
2
               of objects designated to receive replies.
2
               Use {"",replytos} to have a Reply-to: without a Subject:
2

2
This is the canonical way to send a mail message from a program.
2

2
This calls $mail_agent:make_message to format the arguments into an actual message (see `help mail-format') and then $mail_agent:raw_send to do the actual sending which goes as follows:
2

2
  (1) Call :mail_forward on all recipients add any new recipients thus obtained to final recipient list, keep calling mail:forward on the new recipients until we obtain no additional recipients.  If one of the initial recipients is invalid, is not a player or $mail_recipient, or has its mail_forward return a string error, then we print the error message and abort at this point with no mail being sent.  If one of the later recipients bombs similarly, error messages are printed, but in this case mail still goes out to the other recipients.
2

2
  (2)  Call :mail_notify on all recipients encountered in stage (1) to get a list of objects to notify.
2

2
  (3) All final recipients receive the message (see `help receive-mail')
2

2
  (4) All notifications are delivered (using :notify_mail())
2

2
We return {0, @failed_recipients} if we bombed out at step 1. Otherwise return {1, @actual_rcpts} indicating what mail was sent.
35
5
4
15
2
Mail system
2
1==========
2

2
The following topics describe the guts of the LambdaCore mail system:
2

2
sending-mail -- how to send mail from a program; what happens.
2
mail-forwarding -- how to do mail forwarding/notification (the simple version)
2
mail-resolution -- how mail forwarding/notification works, in gory detail
2
receiving-mail -- what :receive_message should do
2
mail-format -- format of transmitted messages
2

2
mail-command-parsing   (TODO) -- routines for parsing mail commands
2

2
$mail_recipient -- generic non-player mail recipient
2
$mail_agent -- mail utility object
35
5
4
28
2
DATABASE OF PLAYERS
2
===================
2

2
This is an instance of the Generic Database ($generic_db) that holds the {name/alias,#objectid} pairs for every name and alias of every player in the MOO.
2

2
Verbs supplied include
2

2
  :find(string)        => player or $ambiguous_match or $failed_match
2
  :find_exact(string)  => player or $failed_match (does not do partial matches)
2
  :find_all(string)    => list of all matching players
2

2
  :insert(string,player) 
2
       records that string is now a name or alias of player
2
  :delete(string) 
2
       removes string from the db
2
  :available(string)
2
       returns 1 if string is available as a player name or alias,
2
       an object if string is in use, or 0 if string is otherwise unavailable.
2
  :load()
2
       resets the db, inserting all current player names and aliases.
2

2
The internal representation and all of the above verbs (except :load() and :available()) are as described for $generic_db.
2

2
It should be noted that for any application that involves resolving a player name from a command line, you should be using $string_utils:match_player() rather than $player_db:find(), since the former will deal correctly with other ways of referring to players apart from their names and aliases (e.g., literal object numbers, "me", "$no_one"...).
2

2
:load() needs to be done periodically as it is possible for the player db to get out of synch with reality.  In particular, there is currently no way to block someone writing his own player :recycle() verb that neglects to remove his names from the player db.
2

2
While a :load() is in progress the .frozen property is set to 1 to indicate that any results of :find*() are not to be trusted.
35
5
4
2
2
*index*
2
Core Utility Help Topics
35
5
4
68
2
Which :match...() verb do I call?
2
1--------------------------------
2

2
There are many situations where one wishes to obtain an object from a room or a player's .contents whose name/aliases matches a particular string.  There are four main verbs available for this and it is important to understand the distinctions between them and how they are supposed to be used.
2

2
LOC:match("X")
2
2=============
2

2
What you get looking for something that is inside LOC and named "X".  By default, this looks through LOC.contents to find a unique object having a name or alias that has "X" as a prefix.
2

2
Essentially, you can think of :match as a contents-matching verb, though, e.g., for rooms you also get matches on exits as well.
2

2
:match_object and :my_match_object
2
2=================================
2

2
   LOC:match_object("X", YOU)           [YOU defaults to player]
2
   YOU:my_match_object("X", LOC)        [LOC defaults to player.location]
2

2
What YOU get being located at LOC and looking for something named "X". By default these both return $string_utils:match_object("X",LOC,YOU).
2

2
$string_utils:match_object
2
2=========================
2

2
$string_utils:match_object("X", LOC, YOU)
2

2
What you *would* get *if* YOU were a typical player, YOU were inside LOC, YOU were looking for something named "X", *and* LOC were a typical place.
2

2
In other words, $string_utils:match_object describes the :match_object() algorithm for "typical places" and the :my_match_object for "typical players":
2

2
  o  check for "X" being one of "", "me", "here", "$something", or "#n"
2
  o  try YOU:match("X") i.e., something in your inventory (maybe)
2
  o  try LOC:match("X") i.e., some object in the room (maybe)
2

2
The distinction between these location:match_object and player:my_match_object has to do with whether the player or the location should determine what the matching algorithm is.  Which one you should use depends on the command that you are writing.  If you are writing a command with a virtual-reality flavor, then you should be respecting the room owner's idea of which objects you can "see" and thus the command should be calling the location's :match_object verb.  If you are writing a building/programming command where it is appropriate for the player to determine the matching algorithm  --- whether because the current location is irrelevant, not to be trusted, or both --- then the player's :my_match_object verb should be called.
2

2
Examples
2
3=======
2

2
`look diamond in box'
2

2
calls box:match("diamond").  This is a match on the contents of box.
2

2
`take ball'
2

2
calls player.location:match_object("ball") to determine which "ball" to take.  Note that if the room is dark, we might not be able to find any "ball".
2

2
`@program widget:foo'
2

2
calls player:my_match_object("widget") to get the player's own idea of what "widget" should be.  Note that if I were carrying something named "widget" and expecting to be programming a :foo() verb on it, it would be potentially disastrous should the room where I am decide for me to be programming something else (not even necessarily called "widget").
2

2
Object matching failures
2
2-----------------------
2

2
As with other matching routines, one gets back 
2

2
  o  $failed_match in the case of no matching object
2
  o  $ambiguous_match in the case of more than one matching object
2
  o  $nothing in the case of a blank string argument
2

2
or an object-id.  In these first 3 cases, one usually wants to translate these nonresults to the player; this is what $command_utils:object_match_failed.  The standard idiom to mimic what the builtin parser does, say, with the direct object is
2

2
  dobj = foo:match_???(dobjstr);
2
  if($command_utils:object_match_failed(dobj, dobjstr))
2
    "...give up.  nothing to do.   error message has already printed...";
2
  else
2
    "...dobj is something useful.  Continue...";
2
    ...
2
  endif
35
5
4
27
2
..is a powerless player.  He owns no objects, not even himself; nor does he own any verbs.  He is, however, a programmer and thus may use eval().  In fact his sole purpose is to evaluate questionable code.
2

2
`Questionable' could be in either or both of the following senses
2

2
(1) Its origin is sufficiently uncertain so that there is no obvious way of deciding whose permissions it should run under.
2

2
(2) The code itself is potentially malicious, i.e., to the extent that one does not want to be evaluating it using one's own permissions.
2

2
    set_task_perms($no_one);
2

2
is thus the canonical idiom in wizard code for rendering anything that follows mostly harmless.  For use by ordinary programmers, we have:
2

2
    $no_one:eval(string)
2

2
which attempts to evaluate an arbitrary string using $no_one's permissions.  string is either an expression or ";" followed by one or more statements, of which the final semicolon may be omitted.  return values are what eval() would return (either {1,value} or {0,@error_messages}).
2

2
Similarly, we have
2

2
    $no_one:eval_d(string)
2

2
which attempts to evaluate the specified string, but does it without the debug flag turned on (so that, for example, you'll get an error as opposed to terminating by traceback).
2

2
And, as a helpful utility for calling verbs whose behavior may be unpredictable, there is
2

2
    $no_one:call_verb(object, verb name, args)
2

2
which calls the specified verb with $no_one's permissions.
35
5
4
17
2
Exits
2
-----
2
:move(object)  - moves the object via this exit
2
:invoke()      - equivalent to :move(player)
2

2
When an exit is invoked on a particular object (via exit:move(object)), the following occurs.
2

2
(1) The exit may be locked against the object, in which case we print the 
2
   nogo messages and quit.
2

2
(2) (room=exit.dest):bless_for_entry(object) is called.  Assuming that exit is recognized by room as being a legitimate entrance (i.e., is in room.entrances), this will enable room:accept(object) to return true.
2

2
(3) object:moveto(room) is called and the various messages (see `help exit-messages') are :announced/:told.  Note that this, in accordance with the way the builtin move() (and hence the default :moveto()) works, we get a call to room:accept(object) which checks for the room itself being locked against the object, and otherwise returns true if the blessing in the previous step worked.  The move is performed, here:exitfunc(object) and room:enterfunc(object) are called.  In particular, room:enterfunc clears the blessing bestowed in (2) now that it is no longer needed.
2

2
In general, the move may fail, in which case we :announce the (o)nogo_msgs.
2

2
Transparent exits (the default on this MOO) can have extra substitutions in their description and messages; the new substitutions let you put information from the exit's destination into the exit's messages. %c is replaced with a list of the contents of the destination. %w is a list of the players in the destination. %l is the description (looks) of the destination. These substitutions don't conflict with the standard pronoun substitutions; you can still use them in messages. Example: @describe my_exit as "You see %w standing in the other room.".
35
5
4
73
2
The generic room ($room)
2
1-----------------------
2

2
Announcements
2
2------------
2

2
:announce         (@text)         => broadcasts to all except player
2
:announce_all     (@text)         => broadcasts to all
2
:announce_all_but (objects,@text) => broadcasts to all except those in objects
2

2
say, emote
2

2

2
Command recovery
2
2---------------
2

2
:huh            (verb,args) - server hook: last chance to make sense of verb
2
:here_huh       (verb,args) - room's last attempt to parse something
2
:here_explain_syntax (this,verb,args) - attempts to explain usage of verb
2

2

2
Residency
2
2--------
2

2
free_home  - true => @sethome allows anyone to set his .home to be here
2
residents  - objects on this list may teleport in and/or set their homes here.
2

2
:accept_for_abode(player) 
2
            => true iff player should be allowed to set .home to this room.
2

2
@resident*s
2

2
Looking
2
2------
2

2
dark  - true => contents are not visible
2
ctype - 1..4 for four different styles of .contents lists
2

2
:match         (string)        => exit or object in room's .contents
2
:tell_contents (objects,ctype) - format objects according to ctype, tell player
2

2
l*ook
2

2

2
Topology and movement via exits
2
2------------------------------
2

2
See `help $exit' for an explanation of how the generic $exit works.
2

2
free_entry     - true  => `teleporting' in is allowed
2
                  false => only residents may teleport in
2
exits          - list of invokable exits leading from this room
2
entrances      - list of recognized exits leading to this room
2
blessed_object - object currently entering via an exit
2
blessed_task   - task_id for entering object
2

2
:match_exit      (string) => exit whose name matches string
2
:bless_for_entry (object) - set up room to accept object arriving from entrance
2
:add_exit        (exit)
2
:add_entrance    (exit)
2
:remove_exit     (exit)
2
:remove_entrance (exit)
2

2
e/east/w/west/s/south/n/north/ne/northeast/nw/northwest/se/southeast/sw/southwest/u/up/d/down, go, @add-exit, @add-entrance, @remove-exit, @remove-entrance, @exits, @entrances 
2

2

2
Ejection
2
2-------
2

2
victim_ejection_msg/oejection_msg/ejection_msg
2
:*_msg()  messages
2

2
@eject
35
5
4
2
2
*forward*
2
$generic_help
35
5
4
129
2
This holds a collection of {string-key, datum} pairs, where datum can be anything.  At most one datum may be associated with any given string.  Data may be anything (lists, strings, numbers, objectids).  If you like, you can think of this as an array indexed by strings.
2

2
Lookup Verbs
2
------------
2
  :find(string)          => datum, .ambiguous or .failed
2
  :find_key(string)      => full string key,  .ambiguous or .failed
2
  :find_exact(string)    => datum or .failed (no partial matches)
2

2
  :find_all(string)         => list of all data corresponding to matching keys
2
  :find_all_keys(string)    => list of all matching keys
2
  :find_all_entries(string) => list of all matching {key, datum} pairs
2

2
  :find_all(string,vname)   
2
       does the general find_all operation, i.e.,
2

2
         result == {}. 
2
         for every {key, datum} matching string
2
           result = :("accum_"+vname)(result, key, datum)
2
         endfor
2
         return result;
2

2
       $generic_db already defines :accum_data, :accum_keys, :accum_entries
2
       in order that
2

2
       :find_all(string,"keys")    == :find_all_keys(string)
2
       :find_all(string,"data")    == :find_all(string)          [*]
2
       :find_all(string,"entries") == :find_all_entries(string)  [*]
2

2
       [*] These don't hold if .no_data == 1.
2

2
Modification Verbs
2
------------------
2
  :insert(string,datum)
2
       if the string is already present in the db, 
2
       changes the associated datum and returns {old_datum};
2
       otherwise enters a new {string,datum} pair and return 0.
2
  :delete(string)
2
       if there is a datum associated with string, 
2
       remove this association and return {datum}; otherwise return 0.
2
  :delete2(string,datum)
2
       if the given datum is associated with string, 
2
       removes that association and return {datum}, 
2
       if some other datum is associated with string, just return {other datum}
2
       otherwise return 0.
2
  :clearall([nodata_flag[, key_case_flag]])
2
       removes all associations from the database.
2
       optional arguments change 
2
       the .no_data flag (0 is normal, 1 see below) and/or
2
       the .key_case flag (0 is normal, 1 for case-sensitive keys)
2

2
Statistics Commands
2
-------------------
2
  count [entries|chars] in this
2
        provide some vague statistics about how big this thing is.
2

2
Settable Properties
2
-------------------
2
  .failed     ($failed_match)    return value for :find(_key)
2
  .ambiguous  ($ambiguous_match) return value for :find(_key)
2
  .node_info  ({$generic_db.owner, ""})  info for creating new properties
2

2
Access
2
------
2
By default anyone may do lookups (:find*), while modifications (:insert/delete(2)/clearall) may only be done by the object owner or verbs located on the object itself.  You may install your own versions of
2

2
  :prohibit_lookup()
2
  :prohibit_modify()
2

2
to change this.  The :prohibit_* verbs are expected to return true iff the operation in question is not to be allowed.
2

2
As entries get made, properties will be created on the db object itself, each having info as specified by .node_info.   By default the properties created are unreadable, but you can set .node_info to {$generic_db.owner, "r"} to have these properties be readable.  Changing the ownership portion of .node_info or setting the c flag is not recommended.
2

2
key_case
2
--------------
2
This property is normally 0 indicating that key matching is case-insensitive.  If you want case-sensitive key matching, specify .key_case == 1.  Note, however, that .key_case can only be changed by clearing all entries using :clearall.
2

2
no_data == 1
2
-------------
2
One may also use $generic_db to store strings without associated data (i.e., only the presence or absence of the string has any significance).  .no_data can only be changed by clearing all entries using :clearall.  With .no_data == 1, :find is equivalent to :find_key, and both :find_all (with no accum_ verb specified) and :find_all_entries are equivalent to :find_all_keys.
2

2
last_modify
2
------------
2
This property is set to a fresh value every time a modification operation actually changes the contents of the db.  This is intended for use in :accum_ and other verbs that might suspend, in order that it be possible to detect that the db has been modified out from under you.  Note that once you've determined this, it's best to simply raise an error; resuming a :find_all operation after a modification can lead to unpredictable results.
2

2
Implementation Notes
2
--------------------
2
The internal representation is as a `trie', a tree in which each internal node corresponds to a prefix shared by two or more strings in the db.
2
Each internal node is kept in a property named " "+<prefix>, where <prefix> is a prefix shared by all strings in the subtree under this node.
2
The property value is a 4 element list
2

2
this.(" "+<prefix>)[1] = <common>
2
   maximal continuation shared by all strings beginning with prefix
2
   i.e., all these names actually begin with <prefix>+<common>
2

2
this.(" "+<prefix>)[2] = <continuations>
2
   string of all characters <c> that can follow <prefix>+<common> for which
2
   there is more than one string in the db beginning with <prefix>+<common>+<c>
2

2
this.(" "+<prefix>)[3] = <exact_matches>
2
   list of all strings in this subtree for which 
2
   the character (or lack thereof) following the <prefix>+<common> substring 
2
   suffices to determine the string.  Note that these strings are not
2
   guaranteed to be in any particular order.
2

2
this.(" "+<prefix>)[4] = <data>
2
   list of data corresponding to the strings in [3].
2

2
Child nodes are       this.(" "+<prefix>+<common>+<c>) 
2
       for all <c> in this.(" "+<prefix>)[2].
2
The root node is this.(" ").
2
If, e.g., there are 2 or more strings in the db beginning with a, 
2
there will be a node this.(" a").  
2
If all of these strings actually begin with "ani", then this.(" a")[1]=="ni".
2
The db consisting of the 5 correspondences
2

2
  {"animal", #1}
2
  {"anime",  #2}
2
  {"anil",   #3}
2
  {"anile",  #4}
2
  {"banal",  #5}
2

2
would be represented
2

2
this.(" ")    =={"",  "a",  {"banal"},         {#5}}
2
this.(" a")   =={"ni","lm", {},                {}}
2
this.(" anim")=={"",  "",   {"animal","anime"},{#1,#2}}
2
this.(" anil")=={"",  "",   {"anil","anile"},  {#3,#4}}
2

2
In some cases one may merely wish to hold a collection of strings without trying to associate a particular datum with each string.  One may then instead set up a db without the fourth field on each of the properties.  In this case the datum is taken to be the found string itself and that is what gets returned by :find*() in the event of a successful search.   :find and :find_key are then equivalent as are :find_all and :find_all_keys.  To setup the db this way, do a :clearall(1).  :clearall(0) reverts to the above described type of db with a separately kept datum.  Note that you can't change the type without emptying the db.
35
5
4
197
2
The Generic Editor enables a player to edit a list of strings.  While one might contrive to use it directly, it is rather intended as a parent for some actual editor.  It supplies the following commands:
2

2
say         <text>                      w*hat       
2
emote       <text>                      abort       
2
lis*t       [<range>] [nonum]           q*uit,done,pause 
2
ins*ert     [<ins>] ["<text>]           
2
n*ext,p*rev [n] ["<text>]               
2
del*ete     [<range>]                   
2
f*ind       /<str>[/[c][<range>]]       
2
s*ubst      /<str1>/<str2>[/[g][c][<range>]]
2
m*ove,c*opy [<range>] to <ins>          
2
join*l      [<range>]                   
2
fill        [<range>] [@<col>]          
2

2
$editor_help.(cmdname) descrbes cmdname
2
$editor_help.insert    descrbes insertion points (<ins>)
2
$editor_help.ranges    descrbes range specifications (<range>)
2

2
You'll notice that nowhere does it say how to load in a given list of strings or how and where one may save said list away when one is done editing.  These commands are supplied by the child editor object.  The generic editor contains only the code for editing lines, though it defines additional functions for use by its children:
2

2
  :loaded(player)
2
     returns the index (player in this.active) iff text has been loaded
2
     from somewhere, otherwise returns 0.
2

2
Note that, by default, there is a difference between 
2

2
        having nothing loaded                (:text(who)==0) and 
2
        having loaded something with no text (:text(who)=={}).
2

2
If you don't care about this distinction in a particular case, just do (player in this.active) instead of this:loaded(player).  If you don't want your editor to make this distinction at all, do
2

2
        @stateprop texts={} for <youreditor>
2
     which changes the initial value of :text() to {} 
2

2
In all functions below, 'who' is the index returned by :loaded(player) 
2

2
BTW, be careful about using 'player' in non-user (i.e., +x this-none-this) verbs --- much better to have the user verb get the index with :loaded() and then pass that around.  For your non-user verbs, we have
2

2
  :ok(who)
2
     returns E_PERM if the caller is not an editor verb and E_RANGE
2
     if 'who' does not point to a valid session.
2

2
which should take care of the more egregious security holes (but maybe not the less egregious ones).
2

2
For getting and loading text, we have
2

2
  :text(who)    
2
     the current text string list or 0 if nothing loaded yet.
2
  :load(who,text)
2
     loads the given list of strings as the text to be edited.
2
     this also resets the 'changed' flag and pushes the insertion 
2
     point to the end.
2

2
and various flags and properties (all of the set_* routines return E_PERM when not called from an editor verb, E_RANGE if who is out of bounds, E_INVARG if something is wrong with the 2nd arg, or the new value, which may not necessarily be the same as the 2nd arg (e.g., set_insertion(..,37) on a 5 line text buffer returns 6).
2

2
  :changed(who)
2
     has the text been altered since the last save/load?
2
     (the child editor gets to define what "save" means).
2
  :set_changed(who,value)
2
     Any child editor command that is considered to save the text should do a 
2
     :set_changed(who,0).  
2
     Note that if the changed flag is 0, the session will be flushed when 
2
     the player leaves the editor, so you may also want certain commands to
2
     do set_changed(who,1)...
2

2
  :origin(who)
2
     room where the player came from.  
2
  :set_origin(who,room)
2
     can be used to change the room the player will return to when finished
2
     editing.  Since origin gets set even in cases where the player teleports
2
     into the editor you probably won't usually need to do this.
2

2
  :insertion(who)
2
     current insertion point.
2
  :set_insertion(who,linenumber)
2
     linenumber needs to be a positive integer and will get 
2

2
  :readable(who)
2
     whether the current editing session has been made globally readable.
2
  :set_readable(who,boolean)
2
     change the readability of the current editing session.
2
     This is used by the publish/perish verbs.
2

2
We also provide
2

2
  :invoke(...)
2
      If the player has a previous unsaved (i.e., :changed()!=0)
2
      session, we return to it, moving the player to the editor.  
2
      If the player is already in the editor, this has no effect other
2
      than to print a few nasty messages.  In any case a :changed()
2
      session must be aborted or set_changed(,0) before anything else 
2
      can be started
2

2
      Otherwise, we pass the arguments (which are assumed to be the
2
      result of some munging of the command line) to :parse_invoke(),
2
      move the player to the editor and load whatever parse_invoke()
2
      specified.  The only interpretation the generic editor makes on
2
      the arguments is that if the boolean value of the first is true,
2
      this indicates that the player wanted to load something as
2
      opposed to resume a previous session.  Usually a command calling
2
      :invoke will have a true (i.e., nonzero number, nonempty list or
2
      string) first arg iff the command line is nonempty, in which case 
2
      'args' works fine for this purpose.
2

2
      If the command parses sucessfully (:parse_invoke() returns a list),
2
      we move the player to the editor if necessary and then call 
2
      :init_session() to set things up.
2

2
The child editor is assumed to provide
2

2
  :parse_invoke(...)
2
     given :invoke()'s arguments, determines what the player wants to edit.
2
     It either returns 0 and reports syntax errors to player,
2
     or it returns some list that :init_session() will understand.
2

2
  :init_session(who,@spec)
2
     where spec is something that was returned by :parse_invoke().
2
     Loads the text and sets the stateprops (below) to indicate that 
2
     we are working on whatever it is we're suppose to be working on.
2

2
  :working_on(who)   
2
     returns a string X as in "You are working on X."
2
     This is called by the 'w*hat' command, among other things.
2

2
Child editors may have their own properties giving state information for the various editing sessions.  The value of each such property will be a list giving a value for each player in the editor.  For each such property, you should, once the editor object has been created, do one of
2

2
    @stateprop <propname>                 for <editor>
2
    @stateprop <propname>=<default-value> for <editor>
2
               (0 is the default <default-value>)
2

2
Henceforth, adding and deleting new editing sessions will amend the list held by the given property.
2

2
The value of the property for a given session can be obtained via this.<propname>[player in this.active] and can be changed with a corresponding indexed property assignment.  The usual idiom for an editor command is
2

2
   if(!(who=this:loaded(player)))
2
     player:tell(nothing_loaded_msg());
2
   else
2
      ... various references to  this.<propname>[who] ...
2
   endif
2

2
To remove such a property from the list of such state properties:
2

2
    @rmstateprop <propname> from <editor>
2

2
Note that you can only do this with properties defined on the child editor itself.  Using @stateprop or @rmstateprop also has the effect of flushing any editing sessions in progress and booting anyone currently in the editor.
2

2
Incidentally, the @flush command may be used at any time to clean out the
2
editor or to remove all sessions older than a given date.
2

2
There are also numerous _msg properties that may be customized
2

2
    @depart          announced at the origin when :invoke() is called. 
2
    @return          announced at the origin the player is returned there.
2
    @nothing_loaded  printed when user attempts editing 
2
                     before anything has been loaded.
2
    @no_text         response to 'list' when :text()=={}
2
    @no_change       printed by 'what' when :changed()==0
2
    @change          printed by 'what' when :changed()==1
2
    @no_littering    printed upon leaving the editor with :changed()==1.
2
    @previous_session  printed by :invoke() when player tries to start a 
2
                     new session without aborting or saving the old one
2

2
The general procedure for creating a child editor:
2

2
. @create #5400 named <editor>
2

2
. define additional <editor> verbs/properties
2
    At the very least you need 'edit' and 'save' commands.
2
    Usually you can get away with just having 'edit' call :invoke();
2
    Presumably, you'll need at least a command to load text from somewhere
2
    as well as a command to save it back out.
2

2
. define a verb (somewhere) to invoke the editor 
2
    This could be just a one-liner that calls <editor>:invoke(args,verb).
2
    Either that or
2
      .  you have to set up an exit somewhere whose destination is <editor>
2
      .  you have to advertise the object number so that people can 
2
         teleport to it.
2
  
2
. @stateprop x for <editor>
2

2
. if you want the 'abort' command to boot the player from the editor do
2
    <editor>.exit_on_abort = 1;
2

2
. set <editor>.commands to be the list of additional commands defined
2
    by <editor>.  
2
    Each element of the list is itself a list of the form {name,args}.
2
  set <editor>.commands2 to be the list of commands that should appear
2
    in the `look' listing, and should be a list of strings appearing 
2
    as names in .commands on either <editor> or some editor ancestor.
2
  look at $verb_editor or $note_editor for an example.
2

2
. If you want to have help text for new verbs you define, create a child of 
2
    $generic_help and add properties to this object for each of the topics 
2
    that you want to provide help text.
2
    Finally, set <editor>.help = {this object} so that the help system
2
    knows to consult this object.
35
5
4
118
2
The Help System
2
1--------------
2

2
When a player types help, the following list of objects is consulted for .help properties:  the player itself, all ancestors of player up to and including $player, and, if the current location is a room, the current location together with all ancestors of the current location back to and including $room.  Each help property should have as value either an object or a list of objects (otherwise we just ignore it).  These objects are then strung together as a list of "help databases" to be searched in order for the requested topic.
2

2
A help database (in the sense of anything that is usable by $player:help()) is any object having the following verbs:
2

2
  :find_topics([string])
2
     where string is a supposed help topic, returns a list of strings,
2
     i.e., actual help topics that this db knows about, or some boolean 
2
     false value in the event that this db is clueless...
2
     If no arguments are given, this should return a list of all topics
2
     in the db.  However, in the case that such a list can't (reasonably)
2
     be constructed, it might just return {} or another subset of all
2
     available topics.
2

2
  :get_topic(string,dblist)
2
     given one of the strings returned by :find_topics this either
2
     returns a list of strings (text to be spewed to the player) or
2
     returns 1 to indicate that it has already taken care of printing
2
     information to the player.
2
     The second argument is the list of all help dbs that are being 
2
     consulted.
2

2
  :topic_text(string)
2
     like get_topic, but instead returns the raw contents of a help topic
2

2
  :set_topic_text(string, text)
2
     sets the contents of a help topic
2

2
  :dump_topic(string)
2
     like topic_text, but instead returns a script that can be used to
2
     upload the text.  this is somewhat obsolete now, as it doesn't need
2
     to be customized on any help databases but instead always uses
2
     :set_topic-text.
2

2
  :has_topic(string)
2
     returns true if and only if this db has a topic matching that name
2
     exactly
2

2
  :add_topic(string)
2
     returns true if the topic was added
2

2
  :delete_topic(string)
2
     likewise with deletion
2

2
If :find_topic reports that a particular db knows about a given topic it returns the full topic name, so that :get_topic may be called on it later.
2

2
The remaining verbs are used by maintainers (see $prog:@gethelp) to edit help topics.
2

2
$generic_help and $help
2
2----------------------
2

2
The Generic Help Database, $generic_help, is the parent class of a particular kind of help database of which $help is an instance.  On help databases of this type, every help topic has a corresponding property, interpreted as follows:
2

2
  this.(topic) = string             
2
      one-line help text.
2

2
  this.(topic) = {"*<verb>*",@args}
2
      call this:<verb>(args,dblist) to get text; dblist is the full list of 
2
      help objects that are being consulted (as passed in by :get_topic).
2

2
  this.(topic) = other list of strings 
2
      multi-line help text--see `help help-format' for more information
2

2
For the {"*<verb>*",...} form, the current verbs available are
2

2
  {"*forward*", topic, @rest}   
2
     - get help text for topic and then append the lines of `rest'.  
2
       rest may, in turn, begin with a "*<verb>*"...
2

2
  {"*pass*", topic, @rest}   
2
     - get help text for topic from the first help database after this one
2
       that actually has help text for topic, and then append lines of `rest'.
2
       As with "*forward*" rest may, in turn, begin with a "*<verb>*"...
2

2
  {"*unpass*", topic, @rest}
2
     - get help text for topic from the first help database
2
       that actually has help text for topic, and then append lines of `rest'.
2
       This is identical to *pass* except that we begin the search of help
2
       dbs from the beginning of the list.  Care should be exercised since
2
       this can cause loops...
2

2
  {"*subst*", @lines} 
2
     - All occurences of %[exp] in lines are replaced with the value of exp
2
         which is assumed to evaluate to a string.  
2
       All lines beginning with %;exp are replaced with the value of exp 
2
         which is assumed to evaluate to a list of strings.
2
       Evaluation is done using $no_one's permissions so exp in either case
2
       can only refer to public information.
2

2
  {"*index*", title}
2
     - returns a list of all topics in this database, arranged in columns.
2
       title is used as a heading for this index.
2

2
  {"*objectdoc*", object}
2
     - gets the documentation for the given object (i.e., object:help_msg())
2
       N.B. as with all other *verb* arguments, object must be a string.
2

2
  {"*verbdoc*", objec
2
       N.B. as with all other *verb* arguments, object must be a string.
2

2
  {"*verbdoc*", object, verbname}
2
     - gets the documentation for the named verb on the given object
2
       (i.e., any strings at the beginnine documentation for the named verb on the given object
2
       (i.e., any strings at the beginning of said verbcode)
2

2
Individual help dbs are free to define additional verbs that may be used in this context.  $help itself defines the following additional such verbs:
2

2
  {"*index_list*"}
2
     - returns a list of all index topics in all databases in the search list.
2
       An index topic is one whose actual text is {"*index*", something}.
2
       When creating a help db, you should be sure to make an index topic.
2

2
  {"*full_index*"}
2
     - prints indices for all help databases in the search list.
2

2
It should be noted (once again) that help databases need not be children of $generic_help, so long as they have :find_topics/:get_topic/:dump_topic working as specified above.
35
5
4
65
2
Generic Option Package
2
----------------------
2
If one has a command or set of commands for which one wishes to provide several options/flags that a player can set to customize how the command behaves for him.  Making each option a separate property is a bit expensive, especially when the option in question is merely be a boolean flag (possibly being set to false in most cases).  This package provides an alternative, as well as providing a uniform set of commands for setting these flags/options and checking that the values given are of appropriate types.
2

2
Instead of needing several properties, only one is required to store a list containing values for all of the options.  An "option package" (pkg, below) is then an object of this class, which provides routines for manipulating such lists.
2

2
The set of option names is divided into a set of "real" options, those whose names will actually appear in a given list, and "extras" which are either synonyms for or represent combinations of real options.
2

2
 pkg:add_name(name)      adds name to .names  (remove it from .extras if there)
2
 pkg:add_name(name,1)    adds name to .extras (remove it from .names if there)
2
    => 1 - ok, 0 - already added, E_INVARG - illegal name, E_PERM
2

2
 pkg:remove_name(name)   remove name from either .names or .extras
2
    => 1 - ok, 0 - not present, E_PERM
2

2
For setting or retrieving values we have
2

2
 pkg:get(options,name) 
2
    => value (or 0 if name isn't a real option)
2
 pkg:set(options,name,value)
2
    => revised options (or string error message if something goes wrong)
2

2
By default, a given option can only be a boolean flag, having one of the values 0 (absent from the list), or 1 (present in the list).  :set translates 0/""/{} to 0 and any other non-object value to 1.
2

2
One may however designate a wider range of possible values for an option "foo" by either installing one of
2

2
  pkg.type_foo
2
    -- list of allowed types, 
2
       e.g., {NUM,STR}   => must be a number or a string
2
       e.g., {OBJ,{OBJ}} => must be an object or a list of objects
2
    for anything fancier use:
2

2
  pkg:check_foo(value)
2
    => string error message or {value munged as desired}
2

2
In general, the only restriction on option values is that 0 is the only false value; setting an option to "" or {} sets it to 0.  Every option defaults to 0, and no matter what you install as .type_foo or :check_foo(), 0 will always be a legal value for option "foo".
2

2
When presented with an option that is in .extras, :set will typecheck the value as described, however, then :actual(name, value) will be called to obtain a list of {name-of-real-option, value} pairs indicating which combination of real options should be set.
2

2
Other verbs
2
  pkg:parse(args,...)
2
    parses the command line arguments of a @whatever_option command
2
    => {optionname, value}  if the player wants to set an option
2
    => {optionname}         if the player wants to view an option
2
    => string error message  otherwise
2

2
  one may install pkg:parse_foo to parse arguments for option "foo" 
2
    !foo     => {"foo",0}  (:parse_foo not called)
2
    foo=     => {"foo",0}  (:parse_foo not called)
2
    -foo     => {"foo",0}  (:parse_foo not called)
2
    +foo     => pkg:parse_foo("foo",1)
2
    foo=word => pkg:parse_foo("foo","word")
2
    foo word1 word2    => pkg:parse_foo("foo",{"word1","word2"})
2
    foo is word1 word2 => pkg:parse_foo("foo",{"word1","word2"})
2

2
  If you install a .type_foo or a :check_foo verb,
2
  you need to also provide :parse_foo.
2

2
 pkg:show(options,name|list of names)
2
    => list of strings describing the current value of the named option(s).
2
       calls     pkg:show_foo(options,list of names) or
2
       refers to pkg.show_foo
2
       to describe option "foo"
2

2
(see sources for details...  at some point I'll finish writing this...)
35
5
4
25
2
Message Sequences
2
1----------------
2

2
A "message sequence" is a handle by which one may refer to a particular subset of a mail recipient's (player or $mail_recipient-descendant) saved messages.  Routines like rcpt:display_seq_headers or rcpt:display_seq_full need to be supplied with message-sequence arguments to deterimine which headers or full-messages to display.
2

2
Message sequences can in turn be obtained from routines like rcpt:parse_message_seq, which takes a command-line description of a message sequence on that particular recipient and returns the corresponding message sequence handle.
2

2
The actual form of a message sequence (though you shouldn't actually need to make use of this) is that of a set of integers in the format used by $seq_utils (see `help $seq_utils').  It should however be noted that these integers are *not* themselves message numbers, but rather indices into the list of saved messages.  For example, if a particular recipient holds 5 messages numbered 1,3,5,7,9.  Then the message sequence handle representing messages 3,5,7 collectively, would be {2,5} which is $seq_utils-ese for the range 2..4, namely the second, third and fourth messages saved on that recipient.
2

2
The following verbs are available for obtaining indices to use in message sequences
2

2
  :length_all_msgs()    => total number of messages, or equivalently,
2
                        => index of last message
2
  :length_num_le(n)     => number of messages numbered <= n, or equivalently,
2
                        => index of highest numbered message <= n
2
  :exists_num_eq(n)     => 0 unless there exists a message numbered n in which
2
                           case we return the index of that message.
2
  :length_date_le(date) => number of messages dated <= date, or equivalently,
2
                        => index of most recent message dated <= date
2
 
2
  :length_date_gt(date) => number of messages dated > date
2

2
Note that r:length_date_gt(date) == r:length_all_msgs()-r:length_date_le(date).
2
The only reason :length_date_gt is provided as a separate routine is in order 
2
to do quick checks for the existence of new mail (as @rn needs to do).
35
5
4
26
2
Read verbs
2
1---------
2

2
The following verbs may be used to extract headers/messages from readable mail recipients/players;
2

2
:display_seq_headers (message sequence, current message number, last_read_date)
2

2
Does a @mail listing of the given message sequence.  If current message number is given and the sequence includes it, we mark it with a `>'.  Likewise if the sequence includes any new messages (i.e., dated after last_read_date), these are also indicated as such.
2

2
display_seq_full (message sequence, preamble)
2

2
Does a @read listing of the given message sequence.  Each message is preceded by preamble.
2

2
=> {new current message number, new last_read_date}
2

2
:messages_in_seq (index)
2
  => {n, msg}
2

2
:messages_in_seq (message sequence)
2
  => {{n_1,msg_1},{n_2,msg_2},...}
2

2
where the n_i are message numbers and the msg_i are messages in transmission format (see `help mail-format')
2

2
:list_rmm ()
2

2
Does an `@unrmm list' listing of messages in .messages_going
35
5
4
18
2
Write verbs
2
1----------
2

2
The following verbs can be used to manipulate writable mail recipients/players:
2

2
:rm_message_seq (message sequence)
2
  Does an @rmmail.  Messages in message sequence are removed from this 
2
  recipient's saved .messages and written to .messages_going.
2

2
:undo_rmm ()
2
  Does an @unrmm.  Messages in .messages_going are copied back to .messages.
2

2
:expunge_rmm ()
2
  Does an @unrmm expunge.  Blows away .messages_going.
2

2
:renumber ()
2
  Does a @renumber.
2

35
5
4
26
2
Search verbs
2
1-----------
2

2
The following verbs can be used on a readable mail-recipient/player to search for messages with fields matching a given pattern.
2

2
from_msg_seq (objectid or list [,mask])
2
  => message sequence: messages from (one of) the given objectid(s)
2

2
%from_msg_seq (string or list [,mask])
2
  => message sequence: messages with (one of) the given string(s)
2
     in the From: line
2

2
to_msg_seq (objectid or list [,mask])
2
  => message sequence: messages to (one of) the given objectid(s)
2

2
%to_msg_seq (string or list [,mask])
2
  => message sequence: messages with (one of) the given string(s)
2
     in the To: line
2

2
subject_msg_seq (string [,mask])
2
  => message sequence: messages with given string occurring in Subject:
2

2
body_msg_seq (string [,mask])
2
  => message sequence: messages with given string occurring in body of message
2

2
In all cases `mask' is a message sequence which one may supply to limit the range of the search.  One way of looking at it is that the message sequence to be returned is first intersected with mask.
35
5
4
13
2
An object for storing verbs and properties dealing with the english language.
2

2
Properties:
2

2
vowels                     {"a", "e", "i", "o", "u"}
2

2
[non]vowel_exceptions      A list of prefixes which start with vowels [consonants] but don't take 'an' ['a'] as indefinite articles.
2

2
Verbs:
2

2
@add-[non]vowel-exc*eption <exception> to #116
2
    Add an exception to the 'an' before vowel ['a' before consonant] rule.
2
    Sends mail to *English if the player is not a wizard.
35
5
4
43
2
The housekeeper is an object that can help keep other objects where they belong.  New MOOs may want to add their own user interface for the housekeeper; here is some information that may be helpful.
2

2
To indicate what objects should be cleaned:
2

2
  :add_cleanup(object[, requestor[, where]])
2
    Ask the housekeeper to clean 'object' for 'requestor' to 'where'.
2
    Requestor defaults to 'player'.
2
    Where defaults to object.location.
2

2
  :remove_cleanup(what[, requestor])
2
    Remove 'what' from the cleanup list at 'requestor's request.
2
    Will remove it only if 'requestor' made the original request and owns
2
    the object or the destination.
2

2
To actually get the housekeeper to clean stuff up:
2

2
  :cleanup([insist])
2
    Clean up player's objects.  Argument is 'up' or 'up!' for manually
2
    requested cleanups.  'up!' means to clean things even if it's against
2
    the housekeeper's better judgement.
2

2
  :replace(object[, insist])
2
    Clean up the indicated object.  'insist' is as in :cleanup.
2

2
  :continuous()
2
    Starts the housekeeper cleaning continuously, killing any previous
2
    continuous task.  This should be called only when starting up a new MOO,
2
    or if something has gone wrong, as normally it will just keep going
2
    without any help.
2

2
  :litterbug()
2
    Clean up all the places in housekeeper.public_places by getting rid of
2
    all contents not in their .residents lists.  This is called by
2
    :continuous, so it doesn't need to be called directly.
2

2
To find out what's being cleaned to where for whom:
2

2
  :cleanup_list([whom])
2
    Show 'player' the personal cleanup list for 'whom', or the housekeeper's
2
    complete list if no argument is given.
2

2
  :clean_status()
2
    Show 'player' a brief summary of eir personal cleanup list.
35
1
4
2
2
*forward*
2
$mail_recipient
35
5
4
0
35
1
5
35
4
4
1
2
Core Utility Help
35
5
2
Help database for LambdaCore utility objects and generics.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
81961
0
913134427
200
1
#22
Programmer Help

16
199
-1
-1
-1
30
-1
21
3
errors
35
173
-1
prepositions
35
173
-1
nominate_for_core
199
173
-1
80
@check-property
@check-chparent
@egrep
regular-expressions
@show
@grep
prog-index
help
prepositions
;
utilities
truth
tasks
statements
programming
precedence
language
functions
expressions
eval
errors
@verb
@setenv
@rmverb
@rmproperty
@prospectus
@property
@program
@list
@kill
@kids
@forked
@display
@dbsize
@copy
@chparent
@chmod
@args
.program
@clearproperty
@disown
@disinherit
@displayoptions
@display-options
@add-feature
@remove-feature
features
examine
integration
events
valid()
@prop
@addfeature
@d
mpl
listen()
unlisten()
integrate
exam
(aliases_extra)
 aliases
#
named-args
named-arguments
named
@eval-options
fork()
return
while()
for
@copy-x
@list#
@program#
@rmverb#
@args#
@rename#
@addalias#
@rmalias#
@list-options
@listoptions
95
4
2
2
*forward*
2
@check-chparent
199
5
4
10
2
Syntax:  @check-property <object>.<propname>
2
         @check-chparent <object> to <newparent>
2

2
You cannot add a new property to an object if an ancestor or a descendant already defines a property with the same name.  @check-property will give you the list of all descendants of <object> that that define .<propname>.  
2

2
Likewise you cannot chparent an object to a new parent if the new parent has a property that is also defined on the object or some descendant.  Use @check-chparent to find out all instances of conflicting properties that would interfere with @chparent in this manner.
2

2
Note that @check-property requires either that you own the object or that it be writeable, the same conditions that need to hold if you are to create new properties on the object.  Similarly, @check-chparent requires that you own the object and that the new parent is either readable or likewise owned by you.
2

2
For objects with large numbers of descendants, these commands can be time-consuming.
199
5
4
2
2
*forward*
2
@grep
199
5
4
162
2
Regular expression matching allows you to test whether a string fits into a specific syntactic shape.  You can also search a string for a substring that fits a pattern.
2

2
A regular expression describes a set of strings.  The simplest case is one that describes a particular string; for example, the string `foo' when regarded as a regular expression matches `foo' and nothing else.  Nontrivial regular expressions use certain special constructs so that they can match more than one string.  For example, the regular expression `foo%|bar' matches either the string `foo' or the string `bar'; the regular expression `c[ad]*r' matches any of the strings `cr', `car', `cdr', `caar', `cadddar' and all other such strings with any number of `a''s and `d''s.
2

2
Regular expressions have a syntax in which a few characters are special constructs and the rest are "ordinary".  An ordinary character is a simple regular expression that matches that character and nothing else.  The special characters are `$', `^', `.', `*', `+', `?', `[', `]' and `%'.  Any other character appearing in a regular expression is ordinary, unless a `%' precedes it.
2

2
For example, `f' is not a special character, so it is ordinary, and therefore `f' is a regular expression that matches the string `f' and no other string.  (It does *not*, for example, match the string `ff'.)  Likewise, `o' is a regular expression that matches only `o'.
2

2
Any two regular expressions A and B can be concatenated.  The result is a regular expression which matches a string if A matches some amount of the beginning of that string and B matches the rest of the string.
2

2
As a simple example, we can concatenate the regular expressions `f' and `o' to get the regular expression `fo', which matches only the string `fo'.  Still trivial.
2

2
The following are the characters and character sequences that have special meaning within regular expressions.  Any character not mentioned here is not special; it stands for exactly itself for the purposes of searching and matching.
2

2
`.'  is a special character that matches any single character.  Using
2
     concatenation, we can make regular expressions like `a.b', which matches
2
     any three-character string that begins with `a' and ends with `b'.
2

2
`*'  is not a construct by itself; it is a suffix that means that the preceding
2
     regular expression is to be repeated as many times as possible.  In `fo*',
2
     the `*' applies to the `o', so `fo*' matches `f' followed by any number of
2
     `o''s.
2

2
     The case of zero `o''s is allowed: `fo*' does match `f'.
2

2
     `*' always applies to the *smallest* possible preceding expression.  Thus,
2
     `fo*' has a repeating `o', not a repeating `fo'.
2

2
     The matcher processes a `*' construct by matching, immediately, as many
2
     repetitions as can be found.  Then it continues with the rest of the
2
     pattern.  If that fails, it backtracks, discarding some of the matches of
2
     the `*''d construct in case that makes it possible to match the rest of
2
     the pattern.  For example, matching `c[ad]*ar' against the string
2
     `caddaar', the `[ad]*' first matches `addaa', but this does not allow the
2
     next `a' in the pattern to match.  So the last of the matches of `[ad]' is
2
     undone and the following `a' is tried again.  Now it succeeds.
2

2
`+'  is like `*' except that at least one match for the preceding pattern is
2
     required for `+'.  Thus, `c[ad]+r' does not match `cr' but does match
2
     anything else that `c[ad]*r' would match.
2

2
`?'  is like `*' except that it allows either zero or one match for the
2
     preceding pattern.  Thus, `c[ad]?r' matches `cr' or `car' or `cdr', and
2
     nothing else.
2

2
`[ ... ]'
2
     `[' begins a "character set", which is terminated by a `]'.  In the
2
     simplest case, the characters between the two brackets form the set.
2
     Thus, `[ad]' matches either `a' or `d', and `[ad]*' matches any string of
2
     `a''s and `d''s (including the empty string), from which it follows that
2
     `c[ad]*r' matches `car', etc.
2

2
     Character ranges can also be included in a character set, by writing two
2
     characters with a `-' between them.  Thus, `[a-z]' matches any lower-case
2
     letter.  Ranges may be intermixed freely with individual characters, as in
2
     `[a-z$%.]', which matches any lower case letter or `$', `%' or period.
2

2
     Note that the usual special characters are not special any more inside a
2
     character set.  A completely different set of special characters exists
2
     inside character sets: `]', `-' and `^'.
2

2
     To include a `]' in a character set, you must make it the first character.
2
     For example, `[]a]' matches `]' or `a'.  To include a `-', you must use it
2
     in a context where it cannot possibly indicate a range: that is, as the
2
     first character, or immediately after a range.
2

2
`[^ ... ]'
2
     `[^' begins a "complement character set", which matches any character
2
     except the ones specified.  Thus, `[^a-z0-9A-Z]' matches all characters
2
     *except* letters and digits.
2

2
     `^' is not special in a character set unless it is the first character.
2
     The character following the `^' is treated as if it were first (it may be
2
     a `-' or a `]').
2

2
`^'  is a special character that matches the empty string -- but only if at the
2
     beginning of the string being matched.  Otherwise it fails to match
2
     anything.  Thus, `^foo' matches a `foo' which occurs at the beginning of
2
     the string.
2

2
`$'  is similar to `^' but matches only at the *end* of the string.  Thus,
2
     `xx*$' matches a string of one or more `x''s at the end of the string.
2

2
`%'  has two functions: it quotes the above special characters (including `%'),
2
     and it introduces additional special constructs.
2

2
     Because `%' quotes special characters, `%$' is a regular expression that
2
     matches only `$', and `%[' is a regular expression that matches only `[',
2
     and so on.
2

2
     For the most part, `%' followed by any character matches only that
2
     character.  However, there are several exceptions: characters that, when
2
     preceded by `%', are special constructs.  Such characters are always
2
     ordinary when encountered on their own.
2

2
     No new special characters will ever be defined.  All extensions to the
2
     regular expression syntax are made by defining new two-character
2
     constructs that begin with `%'.
2

2
`%|' specifies an alternative.  Two regular expressions A and B with `%|' in
2
     between form an expression that matches anything that either A or B will
2
     match.
2

2
     Thus, `foo%|bar' matches either `foo' or `bar' but no other string.
2

2
     `%|' applies to the largest possible surrounding expressions.  Only a
2
     surrounding `%( ... %)' grouping can limit the grouping power of `%|'.
2

2
     Full backtracking capability exists for when multiple `%|''s are used.
2

2
`%( ... %)'
2
     is a grouping construct that serves three purposes:
2

2
       1. To enclose a set of `%|' alternatives for other operations.  Thus,
2
          `%(foo%|bar%)x' matches either `foox' or `barx'.
2

2
       2. To enclose a complicated expression for a following `*', `+', or `?'
2
          to operate on.  Thus, `ba%(na%)*' matches `bananana', etc., with any
2
          number of `na''s, including none.
2

2
       3. To mark a matched substring for future reference.
2

2
     This last application is not a consequence of the idea of a parenthetical
2
     grouping; it is a separate feature that happens to be assigned as a second
2
     meaning to the same `%( ... %)' construct because there is no conflict in
2
     practice between the two meanings.  Here is an explanation of this
2
     feature:
2

2
`%DIGIT'
2
     After the end of a `%( ... %)' construct, the matcher remembers the
2
     beginning and end of the text matched by that construct.  Then, later on
2
     in the regular expression, you can use `%' followed by DIGIT to mean
2
     "match the same text matched by the DIGIT'th `%( ... %)' construct in the
2
     pattern."  The `%( ... %)' constructs are numbered in the order that their
2
     `%(''s appear in the pattern.
2

2
     The strings matching the first nine `%( ... %)' constructs appearing in a
2
     regular expression are assigned numbers 1 through 9 in order of their
2
     beginnings.  `%1' through `%9' may be used to refer to the text matched by
2
     the corresponding `%( ... %)' construct.
2

2
     For example, `%(.*%)%1' matches any string that is composed of two
2
     identical halves.  The `%(.*%)' matches the first half, which may be
2
     anything, but the `%1' that follows must match the same exact text.
2

2
`%b' matches the empty string, but only if it is at the beginning or end of a
2
     word.  Thus, `%bfoo%b' matches any occurrence of `foo' as a separate word.
2
     `%bball%(s%|%)%b' matches `ball' or `balls' as a separate word.
2

2
     For the purposes of this construct and the five that follow, a word is
2
     defined to be a sequence of letters and/or digits.
2

2
`%B' matches the empty string, provided it is *not* at the beginning or end of
2
     a word.
2

2
`%<' matches the empty string, but only if it is at the beginning of a word.
2

2
`%>' matches the empty string, but only if it is at the end of a word.
2

2
`%w' matches any word-constituent character (i.e., any letter or digit).
2

2
`%W' matches any character that is not a word constituent.
199
5
4
7
2
Syntax:  @show <object>
2
         @show <object>.<prop-name>
2
         @show <object>:<verb-name>
2

2
Displays quite detailed information about an object, property or verb, including its name, owner, permission bits, etc.  The information displayed for an object can be quite long.
2

2
See also @display, which displays different information and is controlled differently.
199
5
4
11
2
Syntax:  @grep <string> in <object>
2
         @grep <string> in {<objectlist>}
2

2
         @egrep <regexp> in <object>
2
         @egrep <regexp> in {<objectlist>}
2

2
These are named for the corresponding unix utilities.
2

2
@grep searches the given object(s) for verbs whose verbcode contains the given string as a substring of one of its lines.
2

2
@egrep searches the given object(s) for verbs whose verbcode contains a substring matching the given regular expression (see `help regular-expressions').
199
5
4
2
2
*index*
2
Programmer Help Topics
199
5
4
14
2
*pass*
2
help
2
For programmers, the help system provides the following additional forms:
2

2
  help object:verbname   -- prints any documentation strings that are present
2
                            at the beginning of the program for that verb.
2
  help $<whatever>_utils -- prints general information about one of the 
2
                            $..._utils objects (e.g., $string_utils, 
2
                            $list_utils, etc...), which are all libraries 
2
                            of generally used verbs.
2
  help builtin()         -- prints documentation from the programmers manual
2
                            about the named primitive, for example length()
2

2
For information about how the help system itself works and about how to associate local help databases with specific rooms or player classes, see `help $help'.
199
5
4
3
2
*prepositions*
2
The complete list of prepositions recognized by the command-line parser:
2

199
5
4
2
2
*forward*
2
eval
35
1
4
17
2
The core database has a number of objects serving as libraries of useful verbs.
2
More detailed information can be obtained for (some of) these, via `help $whatever_utils'
2

2
$building_utils -- 
2
$code_utils     -- parsing and manipulating verb code
2
$command_utils  -- reporting matching errors to the player
2
$gender_utils   -- managing gendered objects
2
$list_utils     -- list manipulation
2
$set_utils      -- set manipulation
2
$lock_utils     -- key expression manipulation
2
$match_utils    -- 
2
$object_utils   -- object information 
2
                  (inheritance/location hierarchy, verb/property lists)
2
$perm_utils     -- permissions
2
$string_utils   -- string manipulation
2
$time_utils     -- time (numeric and verbal) manipulation
2
$trig_utils     -- trigonometric and other numerical utilities
199
5
4
5
2
Several kinds of statements, expressions, and functions in the MOO programming language use a notion that some MOO values are 'true' and others 'false'.
2

2
The only values that are considered true are non-zero numbers, non-empty strings, and non-empty lists.
2

2
All other values (i.e., 0, "", {}, objects, and errors) are considered false.
35
1
4
18
2
A task is an execution of a MOO program.  There are five ways for tasks to be created in LambdaMOO:
2

2
   o Every time a player types a command, a task is created to execute that command; we call these 'command tasks'.
2
   o Whenever a player connects or disconnects from the MOO, the server starts a task to do whatever processing is necessary, such as printing out 'Munchkin has connected' to all of the players in the same room; these are called 'server tasks'.
2
   o The FORK statement in the programming language creates a task whose execution is delayed for at least some given number of seconds; these are 'forked tasks'.
2
   o The suspend() function suspends the execution of the current task.  A snapshot is taken of whole state of the execution, and the execution will be resumed later.  These are called `suspended tasks'.
2
   o The read() function also suspends the execution of the current task, in this case waiting for the player to type a line of input.  When the line is received, the task resumes with the read() function returning the input line as result.  These are called `reading tasks'.
2

2
The last three kinds of tasks above are collectively known as `queued tasks' or `waiting tasks', since they may not run immediately.
2

2
To prevent a maliciously- or incorrectly-written MOO program from running forever and monopolizing the server, limits are placed on the running time of every task.  One limit is that no task is allowed to run longer than 15 seconds; this limit is, in practice, never reached.  The reason is that there is a second limit on the number of operations a task may execute.
2

2
The server counts down 'ticks' as any task executes.  Roughly speaking, it counts one tick for every expression evaluation (other than variables and literals), one for every `if', `fork' or `return' statement, and one for every iteration of a loop.  If the count gets all the way down to zero, the task is immediately and unceremoniously aborted.  All tasks begin or resume with an store of 30,000 ticks; this is enough for almost all normal uses.
2

2
Because queued tasks may exist for long periods of time before they begin execution, there are commands to list the ones that you own and to kill them before they execute.  These commands are covered in the following help topics:
2

2
@forked -- listing the forked tasks that you own
2
@kill -- killing a particular forked task
35
1
4
68
2
The following kinds of statements exist in the MOO programming language:
2

2
Null
2
====
2

2
        ;
2

2
The null statement does nothing.
2

2
Expressions
2
===========
2

2
        expression ;
2

2
The expression statement evaluates the expression and then discards the value.
2

2
Conditional
2
===========
2

2
        IF ( expression ) statements ENDIF
2
        IF ( expression ) statements ELSE statements ENDIF
2
        IF ( expression )
2
          statements
2
        ELSEIF ( expression )
2
          statements
2
        ...
2
        ELSE
2
          statements
2
        ENDIF
2

2
The conditional statement evaluates each expression in turn and executes the statements associated with the first one to return a true value; the ELSE statements are executed if none of the expressions returns a true value.  There can be any number of ELSEIF clauses and the ELSE part is optional.  See 'help truth' for the definition of 'true value'.
2

2
List iteration
2
==============
2

2
        FOR name IN ( expression ) statements ENDFOR
2

2
The list iteration statement first evaluates the expression, which must return a list.  It then executes the statements once for each element of that list, each time with the named variable having the value of the corresponding list element.
2

2
Numeric iteration
2
=================
2

2
        FOR name IN [ expression .. expression ] statements ENDFOR
2

2
The numeric iteration statement first evaluates the two expressions, both of which must return numbers; call those numbers N1 and N2, respectively.  The statements are then executed once for each integer I such that N1 <= I <= N2, in increasing order; each time, the named variable has the corresponding value of I.
2

2
Indefinite iteration
2
====================
2

2
        WHILE ( expression ) statements ENDWHILE
2

2
The indefinite iteration statement repeatedly evaluates the expression and, each time it returns a true value, executes the statements.  The loop stops the first time that the expression returns a false value.  The definitions of 'true' and 'false' values is in 'help truth'.
2

2
Return
2
======
2

2
        RETURN ;
2
        RETURN expression ;
2

2
The return statement evalautes the expression, if any, and returns the resulting value (or 0 if there is no expression) to the verb that called the current one.  Execution of the current verb is immediately terminated.
2

2
Fork
2
====
2

2
        FORK ( expression ) statements ENDFORK
2
        FORK name ( expression ) statements ENDFORK
2

2
The fork statement first executes the expression, which must return a number; call that number N.  It then creates a new MOO task that will, after at least N seconds, execute the statements.  When the new task begins, all variables will have the values they had at the time the FORK statement was executed.  The task executing the FORK statement immediately continues execution.  If a variable name is given after the FORK keyword, then it is assigned the 'queue ID' of the newly-created task.  The value of this variable is visible both to the task executing the fork statement and to the statements in the newly-created task.  See 'help tasks' for more information about forked tasks.
35
1
4
29
2
MOO contains a rich programming language for the creation of interesting rooms, exits, and other objects.  Help is available on the following topics concerning programming in MOO:
2

2
language -- a brief reference for the syntax and semantics of the MOO language
2
tasks -- a brief description of MOO tasks and their resource limits
2

2
@property -- adding a property to an object
2
@rmproperty -- removing a property from an object
2

2
@verb -- adding a verb to an object
2
@rmverb -- removing a verb from an object
2
@args -- changing the syntax of a verb
2
@copy -- copying a verb from one object to another
2

2
@program -- entering the program for a verb
2
@list -- printing a listing of the program for a verb
2
@edit -- editing verb code
2
eval -- executing MOO statements and expressions without writing a verb
2

2
@show -- looking at all the details of an object, a property, or a verb
2
@display -- a different way to look at those details
2

2
@parents -- listing the ancestors of an object
2
@kids -- listing the children of an object
2
@chparent -- changing the parent of an object
2

2
@contents -- listing the contents of an object
2
@chmod -- changing the permissions on an object, a property, or a verb
2
@rename -- changing the name of a verb or object
2
@currentobject -- specify an object to work with
35
1
4
17
2
The table below gives the relative precedence of all of the MOO operators; operators on higher lines in the table have higher precedence and those on the same line have identical precedence:
2

2
        !       - (without a left operand)
2
        *       /       %
2
        +-
2
        ==      !=      <       <=      >       >=      in
2
        &&      ||
2
        ... ? ... | ... (the conditional expression)
2
        =
2

2
Thus, the horrendous expression
2

2
        x = a < b && c > d + e * f ? w in y | - q - r
2

2
would be grouped as follows:
2

2
        x = (((a < b) && (c > (d + (e * f)))) ? (w in y) | ((- q) - r))
35
1
4
5
2
The MOO programming language is described in excruciating detail in the LambdaMOO Programmer's Manual, available for FTP from parcftp.xerox.com in the file pub/MOO/ProgrammersManual.txt.  The online help consists of a few quick reference guides here in the help system under the following topics:
2

2
statements -- the syntax and semantics of the various kinds of MOO statements
2
expressions -- the same for the various kinds of MOO expressions
2
functions -- a list of the primitive functions available to MOO programs
35
1
4
141
2
There are many, many built-in functions available to MOO programmers.  The following list gives a brief summary of the arguments and purpose of each function; for more information, see the LambdaMOO Programmer's Manual.  
2

2
pass(arg, ...)              -- call same verb defined on this object's parent
2
raise(code[, msg[, value]]) -- raise code as an error
2

2
typeof(value)      -- determine the data type of value
2
 tostr(value, ...) -- concatenate values into a string
2
 tonum(value)      -- convert non-list value into a integer
2
 toint(value)      == tonum(value)
2
 tofloat(value)    -- convert non-list value into a float
2
 toobj(value)      -- convert non-list value into an object
2
floatstr(float, precision, scientific?)
2
                   -- convert float value to string
2
encode_binary(value, ...)      -- values to binary string
2
decode_binary(bstring[, full]) -- binary string to list of values
2

2
binary_hash(bstring) -- MD5 hash value of binary string
2
string_hash(string)  -- MD5 hash value of string
2
value_hash(value)    == string_hash(toliteral(value))
2

2
value_bytes(value)   --- number of bytes required to store value
2
object_bytes(object) --- number of bytes required to store object
2

2
eval(string)       -- parse and execute string as MOO code
2
toliteral(value)   -- convert value to a string that evaluates to it
2
call_function(func[, arg, ...]) == func([arg, ...])
2

2
function_info([func]) -- builtin function desc (or list of all of them)
2

2
length(list/string) -- returns the length of a string or list
2
listappend(list, value [, index]) -- adding an element at the end of a list
2
listinsert(list, value [, index]) -- adding an element at the head of a list
2
   listset(list, value, index)    -- updating a list at some index
2
listdelete(list, index)           -- removing an element from a list
2
    setadd(list, element) -- adding an element to a set represented as a list
2
 setremove(list, element) -- removing an element from such a set
2
is_member(element, list) -- case-sensitive version of (element IN list)
2
equal(v1,v2)             -- case-sensitive version of (v1 == v2)
2

2
min(n1, n2, ...) -- minimum of n1,n2,...
2
max(n1, n2, ...) -- maximum of n1,n2,...
2
random(n)        -- random integer between 1 and n inclusive
2
 time()          -- current time in seconds since midnight GMT, 1 Jan 70
2
ctime([time])    -- time (or current time) converted to a human-readable string
2

2
  abs(n) -- absolute value of n
2
floor(x) -- truncate x towards minus infinity
2
trunc(x) -- truncate x towards zero
2
 ceil(x) -- truncate x towards plus infinity
2
 sqrt(n) -- square root of n
2
  exp(x) -- e to the x
2
  log(x) -- natural log
2
log10(x) -- base 10 log
2
 sin(a),  cos(a),  tan(a)     -- circular trignometric functions
2
asin(y), acos(x), atan(x[,y]) -- inverse circular trignometric functions
2
 sinh(a), cosh(a), tanh(a)    -- hyperbolic trigonometric functions
2

2
 index(str1, str2 [, case-matters]) -- index of first str2 in str1
2
rindex(str1, str2 [, case-matters]) -- index of last  str2 in str1
2
strcmp(str1, str2) -- case-sensitive string comparison
2
strsub(subject, what, with [, case-matters]) -- substitution in a string
2
crypt(string [, salt]) -- one-way string encryption
2
 match(str1, str2 [, case-matters]) -- match first pattern str2 in str1
2
rmatch(str1, str2 [, case-matters]) -- match last  pattern str2 in str1
2
substitute(template, subs) -- perform substitutions on template
2

2
   valid(object)             -- testing whether an object exists
2
  create(parent [, owner(*)])-- creating a new MOO object
2
 recycle(object)             -- destroying a MOO object
2
    move(object, where)      -- altering the object-containment hierarchy
2
chparent(object, new-parent) -- altering the object-inheritance hierarchy
2
  parent(object)             -- object's parent   in the inheritance hierarchy
2
children(object)             -- object's children in the inheritance hierarchy
2
max_object()       -- the highest-numbered object in the MOO
2
renumber(obj)      -- changes an object's number to lowest available one (*)
2
reset_max_object() -- resets max_object() to the largest valid object (*)
2

2
     properties(object) -- a list of the properties defined on an object
2
   add_property(object, prop-name, value, info) -- add a new property
2
delete_property(object, prop-name)              -- remove a property
2
    property_info(object, prop-name)       -- {owner, perms} info on a property
2
set_property_info(object, prop-name, info) -- setting same
2
is_clear_property(object, prop-name) -- find out if a property is "clear"
2
   clear_property(object, prop-name) -- make a property "clear"
2

2
      verbs(object) -- a list of the verbs defined on an object
2
   add_verb(object, info, args)  -- add a verb to an object
2
delete_verb(object, verb-name)   -- remove a verb from an object
2
    verb_info(object, verb-name) -- {owner, perms, names} info for a verb defn.
2
    verb_args(object, verb-name) -- {dobj, prep, iobj} argument info for a verb
2
    verb_code(object, verb-name [, fully-paren [, indent]]) -- program listing
2
set_verb_info(object, verb-name, {owner, perms, names})
2
set_verb_args(object, verb-name, {dobj, prep, iobj})   
2
set_verb_code(object, verb-name, {line, line, ...})
2
  disassemble(object, verb-name) -- moo bytecode for verb
2

2
notify(conn, string[, noflush])      -- output string to connection
2
read([conn])                         -- read input line from connection (*)
2
flush_input(conn[, showmsg?])        -- flush pending input on connection (*)
2
force_input(conn, line[, at-front?]) -- insert line as pending input (*)
2

2
        is_player(object) -- testing whether or not object is a player
2
          players()       -- a list of all players, active or not
2
connected_players()       -- a list of all currently-connected players
2
     idle_seconds(player) -- seconds since given player typed anything
2
connected_seconds(player) -- seconds given player has been logged in
2
    boot_player(player)        -- disconnect player from the MOO immediately(*)
2
set_player_flag(player, value) -- set/clear player bit; boot player if clear(*)
2
connection_name(player)   -- a server-assigned name for player's connection
2
open_network_connection(@args) -- open a connection to another network site
2
connection_option(conn,option)           -- setting of option for connection
2
connection_options(conn)                 -- list of {option, value}
2
set_connection_option(conn,option,value) -- change setting of option
2
output_delimiters(player)      -- {prefix,suffix} set by PREFIX/SUFFIX cmds
2
buffered_output_length([conn]) -- number of bytes currently buffered on output
2

2
caller_perms()         -- the player whose permissions your caller was using
2
set_task_perms(player) -- changing permissions of the running task (*)
2
callers([lineno?])  -- stack: list of {obj, verb, owner, vloc, player[,line]}
2
task_stack(taskid[,lineno?]) -- callers() stack for suspended task
2

2
seconds_left() -- number of seconds left in the current task
2
ticks_left()   -- number of ticks   left in the current task
2
task_id()              -- id number for the currently-running task
2
suspend(secs)          -- suspend current task for a number of seconds
2
resume(taskid[,value]) -- resume the specified task
2
kill_task(taskid)      -- delete one of your tasks from the queue
2
queued_tasks()         -- list of {taskid,start,,,owner,obj,verb,line,this}
2
queue_info([player])   -- number of tasks for player or list of tasking players
2

2
server_version()   -- a string of three numbers "major.minor.release"
2
server_log(string) -- add a comment to the server log file (*)
2
listen(obj, point[, msgs?]) -- start listening, return canonicalized point (*)
2
unlisten(canonpoint)        -- stop listening at canonpoint (*)
2
listeners() -- list of {object, canonpoint, msgs?} listening points (*)
2
shutdown(msg)    -- print msg and kill the server (*)
2
dump_database()  -- what it says (*)
2
db_disk_size()   -- size in bytes of database disk file
2
memory_usage()   -- {{blocksize, nused, nfree}, ...}, the server's memory stats
2

2
(*) => as you might have expected, these usually require wizard permissions.
35
1
4
120
2
The following kinds of expressions exist in the MOO programming language:
2

2
Literals
2
========
2

2
        number
2
        # number
2
        # - number
2
        "character string"
2
        error-name
2

2
Literal expressions return the obvious values: numbers, object numbers, strings, and errors.
2

2
List construction
2
=================
2

2
        { expression , expression , ... , expression }
2

2
The list-construction expression evaluates the each of the expressions in turn and returns a list whose elements are the results of those expressions.  Any of the expressions may be prefixed with an at-sign ('@'); in this case, that expression must return a list and, rather than that list becoming an element of the final list, its elements are spliced into the final list.
2

2
Variables
2
=========
2

2
        name
2

2
Variable expressions return the current value of the named variable.  Variable names must start with a letter or underscore ('_') and contain only letters, digits, and underscores.  The following variables are predefined:
2

2
            OBJ, STR, LIST, ERR, NUM
2
            player, caller, this, verb, args
2
            argstr, dobj, dobjstr, prepstr, iobj, iobjstr
2

2
Their initial values are described in detail in the LambdaMOO Programmer's Manual.
2

2
Property reading
2
================
2

2
        expression . name
2
        expression . ( expression )
2
        $ name
2

2
Property-reading expressions return the current value of a named property on the object that is the value of the first subexpression.  In the second form, the second subexpression must return a string, the name of the property to be read.  The third form is an abbreviation for '#0.name'.
2

2
Verb calls
2
==========
2

2
        expression : name ( arguments )
2
        expression : ( expression ) ( arguments )
2

2
Verb-call expressions invoke a named verb on the object that is the value of the first subexpression, passing the given arguments.  In the second form, the second subexpression must return a string, the name of the verb to invoke.  The syntax and semantics of arguments is exactly as in the list-construction expression but no initial or final curly-braces ('{' or '}') are used.
2

2
Primitive functions
2
===================
2

2
        function ( arguments )
2

2
The function-call expression invokes one of the MOO primitive functions, as listed in 'help functions', passing the given arguments.
2

2
List indexing
2
=============
2

2
        expression [ expression ]
2

2
The indexing expression first evaluates the two subexpressions; call their values S and N, respectively.  S must be a string or a list and N must be a number between 1 and the length of S, inclusive.  The Nth element of S is returned.  The elements of a string are themselves one-character strings.
2

2
        expression [ expression .. expression ]
2

2
The subsequence expression first evaluates the three subexpressions; call their values S, N1, and N2, respecitively.  S must be a string or a list and N1 and N2 must be numbers.  If N1 <= N2, then both must be between 1 and the length of S, inclusive; the subsequence of S beginning at index N1 and continuing through index N2 is returned.  If N1 > N2, the empty sequence of the same type as S is returned, either "" or {}.
2

2
Assignment
2
==========
2

2
        name = expression
2
        expression . name = expression
2
        expression . ( expression ) = expression
2
        $ name = expression
2

2
Assignment expressions give new values to variables and object properties.  For the second and third forms, the expressions on the left-hand side of the '=' are evaluated first.  Then the right-hand side expression is evaluated and result is stored in the indicated variable or object property.
2

2
Arithemetic
2
===========
2

2
        expression + expression
2
        expression - expression
2
        expression * expression
2
        expression / expression
2
        expression % expression
2
        - expression
2

2
The arithmetic expressions evaluate the subexpressions, all of which must return numbers, and then perform addition, subtraction, multiplication, division, remaindering, or negation, respectively.  For addition, the subexpressions may both return strings as well; in this case, the result is the concatenation of the two strings.
2

2
Comparisons
2
===========
2

2
        expression == expression
2
        expression != expression
2
        expression < expression
2
        expression <= expression
2
        expression > expression
2
        expression >= expression
2

2
The comparison expressions evaluate the subexpressions and then test whether or not the first result is equal to, unequal to, less than, less than or equal to, greater than, or greater than or equal to the second result, respectively.  If the indicated relation holds then they return 1 and otherwise they return 0.  Comparisons of strings are performed case-insensitively, those of lists are performed on an element-by-element basis, objects are compared by their object numbers, and errors by an ordering given in the LambdaMOO Programmer's Manual.
2

2
Logical expressions
2
===================
2

2
        expression ? expression | expression
2
        expression && expression
2
        expression || expression
2
        ! expression
2

2
The logical expressions each return results based upon the truth value of their first subexpression; call the value of this expression X.  The first of these returns the value of the second subexpression if X is a true value and that of the third expression if X is a false value; the unused subexpression is not evaluated.  The definitions of 'true value' and 'false value' are given in 'help truth'.  The expression 'E1 && E2' is an abbreviation for 'E1 ? E2 | E1' except that E1 is only evaluated once.  The expression 'E1 || E2' is an abbreviation for 'E1 ? E1 | E2' except that E1 is only evaluated once.  The expression '! E' is an abbreviation for 'E ? 0 | 1'.
2

2
List membership
2
===============
2

2
        expression IN expression
2

2
The list-membership expression first evaluates both subexpressions; call their values E and L, respectively.  L must be a list.  If E is an element of L, then the index of the first occurence of E in L is returned.  If E is not an element of L, then 0 is returned.
2

2
The method for disambiguating the meaning of a complex MOO expression in the absence of sufficient parentheses is described in 'help precedence'.
35
1
4
29
2
Syntax:  eval <MOO-code>
2
         ; <MOO-code>
2
         eval-d <MOO-code>
2
         eval+d <MOO-code>
2

2
Evaluates the given piece of MOO code and prints the resulting value.  If the MOO code begins with one of the MOO language keywords ('if', 'for', 'while', 'fork', or 'return') or with the character ';', then the entire piece of code is treated as the program for a verb, with ';' appended to the end.  Otherwise, 'return' is appended to the front and ';' is appended to the end and that string is treated as the code for a verb.  In either case, the resulting verb is invoked and whatever value it returns is printed.
2

2
For programmers, this is such a mind-bogglingly useful thing to do that there is a simple abbreviation for this command; any command beginning with a semicolon (';') is treated as a use of 'eval'.
2

2
Eval treats specially a duplicated semicolon at the beginning.  It enables you to make multi-statement programs within eval (but does not by default print the return value).
2

2
Eval-d (no ";" abbreviation for this) evaluates the following text exactly as eval, except that the "d" debug flag (see programmer's manual for explanation) is turned off.  Thus errors will cause an error return value rather than a traceback.  Eval+d evaluates code with the "d" flag on.  Both of these are useful for overriding `@eval-option no-debug'.
2

2
Examples:
2
   eval 3 + 4
2
   =>  7
2
   ;3+4
2
   =>  7
2
   ;for x in (player.aliases) player:tell(x); endfor
2
   Jay
2
   J
2
   Jay^2
2
   =>  0
2
   ;;l = {}; for i in [1..10] l = {@l, i}; endfor return l
2
   =>  {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
2
   eval-d 8 + "foo"
2
   => E_TYPE  (Type mismatch)
2

2
See `help @eval-options' for various ways you can customize the `eval' command.
35
1
4
3
2
*errors*
2
The complete list of error codes:
2

199
5
4
18
2
Syntax:  @verb <object>:<verb-name(s)>
2
         @verb <object>:<verb-name(s)> <dobj> [<prep> [<iobj>]]
2
         @verb <object>:<verb-name(s)> (<named args>)
2

2
Adds a new verb with the given name(s) to the named object.  If there are multiple names, they should be separated by spaces and all enclosed in quotes:
2

2
        @verb foo:"bar baz mum*ble"
2

2
The direct and indirect object specifiers (<dobj> and <iobj>) must be either 'none', 'this', or 'any'; their meaning is discussed in the LambdaMOO Programmer's Manual.  The preposition specifier (<prep>) must be either 'none', 'any', or one of the prepositional phrases listed in `help prepositions' (a prepositional phrase with more than one word must be enclosed in quotes ("")).  All three specifiers default to 'none'.
2

2
It is also possible to specify the new verb's permissions and owner as part of the same command (rather than having to issue separate @chmod/@chown commands)
2

2
     @verb <object>:<verb-name(s)> <dobj> <prep> <iobj> <permissions>
2
     @verb <object>:<verb-name(s)> <dobj> <prep> <iobj> <permissions> <owner>
2

2
<permissions> are as with @chmod, i.e., must be some subset of "rwxd".  They default to "rd" (specifying "w" for a verb is highly inadvisable).  The owner defaults to the player typing the command; only members of the Core Group can create verbs with owners other than themselves.
2

2
You may also use "tnt" in place of "this none this" for the dobj prep iobj arguments.  "this none this" is used to indicate non-command verbs, since the parser can't possibly interpret a command as "this none this".  For these verbs, the permissions default to "rxd"; the "x" bit is set so that they can be called from other programs.  (If they couldn't be used as commands, and they couldn't be called from programs, they wouldn't be good for anything!)
35
1
4
3
2
Syntax:  @setenv <environment string>
2

2
Defines the environment for eval (@eval-option env).  See `help eval' for more information.
35
1
4
9
2
Syntax:  @rmverb  <object>:<verb-name>
2
         @rmverb  <object>:<verb-name>  <dobj> <prep> <iobj>
2
         @rmverb# <object>:<verb-number>
2

2
Removes the named verb from the named object.
2

2
If there is more than one verb matching the given verb-name, this removes the most recently defined one. With the 2nd form of the command the verb removed is the most recent one matching both the given verb-name *and* the given dobj/prep/iobj specifiers.
2

2
The 3rd form is useful for removing verbs that are otherwise inaccessible by name (say, the 3rd verb on an object is named "*" and you want to remove the 4th verb).  verb-number> is the (1-based) number of the verb to be removed, i.e., `@rmverb# object:1' removes the first verb on `object', etc...
199
5
4
3
2
Syntax:  @rmproperty <object>.<prop-name>
2

2
Removes the named property from the named object.  '@rmproperty' may be abbreviated as '@rmprop'.
199
5
4
29
2
Usage:  @prospectus player [from number] [to number]
2

2
Like @audit, but displays more information.  The optional from and to arguments are for restricting the display to specific object numbers, if you happen to know the player only owns objects in the included range.
2

2
Example:
2

2
   @prospectus Frand
2
   Objects owned by Frand (from #0 to #31505):
2
     P      Frand (#47)
2
     T      Frand's trio of shoes (#152)
2
   KfT[ 10] Frand's notifier class (#391)
2
   KfT[ 11] Frand's generic game board (#775)
2
     T      Frand's mind bender (#894)
2
     C      polka-dot hole (#997)
2
     R      Hyperspace Hovel (#1002)
2
   ...
2

2
The K in the first column indicates that the object has children owned by other players.  A lowercase k indicates the object has children but owned only by the player.  The second column indicates whether the object is publicly readable or publicly parentable.  An r indicates readability.  A lowercase f indicates the object is both readable and allows children (is fertile).  An uppercase F indicates the object is not readable, yet allows children to be created.  (This is usually an error.)  If the object is readable by the issuer of the @prospectus command (that is, publicly readable or owned by the issuer), then the number in brackets indicates the number of verbs which have been defined on this object (not including any verbs from any parents).
2

2
The third column indicates what type of object this is.
2

2
        T       Thing
2
        E       Exit
2
        R       Room
2
        C       Container
2
        N       Note
2
        P       Player
2
        p       Parent object appropriate for players ("Player class")
2
        blank   Other
199
5
4
14
2
*subst*
2
Syntax:  @property <object>.<prop-name>
2
         @property <object>.<prop-name> <initial-value>
2

2
Adds a new property named <prop-name> to the named object.  The initial value is given by the second argument, if present; it defaults to 0.  
2

2
Normally, a property is created with permissions 'rc' and owned by whoever types the command.  However, you may also specify these explicitly
2

2
         @property <object>.<prop-name> <initial-value> <permissions>
2
         @property <object>.<prop-name> <initial-value> <permissions> <owner>
2

2
Only members of %[$db_group:dname()] can create properties with owners other than themselves.
2

2
'@property' can be abbreviated as '@prop'.
35
1
4
24
2
Syntax:  @program <object>:<verb-name>
2
         @program <object>:<verb-name> <dobj> <preposition> <iobj>
2
         @program <object>:<verb-name> (<named args>)
2
         @program# <object>:<verb-number>
2

2
Changes the MOO program associated with the named verb on the named object.
2

2
If you provide <dobj> <preposition> and <iobj> as in the second form of this command, then it is the first verb with matching direct object, preposition and indirect object specifiers that is the one getting the new program.  This is useful if you have several verbs matching the same name.
2

2
In the third form of the command, the verb changed will be the first `this none this' verb, provided its named arguments match those currently set for that verb.
2

2
In the fourth form of the command, the verb is specified by number; 1 programs the first verb, 2 programs the second verb, etc...  This is useful if you have inaccessibly-named verbs (e.g., the 3rd verb on an object is named "*" and you want to change the code on the 4th).
2

2
Typing the @program command always puts the server into a line-reading mode, in which each line you type is saved away without any action unless said line is one of the following:
2

2
  .
2
  @abort
2
  .<text>
2

2
A period on a line by itself ends the line-reading mode and continues with the command, in this case, the saved lines are considered as a program, checked for syntax errors and, if no errors are found, installed as the new program for the specified verb.  
2

2
@abort causes the command to terminate immediately with no change to any verb's program.  .<text> enters <text> literally as one of the lines to be saved, which is used for when, e.g., you want to enter the line `.' or the line `@abort'.
2

2
Note that this command *always* enters the line-reading mode, even if the indicated verb is not found.  In this case, lines of text are still read but they are ignored.  After any @program command, you always need to type a period or `@abort' to get back into the normal command-reading mode.
199
5
4
31
2
Usage:  @list <object>:<verb>
2
              [with|without parentheses|numbers]
2
              [<dobj> <prep> <iobj>] | [(<named args>)]
2
              [all]
2
              [ranges]
2
        @list# <object>:<verb-number>
2
              [with|without parentheses|numbers] [all] [ranges]
2

2
Prints out the code for the MOO program associated with the named verb on the named object.  
2

2
Normally, the code is shown with each line numbered and with only those parentheses that are necessary to show the meaning of the program.  You can e.g., specify `without numbers' to have the numbers omitted or `with parentheses' to include all parentheses or even `with parentheses without numbers' to do both.
2

2
Type `@list $room:@move' to see the code for the `@move' command, or even `@list $prog:@list' to see the code implementing @list itself...
2

2
Giving an args (dobj/prep/iobj) specification indicates that verbs whose verb_args do not matching that specification are to be ignored.
2

2
The `@list#' form of the command is available for specifying a verb by number, in case you have an object with inaccessible verbs (e.g,. say the 3rd verb is named "*" and you want to view the 4th).  For example
2

2
    @list# object:2
2

2
lists the second verb on `object'.
2

2
Specifying `all', indicates that all verbs on <object> or its ancestors matching the given name (and args specification, if given) should be listed.
2

2
One may also specify one or more ranges of the form <number>..<number> to indicate that only particular subranges of the lines of the verb code should be listed.
2

2
These may combined in any order, e.g.,
2

2
  @list frobule:burfle this in front of any without numbers all
2

2
which would be useful if `frobule' had more than one `burfle' verb and we are only interested in those that have `this' `in front of' `any' as its respective dobj/prep/iobj specifiers, and we want to see ALL such verbs on frobule or its ancestors.
35
1
4
24
2
Syntax:  @kill task_id
2
         @kill [object]:[verb]
2
         @kill soon [number-of-seconds]
2
         @kill all
2
         @kill %trailing_id
2

2

2
Immediately kills one or more forked tasks.  The '@forked' command is useful for finding out what tasks you have pending; see 'help @forked' for details.  Only the owner of a task may kill it.
2

2
@kill task_id kills only the task with that id.
2

2
@kill object:verb kills all tasks which were scheduled by the object running the verb named.  Both object and verb are optional:  @kill object: kills all tasks scheduled by that object, and @kill :verb kills all tasks which were scheduled by any object running that verb.  This can be useful if you have several similar objects which are running tasks from similarly named verbs.  (Perversely, @kill : kills all tasks...  Any object running any task.)
2

2
@kill soon kills all tasks scheduled within the next minute.  @kill soon number kills all tasks scheduled within that number of seconds, e.g. @kill soon 300 would kill all tasks scheduled within the next five minutes.  This can be useful if you have a runaway task you want to quickly remove, but don't want to kill you later tasks.
2

2
@kill all kills all tasks.  Like @kill soon, but more dramatic.
2

2
@kill %trailing_id expects you to specify the last few digits of a task id.  It then kills all tasks that end with those digits.
2

2
Example:
2
  @forked
2
  1359083655  Sep 16 21:45:00 1991  yduJ          #5803:heartbeat (10) [#68]
2
  @kill %655
2
  Killed:   task 1359083655, verb #5803:heartbeat, line 10, this==#68
35
1
4
8
2
Syntax:  @kids object
2

2
A quick way to find out the children of an object.  Prints out the names and object numbers of the found children.  Note: this is not a list of all descendents, just direct children.
2

2
Example:
2
  @kids #3107
2
  Generic Body of Chlorinated Water(#3107) has 3 kids.
2
  The Pool(#1428)   The Hot Tub(#388)   Deep Blue Underground Pool(#17340)
199
5
4
33
2
Syntax:  @forked
2

2
Gives a list of all of the forked tasks you own, along with detailed information about each one.  The information includes the following:
2

2
Queue ID
2
========
2

2
A numeric identifier for the task, for use in killing it (see 'help @kill').
2

2
Start Time
2
==========
2

2
The time after which the task will begin execution.
2

2
Owner
2
=====
2

2
The player whose permissions under which the task is running. Unless you are in the Core Group, @forked will show only your tasks.
2

2
Verb
2
====
2

2
The object and verb-name of the code that forked the task.
2

2
Line
2
====
2

2
The line number of the first statement that the task will execute when it starts.  Note that the code for the verb in question may have changed since the task was forked; the forked task will use the version that was being executed when it was forked.
2

2
This
2
====
2

2
The value of `this' for the forked task, in the case that it is different from (i.e., is a descendant of) the object on which the verb code lives.
35
1
4
42
2
Syntax: @display <object>.[property]
2
                         ,[inherited_property]
2
                         :[verb]
2
                         ;[inherited_verb]
2

2
@display is a fancy version of @show.  As @show, it can select individual verbs or properties to display.  In addition, it can display all the verbs or properties defined on an object, or all the verbs or properties defined on any of the object's ancestors.  Don't specify a property or verbname after the punctuation mark to get the "all" feature.  Its display is more compact than that of @show (it uses a one-line format, and truncates values that don't fit in the value field).
2

2
You may mix properties and verbs on the command line, but the parser may become confused.  (E.g. @display object,: displays all properties including inherited ones plus all locally defined verbs on the object.)
2

2
Examples:
2
Individual property:
2
  @display poolsweep.count
2
  .count                   yduJ (#68)            r c    8
2

2
Individual verb:
2
  @display poolsweep:tell
2
  #3560:tell                     yduJ (#68)           rxd    this none this
2

2
All properties, including one truncated value:
2
  @display poolsweep.
2
  poolsweep (#3560) [ readable ]
2
    Owned by yduJ (#68).
2
    Child of generic thing (#5).
2
    Location The Pool (#1428).
2
  .gagged                  yduJ (#68)            r c    0
2
  .count                   yduJ (#68)            r c    8
2
  .messages                yduJ (#68)            r c    {"The poolsweep stir..
2
  .index                   yduJ (#68)            r c    2
2
  .quantum                 yduJ (#68)            r c    20
2

2
Inherited verbs, edited for brevity, showing verbs from various parents, with owners, permissions, and argument lists.
2
  @d poolsweep;
2
  poolsweep (#3560) [ readable ]
2
   #3560:tell                     yduJ (#68)           rxd    this none this
2
   #3560:description              yduJ (#68)           rxd    this none this
2
      #5:"g*et t*ake"             Haakon (#2)          rxd    this none none
2
      #5:"d*rop th*row"           Haakon (#2)          rxd    this none none
2
      #5:moveto                   Haakon (#2)          rxd    this none this
2
      #1:description              Haakon (#2)          rxd    this none this
2
      #1:look_self                Haakon (#2)          rxd    this none this
2

2
Some aspects of @display can be customized (see `help @display-options').
199
5
4
3
2
Syntax:  @dbsize
2

2
@dbsize goes through the entire database, counting the valid and invalid objects, giving a summary at the end.  This information can be useful, but because this command is cpu intensive, it should be used sparingly.
199
5
4
15
2
Syntax:  @copy   <object>:<verb> to [<newobject>][:<newverb>]
2
         @copy-x <object>:<verb> to [<newobject>][:<newverb>]
2

2
Copies the code of the named verb to the new object and verbname.  Permissions, and arguments of the new verb are set to match those of the old verb in the event that the new verb does not already exist.  One of <newobject> or :<newverb> must be supplied.  If no new verbname is given, the old name is retained.  Likewise, <newobject> defaults to <object> if not given.
2

2
@copy-x is like @copy but causes any new verb to be created with its x flag reset.
2

2
Examples:
2
  @copy me:verbname to myobject
2
  @copy me:test_verb to myobject:real_verb
2
  @copy-x me:verbname to verbname(old)
2

2
In general, @copy'ing verbs is a bad idea.  In the vast majority of cases, the desired effect can be accomplished with parenting (i.e., having <object> be an ancestor of <newobject>), which has the advantage that if a verb is updated or fixed, this immediately becomes available to child objects that inherit this verb.  In such a case, copies that were made using @copy have to be tracked down and fixed by hand.
2

2
This facility is provided for those rare occasions where one has no choice but to actually copy the verb.  @copy-x is useful for making unusable backup copies of verbcode.
199
5
4
7
2
Syntax:  @chparent <object> to <new parent>
2

2
Changes the parent of the named object to be the named parent.  The object acquires all the verb and property definitions of its parent.  Newly acquired properties are initilialized with `clear' values so that they inherit whatever values are currently assigned to the parent's corresponding properties (see `help @clearproperty').
2

2
If the player does not own <new parent>, it must have been set `fertile'.  <object> must be owned by the player.  Neither <object> nor any descendant can define any property which already exist on <new parent>.  Use @check-chparent (see `help @check-chparent') to list such property conflicts.
2

2
It is also sometimes the case that you will own some object and want to @chparent some child of that object that you do not own.  Use @disinherit (see `help @disinherit') in such situations.
199
5
4
14
2
Syntax:  @chmod <object> <object-permissions>
2
         @chmod <object>.<prop-name> <property-permissions>
2
         @chmod <object>:<verb-name> <verb-permissions>
2
         @chmod# <object>:<verb-number> <verb-permissions>
2

2
Changes the permissions of an object, property or verb, to those given.  The following table shows what permission bits are allowed for each form of the command:
2

2
        <object-permissions>        r, w, f
2
        <property-permissions>      r, w, c
2
        <verb-permissions>          r, w, x, d
2

2
See the LambdaMOO Programmer's Manual for their meanings.
2

2
To clear all of the permissions for an object, verb, or property, use "" as the second argument.
199
5
4
6
2
Syntax:  @args  <object>:<verb-name> <dobj> [<prep> [<iobj>]]
2
         @args  <object>:<verb-name> (<named args>)
2
         @args# <object>:<verb-number> <dobj> [<prep> [<iobj>]]
2
         @args# <object>:<verb-number> (<named args>)
2

2
Changes the direct object, preposition, and/or indirect object specifiers for the named verb on the named object.  Any specifiers not provided on the command line are not changed.  The direct and indirect object specifiers (<dobj> and <iobj>) must be either 'none', 'this', or 'any'.  The preposition specifier (<prep>) must be either 'none', 'any', or one of the prepositional phrases listed in `help prepositions'.
35
1
4
13
2
Syntax:  .program <object>:<verb-name>
2
              :
2
              :
2
              <lines of MOO code>
2
              :
2
              :
2
         .
2

2
Provides or changes the MOO program associated with the named verb on the named object.
2

2
This command is mostly obsolete.  Use @program instead.  The only reason this command still exists is that it is a server builtin command that will continue to work in the (unlikely) event that @program gets trashed ...
2

2
This command works differently from most other MOO commands, in that it actually changes how the server will interpret later lines that you type to it.  After typing the '.program' line, you are in 'programming mode'.  All lines that you type in this mode are simply saved away in the server until you type a line containing only a single period ('.').  At that point, those lines are interpreted as a MOO program and are checked for syntax errors.  If none are found, a message to that effect is printed and the code you typed is installed as the program for the verb in question.  In any case, after typing the '.' line, you are returned to the normal input-handling mode.
35
1
4
32
2
Syntax:   @clearproperty <object>.<prop-name>
2

2
This clears <object>'s <prop-name> property.  That is the property value becomes `clear' and all further references to this property will use the value of the same property on the parent object.  Note that you can only clear inherited properties.  Nor is this the same as removing a property; the property continues to exist.
2

2
`@clearproperty' can be abbreviated as `@clearp'.
2

2
Example:
2

2
  @create #1 named foo
2
  You now have foo with object number #42 and parent Root Class (#1).
2
    [foo, as a child of #1 has a .description property which starts out clear]
2
  ;#1.description
2
  => ""
2
  ;#1.description = "You see nothing special"
2
  => "You see nothing special"
2
  ;#42.description  
2
  => "You see nothing special"
2
  ;#42.description = "Something special"
2
  => "Something special"
2
   [foo.description is now no longer clear; it has a value of its own]
2
  ;#1.description = "Boring"
2
  => "Boring"
2
  ;#42.description  
2
  => "Something special"
2
   
2
  @clearp foo.description
2
  Property #42.description cleared; value is now "Boring".
2
   [foo.description is now clear again]
2
  ;#1.description = ""
2
  => ""
2
  ;#42.description
2
  => ""
199
5
4
2
2
*forward*
2
@disinherit
199
5
4
12
2
Syntax:   @disinherit <object> 
2
          @disinherit <object> [from <parent>]
2

2
Synonym:  @disown
2

2
This command is used to remove an unwanted child from an object you own.  If you owned said child, you could use @chparent; this command is to cover the other case, namely where you don't own the child.  
2

2
Both forms of this command chparent <object> to its grandparent, provided you own the parent.  The second form matches the string you supply for <object> against the list of children of the given <parent>.
2

2
Turning off the fertile bit (.f) for a particular object prevents others from creating children of it or chparenting to it (see `help @chmod').
2

2
Note also that, though the name might seem to indicate otherwise, this command does not change the ownership of any object.
199
5
4
2
2
*forward*
2
@display-options
199
5
4
26
2
Syntax:  @display-option
2
         @display-option <option>
2

2
Synonym:  @displayoption
2

2
The display options customize the behavior of the @display command to your particular taste.  The first form of this command displays all of your display options.  The second form displays just that one option, one of the flags listed below.
2

2
The remaining forms of this command are for setting your display options:
2

2
         @display-option +<flag>
2
         @display-option -<flag>
2
         @display-option !<flag>           (equivalent to -<flag>)
2

2
These respectively set and reset the specified flag
2

2
-blank_tnt     Show the verb args on all verbs.
2
+blank_tnt     Don't show the verb args on `this none this' verbs.
2
-shortprep     Use full prepositions  (e.g., "on top of/on/onto/upon")
2
+shortprep     Use short prepositions (e.g., "on")
2
-firstancestor Specifying . (:) to retrieve all properties (verbs) will only
2
               display properties (verbs) defined on the object itself.
2
+firstancestor Specifying . (:) to retrieve all properties (verbs) will go
2
               up the ancestor chain until it finds a readable object with
2
               properties (verbs) defined on it.
2

2
For backwards compatibility, the obsolete option `thisonly' is recognized as being the negation of `firstancestor'
199
5
4
19
2
*pass*
2
@add-feature
2
Note to programmers: @add-feature and @remove-feature are front-ends for player:add_feature and :remove_feature.
2

2
:add_feature returns
2

2
  * E_PERM unless caller == this || $perm_utils:controls(caller_perms())
2
  * E_INVARG if feature is not an object or is invalid
2
  * E_PERM if the object is not feature_ok
2
  * a true value otherwise
2

2
and calls feature:feature_add, if the verb exists.
2

2
:remove_feature returns
2

2
  * E_PERM unless caller == this || $perm_utils:controls(caller_perms()) || caller_perms() == feature.owner
2
  * a true value otherwise
2

2
and calls feature:feature_remove, if the verb exists.
199
5
4
2
2
*forward*
2
@add-feature
199
5
4
6
2
*pass*
2
features
2

2
Note to programmers: In order to be available for general use as a feature, an object must have a verb or property named "feature_ok" which returns a true value.
2

2
When a feature is added to a player's features list, feature:feature_add is called, if it exists, with the player in question as its argument.  Likewise, when a feature is removed, feature:feature_remove is called.
199
5
4
4
2
*pass*
2
examine
2

2
[Note to programmers: the 'obvious' verbs are those that can be invoked as commands and are not specified by the :hidden_verbs verb.  The default definition of "hidden" is "not readable".  You can override this definition with a :hidden_verbs verb that gets the default list with pass(@args) and then alters that list.]
199
5
4
31
2
Some rooms have descriptions which incorporate text from each of the objects in them.  For example, in Jay's kitchen, the thermostat is an object which contributes the sentence "A thermostat is attached to the wall near the appliances."  A description of this type is said to `integrate' objects into itself.
2

2
Each object may have separate messages for integrating into room descriptions, player descriptions, and container descriptions, as well as a default message used for integration into anything.  These messages are @integrate_room, @integrate_player, @integrate_container, and @integrate.  Room integration is the best developed of these, and the subject of this documentation.
2

2
These instructions are centered around a particular model of integration:  Namely, that you as a builder have created a room, with certain objects that belong in it (at least part of the time), and you want them to be part of the description, instead of showing up in the contents list at its end.  It's also possible for random wandering objects to integrate into the description, but it's hard to make that effective.
2

2
Each object's part
2
2-----------------
2

2
If the room wants to integrate a given object, the object has control over how it gets integrated, or whether it gets integrated at all.  There are three basic options.
2

2
First, an object can be invisible--that is, not to show up in the description at all.  To do this, it must either have a verb :visible or a property .visible that returns or contains a false value.
2

2
Second, an object can be visible but not integrate--it'll show up in the contents list, but not in the main text of the description.  If a visible object's @integrate_room message is empty, this happens.
2

2
Third, an object can contribute to the description's main text.  Having a non-empty @integrate_room message accomplishes this.  These messages are run through pronoun substitution (see `help pronouns'), so you can include the object's name or its location's name in the text.
2

2
It's actually possible for objects to have different integration messages for different locations, but that's not discussed here.
2

2
Putting the pieces together
2
2--------------------------
2

2
The text description that people see in a room is effectively made up of its .description and .integrate_paragraphs properties.  The first is a piece of text; the second may be a combination of objects to be integrated and `filler' text to put between them.
2

2
The .description property is either a string or a list of strings; it's set with @describe.  This part is simple.  (See `help describe'.)  If it's a list, each string is a paragraph; otherwise, the string constitutes the first and only paragraph.
2

2
The .integrate_paragraphs property is a little more complicated, and has no associated setter command.  It's a list of lists, representing paragraphs.  The first list is not actually its own paragraph but is appended to the last paragraph of .description; if it's empty, the text paragraph will stand alone.
2

2
Each paragraph list is composed of a number of objects and strings.  When somebody looks at a room, the objects' integration messages are all checked; these and the strings are put together in the order given.
2

2
[There should be a medium-length example here, and probably furniture should be mentioned.  (Furniture has an automatic integration message.)  And, really, we just need a decent user interface so we can cover up the ugly distinction between .description and .integrate_paragraphs, and probably do something more sensible with visibility too.]
199
5
4
11
2
The event system allows a room to notify its dependents (contents and exits) that something has taken place within the room.
2

2
To add another object to the room's dependents list, use :add_dependent(object).  To remove an object from the room's dependents list, use :remove_dependent(object).
2

2
To broadcast an event, use :broadcast_event_<type>(@args), where type is the type of event you wish to broadcast.  It'd be a good idea to make args[1] be an alist of {string, value} pairs; if this is so, and one of the strings is "default_handler", the corresponding value will be used as an event handler object (see below) which will handle the event if the receiver doesn't otherwise handle it.
2

2
For more information on the above verbs, see help $room:add_dependent/remove_dependent/broadcast_event_*.
2

2
For an object to handle an event it must define an event handler.  Event handlers are simply verbs defined on the objects with the name :event_<type>, where type is the type of the event you wish to handle.  Event handler objects (descended from $event_handler) may also be added to an object with object:add_handler(handler); event handler objects handle events via verbs named :handle_event_<type>.
2

2
The currently supported standard events notified from $room are :event_enter(who) and :event_exit(who), broadcast when `who' enters and exits the room.
199
5
4
4
2
*pass*
2
valid()
2

2
In almost all cases, you really want to be using $recycler:valid(), which behaves the same as valid() but additionally returns false if the object is $garbage.
199
5
4
2
2
*forward*
2
@property
199
5
4
2
2
*forward*
2
@add-feature
199
5
4
2
2
*forward*
2
@display
199
5
4
2
2
*forward*
2
ports
199
5
4
3
2
*pass*
2
listen()
2
See `help $network:listen' for possible non-wizardly access to the listen() function.
199
5
4
3
2
*pass*
2
unlisten()
2
See `help $network:unlisten' for possible non-wizardly access to the unlisten() function.
199
5
4
2
2
*forward*
2
integration
199
5
4
2
2
*forward*
2
examine
199
5
4
3
2
*pass*
2
(common_aliases)
2
Typing `examine object' (see `help examine') will show you its aliases.  If you are a programmer, you can type `#<object>.aliases', using an object's number, or `#Munchkin.aliases p'.  (The `p' indicates that the prefix is a player's name.)
199
5
4
7
2
*forward*
2
(aliases_extra)
2
For information about setting aliases, see help on the following topics:
2

2
@rename -- setting an object's name and all aliases
2
@addalias -- adding an alias to an object
2
@rmalias -- removing an alias from an object
199
5
4
18
2
Usage:  #<string>[.<property>|.parent] [<match type>] [for <code>]
2

2
This command returns information about the object (we'll call it <thing>) named by the given string.  String is matched in the current room unless a match type is given; type can be "exit", "player", "inventory", or "room".
2

2
If neither .<property>|.parent nor <code> is specified, # just shows you <thing>.
2

2
If .<property> is named, it shows <thing>.<property>.  .parent shows parent(<thing>).
2

2
If <code> is given, it is evaluated, with the value returned by the first part being substituted for %# in <code>.
2

2
Example:
2

2
  Munchkin types:
2
    #Joe.parent player for tonum(%#)
2
  and sees
2
    => 58
2

2
This means that Joe's parent class is #58, the generic programmer.
199
5
4
2
2
*forward*
2
named-arguments
199
5
4
33
2
Named arguments are a way of specifying the input arguments to a function by name, rather than referring directly to the `args' variable.  They can be used for a limited but common set of argument configurations.
2

2
The way named arguments actually work is by automatically creating and interpreting variable assignment statements to hide them from you.  For example, you might @list a verb:
2

2
   #0:"foo" (bar, baz, @quux)
2
    1:  // Usage: :foo(bar, baz, @quux)
2
    2:  // 
2
    3:  // Call bar:baz with the given quux arguments.
2
    7:  if (caller == this)
2
    8:    bar:(baz)(@quux);
2
    9:  endif
2

2
Note that there are no lines 4-6.  (Also note the comment format, which is enabled when you type `@list-option +new-comments'; this is independent of named arguments.)  The underlying verb code is this:
2

2
    1:  "Usage: :foo(bar, baz, @quux)";
2
    2:  "";
2
    3:  "Call bar:baz with the given quux arguments.";
2
    4:  bar = args[1];
2
    5:  baz = args[2];
2
    6:  quux = args[3..length(args)];
2
    7:  if (caller == this)
2
    8:    bar:(baz)(@quux);
2
    9:  endif
2

2
The named arguments code will interpret any number of simple variables; if the last named argument is prefixed with an `@' sign, it will receive all the remaining arguments.
2

2
The following verbs work with named arguments:
2

2
  * @verb allows you to specify an initial named arguments set.
2
  * @args both provides and accepts named arguments (in its `set args' and `show args' forms).
2
  * @program allows you to specify named arguments.  If the specified object has a verb with the specified name and arguments `this none this', it will be recoded to use the given named args.
2
  * @edit with local editing provides named arguments as part of the upload script it creates.
2
  * @list shows named arguments.
199
5
4
2
2
*forward*
2
named-arguments
199
5
4
47
2
Usage:  @eval-options
2
        @eval-option <option>
2

2
These options allow you to customize the `eval' command (see `help eval'), along with some other commands that evaluate code (like the `#' command).
2

2
The first form displays all of your eval options.  The second shows or sets a given option.  Current eval options are:
2

2
 +no-debug      `;' runs `eval-d' (i.e. no debug flag).
2
 -no-debug      `;' runs `eval' (i.e. with debug flag).
2

2
 +print-names   Show names of objects in lists.
2
 -print-names   Don't show names of objects in lists.
2

2
The `print-names' option allows eval to print results like {#0 (System Object), #1 (root class)}.  This isn't a valid MOO expression, but it may be more readable.
2

2
  subs={{"x", "y"}, {"z", "w"}, ...}
2
		Substitutions to perform upon each eval command.
2
 !subs		Don't perform any substitutions upon each eval command.
2

2
The `subs' option causes the eval command to perform simple textual substitutions on the expressions you evaluate.  For example, if you can't type brackets, you might use the following substitutions to allow you to evaluate list indexing expressions:
2

2
  >@eval-options subs="{{\"<<\", \"[\"}, {\">>\", \"]\"}}"
2
  subs          Substitutions to perform upon each eval command:
2
                {{"<<", "["}, {">>", "]"}}
2
  >;{1, 2}<<2>>
2
  => 2
2

2
This of course begs the question of how you enter the @eval-options line without being able to type brackets.  Note also that you need to do a great deal of quoting to enter the `subs' option.
2

2
This option shouldn't be used simply for creating variables; the `env' option described below does that much better.  But it's useful for substituting non-variable expressions, if necessary.
2

2
The MOO verb editor will perform the same substitutions, if you tell it to.  See `help @edit-options'.
2

2
 +time		Show ticks and seconds after each eval command.
2
 -time          Do not show ticks and seconds after each eval command.
2

2
  env=me=player;here=me.location;_=me.current_object[1]
2
 !env
2

2
  ticks=6	Subtract 6 ticks from the time displayed for an eval.
2
 !ticks		Don't subtract any ticks from the time displayed for an eval.
2

2
The `env' option is a string to be prepended to each string evaluated.  It is typically used to declare variables for commonly-used expressions; for example, the default string, shown above, sets "me" and "here" to the objects given those names by the built-in parser.  It also sets `_' to your `current object' (see `help current-object').
2

2
The `ticks' option should be the number of ticks used by the `env' string.  When you eval an expression (like `2'), the number of ticks is counted, and displayed if you have the `time' option on.  (Evaluating `2' takes 1 tick.)  But if you have an `env' set, the cost of evaluating the environment string will also be counted.  (Evaluating the default `env' takes 6 ticks, so the full expression will take 7.)  If the `ticks' option is correct, subtracting it from the total ticks will leave the ticks used by the expression in the `eval' command.
2

2
Ordinarily, you should set these two options with the `@setenv' command, which evaluates the expression you give it, counts the ticks it takes by itself, and sets your `ticks' option to that.  It remains possible to set the options independently, though, in case you want to do something tricky.
199
5
4
2
2
*forward*
2
statements
199
5
4
2
2
*forward*
2
statements
199
5
4
2
2
*forward*
2
statements
199
5
4
2
2
*forward*
2
statements
199
5
4
2
2
*forward*
2
@copy
199
5
4
2
2
*forward*
2
@list
199
5
4
2
2
*forward*
2
@program
199
5
4
2
2
*forward*
2
@rmverb
199
5
4
2
2
*forward*
2
@args
199
5
4
2
2
*pass*
2
@rename
199
5
4
2
2
*pass*
2
@addalias
199
5
4
2
2
*pass*
2
@rmalias
199
5
4
25
2
Syntax:  @list-option
2
         @list-option <option>
2

2
Synonym:  @listoption
2

2
The list options customize the behavior of the @list command to your particular taste.  The first form of this command displays all of your display options.  The second form displays just that one option, one of the flags listed below.
2

2
The remaining forms of this command are for setting your list options:
2

2
         @list-option +<flag>
2
         @list-option -<flag>
2
         @list-option !<flag>           (equivalent to -<flag>)
2

2
These respectively set and reset the specified flag
2

2
-parentheses       Parenthesize expressions in listings only as needed.
2
+parentheses       Fully parenthesize expressions in listings.
2
-nonumbers         Include line numbers.
2
+nonumbers         Omit line numbers.
2
-//_comments       Don't mangle comments in verb code
2
+//_comments       Display comment lines with //
2
-args_assignment   Suppress assignment of args; do named-arg extraction
2
+args_assignment   Show assignment of args; no named-arg extraction
2

2
The option `numbers' is recognized as being the negation of `nonumbers'.
199
5
4
2
2
*forward*
2
@list-options
199
5
4
0
35
1
5
199
4
4
1
2
Programmer Help
199
5
2
This provides help on the programmer commands available on $prog and related topics.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
89484
0
913134427
200
1
#23
Wizard Help

16
35
-1
-1
-1
30
-1
22
0
36
@guests
@log
@egrep
@unnewt
@denewt
@newt
@grep
$site_db
graylist
blacklist
@recycle
wiz-index
@dump-database
@players
@net-who
@@who
make-core-database
@make-player
@quota
@detoad
@untoad
@toad
@grepcore
@who-calls
@abort-shutdown
@shutdown
@programmer
@shout
@chown
@grant
redlist
@blacklist
@graylist
@redlist
@make-guest
@chmod-raw
51
4
7
2
Syntax:  @guests [<n>]
2

2
Prints out the log of guest player connections, indicating connect/disconnect times and where they came from.  If a numeric argument n is given, then only the last n entries in the log are consulted (useful for when the full log is rather long) --- note that connections and disconnections are separate entries so the actual printed listing will be about half this length.
2

2
Alternate:  @guests now
2

2
Prints out in @who format all connected guests.  In place of the location field is the current connect site.
35
5
4
11
2
Syntax:  @log <message>
2
         @log
2

2
The first form enters <message> as a one-line comment in the server log.
2
The second form prompts for a sequence of lines to be collectively entered as an extended comment.  This uses $command_utils:read_lines so all of those conventions apply, i.e., a period on a line by itself ends the text, `@abort' aborts the command, etc...).  Example:  If Wizard (#2) types
2

2
    @log I did $dump_interval=3600
2

2
the following line appears in the server log
2

2
    Aug 19 22:36:52:  COMMENT:  from Wizard (#2):  I did $dump_interval=3600
35
5
4
2
2
*forward*
2
@grep
35
5
4
2
2
*forward*
2
@denewt
35
5
4
9
2
Syntax:    @denewt <user> [commentary]
2
Synonyms:  @unnewt
2
           @get-better
2

2
@denewt reverses the effects of @newt, moving a user's :denewt_confunc back to :confunc, but checking first that :confunc is the same as $wiz_utils:newt_confunc (if not, we save :confunc as :newt_confunc and complain).
2

2
Mail is sent to $newt_log including any commentary you provide.  E.g.,
2

2
  @denewt Twit He promises not to do it again.
35
5
4
25
2
*subst*
2
Syntax:  @newt <player> [commentary]
2

2
The @newt command temporarily prevents logins on a given player.
2
It works by installing a confunc ($wiz_utils:newt_confunc) on player that does an immediate ;boot_player(), saving any existing :confunc the user may have as :denewt_confunc.  Use @denewt to reverse this.
2

2
You must give either the player's full name or its object number.
2
Also, this command does not let you @newt yourself.
2

2
Mail will be sent to $newt_log, listing the player's .all_connect_places and including any commentary you provide.  E.g.,
2

2
  @newt Twit  did real annoying things.
2

2
As with @toad and @programmer, there are messages that one may set
2

2
@newt  [%Nd %n:(@newts) %dd (%d#).]
2
  Printed to everyone in the room in which the victim is being @newted.
2
  If you're worried about accidentally newting yourself in the process of
2
  setting this message, you can't (see above).
2

2
@newt_victim  [unset]
2
  Printed to the victim.  
2
  This is followed by $login:newt_registration_string().
2

2
See `help @toad' if you need something more drastic.
35
5
4
13
2
*pass*
2
@grep
2

2
For wizards, the following forms are also available for doing full-db searches
2

2
         @grep  <pattern>
2
         @egrep <pattern>
2
         @grep  <pattern> from [#]<n>
2
         @egrep <pattern> from [#]<n>
2

2
the first two search all objects in the database while the last two search the range [#<n>..max_object()]
2

2
See also:  @grepcore, @who-calls.
35
5
4
20
2
$site_db is a database of places people have connected from.  Important verbs and properties are:
2

2
  :add(sitename,player)
2
      records the fact that player connected from sitename.
2
  :load()
2
      clears the db and reloads all of the player connection info.
2

2
  .domain
2
      default domain for unqualified sitenames given to :add.
2

2
For each domain we keep a list of players and subdomains. 
2

2
For example, :add("doc.ic.ac.uk",#666) enters #666 on the lists for "doc.ic.ac.uk", and, if we have to create an entry for "doc.ic.ac.uk", we enter "doc" on the list for "ic.ac.uk", "ic" on the list for "ac.uk", etc....  In this case, :find("ic") will return the "ic.ac.uk" list if there is no other domain in $site_db starting with "ic".  Note that the "ic.ac.uk" list may contain both objects, i.e., players that have connected from the site "ic.ac.uk", and strings, i.e., subdomains of "ic.ac.uk" like "doc".
2

2
  :find_exact(string)    => player/subdomain list or $failed_match
2
  :find_all_keys(string) => list of all domains that begin with string
2
  :find_key     (string) => unique domain that begins with string, 
2
                            $ambiguous_match or $failed_match
2

2
The other $generic_db functions (:find, :find_all) are also available, though admittedly less useful.
35
5
4
2
2
*forward*
2
blacklist
35
5
4
24
2
THE SITE BLACKLIST
2
==================
2

2
$login maintains three lists of hosts/domains to support player registration schemes and blocking of connections from highly untrusted hosts:
2

2
  .redlist   -- all connections from these sites are disabled 
2
  .blacklist -- player creation and guest logins are disabled
2
  .graylist  -- advisory list of potential trouble spots (putting a site on the .graylist merely annotates it in @net-who listings).
2

2
The lists are kept in a special format so it is highly recommended that you either use $wiz:@*list/@un*list or the following verbs to query/update the respective lists rather than bash them directly:
2

2
  $login:*listed     (host)              is host is on .*list?
2
  $login:*list_add   (domain or subnet)  add domain or subnet to .*list
2
  $login:*list_remove(domain or subnet)  remove domain or subnet from .*list
2

2
where `*' is one of `black', `red', or `gray'.
2

2
One may either specify a domain name (e.g., "baz.edu") or a numeric IP address (e.g., "36.0.23.17").  Domain names match all hosts underneath that domain, so, e.g., puting "baz.edu" on a list effectively adds "x.bax.edu" for all x as well.  
2

2
Likewise, an incomplete numeric address, e.g., "128.42" will match that entire subnet, in this case all hosts whose IP numbers have the form "128.42.m.n" for arbitrary integers m and n.
2

2
One may also give a domain name containing a wildcard ("*"), e.g., "fritz*.baz.edu", in which case all hostnames matching in the sense of $string_utils:match_string() are considred to be on the list.  Wildcard matching should be avoided since it is more time-consuming.
2

2
It should be noted that, since there is no direct access to the domain name service from within the MOO, it is possible for a host to be blacklisted or redlisted via its domain name, and yet have someone be able to connect from that host (and, in the case of a blacklisted host, create a character) --- this can happen if the name service is down and connection_name() on that player thus has given the numeric IP address rather than the domain name.  Similarly, if you list a host by IP number alone, it will still be possible to get in via the site's domain name.  Thus to be completely assured of shutting out a site, you need to list it both by domain name and IP number.
35
5
4
6
2
*pass*
2
@recycle
2

2
Of course, wizards are allowed to @recycle anything at all.
2

2
There is, however, a block (in $player:recycle) against recycling actual players, i.e., descendants of $player that have the player flag set.  This is mainly to prevent stupid mistakes.  If, for some reason, you want to recycle a player, you need to @toad it first.
35
5
4
2
2
*index*
2
Wizard Help Topics
35
5
4
3
2
Syntax:  @dump-database
2

2
Invokes the builtin dump_database(), which requests that the server checkpoint the database at its next opportunity.  It is not normally necessary to call this function; the server automatically checkpoints the database at regular intervals; see the chapter on server assumptions about the database for details.
35
5
4
3
2
Syntax:  @players [with objects]
2

2
Hmmm... what *does* this do, anyway?
35
5
4
7
2
Syntax:  @net-who [<player>...]
2
         @net-who from [<domain>]
2
Synonym: @@who
2

2
@net-who without any arguments prints all connected users and hosts.  If one or more <player> arguments are given, the specified users are printed along with their current or most recent connected hosts.  If any of these hosts are mentioned on $login.blacklist or $login.graylist (see `help blacklist'), an annotation appears.
2

2
With a `from...' argument, this command consults $site_db and prints all players who have ever connected from the given domain.
35
5
4
2
2
*forward*
2
@net-who
35
5
4
3
2
Syntax:  make-core-database
2

2
...makes a core database (surprise).  Film at 11...
35
5
4
5
2
Syntax:  @make-player <name>[,<aliases>] [<email-address> [password>]]
2

2
This creates a new player having the given name and password.
2
If an email address is given, this information is sent off to $new_player_log.
2
If password is null or not given, a random password is generated.
35
5
4
7
2
*pass*
2
@quota
2

2
 - - - - - - - - - - - - - - - - - - - - - - - - - -
2
Syntax:  @quota <player> is <number> [<reason>]
2

2
This second and more interesting form of the verb changes a player's quota to the given number.  Mail will be sent to $quota_log; the message will include the <reason> if such is given.
35
5
4
2
2
*forward*
2
@untoad
35
5
4
10
2
Syntax:  @untoad <object> [as <name>,<alias>,<alias>...]
2
Synonym: @detoad
2

2
Turns the object into a player.  If the name/alias... specification is given, the object is also renamed.
2

2
In order for this to work, the object must be a nonplayer descendant of $player and the new object name (or the original name if none is given in the command line) must be available for use as a player name.  As with ordinary player @renaming, any aliases which are unavailable for use as player names are eliminated.
2

2
If the object is a descendant of $guest, then it becomes a new guest character.  Otherwise the object is chowned to itself.  In the latter case, it is advisable to check that the .password property has something nontrivial in it.
2

2
If the object is a descendant of $prog, then its .programmer flag is set.  Note that the .wizard flag is not set under any circumstances.
35
5
4
27
2
*subst*
2
Syntax:  @toad   <player>  [graylist|blacklist|redlist] [commentary]
2
         @toad!  <player>                               [commentary]
2
         @toad!! <player>                               [commentary]
2

2
Resets the player flag of <player> (thus causing <player> to be booted), resets the .programmer and .wizard flags, chowns the player object to $hacker, and removes all of its names and aliases from $player_db.
2

2
You must give either the player's full name or its object number.  Also, this command does not let you @toad yourself.
2

2
A message will be sent to $newt_log, containing a list of sites the player has connected from, and the commentary if you provided any.
2

2
In some cases you may wish to add the player's last connected site to the site graylist, blacklist or redlist -- see `help blacklist' -- in order to invoke various kinds of blocking on that site (e.g., if player creation is enabled, you may want to enter the player on the blacklist to keep him from immediately creating a new character).  Specifying one of the listnames `graylist', `blacklist' or `redlist' will do this.
2

2
@toad!  <player>  is synonymous with  @toad <player> blacklist
2
@toad!! <player>  is synonymous with  @toad <player> redlist
2

2
There are messages that one may set to customize toading.  After all, a toading is (supposed to be) a rare event and you will doubtless want to put on a good show.  Thus we have
2

2
@toad  [%Nd %n:(@toads) %dd (%d#).]
2
  Printed to everyone in the room in which the victim is being @toaded.
2
  If you're worried about accidentally toading yourself in the process of
2
  setting this message, see above.
2

2
@toad_victim  [Have a nice life...]
2
  Printed to the victim.
2

2
These are pronoun_subbed with victim == dobj.
35
5
4
6
2
Syntax:  @grepcore <pattern>
2
         @who-calls <verbname>
2

2
@grepcore pattern is @grep pattern in {all core objects}.  Core objects are computed for you by #0:core_objects().
2

2
@who-calls greps for the verbname + "(", hoping to catch it as a verb call.  Currently @who-calls does not allow you to restrict the search as @grep does.  (Volunteers?)
35
5
4
2
2
*forward*
2
@grepcore
35
5
4
3
2
Syntax:  @abort-sh*utdown [<text>]
2

2
This aborts any shutdown currently in progress (i.e., set in motion by @shutdown).  All players are notified that no shutdown will actually occur; <text>, if given will be included in this notification.
35
5
4
5
2
Syntax:  @shutdown [<text>]
2

2
This is the friendly way to do a server shutdown; it arranges for the actual shutdown to take place two minutes hence.  Shutdown is preceded by a sequence of warnings to all connected players.  Warnings are likewise given to all players who connect during this time.  <text>, if given is included in these warning messages, perhaps as an explanation for why the server is being shut down.
2

2
Shutdown may be aborted at any time by using @abort-shutdown.
35
5
4
16
2
*subst*
2
Syntax:  @programmer <player>
2

2
Sets the programmer flag on the indicated player and sends mail to $new_prog_log.  
2

2
If the player is not already a descendant of $prog, we @chparent him/her to $prog.  In this case, if $prog has a larger .ownership_quota than its ancestors, then we raise the player's quota by the difference between $prog.ownership_quota and the .ownership_quota of the common ancestor of player and $prog, be this $player or some intermediate class.
2

2
There are messages that one may set to customize how the granting of a programmer bit looks to the victim and to any onlookers.  After all, this is a seminal event in a MOOer's life...  Thus we have
2

2
@programmer  [%[$wiz.programmer_msg]]
2
  Printed to everyone in the room with the victim being @programmer'ed.
2

2
@programmer_victim  [%[$wiz.programmer_victim_msg]]
2
  Printed to the victim.
2

2
These are pronoun subbed with victim == dobj.
35
5
4
3
2
Syntax:  @shout <text>
2

2
Broadcasts the given text to all connected players.
35
5
4
14
2
Syntax:  @chown <object>            [to] <owner>
2
         @chown <object>.<propname> [to] <owner>
2
         @chown  <object>:<verbname> [to] <owner>
2
         @chown# <object>:<verb-number> [to] <owner>
2

2
Changes the ownership of the indicated object, property or verb.
2

2
Verb ownership changes are fairly straightforward, being merely a matter of changing the verb_info() on a single verb.  The `@chown#' form is used to reference verbs by number, 1 being the first verb on <object>.
2

2
Changing an object ownership includes changing the ownership on all +c properties on that object.  Note that @chown will not change the ownership of any other properties, nor will it change verb ownerships.  Use @grant if you need to do a more complete ownership change.  The quota of the former owner is increased by one, as is the quota of the new owner decreased by one.
2

2
Changing a property ownership is truly hairy.  If the property is +c one shouldnot be doing this, unless it is to correct a past injustice which caused the property to be owned by the wrong player.  In the case of -c properties, the property ownership is changed on all descendent objects (currently, if +c instances of a -c property are found in the traversal of all of the descendants, these are not changed, being deemed sufficiently weird that they should be handled on a case-by-case basis...).
2

2
If there's any justice, a future version of the server will prevent occurrences of (1) +c properties being owned by someone other than the object owner (2) -c properties with different owners on descendant objects (3) -c properties that are +c on some descendants.
35
5
4
3
2
*pass*
2
@grant
2
Wizards can @grant objects in one step.
35
5
4
2
2
*forward*
2
blacklist
35
5
4
14
2
Syntax:  @redlist   [<domain or subnet> [commentary]]
2
         @blacklist [<domain or subnet> [commentary]]
2
         @graylist  [<domain or subnet> [commentary]]
2

2
Syntax:  @unredlist   [<domain or subnet> [commentary]]
2
         @unblacklist [<domain or subnet> [commentary]]
2
         @ungraylist  [<domain or subnet> [commentary]]
2

2
With no argument, the current contents of the <color>list are printed.
2
Otherwise, the specified domain or subnet is added to or removed from the list and mail will be sent to $site_log.  
2

2
If the given domain or subnet has subdomains/subsubnets that are already on the list, you will be prompted as to whether you want to remove them.  Note that adding an entry for a particular domain or subnet effectively adds all subdomains/subsubnets, so unless there's some reason for keeping an explicit entry for a particular subdomain, chances are you will indeed want to remove them.  One reason to keep an explicit entry for a subdomain would be if you intended to unlist the full domain later but wanted to be sure you didn't unlist the subdomain in the process.
2

2
See `help blacklist' for a description of the functions of these <color>lists.
35
5
4
2
2
*forward*
2
@blacklist
35
5
4
2
2
*forward*
2
@blacklist
35
5
4
9
2
Syntax:  @make-guest <adjective>
2

2
This creates a new guest character.  For example,
2
  @make-guest Loud
2
creates a child of $guest, owned by $hacker, named Loud_Guest and with aliases Loud and Loud_Guest.
2

2
Note that in order to have `connect guest' connect to a guest character, there needs to exist some guest character having "Guest" as a name or alias.
2

2
See also `help @make-player'.
35
5
4
7
2
Syntax:  @chmod-raw <object>.<property-name> <permissions>
2

2
Like @chmod, this Changes the permissions of a property to those given.  
2

2
Unlike @chmod this does a raw set_property_info rather than $wiz_utils:set_property_flags, thus changing ONLY the property flags on the specified object.  The corresponding flags on descendant objects are not updated, nor is there any restriction on using @chmod-raw on properties actually defined on an ancestral object.
2

2
This command is provided for fixing existing mistakes in property flag settings; its use is otherwise discouraged.
35
5
4
0
35
1
5
35
4
4
1
2
Wizard Help
35
5
4
1
2
This describes the various commands available on $wiz.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
21290
0
913134427
200
1
#24
wizard utilities

16
199
-1
-1
-1
156
-1
27
25
set_programmer
199
173
-1
set_player
199
173
-1
set_owner
199
173
-1
set_property_owner
199
165
-1
unset_player
199
173
-1
set_property_flags
199
165
-1
_set_property_flags
199
173
-1
random_password
199
173
-1
queued_tasks
199
165
-1
isnewt
199
173
-1
newt_confunc
199
169
-1
connected_wizards
35
165
-1
all_wizards
35
165
-1
rename_all_instances
199
173
-1
missed_help
199
173
-1
show_missing_help
199
173
-1
init_for_core
199
173
-1
net_connections
199
165
-1
do_grant
199
173
-1
check_player_request
199
173
-1
do_make_guest
199
173
-1
do_make_player
199
165
-1
send_new_player_mail
199
165
-1
do_newt
199
165
-1
do_denewt
199
165
-1
3
missed_help_strings
missed_help_counters
dont_reap
18
4
0
199
1
4
0
199
1
4
5
1
63
1
200
1
35
1
196
1
190
199
5
5
199
5
5
199
4
4
1
2
Wizard Utilities
199
5
4
36
2
Wizard Utilities
2
----------------
2
The following functions are substitutes for various server builtins.
2
Anytime one feel tempted to use one of the expressions on the right,
2
use the corresponding one on the left instead.  This will take care
2
of various things that the server (for whatever reason) does not handle.
2

2
:set_programmer(object)             object.programmer = 1;
2
    chparent object to $prog
2
    send mail to $prog_log
2

2
:set_player(object[,nochown])       set_player_flag(object,1);
2
    set player flag, 
2
    add name/aliases to $player_db,
2
    and maybe do a self chown.
2

2
:unset_player(object[,newowner])    set_player_flag(object,0);
2
    unset player flag,
2
    remove name/aliases from $player_db
2
    chown to newowner if given
2

2
:set_owner(object, newowner)        object.owner = newowner;
2
    change ownership on object
2
    change ownership on all +c properties
2
    juggle .ownership_quotas
2

2
:set_property_owner(object, property, newowner)
2
    change owner on a given property
2
    if this is a -c property, we change the owner on all descendants
2
    for which this is also a -c property.
2
    Polite protest if property is +c and newowner != object.owner.
2

2
:set_property_flags(object, property, flags)
2
    change the permissions on a given property and propagate these to 
2
    *all descendants*.  property ownership is changed on descendants 
2
    where necessary.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
31023
0
913134427
200
1
#25
Site DB

0
35
-1
-1
-1
79
-1
16
11
find(old)
35
169
-1
add
35
173
-1
load
199
173
-1
domain_literal
35
173
-1
init_for_core
199
173
-1
prohibit_lookup prohibit_modify
35
173
-1
prune
199
173
-1
report_prune_progress
199
173
-1
prune_fixup
199
173
-1
schedule_prune
199
173
-1
prune_reset
199
173
-1
7
domain
prune_task
prune_progress
prune_stop
total_pruned_people
total_pruned_sites
alphabet
28
3
13
35
1
0
717706778
35
5
2
ss10.karelia.ru
35
5
2
zzz
35
5
0
887
35
5
0
12087
35
5
2
abcdefghijklmnopqrstuvwxy0123456789_z
35
5
4
2
1
35
2

35
5
0
0
35
1
0
0
35
1
5
35
5
5
35
5
4
2
0
913134424
0
1439449680
35
1
4
4
2

2

4
0
4
0
35
1
5
35
4
4
3
2
sitedb
2
site
2
db
35
5
4
2
2
This object holds a db of places from which players have connected (see `help $site_db').
2
The site blacklist and the graylist live as well (see `help blacklist').
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
11400
0
913134427
200
1
#26
math utilities

16
35
-1
-1
-1
156
-1
39
34
sin
35
173
-1
cos
35
173
-1
tan
35
173
-1
xsin
35
173
-1
xcos
35
173
-1
factorial
35
173
-1
pow
35
173
-1
fibonacci
35
173
-1
geometric
35
173
-1
divmod
35
173
-1
combinations
35
173
-1
permutations
35
173
-1
simpson
35
173
-1
parts
35
173
-1
sqrt
35
173
-1
arctan
35
173
-1
div
35
173
-1
mod
35
173
-1
aexp
35
173
-1
is_prime
35
173
-1
AND
35
173
-1
OR
35
173
-1
XOR
35
173
-1
NOT
35
173
-1
BLFromInt
35
173
-1
IntFromBL
35
173
-1
gcd greatest_common_divisor
35
173
-1
lcm least_common_multiple
35
173
-1
are_rel_prime are_relatively_prime
35
173
-1
base_conversion
35
173
-1
exp
35
173
-1
norm
35
173
-1
percent
35
173
-1
sqrt_int
35
173
-1
5
base_alphabet
tangents
factor
taylor
pi
20
2
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
35
5
4
45
0
174
0
349
0
524
0
699
0
874
0
1051
0
1227
0
1405
0
1583
0
1763
0
1943
0
2125
0
2308
0
2493
0
2679
0
2867
0
3057
0
3249
0
3443
0
3639
0
3838
0
4040
0
4244
0
4452
0
4663
0
4877
0
5095
0
5317
0
5543
0
5773
0
6008
0
6248
0
6494
0
6745
0
7002
0
7265
0
7535
0
7812
0
8097
0
8390
0
8692
0
9004
0
9325
0
9656
0
10000
35
5
0
10000
35
5
0
100
35
5
9
3.141592653589793116
35
5
5
35
5
5
35
4
4
4
2
Math Utilities
2
Math_Utils
2
trigonometric utilites
2
trig_utils
35
5
4
47
2
Trigonometric/Exponential functions:
2
  sin(a),cos(a),tan(a) -- returns 10000*(the value of the corresponding
2
       trigonometric function) angle a is in degrees.
2
  arctan([x,]y) -- returns arctan(y/x) in degrees in the range -179..180.
2
       x defaults to 10000.  Quadrant is that of (x,y).
2
  exp(x[,n]) -- calculates e^x with an nth order taylor polynomial
2

2
Statistical functions:
2
  combinations(n,r) -- returns the number of combinations given n objects
2
       taken r at a time.
2
  permutations(n,r) -- returns the number of permutations possible given
2
       n objects taken r at a time.
2

2
Number decomposition:
2
  div(n,d) -- correct version of / (handles negative numbers correctly)
2
  mod(n,d) -- correct version of % (handles negative numbers correctly)
2
  divmod(n,d) -- {div(n,d),mod(n,d)}
2
  parts(n,q[,i]) -- returns a list of two elements {integer,decimal fraction}
2

2
Other math functions:
2
  sqrt(x)      -- returns the largest integer n <= the square root of x
2
  pow(x,n)     -- returns x^n
2
  factorial(x) -- returns x!
2

2
Series:
2
  fibonacci(n) -- returns the 1st n fibonacci numbers in a list
2
  geometric(x,n) -- returns the value of the nth order geometric series at x
2

2
Integer Properties:
2
  gcd(a,b) -- find the greatest common divisor of the two numbers
2
  lcm(a,b) -- find the least common multiple of the two numbers
2
  are_relatively_prime(a,b) -- return 1 if a and b are relatively prime
2
  is_prime(n) -- returns 1 if the number is a prime and 0 otherwise
2
  
2
Miscellaneous:
2
  random(n) -- returns a random number from 0..n if n > 0 or n..0 if n < 0
2
  random_range(n[,mean]) -- returns a random number from mean - n..mean + n
2
       with mean defaulting to 0
2
  simpson({a,b},{f(a),f((a+b)/2),f(b)}) -- returns the numerical
2
      approximation of an integral using simpson's rule
2

2
Bitwise Arithmetic:
2
  AND(x,y) -- returns x AND y
2
  OR(x,y) -- returns x OR y
2
  XOR(x,y) -- returns x XOR y (XOR is the exclusive-or function)
2
  NOT(x) -- returns the complement of x
2
      All bitwise manipulation is of 32-bit values.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
22024
0
913134427
200
1
#27
set utilities

16
35
-1
-1
-1
156
-1
32
7
union
35
173
-1
intersection
35
173
-1
diff*erence
35
173
-1
contains
35
173
-1
exclusive_or xor
35
173
-1
difference_suspended diff_suspended
35
173
-1
equals
199
173
-1
0
15
5
35
5
5
35
4
4
2
2
Set Utilities
2
set_utilities
35
5
4
14
2
This object is useful for operations that treat lists as sets (i.e., without concern about order and assuming no duplication).
2

2
 union(set, set, ...)        => union
2
 intersection(set, set, ...) => intersection
2

2
 diff*erence(set 1, set 2, ..., set n)
2
        => result of removing all elements of sets 2..n from set 1.
2
 exclusive_or(set, set, set, ...)
2
        => all elements that are contained in exactly one of the sets
2

2
 equals(set, set, ...)
2
        => true if and only if all the sets are equal
2
 contains(set 1, set 2, ..., set n)
2
        => true if and only if all of sets 2..n are subsets of set 1
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
4556
0
913134427
200
1
#28
Builtin Function Help (1.8.0)

16
35
-1
-1
-1
30
-1
23
0
126
builtin-index
abs()
acos()
add_property()
add_verb()
asin()
atan()
binary_hash()
boot_player()
buffered_output_length()
call_function()
caller_perms()
callers()
ceil()
children()
chparent()
clear_property()
connected_players()
connected_seconds()
connection_name()
connection_option()
connection_options()
cos()
cosh()
create()
crypt()
ctime()
db_disk_size()
decode_binary()
delete_property()
delete_verb()
disassemble()
dump_database()
encode_binary()
equal()
eval()
exp()
floatstr()
floor()
flush_input()
force_input()
function_info()
idle_seconds()
index()
is_clear_property()
is_member()
is_player()
kill_task()
length()
listappend()
listdelete()
listen()
listeners()
listinsert()
listset()
log()
log10()
match()
max()
max_object()
memory_usage()
min()
move()
notify()
object_bytes()
open_network_connection()
output_delimiters()
parent()
pass()
players()
properties()
property_info()
queue_info()
queued_tasks()
raise()
random()
read()
recycle()
renumber()
reset_max_object()
resume()
rindex()
rmatch()
seconds_left()
server_log()
server_version()
set_connection_option()
set_player_flag()
set_property_info()
set_task_perms()
set_verb_args()
set_verb_code()
set_verb_info()
setadd()
setremove()
shutdown()
sin()
sinh()
sqrt()
strcmp()
string_hash()
strsub()
substitute()
suspend()
tan()
tanh()
task_id()
task_stack()
ticks_left()
time()
tofloat()
toint()
toliteral()
tonum()
toobj()
tostr()
trunc()
typeof()
unlisten()
valid()
value_bytes()
value_hash()
verb_args()
verb_code()
verb_info()
verbs()
141
4
2
2
*index*
2
Server Built-in Functions
35
5
4
3
2
Syntax:  abs (num <x>)   => num
2

2
Returns the absolute value of <x>.  If <x> is negative, then the result is `-<x>'; otherwise, the result is <x>.  The number <x> can be either integer or floating-point; the result is of the same kind.
35
5
4
2
2
*forward*
2
asin()
35
5
4
3
2
Syntax:  add_property (obj <object>, str <prop-name>, <value>, list <info>) => none
2

2
Defines a new property on the given <object>, inherited by all of its descendants; the property is named <prop-name>, its initial value is <value>, and its owner and initial permission bits are given by <info> in the same format as is returned by `property_info()'.  If <object> is not valid or <info> does not specify a valid owner and well-formed permission bits or <object> or its ancestors or descendants already defines a property named <prop-name>, then `E_INVARG' is raised.  If the programmer does not have write permission on <object> or if the owner specified by <info> is not the programmer and the programmer is not a wizard, then `E_PERM' is raised.
35
5
4
5
2
Syntax:  add_verb (obj <object>, list <info>, list <args>)   => none
2

2
Defines a new verb on the given <object>.  The new verb's owner, permission bits and name(s) are given by <info> in the same format as is returned by `verb_info()'.  The new verb's direct-object, preposition, and indirect-object specifications are given by <args> in the same format as is returned by `verb_args()'.  The new verb initially has the empty program associated with it; this program does nothing but return an unspecified value.
2

2
If <object> is not valid, or <info> does not specify a valid owner and well-formed permission bits and verb names, or <args> is not a legitimate syntax specification, then `E_INVARG' is raised.  If the programmer does not have write permission on <object> or if the owner specified by <info> is not the programmer and the programmer is not a wizard, then `E_PERM' is raised.
35
5
4
4
2
Syntax:  asin (float <x>)   => float
2
         acos (float <x>)   => float
2

2
Returns the arc-sine or arc-cosine (inverse sine or cosine) of <x>, in the range `[-pi/2..pi/2]' or `[0..pi]', respectively.  Raises `E_INVARG' if <x> is outside the range `[-1.0..1.0]'.
35
5
4
3
2
Syntax:  atan (float <y> [, float <x>])   => float
2

2
Returns the arc-tangent (inverse tangent) of <y> in the range `[-pi/2..pi/2]' if <x> is not provided, or of `<y>/<x>' in the range `[-pi..pi]' if <x> is provided.
35
5
4
2
2
*forward*
2
string_hash()
35
5
4
9
2
Syntax:  boot_player (obj <player>)   => none
2

2
Marks for disconnection any currently-active connection to the given <player>.  The connection will not actually be closed until the currently-running task returns or suspends, but all MOO functions (such as `notify()', `connected_players()', and the like) immediately behave as if the connection no longer exists.  If the programmer is not either a wizard or the same as <player>, then `E_PERM' is raised.  If there is no currently-active connection to <player>, then this function does nothing.
2

2
If there was a currently-active connection, then the following verb call is made when the connection is actually closed:
2

2
    $user_disconnected(<player>)
2

2
It is not an error if this verb does not exist; the call is simply skipped.
35
5
4
3
2
Syntax:  buffered_output_length ([obj <conn>])   => int
2

2
Returns the number of bytes currently buffered for output to the connection <conn>.  If <conn> is not provided, returns the maximum number of bytes that will be buffered up for output on any connection.
35
5
4
3
2
Syntax:  call_function (str <func-name>, <arg>, ...)   => value
2

2
Calls the built-in function named <func-name>, passing the given arguments, and returns whatever that function returns.  Raises `E_INVARG' if <func-name> is not recognized as the name of a known built-in function.  This allows you to compute the name of the function to call and, in particular, allows you to write a call to a built-in function that may or may not exist in the particular version of the server you're using.
35
5
4
3
2
Syntax:  caller_perms ()   => obj
2

2
Returns the permissions in use by the verb that called the currently-executing verb.  If the currently-executing verb was not called by another verb (i.e., it is the first verb called in a command or server task), then `caller_perms()' returns `#-1'.
35
5
4
13
2
Syntax:  callers ([<include-line-numbers>])   => list
2

2
Returns information on each of the verbs and built-in functions currently waiting to resume execution in the current task.  When one verb or function calls another verb or function, execution of the caller is temporarily suspended, pending the called verb or function returning a value.  At any given time, there could be several such pending verbs and functions: the one that called the currently executing verb, the verb or function that called that one, and so on.  The result of `callers()' is a list, each element of which gives information about one pending verb or function in the following format:
2

2
    {<this>, <verb-name>, <programmer>, <verb-loc>, <player>, <line-number>}
2

2
For verbs, <this> is the initial value of the variable `this' in that verb, <verb-name> is the name used to invoke that verb, <programmer> is the player with whose permissions that verb is running, <verb-loc> is the object on which that verb is defined, <player> is the initial value of the variable `player' in that verb, and <line-number> indicates which line of the verb's code is executing.  The <line-number> element is included only if the <include-line-numbers> argument was provided and true.
2

2
For functions, <this>, <programmer>, and <verb-loc> are all `#-1', <verb-name> is the name of the function, and <line-number> is an index used internally to determine the current state of the built-in function.  The simplest correct test for a built-in function entry is
2

2
    (VERB-LOC == #-1  &&  PROGRAMMER == #-1  &&  VERB-NAME != "")
2

2
The first element of the list returned by `callers()' gives information on the verb that called the currently-executing verb, the second element describes the verb that called that one, and so on.  The last element of the list describes the first verb called in this task.
35
5
4
3
2
Syntax:  ceil (float <x>)   => float
2

2
Returns the smallest integer not less than <x>, as a floating-point number.
35
5
4
2
2
*forward*
2
parent()
35
5
4
7
2
Syntax:  chparent (obj <object>, obj <new-parent>)   => none
2

2
Changes the parent of <object> to be <new-parent>.  If <object> is not valid, or if <new-parent> is neither valid nor equal to `#-1', then `E_INVARG' is raised.  If the programmer is neither a wizard or the owner of <object>, or if <new-parent> is not fertile (i.e., its `f' bit is not set) and the programmer is neither the owner of <new-parent> nor a wizard, then `E_PERM' is raised.  If <new-parent> is equal to `object' or one of its current ancestors, `E_RECMOVE' is raised.  If <object> or one of its descendants defines a property with the same name as one defined either on <new-parent> or on one of its ancestors, then `E_INVARG' is raised.
2

2
Changing an object's parent can have the effect of removing some properties from and adding some other properties to that object and all of its descendants (i.e., its children and its children's children, etc.).  Let <common> be the nearest ancestor that <object> and <new-parent> have in common before the parent of <object> is changed.  Then all properties defined by ancestors of <object> under <common> (that is, those ancestors of <object> that are in turn descendants of <common>) are removed from <object> and all of its descendants.  All properties defined by <new-parent> or its ancestors under <common> are added to <object> and all of its descendants.  As with `create()', the newly-added properties are given the same permission bits as they have on <new-parent>, the owner of each added property is either the owner of the object it's added to (if the `c' permissions bit is set) or the owner of that property on <new-parent>, and the value of each added property is "clear"; see the description of the built-in function `clear_property()' for details.  All properties that are not removed or added in the reparenting process are completely unchanged.
2

2
If <new-parent> is equal to `#-1', then <object> is given no parent at all; it becomes a new root of the parent/child hierarchy.  In this case, all formerly inherited properties on <object> are simply removed.
35
5
4
2
2
*forward*
2
is_clear_property()
35
5
4
3
2
Syntax:  connected_players ([<include-all>])   => list
2

2
Returns a list of the object numbers of those player objects with currently-active connections.  If <include-all> is provided and true, then the list includes the object numbers associated with *all* current connections, including ones that are outbound and/or not yet logged-in.
35
5
4
4
2
Syntax:  connected_seconds (obj <player>)   => int
2
              idle_seconds (obj <player>)   => int
2

2
These functions return the number of seconds that the currently-active connection to <player> has existed and been idle, respectively.  If <player> is not the object number of a player object with a currently-active connection, then `E_INVARG' is raised.
35
5
4
17
2
Syntax:  connection_name (obj <player>)   => str
2

2
Returns a network-specific string identifying the connection being used by the given player.  If the programmer is not a wizard and not <player>, then `E_PERM' is raised.  If <player> is not currently connected, then `E_INVARG' is raised.
2

2
For the TCP/IP networking configurations, for in-bound connections, the string has the form
2
    "port <lport> from <host>, port <port>"
2
where <lport> is the decimal TCP listening port on which the connection arrived, <host> is either the name or decimal TCP address of the host from which the player is connected, and <port> is the decimal TCP port of the connection on that host.
2

2
For outbound TCP/IP connections, the string has the form
2
    "port <lport> to <host>, port <port>"
2
where <lport> is the decimal local TCP port number from which the connection originated, <host> is either the name or decimal TCP address of the host to which the connection was opened, and <port> is the decimal TCP port of the connection on that host.
2

2
For the System V `local' networking configuration, the string is the UNIX login name of the connecting user or, if no such name can be found, something of the form
2
    "User #<number>"
2
where <number> is a UNIX numeric user ID.
2

2
For the other networking configurations, the string is the same for all connections and, thus, useless.
35
5
4
3
2
Syntax:  connection_option (obj <conn>, str <name>)   => value
2

2
Returns the current setting of the option <name> for the connection <conn>.  Raises `E_INVARG' if <conn> does not specify a current connection and `E_PERM' if the programmer is neither <conn> nor a wizard.
35
5
4
3
2
Syntax:  connection_options (obj <conn>)   => list
2

2
Returns a list of `{<name>, <value>}' pairs describing the current settings of all of the allowed options for the connection <conn>.  Raises `E_INVARG' if <conn> does not specify a current connection and `E_PERM' if the programmer is neither <conn> nor a wizard.
35
5
4
2
2
*forward*
2
sin()
35
5
4
2
2
*forward*
2
sinh()
35
5
4
22
2
Syntax:  create (obj <parent> [, obj <owner>])   => obj
2

2
Creates and returns a new object whose parent is <parent> and whose owner is as described below.  Either the given <parent> object must be `#-1' or valid and fertile (i.e., its `f' bit must be set) or else the programmer must own <parent> or be a wizard; otherwise `E_PERM' is raised.  `E_PERM' is also raised if <owner> is provided and not the same as the programmer, unless the programmer is a wizard.  After the new object is created, its `initialize' verb, if any, is called with no arguments.
2

2
The new object is assigned the least non-negative object number that has not yet been used for a created object.  Note that no object number is ever reused, even if the object with that number is recycled.
2

2
The owner of the new object is either the programmer (if <owner> is not provided), the new object itself (if <owner> was given as `#-1'), or <owner> (otherwise).
2

2
The other built-in properties of the new object are initialized as follows:
2
    name         ""
2
    location     #-1
2
    contents     {}
2
    programmer   0
2
    wizard       0
2
    r            0
2
    w            0
2
    f            0
2
The function `is_player()' returns false for newly created objects.
2

2
In addition, the new object inherits all of the other properties on <parent>.  These properties have the same permission bits as on <parent>.  If the `c' permissions bit is set, then the owner of the property on the new object is the same as the owner of the new object itself; otherwise, the owner of the property on the new object is the same as that on <parent>.  The initial value of every inherited property is "clear"; see the description of the built-in function `clear_property()' for details.
2

2
If the intended owner of the new object has a property named `ownership_quota' and the value of that property is an integer, then `create()' treats that value as a "quota".  If the quota is less than or equal to zero, then the quota is considered to be exhausted and `create()' raises `E_QUOTA' instead of creating an object.  Otherwise, the quota is decremented and stored back into the `ownership_quota' property as a part of the creation of the new object.
35
5
4
10
2
Syntax:  crypt (str <text> [, str <salt>])   => str
2

2
Encrypts the given <text> using the standard UNIX encryption method.  If provided, <salt> should be a string at least two characters long, the first two characters of which will be used as the extra encryption ``salt'' in the algorithm.  If <salt> is not provided, a random pair of characters is used.  In any case, the salt used is also returned as the first two characters of the resulting encrypted string.
2

2
Aside from the possibly-random selection of the salt, the encryption algorithm is entirely deterministic.  In particular, you can test whether or not a given string is the same as the one used to produce a given piece of encrypted text; simply extract the first two characters of the encrypted text and pass the candidate string and those two characters to `crypt()'.  If the result is identical to the given encrypted text, then you've got a match.
2

2
    crypt("foobar")         =>   "J3fSFQfgkp26w"
2
    crypt("foobar", "J3")   =>   "J3fSFQfgkp26w"
2
    crypt("mumble", "J3")   =>   "J3D0.dh.jjmWQ"
2
    crypt("foobar", "J4")   =>   "J4AcPxOJ4ncq2"
35
5
4
13
2
Syntax:  ctime ([int <time>])   => str
2

2
Interprets <time> as a time, using the same representation as given in the description of `time()', and converts it into a 28-character, human-readable string in the following format:
2

2
    Mon Aug 13 19:13:20 1990 PDT
2

2
If the current day of the month is less than 10, then an extra blank appears between the month and the day:
2

2
    Mon Apr  1 14:10:43 1991 PST
2

2
If <time> is not provided, then the current time is used.
2

2
Note that `ctime()' interprets <time> for the local time zone of the computer on which the MOO server is running.
35
5
4
3
2
Syntax:  db_disk_size ()   => int
2

2
Returns the total size, in bytes, of the most recent full representation of the database as one or more disk files.  Raises `E_QUOTA' if, for some reason, no such on-disk representation is currently available.
35
5
4
9
2
Syntax:  decode_binary (str <bin-string> [, <fully>])   => list
2

2
Returns a list of strings and/or integers representing the bytes in the binary string <bin_string> in order.  If <fully> is false or omitted, the list contains an integer only for each non-printing, non-space byte; all other characters are grouped into the longest possible contiguous substrings.  If <fully> is provided and true, the list contains only integers, one for each byte represented in <bin_string>.  Raises `E_INVARG' if <bin_string> is not a properly-formed binary string.  (See the early section on MOO value types for a full description of binary strings.)
2

2
    decode_binary("foo")               =>   {"foo"}
2
    decode_binary("~~foo")             =>   {"~foo"}
2
    decode_binary("foo~0D~0A")         =>   {"foo", 13, 10}
2
    decode_binary("foo~0Abar~0Abaz")   =>   {"foo", 10, "bar", 10, "baz"}
2
    decode_binary("foo~0D~0A", 1)      =>   {102, 111, 111, 13, 10}
35
5
4
3
2
Syntax:  delete_property (obj <object>, str <prop-name>)   => none
2

2
Removes the property named <prop-name> from the given <object> and all of its descendants.  If <object> is not valid, then `E_INVARG' is raised.  If the programmer does not have write permission on <object>, then `E_PERM' is raised.  If <object> does not directly define a property named <prop-name> (as opposed to inheriting one from its parent), then `E_PROPNF' is raised.
35
5
4
3
2
Syntax:  delete_verb (obj <object>, str <verb-desc>)   => none
2

2
Removes the verb as specified by <verb-desc> from the given <object>.  If <object> is not valid, then `E_INVARG' is raised.  If the programmer does not have write permission on <object>, then `E_PERM' is raised.  If <object> does not define a verb as specified by <verb-desc>, then `E_VERBNF' is raised.
35
5
4
5
2
Syntax:  disassemble (obj <object>, str <verb-desc>)   => list
2

2
Returns a (longish) list of strings giving a listing of the server's internal ``compiled'' form of the verb as specified by <verb-desc> on <object>.  This format is not documented and may indeed change from release to release, but some programmers may nonetheless find the output of `disassemble()' interesting to peruse as a way to gain a deeper appreciation of how the server works.
2

2
If <object> is not valid, then `E_INVARG' is raised.  If <object> does not define a verb as specified by <verb-desc>, then `E_VERBNF' is raised.  If the programmer does not have read permission on the verb in question, then `disassemble()' raises `E_PERM'.
35
5
4
3
2
Syntax:  dump_database ()   => none
2

2
Requests that the server checkpoint the database at its next opportunity.  It is not normally necessary to call this function; the server automatically checkpoints the database at regular intervals; see the chapter on server assumptions about the database for details.  If the programmer is not a wizard, then `E_PERM' is raised.
35
5
4
7
2
Syntax:  encode_binary (<arg>, ...)   => str
2

2
Each argument must be an integer between 0 and 255, a string, or a list containing only legal arguments for this function.  This function translates each integer and string in turn into its binary string equivalent, returning the concatenation of all these substrings into a single binary string.  (See the early section on MOO value types for a full description of binary strings.)
2

2
    encode_binary("~foo")                     =>   "~7Efoo"
2
    encode_binary({"foo", 10}, {"bar", 13})   =>   "foo~0Abar~0D"
2
    encode_binary("foo", 10, "bar", 13)       =>   "foo~0Abar~0D"
35
5
4
7
2
Syntax:  equal (<value1>, <value2>)   => int
2

2
Returns true if <value1> is completely indistinguishable from <value2>.  This is much the same operation as ```<value1> == <value2>''' except that, unlike `==', the `equal()' function does not treat upper- and lower-case characters in strings as equal.
2

2
    "Foo" == "foo"         =>   1
2
    equal("Foo", "foo")    =>   0
2
    equal("Foo", "Foo")    =>   1
35
5
4
23
2
Syntax:  eval (str <string>)   => list
2

2
The MOO-code compiler processes <string> as if it were to be the program associated with some verb and, if no errors are found, that fictional verb is invoked.  If the programmer is not, in fact, a programmer, then `E_PERM' is raised.  The normal result of calling `eval()' is a two element list.  The first element is true if there were no compilation errors and false otherwise.  The second element is either the result returned from the fictional verb (if there were no compilation errors) or a list of the compiler's error messages (otherwise).
2

2
When the fictional verb is invoked, the various built-in variables have values as shown below:
2

2
    player    the same as in the calling verb
2
    this      #-1
2
    caller    the same as the initial value of `this' in the calling verb
2

2
    args      {}
2
    argstr    ""
2

2
    verb      ""
2
    dobjstr   ""
2
    dobj      #-1
2
    prepstr   ""
2
    iobjstr   ""
2
    iobj      #-1
2

2
The fictional verb runs with the permissions of the programmer and as if its `d' permissions bit were on.
2

2
    eval("return 3 + 4;")   =>   {1, 7}
35
5
4
3
2
Syntax:  exp (float <x>)   => float
2

2
Returns <e> raised to the power of <x>.
35
5
4
3
2
Syntax:  floatstr(float <x>, int <precision> [, <scientific>])   => str
2

2
Converts <x> into a string with more control than provided by either `tostr()' or `toliteral()'.  <Precision> is the number of digits to appear to the right of the decimal point, capped at 4 more than the maximum available precision, a total of 19 on most machines; this makes it possible to avoid rounding errors if the resulting string is subsequently read back as a floating-point value.  If <scientific> is false or not provided, the result is a string in the form `"MMMMMMM.DDDDDD"', preceded by a minus sign if and only if <x> is negative.  If <scientific> is provided and true, the result is a string in the form `"M.DDDDDDe+EEE"', again preceded by a minus sign if and only if <x> is negative.
35
5
4
3
2
Syntax:  floor (float <x>)   => float
2

2
Returns the largest integer not greater than <x>, as a floating-point number.
35
5
4
3
2
Syntax:  flush_input (obj <conn> [<show-messages>])   => none
2

2
Performs the same actions as if the connection <conn>'s defined flush command had been received on that connection, i.e., removes all pending lines of input from <conn>'s queue and, if <show-messages> is provided and true, prints a message to <conn> listing the flushed lines, if any.  See the chapter on server assumptions about the database for more information about a connection's defined flush command.
35
5
4
3
2
Syntax:  force_input (obj <conn>, str <line> [, <at-front>])   => none
2

2
Inserts the string <line> as an input task in the queue for the connection <conn>, just as if it had arrived as input over the network.  If <at_front> is provided and true, then the new line of input is put at the front of <conn>'s queue, so that it will be the very next line of input processed even if there is already some other input in that queue.  Raises `E_PERM' if the programmer is neither <conn> nor a wizard.
35
5
4
17
2
Syntax:  function_info ([str <name>])   => list
2

2
Returns descriptions of the built-in functions available on the server.  If <name> is provided, only the description of the function with that name is returned.  If <name> is omitted, a list of descriptions is returned, one for each function available on the server.  Raised `E_INVARG' if <name> is provided but no function with that name is available on the server.
2

2
Each function description is a list of the following form:
2

2
    {<name>, <min-args>, <max-args>, <types>
2

2
where <name> is the name of the built-in function, <min-args> is the minimum number of arguments that must be provided to the function, <max-args> is the maximum number of arguments that can be provided to the function or `-1' if there is no maximum, and <types> is a list of <max-args> integers (or <min-args> if <max-args> is `-1'), each of which represents the type of argument required in the corresponding position.  Each type number is as would be returned from the `typeof()' built-in function except that `-1' indicates that any type of value is acceptable and `-2' indicates that either integers or floating-point numbers may be given.  For example, here are several entries from the list:
2

2
    {"listdelete", 2, 2, {4, 0}}
2
    {"suspend", 0, 1, {0}}
2
    {"server_log", 1, 2, {2, -1}}
2
    {"max", 1, -1, {-2}}
2
    {"tostr", 0, -1, {}}
2

2
`Listdelete()' takes exactly 2 arguments, of which the first must be a list (`LIST == 4') and the second must be an integer (`INT == 0').  `Suspend()' has one optional argument that, if provided, must be an integer.  `Server_log()' has one required argument that must be a string (`STR == 2') and one optional argument that, if provided, may be of any type.  `Max()' requires at least one argument but can take any number above that, and the first argument must be either an integer or a floating-point number; the type(s) required for any other arguments can't be determined from this description.  Finally, `tostr()' takes any number of arguments at all, but it can't be determined from this description which argument types would be acceptable in which positions.
35
5
4
2
2
*forward*
2
connected_seconds()
35
5
4
10
2
Syntax:  index (str <str1>, str <str2> [, <case-matters>])   => int
2
        rindex (str <str1>, str <str2> [, <case-matters>])   => int
2

2
The function `index()' (`rindex()') returns the index of the first character of the first (last) occurrence of <str2> in <str1>, or zero if <str2> does not occur in <str1> at all.  By default the search for an occurrence of <str2> is done while ignoring the upper/lower case distinction.  If <case-matters> is provided and true, then case is treated as significant in all comparisons.
2

2
    index("foobar", "o")        =>   2
2
    rindex("foobar", "o")       =>   3
2
    index("foobar", "x")        =>   0
2
    index("foobar", "oba")      =>   3
2
    index("Foobar", "foo", 1)   =>   0
35
5
4
4
2
Syntax:  is_clear_property (obj <object>, str <prop-name>)   => int
2
            clear_property (obj <object>, str <prop-name>)   => none
2

2
These two functions test for clear and set to clear, respectively, the property named <prop-name> on the given <object>.  If <object> is not valid, then `E_INVARG' is raised.  If <object> has no non-built-in property named <prop-name>, then `E_PROPNF' is raised.  If the programmer does not have read (write) permission on the property in question, then `is_clear_property()' (`clear_property()') raises `E_PERM'.  If a property is clear, then when the value of that property is queried the value of the parent's property of the same name is returned.  If the parent's property is clear, then the parent's parent's value is examined, and so on.  If <object> is the definer of the property <prop-name>, as opposed to an inheritor of the property, then `clear_property()' raises `E_INVARG'.
35
5
4
7
2
Syntax:  is_member (<value>, list <list>)   => int
2

2
Returns true if there is an element of <list> that is completely indistinguishable from <value>.  This is much the same operation as ```<value> in <list>''' except that, unlike `in', the `is_member()' function does not treat upper- and lower-case characters in strings as equal.
2

2
    "Foo" in {1, "foo", #24}            =>   2
2
    is_member("Foo", {1, "foo", #24})   =>   0
2
    is_member("Foo", {1, "Foo", #24})   =>   2
35
5
4
3
2
Syntax:  is_player (obj <object>)   => int
2

2
Returns a true value if the given <object> is a player object and a false value otherwise.  If <object> is not valid, `E_INVARG' is raised.
35
5
4
3
2
Syntax:  kill_task (int <task-id>)   => none
2

2
Removes the task with the given <task-id> from the queue of waiting tasks.  If the programmer is not the owner of that task and not a wizard, then `E_PERM' is raised.  If there is no task on the queue with the given <task-id>, then `E_INVARG' is raised.
35
5
4
12
2
Syntax:  length (str <string>)  => int
2
         length (list <list>)   => int
2

2
The first form returns the number of characters in <string>.
2

2
    length("foo")   =>   3
2
    length("")      =>   0
2

2
The second form returns the number of elements in <list>.
2

2
    length({1, 2, 3})   =>   3
2
    length({})          =>   0
35
5
4
2
2
*forward*
2
listinsert()
35
5
4
6
2
Syntax:  listdelete (list <list>, int <index>)   => list
2

2
Returns a copy of <list> with the <index>th element removed.  If <index> is not in the range `[1..length(<list>)]', then `E_RANGE' is raised.
2

2
    x = {"foo", "bar", "baz"};
2
    listdelete(x, 2)   =>   {"foo", "baz"}
35
5
4
11
2
Syntax:  listen (obj <object>, <point> [, <print-messages>])   => value
2

2
Create a new point at which the server will listen for network connections, just as it does normally.  <Object> is the object whose verbs `do_login_command', `do_command', `do_out_of_band_command', `user_connected', `user_created', `user_reconnected', `user_disconnected', and `user_client_disconnected' will be called at appropriate points, just as these verbs are called on `#0' for normal connections.  (See the chapter on server assumptions about the database for the complete story on when these functions are called.)  <Point> is a network-configuration-specific parameter describing the listening point.  If <print-messages> is provided and true, then the various database-configurable messages (also detailed in the chapter on server assumptions) will be printed on connections received at the new listening point.  `Listen()' returns <canon>, a `canonicalized' version of <point>, with any configuration-specific defaulting or aliasing accounted for.
2

2
This raises `E_PERM' if the programmer is not a wizard, `E_INVARG' if <object> is invalid or there is already a listening point described by <point>, and `E_QUOTA' if some network-configuration-specific error occurred.
2

2
For the TCP/IP configurations, <point> is a TCP port number on which to listen and <canon> is equal to <point> unless <point> is zero, in which case <canon> is a port number assigned by the operating system.
2

2
For the local multi-user configurations, <point> is the UNIX file name to be used as the connection point and <canon> is always equal to <point>.
2

2
In the single-user configuration, the can be only one listening point at a time; <point> can be any value at all and <canon> is always zero.
35
5
4
7
2
Syntax:  listeners ()   => list
2

2
Returns a list describing all existing listening points, including the default one set up automatically by the server when it was started (unless that one has since been destroyed by a call to `unlisten()').  Each element of the list has the following form:
2

2
    {<object>, <canon>, <print-messages>}
2

2
where <object> is the first argument given in the call to `listen()' to create this listening point, <print-messages> is true if the third argument in that call was provided and true, and <canon> was the value returned by that call.  (For the initial listening point, <object> is `#0', <canon> is determined by the command-line arguments or a network-configuration-specific default, and <print-messages> is true.)
35
5
4
20
2
Syntax:  listinsert (list <list>, <value> [, int <index>])   => list
2
         listappend (list <list>, <value> [, int <index>])   => list
2

2
These functions return a copy of <list> with <value> added as a new element.  `listinsert()' and `listappend()' add <value> before and after (respectively) the existing element with the given <index>, if provided.
2

2
The following three expressions always have the same value:
2

2
    listinsert(<list>, <element>, <index>)
2
    listappend(<list>, <element>, <index> - 1)
2
    {@<list>[1..<index> - 1], <element>, @<list>[<index>..length(<list>)]}
2

2
If <index> is not provided, then `listappend()' adds the <value> at the end of the list and `listinsert()' adds it at the beginning; this usage is discouraged, however, since the same intent can be more clearly expressed using the list-construction expression, as shown in the examples below.
2

2
    x = {1, 2, 3};
2
    listappend(x, 4, 2)   =>   {1, 2, 4, 3}
2
    listinsert(x, 4, 2)   =>   {1, 4, 2, 3}
2
    listappend(x, 4)      =>   {1, 2, 3, 4}
2
    listinsert(x, 4)      =>   {4, 1, 2, 3}
2
    {@x, 4}               =>   {1, 2, 3, 4}
2
    {4, @x}               =>   {4, 1, 2, 3}
35
5
4
8
2
Syntax:  listset (list <list>, <value>, int <index>)   => list
2

2
Returns a copy of <list> with the <index>th element replaced by <value>.  If <index> is not in the range `[1..length(<list>)]', then `E_RANGE' is raised.
2

2
    x = {"foo", "bar", "baz"};
2
    listset(x, "mumble", 2)   =>   {"foo", "mumble", "baz"}
2

2
This function exists primarily for historical reasons; it was used heavily before the server supported indexed assignments like `x[i] = v'.  New code should always use indexed assignment instead of `listset()' wherever possible.
35
5
4
4
2
Syntax:  log (float <x>)   => float
2
       log10 (float <x>)   => float
2

2
Returns the natural or base 10 logarithm of <x>.  Raises `E_INVARG' if <x> is not positive.
35
5
4
2
2
*forward*
2
log()
35
5
4
23
2
Syntax:  match (str <subject>, str <pattern> [, <case-matters>])   => list
2
        rmatch (str <subject>, str <pattern> [, <case-matters>])   => list
2

2
The function `match()' (`rmatch()') searches for the first (last) occurrence of the regular expression <pattern> in the string <subject>.  If <pattern> is syntactically malformed, then `E_INVARG' is raised.  The process of matching can in some cases consume a great deal of memory in the server; should this memory consumption become excessive, then the matching process is aborted and `E_QUOTA' is raised.
2

2
If no match is found, the empty list is returned; otherwise, these functions return a list containing information about the match (see below).  By default, the search ignores upper-/lower-case distinctions.  If <case-matters> is provided and true, then case is treated as significant in all comparisons.
2

2
The list that `match()' (`rmatch()') returns contains the details about the match made.  The list is in the form:
2

2
    {<start>, <end>, <replacements>, <subject>}
2

2
where <start> is the index in <subject> of the beginning of the match, <end> is the index of the end of the match, <replacements> is a list described below, and <subject> is the same string that was given as the first argument to the `match()' or `rmatch()'.
2

2
The <replacements> list is always nine items long, each item itself being a list of two integers, the start and end indices in <string> matched by some parenthesized sub-pattern of <pattern>.  The first item in <replacements> carries the indices for the first parenthesized sub-pattern, the second item carries those for the second sub-pattern, and so on.  If there are fewer than nine parenthesized sub-patterns in <pattern>, or if some sub-pattern was not used in the match, then the corresponding item in <replacements> is the list {0, -1}.  See the discussion of `%)' in `help regular-expressions', for more information on parenthesized sub-patterns.
2

2
    match("foo", "^f*o$")        =>  {}
2
    match("foo", "^fo*$")        =>  {1, 3, {{0, -1}, ...}, "foo"}
2
    match("foobar", "o*b")       =>  {2, 4, {{0, -1}, ...}, "foobar"}
2
    rmatch("foobar", "o*b")      =>  {4, 4, {{0, -1}, ...}, "foobar"}
2
    match("foobar", "f%(o*%)b")
2
            =>  {1, 4, {{2, 3}, {0, -1}, ...}, "foobar"}
2

2
See `help regular-expressions' for information on the syntax and semantics of patterns.
35
5
4
2
2
*forward*
2
min()
35
5
4
3
2
Syntax:  max_object ()   => obj
2

2
Returns the largest object number yet assigned to a created object.  Note that the object with this number may no longer exist; it may have been recycled.  The next object created will be assigned the object number one larger than the value of `max_object()'.
35
5
4
9
2
Syntax:  memory_usage ()   => list
2

2
On some versions of the server, this returns statistics concerning the server consumption of system memory.  The result is a list of lists, each in the following format:
2

2
    {<block-size>, <nused>, <nfree>}
2

2
where <block-size> is the size in bytes of a particular class of memory fragments, <nused> is the number of such fragments currently in use in the server, and <nfree> is the number of such fragments that have been reserved for use but are currently free.
2

2
On servers for which such statistics are not available, `memory_usage()' returns `{}'.  The compilation option `USE_GNU_MALLOC' controls whether or not statistics are available; if the option is not provided, statistics are not available.
35
5
4
4
2
Syntax:  min (num <x>, ...)   => num
2
         max (num <x>, ...)   => num
2

2
These two functions return the smallest or largest of their arguments, respectively.  All of the arguments must be numbers of the same kind (i.e., either integer or floating-point); otherwise `E_TYPE' is raised.
35
5
4
23
2
Syntax:  move (obj <what>, obj <where>)   => none
2

2
Changes <what>'s location to be <where>.  This is a complex process because a number of permissions checks and notifications must be performed.  The actual movement takes place as described in the following paragraphs.
2

2
<what> should be a valid object and <where> should be either a valid object or `#-1' (denoting a location of `nowhere'); otherwise `E_INVARG' is raised.  The programmer must be either the owner of <what> or a wizard; otherwise, `E_PERM' is raised.
2

2
If <where> is a valid object, then the verb-call
2

2
    <where>:accept(<what>)
2

2
is performed before any movement takes place.  If the verb returns a false value and the programmer is not a wizard, then <where> is considered to have refused entrance to <what>; `move()' raises `E_NACC'.  If <where> does not define an `accept' verb, then it is treated as if it defined one that always returned false.
2

2
If moving <what> into <where> would create a loop in the containment hierarchy (i.e., <what> would contain itself, even indirectly), then `E_RECMOVE' is raised instead.
2

2
The `location' property of <what> is changed to be <where>, and the `contents' properties of the old and new locations are modified appropriately.  Let <old-where> be the location of <what> before it was moved.  If <old-where> is a valid object, then the verb-call
2

2
    <old-where>:exitfunc(<what>)
2

2
is performed and its result is ignored; it is not an error if <old-where> does not define a verb named `exitfunc'.  Finally, if <where> and <what> are still valid objects, and <where> is still the location of <what>, then the verb-call
2

2
    <where>:enterfunc(<what>)
2

2
is performed and its result is ignored; again, it is not an error if <where> does not define a verb named `enterfunc'.
35
5
4
7
2
Syntax:  notify (obj <conn>, str <string> [, <no-flush>])   => none
2

2
Enqueues <string> for output (on a line by itself) on the connection <conn>.  If the programmer is not <conn> or a wizard, then `E_PERM' is raised.  If <conn> is not a currently-active connection, then this function does nothing.  Output is normally written to connections only between tasks, not during execution.
2

2
The server will not queue an arbitrary amount of output for a connection; the `MAX_QUEUED_OUTPUT' compilation option (in `options.h') controls the limit.  When an attempt is made to enqueue output that would take the server over its limit, it first tries to write as much output as possible to the connection without having to wait for the other end.  If that doesn't result in the new output being able to fit in the queue, the server starts throwing away the oldest lines in the queue until the new ouput will fit.  The server remembers how many lines of output it has `flushed' in this way and, when next it can succeed in writing anything to the connection, it first writes a line like `>> Network buffer overflow: <X> lines of output to you have been lost <<' where <X> is the number of flushed lines.
2

2
If <no-flush> is provided and true, then `notify()' never flushes any output from the queue; instead it immediately returns false.  `Notify()' otherwise always returns true.
35
5
4
3
2
Syntax:  object_bytes (obj <object>)   => int
2

2
Returns the number of bytes of the server's memory required to store the given <object>, including the space used by the values of all of its non-clear properties and by the verbs and properties defined directly on the object.  Raised `E_INVARG' if <object> is not a valid object and `E_PERM' if the programmer is not a wizard.
35
5
4
11
2
Syntax:  open_network_connection (<value>, ...)   => obj
2

2
Establishes a network connection to the place specified by the arguments and more-or-less pretends that a new, normal player connection has been established from there.  The new connection, as usual, will not be logged in initially and will have a negative object number associated with it for use with `read()', `notify()', and `boot_player()'.  This object number is the value returned by this function.
2

2
If the programmer is not a wizard or if the `OUTBOUND_NETWORK' compilation option was not used in building the server, then `E_PERM' is raised.  If the network connection cannot be made for some reason, then other errors will be returned, depending upon the particular network implementation in use.
2

2
For the TCP/IP network implementations (the only ones as of this writing that support outbound connections), there must be two arguments, a string naming a host (possibly using the numeric Internet syntax) and an integer specifying a TCP port.  If a connection cannot be made because the host does not exist, the port does not exist, the host is not reachable or refused the connection, `E_INVARG' is raised.  If the connection cannot be made for other reasons, including resource limitations, then `E_QUOTA' is raised.
2

2
The outbound connection process involves certain steps that can take quite a long time, during which the server is not doing anything else, including responding to user commands and executing MOO tasks.  See the chapter on server assumptions about the database for details about how the server limits the amount of time it will wait for these steps to successfully complete.
2

2
It is worth mentioning one tricky point concerning the use of this function.  Since the server treats the new connection pretty much like any normal player connection, it will naturally try to parse any input from that connection as commands in the usual way.  To prevent this treatment, you should use `set_connection_option()' to set the `"hold-input"' option true on the connection.
35
5
4
3
2
Syntax:  output_delimiters (obj <player>)   => list
2

2
Returns a list of two strings, the current "output prefix" and "output suffix" for <player>.  If <player> does not have an active network connection, then `E_INVARG' is raised.  If either string is currently undefined, the value `""' is used instead.  See the discussion of the `PREFIX' and `SUFFIX' commands in the next chapter for more information about the output prefix and suffix.
35
5
4
4
2
Syntax:  parent (obj <object>)   => obj
2
       children (obj <object>)   => list
2

2
These functions return the parent and a list of the children of <object>, respectively.  If <object> is not valid, then `E_INVARG' is raised.
35
5
4
13
2
Syntax:  pass (<arg>, ...)   => value
2

2
Often, it is useful for a child object to define a verb that *augments* the behavior of a verb on its parent object.  For example, in the LambdaCore database, the root object (which is an ancestor of every other object) defines a verb called `description' that simply returns the value of `this.description'; this verb is used by the implementation of the `look' command.  In many cases, a programmer would like the description of some object to include some non-constant part; for example, a sentence about whether or not the object was `awake' or `sleeping'.  This sentence should be added onto the end of the normal description.  The programmer would like to have a means of calling the normal `description' verb and then appending the sentence onto the end of that description.  The function `pass()' is for exactly such situations.
2

2
`pass' calls the verb with the same name as the current verb but as defined on the parent of the object that defines the current verb.  The arguments given to `pass' are the ones given to the called verb and the returned value of the called verb is returned from the call to `pass'.  The initial value of `this' in the called verb is the same as in the calling verb.
2

2
Thus, in the example above, the child-object's `description' verb might have the following implementation:
2

2
    return pass() + "  It is " + (this.awake ? "awake." | "sleeping.");
2

2
That is, it calls its parent's `description' verb and then appends to the result a sentence whose content is computed based on the value of a property on the object.
2

2
In almost all cases, you will want to call `pass()' with the same arguments as were given to the current verb.  This is easy to write in MOO; just call `pass(@args)'.
35
5
4
3
2
Syntax:  players ()   => list
2

2
Returns a list of the object numbers of all player objects in the database.
35
5
4
3
2
Syntax:  properties (obj <object>)   => list
2

2
Returns a list of the names of the properties defined directly on the given <object>, not inherited from its parent.  If <object> is not valid, then `E_INVARG' is raised.  If the programmer does not have read permission on <object>, then `E_PERM' is raised.
35
5
4
8
2
Syntax:  property_info (obj <object>, str <prop-name>)              => list
2
     set_property_info (obj <object>, str <prop-name>, list <info>) => none
2

2
These two functions get and set (respectively) the owner and permission bits for the property named <prop-name> on the given <object>.  If <object> is not valid, then `E_INVARG' is raised.  If <object> has no non-built-in property named <prop-name>, then `E_PROPNF' is raised.  If the programmer does not have read (write) permission on the property in question, then `property_info()' (`set_property_info()') raises `E_PERM'.  Property info has the following form:
2

2
    {<owner>, <perms> [, <new-name>]}
2

2
where <owner> is an object, <perms> is a string containing only characters from the set `r', `w', and `c', and <new-name> is a string; <new-name> is never part of the value returned by `property_info()', but it may optionally be given as part of the value provided to `set_property_info()'.  This list is the kind of value returned by `property_info()' and expected as the third argument to `set_property_info()'; the latter function raises `E_INVARG' if <owner> is not valid, if <perms> contains any illegal characters, or, when <new-name> is given, if <prop-name> is not defined directly on <object> or <new-name> names an existing property defined on <object> or any of its ancestors or descendants.
35
5
4
3
2
Syntax:  queue_info ([obj <player>])   => list
2

2
If <player> is omitted, returns a list of object numbers naming all players that currently have active task queues inside the server.  If <player> is provided, returns the number of background tasks currently queued for that user.  It is guaranteed that `queue_info(<X>)' will return zero for any <X> not in the result of `queue_info()'.
35
5
4
10
2
Syntax:  queued_tasks ()   => list
2

2
Returns information on each of the background tasks (i.e., forked, suspended or reading) owned by the programmer (or, if the programmer is a wizard, all queued tasks).  The returned value is a list of lists, each of which encodes certain information about a particular queued task in the following format:
2

2
    {<task-id>, <start-time>, <x>, <y>,
2
     <programmer>, <verb-loc>, <verb-name>, <line>, <this>}
2

2
where <task-id> is an integer identifier for this queued task, <start-time> is the time after which this task will begin execution (in `time()' format), <x> and <y> are obsolete values that are no longer interesting, <programmer> is the permissions with which this task will begin execution (and also the player who "owns" this task), <verb-loc> is the object on which the verb that forked this task was defined at the time, <verb-name> is that name of that verb, <line> is the number of the first line of the code in that verb that this task will execute, and <this> is the value of the variable `this' in that verb.  For reading tasks, <start-time> is `-1'.
2

2
The <x> and <y> fields are now obsolete and are retained only for backward-compatibility reasons.  They may be reused for new purposes in some future version of the server.
35
5
4
3
2
Syntax:  raise (<code> [, str <message> [, <value>]])   => none
2

2
Raises <code> as an error in the same way as other MOO expressions, statements, and functions do.  <Message>, which defaults to the value of `tostr(<code>)', and <value>, which defaults to zero, are made available to any `try'-`except' statements that catch the error.  If the error is not caught, then <message> will appear on the first line of the traceback printed to the user.
35
5
4
3
2
Syntax:  random ([int <mod>])   => int
2

2
<mod> must be a positive integer; otherwise, `E_INVARG' is raised.  An integer is chosen randomly from the range `[1..<mod>]' and returned.  If <mod> is not provided, it defaults to the largest MOO integer, 2147483647.
35
5
4
11
2
Syntax:  read ([obj <conn> [, <non-blocking>]])   => str
2

2
Reads and returns a line of input from the connection <conn> (or, if not provided, from the player that typed the command that initiated the current task).  If <non-blocking> is false or not provided, this function suspends the current task, resuming it when there is input available to be read.  If <non-blocking> is provided and true, this function never suspends the calling task; if there is no input currently available for input, `read()' simply returns 0 immediately.
2

2
If <player> is provided, then the programmer must either be a wizard or the owner of `player'; if `player' is not provided, then `read()' may only be called by a wizard and only in the task that was last spawned by a command from the connection in question.  Otherwise, `E_PERM' is raised.  If the given `player' is not currently connected and has no pending lines of input, or if the connection is closed while a task is waiting for input but before any lines of input are received, then `read()' raises `E_INVARG'.
2

2
The restriction on the use of `read()' without any arguments preserves the following simple invariant: if input is being read from a player, it is for the task started by the last command that player typed.  This invariant adds responsibility to the programmer, however.  If your program calls another verb before doing a `read()', then either that verb must not suspend or else you must arrange that no commands will be read from the connection in the meantime.  The most straightforward way to do this is to call
2
    set_connection_option(player, "hold-input", 1)
2
before any task suspension could happen, then make all of your calls to `read()' and other code that might suspend, and finally call
2
    set_connection_option(player, "hold-input", 0)
2
to allow commands once again to be read and interpreted normally.
35
5
4
5
2
Syntax:  recycle (obj <object>)   => none
2

2
The given <object> is destroyed, irrevocably.  The programmer must either own <object> or be a wizard; otherwise, `E_PERM' is raised.  If <object> is not valid, then `E_INVARG' is raised.  The children of <object> are reparented to the parent of <object>.  Before <object> is recycled, each object in its contents is moved to `#-1' (implying a call to <object>'s `exitfunc' verb, if any) and then <object>'s `recycle' verb, if any, is called with no arguments.
2

2
After <object> is recycled, if the owner of the former object has a property named `ownership_quota' and the value of that property is a integer, then `recycle()' treats that value as a "quota" and increments it by one, storing the result back into the `ownership_quota' property.
35
5
4
7
2
Syntax:  renumber (obj <object>)   => obj
2

2
The object number of the object currently numbered <object> is changed to be the least nonnegative object number not currently in use and the new object number is returned.  If <object> is not valid, then `E_INVARG' is raised.  If the programmer is not a wizard, then `E_PERM' is raised.  If there are no unused nonnegative object numbers less than <object>, then <object> is returned and no changes take place.
2

2
The references to <object> in the parent/children and location/contents hierarchies are updated to use the new object number, and any verbs, properties and/or objects owned by <object> are also changed to be owned by the new object number.  The latter operation can be quite time consuming if the database is large.  No other changes to the database are performed; in particular, no object references in property values or verb code are updated.
2

2
This operation is intended for use in making new versions of the LambdaCore database from the then-current LambdaMOO database, and other similar situations.  Its use requires great care.
35
5
4
5
2
Syntax:  reset_max_object ()   => none
2

2
The server's idea of the highest object number ever used is changed to be the highest object number of a currently-existing object, thus allowing reuse of any higher numbers that refer to now-recycled objects.  If the programmer is not a wizard, then `E_PERM' is raised.
2

2
This operation is intended for use in making new versions of the LambdaCore database from the then-current LambdaMOO database, and other similar situations.  Its use requires great care.
35
5
4
3
2
Syntax:  resume (int <task-id> [, <value>])   => none
2

2
Immediately ends the suspension of the suspended task with the given <task-id>; that task's call to `suspend()' will return <value>, which defaults to zero.  `Resume()' raises `E_INVARG' if <task-id> does not specify an existing suspended task and `E_PERM' if the programmer is neither a wizard nor the owner of the specified task.
35
5
4
2
2
*forward*
2
index()
35
5
4
2
2
*forward*
2
match()
35
5
4
2
2
*forward*
2
ticks_left()
35
5
4
3
2
Syntax:  server_log (str <message> [, <is-error>])   => none
2

2
The text in <message> is sent to the server log with a distinctive prefix (so that it can be distinguished from server-generated messages).  If the programmer is not a wizard, then `E_PERM' is raised.  If <is-error> is provided and true, then <message> is marked in the server log as an error.
35
5
4
3
2
Syntax:  server_version ()   => str
2

2
Returns a string giving the version number of the running MOO server.
35
5
4
23
2
Syntax:  set_connection_option (obj <conn>, str <option>, <value>)   => none
2

2
Controls a number of optional behaviors associated the connection <conn>.  Raises `E_INVARG' if <conn> does not specify a current connection and `E_PERM' if the programmer is neither <conn> nor a wizard.  The following values for <option> are currently supported:
2

2
"hold-input"
2
2===========
2

2
If <value> is true, then input received on <conn> will never be treated as a command; instead, it will remain in the queue until retrieved by a call to `read()'.
2

2
"client-echo"
2
2============
2

2
Send the Telnet Protocol `WONT ECHO' or `WILL ECHO' command, depending on whether <value> is true or false, respectively.  For clients that support the Telnet Protocol, this should toggle whether or not the client echoes locally the characters typed by the user.  Note that the server itself never echoes input characters under any circumstances.  (This option is only available under the TCP/IP networking configurations.)
2

2
"binary"
2
2=======
2

2
If <value> is true, then both input from and output to <conn> can contain arbitrary bytes.  Input from a connection in binary mode is not broken into lines at all; it is delivered to either the read() function or the built-in command parser as "binary strings", in whatever size chunks come back from the operating system.  (See the early section on MOO value types for a description of the binary string representation.)  For output to a connection in binary mode, the second argument to `notify()' must be a binary string; if it is malformed, E_INVARG is raised.
2

2
"flush-command"
2
2==============
2

2
If <value> is a non-empty string, then it becomes the new "flush" command for this connection, by which the player can flush all queued input that has not yet been processed by the server.  If <value> is not a non-empty string, then <conn> is set to have no flush command at all.  The default value of this option can be set via the property `$server_options.default_flush_command'; see the chapter on server assumptions about the database for details.
35
5
4
7
2
Syntax:  set_player_flag (obj <object>, <value>)   => none
2

2
Confers or removes the ``player object'' status of the given <object>, depending upon the truth value of <value>.  If <object> is not valid, `E_INVARG' is raised.  If the programmer is not a wizard, then `E_PERM' is raised.
2

2
If <value> is true, then <object> gains (or keeps) ``player object'' status: it will be an element of the list returned by `players()', the expression `is_player(<object>)' will return true, and the server will treat a call to `$do_login_command()' that returns <object> as logging in the current connection.
2

2
If <value> is false, the <object> loses (or continues to lack) ``player object'' status: it will not be an element of the list returned by `players()', the expression `is_player(<object>)' will return false, and users cannot connect to <object> by name when they log into the server.  In addition, if a user is connected to <object> at the time that it loses ``player object'' status, then that connection is immediately broken, just as if `boot_player(<object>)' had been called (see the description of `boot_player()' below).
35
5
4
2
2
*forward*
2
property_info()
35
5
4
5
2
Syntax:  set_task_perms (obj <who>) => none
2

2
Changes the permissions with which the currently-executing verb is running to be those of <who>.  If the programmer is neither <who> nor a wizard, then `E_PERM' is raised.
2

2
*NOTE*:  This does not change the owner of the currently-running verb, only the permissions of this particular invocation.  It is used in verbs owned by wizards to make themselves run with lesser (usually non-wizard) permissions.
35
5
4
2
2
*forward*
2
verb_args()
35
5
4
2
2
*forward*
2
verb_code()
35
5
4
2
2
*forward*
2
verb_info()
35
5
4
10
2
Syntax:  setadd (list <list>, <value>)   => list
2
      setremove (list <list>, <value>)   => list
2

2
Returns a copy of <list> with the given <value> added or removed, as appropriate.  `setadd()' only adds <value> if it is not already an element of <list>; <list> is thus treated as a mathematical set.  <value> is added at the end of the resulting list, if at all.  Similarly, `setremove()' returns a list identical to <list> if <value> is not an element.  If <value> appears more than once in <list>, only the first occurrence is removed in the returned copy.
2

2
    setadd({1, 2, 3}, 3)         =>   {1, 2, 3}
2
    setadd({1, 2, 3}, 4)         =>   {1, 2, 3, 4}
2
    setremove({1, 2, 3}, 3)      =>   {1, 2}
2
    setremove({1, 2, 3}, 4)      =>   {1, 2, 3}
2
    setremove({1, 2, 3, 2}, 2)   =>   {1, 3, 2}
35
5
4
2
2
*forward*
2
setadd()
35
5
4
3
2
Syntax:  shutdown ([str <message>])   => none
2

2
Requests that the server shut itself down at its next opportunity.  Before doing so, a notice (incorporating <message>, if provided) is printed to all connected players.  If the programmer is not a wizard, then `E_PERM' is raised.
35
5
4
5
2
Syntax:  sin (float <x>)   => float
2
         cos (float <x>)   => float
2
         tan (float <x>)   => float
2

2
Returns the sine, cosine, or tangent of <x>, respectively.
35
5
4
5
2
Syntax:  sinh (float <x>)   => float
2
         cosh (float <x>)   => float
2
         tanh (float <x>)   => float
2

2
Returns the hyperbolic sine, cosine, or tangent of <x>, respectively.
35
5
4
3
2
Syntax:  sqrt (float <x>)   => float
2

2
Returns the square root of <x>.  Raises `E_INVARG' if <x> is negative.
35
5
4
3
2
Syntax:  strcmp (str <str1>, str <str2>)   => int
2

2
Performs a case-sensitive comparison of the two argument strings.  If <str1> is lexicographically less than <str2>, the `strcmp()' returns a negative integer.  If the two strings are identical, `strcmp()' returns zero.  Otherwise, `strcmp()' returns a positive integer.  The ASCII character ordering is used for the comparison.
35
5
4
8
2
Syntax:  string_hash (str <text>)       => str
2
         binary_hash (str <bin-string>) => str
2

2
Returns a 32-character hexadecimal string encoding the result of applying the MD5 cryptographically secure hash function to the contents of the string <text> or the binary string <bin-string>.  MD5, like other such functions, has the property that, if
2
    string_hash(<x>) == string_hash(<y>)
2
then, almost certainly,
2
    equal(<x>, <y>)
2
This can be useful, for example, in certain networking applications: after sending a large piece of text across a connection, also send the result of applying `string_hash()' to the text; if the destination site also applies `string_hash()' to the text and gets the same result, you can be quite confident that the large text has arrived unchanged.
35
5
4
7
2
Syntax:  strsub (str <subject>, str <what>, str <with> [, <case-matters>])   => str
2

2
Replaces all occurrences in <subject> of <what> with <with>, performing string substitution.  The occurrences are found from left to right and all substitutions happen simultaneously.  By default, occurrences of <what> are searched for while ignoring the upper/lower case distinction.  If <case-matters> is provided and true, then case is treated as significant in all comparisons.
2

2
    strsub("%n is a fink.", "%n", "Fred")   =>   "Fred is a fink."
2
    strsub("foobar", "OB", "b")             =>   "fobar"
2
    strsub("foobar", "OB", "b", 1)          =>   "foobar"
35
5
4
9
2
Syntax:  substitute (str <template>, list <subs>)   => str
2

2
Performs a standard set of substitutions on the string <template>, using the information contained in <subs>, returning the resulting, transformed <template>.  <Subs> should be a list like those returned by `match()' or `rmatch()' when the match succeeds; otherwise, `E_INVARG' is raised.
2

2
In <template>, the strings `%1' through `%9' will be replaced by the text matched by the first through ninth parenthesized sub-patterns when `match()' or `rmatch()' was called.  The string `%0' in <template> will be replaced by the text matched by the pattern as a whole when `match()' or `rmatch()' was called.  The string `%%' will be replaced by a single `%' sign.  If `%' appears in <template> followed by any other character, `E_INVARG' will be raised.
2

2
    subs = match("*** Welcome to LambdaMOO!!!", "%(%w*%) to %(%w*%)");
2
    substitute("I thank you for your %1 here in %2.", subs)
2
            =>   "I thank you for your Welcome here in LambdaMOO."
35
5
4
36
2
Syntax:  suspend ([int <seconds>])   => value
2

2
Suspends the current task, and resumes it after at least <seconds> seconds.  (If <seconds> is not provided, the task is suspended indefinitely; such a task can only be resumed by use of the `resume()' function.)  When the task is resumed, it will have a full quota of ticks and seconds.  This function is useful for programs that run for a long time or require a lot of ticks.  If <seconds> is negative, then `E_INVARG' is raised.  `Suspend()' returns zero unless it was resumed via `resume()', in which case it returns the second argument given to that function.
2

2
In some sense, this function forks the `rest' of the executing task.  However, there is a major difference between the use of `suspend(<seconds>)' and the use of the `fork (<seconds>)'.  The `fork' statement creates a new task (a "forked task") while the currently-running task still goes on to completion, but a `suspend()' suspends the currently-running task (thus making it into a "suspended task").  This difference may be best explained by the following examples, in which one verb calls another:
2

2
    .program   #0:caller_A
2
    #0.prop = 1;
2
    #0:callee_A();
2
    #0.prop = 2;
2
    .
2

2
    .program   #0:callee_A
2
    fork(5)
2
      #0.prop = 3;
2
    endfork
2
    .
2

2
    .program   #0:caller_B
2
    #0.prop = 1;
2
    #0:callee_B();
2
    #0.prop = 2;
2
    .
2

2
    .program   #0:callee_B
2
    suspend(5);
2
    #0.prop = 3;
2
    .
2

2
Consider `#0:caller_A', which calls `#0:callee_A'.  Such a task would assign 1 to `#0.prop', call `#0:callee_A', fork a new task, return to `#0:caller_A', and assign 2 to `#0.prop', ending this task.  Five seconds later, if the forked task had not been killed, then it would begin to run; it would assign 3 to `#0.prop' and then stop.  So, the final value of `#0.prop' (i.e., the value after more than 5 seconds) would be 3.
2

2
Now consider `#0:caller_B', which calls `#0:callee_B' instead of `#0:callee_A'.  This task would assign 1 to `#0.prop', call `#0:callee_B', and suspend.  Five seconds later, if the suspended task had not been killed, then it would resume; it would assign 3 to `#0.prop', return to `#0:caller_B', and assign 2 to `#0.prop', ending the task.  So, the final value of `#0.prop' (i.e., the value after more than 5 seconds) would be 2.
2

2
A suspended task, like a forked task, can be described by the `queued_tasks()' function and killed by the `kill_task()' function.  Suspending a task does not change its task id.  A task can be suspended again and again by successive calls to `suspend()'.
2

2
By default, there is no limit to the number of tasks any player may suspend, but such a limit can be imposed from within the database.  See the chapter on server assumptions about the database for details.
35
5
4
2
2
*forward*
2
sin()
35
5
4
2
2
*forward*
2
sinh()
35
5
4
3
2
Syntax:  task_id ()   => int
2

2
Returns the non-zero, non-negative integer identifier for the currently-executing task.  Such integers are randomly selected for each task and can therefore safely be used in circumstances where unpredictability is required.
35
5
4
3
2
Syntax:  task_stack (int <task-id> [, <include-line-numbers>])   => list
2

2
Returns information like that returned by the `callers()' function, but for the suspended task with the given <task-id>; the <include-line-numbers> argument has the same meaning as in `callers()'.  Raises `E_INVARG' if <task-id> does not specify an existing suspended task and `E_PERM' if the programmer is neither a wizard nor the owner of the specified task.
35
5
4
4
2
Syntax:  ticks_left ()   => int
2
       seconds_left ()   => int
2

2
These two functions return the number of ticks or seconds (respectively) left to the current task before it will be forcibly terminated.  These are useful, for example, in deciding when to call `suspend()' to continue a long-lived computation.
35
5
4
3
2
Syntax:  time ()   => int
2

2
Returns the current time, represented as the number of seconds that have elapsed since midnight on 1 January 1970, Greenwich Mean Time.
35
5
4
9
2
Syntax:  tofloat (<value>)   => float
2

2
Converts the given MOO value into a floating-point number and returns that number.  Integers and object numbers are converted into the corresponding integral floating-point numbers.  Strings are parsed as the decimal encoding of a real number which is then represented as closely as possible as a floating-point number.  Errors are first converted to integers as in `toint()' and then converted as integers are.  `Tofloat()' raises `E_TYPE' if <value> is a list.  If <value> is a string but the string does not contain a syntactically-correct number, then `tofloat()' returns 0.
2

2
    tofloat(34)          =>   34.0
2
    tofloat(#34)         =>   34.0
2
    tofloat("34")        =>   34.0
2
    tofloat("34.7")      =>   34.7
2
    tofloat(E_TYPE)      =>   1.0
35
5
4
12
2
Syntax:  toint (<value>)   => int
2
         tonum (<value>)   => int
2

2
Converts the given MOO value into an integer and returns that integer.  Floating-point numbers are rounded toward zero, truncating their fractional parts.  Object numbers are converted into the equivalent integers.  Strings are parsed as the decimal encoding of a real number which is then converted to an integer.  Errors are converted into integers obeying the same ordering (with respect to `<=' as the errors themselves.  `Toint()' raises `E_TYPE' if <value> is a list.  If <value> is a string but the string does not contain a syntactically-correct number, then `toint()' returns 0.
2

2
    toint(34.7)        =>   34
2
    toint(-34.7)       =>   -34
2
    toint(#34)         =>   34
2
    toint("34")        =>   34
2
    toint("34.7")      =>   34
2
    toint(" - 34  ")   =>   -34
2
    toint(E_TYPE)      =>   1
35
5
4
10
2
Syntax:  toliteral (<value>)   => str
2

2
Returns a string containing a MOO literal expression that, when evaluated, would be equal to <value>.
2

2
    toliteral(17)         =>   "17"
2
    toliteral(1.0/3.0)    =>   "0.333333333333333"
2
    toliteral(#17)        =>   "#17"
2
    toliteral("foo")      =>   "\"foo\""
2
    toliteral({1, 2})     =>   "{1, 2}"
2
    toliteral(E_PERM)     =>   "E_PERM"
35
5
4
2
2
*forward*
2
toint()
35
5
4
8
2
Syntax:  toobj (<value>)   => obj
2

2
Converts the given MOO value into an object number and returns that object number.  The conversions are very similar to those for `toint()' except that for strings, the number *may* be preceded by `#'.
2

2
    toobj("34")       =>   #34
2
    toobj("#34")      =>   #34
2
    toobj("foo")      =>   #0
2
    toobj({1, 2})     -error->   E_TYPE
35
5
4
13
2
Syntax:  tostr (<value>, ...)   => str
2

2
Converts all of the given MOO values into strings and returns the concatenation of the results.
2

2
    tostr(17)                  =>   "17"
2
    tostr(1.0/3.0)             =>   "0.333333333333333"
2
    tostr(#17)                 =>   "#17"
2
    tostr("foo")               =>   "foo"
2
    tostr({1, 2})              =>   "{list}"
2
    tostr(E_PERM)              =>   "Permission denied"
2
    tostr("3 + 4 = ", 3 + 4)   =>   "3 + 4 = 7"
2

2
Note that `tostr()' does not do a good job of converting lists into strings; all lists, including the empty list, are converted into the string `"{list}"'.  The function `toliteral()', below, is better for this purpose.
35
5
4
3
2
Syntax:  trunc (float <x>)   => float
2

2
Returns the integer obtained by truncating <x> at the decimal point, as a floating-point number.  For negative <x>, this is equivalent to `ceil()'; otherwise it is equivalent to `floor()'.
35
5
4
11
2
Syntax:  typeof (<value>)   => int
2

2
Takes any MOO value and returns an integer representing the type of <value>.  The result is the same as the initial value of one of these built-in variables: `INT', `FLOAT', `STR', `LIST', `OBJ', or `ERR'.  Thus, one usually writes code like this:
2

2
    if (typeof(x) == LIST) ...
2

2
and not like this:
2

2
    if (typeof(x) == 3) ...
2

2
because the former is much more readable than the latter.
35
5
4
3
2
Syntax:  unlisten (<canon>)   => none
2

2
Stop listening for connections on the point described by <canon>, which should be the second element of some element of the list returned by `listeners()'.  Raises `E_PERM' if the programmer is not a wizard and `E_INVARG' if there does not exist a listener with that description.
35
5
4
6
2
Syntax:  valid (obj <object>)   => int
2

2
Returns a non-zero integer (i.e., a true value) if <object> is a valid object (one that has been created and not yet recycled) and zero (i.e., a false value) otherwise.
2

2
    valid(#0)    =>   1
2
    valid(#-1)   =>   0
35
5
4
3
2
Syntax:  value_bytes (<value>)   => int
2

2
Returns the number of bytes of the server's memory required to store the given <value>.
35
5
4
3
2
Syntax:  value_hash (<value>)   => str
2

2
Returns the same string as `string_hash(toliteral(<value>))'; see the description of `string_hash()' for details.
35
5
4
12
2
Syntax:  verb_args (obj <object>, str <verb-desc>)              => list
2
     set_verb_args (obj <object>, str <verb-desc>, list <args>) => none
2

2
These two functions get and set (respectively) the direct-object, preposition, and indirect-object specifications for the verb as specified by <verb-desc> on the given <object>.  If <object> is not valid, then `E_INVARG' is raised.  If <object> does not define a verb as specified by <verb-desc>, then `E_VERBNF' is raised.  If the programmer does not have read (write) permission on the verb in question, then `verb_args()' (`set_verb_args()') raises `E_PERM'.  Verb args specifications have the following form:
2

2
    {<dobj>, <prep>, <iobj>}
2

2
where <dobj> and <iobj> are strings drawn from the set `"this"', `"none"', and `"any"', and <prep> is a string that is either `"none"', `"any"', or one of the prepositional phrases listed much earlier in the description of verbs in the first chapter.  This is the kind of value returned by `verb_args()' and expected as the third argument to `set_verb_args()'.  Note that for `set_verb_args()', <prep> must be only one of the prepositional phrases, not (as is shown in that table) a set of such phrases separated by `/' characters.  `set_verb_args' raises `E_INVARG' if any of the <dobj>, <prep>, or <iobj> strings is illegal.
2

2
    verb_args($container, "take")
2
                        =>   {"any", "out of/from inside/from", "this"}
2
    set_verb_args($container, "take", {"any", "from", "this"})
35
5
4
8
2
Syntax:  verb_code (obj <object>, str <verb-desc> [, <fully-paren> [, <indent>]])   => list
2
     set_verb_code (obj <object>, str <verb-desc>, list <code>)   => list
2

2
These functions get and set (respectively) the MOO-code program associated with the verb as specified by <verb-desc> on <object>.  The program is represented as a list of strings, one for each line of the program; this is the kind of value returned by `verb_code()' and expected as the third argument to `set_verb_code()'.  For `verb_code()', the expressions in the returned code are usually written with the minimum-necessary parenthesization; if <full-paren> is true, then all expressions are fully parenthesized.  Also for `verb_code()', the lines in the returned code are usually not indented at all; if <indent> is true, each line is indented to better show the nesting of statements.
2

2
If <object> is not valid, then `E_INVARG' is raised.  If <object> does not define a verb as specified by <verb-desc>, then `E_VERBNF' is raised.  If the programmer does not have read (write) permission on the verb in question, then `verb_code()' (`set_verb_code()') raises `E_PERM'.  If the programmer is not, in fact. a programmer, then `E_PERM' is raised.
2

2
For `set_verb_code()', the result is a list of strings, the error messages generated by the MOO-code compiler during processing of <code>.  If the list is non-empty, then `set_verb_code()' did not install <code>; the program associated with the verb in question is unchanged.
35
5
4
8
2
Syntax:  verb_info (obj <object>, str <verb-desc>)              => list
2
     set_verb_info (obj <object>, str <verb-desc>, list <info>) => none
2

2
These two functions get and set (respectively) the owner, permission bits, and name(s) for the verb as specified by <verb-desc> on the given <object>.  If <object> is not valid, then `E_INVARG' is raised.  If <object> does not define a verb as specified by <verb-desc>, then `E_VERBNF' is raised.  If the programmer does not have read (write) permission on the verb in question, then `verb_info()' (`set_verb_info()') raises `E_PERM'.  Verb info has the following form:
2

2
    {<owner>, <perms>, <names>}
2

2
where <owner> is an object, <perms> is a string containing only characters from the set `r', `w', `x', and `d', and <names> is a string.  This is the kind of value returned by `verb_info()' and expected as the third argument to `set_verb_info()'.  `set_verb_info()' raises `E_INVARG' if <owner> is not valid, if <perms> contains any illegal characters, or if <names> is the empty string or consists entirely of spaces; it raises `E_PERM' if <owner> is not the programmer and the programmer is not a wizard.
35
5
4
3
2
Syntax:  verbs (obj <object>)   => list
2

2
Returns a list of the names of the verbs defined directly on the given <object>, not inherited from its parent.  If <object> is not valid, then `E_INVARG' is raised.  If the programmer does not have read permission on <object>, then `E_PERM' is raised.
35
5
4
0
35
1
5
35
4
4
1
2
Builtin Function Help (1.8.0)
35
5
4
32
2
A help database (in the sense of anything that is usable by $player:help()) is any object having the following two verbs:
2

2
  :find_topics(string)
2
     returns a list of strings or some boolean false value.
2

2
  :get_topic(string)
2
     given one of the strings returned by :find_topics this either
2
     returns a list of strings (text to be spewed to the player) or
2
     returns 1 to indicate that it has already taken care of printing
2
     information to the player.
2

2
$player:help() consults any .help properties that exist on the player, its ancestors, player.location and its ancestors (in that order).  These properties are assumed to have values that are objects or lists of objects, each object itself assumed to be a help database in the above sense.  The main help database ($help) is placed at the end of the list of databases to be consulted.
2

2
The Generic Help Database (this object) is the standard model help database of which the actual help database itself ($help) is an instance.  On help databases of this type, every help topic has a corresponding property, interpreted as follows:
2

2
this.(topic) = string           - one-line help text.
2
this.(topic) = {"*verb*",@args} - call this:verb(@args) to get text
2
this.(topic) = any other list   - multi-line help text
2

2
For the {"*verb*",...} form, the current verbs available are
2

2
  {"*forward*", topic2, @rest}   
2
     - get topic2 help text and then append rest.  
2
       rest may, in turn, begin with a "*verb*"...
2

2
  {"*subst*", @lines} 
2
     - all occurences of %[exp] in lines are replaced with value of exp.  
2
       exp is assumed to evaluate to a string.  Evaluation is done using 
2
       $no_one's permissions so exp can only refer to public information.
2

2
  {"*index*"}
2
     - returns a list of all topics in this database, arranged in columns.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
88236
0
913134427
200
1
#29
New-Prog-Log

0
35
42
-1
62
113
-1
62
7
is_usable_by
35
173
-1
receive_message
35
173
-1
display_seq_headers display_seq_full
35
173
-1
from_msg_seq
35
173
-1
to_msg_seq
35
173
-1
%to_msg_seq subject_msg_seq
199
173
-1
%from_msg_seq
199
173
-1
0
29
1
147
35
5
5
35
5
5
35
5
5
35
5
5
35
1
4
0
35
0
0
1
35
5
5
35
5
5
35
5
5
35
5
4
1
1
2
35
1
4
0
35
1
5
35
5
5
35
1
4
0
35
0
5
35
4
4
3
2
New-Prog-Log
2
New_Prog_Log
2
NPL
35
1
2
Record of who's been made a @programmer.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5772
0
913134427
200
1
#30
Generic Help Database

144
35
-1
-1
-1
1
54
15
26
find_topics
199
165
-1
get_topic
199
165
-1
sort_topics
35
173
-1
columnize
35
173
-1
forward pass unpass
35
165
-1
subst
35
173
-1
index
35
173
-1
initialize
199
173
-1
verbdoc
199
173
-1
dump_topic
199
165
-1
to_jtext
35
165
-1
plaintext_or_dlist
35
165
-1
make_link
35
173
-1
line_to_hboxes
199
165
-1
objectdoc
199
173
-1
topic_text
199
165
-1
set_topic_text
199
5
-1
has_topic
35
173
-1
add_topic
199
165
-1
real_add_topic
199
165
-1
delete_topic
199
165
-1
real_delete_topic
199
165
-1
editing_for
199
173
-1
nominate_for_core
199
173
-1
init_for_core
199
173
-1
@dump-help-db
199
41
-1
1
unconvertible_topics
15
4
3
3
10
2
Proposal for a MOO Script Preprocessor
2
note about the old HTTP gateway
35
1
5
35
4
4
1
2
Generic Help Database
35
5
2
A help database of the standard form in need of a description. See `help $generic_help'...
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
0
1
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
20816
0
913134427
200
1
#31
generic guest

16
199
3
-1
80
6
-1
-1
27
@password
199
93
-2
boot
199
165
-1
disfunc
199
173
-1
defer
199
173
-1
mail_catch_up
199
173
-1
create
35
89
-2
eject
35
173
-1
log
35
173
-1
confunc
199
173
-1
log_disconnect
199
165
-1
@last-c*onnection
199
29
-1
@register @request*-character
199
89
11
my_huh
199
173
-1
@boot*!
199
97
11
moveto
35
173
-1
description
199
173
-1
@name
199
89
-2
base_name
199
173
-1
@boot*!
199
33
-1
do_boot
199
173
-1
nominate_for_core
199
173
-1
init_for_core
199
173
-1
set_description
199
173
-1
@desc*ribe
199
89
13
set_gender set_gender_name set_gender_obj
199
173
-1
connection_name
199
173
-1
reset
199
173
-1
13
default_gender
default_description
request
special_exits
guest_name
known_player
default_aliases
booting_msg
show_site
registration_ok
noregister_msg
available
enabled
114
1
169
35
1
4
1
2
By definition, guests appear nondescript.
35
1
0
0
199
0
4
0
35
1
2

199
1
1
36
199
1
4
0
199
1
4
8
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
boots
2
 
4
3
2
dname
2
thing
0
0
2
.
199
5
0
1
199
1
0
0
199
1
4
3
1
69
2
do
2
Sorry, automatic registration not allowed.
199
5
0
1
199
1
0
1
199
1
5
35
1
4
2
0
0
2
?
199
0
5
199
5
5
199
0
5
199
1
1
181
199
5
5
35
1
5
35
1
5
35
0
5
35
1
5
199
5
5
200
1
0
79
35
1
5
199
4
5
199
0
5
199
0
5
199
5
5
199
4
5
199
4
5
199
4
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
4
5
199
4
0
742592989
199
1
0
0
200
0
5
199
5
1
70
35
1
0
0
199
0
5
199
5
0
1
199
5
5
199
5
5
199
5
5
35
1
5
199
1
5
199
1
5
35
1
5
35
1
5
199
5
5
199
5
5
199
1
5
199
1
5
199
5
5
199
0
5
199
5
5
35
1
5
35
1
5
199
5
4
0
35
1
5
199
0
5
199
0
5
199
0
5
199
1
5
199
1
5
35
1
5
199
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
1
5
199
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
199
1
5
199
1
5
199
0
5
199
1
5
200
0
5
199
5
5
199
0
5
35
1
5
199
1
5
199
1
5
199
5
5
199
1
5
199
4
4
2
2
generic guest
2
guest0
199
1
4
1
2
By definition, guests appear nondescript.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
0
0
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
24190
0
913134427
200
1
#32
sequence utilities

16
35
-1
-1
-1
156
-1
38
17
add remove
35
173
-1
contains
35
173
-1
complement
35
173
-1
union intersection
35
173
-1
tostr
35
173
-1
for
199
173
-1
extract
35
173
-1
tolist
35
173
-1
from_list
35
173
-1
from_sorted_list
35
173
-1
first
35
173
-1
last
35
173
-1
size
35
173
-1
from_string
35
173
-1
firstn
35
173
-1
lastn
35
173
-1
range
35
173
-1
0
15
5
35
5
5
35
4
4
3
2
sequence utilities
2
seq_utils
2
squ
35
5
4
35
2
A sequence is a set of integers (*)
2
This package supplies the following verbs:
2

2
  :add      (seq,f,t)  => seq with [f..t] interval added
2
  :remove   (seq,f,t)  => seq with [f..t] interval removed
2
  :range    (f,t)      => sequence corresponding to [f..t]
2
  {}                   => empty sequence
2
  :contains (seq,n)    => n in seq
2
  :size     (seq)      => number of elements in seq
2
  :first    (seq)      => first integer in seq or E_NONE
2
  :firstn   (seq,n)    => first n integers in seq (as a sequence)
2
  :last     (seq)      => last integer in seq  or E_NONE
2
  :lastn    (seq,n)    => last n integers in seq (as a sequence)
2

2
  :complement(seq)       => [-2147483648..2147483647] - seq
2
  :union    (seq,seq,...) 
2
  :intersect(seq,seq,...) 
2

2
  :extract(seq,array)           => array[@seq]
2
  :for([n,]seq,obj,verb,@args)  => for s in (seq) obj:verb(s,@args); endfor
2

2
  :tolist(seq)            => list corresponding to seq
2
  :tostr(seq)             => contents of seq as a string
2
  :from_list(list)        => sequence corresponding to list
2
  :from_sorted_list(list) => sequence corresponding to list (assumed sorted)
2
  :from_string(string)    => sequence corresponding to string
2

2
For boolean expressions, note that
2
  the representation of the empty sequence is {} (boolean FALSE) and
2
  all non-empty sequences are represented as nonempty lists (boolean TRUE).
2

2
The representation used works better than the usual list implementation for sets consisting of long uninterrupted ranges of integers.  
2
For sparse sets of integers the representation is decidedly non-optimal (though it never takes more than double the space of the usual list representation).
2

2
(*) Actually what this package implements is sets of integers-mod-2^32, but this assumes the underlying machine on which the server runs has 32-bit integers.  If not, you need to change this.maxneg to be the largest negative ("smallest"?) integer available.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
11553
0
913134427
200
1
#33
Quota-Log

0
35
42
-1
29
113
-1
29
0
0
29
1
147
35
5
5
35
5
5
35
5
5
35
5
5
35
1
4
0
35
0
0
1
35
5
5
35
5
5
35
5
5
35
5
4
1
1
2
35
1
4
0
35
1
5
35
5
5
35
1
4
0
35
0
5
35
4
4
3
2
Quota-Log
2
Quota_Log
2
QL
35
1
2
Record of whose quota has been messed with and why.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
709
0
913134427
200
1
#34
you

16
35
-1
-1
-1
64
172
165
6
say_action
35
165
-1
_you
199
173
-1
say
35
165
-1
verb_sub noun_sub
199
173
-1
modname_p
199
173
-1
say_action(new)
35
165
-1
1
help_text
28
4
16
2
This object is useful for announcing messages that switch between third and second person when addressed to the appropriate parties in a room.
2

2
Verbs:
2

2
  :verb_sub(STR verbspec) -> conjugates the given verb into singular form
2
  :say_action(message [,who [,thing, [,where]]]) -> appropriately pronoun 
2
      substituted message announced to where, which defaults to who.location
2
      where who defaults to player.
2
  Ex:  if player=#123 (Munchkin), dobj=#456 (Frebblebit), and iobj=#789
2
       (Bob) and they are all in the same room,
2
       $you:say_action("%N %<waves> happily to %d and %i.") would do this:
2

2
Munchkin sees:       You wave happily to Frebblebit and Bob.
2
Frebblebit sees:     Munchkin waves happily to you and Bob.
2
Bob sees:            Munchkin waves happily to Frebblebit and you.
2
Everyone else sees:  Munchkin waves happily to Frebblebit and Bob.
35
5
1
34
35
5
2
Yours
35
5
2
yours
35
5
2
Your
35
5
2
your
35
5
2
Yourself
35
5
2
yourself
35
5
2
You
35
5
2
you
35
5
2
You
35
5
2
you
35
5
0
1
35
5
2
second
35
5
5
35
4
4
3
2
2nd
2
you
2
you
35
5
2
Second person singular.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
6324
0
913134427
200
1
#35
hacker

19
35
15
184
36
52
-1
63
1
base_name
35
173
-1
0
106
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
199
0
5
35
5
5
199
0
5
199
1
0
0
35
5
5
35
1
5
35
1
5
35
0
5
35
1
0
1976290015
35
5
0
-46
200
1
5
35
1
5
35
4
5
199
0
5
199
0
5
35
5
5
35
4
5
35
4
5
35
4
5
35
5
5
35
5
5
35
5
5
35
5
2
The %t isn't a real person.  You may want to mail an admin group instead.
35
5
5
35
5
5
35
5
4
2
0
0
0
0
35
4
5
35
4
0
2147483647
199
1
0
-9031
200
0
5
35
5
1
70
35
1
5
199
0
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
199
1
5
199
1
5
35
1
5
35
1
5
35
5
5
35
5
5
199
1
5
199
1
5
35
5
5
199
0
5
35
5
5
35
1
0
0
35
1
5
35
5
4
0
35
1
5
199
0
5
199
0
5
199
0
5
199
1
5
199
1
4
0
35
1
5
199
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
199
1
5
199
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
199
1
5
199
1
5
199
0
5
199
1
4
4
0
100590000
0
-17253688
0
785633380
0
1289850
200
0
5
35
5
5
199
0
5
35
1
0
0
199
1
5
199
1
5
35
5
5
199
1
5
35
4
4
5
2
hacker
2
unknown hacker
2
the unknown hacker
2
uh
2
unknown_hacker
199
1
2
You see a player who should type '@describe me as ...'.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
0
0
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2401
0
913134427
200
1
#36
nobody

19
35
15
-1
63
6
-1
4
5
eval
199
165
-1
moveto
35
173
-1
eval_d
199
165
-1
call_verb
199
173
-1
modname_c
35
165
-1
1
namec
102
2
Nobody
35
5
5
35
1
5
199
0
5
35
5
5
199
0
5
199
1
0
0
35
5
5
35
1
5
35
1
5
35
0
5
35
1
5
35
5
5
200
1
5
35
1
5
35
4
5
199
0
5
199
0
5
35
5
5
35
4
5
199
4
5
199
4
5
199
5
5
199
5
2
... no one out there to see it.
199
5
5
199
5
2
The %t isn't a real person.  You may want to mail an admin group instead.
199
5
5
199
5
5
199
5
5
35
4
5
35
4
0
2147483647
199
1
0
0
200
0
1
178
199
5
1
70
35
1
5
199
0
5
35
5
5
199
5
5
35
5
5
35
5
5
35
1
5
199
1
5
199
1
5
35
1
5
35
1
5
35
5
5
35
5
5
199
1
5
199
1
5
35
5
5
199
0
5
35
5
5
35
1
0
0
35
1
5
35
5
4
0
35
1
5
199
0
5
199
0
5
199
0
5
199
1
5
199
1
4
0
35
1
5
199
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
199
1
5
199
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
199
1
5
199
1
5
199
0
5
199
1
4
4
0
0
0
0
0
873964801
0
0
200
0
5
35
5
5
199
0
5
35
1
0
0
199
1
5
199
1
5
35
5
5
199
1
5
199
4
4
1
2
nobody
199
1
2
... trapped in a world he never made.
199
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
4877
0
913134427
200
1
#37
Player Database

16
35
-1
-1
-1
79
-1
25
15
load
35
173
-1
check
35
45
-1
init_for_core
199
173
-1
available(old)
35
169
-1
suspend_restart
35
173
-1
create_player
199
173
-1
aliases_bad
35
173
-1
alias_taken
35
165
-1
alias_ignored
35
165
-1
alias_ok
35
165
-1
alias_bad
35
165
-1
name_ok
35
165
-1
gensym_name gensym_alias
35
173
-1
create_registered_player
199
173
-1
check_name_request
199
165
-1
8
stupid_names
frozen
registrations_not_allowed_now
 u
 h
 J
 t
 c
29
4
46
2
with
2
using
2
at
2
to
2
in front of
2
in
2
inside
2
into
2
on top of
2
on
2
onto
2
upon
2
out of
2
from inside
2
from
2
over
2
through
2
under
2
underneath
2
beneath
2
behind
2
beside
2
for
2
about
2
is
2
as
2
off
2
off of
2
you
2
it
2
I
2
they
2
we
2
he
2
she
2
e
2
s/he
2
*e
2
none
2
test
2
me
2
here
2
all
2
everything
2
everyone
2
everybody
35
5
0
0
35
5
0
0
35
5
4
4
2

2

4
2
2
unknown_hacker
2
uh
4
2
1
35
1
35
35
0
4
4
2

2

4
2
2
housekeeper
2
hacker
4
2
1
63
1
35
35
0
4
4
2

2

4
2
2
J.
2
jtext
4
2
1
190
1
190
35
0
4
4
2

2

4
2
2
topic-owner
2
Text
4
2
1
196
1
190
35
0
4
4
2
ore
2

4
3
2
Core
2
core_wizard
2
Core-Wizard
4
3
1
199
1
199
1
199
35
0
4
2
1
35
2

35
5
0
0
35
1
0
0
35
1
5
35
5
5
35
5
4
2
0
913134424
0
1439449680
35
1
4
4
2

2
uhJtc
4
4
2
Quota
2
$core_wizard
2
nobody
2
Wizard
4
4
1
200
1
199
1
36
1
2
35
1
5
35
4
4
3
2
player_db
2
plyrdb
2
pdb
35
5
4
2
2
A database containing all player names and aliases.  
2
See `help $player_db' for more information.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
16379
0
913134427
200
1
#38
gender utilities

16
35
-1
-1
-1
156
-1
26
8
get_noun get_conj*ugation
35
165
-1
player_genders player_gender_names
35
173
-1
genders gender_names
35
173
-1
gender_objs
35
173
-1
match_gender match_gender_name match_gender_obj
35
173
-1
gender_match_failed
199
173
-1
player_gender_objs
35
173
-1
init_for_core
199
173
-1
1
player_gender_objs
16
4
7
1
167
1
168
1
169
1
173
1
177
1
178
1
170
35
5
5
35
5
5
35
4
4
1
2
Gender_Utilities
35
5
4
18
2
Defines the list of standard genders, the default pronouns for each, and routines for adding or setting pronoun properties on any gendered object.
2

2
Properties:
2
  .genders  -- list of standard genders
2
  .pronouns -- list of pronoun properties
2
  .ps .po .pp .pq .pr .psc .poc .ppc .pqc .prc 
2
            -- lists of pronouns for each of the standard genders
2

2
  If foo is of gender this.gender[n], 
2
  then the default pronoun foo.p is this.p[n] 
2
  (where p is one of ps/po/pp/pq...)
2

2
Verbs:
2
  :set(object,newgender) -- changes pronoun properties to match new gender.
2
  :add(object[,perms[,owner]]) -- adds pronoun properties to object.
2

2
  :get_pronoun     (which,object) -- return pronoun for a given object
2
  :get_conj*ugation(verbspec,object) -- return appropriately conjugated verb
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
0
1
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
7515
0
913134427
200
1
#39
time utilities

16
35
-1
-1
-1
156
-1
47
15
day
35
13
-1
month
35
13
-1
ampm
35
13
-1
to_seconds
35
173
-1
sun
35
173
-1
from_ctime
35
173
-1
dhms dayshoursminutesseconds
35
173
-1
english_time
35
173
-1
from_day
35
173
-1
from_month
35
173
-1
dst_midnight
35
173
-1
time_sub
35
173
-1
lunar_phase
199
173
-1
parse_english_time_interval
199
173
-1
nominate_for_core
199
173
-1
14
monthlens
timezones
stsd
ctcd
ct
corr
dayabbrs
days
months
monthabbrs
zones
lunar_phase
lunar_period
time_units
29
4
12
0
31
0
28
0
31
0
30
0
31
0
30
0
31
0
31
0
30
0
31
0
30
0
31
35
5
4
15
4
2
2
AuEST
0
-10
4
2
2
AuCST
0
-9
4
2
2
AuWST
0
-8
4
2
2
WET
0
-1
4
2
2
GMT
0
0
4
2
2
AST
0
4
4
2
2
EDT
0
4
4
2
2
EST
0
5
4
2
2
CDT
0
5
4
2
2
CST
0
6
4
2
2
MDT
0
6
4
2
2
MST
0
7
4
2
2
PDT
0
7
4
2
2
PST
0
8
4
2
2
HST
0
10
35
5
0
2427
35
5
0
7276
35
5
0
7934
35
5
0
-122
35
5
4
7
2
Sun
2
Mon
2
Tue
2
Wed
2
Thu
2
Fri
2
Sat
35
5
4
7
2
Sunday
2
Monday
2
Tuesday
2
Wednesday
2
Thursday
2
Friday
2
Saturday
35
5
4
12
2
January
2
February
2
March
2
April
2
May
2
June
2
July
2
August
2
September
2
October
2
November
2
December
35
5
4
12
2
Jan
2
Feb
2
Mar
2
Apr
2
May
2
Jun
2
Jul
2
Aug
2
Sep
2
Oct
2
Nov
2
Dec
35
5
4
5
4
2
4
4
2
est
2
edt
2
Massachusetts
2
MA
0
10800
4
2
4
2
2
cst
2
cdt
0
7200
4
2
4
2
2
mst
2
mdt
0
3600
4
2
4
5
2
pst
2
pdt
2
California
2
CA
2
Lambda
0
0
4
2
4
1
2
gmt
0
28800
35
5
0
-1162544
35
5
0
2553414
35
5
4
7
4
4
0
31536000
2
year
2
years
2
yrs
4
5
0
2628000
2
month
2
months
2
mnths
2
mo
4
5
0
604800
2
week
2
weeks
2
wk
2
wks
4
5
0
86400
2
day
2
days
2
dy
2
dys
4
5
0
3600
2
hour
2
hours
2
hr
2
hrs
4
5
0
60
2
minute
2
minutes
2
min
2
mins
4
5
0
1
2
second
2
seconds
2
sec
2
secs
35
5
5
35
5
5
35
4
4
2
2
time utilities
2
time
35
5
4
23
2
    Converting from seconds-since-1970    
2
dhms          (time)                 => string ...DD:HH:MM:SS
2
english_time  (time[, reference time)=> string of y, m, d, m, s
2

2
    Converting to seconds
2
to_seconds    ("hh:mm:ss")           => seconds since 00:00:00
2
from_ctime    (ctime)                => corresponding time-since-1970
2
from_day      (day_of_week, which)   => time-since-1970 for the given day*
2
from_month    (month, which)         => time-since-1970 for the given month*
2
    (* the first midnight of that day/month)
2

2
    Converting to some standard English formats
2
day           ([c]time)              => what day it is
2
month         ([c]time)              => what month it is
2
ampm          ([c]time[, precision]) => what time it is, with am or pm
2

2
    Substitution
2
time_sub      (string, time)         => substitute time information
2

2
    Miscellaneous
2
sun           ([time])               => angle between sun and zenith
2
dst_midnight  (time)                 
2
lunar_phase   (time)                 => string of phase of the moon
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
18655
0
913134427
200
1
#40
Editor Help

16
35
-1
-1
-1
30
-1
28
0
43
summary
edit-index
emote
say
delete
insert
view
depublish
perish
unpublish
publish
mode
enter
quit
unsubscribe
subscribe
reply-to
moo
find
edit
compile
save
showlists
subject
to
also-to
who
print
send
list
next
prev
subst
join
fill
move
copy
what
abort
done
pause
ranges
cc
58
4
11
2
You are inside an editor.  Do
2

2
look          -- for list of commands
2
what          -- to find out what you're editing.
2
list          -- to list out some portion of the text
2
say / emote   -- to add new text to whatever you're editing
2

2
help edit-index -- for a full list of editor help topics
2
help editors    -- for a general discussion about editors
2
help moo        -- for the general MOO help summary (i.e., what you get by 
2
                   typing `help' with no arguments from outside the editor).
35
5
4
2
2
*index*
2
Editor Help Topics
35
5
4
19
2
Syntax: emote <text>
2
        :<text>
2

2
(EDITOR)
2
Appends <text> to the end of the line before the insertion point.
2
The second form is equivalent to the first except that it doesn't strip leading blanks off of <text> (just as with the normal `emote' and `:' commands).
2
The insertion point is left unmoved.
2

2
    >list .
2
    _37_ Hello there
2
    ^38^ Oh, I'm fine.
2
    >:, how are you
2
    Appended to line 37.
2
    >:?
2
    Appended to line 37.
2
    >list .
2
    _37_ Hello there, how are you?
2
    ^38^ Oh, I'm fine.
2

35
5
4
17
2
Syntax: say <text>
2
        "<text>
2

2
(EDITOR)
2
Adds <text> to whatever you are editing.
2
The second form is equivalent to the first except in that it doesn't strip leading blanks off of <text> (just as with the normal `say' and `"' commands).
2

2
The added text appears as a new line at the insertion point.  The insertion point, in turn, gets moved so as to be after the added text.  For example:
2

2
    >"first line
2
    Line 1 added.
2
    >"  second line"
2
    Line 2 added.
2
    >list
2
      1: first line
2
    __2_   second line"
2
    ^^^^
35
5
4
5
2
Syntax:  del*ete [<range>] 
2

2
(EDITOR)
2
Deletes the specified range of lines
2
<range> defaults to the line *before* the current insertion point.
35
5
4
21
2
Syntax:  ins*ert [<ins>] ["<text>]
2
         .                    (`.' == `insert' without arguments)
2

2
(EDITOR)
2
Many editor commands refer to an "insertion point" which is (usually) the place right below where the most recent line was inserted.  The insertion point should really be thought of as sitting *between* lines.  In listings, the line above the insertion point is marked with `_' while the one below is marked with `^'.
2

2
The `insert' command, when given an argument, sets the insertion point.
2
If <text> is provided, a new line will be created and inserted as with `say'.
2
<ins>, both here and in other commands that require specifying an insertion point (e.g., copy/move), can be one of
2
          
2
    ^n   above line n
2
     n   above line n
2
    _n   below line n
2
     $   at the end
2
    ^$   before the last line
2
   n^$   n lines before the end
2
     .   the current insertion point  (i.e., `insert .' is a no-op)
2
    +n   n lines below the current insertion point.
2
    -n   n lines above the current insertion point.
2

2
For the truly perverse, there are other combinations that also work due to artifacts of the parsing process, but these might go away...
35
5
4
10
2
Syntax:  view <player> [<range>] [nonum]
2
         view
2

2
Prints some subset of the specified player's text.
2
Said player must have previously made his text readable with `publish'.
2
<ranges> are specified as in other commands (see `help ranges').
2
References to the insertion point refer to wherever the other player has set his/her insertion point; you have no control over it.
2
The default range is as in list.
2

2
If no arguments are given, this lists all of the players that have published anything in this editor.
35
5
4
2
2
*forward*
2
unpublish
35
5
4
2
2
*forward*
2
unpublish
35
5
4
5
2
Syntax:  unpub*lish
2
         depub*lish
2
         perish
2

2
This command reverses the effects of `publish', making your text readable only by you.
35
5
4
5
2
Syntax:  pub*lish
2

2
By default, only you (and administrators) can read the text you are editing.  This command makes your text readable by the entire world (see `help view').  This is useful if you need help from someone or if you just want to show off your programming acumen.
2

2
Use `unpublish' to make your text private again.
35
5
4
14
2
(NOTE EDITOR)
2
Syntax:  mode
2
         mode string
2
         mode text
2
         mode value
2
         
2
There are (currently) three modes the note editor can be in.
2
One is string mode, in which if the text being edited is one line or less, 
2
it will be saved as a single string (or an empty string) rather than as a list.
2
One is text mode, in which text is always saved as a list of strings.
2
The final mode is value mode, which allows you to edit an arbitrary list of values, not necessarily strings.
2
The mode is set when the text is first loaded (string mode if the text is a string, text if it is a list of strings, or value mode if any values in the list are non-string values), but can be changed using this command.
2

2
The first form above (i.e., without any arguments) reports the current mode.
35
5
4
12
2
Syntax:  enter
2

2
(EDITOR)
2
Enters a sequence of lines at the insertion point (see `help insert').
2
This is similar to .program in that every line you type after the `enter' command is inserted verbatim into the text until you type a line with a single period (`.') on it.  This command is essentially for if you don't like the idea of putting " at the beginning of each line you type.  The only exceptions, i.e., lines that are not entered verbatim (aside from the `.' line), are
2

2
 - If you type a line whose sole text is `@abort', 
2
   that aborts this command without making any changes to the text.  
2
 - Any line whose first nonblank character is `.' and has additional text
2
   is entered but with its first `.' stripped off.  
2

2
Thus, to enter a line whose text is `@abort', you could enter it as `.@abort'.
35
5
4
7
2
Syntax:  q*uit
2
         done
2
         pause  
2

2
(EDITOR)
2
Leaves the editor.  If you have unsaved text it will be there when you return (and in fact you will not be able to do anything else with this editor until you 'abort' or save the text).
2

35
5
4
9
2
Syntax: unsubscribe from <list-name>
2
        unsubscribe <name>... from <list-name>
2

2
(MAILROOM)
2
Remove yourself from the given mailing list.
2
The second form removes arbitrary people from a mailing list.
2
You can only do this if you own whatever is being removed or you own the list.
2

2
Use the `who' command to determine if you are on a given mailing list.
35
5
4
11
2
Syntax: subscribe to <list-name>
2
        subscribe [<name>...] to <list-name>
2

2
(MAILROOM)
2
Add yourself to the given mailing list.  
2
The second form adds arbitrary people to a mailing list.
2
You can only do this if you own the list or if it is listed as [Public] and you own whatever is being added.
2

2
The first form of this command is probably obsolete since if <list-name> is public, you can already read it via `@mail on *<list-name>' and it's much better for the MOO if you do so.  `@mail-option +sticky' makes this even easier.
2

2
Use the `who' command to determine if you are on a given mailing list.
35
5
4
11
2
Syntax:  reply-to [<recipients>]
2

2
(MAIL ROOM)
2
Reports the current contents of the Reply-to: field of your message.
2
With arguments, adds (or changes) the Reply-to: field.
2

2
When someone @answers a message, the Reply-to: field is checked first when determining to whom the reply should be sent --- see `help @answer'.
2

2
To clear the Reply-to: field, do
2

2
         reply-to ""
35
5
4
2
2
*pass*
2

35
5
4
9
2
Syntax:  f*ind  /<str>[/[c][<ins>]]
2
         /<str>[/[c][<ins>]]
2

2
Searches for the first line after <ins> containing <str>.  <ins> defaults to  the current insertion point (see `help insert' for how to specify other places).  With the first form, any character (not just `/') may be used as a delimiter.
2
For the second form, you must use '/'.
2

2
The 'c' flag, if given, indicates that case is to be ignored while searching.
2

2
[Bug: With the second form, there are problems if the search string contains quotes, backslashes or a run of spaces.  The first whitespace will always be treated as a single space.  Likewise, quotes and backslashes occuring in the first word of the command (i.e., the "verb") need to be escaped with `\'.  Unfortunately it will not be possible to fix this until we get a new command parser.]
35
5
4
17
2
(VERB EDITOR)
2
Syntax:  edit <object>:<verb>
2

2
Changes what verb you are editing and loads the code for that verb
2
into the editor. 
2
Equivalent to @edit <object>:<verb>.
2

2
(NOTE EDITOR)
2
Syntax:  edit <note-object>
2
         edit <object>.<property>
2

2
Changes to a different note or a different object text property and 
2
loads its text into the editor.
2
These are equivalent to @notedit <note> or @notedit <object>.<property>
2
respectively.
2

2
For both the verb-editor and note-editor commands, <object> will match on the room you came from, though if the room you came from was another editor, then all bets are off...
35
5
4
5
2
Syntax:  compile [as <object>:<verb>]
2

2
(VERB EDITOR)
2
Installs the new program into the system if there are no syntax errors.
2
If a new object:verb is specified and actually turns out to exist, that <object>:<verb> becomes the default for subsequent compilations.
35
5
4
5
2
Syntax:  save [<note-object>]
2
         save [<object>.<property>]
2

2
(NOTE EDITOR)
2
Installs the freshly edited text.  If <note> or <object>.<property> is specified, text is installed on that note or property instead of the original one.  In addition the new note or property becomes the default for future save commands.
35
5
4
4
2
Syntax:  showlists
2

2
(MAIL ROOM)
2
Print a list of the publically available mailing lists/archives and other non-player entities that can receive mail.
35
5
4
4
2
Syntax:  subj*ect [<text>]
2

2
(MAIL ROOM)
2
Specifies a Subject: line for your message.  If <text> is "", the Subject: line is removed.
35
5
4
6
2
Syntax:  to [<recipients>]
2

2
(MAIL ROOM)
2
Specifies a new set of recipients (the To: line) for your message.
2
Recipient names not beginning with * are matched against the list of players.
2
Recipient names beginning with * are interpreted as mailing-lists/archives/other types of non-person addresses and are matched against all such publically available objects (see `help showlists').  If the list you want to use isn't in the database (i.e., isn't located in the database ($mail_agent)) you need to refer to it by object id.
35
5
4
7
2
Syntax:  also-to [<recipients>]
2

2
Synonym: cc
2

2
(MAIL ROOM)
2
Adds additional recipients to the To: line of your message.
2
Same rules apply as for the `to' command.
35
5
4
7
2
Syntax:  who 
2
         who <rcpt>...
2

2
(MAIL ROOM)
2
Invokes $mail_agent's mail-forwarding tracer and determines who (or what) is actually going to receive your message.  The resulting list will not include destinations that will simply forward the message without :receive_message()'ing a copy for themselves.
2

2
The second form expands an arbitrary list of recipients, for if e.g., you're curious about the members of particular mailing list.
35
5
4
4
2
Syntax:  pri*nt
2

2
(MAIL ROOM)
2
Print your message as it is going to appear at the far end.
35
5
4
6
2
Syntax:  send
2

2
(MAIL ROOM)
2
Send your message and exit the mail room.
2
If there are bogus addresses on your To: line, the message will not be sent.
2
It may be, however, that valid addresses on your To: line will forward to other addresses that are bogus; you'll receive warnings about these, but in this case your message will still be delivered to those addresses that are valid.
35
5
4
8
2
Syntax:  lis*t [<range>] [nonum]
2

2
Prints some subset of the current verb text.
2
The default range is some reasonable collection of lines around the current insertion point:  currently this is 8_-8^, ie., 8 lines above the insertion point to 8 lines below it unless this runs up against the beginning or end of file, in which case we just take the first or last 16 lines, or just 1-$ if there aren't that many.  (See `help ranges' for how to specify line numbers and ranges.)
2

2
`nonum' prints without line numbers.
2

2
Yes, window heights will be customizable some day.
35
5
4
4
2
Syntax:  n*ext [n] ["<text>]
2

2
Moves the insertion point down n lines.  If <text> is provided, inserts a new line there just like `say'.
2
Equivalent to `insert +n'.  As one might expect, n defaults to 1.
35
5
4
4
2
Syntax:  p*rev [n] ["<text>]
2

2
Moves the insertion point up n lines.  If <text> is provided, a new line is inserted as with `say'.
2
Equivalent to `insert -n'.  As one might expect, n defaults to 1.
35
5
4
13
2
Syntax:  s*ubst/<str1>/<str2>[/[g][c][<range>]]
2

2
Substitutes <str2> for <str1>, in all of the lines of <range>.
2
Any character (not just `/') may be used to delimit the strings. 
2
If <str1> is blank, <str2> is inserted at the beginning of the line.  
2
(For inserting a string at the end of a line use emote/:).
2

2
Normally, only one substitution is done per line in the specified range, but if the 'g' flag is given, *all* instances of <str1> are replaced.
2
The 'c' flag indicates that case is not significant when searching for substitution instances.
2
<range> defaults to the line *before* the insertion point.
2

2
You do *not* need a space between the verb and the delimeter before <str1>.
2
[Bug: If you omit the space and the first whitespace in <str1> is a run of more than one space, those spaces get treated as one.  Likewise, quotes and backslashes occuring in the first word of the command (i.e., the "verb") need to be escaped with `\'.  The fix on this will have to wait for a new command parser.]
35
5
4
4
2
Syntax:  j           [<range>]
2
         joinliteral [<range>]
2

2
combines the lines in the specified range.  Normally, spaces are inserted and double space appears after periods and colons, but 'joinliteral' (abbreviates to 'joinl') suppresses this and joins the lines as is.  <range> defaults to the two lines surrounding the insertion point.
35
5
4
3
2
Syntax:  fill [<range>] [@ c]
2

2
combines the specified lines as in join and then splits them so that no line is more than c characters (except in cases of pathological lines with very long words).  c defaults to 70.  <range> defaults to the single line preceding the insertion point.
35
5
4
6
2
Syntax:  m*ove [<range>] to <ins>
2

2
Moves the range of lines to place specified by <ins>.
2
If <ins> happens to be the current insertion point, the insertion point is moved to the end of the freshly moved lines.  If the range of lines contains the insertion point, the insertion point is carried over to the range's new location.
2

2
See `help insert' for a list of possibilities for <ins>.
35
5
4
7
2
Syntax:  c*opy [<range>] to <ins>
2

2
Copies the specified range of lines to place given by <ins>.
2
If <ins> happens to be the current insertion point, the insertion 
2
point moves to the end of the inserted lines.
2

2
See `help insert' for a list of possibilities for <ins>.
35
5
4
3
2
Syntax:  w*hat
2

2
Prints information about the editing session.
35
5
4
3
2
Syntax:  abort
2

2
Abandons this editing session and any changes.
35
5
4
2
2
*forward*
2
quit
35
5
4
2
2
*forward*
2
quit
35
5
4
20
2
Most editor commands act upon a particular range of lines.
2
Essentially, one needs to specify a first line and a last line.
2
Line numbers may be given in any of the following forms
2
  
2
  n      (i.e., the nth line of text)
2
  n^     n-th line after/below  the current insertion point
2
  n_     n-th line before/above the current insertion point
2
  n$     n-th line before the end.
2

2
In the latter three, n defaults to 1, so that `^' by itself refers to the line below the current (i.e., the line that gets `^' printed before it), and likewise for `_' while `$' refers to the last line.  Note that the usage depends on whether you are specifying a line or an insertion point (space between lines). `^5' is the space above/before line 5, while `5^' is the fifth line after/below the current insertion point.
2

2
Ranges of lines may be specified in any of the
2
following ways:
2

2
  <line>                  just that line
2
  from <line> to <line>   what it says; the following two forms are equivalent:
2
  <line>-<line>            
2
  <line> <line>
2

2
With the `from l to l' form, either the from or the to can be left off and it will default to whatever is usual for that command (usually a line above or below the insertion point).  Actually I was thinking of punting the `from'/`to' specifications entirely because they're so verbose.  Opinions?
35
5
4
2
2
*forward*
2
also-to
35
5
4
0
35
1
5
35
4
4
1
2
Editor Help
35
5
0
0
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
20271
0
913134427
200
1
#41
Generic Mail Recipient

144
35
-1
-1
-1
1
14
64
31
set_aliases
35
173
-1
look_self
199
173
-1
is_writable_by
35
173
-1
is_readable_by
35
173
-1
is_usable_by
35
165
-1
mail_notify
35
173
-1
mail_forward
35
173
-1
moderator_forward
35
173
-1
add_forward
35
173
-1
delete_forward
35
173
-1
add_notify
35
173
-1
delete_notify
35
173
-1
receive_message
35
173
-1
ok
35
173
-1
ok_write
35
173
-1
parse_message_seq from_msg_seq %from_msg_seq to_msg_seq %to_msg_seq subject_msg_seq body_msg_seq display_seq_headers display_seq_full messages_in_seq list_rmm new_message_num length_num_le length_date_le length_all_msgs exists_num_eq
35
173
-1
rm_message_seq undo_rmm expunge_rmm renumber
35
173
-1
messages
35
173
-1
date_sort
35
173
-1
_fix_last_msg_date
35
173
-1
moderator_notify
35
173
-1
msg_summary_line
35
173
-1
__check
35
173
-1
__fix
199
173
-1
init_for_core
199
165
-1
initialize
199
173
-1
length_date_gt
35
173
-1
nominate_for_core
199
173
-1
rmm_for_core
199
173
-1
handle_error
35
173
-1
subscribed
199
173
-1
12
moderator_notify
last_msg_date
messages_going
moderated
moderator_forward
writers
readers
mail_notify
mail_forward
expire_period
last_used_time
messages
26
4
0
35
5
0
0
35
1
4
0
35
0
4
0
35
5
2
%n (%#) can't send to moderated list %t (%[#t]) directly.
35
5
4
0
35
5
4
0
35
5
4
0
35
1
2
%t (%[#t]) is a generic recipient.
35
1
0
1000000
35
5
0
0
35
1
4
0
35
0
5
35
4
4
1
2
Generic Mail Recipient
35
1
2
This can either be a mailing list or a mail folder, depending on what mood you're in...
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
20807
0
913134427
200
1
#42
Mail Distribution Center

16
35
-1
33
-1
1
-1
41
53
resolve_addr
35
173
-1
sends_to
35
173
-1
send_message
35
173
-1
raw_send
199
173
-1
mail_forward mail_notify
35
173
-1
touch
35
173
-1
look_self
35
173
-1
check_names
35
173
-1
match
35
173
-1
match_recipient
35
173
-1
match_failed
35
173
-1
make_message
35
173
-1
name namec
35
173
-1
name_list nn_list
35
173
-1
parse_address_field
35
173
-1
display_seq_full
199
173
-1
display_seq_headers
199
173
-1
rm_message_seq
199
165
-1
undo_rmm
199
173
-1
expunge_rmm list_rmm
199
173
-1
renumber
199
173
-1
msg_summary_line
35
173
-1
msg_summary_line(slow)
35
173
-1
parse_message_seq
199
173
-1
_parse_from _parse_to
35
173
-1
_parse_date
35
173
-1
new_message_num
199
173
-1
length_all_msgs
199
173
-1
length_date_le
199
173
-1
length_date_gt
199
173
-1
length_num_le
199
173
-1
exists_num_eq
199
173
-1
from_msg_seq
199
173
-1
%from_msg_seq
199
173
-1
to_msg_seq
199
173
-1
%to_msg_seq
199
173
-1
subject_msg_seq
199
173
-1
body_msg_seq
199
173
-1
messages_in_seq
199
173
-1
__convert_new
199
173
-1
to_text
35
165
-1
is_readable_by is_writable_by is_usable_by
35
173
-1
raw_send(new)
199
169
-1
english_address_field
199
173
-1
resend_message
199
173
-1
parse_misc_headers
35
173
-1
english_address_header
35
173
-1
nn
35
173
-1
addrname
35
173
-1
handle_error
35
173
-1
acceptable
35
173
-1
init_for_core
199
173
-1
send_log_message
199
173
-1
0
14
5
35
4
4
2
2
Mail Distribution Center
2
Postmaster
35
5
4
5
2
This is the database of mailing-list/mail-folder objects.
2
The basic procedure for creating a new list/folder is to create a child of $mail_recipient (Generic Mail Recipient) assign it a suitable name&aliases, set a suitable .mail_forward/.mail_notify (or create suitable :mail_forward() and :mail_notify() verbs) and then teleport it here.
2

2
Avaliable aliases:
2

35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
48772
0
913134427
200
1
#43
Mail Room

16
35
-1
-1
-1
46
-1
-1
23
working_on
35
165
-1
parse_invoke
35
173
-1
init_session
35
173
-1
pri*nt
35
25
-1
message_with_headers
35
173
-1
subj*ect:
35
89
-2
set_subject
35
173
-1
to*:
35
89
-2
also*-to: cc*:
35
89
-2
parse_recipients
35
173
-1
recipient_names
35
173
-1
make_message
35
173
-1
name_list
35
173
-1
parse_msg_headers
35
173
-1
check_answer_flags
35
173
-1
reply-to*: replyto*:
35
89
-2
send
199
9
-1
who
35
29
-1
showlists
35
25
-1
subsc*ribe
35
89
1
unsubsc*ribe
35
89
5
parse_msg_headers(slow)
35
173
-1
local_editing_info
199
173
-1
3
replytos
recipients
subjects
61
4
0
35
0
4
0
35
0
4
0
35
0
4
0
35
1
4
0
35
1
4
2
4
12
2
say
2
emote
2
lis*t
2
ins*ert
2
n*ext,p*rev
2
enter
2
del*ete
2
f*ind
2
s*ubst
2
m*ove,c*opy
2
j,joinl
2
fill
4
11
2
w*hat
2
subj*ect
2
to
2
also-to
2
reply-to
2
showlists,unsubscribe
2
who
2
pri*nt
2
send
2
abort
2
q*uit,done,pause
35
5
1
40
35
5
2
Message body is empty.
35
5
4
8
4
2
2
subj*ect
2
[<text>]
4
2
2
to
2
[<rcpt>..]
4
2
2
also-to
2
[<rcpt>..]
4
2
2
reply-to
2
[<rcpt>..]
4
2
2
who
2
[<rcpt>..]
4
2
2
pri*nt
2

4
2
2
send
2

4
2
2
showlists,unsubscribe
2

35
5
0
0
35
1
0
1
35
5
2
You need to either SEND it or ABORT it before you can start another message.
35
5
4
7
4
2
2
replytos
4
0
4
2
2
recipients
4
0
4
2
2
subjects
2

4
2
2
texts
4
0
4
2
2
changes
0
0
4
2
2
inserting
0
1
4
2
2
readable
0
0
35
1
5
35
5
5
35
5
4
4
2
Saving your message so that you can finish it later.
2
To come back, give the `@send' command with no arguments.
2
Please come back and SEND or ABORT if you don't intend to be working on this
2
message in the immediate future.  Keep Our MOO Clean!  No Littering!
35
5
5
35
5
5
35
5
2
You're not editing anything!
35
5
4
0
35
0
4
0
35
1
4
0
35
0
4
0
35
0
4
0
35
1
4
3
1
69
2
do
2
[mailing] 
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
4
0
35
4
5
35
5
0
282386700
35
5
5
35
4
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
1
-1
196
1
5
35
5
5
35
1
5
35
5
5
199
1
5
35
4
4
1
2
Mail Room
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
1
35
5
0
0
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
20522
0
913134427
200
1
#44
Note Editor

16
35
-1
-1
-1
46
184
43
21
e*dit
35
25
-1
save
35
25
-1
init_session
35
173
-1
working_on
35
173
-1
parse_invoke
35
173
-1
note_text
199
165
-1
set_note_text
199
165
-1
note_match_failed
35
173
-1
w*hat
35
9
-1
mode
35
25
-1
local_editing_info
35
173
-1
to_value
35
173
-1
expl*ode
35
89
-2
explode_line
35
173
-1
explode_list
35
173
-1
is_delimiter
35
173
-1
implode
35
17
-1
end_of_list
35
173
-1
beginning_of_list
35
173
-1
j*oinliteral
35
89
-2
set_*
35
173
-1
2
objects
mode
60
4
0
35
5
4
0
35
1
4
0
35
1
4
0
35
1
4
2
4
12
2
say
2
emote
2
lis*t
2
ins*ert
2
n*ext,p*rev
2
enter
2
del*ete
2
f*ind
2
s*ubst
2
m*ove,c*opy
2
j,joinl
2
fill
4
6
2
w*hat
2
mode
2
e*dit
2
save
2
abort
2
q*uit,done,pause
35
5
1
40
35
5
2
Note is devoid of text.
35
5
4
3
4
2
2
e*dit
2
<note>
4
2
2
save
2
[<note>]
4
2
2
mode
2
[string|list]
35
5
0
0
35
1
5
35
5
2
You need to ABORT or SAVE this note before editing any other.
35
5
4
6
4
2
2
mode
2
text
4
2
2
objects
0
0
4
2
2
texts
0
0
4
2
2
changes
0
0
4
2
2
inserting
0
1
4
2
2
readable
0
0
35
1
5
35
5
5
35
5
4
3
1
69
2
do
2
{Partially edited text will be here when you get back., To return, give the `@edithelp' command with no arguments., Please come back and SAVE or ABORT if you don't intend to be working on this text in the immediate future.  Keep Our MOO Clean!  No Littering!}
35
5
2
Note has not been modified since the last save.
35
5
2
There are changes.
35
5
2
Use the EDIT command to select a note.
35
5
4
0
35
0
4
0
35
1
4
0
35
0
4
0
35
0
4
0
35
1
4
3
1
69
2
do
2
[editing notes] 
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
4
0
35
4
5
35
5
0
1343088092
35
5
4
0
35
4
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
196
1
5
35
5
5
35
1
5
35
5
5
199
1
5
35
4
4
2
2
Note Editor
2
nedit
35
5
4
0
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
20512
0
913134427
200
1
#45
Verb Editor

16
35
-1
-1
-1
46
-1
44
9
e*dit
35
89
-2
com*pile
35
73
-2
working_on
199
165
-1
init_session
35
173
-1
parse_invoke
35
165
-1
fetch_verb_code
199
173
-1
set_verb_code
199
173
-1
local_editing_info
199
165
-1
verb_name
199
173
-1
3
objects
verbnames
definers
61
4
0
35
0
4
0
35
0
4
0
35
0
4
0
35
1
4
0
35
1
4
2
4
12
2
say
2
emote
2
lis*t
2
ins*ert
2
n*ext,p*rev
2
enter
2
del*ete
2
f*ind
2
s*ubst
2
m*ove,c*opy
2
j,joinl
2
fill
4
5
2
w*hat
2
e*dit
2
com*pile
2
abort
2
q*uit,done,pause
35
5
1
40
35
5
2
Verb body is empty.
35
5
4
2
4
2
2
e*dit
2
<obj>:<verb>
4
2
2
com*pile
2
[as <obj>:<verb>]
35
5
0
0
35
1
5
35
5
2
You need to either COMPILE or ABORT this verb before you can start on another.
35
5
4
6
4
2
2
objects
0
0
4
2
2
verbnames
0
0
4
2
2
texts
0
0
4
2
2
changes
0
0
4
2
2
inserting
0
1
4
2
2
readable
0
0
35
1
5
35
5
5
35
5
4
3
2
Keeping your verb for later work.  
2
To return, give the `@edit' command with no arguments.
2
Please come back and COMPILE or ABORT if you don't intend to be working on this verb in the immediate future.  Keep Our MOO Clean!  No Littering!
35
5
2
The verb has no pending changes.
35
5
2
You have changed the verb since last successful compile.
35
5
2
First, you have to select a verb to edit with the EDIT command.
35
5
4
0
35
0
4
0
35
1
4
0
35
0
4
0
35
0
4
0
35
1
4
3
1
69
2
do
2
[editing verbs] 
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
4
0
35
4
5
35
5
0
964554610
35
5
5
35
4
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
196
1
5
35
5
5
35
1
5
35
5
5
199
1
5
35
4
4
4
2
Verb Editor
2
vedit
2
verbedit
2
verb edit
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
11556
0
913134427
200
1
#46
Generic Editor

144
35
-1
-1
-1
3
45
70
67
say
35
93
-2
emote
35
93
-2
enter
35
25
-1
lis*t view
35
89
-2
ins*ert n*ext p*revious .
35
25
-1
del*ete
35
89
-2
f*ind
35
93
-2
s*ubst
35
93
-2
m*ove c*opy
35
89
-2
j*oinliteral
35
89
-2
fill
35
81
-2
pub*lish perish unpub*lish depub*lish
35
9
-1
w*hat
35
13
-1
abort
35
1
-1
done q*uit pause
35
13
-1
huh2
199
173
-1
insertion
35
173
-1
set_insertion
35
173
-1
changed
35
173
-1
set_changed
35
173
-1
origin
35
173
-1
set_origin
35
173
-1
readable
35
173
-1
set_readable
35
173
-1
text
35
165
-1
load
35
173
-1
working_on
35
173
-1
ok
35
173
-1
loaded
35
173
-1
list_line
35
173
-1
insert_line
35
173
-1
append_line
35
173
-1
join_lines
35
173
-1
parse_number
35
173
-1
parse_range
35
173
-1
parse_insert
35
173
-1
parse_subst
35
173
-1
invoke
35
173
-1
suck_in
35
173
-1
new_session
199
173
-1
kill_session
199
165
-1
reset_session
199
173
-1
kill_all_sessions
199
173
-1
enterfunc
35
173
-1
exitfunc
35
173
-1
@flush
35
105
-2
@stateprop
35
153
11
@rmstateprop
35
153
5
initialize
35
173
-1
init_for_core
199
173
-1
set_stateprops
35
165
-1
description
35
173
-1
commands_info
35
173
-1
match_object
35
173
-1
who_location_msg
35
165
-1
nothing_loaded_msg no_text_msg change_msg no_change_msg no_littering_msg depart_msg return_msg previous_session_msg
35
173
-1
announce announce_all announce_all_but tell_contents
35
173
-1
fill_string
35
173
-1
here_huh
35
173
-1
match
199
173
-1
get_room
35
173
-1
invoke_local_editor
199
173
-1
invoke_parsed
35
173
-1
party_location
35
173
-1
nominate_for_core
199
173
-1
acceptable
35
173
-1
topic
35
89
-2
21
readable
times
commands2
help
no_text_msg
commands
invoke_task
exit_on_abort
previous_session_msg
stateprops
depart_msg
return_msg
no_littering_msg
no_change_msg
change_msg
nothing_loaded_msg
texts
active
changes
inserting
original
58
4
0
35
1
4
0
35
1
4
2
4
11
2
say
2
emote
2
lis*t
2
ins*ert
2
n*ext,p*rev
2
del*ete
2
f*ind
2
s*ubst
2
m*ove,c*opy
2
j*oinl
2
fill
4
3
2
w*hat
2
abort
2
q*uit,done,pause
35
5
1
40
35
5
2
There are no lines of text.
35
5
4
15
4
2
2
say
2
<text>
4
2
2
emote
2
<text>
4
2
2
lis*t
2
[<range>] [nonum]
4
2
2
ins*ert
2
[<ins>] ["<text>]
4
2
2
n*ext,p*rev
2
[n] ["<text>]
4
2
2
del*ete
2
[<range>]
4
2
2
f*ind
2
/<str>[/[c][<range>]]
4
2
2
s*ubst
2
/<str1>/<str2>[/[g][c][<range>]]
4
2
2
m*ove,c*opy
2
[<range>] to <ins>
4
2
2
j,joinl
2
[<range>]
4
2
2
fill
2
[<range>] [@<col>]
4
2
2
w*hat
2

4
2
2
abort
2

4
2
2
q*uit,done,pause
2

4
2
2
enter
2

35
5
0
0
35
1
0
0
35
5
2

35
5
4
4
4
2
2
texts
0
0
4
2
2
changes
0
0
4
2
2
inserting
0
1
4
2
2
readable
0
0
35
1
4
6
1
69
2
do
4
3
2
dname
2
player
0
1
2
 heads off to 
4
3
2
iname
2
thing
0
0
2
.
35
5
4
6
1
69
2
do
4
3
2
dname
2
player
0
1
2
 comes back from 
4
3
2
iname
2
thing
0
0
2
.
35
5
2
Keeping your [whatever] for later work.  Since this the Generic Editor, you have to do your own :set_changed(0) so that we'll know to get rid of whatever it you're working on when you leave.  Please don't litter... especially in the Generic Editor.
35
5
2
There have been no changes since the last save.
35
5
2
Text has been altered since the last save.
35
5
2
You're not currently editing anything.
35
5
4
0
35
0
4
0
35
1
4
0
35
0
4
0
35
0
4
0
35
1
4
3
1
69
2
do
2
[editing] 
35
5
5
35
5
5
35
5
5
35
5
4
8
1
69
2
do
4
3
2
dname
2
dobj
0
1
2
 
4
3
2
verb
2
dobj
2
is
2
 unceremoniously expelled from 
4
3
2
dname
2
iobj
0
0
2
 by
35
5
5
35
5
0
1
35
5
4
0
35
4
5
35
5
0
884346079
35
5
5
35
4
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
196
1
5
35
5
5
35
1
5
35
5
5
199
1
5
35
4
4
3
2
Generic Editor
2
gedit
2
edit
35
5
4
0
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
51633
0
913134427
200
1
#47
object utilities

16
199
-1
-1
-1
156
-1
48
26
has_property
199
165
-1
all_properties
199
165
-1
has_verb
199
173
-1
has_callable_verb
199
173
-1
all_verbs
199
165
-1
match_verb
199
173
-1
isa
199
173
-1
ancestors
199
173
-1
descendants descendents
199
173
-1
descendants_suspended descendents_suspended
199
173
-1
ordered_descendants
199
173
-1
branches
35
173
-1
branches_suspended
199
173
-1
leaves
35
173
-1
leaves_suspended
199
173
-1
contains
199
173
-1
all_contents
199
173
-1
findable_properties
199
173
-1
owned_properties
199
173
-1
property_conflicts
199
165
-1
descendants_with_property_suspended
199
165
-1
locations
199
173
-1
fertile_descendants fertile_descendents
199
173
-1
all_properties_suspended
199
173
-1
match_verbs(new)
35
165
-1
connected
35
165
-1
0
15
5
199
5
5
199
4
4
1
2
object utilities
199
5
4
33
2
These routines are useful for finding out information about individual objects.
2

2
Examining everything an object has defined on it:
2
  all_verbs          (object) => like it says
2
  all_properties     (object) => likewise
2
  findable_properties(object) => tests to see if caller can "find" them
2
  owned_properties   (object[, owner]) => tests for ownership
2

2
Investigating inheritance:
2
  ancestors(object[,object...]) => all ancestors
2
  descendants      (object)     => all descendants
2
  ordered_descendants(object)   => descendants, in a different order
2
  leaves           (object)     => descendants with no children
2
  branches         (object)     => descendants with children 
2
  isa        (object,class) => true iff object is a descendant of class (or ==)
2

2
Considering containment:
2
  contains      (obj1, obj2) => Does obj1 contain obj2 (nested)?
2
  all_contents      (object) => return all the (nested) contents of object
2

2
Verifying verbs and properties:
2
  has_property(object,pname) => false/true   according as object.(pname) exists
2
  has_verb    (object,vname) => false/{#obj} according as object:(vname) exists
2
  has_callable_verb          => same, but verb must be callable from a program
2
  match_verb  (object,vname) => false/{location, newvname}
2
                               (identify location and usable name of verb)
2
Suspending:
2
  Many of the above verbs have ..._suspended versions to assist with very large object hierarchies.  The following exist:
2
   descendants_suspended              
2
   branches_suspended                 
2
   leaves_suspended                   
2
   all_properties_suspended           
2
   descendants_with_property_suspended
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
17186
0
913134427
200
1
#48
lock utilities

16
199
-1
-1
-1
156
-1
49
12
init_scanner
199
173
-1
scan_token
199
173
-1
canonicalize_spaces
199
173
-1
parse_keyexp
199
173
-1
parse_E
199
173
-1
parse_A
199
173
-1
eval_key
199
173
-1
match_object
199
173
-1
unparse_key
199
173
-1
eval_key_new
199
173
-1
parse_A_new
199
173
-1
init_for_core
199
165
-1
4
player
input_index
input_length
input_string
19
1
-1
199
5
0
0
199
5
0
0
199
5
2

199
5
5
199
5
5
199
4
4
1
2
lock utilities
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
8622
0
913134427
200
1
#49
list utilities

16
35
-1
-1
-1
156
-1
50
39
make
35
173
-1
range
35
173
-1
map_prop*erty
199
173
-1
map_verb
199
165
-1
map_arg
199
173
-1
map_builtin
199
173
-1
find_insert
35
165
-1
remove_duplicates
35
173
-1
arrayset
35
173
-1
setremove_all
35
173
-1
append
35
173
-1
reverse
35
173
-1
_reverse
35
173
-1
compress
35
173
-1
sort
35
173
-1
slice
35
173
-1
assoc
35
165
-1
iassoc
35
165
-1
iassoc_suspended
199
165
-1
assoc_prefix
35
173
-1
iassoc_prefix
35
173
-1
iassoc_sorted
35
173
-1
sort_alist
35
173
-1
sort_alist_suspended
199
173
-1
randomly_permute
35
173
-1
passoc
35
173
-1
check_type
35
173
-1
random_permutation
199
173
-1
max_length
35
165
-1
any_of
199
173
-1
flatten
35
173
-1
grep_l
199
173
-1
grep_ll
199
173
-1
slice_suspended
35
173
-1
sort_suspended
199
173
-1
make_alist
199
173
-1
longest shortest
35
173
-1
count
35
173
-1
map_verb_safe
199
165
-1
0
15
5
35
5
5
35
4
4
1
2
list_utilities
35
5
4
31
2
append            (list,list,..) => result of concatenating the given lists
2
flatten           (list of lists)=> list of all non-list elements
2
reverse           (list)         => reversed list
2
remove_duplicates (list)         => list with all duplicates removed
2
compress          (list)         => list with consecutive duplicates removed
2
setremove_all     (list,elt)     => list with all occurrences of elt removed
2
find_insert       (sortedlist,e) => index of first element > e in sortedlist
2
sort              (list[,keys])  => sorted list
2

2
make              (n[,e])        => list of n copies of e
2
range             (m,n)          => {m,m+1,...,n}
2

2
arrayset   (list,val,i[,j,k...]) => array modified so that list[i][j][k]==val
2

2
-- Mapping functions (take a list and do something to each element):
2

2
map_prop ({o...},prop)              => list of o.(prop)            for all o
2
map_verb ({o...},verb[,args)        => list of o:(verb)(@args)     for all o
2
map_arg  ([n,]obj,verb,{a...},args) => list of obj:(verb)(a,@args) for all a
2

2
-- Association list functions --
2

2
An association list (alist) is a list of pairs (2-element lists), though the following functions have been generalized for lists of n-tuples (n-element lists).  In each case i defaults to 1.
2

2
make_alist      (lists[,pad]) => alist from list of parallel lists
2
assoc        (targ,alist[,i]) => 1st tuple in alist whose i-th element is targ
2
iassoc       (targ,alist[,i]) => index of same.
2
assoc_prefix (targ,alist[,i]) => ... whose i-th element has targ as a prefix
2
iassoc_prefix(targ,alist[,i]) => index of same.
2
slice             (alist[,i]) => list of i-th elements
2
sort_alist        (alist[,i]) => alist sorted on i-th elements.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
0
2
35
1
1
-1
199
1
4
2
0
27779
0
913134427
200
1
#50
command utilities

16
199
-1
-1
-1
156
-1
53
17
object_match_failed
199
173
-1
player_match_result player_match_failed
199
173
-1
read
199
165
-1
read_lines
199
165
-1
yes_or_no
199
165
-1
read_lines_escape
199
173
-1
suspend
199
173
-1
running_out_of_time
35
173
-1
suspend_if_needed
199
173
-1
dump_lines
35
173
-1
explain_syntax
199
173
-1
do_huh
199
165
-1
task_info
199
173
-1
room_match_failed
199
173
-1
_suspend
199
173
-1
_resume
199
173
-1
init_for_core
199
173
-1
1
suspend_database
16
4
0
199
0
5
199
5
5
199
4
5
199
5
4
33
2
$command_utils is the repository for verbs that are of general usefulness to authors of all sorts of commands.
2

2
Detecting and Handling Failures in Matching
2
-------------------------------------------
2
:object_match_failed(match_result, name)
2
    Test whether or not a :match_object() call failed and print messages if so.
2
:player_match_failed(match_result, name)
2
    Test whether or not a :match_player() call failed and print messages if so.
2
:player_match_result(match_results, names)
2
    ...similar to :player_match_failed, but does a whole list at once.
2
:room_match_failed(match_result, name)
2
    Test whether or not a :match_room() call failed and print messages if so.
2

2
Reading Input from the Player
2
-----------------------------
2
:read()         -- Read one line of input from the player and return it.
2
:yes_or_no([prompt])
2
                -- Prompt for and read a `yes' or `no' answer.
2
:read_lines()   -- Read zero or more lines of input from the player.
2
:dump_lines(lines) 
2
                -- Return list of lines quoted so that feeding them to 
2
                   :read_lines() will reproduce the original lines.
2

2
Utilities for Suspending
2
------------------------
2
:running_out_of_time()
2
                -- Return true if we're low on ticks or seconds.
2
:suspend_if_needed(time)
2
                -- Suspend (and return true) if we're running out of time.
2

2
Client Support for Lengthy Commands
2
-----------------------------------
2
:suspend(args)  -- Handle PREFIX and SUFFIX for clients in long commands.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
21284
0
913134427
200
1
#51
generic wizard

16
199
3
-1
6
52
2
35
36
@chown*#
199
89
-2
@grant @grants*
199
89
1
@programmer
199
25
-1
@shutdown
199
89
-2
@dump-d*atabase
199
9
-1
@who-calls
199
89
-2
mcd_2
2
173
-1
@toad*
199
81
-2
@untoad @detoad
199
89
-2
@quota
199
81
12
@players
199
89
-2
kill_aux_wizard_parse
199
173
-1
@net-who @@who
199
81
-2
@make-player
199
89
-2
@abort-sh*utdown
199
89
-2
toad_msg toad_victim_msg programmer_msg programmer_victim_msg newt_msg newt_victim_msg
199
173
-1
moveto
199
165
-1
@newt
199
17
-1
@unnewt @denewt @get-better
199
81
-2
@log
199
89
-2
@guests
199
25
-1
@rn mail_catch_up check_mail_lists current_message set_current_message get_current_message make_current_message kill_current_message @subscribed @read-all-new @mail-all-new
199
13
-1
@blacklist @graylist @redlist @unblacklist @ungraylist @unredlist @spooflist @unspooflist
199
89
-2
@copyo*bject
199
89
-2
@corify
199
81
13
@make-guest
199
89
-2
@wiz-boot
199
89
-2
@idlers @kill-idlers @kill-useless @useless @Kill-unconnected @unconnected
199
25
-1
@report
199
29
-1
@immortal*ize
199
25
-1
@zap
199
93
-2
@chmod-raw
199
89
-2
@grepjhcore @egrepjhcore @grepcore @egrepcore
199
89
-2
@make-core-db
2
25
-1
renumber_for_core
2
173
-1
@check-all-owned-objects
199
9
-1
9
newt_victim_msg
newt_msg
public_identity
programmer_msg
programmer_victim_msg
toad_victim_msg
toad_msg
mail_identity
boot_player_msg
115
2

199
5
4
10
1
69
2
do
4
3
2
name
2
player
4
1
2
dc
2
 
4
3
2
verb
2
player
2
@newts
2
 
4
3
2
name
2
dobj
4
1
2
d
2
 (
4
2
2
objnum
2
dobj
2
).
199
5
1
-1
199
5
4
6
1
69
2
do
4
3
2
dname
2
dobj
0
1
2
 
4
3
2
verb
2
dobj
2
is
2
 now a programmer.
199
5
2
You are now a programmer.
199
5
2
Have a nice life...
199
5
4
10
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
@toads
2
 
4
3
2
dname
2
dobj
0
0
2
 (
4
2
2
objnum
2
dobj
2
).
199
5
1
-1
199
4
4
8
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
boots
2
 
4
3
2
dname
2
dobj
0
0
2
.
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
199
0
5
199
5
5
199
0
5
199
1
1
23
199
5
5
35
1
5
35
1
5
35
0
5
35
1
5
199
5
5
200
1
5
35
1
5
199
4
5
199
0
5
199
0
5
199
5
5
199
4
5
199
4
5
199
4
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
4
5
199
4
5
199
1
5
200
0
1
169
199
5
1
70
35
1
2
really impossible password to type
199
0
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
199
1
5
199
1
5
35
1
5
35
1
5
199
5
5
199
5
5
199
1
5
199
1
5
199
5
5
199
0
5
199
5
5
35
1
5
35
1
5
199
5
4
0
35
1
5
199
0
5
199
0
5
199
0
5
199
1
5
199
1
5
35
1
5
199
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
1
5
199
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
199
1
5
199
1
5
199
0
5
199
1
5
200
0
5
199
5
5
199
0
5
35
1
5
199
1
5
199
1
5
199
5
5
199
1
5
199
4
4
1
2
player
199
1
2
You see a wizard who chooses not to reveal eir true appearance.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
55997
0
913134427
200
1
#52
generic programmer

144
199
3
-1
51
4
51
-1
52
@prop*erty
199
81
-2
@chmod*#
199
89
-2
@args @args#
199
81
-2
@chparent
199
81
1
eval eval-d eval+d
199
81
-2
@rmprop*erty
199
81
-2
@verb
199
89
-2
@rmverb @rmverb#
199
89
-2
@list*#
199
89
-2
@kill
199
17
-1
@copy @copy-x
199
89
1
_kill_task_message
199
173
-1
@prog*ram @program#
199
89
-2
@setenv
199
81
-2
@pros*pectus pros*pectus
199
85
-2
@d*isplay
199
17
-1
@db*size
199
9
-1
@prospectusDB
199
85
-2
@grep @egrep
199
81
-2
@s*how
199
89
-2
@check-chp*arent
199
89
1
@check-p*roperty
199
17
-1
set_eval_env
199
165
-1
@clearp*roperty @clprop*erty @clear-p*roperty
199
17
-1
@disown @disinherit
199
89
-2
eval_cmd_string
199
173
-1
@dump
199
81
-2
@verbs
199
17
-1
@make-setter @make_setter
199
17
-1
display_option list_option eval_option
199
173
-1
set_list_option set_display_option set_eval_option
199
173
-1
@displayo*ptions @display-o*ptions @listo*ptions @list-o*ptions @evalo*ptions @eval-o*ptions
199
89
-2
options_packages
199
173
-1
debug
199
165
-1
#*
199
81
-2
@mailverb @mailme-verb @rubber-chicken
199
89
-1
@find
35
25
-1
find_verb
35
173
-1
find_verbs_on
35
173
-1
property_inherited_from
35
173
-1
find_property
35
173
-1
find_properties_on
35
173
-1
@set*property
199
81
1
@jdump
199
81
-2
print_eval_result
199
173
-1
nominate_for_core
199
173
-1
verbcode_internal_to_external
35
173
-1
verbcode_external_to_internal
35
173
-1
@regexp-d*isplay(new) @rd*isplay(new)
199
17
-1
@forked
199
89
-2
init_for_core
199
173
-1
edit_set_program
199
173
-1
3
display_options
list_options
eval_options
106
4
0
199
5
4
0
199
5
4
2
4
2
2
ticks
0
6
4
2
2
env
2
me=player;here=player.location;_=player.current_object[1]
199
5
5
199
5
5
199
5
5
35
1
5
199
0
5
199
5
5
199
0
5
199
1
4
7
1
22
1
28
1
20
1
21
1
163
1
201
1
202
199
5
5
35
1
5
35
1
5
35
0
5
35
1
5
199
5
5
200
1
5
35
1
5
199
4
5
199
0
5
199
0
5
199
5
5
199
4
5
199
4
5
199
4
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
4
5
199
4
5
199
1
0
0
200
0
1
169
199
5
5
35
1
5
199
0
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
199
1
5
199
1
5
35
1
5
35
1
5
199
5
5
199
5
5
199
1
5
199
1
5
199
5
5
199
0
5
199
5
5
35
1
5
35
1
5
199
5
4
0
35
1
5
199
0
5
199
0
5
199
0
5
199
1
5
199
1
5
35
1
5
199
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
1
5
199
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
199
1
5
199
1
5
199
0
5
199
1
5
200
0
5
199
5
5
199
0
5
35
1
5
199
1
5
199
1
5
199
5
5
199
1
5
199
4
4
2
2
generic
2
programmer
199
1
2
You see a player who is too experienced to have any excuse for not having a description.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
95407
0
913134427
200
1
#53
code utilities

16
35
-1
-1
-1
156
-1
137
73
eval_d
199
93
-2
1
199
165
-1
tonum
35
173
-1
toobj
35
173
-1
toerr
35
173
-1
error_name
35
173
-1
show_object
199
165
-1
show_property
199
165
-1
show_verbdef
199
173
-1
explain_verb_syntax
199
173
-1
verb_p*erms verb_permi*ssions
199
173
-1
verb_loc*ation
35
173
-1
verb_documentation
199
173
-1
set_verb_documentation
199
173
-1
parse_propref
199
173
-1
parse_verbref
199
173
-1
parse_argspec
35
173
-1
prepositions
35
173
-1
short_prep
35
173
-1
full_prep
35
173
-1
get_prep
35
173
-1
_fix_preps
35
165
1
find_verb_named find_verb_named_0_based
199
165
-1
find_last_verb_named find_last_verb_named_0_based
199
165
-1
find_callable_verb_named find_callable_verb_named_0_based
199
165
-1
verbname_match(new)
35
173
-1
find_verbs_containing
199
173
-1
_find_verbs_containing
199
165
-1
find_verbs_matching
199
173
-1
_find_verbs_matching
199
165
-1
_grep_verb_code
199
173
-1
_egrep_verb_code
199
173
-1
_parse_audit_args
35
173
-1
help_db_list
199
165
-1
help_db_search
35
165
-1
corify_object
35
165
-1
substitute
199
173
-1
inside_quotes
199
173
-1
verb_or_property
199
165
-1
task_valid
199
165
-1
task_owner
199
173
-1
argstr
199
173
-1
verbname_match
35
173
-1
show_who_listing
199
165
-1
_egrep_verb_code_all
199
173
-1
_grep_verb_code_all
199
173
-1
standard_set_property
199
169
-1
verb_usage
199
165
-1
connected_players
35
173
-1
players
35
173
-1
safe_eval
35
173
-1
verb_frame
35
173
-1
verb_all_frames
35
173
-1
parse_named_args
35
173
-1
split_verb_code
35
173
-1
named_args_to_usage
35
173
-1
commentify
35
173
-1
uncommentify
199
173
-1
named_args_list
35
173
-1
named_args_from_code
35
173
-1
named_args_to_code
35
173
-1
gensym
35
173
-1
verbs
199
165
-1
rename_property
199
173
-1
format_traceback
35
173
-1
_format_traceback_frame
35
173
-1
find_verb_named_1_based
199
173
-1
find_last_verb_named_1_based
199
173
-1
find_callable_verb_named_1_based
199
173
-1
callers_before_pass
35
173
-1
frame_before_pass
35
165
-1
caller_before_pass
35
165
-1
caller_perms_before_pass
35
165
-1
13
prepositions
_version
_multi_preps
_other_preps_n
_other_preps
_short_preps
_all_preps
builtin_props
error_names
error_list
reserved
predefined
no_open_quotes_regexp
28
4
15
2
with/using
2
at/to
2
in front of
2
in/inside/into
2
on top of/on/onto/upon
2
out of/from inside/from
2
over
2
through
2
under/underneath/beneath
2
behind
2
beside
2
for/about
2
is
2
as
2
off/off of
35
5
2
1.8.0r5
35
5
4
7
2
off
2
from
2
out
2
on
2
on top
2
in
2
in front
35
5
4
13
0
1
0
2
0
4
0
4
0
5
0
5
0
5
0
6
0
6
0
9
0
9
0
12
0
15
35
5
4
13
2
using
2
at
2
inside
2
into
2
on top of
2
onto
2
upon
2
out of
2
from inside
2
underneath
2
beneath
2
about
2
off of
35
5
4
15
2
with
2
to
2
in front of
2
in
2
on
2
from
2
over
2
through
2
under
2
behind
2
beside
2
for
2
is
2
as
2
off
35
5
4
28
2
with
2
using
2
at
2
to
2
in front of
2
in
2
inside
2
into
2
on top of
2
on
2
onto
2
upon
2
out of
2
from inside
2
from
2
over
2
through
2
under
2
underneath
2
beneath
2
behind
2
beside
2
for
2
about
2
is
2
as
2
off
2
off of
35
5
4
9
2
name
2
r
2
w
2
f
2
programmer
2
wizard
2
owner
2
location
2
contents
199
1
4
16
2
E_NONE
2
E_TYPE
2
E_DIV
2
E_PERM
2
E_PROPNF
2
E_VERBNF
2
E_VARNF
2
E_INVIND
2
E_RECMOVE
2
E_MAXREC
2
E_RANGE
2
E_ARGS
2
E_NACC
2
E_INVARG
2
E_QUOTA
2
E_FLOAT
35
5
4
15
3
0
3
1
3
2
3
3
3
4
3
5
3
6
3
7
3
8
3
9
3
10
3
11
3
12
3
13
3
14
35
5
4
19
2
IF
2
ELSE
2
ELSEIF
2
ENDIF
2
FOR
2
IN
2
ENDFOR
2
FORK
2
ENDFORK
2
WHILE
2
ENDWHILE
2
RETURN
2
TRY
2
EXCEPT
2
ANY
2
FINALLY
2
ENDTRY
2
BREAK
2
CONTINUE
35
5
4
18
2
NUM
2
INT
2
OBJ
2
STR
2
LIST
2
ERR
2
FLOAT
2
player
2
this
2
caller
2
verb
2
args
2
argstr
2
dobj
2
dobjstr
2
prepstr
2
iobj
2
iobjstr
35
5
2
^[^"]*%("%([^\"]%|\.%)*"[^"]*%)*$
35
5
5
35
5
5
35
4
4
2
2
code
2
utils
35
5
4
54
2
parse_propref("foo.bar")  => {"foo","bar"} (or 0 if arg. isn't a property ref.)
2
parse_verbref("foo:bar")  => {"foo","bar"} (or 0 if arg. isn't a verb ref.)
2
parse_argspec("any","in","front","of","this","baz"...)
2
                          => {{"any", "in front of", "this"},{"baz"...}} 
2
                                           (or string if args don't parse)
2

2
tonum(string)           => number (or E_TYPE if string is not a number)
2
toobj(string)           => object (or E_TYPE if string is not an object)
2
toerr(number or string) => error value (or 1 if out of range or unrecognized)
2
error_name(error value) => name of error (e.g., error_name(E_PERM) => "E_PERM")
2

2
verb_perms()    => the current task_perms (as set by set_task_perms()).
2
verb_location() => the object where the current verb is defined.
2
verb_documentation([object,verbname]) => documentation at beginning of
2
           verb code, if any -- default is the calling verb
2

2
   Preposition routines
2

2
prepositions()     => full list of prepostions
2
full_prep ("in")   => "in/inside/into"
2
short_prep("into") => "in"
2
short_prep("in/inside/into") => "in"
2
get_prep  ("off", "of", "the", "table") => {"off of", "the", "table"}
2

2
   Verb routines
2

2
verbname_match (fullname,name) => can `name' be used to call `fullname'
2
find_verb_named          (object,name[,n]) => verb number or -1 if not found
2
find_callable_verb_named (object,name[,n]) => verb number or -1 if not found
2
find_verbs_containing (pattern[,object|objlist])
2

2
verbs(object) => (partial) list of verbs on the object
2

2
   Verbs that do the actual dirty work for @show:
2

2
show_object  (object)
2
show_property(object,propname)
2
show_verbdef (object,verbname)
2

2
   Dirty work for explain_syntax
2

2
explain_verb_syntax(thisname,verbname,@verbargs)
2

2
   A random but useful verb
2

2
verb_or_property(object,name[,@args]) => result of verb or property call,
2
                                         or E_PROPNF
2

2
   Player manipulation:
2
connected_players()   =>   returns all actual players connected
2
players()             =>   returns all actual players
2

2
   Regular Expressions for parsing code
2
no_open_quotes_regexp => a regexp that matches a moo-code line with all strings properly closed
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
61844
0
913134427
200
1
#54
main help database

16
35
-1
-1
-1
30
-1
40
9
full_index
35
165
-1
index_list
35
165
-1
match_help_db
35
173
-1
help_db_match_failed
199
173
-1
topic_text
35
173
-1
set_topic_text
199
165
-1
verify_db
199
173
-1
nominate_for_core
199
173
-1
init_for_core
199
165
-1
257
@locations
@uptime
@mailoptions
@sort-owned
@verify-owned
@add-owned
@mail-options
wizard-list
@wrap
full-index
 index
gen-index
mail-forwarding
@pagelength
@more
programming
@forward
@subscribe
@rn
@unsubscribe
@skip
zombie-messages
message-sequences
common_quota
@linelength
room-messages
@unrmmail
@gaglist
::
@comment
@remove-entrance
@remove-exit
@parents
@contents

spoofing
privacy
@resident
@examine
security
@sweep
@paranoid
@check
@reply
@eject
@quit
whereis
@suggest
@idea
@bug
@typo
@renumber
@notedit
editors
@prev
@unlock_for_open
@lock_for_open
@opacity
container-messages
@memory
"
:
@lastlog
@version
miscellaneous
insert
information
?
put
remove
burn
decrypt
encrypt
delete
erase
write
read
examine
hand
key-representation
keys
@unlock
@lock
locking
thing-messages
throw
take
@messages
pronouns
exit-messages
messages
descriptions
@describe
@add-entrance
@add-exit
topology
@audit
@count
@quota
@next
@answer
@rmmail
@read
@send
mail
@gripe
creation
@mail
@listgag
@ungag
@gag
go
@password
@sethome
@who
introduction
give
news
gagging
@move
inventory
@entrances
@exits
@gender
@recycle
@rename
containers
notes
look
drop
get
manipulation
help
rooms
movement
home
communication
say
whisper
page
emote
building
summary
@edit-options
@editoptions
@add-feature
@remove-feature
@features
features
@dump
me
@rmalias
@addalias
commands
 name
 alias
wizards
@register
@edit
@quicksend
@peek
@forward-me
manners
@options
help-format
administrator-policy
admin-policy
objective
point
quota-policy
builder-policy
admin
community-group
db-group
unix-group
administrators
tech-group
@helpme
@911
cg
@administrators
@admins
following
@current-object
current-object
@currentobject
@co
@pushobject
@pusho
@popobject
@popo
@swapo
@swapobject
walk
join
@subscribed
@unsubscribed
@gag!
@ungag!
@listgag!
@gag-announce
!
@aliases
communications
@qsend
character
characters
programming-policy
registration
email
teleportation
@join
@go
'
index
@boot
@boot!
@edito
 gender
idle
reidle
doing
idling
topics
@who-options
@whooptions
@grant
 aliases
(aliases_extra)
(common_aliases)
@netforward
@gagged
time-stamping
tstamp
timestamping
two-letter-spec
timestamps
time-stamps
(core-introduction)
(core-home)
(core-news)
(core-teleportation)
getting-started
core-copyright
@auditdb
(core-quota-policy)
(core-administrator-policy)
(core-@sethome)
(core-go)
_
@resend
(core-manners)
time-stamp
naming
known_dbs
author_feature
copyright
272
4
7
2
Syntax:  @locations object
2

2
Prints out the names and object numbers of all containing objects.
2

2
Example:
2
  @locations ur-Rog
2
  ur-Rog(#6349)   ur-Rog's Display Case(#6355)   Editorial Boardroom(#5747)
35
5
4
5
2
Syntax:  @uptime
2

2
The @uptime command displays the amount of time since the last restart of the server.
2

2
Note to programmers:  The last restart time of the server is stored in $last_restart_time.
35
5
4
2
2
*forward*
2
@mail-options
35
1
4
3
2
Syntax:  @sort-owned
2

2
Sorts your .owned_objects property so @audit shows up sorted.  See `help @audit' for more information.
35
5
4
3
2
Syntax:  @verify-owned
2

2
Checks that all the objects in your .owned_objects property are actually owned by you, and effects repairs if needed.  See `help @audit' for more information.
35
5
4
3
2
Syntax:  @add-owned <object>
2

2
Adds an object to your .owned_objects property in case it managed not to get updated properly upon creation of that object.  Checks to ensure that the object is really owned by you and otherwise belongs in your .owned_objects property.  See `help @audit' for more information.
35
5
4
66
2
Syntax:  @mail-option
2
         @mail-option <option>
2

2
Synonym:  @mailoption
2

2
The first form displays all of your mail options.
2

2
The second displays just that one option, which may be either `@mail', `replyto', or one of the flags listed below.  The mail options control various annoying details of your mail reading and mail editing commands.
2

2
The remaining forms of this command are for setting your mail options:
2

2
         @mail-option +<flag>
2
         @mail-option -<flag>
2
         @mail-option !<flag>           (equivalent to -<flag>)
2

2
These respectively set and reset the specified flag.
2

2
 -include      @replys start out with a blank message body
2
 +include      @replys start with original message included
2
 -all          @replys go to sender only
2
 +all          @replys go to sender and all original recipients
2
 -nosubject    @send forces you to provide a Subject: line
2
 +nosubject    allow entering the mail editor without giving a subject line
2
 -enter        start each mail editing session in the usual command mode
2
 +enter        start each mail editing session with an implicit `enter' command
2
 -expert       novice mail user (various annoying messages will be printed)
2
 +expert       expert mail user (suppress printing of annoying messages)
2
 -sticky       each mail command applies by default to one's own collection
2
 +sticky       each mail command applies by default to the same message 
2
                 collection that the previous successful command did
2
 -netmail      mail to you accumulates in your MOO mailbox
2
 +netmail      mail to you is actually forwarded to your registered email
2
                address, if you have one.
2
 -resend_forw  @resend puts player in Resent-By: header
2
 +resend_forw  @resend puts player in From: header (like @forward)
2

2
For "sticky", `mail command' is one of @mail, @read, @prev, @next, @answer.
2

2
All flags default to the `-' settings.  
2

2
Next, we have
2

2
         @mail-option  manymsgs [is] <number>
2
         @mail-option  manymsgs=<number>
2
         @mail-option -manymsgs
2

2
The first two forms specify that if you give a @mail or @read command asking for <number> or more messages, you will first be given a yes-or-no prompt to continue, the idea being that you many not actually have wanted to see that many messages.  The third form turns off this behavior.
2

2
         @mail-option  @mail [is] <message-sequence>
2

2
The "@mail" option determines what message-sequence the @mail command uses by default.  Initially, this is "last:15", but other reasonable choices include "new" and "1-last"
2

2
         @mail-option  replyto [is] <recipient> [<recipient>...]
2
         @mail-option -replyto
2

2
The first form specifies that a Reply-To: field is to be placed in all messages constructed by @send or @answer.  Note this can still be changed before sending via the mail room's reply-to command.  
2

2
The second form resets this option so that no Reply-to: is initially inserted.
2

2
        @mail-option rn_order=<order>
2

2
controls the order in which folders listed by @rn and @subscribed will appear.  <order> can be one of
2

2
 read    folders are sorted by last read date. (default)
2
 send    folders are sorted by last send date.
2
 fixed   folders are not sorted
35
5
4
1
2
Please see `help administrators' for information about the people who run JHM.
35
5
4
2
2
*forward*
2
@linelength
35
1
4
1
2
*full_index*
35
5
4
1
2
*index_list*
35
5
4
2
2
*index*
2
General Help Topics
35
5
4
28
2
There are 3 personal properties that you can use to customize how your mail is composed and forwarded.
2

2
.mail_forward
2
=============
2

2
A list of objects that will receive any mail that gets sent to you. Objects on this list should either be players or descendants of $mail_recipient. If this list is nonempty, you will not receive any mail yourself unless you are on it.  E.g., if Rog is #4292 and ur-Rog is #6349
2

2
  #6349.mail_forward={}            -- usual case; ur-Rog gets his own mail.
2
  #6349.mail_forward={#4292}       -- Rog gets ur-Rog's mail instead.
2
  #6349.mail_forward={#6349,#4292} -- ur-Rog gets mail and Rog gets a copy.
2
  #6349.mail_forward={#-1}         -- ur-Rog's mail disappears without a trace.
2

2
.mail_notify
2
============
2

2
A list of objects to be notified whenever mail is sent to you. This list may include anything that has a :notify_mail() verb. Notification will take place regardless of whether or how your mail is forwarded.  
2

2
Thus, in the previous example
2

2
  #4292.mail_notify={#6349} --- means that ur-Rog will be told
2
                                whenever Rog is sent new mail.
2

2
.mail_options
2
=============
2

2
This controls lots of miscellaneous things.  Use the @mail-option command to view and set these options (see `help @mail-option').
2

2
See `help mail-resolve' for more detail on how mail forwarding and mail notification work.  See `help MR-subscribing' for information on how to change .mail_forward and .mail_notify on $mail_recipient children, where they are !c properties.
35
5
4
20
2
Syntax:  @pagelength <number>
2
         @pagelength
2

2
If the lines you see scroll off the top of your screen too quickly for you to
2
read and your client program is such that any lines scrolling off the top are
2
gone forever, you can use the @pagelength command to invoke page buffering to
2
limit the number of lines sent at a time.  E.g., if your terminal has a 24 line
2
screen, you can do @pagelength 24 and output will stop every 24 lines if you
2
don't type any other commands.
2

2
You will need to use the @more command to continue reading output once it 
2
has been stopped.  Make sure you read `help @more' before setting @pagelength.
2

2
@pagelength 0 means no page buffering will be done by the MOO.
2

2
By default the MOO will assume you have an infinitely wide terminal screen, 
2
so you may wish to set @linelength as well.  As with word wrapping, you are
2
best off running a client that can do its own page buffering; the MOO server's
2
page buffering is inherently slower and many MUD's do not have page buffering
2
at all.
199
1
4
16
2
*subst*
2
Syntax:  @more
2
         @more rest
2
         @more flush
2

2
If you have @pagelength set (see `help @pagelength') and some combination of events or commands produces sufficiently many lines of output, you will see a message of the form
2

2
        %[player:more_string(37)]
2

2
indicating (in this case) 37 more lines of text waiting to be read.  At this point, you should give one of the @more commands above.  
2

2
@more without arguments prints sufficiently many lines to fill your screen, assuming you've set @pagelength correctly, unless there are not that many lines left to print.
2

2
@more rest will print all of the remaining lines, regardless of your @pagelength setting.  
2

2
@more flush discards all remaining lines.
199
1
4
3
2
MOO contains a rich programming language for the creation of interesting rooms, exits, and other objects.
2

2
You're not currently a programmer---for information on how you could become one, see `help programmer-policy'.
35
5
4
18
2
Syntax:  @forward <msg> [on *<collection>] to <recipient> [<recipient>...]
2
Syntax:  @resend  <msg> [on *<collection>] to <recipient> [<recipient>...]
2

2
Both of these commands take the indicated message in your (or some other) message collection, and sends it on to the indicated recipients in some form.
2

2
@forward sends an entirely new message whose body is the original message (both headers and body).
2
@resend sends the original message, but with a header containing the lines
2

2
  From:  original-sender
2
  To:    original-recipients...
2
  Resent-By: you
2
  Resent-To: new-recipients...
2

2
If you prefer to have yourself in the From: line of messages you @resend, set the mail option `resend_forw (see `help @mail-options').  In this case, the message will instead have a header containing the lines
2

2
  From:  you
2
  To:    new-recipients...
2
  Original-From:  original-sender
35
5
4
19
2
Syntax:  @subscribe *<collection> [with|without notification] [before|after *<collection>]
2
         @subscribe
2

2
The second form of the command gives a list of all mail collections that are readable by you.
2

2
The first form of this command sets up a current message and a last-read-time for the given mail collection so that when you next log in or issue the @rn command, you will be informed about new mail that has appeared there.  Note that this happens automatically whenever you @read messages on a given collection, so if this much is all you care about, you don't need to use this command; just do, e.g.,
2
         @read last on *<collection>
2

2
Specifying "with notification" causes you to be added to the immediate-notification list (.mail_notify) for that collection, i.e., whenever new mail arrives there, you will be notified immediately.  Specifying "without notification" causes you to be removed from the collection's .mail_notify.
2

2
Specifying "before *<other-collection>" causes <collection> to be placed immediately before *<collection> in your @rn listing (which see) and likewise for the "after" clause.  By default, new collections are placed at the end of your list.  The before/after specification is only useful if you have @mail-option rn_order=fixed set (see `help @mail-options').
2

2
@subscribing to a collection for which you already have a current-message/last-read-time has no effect other from possibly changing that collection's .mail_notify and/or reordering your collections for @rn.
2

2
You can only @subscribe to collections that are readable by you.
2

2
Note that this is entirely different from the Mail Room `subscribe' command which actually adds you to the .mail_forward list for a given collection/mailing-list, so that mail sent to the list actually shows up in your own mail collection.
2

2
We're probably going to phase out the Mail Room `subscribe' command...
35
5
4
9
2
Syntax:  @rn
2
         @subscribed
2
         @unsubscribed
2

2
For each collection of mail messages that you read other from your own, a last-read-time is kept.  The `@rn' command tells you which collections (out of all those you have ever read) have recently had messages added to them, i.e., more recently than when you last did a @read, @prev, @next, or @skip on that collection.
2

2
The `@subscribed' command shows all your mailing lists, even those which have no new mail; the `@unsubscribed' command shows mailing lists which you haven't read, and indicates how many messages you're missing out on.
2

2
Etymologists' note:  If you thought @rn had anything to do with the popular UNIX newsreading program `rn', it's just your imagination.
35
5
4
2
2
*forward*
2
@skip
35
5
4
8
2
Syntax:  @skip [<collection>...]
2
         @unsubscribe [<collection>...]
2

2
For each collection of mail messages that you read other from your own, a current message and a last-read-time is kept.  Normally, if you neglect to actually @read any messages on a collection other from your own, @rn (or :check_mail_lists) will continue to remind you that they are there.
2

2
The @skip command indicates that you're not interested in reading the rest of the messages that currently exist in that given collection.  
2

2
The @unsubscribe command flushes the current-message/last-read-time information completely, indicating that you are not only uninterested in the rest of the messages on that collection, but also likewise uninterested in anything else that may appear on that collection later on.  @unsubscribe also removes you from the collection's .mail_notify list.
35
5
4
2
2
*forward*
2
@unrmmail
35
5
4
52
2
Certain mail commands, including @mail, @read, and @rmmail, allow a <message-sequence> argument that indicates to which messages in one's collection the command is to apply.  Any combination of the following may appear as a <message-sequence> argument to any of the various mail commands (@mail, @read, @answer, @rmm).
2

2
  17        message number 17 if there is one (and likewise for other integers)
2
  17..23    all messages numbered between 17 and 23 (inclusive), if any
2
  cur       the current message
2
  prev      the message before
2
  next      the message after
2
  prev17    the 17 messages prior to the current message
2
  next17    the 17 messages after the current message
2
  first     the first message if any
2
  last      the final message if any (`$' is a synonym for `last')
2

2
You may use as many of these at once as sanity permits, e.g.,
2

2
  @mail cur 1..5 last
2

2
which will display the header for your current message, your messages in the range 1..5, and your last message.  Though some of these ranges may overlap, the header for any given message is only shown once in any event.
2

2
In addition, there are other message-sequence arguments that act as filters on whatever precedes them:
2

2
 before:<date>               messages strictly before the given date
2
 after:<date>                messages strictly after the given date
2
 since:<date>                messages on or after the given date
2
 until:<date>                messages on or before the given date
2
 from:<player>[|<player...]  messages from the given player(s)
2
 to:<recip>[|<recip>...]     messages to the given recipient(s)
2
 %from:<string>              messages with <string> in the From: line
2
 %to:<string>                messages with <string> in the To: line
2
 subject:<string>            messages with <string> in the subject
2
 body:<string>               messages with <string> in the body (SLOW!!!)
2
 first:<number>              the first <number> messages
2
 last:<number>               the last <number> messages
2

2
<date>  is either a weekday, "yesterday", "today" or
2
        a dd-Month, dd-Month-yy or dd-Month-yyyy date.
2
<recip> is either <player> or *<$mail_recipient kid>.
2

2
Examples:
2

2
  @read from:G7|Gemba              read all messages from G7 or Gemba
2
  @rmm to:yduJ|*Core               remove messages that are to yduJ or to *Core
2
  @mail since:1-Jan before:1-Feb   show messages dated in January
2
  @mail since:Tues                 show messages dated on or after Tuesday
2
  @rmm subject:manners             remove msgs with `manners' in the subject:
2
  @mail subject:"stupid idiots"    (search string contains a space => need "'s)
2
  @rmm to:yduJ to:*Core            remove messages that are to yduJ and *Core
2
  @mail from:Haakon last:5         show the last 5 messages from Haakon
2
  @mail %from:guest                show mail from players with "guest" in
2
                                   their names
2
  @mail last:10 body:fribble       show those of the last 10 messages having
2
                                   `fribble' in the body (one should always try
2
                                   to narrow body searches in this way).
35
5
4
9
2
Usage:  @quota [<someone>]
2

2
Each player has a limit as to how much database disk space e may consume with their objects, properties, and verbs.  Objects are periodically measured (and can have their measurement forced---see help @measure).  If a player is over quota after a measurement, e will not be able to @create any objects, use @copy, @verb, or @property to grow any objects.  Existing properties and verbs may be edited/changed at will, even while over quota.
2

2
The @quota command prints out your current quota and measured usage.  If you have more than one character, it will summarize all your other characters' usage as well.
2

2
@quota <player> will print out the quota and usage of another player.  [Note: you will not be able to obtain second character information for another player; whether primary or secondary character, the @quota display will only show one character's quota.]
2

2
@quota prints a time of measurement in its display.  This is the time of the oldest measurement out of all your objects.  For example, if you @measure three of your objects right now, those individual times get updated, but if you still have a couple of objects with older measurements, that older time will be the time printed by @quota.  This is confusing to many people.
199
1
4
22
2
Syntax:  @wrap <on|off>
2
         @wrap
2

2
         @linelength <number>
2
         @linelength
2

2
If the lines you see get cut off at the edge of your screen (you don't have
2
word-wrap), you can get the MOO to split lines for you.  The @linelength
2
command tells the MOO how many columns you have on your screen--you probably
2
want @linelength 79--and "@wrap on" tells the MOO you want it to do word-
2
wrap.
2

2
It's better if you can fix this problem without the MOO's help, though,
2
because the MOO's solution will be slower than a local solution, and
2
because not all MUDs are willing to do word-wrap.
2

2
If you don't want the MOO to split lines for you, there might still be some
2
use for the @linelength command.  Certain commands, like @who and @check,
2
print truncated lines so they can print in neat columns.  The default for
2
these is generally about 79 columns, which looks fine if you have an
2
eighty-column screen.  If your screen is a different width, though, you
2
can set @linelength and some of these commands will react accordingly.
35
5
4
13
2
*subst*
2
A few different messages can be set on a room object (see 'help messages' for instructions on doing so); they are printed to various audiences when a player or other object is ejected from the room.  (See 'help @eject'.)  The standard pronoun substitutions are made on each message before it is printed; see 'help pronouns' for details.
2

2
The default message is given in brackets after each name below:
2

2
@ejection  [%[$room.ejection_msg]]
2
  Printed to the player doing the ejecting.
2

2
@victim_ejection  [%[$room.victim_ejection_msg]]
2
  Printed to the object being ejected.
2

2
@oejection  [%[$room.oejection_msg]]
2
  Printed to others in the room from which the object is being ejected.
35
5
4
13
2
Syntax:  @unrmmail [list|expunge] [on *<collection>]
2

2
When you do @rmmail on a particular message collection, the messages removed don't go away immediately, but are rather saved elsewhere.  These "zombie" messages can be brought back or examined using the @UNrmmail command.
2

2
Without `list' or `expunge', @unrmm restores the zombie messages, thus undoing the effect of the most recent @rmmail command.  Only the most recent @rmmail can be undone in this way; messages deleted by any previous @rmmail commands on this same collection are lost and gone forever.
2

2
The `list' option merely lists the headers of the zombie messages without actually restoring them.
2

2
The `expunge' option banishes the zombie messages forever.
2

2
Note that the message numbers used by zombie messages are held in reserve against the possibility that you might @unrmm them some day; with such messages around, new messages received will be given higher numbers than you might have expected.  @renumber does an implicit @unrmm expunge.
2

2
`@unrmmail' and `@unrmmail expunge' on collections other than your own are only allowed when you have write access.  Likewise, `@unrmmail list' on other collections is only possible when they are readable by you.
35
5
4
2
2
*forward*
2
@listgag
35
5
4
2
2
*forward*
2
emote
35
5
4
2
2
*forward*
2
@typo
35
5
4
3
2
Syntax:  @remove-entrance <entrance>
2

2
Remove the specified entrance from the current entrances list of the room.  Entrance may be either the name or object number of an entrance to this room.
35
5
4
3
2
Syntax:  @remove-exit <exit>
2

2
Remove the specified exit from the current exits list of the room.  Exit may be either the name or object number of an exit from this room.
35
5
4
8
2
Syntax:  @parents object
2

2
A quick way to find out the ancestry of an object.  Prints out the names and object numbers of all ancestors.
2

2
Example:
2

2
  @parents #224
2
  living room (#224)   Generic House Room (#1412)   generic room (#3)   Root Class (#1)
35
5
4
9
2
Syntax:  @contents object
2

2
A quick way to find out the contents of an object.  Prints out the names and object numbers of all direct contents.  This can be useful when you need to refer to something by object number because something is wrong with its aliases.
2

2
Example:
2

2
  @contents here
2
  The Entrance Hall(#19) contains:
2
  Strasbourg Clock(#71)   mirror at about head height(#7444)
35
5
4
3
2
*forward*
2
summary
4
2
1
107
2
Type `help <topic>' for information on a particular topic, or `pick <number>' to follow a numbered link.
35
5
4
2
2
*forward*
2
security
35
1
4
17
2
OMNISCIENT CORE GROUP AND SYSADMINS
2
-----------------------------------
2

2
People in the Core Group (see `help admin-policy') can look at absolutely *anything* in the MOO database.  
2

2
The above mentioned parties, while they will endeavor to be discreet about anything incidental that turns up, nevertheless reserve the right look at anything they want, if only for the sake of being able to resolve technical problems.
2

2
LOGGING
2
-------
2

2
Some client programs (the "client" is the program you use to connect to the MOO, e.g., telnet, tinytalk, tinyfugue, emacs with mud.el...) are capable of logging everything that happens to the corresponding player.  In fact, with some clients this happens by default.  If a given player's client has logging enabled and said player is either in the room with you or is monitoring an object that is in the room with you, then *everything* you say and emote gets recorded.  Also, if you are in a room owned by someone else, all bets are off.  There is *no way* that the MOO server can know about what client a given player is using; thus, anyone who can hear you is a potential logger.
2

2
In and of itself this would not be a problem---indeed, logs are often useful for reference purposes.  However, there is no guarantee that the log will not end up someplace where you'd rather it didn't, e.g., posted on the rec.games.mud Usenet newsgroup.  While it considered bad form (i.e., rude) to circulate or post a log without having the permission of at least the major participants in the activities logged, there is not a whole lot we can do on the technical side to prevent it from happening.
2

2
Be aware of the problem.  The @sweep command (see `help @sweep') attempts to determine what players can be listening at any given moment.  If anything, it errs on the side of paranoia.  Even so, it doesn't cover *all* possible avenues of eavesdropping, and there's no hope for it covering the situations like the one where someone manages to convince one of the participants in your discussion who kept a log that it really doesn't need to be kept private after all.
2

2
If you've got something really sensitive to discuss, you are best off doing it by encrypted email or in person.
35
5
4
13
2
Syntax: @resident player
2
        @resident !player
2
        @resident
2

2
Adds or removes a player from the residents list of a room.  The residents list controls who is allowed to use @sethome in that room.  This defaults to just the owner of the room; by manipulating the residents list you may allow additional players to use that room as their home.
2

2
  * @resident player adds that player to the list.  
2
  * @resident !player removes that player from the list.
2
  * @resident with no arguments simply displays the current list (which may be "none", indicating no additional people besides the owner may use that room as their home).
2

2
See also `help @sethome'.
2

2
Hints for programmers: The verb $room:accept_for_abode is called by @sethome.  By overriding this verb you can give different criteria to @sethome.  It should return 1 for allowed and 0 for denied.
35
5
4
13
2
Syntax:  @examine <object>
2
         @exam <object>
2

2
Prints several useful pieces of information about the named object, including the following:
2

2
        - its full name, aliases, and object number
2
        - its owner's name and object number
2
        - its description
2
        - its key expression (if it is locked and if you own it)
2
        - its contents and their object numbers
2
        - the 'obvious' verbs defined on it
2

2
[Note to programmers: the 'obvious' verbs are those that are readable and that can be invoked as commands.  To keep a verb off this list, either make it unreadable (see 'help @chmod') or, if it shouldn't be used as a command, give it 'args' of 'this none this' (see 'help @args').]
35
5
4
5
2
There are several commands available to determine the origins of messages and to check that your communications with other players are secure. Help is available on the following topics:
2

2
@paranoid -- keeping a record of messages your character hears.
2
@check -- looking at that record to determine responsibility for messages.
2
@sweep -- checking who is listening in on your conversation.
35
5
4
3
2
Syntax: @sweep
2

2
Used when you wish to have a private conversation, and are concerned someone may be listening in. @sweep tries to list the avenues by which information may be leaving the room. In a manner analogous to @check, it assumes that you don't want to hear about your own verbs, or those belonging to the Core Group (see `help admin-policy'), who presumably wouldn't stoop to bugging.
35
5
4
6
2
Syntax:  @paranoid
2
         @paranoid off
2
         @paranoid immediate
2
         @paranoid <number>
2

2
In immediate mode, the monitor prepends everything you hear with the name of the character it considers responsible for the message. Otherwise, it keeps records of the last <number> (defaults to 20) lines you have heard. These records can be accessed by the @check command.
35
5
4
7
4
2
1
135
2
Syntax:   @check <options>
4
2
1
107
2
where <options> is one or more of:
4
4
1
120
2
the number of lines to be displayed
2
a player's name, someone to be "trusted" during the assignment of responsibility for the message.
2
a player's named prefixed by !, someone not to be "trusted".
4
2
1
107
2
Used when you are suspicious about the origin of some of the messages your character has just heard.
4
2
1
107
2
Output from @check is in columns that contain, in order, the monitor's best guess as to:
4
5
1
120
2
what object the message came from,
2
what verb on that object that was responsible,
2
whose permissions that verb was running with, and
2
the beginning of the actual message.
4
2
1
107
2
@check operates by examining the list of verbs that were involved in delivering the message, and assigning responsibility to the first owner it sees who is not "trusted".  By default, it trusts you and all the wizards.  It uses the records maintained by @paranoid, so you must have used that command before you received the message.
35
5
4
2
2
*forward*
2
@answer
35
5
4
10
2
Syntax: @eject <object>
2
        @eject <object> from <place>
2

2
This command is used to remove unwanted objects from places you own.  Players thus removed are unceremoniously dumped in their homes (unless that's this room, in which case they are dumped in the default player starting place).  Other kinds of objects are checked for a .home property and sent there if possible, otherwise they get thrown into #-1.  Unlike @move, @eject does *not* check to see if the object wants to be moved, and with the destination being what it is, there is no question of the destination refusing the move, either.  Generally, you should only resort to @eject if @move doesn't work.
2

2
The first form of the command removes the object from the current room.  The second form removes the object from the specified place (which, in most cases, you'll have to specify as an object number).  In either case, this command only works if you own the room/entity from which the object is being ejected.
2

2
"@eject ... from me" suffices to get rid of some unwanted object in your inventory.
2

2
See 'help room-messages' for a list of messages one can set regarding the @eject command.
35
5
4
3
2
Syntax:  @quit
2

2
Disconnect from the MOO.  This breaks your network connection and leaves your player sleeping.  Disconnecting in most parts of the MOO automatically returns your player to its designated home (see 'help home').
35
1
4
4
2
Syntax:  whereis [<player> [<player>...]]
2
        @whereis [<player> [<player>...]]
2

2
Returns the current location of each of the specified players, or of all players if not arguments given.
35
5
4
2
2
*forward*
2
@typo
35
5
4
2
2
*forward*
2
@typo
35
5
4
2
2
*forward*
2
@typo
35
5
4
9
2
Syntax:  @typo    [<text>]
2
         @bug     [<text>]
2
         @suggest [<text>]
2
         @idea    [<text>]
2
         @comment [<text>]
2

2
If <text> is given, a one-line message is sent to the owner of the room, presumably about something that you've noticed.  If <text> is not given, we assume you have more to say than can fit comfortably on a single line; the usual mail editor is invoked.  The convention is that @typo is for typographical errors on the room or objects found therein, @bug is for anomalous or nonintuitive behaviour of some sort, @idea/@suggest for any particular brainstorms or criticisms you might happen to have, and @comment for anything else.
2

2
The usual mail editor is only invoked for this command when in rooms that allow free entry, i.e., rooms that are likely to allow you back after you are done editing your message.  Otherwise these commands will require <text> and only let you do one-line messages.  
35
5
4
5
2
Syntax:  @renumber [<collection>]
2

2
Renumbers the messages in your collection to go from 1 to however many you have at the moment.  The optional argument allows you to renumber the messages stored in some other collection (@renumber, like @rmmail, requires write access).
2

2
Note that if you have zombie messages from a previous @rmmail command (see `help zombie-messages'), these will be expunged.
35
5
4
8
2
Syntax:  @notedit <note-object>
2
         @notedit <object>.<property>
2

2
Enters the MOO Note Editor to edit the text on the named object.
2

2
For the first form, <note-object> must be a descendant of $note.  For the second form, <object>.<property> can be any string-valued or text-valued (i.e., list of strings) property on any object.
2

2
See 'help editors' for more detail.
35
5
4
40
2
One can always enter an editor by teleporting to it, or you can use one of the commands provided
2

2
    @edit     <object>:<verb>    invokes the Verb Editor (edits verb code)
2
    @notedit  <note_object>      invokes the Note Editor (edits note text)
2
    @notedit  <object>.<prop>    invokes the Note Editor (edits text property)
2
    @send     <list of recipients>        invokes the Mailer (edits a mail msg)
2
    @answer   [<msg_number>] [<flags>...] invokes the Mailer (edits a reply)
2

2
This will transport you to one of several special rooms that have editing commands available.  These editors are admittedly not as good as EMACS, but for those with no other editing capability on their host systems, they are better than nothing.
2

2
There is a generic editor that provides basic editing commands that are applicable to all editors.  Documentation for these commands can be obtained by typing `help <topic>' within the editor:
2

2
    abort              emote/:            pause              send      (M) 
2
    also-to (M)        fill               prev               showlists (M) 
2
    compile (V)        insert             print     (M)      subject   (M) 
2
    copy               join               quit               subst         
2
    delete             list               ranges             to    (M)     
2
    done               move               save      (N)      what          
2
    edit    (V,N)      next               say/"              who   (M)     
2

2
In addition, individual editors provide their own additional commands for loading text from places, saving text to places, and various other specialized functions which are denoted in the above list with (M),(N),(V) according as they apply to the mail editor, the note editor, or the verb editor, respectively.
2

2
Note that a given editor only allows you one session at a time (ie. one verb, one note, or one mail message).  If you leave an editor without either aborting or compiling/saving/sending the item you're working on, that editor remembers what you are doing next time you enter it, whether you enter it by teleporting or by using the appropriate command.  Note that editors are periodically flushed so anything left there for sufficiently long will eventually go away.
2

2
A player may have his own .edit_options property which is a list containing one or more (string) flags from the following list
2

2
  quiet_insert
2
      suppresses those annoying "Line n added." or "Appended..." messages
2
      that one gets in response to 'say' or 'emote'.  This is useful if you're
2
      entering a long list of lines, perhaps via some macro on your client,
2
      and you don't want to see an equally long list of "Line n added..."
2
      messages.  What you do want, however is some indication that this all
2
      got through, which is why the "." command is an abbreviation for insert.
2

2
  eval_subs
2
      Enables the verb editor to process your eval_subs property when
2
      compiling your verb.  See `help eval' for more information about
2
      the eval_subs property.
2

2
There will be more options, some day.
35
5
4
3
2
Syntax:  @prev [<number>] [on <collection>]
2

2
Print the 'previous' message in a given email collection (defaults to your own).  The mail system's notion of your 'current message' for that collection is decremented.  Thus, e.g., one can review all of one's previous messages one-by-one simply by typing '@prev' repeatedly.  If <number> is supplied, prints and decrements the current message by that many messages.
35
5
4
6
2
Syntax:
2
  @unlock_for_open <container>
2

2
Clears the lock which restricts who may open <container>.  See 'help locking' for general information about locking. 
2

2
See 'help containers' for information on containers.
35
5
4
6
2
Syntax:
2
  @lock_for_open <container> with <key expression>
2

2
Set the lock on <container> which restricts who can open it.  See 'help locking' for general information about locking and 'help keys' for the syntax and semantics of key expressions.
2

2
See 'help containers' for information on containers.
35
5
4
4
4
2
1
107
2
Usage:  @opacity <container> is <integer>
4
2
1
107
2
The opacity can take on one of three values:
4
4
1
103
4
2
2
0
2
The container is transparent and you can always see into it.
4
2
2
1
2
The container is opaque, and you cannot see into it when it's closed.
4
2
2
2
2
The container is a black hole, and you can never see into it.
4
2
1
107
2
The default @opacity is 1.
35
5
4
30
2
*subst*
2
Several kinds of messages can be set on a container object; they are printed to various audiences at certain times whenever an attempt is made to use the container.  The standard pronoun substitutions (with respect to the player) are made on each message before it is printed; see 'help pronouns' for details.
2

2
The default message is given in brackets after each name below:
2

2
@empty  [%[player:unparse_message($container.empty_msg)]]
2
  Printed in place of the contents list when the container is empty.
2

2
@put  [%[player:unparse_message($container.put_msg)]]
2
  Printed to the player and the room when an object is successfully placed in a container.
2

2
@put_fail  [%[player:unparse_message($container.put_fail_msg)]]
2
  Printed when the player fails to put an object in a container.
2

2
@remove  [%[player:unparse_message($container.remove_msg)]]
2
  Printed to the player and the room when the player succeeds in removing an object from a container.
2

2
@remove_fail  [%[player:unparse_message($container.remove_fail_msg)]]
2
  Printed to the player when the player fails to remove an object from a container.
2

2
The following messages only apply to openable containers (descendants of $openable_container):
2

2
@open  [%[player:unparse_message($openable_container.open_msg)]]
2
  Printed to the player and the room if the player successfully opens the container.
2

2
@open_fail  [%[player:unparse_message($openable_container.open_fail_msg)]]
2
  Printed to the player who cannot open the container.
2

2
@close  [%[player:unparse_message($openable_container.close_msg)]]
2
  Printed to the player who closes a container.
35
5
4
3
2
Syntax:  @memory
2

2
Prints out all information available on the current memory-usage behavior of the MOO server.  Probably only an administrator, if anyone, cares about this.
35
1
4
2
2
*forward*
2
say
35
1
4
2
2
*forward*
2
emote
35
1
4
6
2
Syntax:  @lastlog
2
         @lastlog <player>
2

2
The first form prints out a list of all players, roughly sorted by how long it's been since that player last connected to the MOO.  For each player, the precise time of their last connection is printed.
2

2
The second form only shows the last-connection time for the named player.
35
1
4
3
2
Syntax:  @version
2

2
Prints out the version number for the currently-executing MOO server.
35
1
4
4
2
Here are a few commands of occasional utility that didn't fit into any of the neat categories for the rest of the help system:
2

2
@version -- printing the MOO server version number
2
@lastlog -- finding out when some player last connected to the MOO
35
1
4
2
2
*forward*
2
put
35
1
4
2
2
*forward*
2
help
35
1
4
2
2
*forward*
2
help
35
1
4
4
2
Syntax:  put <object> into <container>
2
         insert <object> in <container>
2

2
Moves the named object into the named container (see `help containers').  Sometimes the owners of the object and/or the container will not allow you to do this.
35
1
4
2
2
*forward*
2
take
35
1
4
3
2
Syntax:  burn <letter>
2

2
Destroy the named letter irretrievably.  Only players who can read the letter can do this.
35
1
4
3
2
Syntax:  decrypt <note>
2

2
Removes any restriction on who may read the named note or letter.  Only the owner of a note may do this.
35
1
4
3
2
Syntax:  encrypt <note> with <key-expression>
2

2
Restricts the set of players who can read the named note or letter to those for whom the given key expression is true.  See 'help keys' for information on the syntax and semantics of key expressions.  Only the owner of a note may do this.
35
1
4
3
2
Syntax:  delete <line-number> from <note>
2

2
Removes a single line of text from a note.  The first line of text is numbered 1, the second is 2, and so on.  Only the owner of a note may do this.
35
1
4
3
2
Syntax:  erase <note>
2

2
Deletes all of the text written on a note or letter.  Only the owner of a note may do this.
35
1
4
3
2
Syntax:  write "<any text>" on <note>
2

2
Adds a line of text to the named note or letter.  Only the owner of a note may do this.
35
1
4
3
2
Syntax:  read <note>
2

2
Prints the text written on the named object, usually a note or letter.  Some notes are encrypted so that only certain players may read them.
35
1
4
11
2
Syntax:  examine <object>
2
         exam <object>
2

2
Prints several useful pieces of information about the named object, including the following:
2

2
        - its full name, object number, and aliases
2
        - its owner's name
2
        - its description
2
        - its key expression (if it is locked and if you own it)
2
        - its contents
2
        - the 'obvious' verbs defined on it
35
1
4
2
2
*forward*
2
give
35
1
4
13
2
The representation of key expressions is very simple and makes it easy to construct new keys on the fly.
2

2
Objects are represented by their object numbers and all other kinds of key expressions are represented by lists.  These lists have as their first element a string drawn from the following set:
2

2
        "&&"     "||"     "!"     "?"
2
  
2

2
For the first two of these, the list should be three elements long; the second and third elements are the representations of the key expressions on the left- and right-hand sides of the appropriate operator.  In the third case, "!", the list should be two elements long; the second element is again a representation of the operand.  Finally, in the "?" case, the list is also two elements long but the second element must be an object number.
2

2
As an example, the key expression
2
        #45  &&  ?#46  &&  (#47  ||  !#48)
2
would be represented as follows:
2
        {"&&", {"&&", #45, {"?", #46}}, {"||", #47, {"!", #48}}}
35
1
4
24
2
There is a simple but powerful notation for specifying locks on objects, encryption on notes, and other applications.  The idea is to describe a constraint that must be satisfied concerning what some object must be or contain in order to use some other object.
2

2
The constraint is given in the form of a logical expression, made up of object numbers connected with the operators 'and', 'or', and 'not' (written '&&', '||', and '!', for compatibility with the MOO programming language).  When writing such expressions, though, one usually does not use object numbers directly, but rather gives their names, as with most MOO commands.
2

2
These logical expressions (called 'key expressions') are always evaluated in the context of some particular 'candidate' object, to see if that object meets the constraint.  To do so, we consider the candidate object, along with every object it contains (and the ones those objects contain, and so on), to be 'true' and all other objects to be 'false'.
2

2
As an example, suppose the player Munchkin wanted to lock the exit leading to his home so that only he and the holder of his magic wand could use it.  Further, suppose that Munchkin was object #999 and the wand was #1001.  Munchkin would use the '@lock' command to lock the exit with the following key expression:
2
        me || magic wand
2
and the system would understand this to mean
2
        #999 || #1001
2
That is, players could only use the exit if they were (or were carrying) either #999 or #1001.
2

2
To encrypt a note so that it could only be read by Munchkin or someone carrying his book, his bell, and his candle, Munchkin would use the 'encrypt' command with the key expression
2
        me || (bell && book && candle)
2

2
Finally, to keep players from taking a large gold coffin through a particularly narrow exit, Munchkin would use this key expression:
2
        ! coffin
2
That is, the expression would be false for any object that was or was carrying the coffin.
2

2
There is one other kind of clause that can appear in a key expression:
2
        ? <object>
2
This is evaluated by testing whether the given object is unlocked for the candidate object; if so, this clause is true, and otherwise, it is false.  This allows you to have several locks all sharing some single other one; when the other one is changed, all of the locks change their behavior simultaneously.
2

2
[Note to programmers: The internal representation of key expressions, as stored in .key on every object, for example, is very simple and easy to construct on the fly.  For details, see 'help key-representation'.]
35
1
4
3
2
Syntax:  @unlock <object>
2

2
Clear any lock that might exist on the given object.  See 'help locking' for general information about locking.
35
1
4
5
2
Syntax:  @lock <object> with <key expression>
2

2
Set a lock on <object> to restrict its use.  See 'help locking' for general information about locking and 'help keys' for the syntax and semantics of key expressions.
2

2
N.B.  In the case of rooms, you are actually better off setting room.free_entry to 0 thus preventing teleportation and then @locking the various entrances.  The problem with @locking the room itself is that this can make it impossible to drop objects in the room.
35
1
4
10
4
2
1
107
2
It is frequently useful to restrict the use of some object.  For example, one might want to keep people from using a particular exit unless they're carrying a bell, a book, and a candle.  Alternatively, one might allow anyone to use the exit unless they're carrying that huge golden coffin in the corner.  LambdaMOO supports a general locking mechanism designed to make such restrictions easy to implement, usually without any programming.
4
2
1
107
2
Every object supports a notion of being 'locked' with respect to certain other objects.  For example, the exit above might be locked for any object that was carrying the coffin object but unlocked for all other objects.  In general, if some object 'A' is locked for another object, 'B', then 'B' is usually prevented from using 'A'.  Of course, the meaning of 'use' in this context depends upon the kind of object.
4
2
1
107
2
The various standard classes of objects use locking as follows:
4
4
1
120
4
5
1
96
4
3
1
110
4
2
1
139
4
1
4
2
2
subject
2
rooms
2
rooms
2
 and 
4
3
1
110
4
2
1
139
4
1
4
2
2
subject
2
containers
2
containers
2
 refuse to allow any object inside them if they're locked for it.
4
3
1
96
4
3
1
110
4
2
1
139
4
1
4
2
2
subject
2
exits
2
exits
2
 refuse to transport any object that they're locked for.
4
7
1
96
4
3
1
110
4
2
1
139
4
1
4
2
2
subject
2
things
2
things
2
 (including 
4
3
1
110
4
2
1
139
4
1
4
2
2
subject
2
notes
2
notes
2
 and 
4
3
1
110
4
2
1
139
4
1
4
2
2
subject
2
letters
2
letters
2
) cannot be moved to locations that they're locked for.
4
2
1
107
2
There are two sides to locking:
4
3
1
120
2
How is it specified whether one object is locked for another one?
2
What is the effect of an object being locked?
4
2
1
107
2
Note that these two questions are entirely independent: one could invent a brand-new way to specify locking, but the effect of an exit being locked would be unchanged.
4
2
1
107
2
[Note to programmers: the interface between these two sides is the verb x:is_unlocked_for(y), which is called by x to determine if it is locked for the object y.  The way in which 'is_unlocked_for' is implemented is entirely independent of the ways in which x uses its results.  Note that you can play on either side of this interface with your own objects, either defining new implementations of 'is_unlocked_for' that match your particular circumstances or having your objects interpret their being locked in new ways.]
4
2
1
107
2
There is a default way to specify locks on objects; the following help topics cover the relevant commands:
4
3
1
103
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@lock
2
@lock
2
setting a lock on an object
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@unlock
2
@unlock
2
clearing the lock on an object
35
1
4
28
2
*subst*
2
Several kinds of messages can be set on 'things', objects that have $thing as an ancestor (see 'help messages' for instructions on doing so).  They are printed to various audiences under various circumstances when an attempt is made to 'take' or 'drop' a thing.  The ones whose names begin with 'o' are always shown prefixed with the name of the player making the attempt and a single space character.  The standard pronoun substitutions (with respect to the player) are made on each message before it is printed; see 'help pronouns' for details.
2

2
The default message is given in brackets after each name below:
2

2
@take_failed  [%[player:unparse_message($thing.take_failed_msg)]]
2
  Printed to a player who fails to take the object.
2

2
@otake_failed [%[player:unparse_message($thing.otake_failed_msg)]]
2
  Printed to others in the same room if a player fails to take the object.
2

2
@take_succeeded  [%[player:unparse_message($thing.take_succeeded_msg)]]
2
  Printed to a player who succeeds in taking the object.
2

2
@otake_succeeded  [%[player:unparse_message($thing.otake_succeeded_msg)]]
2
  Printed to others in the same room if a player succeeds in taking the object.
2

2
@drop_failed  [%[player:unparse_message($thing.drop_failed_msg)]]
2
  Printed to a player who fails to drop the object.
2

2
@odrop_failed [%[player:unparse_message($thing.odrop_failed_msg)]]
2
  Printed to others in the same room if a player fails to drop the object.
2

2
@drop_succeeded  [%[player:unparse_message($thing.drop_succeeded_msg)]]
2
  Printed to a player who succeeds in dropping the object.
2

2
@odrop_succeeded  [%[player:unparse_message($thing.odrop_succeeded_msg)]]
2
  Printed to others in the room if a player succeeds in dropping the object.
35
1
4
2
2
*forward*
2
drop
35
1
4
9
2
Syntax:  take <object>
2
         get <object>
2
         take <object> from <container>
2
         get <object> from <container>
2
         remove <object> from <container>
2

2
The first two forms pick up the named object and place it in your inventory.  Sometimes the owner of the object won't allow it to be picked up for some reason.
2

2
The remaining forms move the named object from inside the named container (see 'help containers') into your inventory.  As before, sometimes the owner of an object will not allow you to do this.
35
1
4
3
2
Syntax:  @messages <object>
2

2
List all of the messages that can be set on the named object and their current values.  See 'help messages' for more details.
35
1
4
16
2
*subst*
2
Some kinds of messages are not printed directly to players; they are allowed to contain special characters marking places to include the appropriate pronoun for some player.  For example, a builder might have a doorway that's very short, so that people have to crawl to get through it.  When they do so, the builder wants a little message like this to be printed:
2

2
        Balthazar crawls through the little doorway, bruising his knee.
2

2
The problem is the use of 'his' in the message; what if the player in question is female?  The correct setting of the 'oleave' message on that doorway is as follows:
2

2
        "crawls through the little doorway, bruising %[$player:unparse_message("%%{pronoun player pp}")] knee."
2

2
The `%[player:unparse_message("%%{pronoun player pp}")]' in the message will be replaced by either 'his', 'her', or 'its', depending upon the gender of the player.  
2

2
As it happens, you can also refer to elements of the command line (e.g., direct and indirect objects) the object issuing the message, and the location where this is all happening.  In addition one can get at various other information, like verb conjugation, plural/singular nouns, and strings from the command line.
2

2
The MOO offers a variety of pronoun substitution styles; the default is described in `help two-letter'.
2

2
For an example of using pronoun substitution in programming, see `help pronoun-example'.
35
1
4
22
2
*subst*
2
Several kinds of messages can be set on an exit object (see 'help messages' for instructions on doing so); they are printed to various audiences at certain times whenever an attempt is made to go through the exit.  The ones whose names begin with 'o' are always shown prefixed with the name of the player making the attempt and a single space character.  The standard pronoun substitutions (with respect to the player) are made on each message before it is printed; see 'help pronouns' for details.
2

2
The default message is given in brackets after each name below:
2

2
@leave  [%[player:unparse_message($exit.leave_msg)]]
2
  Printed to the player just before they successfully use the exit.
2

2
@oleave  [%[player:unparse_message($exit.oleave_msg)||"has left."]]
2
  Printed to others in the source room when a player successfully uses the exit.
2

2
@arrive  [%[player:unparse_message($exit.arrive_msg)]]
2
  Printed to the player just after they successfully use the exit.
2

2
@oarrive  [%[player:unparse_message($exit.oarrive_msg)||"has arrived."]]
2
  Printed to others in the destination room when a player successfully uses the exit.
2

2
@nogo  [%[player:unparse_message($exit.nogo_msg)||"You can't go that way."]]
2
  Printed to the player when they fail in using the exit.
2

2
@onogo  [%[player:unparse_message($exit.onogo_msg)]]
2
  Printed to others when a player fails in using the exit.
35
1
4
21
2
Most objects have messages that are printed when a player succeeds or fails in manipulating the object in some way.  Of course, the kinds of messages printed are specific to the kinds of manipulations and those, in turn, are specific to the kind of object.  Regardless of the kind of object, though, there is a uniform means for listing the kinds of messages that can be set and then for setting them.
2

2
The '@messages' command prints out all of the messages you can set on any object you own.  Type 'help @messages' for details.
2

2
To set a particular message on one of your objects use a command with this form:
2

2
        @<message-name> <object> is "<message>"
2

2
where '<message-name>' is the name of the message being set, <object> is the name or number of the object on which you want to set that message, and <message> is the actual text.
2

2
For example, consider the 'leave' message on an exit; it is printed to a player when they successfully use the exit to leave a room.  To set the 'leave' message on the exit 'north' from the current room, use the command
2

2
        @leave north is "You wander in a northerly way out of the room."
2

2
[Note to programmers: This class of commands automatically applies to any property whose name ends in '_msg'.  Thus, in the example above, the command is setting the 'leave_msg' property of the named exit.  You can get such a command to work on new kinds of objects simply by giving the appropriate properties names that end in '_msg'.  Additionally, in many cases the _msg property is accompanied by a _msg verb, which defaultly returns the named property, but which is available to be customized in more complex ways than allowed by simple string substitution.  You should check for the particular property you're considering whether the verb form exists (typically with @list).]
2

2
The following help topics describe the uses of the various messages available on standard kinds of objects:
2

2
container-messages -- the messages on objects that can contain other objects
2
exit-messages -- the messages on exit objects
2
thing-messages -- the messages on objects that can be taken and dropped
35
1
4
5
2
Most objects have one or more descriptive pieces of text associated with them; these texts are printed under various circumstances depending on the kind of text.  For example, every object has a 'description' text that is printed whenever a player looks at the object.  The following help topics discuss the commands for manipulating these descriptive texts on your objects:
2

2
@rename -- setting the name and aliases of your objects
2
@describe -- setting what others see when they look at your objects
2
messages -- listing and setting the other descriptive texts on an object
35
1
4
15
2
Syntax:  @describe <object> as <description>
2

2
Sets the description string of <object> to <description>.  This is the string that is printed out whenever someone uses the 'look' command on <object>.  To describe yourself, use 'me' as the <object>.
2

2
Example:
2

2
Munchkin types this:
2
  @describe me as "A very fine fellow, if a bit on the short side."
2
People who type 'look Munchkin' now see this:
2
  A very fine fellow, if a bit on the short side.
2

2
Note for programmers
2
====================
2

2
The description of an object is kept in its .description property.  For descriptions of more than one paragraph, .description can be a list of strings.
35
1
4
3
2
Syntax:  @add-entrance <exit-object-number>
2

2
Add the exit with the given object number as a recognized entrance to the current room (that is, one whose use is not considered teleportation).  Usually, @dig does this for you, but it doesn't if you don't own the room in question.  Instead, it tells you the object number of the new exit and you have to find the owner of the room and get them to use the @add-entrance command to link it up.
35
1
4
3
2
Syntax:  @add-exit <exit-object-number>
2

2
Add the exit with the given object number as a conventional exit from the current room (that is, an exit that can be invoked simply by uttering its name, like 'east').  Usually, @dig does this for you, but it doesn't if you don't own the room in question.  Instead, it tells you the object number of the new exit and you have to find the owner of the room and get them to use the @add-exit command to link it up.
35
1
4
8
2
The topology of the MOO universe is determined by the rooms that exist and the exits that connect them.  Several commands are available for creating and discovering the topology of the MOO.  Help on them is available under the following topics:
2

2
@dig -- creating new rooms and exits
2
@add-exit -- adding other players' exits from your rooms
2
@add-entrance -- adding other player's entrances to your rooms
2
@exits -- listing all of the conventional exits from your rooms
2
@entrances -- listing all of the conventional entrances to your rooms
2
@resident -- listing or changing the residents of your rooms
35
1
4
23
2
Syntax:  @audit
2
         @audit <player>
2
         @audit [<player>] [from <number>] [to <number>] [for <string>]
2

2
The first form prints out a list of every object you own, giving each one's name and object number.
2

2
The second form does the same for the named player.
2

2
The third form shows the full blown syntax.  It prints out objects owned by the named player (defaulting to yourself if not supplied), but begins searching the database with the `from' numbered object (defaulting to #0), and stops searching with the `to' numbered object (defaulting to the highest numbered object in the database).  The `for' string restricts the search to objects whose names begin with that string.  These search restrictors can be useful if you know the player does not own any objects below or above a certain number, or if the player owns so many things as to be confounding.
2

2
All forms of @audit print out two kinds of numbers, for example:
2

2
#2450 ...
2
#5582 ...
2
My First Thing (#7884)
2

2
The first two numbers are just to let you know @audit is still working--you do not own them.  The third number is an object you own.
2

2
See also `help @prospectus', a slightly different brand of @audit.
2

2
Note: The current implementation of @audit uses a property .owned_objects on the player, for speed.  This property is updated at the time of each object creation and destruction and ownership change.  The verb @auditdb (same args as @audit) actually searches through the entire database for objects.
2

2
See also `help @verify-owned', `help @sort-owned', and `help @add-owned' for information on maintaining the .owned_objects list.
35
1
4
3
2
Syntax:  @count
2

2
Prints out the number of objects you own.  Do not be surprised if this is one larger than you think it should be: remember that your player object is owned by you as well, even though you didn't create it in the usual way.
35
1
4
3
2
*forward*
2
common_quota
2
If you want to get more quota, please see `help quota-policy'.
35
1
4
3
2
Syntax:  @next [<number>] [on <collection>]
2

2
Print the `next' message in a given email collection (defaults to your own).  The mail system's notion of your 'current message' for that collection is incremented.  Thus, e.g., one can read all of one's new messages one-by-one simply by typing '@next' repeatedly.  If <number> is supplied, prints and advances that many messages at once.
35
1
4
13
2
Syntax:  @answer [<message-number>] [sender] [all] [include] [noinclude]
2

2
Synonym: @reply
2

2
Prepares for you to compose a MOO email message to the players who either received or composed the indicated message from your collection.  The usual editor is invoked (see `help editors' for details).  The subject line for the new message will be initialized from that of the indicated message.  If you leave off the message number, the reply will be to your current message, if that exists.  In fact you may give a general message-sequence (see `help message-sequences') argument here, provided that it indicates only a single message (e.g., `@answer last:1')
2

2
If there is a Reply-to: field in the message you are @answer'ing, its contents will be used to initialize the To: line of your reply.  Otherwise, a To: line is determined depending on whether you specified `sender' or `all' in the command line (or your .mail_options).
2

2
`include' includes the text of the original message in your reply, `noinclude' does not.  
2

2
`sender', `all', `include', and `noinclude' can each be abbreviated (e.g., `@answer i').
2

2
Defaults are `sender' and `noinclude', but you can change this by setting your .mail-options (see `help mail-options').  
35
5
4
9
2
Syntax:  @rmmail [<message-sequence>] [from *<recipient>]
2

2
Deletes the indicated messages from your MOO email collection.  By default, your current message is deleted.
2

2
See `help message-sequence' for the full list of possible arguments.  You get the message numbers for use here by typing the '@mail' command  (see `help @mail').
2

2
There is no confirmation for this action, so be careful.  While it is true that @unrmmail will be able to bring back the messages deleted by the last @rmm, any messages deleted before then are really gone, i.e., irrecoverable.  See `help @unrmmail' for more information.
2

2
This command may be used on other mail recipients (children of $mail_recipient), but only ones to which you have write access (you either have to own it or be on good terms with whoever does...).
35
5
4
14
2
Syntax:  @read
2
         @read <message-sequence>...
2
         @read <message-sequence>... on *<recipient>
2
         @peek [<message-sequence>...[ on *<recipient>]]
2

2
Prints the contents of the indiciated messages from your MOO email collection.  You get the message numbers for use here by typing the '@mail' command, which prints a table of contents for your entire MOO email collection.  If no arguments are given to @read, then the 'current message' in your collection is printed.  In any case, the 'current message' after @read finishes is the last one printed.
2

2
The most general form of the @read command is
2

2
         @read <message-sequence> [on *<collection>]  
2

2
where <message-sequence> is as described in `help message-sequences'.  As with the @mail command you may @read messages on any publically readable collection (child of $mail_recipient).
2

2
The @peek command is identical to @read, but doesn't set your current message.  Use this if you want to take a look at a message other than the current one but not lose your place.
35
5
4
9
2
Syntax:  @send <recipient> [<recipient> ...]  [subj[ect]="<subject>"]
2

2
Prepares for you to compose a MOO email message to the recipients named on the command line.  A recipient can be specified by giving a player name or object-id, or a '*' followed by the name or object-id of some non-player mail recipient (e.g., a mailing list or a mail folder) -- a list of such non-player recipients is available from within the mailroom with the 'showlists' command.
2

2
The usual editor is invoked.  You will be prompted for a subject line if you did not specify one in the @send command (see `help mail_options' for how to avoid this).
2

2
Use `say' (") to insert lines in the body of your message.  See `help editors' for details.
2

2
Giving this command without arguments resumes editing the previous unsent draft message if one exists.
35
5
4
9
4
2
1
107
2
The MOO email system allows you to send and receive messages to and from other players.  It provides commands to view and manage your own collection of saved messages and, in addition, to browse through other collections that may be available (e.g.,archives of public mailing lists).  Help is available on the following commands:
4
5
1
103
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@mail       
2
@mail       
2
seeing a table of contents for a collection of email messages
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@read       
2
@read       
2
reading individual messages 
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@next       
2
@next       
2
reading the 'next'     message
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@prev       
2
@prev       
2
reading the 'previous' message
4
5
1
103
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@send       
2
@send       
2
composing and sending a message to other players
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@answer     
2
@answer     
2
replying to one of the messages in a collection
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@forward    
2
@forward    
2
resending one of the messages in a collection somewhere else
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@forward-me 
2
@forward-me 
2
resending a message to your email address
4
4
1
103
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@rmmail     
2
@rmmail     
2
discarding some subset of a collection
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@unrmmail   
2
@unrmmail   
2
undoing the most recent @rmm on a collection
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@renumber   
2
@renumber   
2
renumbering the messages in a collection
4
2
1
103
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@mail-option
2
@mail-option
2
describing and setting various customization flags
4
2
1
107
2
For viewing collections other from your own, the following commands are useful:
4
5
1
103
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@rn         
2
@rn         
2
list those collections that have new messages on them
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@subscribe  
2
@subscribe  
2
indicate that you want @rn to report on a given collection and add yourself to its .mail_notify list
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@skip       
2
@skip       
2
ignore any remaining new messages in a given collection
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
@unsubscribe
2
@unsubscribe
2
ignore a given collection entirely from now on and remove yourself from its .mail_notify list
4
2
1
107
2
Two other related topics:
4
3
1
103
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
mail-forwarding
2
mail-forwarding
2
describes mail forwarding
4
2
4
3
1
99
4
2
1
139
4
1
4
2
2
subject
2
message-sequences
2
message-sequences
2
describes message-sequences arguments to @mail, @read, @rmm, and @answer.
35
5
4
13
2
Syntax:  @gripe <anything> ...
2

2
Puts you into the MOO mail system to register a complaint (or, conceivably, a compliment) with the wizards.  The rest of the command line (the <anything> ... part) is used as the subject line for the message.  More information on using the MOO mail system is given once you're in it.
2

2
You may hear back from the wizards eventually; see 'help @mail' for how to read their reply.
2

2
Example:
2
Munchkin types:
2
  @gripe The little bird
2
  "How come I can't ever see the little bird in the cuckoo clock?
2
  "        -- A frustrated player
2
  send
2
and, somewhat later, the wizards reply with a note about being sure to look while the clock is chiming.
35
5
4
9
2
The primary means for players to extend the MOO is for them to create new objects with interesting behavior.  There are convenient commands for creating and recycling objects and for keeping track of the objects you've created.  Help is available on these commands in the following topics:
2

2
@dig -- conveniently building new rooms and exits
2
@create -- making other kinds of objects
2
@recycle -- destroying objects you no longer want
2
@quota -- determining how many more objects you can build
2
@count -- determining how many objects you already own
2
@audit -- listing all of your objects
2
@move -- moving your objects from place to place
35
1
4
15
2
Syntax:  @mail
2
         @mail new            (to see recent messages)
2
         @mail 1-$            (to see *all* messages)
2

2
Shows a table of contents for your MOO email message collection.  You are notified when you connect to the MOO if there are any such messages.  A little arrow indicates the mail system's notion of your 'current message'.
2

2
The first form lists all of your messages or the last 15 if you have more than that many; the second form lists only those messages after your `current message'.  The third form shows your entire collection.
2

2
If you have a large number of mail messages, you can give arguments so that @mail only lists the messages you're interested in.  You can also list messages residing on mail recipients which are public or for which you have read access.  The general format is
2

2
         @mail <message-sequence> [on *<recipient>]
2

2
where <recipient> names some child of $mail_recipient (e.g., a mailing list) and <message-sequence> is either a sequence of message numbers or one of the other possibilites described in `help message-sequences'.
2

2
Note that if you view mail on some other recipient and have "sticky" in your .mail_options (see `help mail-options'), all further mail commands (e.g., @read, @next, @rmm,...) will apply to that recipient.  In this case use `@mail on me' to go back to looking at your own mail.
35
1
4
4
2
Syntax:  @listgag
2
         @gaglist
2

2
Shows you a list of the players and objects currently on your 'gag list'.  You don't see any messages that result from actions initiated by the players or objects on this list.  In particular, you will not hear them if they try to speak, emote, or whisper to you.  See 'help gagging' for an explanation of gagging in general.
35
1
4
10
2
Syntax:  @ungag <player or object>
2
         @ungag everyone
2

2
Remove the given player or object (or, in the second form, everyone) from your 'gag list'.  You will once again see any messages that result from actions initiated by the ungagged player(s) or objects.  In particular, you will once again be able to hear them if they speak, emote, or whisper to you.  See 'help gagging' for an explanation of gagging in general.
2

2
Example:
2

2
Munchkin types:
2
  @ungag Grover
2
and is once again able to hear Grover's witty remarks.  Sigh...
35
1
4
17
2
Syntax:  @gag <player or object> [<player or object>...]
2

2
Add the given players to your 'gag list'.  You will no longer see any messages that result from actions initiated by these players.  In particular, you will not hear them if they try to speak, emote, or whisper to you.  See 'help gagging' for an explanation of gagging in general.
2

2
Example:
2

2
Munchkin types:
2
  @gag Grover
2
and no longer hears anything that Grover says.  What a relief!
2

2
If you specify an object, then any text originating from that object will not be printed.  
2

2
Example:  Noisy Robot prints "Hi there" every 15 seconds.   In order to avoid seeing that, Munchkin types:
2

2
  @gag Noisy
2

2
and no longer hears that robot!  (Munchkin must be in the same room as Noisy Robot for this to work, or know its object number.)
35
1
4
2
4
3
1
135
2
Usage: go <direction> ...
2
       go to <location>
4
2
1
107
2
The first form moves in the given directions in order, taking you through many rooms in a single command.  The second form will try to figure out a path that will bring you to your desired destination.
35
1
4
5
2
Syntax: @password <old-password> <new-password>
2

2
Changes your player's password (as typed in the 'connect' command when you log in to the MOO) to <new-password>.  For security reasons, you are required to type your current (soon to be old) password as the first argument.
2

2
Your password is stored in an encrypted form in the MOO database; in principle, not even the administrators can tell what it is, though they can change it, of course.  It is recommended that your password not be your name or a common word; MOO passwords have been stolen or cracked in the past and characters have been made unhappy by such theft.  Your password is your security; choose a safe one.
35
1
4
3
2
Syntax: @sethome
2

2
Sets your designated home (see `help home') to be the room you're in now.  If the current room wouldn't allow you to teleport in, then the `@sethome' command nicely refuses to set your home there.  This avoids later, perhaps unpleasant, surprises.  Additionally, your home must be a room that will allow you to stay there.  Rooms which you own will do this, as will rooms to which you have been added as a resident.  See the @resident command for help on adding someone as a resident to a room you own.
35
1
4
17
2
*subst*
2
Syntax: @who
2
        @who <player> [<player> ... ]
2
        @who [<players>] by <order>
2

2
        @who in <room>
2
        @who with <player>
2

2
The first form lists all of the currently-connected players, along with an indication of how long they've been idle, their present location in the MOO, and their doing/idle string (see `help doing' and `help idle').
2

2
The second form, in which a list of player names is given, shows information for just those players.  For any listed players that are not connected, we show the last login time instead of the idle info.
2

2
The third form lets you specify a sort order.  (See `help @who-options'.)  You can also specify the players you want to see, or specify nothing to see everybody who's on.
2

2
The fourth and fifth forms show you @who information for everybody in a given room, or everybody in the same room as a given player, respectively.
2

2
@who's format and sorting order can be modified using @who-options (see `help @who-options').
35
1
4
19
2
A MOO is a kind of virtual reality, in which players move about from place to place manipulating their environment in what we hope are amusing, entertaining, or enlightening ways.
2

2
This is just a simple introduction to how to get around; more introductory texts are being written and will be listed here as we finish them.
2

2
Most commands have the form of simple English sentences:
2

2
    <verb>
2
    <verb>  <direct object>
2
    <verb>  <direct object>  <preposition>  <indirect object>
2

2
Don't use English articles (e.g. 'a', 'an', or 'the') in your commands; the MOO won't understand them.  You can refer to yourself as 'me' and the room you're in as 'here'.
2

2
The first five kinds of commands you'll want to know are listed below.  Type 'help <topic-name>' for details on any of them:
2

2
look -- getting a description of the current room or any other object
2
say -- speaking to the other players in the same room as you
2
@who -- showing which players are currently connected to the MOO
2
movement -- how to move around in the MOO, from room to room
2
@quit -- disconnecting from the MOO
35
1
4
4
2
Syntax:  give <object> to <player>
2
         hand <object> to <player>
2

2
Move an object from your contents to that of another player.  This doesn't change the ownership of the object.  Some players may refuse to accept gifts and some objects may refuse to be given.
35
5
4
11
2
Usage:  news [contents] [articles]
2

2
Read the latest edition of the newspaper, which carries articles concerning recent changes to the MOO server or to the main public classes, or whatever the administrators feel like writing about.
2

2
Specifying `contents' means to print only the table of contents for the newspaper.
2

2
The most common uses of this command:
2

2
news          -- display articles in the current edition
2
news new      -- display articles you haven't read yet
2
news contents -- list articles in the current edition
35
5
4
13
2
Occasionally, you may run into a situation in which you'd rather not hear from certain other players.  It might be that they're being annoying, or just that whatever they're doing makes a lot of noise.  Gagging a player will stop you from hearing the results of any task initiated by that player.  You can also gag a specific object, if you want to hear what the owner of that object says, but not the output from their noisy robot.  The commands to use gagging are listed below; detailed help is available on each of them:
2

2
@gag -- add one or more players to your gag list
2
@ungag -- remove a player from your gag list
2
@listgag -- list the players you currently have gagged
2

2
The following commands are identical to the foregoing except that they concern your "noisy gag" list:
2

2
@gag! -- add one or more players to your NOISY gag list
2
@ungag! -- remove a player from your NOISY gag list
2
@listgag! -- list the players you currently have NOISY-gagged
2

2
The noisy gaglist is for those situations where you feel it necessary NOT ONLY to gag a player but to also make it apparent to others in the room that you indeed have this player gagged.  If a player you have noisy-gagged makes an utterance in your presence, those in the room who do not already have this player gagged will see an announcement to the effect that you have him/her gagged.
35
5
4
5
2
Syntax:  @move <thing> to <place>
2

2
Move the specified object to the specified location.  This is not guaranteed to work; in particular, the object must agree to be moved and the destination must agree to allow the object in.  This is usually the case, however.
2

2
If @move doesn't work and you own the room where the object is located, try using @eject instead (see `help @eject').
35
5
4
4
2
Syntax:  inventory
2
         i
2

2
Prints a list showing every object you're carrying.
35
5
4
3
2
Syntax:  @entrances
2

2
Prints a list of all recognized entrances to the current room (but only if you own the room).  A recognized entrance is one whose use is not considered to be teleportation.
35
5
4
3
2
Syntax:  @exits
2

2
Prints a list of all conventional exits from the current room (but only if you own the room).  A conventional exit is one that can be used simply by uttering its name, like 'east'.
35
5
4
6
2
Syntax: @gender <gender>
2
        @gender
2

2
The first form, with an argument, defines your player to have the gender <gender>.  If <gender> is one of the standard genders (e.g., 'male', 'female', 'neuter',...), your various pronouns will also be set appropriately, making exits and certain other objects behave more pleasantly for you.
2

2
The second form tells you the current definition of your player's gender, your current pronouns, and the complete list of standard genders.
35
5
4
3
2
Syntax:  @recycle <object-name-or-number>
2

2
Destroys the indicated object utterly and irretrievably.  Naturally, you may only do this to objects that you own.
35
5
4
25
2
Syntax: @rename <object>        to [name-and-alias],<alias>,...,<alias>
2
        @rename <object>        to [name]:<alias>,...,<alias>
2
        @rename <object>:<verb> to <new-verb-name>
2
        @rename# <object>:<verb-number> to <new-verb-name>
2

2
The first two forms are used to change the name and aliases of an object.  The name is what will be used in most printed descriptions of the object.  The aliases are the names by which players can refer to the object in commands.  Typically you want to include the name in the aliases, as the MOO parser only checks .aliases when matching, so the first syntax is generally preferred.
2

2
If you leave out the "name" part of the list, @rename will leave the object's name as it is, and only change the aliases.
2

2
You can't give an object an empty name or alias.
2

2
Examples:
2
Munchkin names his dog:
2
  @rename #4237 to "Rover the Wonder Dog":Rover,dog
2
Now we'll see 'Rover the Wonder Dog' if we're in the same room as him and we can refer to him as either 'Rover' or just 'dog' in our commands, like 'pet dog'.
2

2
Munchkin changes his dog's aliases:
2
  @rename #4237 to ,Rover,dog,Rover the Wonder Dog
2
The name remains the same--we still see 'Rover the Wonder Dog'--but now any of 'Rover', 'dog', or 'Rover the Wonder Dog' can be used to refer to him.  This can help reduce confusion.
2

2
NOTE that for renaming players, more stringent rules apply.  Your new name must not include spaces and must be distinct from any other player's name or alias; likewise for any of your single-word aliases.  Also, certain names (mainly prepositions) are not allowed, and names of the form "Something_Guest" are reserved for actual guests.  There are no restrictions on giving yourself multi-word aliases, however the commands that expect player names (e.g., @who, whereis) will not recognize multi-word aliases.
2

2
The third and fourth forms of the @rename command are for use by programmers, to change the name of a verb they own.  If the <new-verb-name> contains spaces, the verb will have multiple names, one for each space-separated word.  The fourth form is useful for renaming verbs that are otherwise inaccessible.
2

2
See `help naming' for a discussion of names and articles.
35
5
4
17
2
Containers are objects that allow you to store other objects inside them.  The following help topics cover verbs that can be used with containers:
2

2
put -- putting an object into a container
2
remove -- taking an object out of a container
2

2
You can make a container by creating a child of the standard container, $container (see `help @create').
2

2
Openable containers may be open or closed, using the verbs `open container' and `close container'.  Openable containers have a separate lock to determine if a player may open them.  See the following help topics:
2

2
@lock_for_open -- setting the lock for opening a container
2
@unlock_for_open -- clearing the lock
2

2
You can make an openable container by creating a child of $openable_container.
2

2
Containers have a large number of messages which get printed when players act upon them.  See 'help container-messages' for more information.
2

2
Openable containers have opacity.  See 'help @opacity' for more information.
35
1
4
14
2
Notes are objects that can have text written on them to be read later.  They are useful for leaving messages to people, or for documenting your creations.
2

2
The following help topics cover verbs that can be used with notes:
2

2
read -- reading the text on the note
2
write -- adding text to a note
2
erase -- removing all the text from a note
2
delete -- deleting one line of text from a note
2
@edit -- general editing on the text of a note
2

2
encrypt -- restricting who can read a note
2
decrypt -- undoing a previous encryption
2

2
You can make a note by creating a child of the standard note, $note (see 'help @create').  Note that, like most objects, only the owner of a note can recycle it.  
35
5
4
11
2
Syntax: look
2
        look <object>
2
        look <object> in <container>
2

2
Show a description of something.
2

2
The first form, with no arguments, shows you the name and description of the room you're in, along with a list of the other objects that are there.
2

2
The second form lets you look at a specific object.  Most objects have descriptions that may be read this way.  You can look at your own description using 'look me'.  You can set the description for an object or room, including yourself, with the 'describe' command (see 'help describe').
2

2
The third form shows you the description of an object that is inside some other object, including objects being carried by another player.
35
5
4
4
2
Syntax:  drop <object>
2
         throw <object>
2

2
Remove an object you are carrying from your inventory and put it in your current room.  Occasionally you may find that the owner of the room won't allow you to do this.
35
5
4
2
2
*forward*
2
take
35
5
4
17
2
Objects usually have verbs defined on them that allow players to manipulate and use them in various ways. Standard ones are:
2

2
get -- pick an object up and place it in your inventory
2
drop -- remove an object from your inventory and place it in the room
2
put -- take an object from your inventory and place it in a container
2
give -- hand an object to some other player
2
look -- see what an object looks like
2

2
You can see what objects you're carrying with the 'inventory' command; see 'help inventory' for details.
2

2
Some specialized objects will have other commands. The programmer of the object will usually provide some way for you to find out what the commands are.  One way that works for most objects is the 'examine' command; see 'help examine' for details.
2

2
The following specialized objects have help entries you should consult:
2

2
notes -- objects that allow text to be written on them and read later
2
letters -- notes that a recipient can burn after reading
2
containers -- objects that may contain other objects
35
5
4
19
2
Syntax:  help
2
         help <topic>
2
         help index
2

2
Print out entries from the online documentation system.  The commands `?' and `information' (usually abbreviated `info') are synonyms for `help'.
2

2
The first form prints out a summary table of contents for the entire help system.  
2

2
The second form prints out the documentation available on the given topic.  Many help system entries contain references to other entries accessible in this way.  The topic name may be abbreviated; if there is no topic exactly matching the name you give, the help system checks for topics for which the name is a prefix, perhaps with the addition or omission of an initial `@', or perhaps with some confusion beween dashes (-) and underscores (_), e.g., 
2

2
      `bui' instead of `building', 
2
      `who' instead of `@who', 
2
     `@wri' instead of `write',
2
  `add_ent' instead of `@add-entrance',
2
 `unlock-'  instead of `@unlock_for_open'
2

2
If the abbreviation you give is ambiguous, you will be presented with a list of the matching complete topic names.
2

2
The `help index' commands prints out a list of indices for the various help databases.  Each index gives a list of topics available on that database.  It is sometimes easier to find the topics you're interested in this way, rather than tracing through the chain of cross references.
35
1
4
1
2
Rooms may be made by builders; see `help @dig'. By default, all rooms are instances of the generic room, $room.  `help room-messages' and `help locking' discuss how to customize rooms.
35
1
4
9
2
The descriptions of most rooms outline the directions in which exits exist.  Typical directions include the eight compass points ('north', 'south', 'east', 'west', 'northeast', 'southeast', 'northwest', and 'southwest'), 'up', 'down', and 'out'.
2

2
To go in a particular direction, simply type the name of that direction (e.g, 'north', 'up').  The name of the direction can usually be abbreviated to one or two characters (e.g., 'n', 'sw').  You can also type 'go <direction>' or 'go to <location>' to move; this is particularly useful if you know you're going to type several movement commands in a row (see `help go').  
2

2
Other commands for moving about include:
2

2
walk -- going to a particular place
2
join -- joining a particular person
2
home -- returning to the place you live
35
1
4
6
2
*subst*
2
Syntax: home
2

2
Instantly teleports you to your designated home room.
2

2
Initially, this is %[$player_start:dname()], but you can change that; see 'help @sethome' for details.
35
1
4
15
2
There are several commands available to allow you to communicate with your fellow MOOers.  Help is available on the following communication-related topics:
2

2
say -- talking to the other connected players in the room
2
whisper -- talking privately to someone in the same room
2
page -- yelling to someone anywhere in the MOO
2
emote -- non-verbal communication with others in the same room
2
gagging -- screening out noise generated by certain other players
2
news -- reading the most recent set of general announcements
2
@gripe -- sending complaints to the admins
2
@typo/@bug/@idea/@suggest -- sending complaints/ideas to the owner of the current room
2
whereis -- locating other players
2
@who -- finding out who is currently logged in
2
mail -- the MOO email system
2
security -- the facilities for detecting forged messages and eavesdropping.
2

35
1
4
13
2
Syntax:  say <anything> ...
2
         "<anything> ...
2

2
Says <anything> out loud, so that everyone in the same room hears it.  This is so commonly used that there's a special abbreviation for it: any command-line beginning with a double-quote ('"') is treated as a 'say' command.
2

2
Example:
2

2
Munchkin types this:
2
  "This is a great MOO!
2
Munchkin sees this:
2
  You say, "This is a great MOO!"
2
Others in the same room see this:
2
  Munchkin says, "This is a great MOO!"
35
1
4
3
2
Usage:  whisper "<text>" to <player>
2

2
Whisper a message to another person in the same room.  Only you and the recipient of the whisper will know about it.  Good for keeping secrets.
35
1
4
33
2
*subst*
2
Syntax:  page <someone> [[with] <text>]
2
         '<someone> [text]
2
         ' text
2

2
Sends a message to a connected user, telling them your location and, optionally, <text>.
2

2
Example:
2

2
Munchkin types:
2
        page Frebble with "Where are you?"
2
Frebble sees:
2
        Munchkin pages, "Where are you?"
2
Munchkin sees:
2
        Your message has been sent to Frebble.
2

2
The "'" command is just an abbreviation for "page"; it also aids repeated paging.  If you use "'" without specifying who you want to send the message to, it will be sent to the last person you paged with "'".
2

2
Advanced Features
2
=================
2

2
Page refers to the following messages on the players involved (see 'help messages'):
2

2
@page_origin [%[player:unparse_message($player.page_origin_msg)]]
2
  Determines how the recipient is told of your location.
2

2
@page_echo   [%[player:unparse_message($player.page_echo_msg)]]
2
  Determines the response received by anyone who pages you.
2

2
@page_absent [%[player:unparse_message($player.page_absent_msg)]]
2
  Determines the response received by anyone who tries to page you when you aren't connected.
2

2
All of these undergo the usual pronoun substitutions (see `help pronouns') except that in both cases the direct object (%[player:unparse_message({#98, "do", {"dname", "dobj", 0}})]) refers to the recipent of the page and the indirect object (%[player:unparse_message({#98, "do", {"dname", "iobj", 0}})]) refers to the sender.
35
1
4
19
2
Syntax:  emote <anything> ...
2
         :<anything> ...
2
         ::<anything> ...
2

2
Announces <anything> to everyone in the same room, prepending your name.  This is commonly used to express various non-verbal forms of communication.  In fact, it is so commonly used that there's a special abbreviation for it: any command-line beginning with ':' is treated as an 'emote' command.
2

2
The alternate form, '::' (less commonly 'emote :'), does not insert the space between the player name and the text.
2

2
Examples:
2

2
Munchkin types this:
2
  :wishes he were much taller...
2
Everyone in the same room sees this:
2
  Munchkin wishes he were much taller...
2

2
Munchkin types this:
2
  ::'s eyes are green.
2
Everyone in the same room sees this:
2
  Munchkin's eyes are green.
35
1
4
6
2
There are a number of commands available to players for building new parts of the MOO.  Help on them is available under the following topics:
2

2
creation -- making, unmaking, and listing your rooms, exits, and other objects
2
topology -- making and listing the connections between rooms and exits
2
descriptions -- setting the names and descriptive texts for new objects
2
locking -- controlling use of and access to your objects
35
1
4
18
2
Help is available on the following general topics:
2

2
introduction -- what's going on here and some basic commands
2
index -- index into the help system
2

2
characters -- setting characteristics of yourself
2
movement -- moving yourself between rooms
2
communication -- communicating with other players
2
manipulation -- moving or using other objects
2
miscellaneous -- commands that don't fit anywhere else
2

2
building -- extending the MOO
2
programming -- writing code in the MOO programming language
2
editors -- editing text and code in the MOO
2

2
@options -- customizing your MOO environment
2
@pagelength -- what to do if lines scroll off your screen too fast
2
@linelength -- what to do if lines are truncated
35
5
4
21
2
Syntax:  @edit-option
2
         @edit-option <option>
2

2
Synonym:  @editoption
2

2
The edit options customize the behavior of the various editors (mail editor, verb editor, etc...) to your particular taste.  The first form of this command displays all of your edit options.  The second form displays just that one option, one of the flags listed below.
2

2
The remaining forms of this command are for setting your edit options:
2

2
         @edit-option +<flag>
2
         @edit-option -<flag>
2
         @edit-option !<flag>           (equivalent to -<flag>)
2

2
These respectively set and reset the specified flag
2

2
-quiet_insert    insert (") and append (:) echo back the line numbers
2
+quiet_insert    insert (") and append (:) produce no output
2
-eval_subs       (VERB EDITOR) ignore .eval_subs when compiling verbs
2
+eval_subs       (VERB EDITOR) apply .eval_subs to verbs being compiled
2
-local		 use in-MOO text editors.
2
+local		 ship text to client for local editing.
35
5
4
2
2
*forward*
2
@edit-options
35
5
4
6
2
Usage:  @add-feature  <object>
2
        @remove-feature <object>
2

2
Add or remove a feature from your list.  A feature is an object which provides additional commands you can use.  For more information, see `help features'.
2

2
@add-feature without an object specification will list all the available features you do not already have.
35
5
4
2
2
*forward*
2
@add-feature
35
5
4
3
2
Usage:  @features [<name>] [for <player>]
2

2
List all of <player>'s features matching <name>, or all of <player>'s features if <name> is not supplied.  <player> defaults to you.  See `help features' for more information.
35
5
4
3
2
Features are objects that provide you with commands not covered by the ordinary player objects.  The advantage of using features is that you can mix and match the things you like; whereas if you like a command that's defined on a player class, you have to also get all the commands it defines, and all the commands its ancestors define.
2

2
You can list your features (see `help @features'), and add or remove features from your list (`help @add-feature' and `help @remove-feature').
35
5
4
8
2
Syntax:  @dump <object> [with [id=#<id>] [noprops] [noverbs] [create]]
2

2
This spills out all the properties and verbs on an object, calling suspend at appropriate intervals.
2

2
  * id=#<id> -- specifies an idnumber to use in place of the object's actual id (for porting to another MOO)
2
  * noprops  -- don't show properties.
2
  * noverbs  -- don't show verbs.
2
  * create   -- indicates that a @create command should be generated and all of the verbs be introduced with @verb rather than @args; the default assumption is that the object already exists and you're just doing this to have a look at it.
199
1
4
1
2
The MOO helps those who help themselves.  Try starting with `help summary'.  (If you really need immediate help, try typing `@helpme'.)
35
5
4
7
2
Syntax: @rmalias  <alias>[,...,<alias>] from <object>
2
        @rmalias  <alias>[,...,<alias>] from <object>:<verb>
2
        @rmalias# <alias>[,...,<alias>] from <object>:<verb-number>
2

2
The first form is used to remove aliases from an object.  If the object is a valid player, space and commas will be assumed to be separations between unwanted aliases.  Otherwise, only commas will be assumed to be separations.  Note that @rmalias will not affect the object's name, only its aliases.
2

2
The other two forms are for use by programmers, to remove aliases from a verb they own.  All spaces and commas are assumed to be separations between unwanted aliases.  For `@rmalias#', verbs are specified by number, where 1 refers to the first verb on <object>.
35
5
4
25
2
Syntax: @addalias <alias>[,...,<alias>] to <object>
2
        @addalias <alias>[,...,<alias>] to <object>:<verb>
2
        @addalias# <alias>[,...,<alias>] to <object>:<verb-number>
2

2
The first form is used to add aliases to an object's list of aliases.  You can separate multiple aliases with commas.  The aliases will be checked against the object's current aliases and all aliases not already in the object's list of aliases will be added.
2

2
The second and third forms of the @addalias command are for use by programmers, to add aliases to a verb they own.  All commas and spaces are assumed to be separations between aliases.  In the third form, the verb is specified by number, where 1 refers to the first verb on the object.
2

2
Example:
2

2
Muchkin wants to add new aliases to Rover the Wonder Dog:
2
  @addalias Dog,Wonder Dog to Rover
2
Since Rover the Wonder Dog already has the alias "Dog" but does not have the alias "Wonder Dog", Munchkin sees:
2
  Rover the Wonder Dog(#4237) already has the alias Dog.
2
  Alias Wonder Dog added to Rover the Wonder Dog(#4237).
2

2
If the object is a player, each alias will be checked against the Player Name Database to make sure no one else is using it. Any already used aliases will be identified.
2

2
Example:
2

2
Munchkin wants to add his nicknames to his own list of aliases:
2
  @addalias Foobar Davey to me
2
@Addalias recognizes that Munchkin is trying to add an alias to a valid player and checks the aliases against the Player Name Database.  Unfortunately, DaveTheMan is already using the alias "Davey" so Munchkin sees:
2
  DaveTheMan(#5432) is already using the alias Davey
2
  Alias Foobar added to Munchkin(#1523).
35
5
4
5
2
*forward*
2
summary
2

2
Type 'help <topic>' for information on a particular topic.
2

35
5
4
8
2
Every object (including players, rooms, exits) has a name and a set of aliases. The object name is commonly used to display an object in various contexts. The object aliases are used to refer to an object when players type commands.
2

2
Help is available on the following commands:
2

2
    @rename -- change the names or aliases of an object or yourself.
2
    @addalias, @rmalias -- add and remove aliases.
2

2
See also `help naming' for a discussion of base name vs. modified name.
35
5
4
2
2
*forward*
2
 name
35
5
4
2
2
*forward*
2
wizard-list
35
5
4
12
2
Usage:  @register [<someone>]
2
        @register <someone> as <email-address>[ <commentary>]
2

2
Check or update an email address.
2

2
The first form just tells you the person's email address.  It works only if you are that person or are on the community group (see `help cg'), or if the person has made eir address public (with `@miscoption +public_email').  It may also tell you about others registered to the same address.
2

2
The second form will change the database entry, assign a new password, and mail the new password to the given email address.
2

2
Most people can only register themselves, and can't provide commentary (additional text that's stored with the email address).  Community group members can register others and add comments.
2

2
This is different from the @request/@register command for guests.
35
5
4
5
2
Usage:  @edit <object>:<verb-name> [<args>]
2
        @edit <object>.<prop-name>
2
        @edit <object>
2

2
Enter the appropriate editor for the specified target.  The first form is handled by the verb editor (and is available only to programmers); the second is handled by either the note editor or the list editor; and the third is handled in an object-dependent way.  (For notes, this translates to editing <object>.text; for most other objects, it translates to editing <object>.description.)
35
5
4
3
2
*verbdoc*
2
$player
2
@quicksend
35
5
4
2
2
*forward*
2
@read
35
5
4
3
2
Syntax:  @forward-me <msg> [on *<collection>]
2

2
Takes the indicated message in your (or some other) message collection, creates a new message whose body is the original message (both headers and body) and sends it to your registered email address.
35
5
4
19
2
This is the JHCore sample help manners text.  You should probably modify it for your MOO.
2

2
Behavior that would be considered rude in `face-to-face' interaction is rude here, too.
2

2
Some ways to interpret that:
2

2
Be nice.  Don't be rude or annoy other people.  If someone requests that you stop a particular behavior or action, please respect eir wishes.
2

2
Avoid interrupting other people who are working or conversing with their friends.  You're welcome to explore the public places and ask for advice if you're stuck, but please try to read the online documentation and help yourself, first.
2

2
In addition, please realize that many players who log in here are busy with other things, both virtually and in real life, and therefore may not always be able respond to you immediately.
2

2
Harrassment of other players will not be tolerated.  Emoted violence and obscenities are considered inappropriate.
2

2
It isn't reasonable to `:kiss' or `:hug' folks you don't know.  Also, speak out loud to people you don't know, rather than paging or whispering.
2

2
Respect other player's sensibilities.  MOO inhabitants and visitors come from a diverse range of cultural backgrounds, both in the U.S. and abroad, and have varying ideas of what constitutes offensive expression.  Please keep text as free of potentially-offensive language and material as you can.
2

2
Try to avoid arguing or debating with another player in a crowded public room; such interactions interrupt the other players in the room.  Instead, consider paging or moving your conversation to one of the many empty public rooms.
35
5
4
16
2
Usage:  @options
2
        @options <category>
2
        @options all
2
        @options <category> <option>
2

2
There are a number of variations you can request in the way the MOO interacts with you.  For example, you can specify that you want your email address to be generally available to other people's MOO programs.  The options system allows you to view and set these preferences.
2

2
The @options command with no arguments will give you a list of different option categories available.  Each shows a category name (which you can use as a <category> argument to @options), a command (which you can use instead of `@options <category>'), and a very brief explanation of what the category is about.
2

2
`@options <category>' displays the options for the given category.  Each option is shown with its current setting, and a brief explanation of what the current setting means.  `@options all' shows all the options for all the categories.
2

2
`@options <category> <option>' either shows or sets the given option in the given category.  Each option has a name; the ways you can use this name in an @options command are as follows:
2

2
  -  <name> alone shows you the current setting, with a brief explanation.
2
  -  +<name> and -<name> turn most options on or off.  Obviously, this only works for simple on/off options.
2
  -  <name>=<value> sets a more complicated option.  For an example, see the manymsgs option in `help mail-options'.
35
5
4
95
2
STANDARD HELP TEXT FORMAT
2
=========================
2

2
The JaysHouse help system attempts automatic conversion of help texts written in a certain format into jtext, the rich/hypertext format under development here.  The format is heavily based on certain conventions found in LambdaCore help documents, so most of them could be automatically converted.  This document details the expected format.
2

2
Help Text Elements
2
2-----------------
2

2
The JaysHouse help format is designed to be highly readable in raw format--it is derived from the texts that are presented verbatim to users on other MOOs.  However, it also includes (or more rigorously defines) constructs that a machine can translate into paragraphs, headers, itemized lists, definition lists, hypertext links, and plaintext.
2

2
Blank Lines
2
===========
2

2
Blank lines separate paragraphs from headers from definition lists from itemized lists from plaintext.  The term "blank line" means a completely empty line; a line composed entirely of whitespace doesn't count.  This is mostly for efficiency.
2

2
At some point there may be a "help text compiler", which will attempt to parse a text written in this format, show you the result, and point out common problems.  A good use of this would be checking for whitespace lines.  Meanwhile, we're just converting help texts on the fly, and don't want to be printing error messages and such.
2

2
Paragraphs
2
==========
2

2
The most common help text element is the paragraph.  A paragraph (like this one) is a single line of text, preceded and followed by blank lines.  Paragraphs should _not_ be linewrapped text.  The help system makes no assumptions about screen width, and you shouldn't either; if the help text converter finds more than one line of text with no blank lines in between, it assumes that there's some special formatting going on.  (It will try to recognize headers, definition lists, and itemized links; if this fails, it formats as plaintext and hopes for the best.)
2

2
Note that a lot of MOO text manipulation systems--for example, the linewrapping (see `help @wrap') done for people without special clients, and the algorithm used to include text in mail replies--make the same assumption that a line is equivalent to a paragraph, so this is a good skill to develop even if you're not writing help texts regularly.  And including blank lines between paragraphs in some of these contexts improves readability.
2

2
Hypertext Links
2
===============
2

2
It is possible to embed hypertext links to other help topics within help text (paragraphs, itemized lists, or definition list descriptions).  A link is marked with a single quote (either direction), the word "help", a single space, a word containing no spaces, and a single closing quote.  For an example, see this sentence in the middle of `help help-format'.  (If you saw a boxed number after that link, please note that it was an artifact of jtext output, rather than a part of the hypertext link format.)
2

2
The word after "help" is the name of the help topic to link to.  If the word is enclosed in angle brackets (actually, if it begins with the "<" character), this piece of text will not be considered a hypertext link.  This is so we can include instructions to type `help <topic-name>' for more information.
2

2
You can also include a similar reference to a project--for example, `@proj info jtext' or `@proj usa doc' are both relevant to this document.
2

2
Headers
2
=======
2

2
Just above this sentence is a header.  A header consists of two lines, preceded and followed by blank lines.  The first line is the text of the header.  The second line should be a string of equals ("=") or hyphen ("-") characters, of the same length as the header text.
2

2
There are four levels of headers available through the help format:
2

2
  - Level 1: All caps, equals signs (see "standard help text format").
2
  - Level 2: All caps, hyphens (see "help text elements").
2
  - Level 3: Normal text, equals signs (see "headers").
2
  - Level 4: Normal text, hyphens (no example here).
2

2
They may, of course, be formatted differently by different clients that understand jtext.
2

2
Sometimes you don't want your first- or second-level header to be in all caps.  You can also indicate header level by replacing the first "=" or "-" with a numeral between 1 and 6.  For example, one of the headers in this document is represented as:
2

2
  Help Text Elements
2
  2-----------------
2

2
Warning:  Many output formats (for example, the in-db line-mode client and most ASCII HTML displayers) will turn headers into all caps anyway.
2

2
If there is not a header of level 1 at the beginning of a help text, it will be given a top-level header based on the name of the help topic.
2

2
Itemized Lists
2
==============
2

2
An itemized list is a series of bulleted lines preceded and followed by blank lines.  A "bullet" is recognized by the presence of several elements at the beginning of a line:  more than one space, followed by one of several bullet characters (currently "-", "o", and "*"), followed by one or more spaces.
2

2
Each line of an itemized list must use the same bullet character and the same number of spaces before and after.
2

2
No nesting of lists is allowed; an item contains only a line of simple text.  It should be possible to include hypertext links (see above) soon.
2

2
Numbered lists are planned for the future.
2

2
Definition Lists
2
================
2

2
The definition list gets its name from its use in glossaries.  Each element of a definition list consists of a term (something to be defined) and a definition (what it means).  In practice, it's used for much more than that, but the help format currently provides only one use of it:  lists of related help texts.
2

2
The help format allows an author to provide a list of links to other help texts and a brief description of each one.  For an example, see `help summary', which contains four separate definition lists.
2

2
The format for one of these definition lists places each element on a line, with blank lines preceding and following the whole list.  There must be at least two elements for a definition list to be recognized.  Each line contains the name of a help topic; the string " -- " (a dash); and some text, usually describing the help topic or its relation to the current one.
2

2
example -- This is an example of the format for a definition list entry.
2

2
It wasn't converted into definition list form because there's only one line.
2

2
Horizontal Rule
2
===============
2

2
A line consisting only of twenty or more hyphens in a row, with blank lines immediately before and after, will be represented as a horizontal line across the page.  Here's an example:
2

2
------------------------------
2

2
Plaintext
2
=========
2

2
The help converter breaks text into chunks separated by blank lines.  If a chunk is only one line long, it's considered a paragraph.  If not, an attempt is made to recognize it as header, itemized list, or definition list.  If none of these match, the converter assumes that the author has done some kind of formatting, and it shouldn't mess with it.
2

2
In this situation, the chunk of text is considered preformatted "plaintext", and will generally be output in a fixed-width font with the formatting provided.
2

2
In most cases, plaintext is the result of either a help text inherited from LambdaCore which didn't conform to the format defined here, or an author needing some kind of formatting not currently provided by the help format (e.g., nested lists).  It usually represents a failure of some sort on the part of the help system; for this reason, the system maintains a list of help texts that required plaintext formatting.  This list should be (but isn't) checked periodically, and help texts should be fixed or new capacities added to the converter.
35
5
4
7
2
There are three separate (but not necessarily disjoint) groups of administrators, with different roles and responsibilities.  You can get help about each of the following groups, listed here with a brief explanation of what it is they're responsible for.
2

2
steering-committee -- defining and maintaining the MOO's goals and objectives
2
tech-group -- technical development and maintenance of the MOO
2
community-group -- handling social problems and promoting a sense of community
2

2
By defining the responsibilities of the various types of administrators, we hope to clear up any confusion about what a person `in charge' is supposed to be doing, and what authority that person has.
35
5
4
2
2
*forward*
2
administrator-policy
35
5
4
2
2
*forward*
2
purpose
35
5
4
2
2
*forward*
2
purpose
35
5
4
1
2
This MOO apparently has no quota policy.   You should talk to an admin to get someone to write a quota policy.
35
5
4
2
2
*forward*
2
quota-policy
35
5
4
2
2
*forward*
2
administrator-policy
35
5
4
2
2
*objectdoc*
2
$community_group
35
5
4
2
2
*objectdoc*
2
$db_group
35
5
4
2
2
*objectdoc*
2
$unix_group
35
5
4
2
2
*forward*
2
administrator-policy
35
5
4
2
2
*objectdoc*
2
$tech_group
35
5
4
3
2
*verbdoc*
2
$player
2
@helpme
35
5
4
3
2
*verbdoc*
2
$player
2
@911
35
5
4
2
2
*forward*
2
community-group
35
5
4
3
2
*verbdoc*
2
$player
2
@administrators
35
5
4
3
2
*verbdoc*
2
$player
2
@admins
35
5
4
3
2
*verbdoc*
2
$player
2
following
35
5
4
19
2
Usage:  @current-object
2
        @current-object <ref>[..]
2
        @current-object <base>[<ref>[..]]
2

2
The @current-object (@co) command is used to set your current object--an object referred to as _ in commands.  For more information on how to use the current object, see `help current-object'.
2

2
With no arguments, the command just tells you what your current object is.
2

2
If an argument is given, it indicates the object to go to, beginning with an optional `base object' and continuing with a series of references.  (If a base is given, no references need follow.)  The base object, if present, is matched as an ordinary object.  For example, "@co $jtext" would set your current object to the Jtext Dispatcher.
2

2
The <ref> segments are mostly properties.  For example, "@co $jtext.link" would set your object to the Link Dispatcher Tag.
2

2
If no base is identified, the current object you're already using is used as the base.  For example, after typing the previous command, you could type "@co .preceding" to change your current object to $jtext.link.root, the generic preceding link tag.
2

2
In addition to property references, there are two special kinds of <ref> segments for navigation through the object hierarchy.  One of them is ".<", and it works by retracing the steps by which your current object was determined.  For example, again following the preceding commands, "@co .<" would take you back to the Link Dispatcher Tag.
2

2
Finally, the ".^" <ref> segment gets you the parent of the current object.  Continuing the example, "@co .^" would bring you to the root class, the parent of the link dispatcher tag.
2

2
As indicated, any number of references can be combined; for example, "@co $jtext.link.preceding.<.<.<.string_utils.^" would be an exceedingly complicated way to get your current object to be the generic utilities.
35
5
4
22
2
Your `current object' is an object that you can refer to in most commands without using its name, just by using the symbol "_".  It's intended primarily for programming.
2

2
For example, you can type
2

2
   @co $jtext.link
2
   @d _:
2

2
and get a list of verbs defined on the link dispatcher tag.
2

2
In addition, if your .eval_env property (see `help eval') sets _ to player.current_object, you can use it in evaluations.  The default .eval_env includes this binding.  So, to continue the example:
2

2
   >;_:dname()
2
   => "the link dispatcher tag"
2

2
A stack is kept of the current objects you've been using, so you can change your current object for a while and then switch back to the previous one without needing to identify it again.
2

2
To find out how to set or view your current object, read the following help topics:
2

2
@currentobject -- Set current object, or find out what it is.
2
   @pushobject -- Push a current object onto the stack.
2
    @popobject -- Return to the previous current object.
2
   @swapobject -- Switch between current and previous object.
35
5
4
2
2
*forward*
2
@current-object
35
5
4
2
2
*forward*
2
@current-object
35
5
4
7
2
Usage:  @pushobject [<object>]
2

2
The @pushobject (@pusho) command changes your current object (see `help current-object'), but remembers where you were by pushing it onto a stack.  The object is specified as in the @currentobject command (see `help @currentobject').
2

2
If used with no arguments, the @pushobject command displays the current object stack.
2

2
See also `help @popobject' and `help @swapobject' for information on manipulating the current object stack.
35
5
4
2
2
*forward*
2
@pushobject
35
5
4
5
2
Usage:  @popobject
2

2
The @popobject (@popo) command returns you to a previous current object (see `help current-object') by popping the present one off the stack.
2

2
See also `help @pushobject' and `help @swapobject' for information on manipulating the current object stack.
35
5
4
2
2
*forward*
2
@popobject
35
5
4
2
2
*forward*
2
@swapobject
35
5
4
7
2
Usage:  @swapobject [<object>]
2

2
The @swapobject (@swapo) command returns to the previous object on the current object stack (see `help current-object'), but it places the _present_ object _on_ the stack.  Repeated use of this command essentially alternatives between two current objects.
2

2
The new current object is specified as in the @currentobject command (see `help @currentobject').  If used with no arguments, the @pushobject command displays the current object stack.
2

2
See also `help @pushobject' and `help @popobject' for information on manipulating the current object stack.
35
5
4
5
2
Usage:  walk to <location>
2
        go to <location>
2
        join <person>
2

2
These commands attempt to find a walkable path from where you are to where you want to be.  The first two require you to name your destination; the last requires the name of someone you want to join.
35
5
4
2
2
*forward*
2
walk
35
5
4
2
2
*forward*
2
@rn
35
5
4
2
2
*forward*
2
@rn
35
5
4
14
2
Syntax:  @gag! <player> [<player>...]
2

2
This is just like @gag, except that the players in question are added to your `NOISY gag list'.  You will no longer see any messages that result from actions initiated by these players and, moreover, announcements will appear to other players in the room indicating that you have these players gagged.
2

2
Example:
2

2
Munchkin types:
2
  @gag Grover
2
Grover types:
2
  "Munchkin is a <expletive deleted>
2
Other players see:
2
  Grover says, "Munchkin is a <expletive deleted>"
2
  **GAG:  Grover is gagged by Munchkin.
2

35
5
4
4
2
Syntax:  @ungag! <player>
2
         @ungag! everyone
2

2
Remove the given player or object (or, in the second form, everyone) from your `noisy gag list'.  You will once again see any messages that result from actions initiated by the ungagged player(s) or objects.  In particular, you will once again be able to hear them if they speak, emote, or whisper to you.  See `help gagging' for an explanation of gagging in general.
35
5
4
3
2
Syntax:  @listgag!
2

2
Shows you a list of the players and objects currently on your `noisy gag list'.  See 'help gagging' for an explanation of gagging in general.
35
5
4
20
2
Syntax:  @gag-announce [default] [disable|
2
                                  enable|
2
                                  minwait is <number>|
2
                                  suspend is <number>]
2
         @gag-announce use default
2

2
This command displays/sets the parameters for announcements of `noisy gags' for this room.  See `help gagging' for a description of noisy gags.
2

2
Without arguments this simply gives the current parameters for gag announcements for this room.  
2

2
Specifying `disable' entirely disables gag announcements.  
2
Specifying `enable' enables gag announcements.
2
`minwait' is the minimum interval (seconds) before a 
2
          particular announcement can be repreated.  
2
`suspend' is how long the gag announcer suspends 
2
          between announcements.
2

2
Specifying `default' displays/changes the parameters for the generic room instead.
2

2
Specifying `use default' indicates that this room should use the parameters for the generic room.
35
5
4
3
2
*verbdoc*
2
$player
2
!
35
5
4
3
2
*verbdoc*
2
$player
2
@aliases
35
5
4
2
2
*forward*
2
communication
35
5
4
2
2
*forward*
2
@quicksend
35
5
4
2
2
*unpass*
2
characters
35
5
4
15
2
CHARACTERS
2
==========
2

2
Your `character' is you, for most intents and purposes; it represents your presence here.  Use it wisely; see `help user-policy' for some information on what's expected of you.
2

2
CUSTOMIZING CHARACTERS
2
----------------------
2

2
There are a number of commands for modifying various characteristics of the object representing you in the MOO.  Help on them is available in the following topics:
2

2
@describe -- setting what others see when they look at you
2
@gender -- changing your character's gender
2
@password -- changing your password
2
@rename -- changing your name and/or aliases
2
@linelength -- adding word-wrap to the lines you see
35
5
4
2
2
*forward*
2
programmer-policy
35
5
4
2
2
*forward*
2
@register
35
5
4
2
2
*forward*
2
mail
35
5
4
2
2
*unpass*
2
movement
35
5
4
2
2
*forward*
2
movement
35
5
4
2
2
*forward*
2
movement
35
5
4
2
2
*forward*
2
page
35
5
4
1
2
*index_list*
35
5
4
10
2
Usage:  @boot[!] <someone> for <reason>
2
        @boot[!] <someone>
2

2
If a guest is really bothering you, you can use this command (e.g., `@boot Maverick_guest for harassing me in the living room') to disconnect the guest.  If you're in a great hurry, you can omit the reason, and you'll be prompted for it after the guest is booted.
2

2
If you use `@boot!' instead, the guest will be disconnected and no guest connections from that site will be allowed for one hour.
2

2
You should be aware that guest bootings are logged and monitored; DON'T use this frivolously.
2

2
Members of the community group (see `help cg') are able to boot non-guest users who are overly disruptive.  The same rules apply.
35
5
4
2
2
*forward*
2
@boot
35
5
4
2
2
*forward*
2
@editoptions
35
5
4
17
2
Every object on the MOO has a "gender" that determines certain grammatical properties of the object, such as what pronouns to use when referring to the object.  "Gender" on this MOO is slightly different than the normal definition of gender; it encompasses gender (male, female, neuter, etc), number (singular or plural), and person (first, second, or third).  Here are the genders available to users (to examine or change your gender, see `help @gender'):
2

2
    Spivak        -- e, em, eir, eirs, emself.
2
    male          -- he, him, his, himself.
2
    female        -- she, her, hers, herself.
2
    either        -- s/he, him/her, his/her, his/hers, (him/her)self.
2
    splat         -- *e, h*, h*s, h*self.
2
    none          -- No pronouns; your name is always used.
2

2
The above are all singular, third person genders.  Here are the other genders:
2

2
    egotistical   -- First person singular: I, me, my, mine, myself.
2
    royal         -- First person plural: we, us, our, ours, ourself.
2
    second        -- Second person singular: you, your, yours, yourself.
2
    second_plural -- Second person plural: you, your, yours, yourself.
2
    neuter        -- Third person singular: it, its, itself.
2
    plural        -- Third person, plural: they, them, their, theirs, themselves.
35
5
4
29
2
Syntax: idle
2
        idle message
2
        reidle
2
        reidle message
2

2
The idle command lets you notify people that you are idling for some kind of activity.  You set an idle message by doing something like:
2

2
  idle to go the bathroom
2
 
2

2
Then, when someone looks at you, they'll see, appended to your description, the fact that you have idled to go to the bathroom.
2

2
In addition, people may see your idle message in @who listings or in other as yet undetermined locations.
2

2
As soon as you enter another command on the moo, you are no longer idle, so your idle message won't show up.
2

2
You can combine both methods at the same time:
2

2
  idle (eating ice cream) going to the mall for a snack
2
 
2

2
If you don't set either message, the default will be printed.  The default idle message is "real life intrusion", while the default short description method is (idling).
2

2
You may set your default idle message to something else:
2

2
  @idle me is <new default message>
2
  
2

2
Normally, when you idle, the fact that you're idling is announced to the room, along with the reason that you're idling.  If you would like to idle quietly, you can use the 'reidle' command instead.  This is often used when you've idled, then said something anyway, and want to reidle but not bother the other members of the room.
35
5
4
2
2
*forward*
2
idle
35
5
4
16
2
DOING
2
=====
2

2
The doing message is used to tell others what you are doing (logical enough). 
2

2
You have a default doing message and a temporary doing message.  They are set in different ways.   Your doing message shows up in @who under the `Doing/Idling' column, except when you've idled.
2

2
TEMPORARY DOING MESSAGE
2
-----------------------
2

2
Your temporary doing message is set using the `doing' verb.  For example:
2

2
   >doing hacking
2
   [doing: hacking]
2

2
In this example, your doing message is set to "hacking."  To clear the message, just use `undoing'.  Your doing message is superceded by your idle message (see `help idle') when you are idle.
35
5
4
2
2
*forward*
2
idle
35
5
4
18
2
A room is a place where talking can take place.  Sometimes, this talking is focused on one topic or purpose.  That is what room topic is for.  Currently, room topics show up in the "parties" list (see `help parties').
2

2
SETTING TOPICS
2
--------------
2

2
There are two types of topics.  One is the default topic, settable by the owner of the room, and the other is a part of the topic stack.
2

2
Think of it like this:  The default topic is carved into the wood of the sign while the other stack topics are slips of paper covering each other and the wood.  
2

2
To create a topic sign and set the first stack topic, type: topic <topic>
2
Note that this command doesn't work once a sign has been created.  Use the below commands then:
2

2
To set the default topic, type: carve <topic> on sign
2
To write over current stack topic, type: write <topic> on sign
2
To add a slip to the stack, type: cover sign with <topic>
2
To remove a slip from the stack, type: uncover sign
2
To remove the sign, type: remove sign
2
To clear the stack, type: shake sign
35
5
4
14
2
*subst*
2
@WHO-OPTIONS
2
============
2

2
@who-options is an options package (see `help options') that allows you to change the way @who (see `help @who') and commands that use it, such as @admins (see `help @admins'), look.
2

2
The @who-options are as follows:
2

2
   o order: One of %[$string_utils:english_list($who_utils.valid_sorts)].  These modify the which field @who is sorted by.
2
   o ascending: This setting also affects sorting order.  Toggling it will always reverse the current sort order.  
2
   o columns: One of %[$string_utils:english_list($who_utils.valid_columns)].  This setting is set with the command @who-options columns="column1 column2 ..." (the quotes are required).  It determines which columns are visible in @who.
2

2

2
The current values for these options are shown using the command @who-options.
35
5
4
2
2
*forward*
2
@who-options
35
5
4
6
2
Usage:  @grant <object> to <recipient> (used by owner)
2
        @grant <object> to me          (used by recipient)
2

2
Change the ownership of an object, including all properties and verbs owned by the owner.  This is a two-step process:  First the owner offers it to the recipient, then the recipient accepts it.
2

2
All properties and verbs on the object that are owned by its owner will be given to the recipient.  This means you should probably be careful in accepting an object.  In addition, !c verbs owned by this object's owner will change hands on all descendants.
35
5
4
7
2
*forward*
2
(aliases_extra)
2
For information about setting aliases, see help on the following topics:
2

2
@rename -- setting an object's name and all aliases
2
@addalias -- adding an alias to an object
2
@rmalias -- removing an alias from an object
35
5
4
3
2
*forward*
2
(common_aliases)
2
Typing `examine object' will show you its aliases, and a great deal more.  See `help examine' for more information.
35
5
4
1
2
Every object on the MOO (players included) has a list of aliases, or names by which it can be referred.  This is useful when an object has a nice long descriptive name that you don't want to have to type every time you refer to it.
35
5
4
14
2
Syntax:  @netforward <message-number>
2
         @netforward 
2
         @netforward <message-sequence> on *collection
2

2
Forwards the contents of the indiciated messages from your MOO email collection to your registered email address.  You get the message numbers for use here by typing the '@mail' command, which prints a table of contents for your entire MOO email collection.  If no arguments are given to @netforward, then the 'current message' in your collection is sent.  In any case, the 'current message' after @netforward finishes is not affected.
2

2
The most general form of the @netforward command is
2

2
         @netforward <message-sequence> [on *<collection>]
2

2
where <message-sequence> is as described in `help message-sequences'.
2
As with the @mail command you may @netforward messages on any publically readable collection (child of $mail_recipient).
2

2
If you do not have a valid registered email address, you will not be able to use this command and must register first.  See `help @register' for details.
35
5
4
2
2
*forward*
2
@gaglist
35
5
4
27
2
*subst*
2
Time stamping is a way to get some idea when things happened in your scrollback--it's most useful when you idle for long periods of time, although it can be handy in other situations too.  If you have time stamping on, you'll be told every so often what time it is.
2

2
Example:
2
  [ 9:10 am ]                               {a time stamp}
2
  Foo says, "I'll be back soon ..."         
2
  (you idle too)
2
  [ 5:10 pm ]                               {a stamp triggered by Foo's return}
2
  Foo says, "I'm back."
2
  >:wakes
2
  [ 6:30 pm ]                               {a stamp triggered by your emote}
2
  (your name) wakes.
2

2
There are two time stamping options, both under `@options misc' (see `help options').
2

2
Time Stamping Options
2
---------------------
2

2
   @options misc tstamp=<number>
2

2
This sets the minimum number of minutes between time stamps.  You won't get time stamps when nothing is happening around you, so the interval between them may be longer.
2

2
If <number> is negative, that doesn't mean you'll get timestamps in reverse order or anything; it's a way of saying that each time stamp should appear BEFORE the event that triggers it.  If <number> is positive, then each time stamp appears AFTER the trigger event.
2

2
   @options misc fstamp=<string>
2

2
This sets the format of the time stamp.  Time stamps are run through $string_utils:pronoun_sub and $time_utils:time_sub (see `help $time_utils:time_sub').  One additional option not is %D.  This expands to ", on <day>" when the day changes (i.e. the first stamp for the new day would have it.  The rest of the time it is ignored.For example, the default is "%[$player:misc_option("fstamp")]", which would come out right now as "%[$player:timestamp_string($player:misc_option("fstamp"), 0)]".
35
5
4
2
2
*forward*
2
time-stamping
35
5
4
2
2
*forward*
2
time-stamping
35
5
4
3
2
*verbdoc*
2
$pronoun_sub.two-letter
2
parse_segment
35
5
4
2
2
*forward*
2
timestamping
35
5
4
2
2
*forward*
2
timestamping
35
5
4
19
2
A MOO is a kind of virtual reality, in which players move about from place to place manipulating their environment in what we hope are amusing, entertaining, or enlightening ways.
2

2
This is just a simple introduction to how to get around; more introductory texts are being written and will be listed here as we finish them.
2

2
Most commands have the form of simple English sentences:
2

2
    <verb>
2
    <verb>  <direct object>
2
    <verb>  <direct object>  <preposition>  <indirect object>
2

2
Don't use English articles (e.g. 'a', 'an', or 'the') in your commands; the MOO won't understand them.  You can refer to yourself as 'me' and the room you're in as 'here'.
2

2
The first five kinds of commands you'll want to know are listed below.  Type 'help <topic-name>' for details on any of them:
2

2
look -- getting a description of the current room or any other object
2
say -- speaking to the other players in the same room as you
2
@who -- showing which players are currently connected to the MOO
2
movement -- how to move around in the MOO, from room to room
2
@quit -- disconnecting from the MOO
35
5
4
6
2
*subst*
2
Syntax: home
2

2
Instantly teleports you to your designated home room.
2

2
Initially, this is %[$player_start:dname()], but you can change that; see 'help @sethome' for details.
35
5
4
11
2
Usage:  news [contents] [articles]
2

2
Read the latest edition of the newspaper, which carries articles concerning recent changes to the MOO server or to the main public classes, or whatever the administrators feel like writing about.
2

2
Specifying `contents' means to print only the table of contents for the newspaper.
2

2
The most common uses of this command:
2

2
news          -- display articles in the current edition
2
news new      -- display articles you haven't read yet
2
news contents -- list articles in the current edition
35
5
4
2
2
*unpass*
2
movement
35
5
4
162
2
*subst*
2
Getting started with your new MOO
2
1================================
2

2
This document is intended to help you get started running a new MOO with a JHCore database.  It doesn't cover everything, but it should be enough to keep you occupied for a while.
2

2
Note that this document assumes you have some knowledge of MOO use and programming, or at least setting properties.  Without it, you will lose horribly.
2

2
Before you start
2
2===============
2

2
You will need a server version 1.8.0 or later.  JHCore requires at least 1.8.0 to work because it uses some of the new features.
2

2
You should note that JHCore inherits JHM's rather generous tick limits.  These values are now set to:
2

2
  Max ticks (foreground): %[tostr($server_options.fg_ticks)]
2
  Max ticks (background): %[tostr($server_options.bg_ticks)]
2

2
We found this increase from the MOO server default was necessary to avoid tickouts in some of the more complicated parts of the core.  You may also need a higher seconds limit.
2

2
We also recommend compiling with OUTBOUND_NETWORK if you can; besides enabling mail sending (which allows autoregistration), it allows for access to various other network services we think are cool.  Since version 1.8.0 of the server supports MPL, JHCore now has multiple port listening support verbs on $network.
2

2
Starting the database
2
2====================
2

2
Once you have a MOO compiled and a JHCore database, you can start it up.  You probably want to fetch JHCore.db and rename it to something else; we'll call it `MyMOO.db' for now.  Assuming you have the MOO executable (moo) and your database file (MyMOO.db) in your current directory, you can type `./restart MyMOO 1234' to start it up on port 1234.  For more detail, see the `README' file in the LambdaMOO source distribution.
2

2
Once it's finished loading the database (you can watch `tail -f MyMOO.log' if you're anxious), you should telnet or otherwise connect to your host and port, and `connect Wizard' to log in.
2

2
Since the database doesn't initially require a password to log in as Wizard, you should set your password immediately; just type `@password <new-password>'.  Then you may want to @rename and @describe yourself.
2

2
Letting people log in
2
2====================
2

2
It takes a surprising amount of setup before other people can log in, but most of it is just setting values for properties.
2

2
First, you almost certainly want to change the message people see when logging in.  Just `@edit $login.welcome_message'.  (If your client supports local editing, you probably want to do `@editoption +local' first.)
2

2
You have four options for letting people log in:
2

2
  * (semi-)anonymous guest characters (can be used with any of the others)
2
  * autocreation at login
2
  * registration (requires guests)
2
  * manual player creation by wizards
2

2
Each of them requires a separate piece of setup.  If you have outbound network enabled and will be sending new player mail, you will probably want to edit $wiz_utils:send_new_player_mail to change the message that gets sent for new characters.
2

2
Guests
2
3-----
2

2
If you want people to be able to log in as guests, you need to create at least one:
2

2
  >@make-guest guest
2
  Created the guest (#415).
2

2
(Before doing that, you may want to set $guest.default_gender; we use "spivak", but "neuter" is a popular choice.)
2

2
Once you have a guest, you can make additional guests with specific adjectives--for example,
2

2
  >@make-guest example
2
  Created an example guest (#416).  Now don't forget to @describe em as
2
      something.
2

2
There are three more things you may well want to change about guests.  First, by default, guests' sites will be shown to anyone who looks at them.  To turn this off, set $guest.show_site to 0.
2

2
Second, $country_db optionally provides the following behavior:  When people log in as "guest", the connection system can try to match their connect domain using $country_db, and for example connect ".uk" users to "the British guest".  (Guests of this type are created automatically.)  To turn this on, just set $login.sitematch_guests to 1.  For more information about customizing this, see `help $country_db'.
2

2
Finally, guests are descendants of $prog, the generic programmer, so they can read code and list properties and such (although they can't create anything).  To change this, just @chparent $guest to a different parent, like $player.
2

2
Autocreation
2
3-----------
2

2
If you set $login.create_enabled to 1, people will be able to create new characters from the login screen, with no registration address.  We don't recommend this at all, but if you're bent on anonymity, this is your option.
2

2
AutoRegistration
2
3---------------
2

2
If you have guest characters and outbound network connections, you can allow people logged in as guests to register automatically for characters.  If you want to allow this, you need to set up $network for outgoing mail (see below), and set $guest.registration_ok to 1.  If not, you may want to set the @noregister message on $guest to something more specific than the default.
2

2
If you use autoregistration, you may want to maintain a list of sites from which automatic registration is not allowed.  (For example, some schools provide their students with one account for each computer science class they take.)  Use the `@spooflist' command to add a domain to this list, which is stored in $login.spooflist.
2

2
@Make-player
2
3-----------
2

2
If you have no registration and no autocreation, the only way to make new characters will be the @make-player command (on $wiz and $feature->community group).  Setting up $network for outgoing mail (see below) will make this easier, as mail can be sent automatically to the new user.
2

2
In addition, you'll probably want to change $login.registration_string so it doesn't make false claims.  If you want to include an address to send to for characters, set $login.registration_address to that address, and use "%e" in the registration string to refer to that address.  For example:
2

2
  >;$login.registration_address = "mymoo-registration@mymoo.mydomain.net"
2
  >;$login.registration_string = "Automatic registration is not allowed.
2
      Send mail to %e explaining why you want a character."
2

2
The network
2
2==========
2

2
The $network object provides various facilities related to networking; most importantly for your initial setup, it handles outgoing mail (through $network:sendmail).  Here are some properties you should set before going much of anywhere:
2

2
  .MOO_name
2
    -- your moo's name--for example, "MyMOO"
2
  .site
2
    -- your moo's site--for example, "mymoo.mydomain.net"
2
  .port
2
    -- your moo's port--for example, 1234
2

2
  .postmaster
2
    -- someone who will handle bounced mail and such
2
  .mail_domain
2
    -- a mail domain to give for reply addresses (used in
2
       $network:reply_address, which see).  If you don't have incoming
2
       mail (we don't either), you might as well leave this as is.
2

2
And finally:
2

2
  .active
2
    -- signals that $network is set up and (hopefully) usable for mail
2

2
Administration
2
2=============
2

2
The JHCore admin system is a way to make certain tasks/responsibilities/privileges available to groups of people without requiring that those people be wizards.  The philosophy is that there are several roles that wizards have traditionally been required to play, and that those people who are best suited to some of the roles are not interested in performing all of them.  Allocating permissions to administrative groups by their functions provides a way to specify the role(s) people hold.
2

2
Our administration system divides into three main groups:
2

2
  * The Steering Committee, which makes decisions about broad goals and such;
2
  * The Community Group, which tries to avoid or resolve social conflicts;
2
  * The Tech Group, composed of three subgroups that handle technical issues.
2

2
This is a gross oversimplification; you can look at each of them (@kids $admin_group) to get more description.
2

2
You'll probably want to customize this.  Simple customizations include changing the descriptions and the names (don't forget to describe the associated mailing lists!); you might go on to moving commands around (between $wiz and $feature->community, most likely); or you might just want to get rid of this complex system.  It should be fairly simple to reduce the administration system to a simple wizard-rule system a la LambdaCore, by recycling admin groups, changing $admin, and probably hacking some code.
2

2
Quota
2
2====
2

2
MOO Quota is a person's usage quota.  The system defaults to using byte quota.  That is, a person has a specific number of bytes that he or she can use on the MOO.  The person cannot create objects, add verbs, or add properties he or she is over quota.
2

2
@quota (see `help @quota') is the command used to display or set a person's quota.  @measure is used to measure objects (in bytes).   The verb you use to start the measurement task (which measures objects in the background) is $quota_utils.byte:measurement_task().  You'll need to start this task by typing:
2
  ;$quota_utils.byte:measurement_task()
2
as a wizard.
2

2
In order to switch to object quota (which is not recommended), you can kill the measurement task if it is running and set $quota_utils to $quota_utils.object.  Then you have to run $quota_utils:initialize_quota().
2

2
Please note that, for whichever system you use, the default quota is 0 (bytes or objects).  You can set this by setting $quota_utils.default_player_quota and running $quota_utils:initialize_quota (which sets everyone's total quota to the value in .default_player_quota, so don't do this on a MOO that has players).  Setting .default_player_quota without running $quota_utils:initialize_quota simply sets the default quota for all players created from that point.
2

2
Also, you will want to edit `help quota-policy' to reflect your MOO's quota policy.  @add-feature the documentation feature and use @helpedit to edit help.
2

2
Porting
2
2======
2

2
There's a lot in JHM and elsewhere that you might want but that JHCore doesn't provide.  For general information about porting, you should check the MOO-Cows "Newbie Arch-Wizard FAQ".  However, the site from which you retrieved JHCore should also have a set of code modules that are easily ported into a JHCore database; be sure to check these out.
2

2
Miscellaneous
2
2============
2

2
You probably want to change the name of the newspaper.  Just `@edit $news.description'.  If you like, you can `@edit $help.news', too.
2

2
By default, JHCore allows only a small set of "genders" (which actually cover person, number, and gender) for players.  The set of defined genders is in $gender_utils.genders; $gender_utils.player_genders holds the list allowed for players.  You may want to expand (or contract!) this list.
2

2
In closing
2
2=========
2

2
If you're using JHCore, we'd like to hear from you.  JHM is at jhm.ccs.neu.edu, port 1709; you can log in as a guest and request a character if you don't have one already.  Please feel free to send mail to the *jhcore list--report bugs, provide fixes, ask for help, or just tell us about your MOO.
35
5
4
22
2
Portions of this database are derived from the LambdaCore
2
distribution, available for anonymous ftp at parcftp.xerox.com.  The
2
following copyright notice applies to new and derived works within
2
this database.
2

2
Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998 by Ken Fox.
2

2
                        All Rights Reserved
2

2
Permission to use, copy, modify, and distribute this software and its 
2
documentation for any purpose and without fee is hereby granted, 
2
provided that the above copyright notice appear in all copies and that
2
both that copyright notice and this permission notice appear in 
2
supporting documentation.
2

2
KEN FOX DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
2
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
2
KEN FOX BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
2
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
2
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
2
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
2
SOFTWARE.
35
5
4
2
2
*forward*
2
@audit
35
5
4
1
2
This MOO apparently has no quota policy.   You should talk to an admin to get someone to write a quota policy.
35
5
4
7
2
There are three separate (but not necessarily disjoint) groups of administrators, with different roles and responsibilities.  You can get help about each of the following groups, listed here with a brief explanation of what it is they're responsible for.
2

2
steering-committee -- defining and maintaining the MOO's goals and objectives
2
tech-group -- technical development and maintenance of the MOO
2
community-group -- handling social problems and promoting a sense of community
2

2
By defining the responsibilities of the various types of administrators, we hope to clear up any confusion about what a person `in charge' is supposed to be doing, and what authority that person has.
35
5
4
3
2
Syntax: @sethome
2

2
Sets your designated home (see `help home') to be the room you're in now.  If the current room wouldn't allow you to teleport in, then the `@sethome' command nicely refuses to set your home there.  This avoids later, perhaps unpleasant, surprises.  Additionally, your home must be a room that will allow you to stay there.  Rooms which you own will do this, as will rooms to which you have been added as a resident.  See the @resident command for help on adding someone as a resident to a room you own.
35
5
4
2
4
3
1
135
2
Usage: go <direction> ...
2
       go to <location>
4
2
1
107
2
The first form moves in the given directions in order, taking you through many rooms in a single command.  The second form will try to figure out a path that will bring you to your desired destination.
35
5
4
2
2
*forward*
2
current-object
35
5
4
2
2
*forward*
2
@forward
35
5
4
19
2
This is the JHCore sample help manners text.  You should probably modify it for your MOO.
2

2
Behavior that would be considered rude in `face-to-face' interaction is rude here, too.
2

2
Some ways to interpret that:
2

2
Be nice.  Don't be rude or annoy other people.  If someone requests that you stop a particular behavior or action, please respect eir wishes.
2

2
Avoid interrupting other people who are working or conversing with their friends.  You're welcome to explore the public places and ask for advice if you're stuck, but please try to read the online documentation and help yourself, first.
2

2
In addition, please realize that many players who log in here are busy with other things, both virtually and in real life, and therefore may not always be able respond to you immediately.
2

2
Harrassment of other players will not be tolerated.  Emoted violence and obscenities are considered inappropriate.
2

2
It isn't reasonable to `:kiss' or `:hug' folks you don't know.  Also, speak out loud to people you don't know, rather than paging or whispering.
2

2
Respect other player's sensibilities.  MOO inhabitants and visitors come from a diverse range of cultural backgrounds, both in the U.S. and abroad, and have varying ideas of what constitutes offensive expression.  Please keep text as free of potentially-offensive language and material as you can.
2

2
Try to avoid arguing or debating with another player in a crowded public room; such interactions interrupt the other players in the room.  Instead, consider paging or moving your conversation to one of the many empty public rooms.
35
5
4
2
2
*forward*
2
time-stamping
35
5
4
122
2
Naming an object can be a tricky business.  The first thing to know is that there are two sorts of names; there is the "base" name of an object, i.e. the value of the .name property, and the "modified" name, i.e. the value returned by the :name(...) verb.  When you specify the name when you @create an object (see `help @create') or @rename it (see `help @rename'), you are only setting the base name (as well as the object's aliases, which don't enter into this discussion).  The modified name is automatically generated from the base name depending on the particular situation.  Usually this just adds an article in front of the name, like "a" or "the", e.g. if an object is listed in a player's inventory.  You can control how the modified name is generated with a number of auxiliary properties.  Let's try a few examples.
2

2
Suppose Janet wants to create a football.  She types:
2

2
        >@create $thing named "football"
2

2
then does an inventory and sees:
2

2
        Carrying:
2
         a football
2

2
Notice that she did not include "a" in the name when creating the football; it gets dynamically added to the name whenever she does an inventory.  Now if she were to drop the football, she would see:
2

2
        You drop the football.
2

2
while other people in the room would see:
2

2
        Janet drops a football.
2

2
Notice that the modified name is different depending on who sees the message.  The difference is that the first message uses the definite-article modified name, while the second message uses the indefinite-article modified name; in pronoun-substitution lingo (see `help pronouns'), the first one uses %dd, while the second uses %di.
2

2
Possessor
2
---------
2

2
Now suppose there are a bunch of footballs around, and Janet wants to distinguish hers from the others by adding her name to it; in other words, she wants the modified name to be "Janet's football".  She can do this by adding a .possessor property to the football and setting it to point to herself:
2

2
        >@prop football.possessor
2
        >@set football.possessor to me
2

2
Now when she picks up the football, she sees:
2

2
        You pick up Janet's football.
2

2
while others in the room see:
2

2
        Janet picks up Janet's football.
2

2
and when she does an inventory she sees:
2

2
        Carrying:
2
         Janet's football
2

2
Unique
2
------
2

2
Now suppose Janet wants to create a famous painting:
2

2
        >@create $thing named "Mona Lisa"
2

2
Her inventory now looks like this:
2

2
        Carrying:
2
         Janet's football                a Mona Lisa
2

2
This isn't quite right, though; there's only *one* Mona Lisa, so it doesn't make much sense to call it "a" Mona Lisa!  Janet can fix this by setting the .unique property:
2

2
        >@set mona.unique to 1
2

2
(Any non-zero value will do, but 1 is the best non-zero value.)  Now her inventory looks like this:
2

2
        Carrying:
2
         Janet's football                the Mona Lisa
2

2
And now when she drops the Mona Lisa, other people in the room see:
2

2
        Janet drops the Mona Lisa.
2

2

2
Number
2
------
2

2
All this has made Janet hungry, so she makes some peanuts:
2

2
        >@create $thing named "peanuts"
2

2
Now her inventory looks like this:
2

2
        Carrying:
2
         Janet's football                a peanuts
2

2
Oops.  "Peanuts" is plural, so it shouldn't use the singular article "a".  She can fix this by setting the .number property:
2

2
        >@set peanuts.number to 20
2

2
(The exact number doesn't really matter, since there's no way to drop, say, 3 peanuts.  Just as long as .number is not 1.)  Now her inventory is:
2

2
        Carrying:
2
         Janet's football                some peanuts
2

2
Proper
2
------
2

2
Finally, Janet decides to create a room called "Janetland":
2

2
        >@dig "north,n|south,s,out" to "Janetland"   (see `help @dig')
2

2
But when she goes there, there's something amiss:
2

2
        >north
2
        The Janetland
2
        You see nothing special.
2

2
"Janetland" is a proper noun, so there shouldn't be any article!  This is easily fixed:
2

2
        >@set here.proper to 1
2

2
(Again, any non-zero value will work.)  Now when she looks at the room, she sees:
2

2
        Janetland
2
        You see nothing special.
2

2
Summary
2
-------
2

2
In summary, here are the various properties you can set on an object to control how the modified name gets generated:
2

2
        * .possessor -- Set this to another object (e.g. a player) to have that object's name appear in the object's name.
2
        * .unique -- Set this to 1 to have an object always use the definite article "the".
2
        * .number -- Set this to something other than 1 to have a collective object use the indefinite article "some".
2
        * .proper -- Set this to 1 to have an object not use any article at all.
2

2
If you need more particular control, you can use the .def_art and .indef_art properties to specify the exact definite and indefinite article to use, respectively.  Also, you can use verbs in place of any of these properties; for example, you might write a :possessor verb which returns the current owner of the object, so that the name of the object will automatically change if the ownership changes.
35
1
4
11
1
23
1
22
1
28
1
20
1
21
1
163
1
106
1
54
1
40
1
181
1
166
199
1
1
182
199
1
4
2
2
*forward*
2
core-copyright
35
5
4
0
35
1
5
35
4
4
2
2
main help database
2
help
35
5
4
9
2
The object $help is the main help database.  For every help topic there is a corresponding property on $help, interpreted as follows:
2

2
$help.(topic) = string           - one-line help text.
2
$help.(topic) = {"*verb*",@args} - call this:verb(args,{}) to get text
2
$help.(topic) = any other list   - multi-line help text
2

2
There is also a "" property which applies in the case of `help' typed without any arguments.
2

2
See the description of $generic_help for more detail.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
189066
0
913134427
200
1
#55
Recycling Center

16
35
-1
-1
-1
5
-1
80
12
_recreate
199
173
-1
_recycle
199
173
-1
_create
199
165
-1
addhist
199
173
-1
show*-history
199
37
-1
request
199
153
5
setup_toad
199
165
-1
add_orphan
35
173
-1
remove_orphan
35
173
-1
valid
199
173
-1
init_for_core
199
173
-1
accept
199
173
-1
4
orphans
announce_removal_msg
nhist
history
26
4
0
35
1
2

35
5
0
50
35
0
4
0
35
0
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
4
4
2
2
Recycling Center
2
Center
35
5
4
1
2
Object reuse. Call $recycler:_create() to create an object (semantics the same as create()), $recycler:_recycle() to recycle an object. Will create a new object if nothing available in its contents. Note underscores, to avoid builtin :recycle() verb called when objects are recycled. Uses $building_utils:recreate() to prepare objects.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
8673
0
913134427
200
1
#56
Generic Garbage Object

16
35
-1
-1
-1
-1
-1
-1
6
description
35
173
-1
look_self
35
173
-1
title titlec name namec dname*c iname*c
35
173
-1
set_verb_info(old)
199
161
-1
add_verb(old)
199
161
-1
name_and_number iname_and_number dname_and_number namec_and_number inamec_and_number dnamec_and_number
199
173
-1
1
aliases
1
4
1
2
garbage
35
1
#57
Mail Options

16
35
-1
-1
-1
60
-1
58
13
actual
35
173
-1
parse_@mail
35
173
-1
parse_sticky parse_manymsgs
35
173
-1
parse_replyto
35
173
-1
show_manymsgs
35
173
-1
show_sticky
35
173
-1
show_@mail
35
173
-1
show_replyto
35
173
-1
show
35
173
-1
check_replyto
35
173
-1
check_netmail
199
173
-1
show_netmail
199
173
-1
nominate_for_core
199
173
-1
11
show_include
show_all
show_nosubject
show_expert
show_enter
type_manymsgs
type_@mail
type_replyto
show_nosubj_notify
show_resend_forw
choices_rn_order
32
4
2
2
Original message will not be included in replies
2
Original message will be included in replies
35
5
4
2
2
Replies will go to original sender only.
2
Replies will go to original sender and all previous recipients.
35
5
4
2
2
Mail editor will initially require a subject line.
2
Mail editor will not initially require a subject line.
35
5
4
2
2
Novice mail user...
2
Expert mail user...
35
5
4
2
2
Mail editor will not start with an implicit `enter' command.
2
Mail editor will start with an implicit `enter' command.
35
5
4
1
0
0
35
5
4
2
0
2
4
1
0
2
35
5
4
2
0
1
4
1
0
1
35
5
4
2
2
Do display subjects in normal mail-notification messages.
2
Don't display subjects in normal mail-notification messages.
35
5
4
2
2
@resend puts player in Resent-By: header
2
@resend puts player in From: header (like @forward)
35
5
4
3
4
2
2
read
4
1
2
.current_message folders are sorted by last read date.
4
2
2
send
4
1
2
.current_message folders are sorted by last send date.
4
2
2
fixed
4
1
2
.current_message folders are not sorted.
35
5
4
13
2
include
2
all
2
nosubject
2
expert
2
enter
2
sticky
2
@mail
2
manymsgs
2
replyto
2
nosubj_notify
2
netmail
2
resend_forw
2
rn_order
35
1
2
!include!noinclude!all!sender!nosubject!expert!enter!sticky!@mail!manymsgs!replyto!nosubj_notify!resend_forw!rn_order!
35
1
4
2
2
noinclude
2
sender
35
1
5
35
5
2
mail
35
5
2
@mail-options
35
5
2
the mail system
35
5
5
35
4
4
1
2
Mail Options
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
9272
0
913134427
200
1
#58
Edit Options

16
35
-1
-1
-1
60
-1
59
0
3
show_quiet_insert
show_eval_subs
show_local
24
4
2
2
Report line numbers on insert or append.
2
No echo on insert or append.
35
5
4
2
2
Ignore .eval_subs when compiling verbs.
2
Use .eval_subs when compiling verbs.
35
5
4
2
2
Use in-MOO text editors.
2
Ship text to client for local editing.
35
5
4
3
2
quiet_insert
2
eval_subs
2
local
35
1
2
!quiet_insert!eval_subs!local!
35
1
5
35
1
0
20
35
5
2
edit
35
5
2
@edit-options
35
5
2
the editors
35
5
5
35
4
4
1
2
Edit Options
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
988
0
913134427
200
1
#59
Display Options

16
35
-1
-1
-1
60
-1
61
2
actual
35
173
-1
show
35
173
-1
3
show_blank_tnt
show_shortprep
show_firstancestor
24
4
2
2
Treat `this none this' verbs like the others.
2
Blank out the args on `this none this' verbs.
35
5
4
2
2
Display prepositions in full.
2
Use short forms of prepositions.
35
5
4
2
2
./: will not show ancestor properties/verbs.
2
./: shows first available ancestor properties/verbs if none on this.
35
5
4
3
2
blank_tnt
2
shortprep
2
firstancestor
35
1
2
!blank_tnt!shortprep!thisonly!firstancestor!
35
1
4
1
2
thisonly
35
1
5
35
5
2
display
35
5
2
@display-options
35
5
2
the @display command
35
5
5
35
4
4
1
2
Display Options
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2056
0
913134427
200
1
#60
Generic Option Package

144
35
-1
-1
-1
1
57
65
12
get
35
173
-1
set
35
173
-1
parse
35
173
-1
_name
35
173
-1
add_name
35
173
-1
remove_name
35
173
-1
show
35
173
-1
actual
35
173
-1
istype
35
173
-1
islistof
35
173
-1
desc_type
35
173
-1
parsechoice
35
173
-1
7
names
_namelist
extras
namewidth
category
command
used_for
21
4
0
35
1
2
!
35
1
4
0
35
1
0
15
35
5
2

35
5
2

35
5
2

35
5
5
35
4
4
1
2
Generic Option Package
35
5
2
an option package in need of a description.  See `help $generic_option'...
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
13175
0
913134427
200
1
#61
List Options

16
35
-1
-1
-1
60
-1
118
2
actual
35
173
-1
show
35
173
-1
4
show_parentheses
show_nonumbers
show_//_comments
show_args_assignment
25
4
2
2
Parenthesize expressions in listings only as needed.
2
Fully parenthesize expressions in listings.
35
5
4
2
2
Include line numbers.
2
Omit line numbers.
35
5
4
2
2
Don't mangle comments in verb code
2
Display comment lines with //
35
5
4
2
2
Suppress assignment of args; do named-arg extraction
2
Show assignment of args; no named-arg extraction
35
5
4
4
2
parentheses
2
nonumbers
2
//_comments
2
args_assignment
35
1
2
!parentheses!nonumbers!numbers!//_comments!args_assignment!
35
1
4
1
2
numbers
35
1
0
20
35
5
2
list
35
5
2
@list-options
35
5
2
the @list command
35
5
5
35
4
4
1
2
List Options
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1992
0
913134427
200
1
#62
Site-Locks

0
35
42
-1
17
113
-1
145
1
is_writable_by
199
173
-1
0
29
1
176
35
5
5
35
5
5
35
5
5
35
5
5
35
1
4
0
35
0
0
1
35
5
5
35
5
5
35
5
5
35
5
4
1
1
2
35
1
4
0
35
1
5
35
5
5
35
1
4
0
35
0
5
35
4
4
1
2
Site-Locks
35
1
2
Notes on annoying sites.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1084
0
913134427
200
1
#63
housekeeper

17
63
15
-1
199
52
-1
104
19
look_self
63
173
-1
cleanup
63
165
-1
replace
63
173
-1
cleanup_list
63
21
-1
add_cleanup
63
93
-2
remove_cleanup
63
29
-1
controls
63
173
-1
continuous
63
165
-1
litterbug
63
173
-1
is_watching
63
173
-1
send_home
63
173
-1
moveit
199
173
-1
ejectit
199
173
-1
is_object_cleaned
63
173
-1
is_litter
63
173
-1
init_for_core
199
173
-1
clean_status
63
173
-1
is_cleaning
63
173
-1
time
63
173
-1
11
recycle_bins
owners
cleaning
litter
eschews
public_places
task
requestors
destination
clean
testing
117
4
0
63
5
4
0
63
5
1
-1
63
5
4
0
63
5
4
0
63
5
4
0
63
5
0
0
63
5
4
0
63
5
4
0
63
5
4
0
63
1
0
0
63
5
5
63
5
5
63
5
5
63
5
5
63
5
5
63
5
5
35
1
4
2
0
719529306
2
poplar
199
0
5
63
5
5
199
0
0
719972596
199
1
0
0
63
5
5
35
1
5
35
1
5
35
0
5
35
1
0
1073074343
63
5
4
1
1
63
200
1
0
-10000000
35
1
5
63
4
5
199
0
5
199
0
5
63
5
5
63
4
5
63
4
0
0
63
4
2
The housekeeper is too busy putting away all of the junk all over Jay's House that there isn't time to listen to pages and stuff like that so your page isn't listened to, too bad.
63
5
5
63
5
5
63
5
5
63
5
2
The %t isn't a real person.  You may want to mail an admin group instead.
63
5
5
63
5
4
0
63
5
4
2
0
2
0
0
63
4
5
63
4
0
719969911
199
1
0
21
200
0
1
178
63
5
1
70
35
1
2
Impossible password to type
199
0
5
63
5
0
0
63
5
5
63
5
5
63
5
5
35
1
5
199
1
5
199
1
5
35
1
5
35
1
5
63
5
5
63
5
5
199
1
5
199
1
5
63
5
5
199
0
5
63
5
5
35
1
0
0
35
1
5
63
5
4
0
35
1
5
199
0
5
199
0
5
199
0
5
199
1
5
199
1
4
0
35
1
5
199
1
5
63
5
5
63
5
5
63
5
5
63
5
5
63
5
5
63
5
5
199
1
5
199
1
5
63
5
5
63
5
5
63
5
5
63
5
5
63
5
5
35
1
5
199
1
5
199
1
5
199
0
5
199
1
4
4
0
219588
0
20977
0
873878401
0
0
200
0
5
63
5
5
199
0
5
35
1
0
0
199
1
5
199
1
5
63
5
5
199
1
5
63
4
4
1
2
housekeeper
199
1
2
A very clean, neat, tidy person who doesn't mind lugging players and their gear all over the place.
63
5
5
35
1
5
63
5
5
63
5
5
63
5
5
63
5
0
0
63
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
17049
0
913134427
200
1
#64
generic gender object

144
199
-1
-1
-1
1
34
30
3
verb_sub noun_sub
199
173
-1
modname_p
199
173
-1
adj_sub
35
165
-1
13
gender
pqc
pq
ppc
pp
prc
pr
poc
po
psc
ps
is_plural
gender_name
27
1
165
199
5
2
Its
199
5
2
its
199
5
2
Its
199
5
2
its
199
5
2
Itself
199
5
2
itself
199
5
2
It
199
5
2
it
199
5
2
It
199
5
2
it
199
5
0
0
199
5
2
neuter
199
5
5
199
4
4
1
2
generic gender object
199
5
2
A generic gender object.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
0
1
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1800
0
913134427
200
1
#65
Generic Feature Object

144
35
-1
-1
-1
1
67
66
16
look_self
35
173
-1
using this
35
173
-1
examine_commands_ok
35
173
-1
@fix-commands @fix_commands
199
37
-1
set_commands
35
165
-1
match_command
35
165
-1
moveto
35
173
-1
initialize
35
165
-1
set_feature_ok
35
173
-1
obvious_verbs
199
173
-1
examine_verb_ok
35
173
-1
@reg*ister @unreg*ister
199
45
-1
match_type_object
35
173
-1
nominate_for_core
199
165
-1
help_text
35
165
-1
recycle
199
173
-1
5
feature_verbs
feature_ok
commands
registry
help_text
19
4
1
2
Using This
35
5
0
0
35
1
4
1
2
@reg*ister @unreg*ister
35
1
1
162
35
1
2
The Generic Feature Object--not to be used as a feature object.
35
5
5
35
4
4
4
2
Generic Feature Object
2
Generic .Features_Huh Object
2
Feature Object
2
.Features_Huh Object
35
5
2
This is the Generic Feature Object.  It is not meant to be used as a feature object itself, but is handy for making new feature objects.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
10553
0
913134427
200
1
#66
Error Generator

16
199
-1
-1
-1
1
-1
69
18
raise
35
173
-1
E_NONE
35
173
-1
E_TYPE
35
173
-1
E_DIV
35
173
-1
E_PERM
35
173
-1
E_PROPNF
35
173
-1
E_VERBNF
35
173
-1
E_VARNF
35
173
-1
E_INVIND
35
173
-1
E_RECMOVE
35
173
-1
E_MAXREC
35
173
-1
E_RANGE
35
173
-1
E_ARGS
35
173
-1
E_NACC
35
173
-1
E_INVARG
35
173
-1
E_QUOTA
199
173
-1
nominate_for_core
199
173
-1
acceptable
35
173
-1
1
names
15
4
16
2
E_NONE
2
E_TYPE
2
E_DIV
2
E_PERM
2
E_PROPNF
2
E_VERBNF
2
E_VARNF
2
E_INVIND
2
E_RECMOVE
2
E_MAXREC
2
E_RANGE
2
E_ARGS
2
E_NACC
2
E_INVARG
2
E_QUOTA
2
E_FLOAT
199
5
5
199
4
4
1
2
Error Generator
199
5
4
3
2
Object to automatically generate errors.
2

2
raise(error) actually raises the error.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
6397
0
913134427
200
1
#67
Stage-Talk Feature

16
35
-1
-1
-1
65
-1
68
6
stage `* -*
199
93
-2
stage [*
35
93
-2
stage ]*
35
93
-2
stage ~*
35
93
-2
stage to
35
93
-2
init_for_core
199
165
-1
3
nothere_msg
to_msg
toself_msg
22
4
6
1
69
2
do
4
3
2
dname
2
dobj
0
1
2
 
4
3
2
verb
2
dobj
2
isn't
2
 here.
35
5
4
7
1
69
2
do
4
3
2
name
2
player
4
1
2
dc
2
 [to 
4
3
2
name
2
dobj
4
1
2
d
2
]: 
4
3
2
string
2
argstr
0
0
35
5
4
7
1
69
2
do
4
3
2
name
2
player
4
1
2
dc
2
 [to 
4
3
2
pronoun
2
player
2
pr
2
]: 
4
3
2
string
2
argstr
0
0
35
5
4
4
2
`
2
to
2
[
2
]
35
5
0
1
35
1
4
5
2
stage `* -*
2
stage [*
2
stage ]*
2
stage to
2
stage ~*
35
1
5
35
1
4
1
2
This feature contains various verbs used in stage talk, which allows players to describe their actions in terms of stage directions instead of prose.
35
5
5
35
4
4
1
2
Stage-Talk Feature
35
5
4
1
2
This feature contains various verbs used in stage talk, which allows players to describe their actions in terms of stage directions instead of prose.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
6800
0
913134427
200
1
#68
Thinking Feature

16
35
-1
-1
-1
65
-1
161
4
think-big
35
89
-2
think %* thikn
35
93
-2
think-med
35
89
-2
good
35
173
-1
0
19
4
1
2
think
35
5
0
1
35
1
4
1
2
think %* thikn
35
1
5
35
1
2
A feature for people who can't think for themselves.
35
5
5
35
4
4
1
2
Thinking Feature
35
5
2
A feature with one verb: think.  It is best explained with experience. @add-feature #93 and try think <message>.  It makes thought-bubbles.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3474
0
913134427
200
1
#69
pronoun substitution

16
35
-1
-1
-1
1
-1
73
27
do
199
173
-1
do_segment
199
173
-1
sub_name sub_dname sub_iname
35
165
-1
do_object
35
165
-1
sub_verb
35
165
-1
sub_pronoun
199
165
-1
sub_objnum
35
165
-1
sub_property
199
165
-1
obj_location obj_outer_location
35
173
-1
sub_noun
35
165
-1
lines
199
165
-1
sub_special
199
173
-1
parse_parties
199
173
-1
_listify
199
173
-1
_locations _outer_locations
199
173
-1
sub_string
35
165
-1
message_empty
35
165
-1
nominate_for_core
35
173
-1
sub_allcaps
35
165
-1
sub_pick
35
165
-1
mentioned_parties
35
173
-1
obj_special
199
165
-1
obj_contents
35
165
-1
parse_objspec
35
9
-1
flatten_message
35
173
-1
_flatten_message
35
173
-1
obj_listargs
35
173
-1
4
lambdacore
generic
two-letter
two_letter
18
1
77
35
1
1
73
35
1
1
78
35
1
1
78
35
5
5
35
4
4
1
2
pronoun substitution
35
5
2
The guts of an experimental new pronoun_sub... the idea is that messages are actually stored as lists in a yet-to-be-documented-in-full format, and players can specify any of several parse/unparse styles they want to use.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
0
1
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
22196
0
913134427
200
1
#70
first room

16
35
-1
2
-1
3
-1
-1
4
disfunc
199
173
-1
enterfunc
199
173
-1
init_for_core
199
165
-1
nominate_for_core
199
173
-1
0
37
5
35
5
0
1
35
5
5
35
5
5
35
5
5
35
5
5
35
5
0
1
35
5
4
0
35
4
5
35
5
0
883391158
35
5
4
0
35
4
0
0
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
1
-1
196
1
5
35
5
5
35
1
4
2
1
69
2
do
35
5
5
199
1
5
35
4
4
1
2
first room
35
5
4
1
2
This is all there is right now.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2690
0
913134427
200
1
#71
they

16
35
-1
-1
-1
64
-1
170
0
0
27
1
71
35
5
2
Theirs
35
5
2
theirs
35
5
2
Their
35
5
2
their
35
5
2
Themselves
35
5
2
themselves
35
5
2
Them
35
5
2
them
35
5
2
They
35
5
2
they
35
5
0
1
35
5
2
plural
35
5
5
35
4
4
3
2
plural
2
they
2
them
199
1
2
Third person plural.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
1
71
35
1
1
-1
199
1
4
2
0
670
0
913134427
200
1
#72
English Utilities

16
35
-1
-1
-1
156
-1
191
23
_noun_singular
35
165
-1
_noun_plural
35
173
-1
@add-vowel-exc*eption @add-nonvowel-exc*eption
199
149
1
capitalize capitalise
199
173
-1
vowel_exception nonvowel_exception
35
173
-1
indef_article
35
173
-1
get_conj*ugation get_noun
35
165
-1
_verb_singular _verb_plural
35
173
-1
_add_s
35
173
-1
_remove_s
35
173
-1
name_list
35
165
-1
name_and_number_list
35
165
-1
titleize
35
173
-1
nominate_for_core
35
173
-1
init_for_core
199
165
-1
selector_words
35
165
-1
selector_word_default
35
165
-1
def_art*icle
35
165
-1
_matchsel_everyone_else _matchsel_everybody_else _matchsel_anyone_else _matchsel_anybody_else
35
165
-1
_matchsel_everyone_but_* _matchsel_everyone_except_* _matchsel_everybody_but_* _matchsel_everybody_except_*
35
165
-1
is_direction_name
35
173
-1
opposite_direction
35
165
-1
name_list(new)
35
161
-1
15
vowels
vowel_exceptions
nonvowel_exceptions
capitalize_exceptions
alphabet
noun_exceptions
letter_names
cardinal_directions
verb_exceptions
cardinal_direction_opposites
here
all
everything
suggestions_recipient
selector_words
30
4
5
2
a
2
e
2
i
2
o
2
u
35
5
4
17
2
usu
2
uke
2
uvu
2
use
2
UPI
2
unit
2
univ
2
unic
2
uniq
2
unix
2
eur
2
uu
2
ubiq
2
union
2
one
2
once
2
uti
35
5
4
7
2
historic
2
ydu
2
honor
2
honest
2
habitual
2
heir
2
hour
35
5
4
30
2
the
2
a
2
an
2
and
2
for
2
but
2
or
2
nor
2
of
2
with
2
using
2
at
2
to
2
in
2
into
2
on
2
onto
2
upon
2
out
2
from
2
over
2
under
2
through
2
underneath
2
beneath
2
behind
2
beside
2
about
2
as
2
off
35
5
2
abcdefghijklmnopqrstuvwxyz
35
5
4
8
4
2
2
child
2
children
4
2
2
deer
2
deer
4
2
2
moose
2
moose
4
2
2
sheep
2
sheep
4
2
2
goose
2
geese
4
2
2
louse
2
lice
4
2
2
ox
2
oxen
4
2
2
mouse
2
mice
35
5
4
26
2
ay
2
bee
2
see
2
dee
2
ee
2
eff
2
jee
2
aitch
2
eye
2
jay
2
kay
2
el
2
em
2
en
2
oh
2
pee
2
queue
2
ar
2
es
2
tee
2
you
2
vee
2
double-you
2
ex
2
why
2
zee
35
5
4
8
2
north
2
northeast
2
east
2
southeast
2
south
2
southwest
2
west
2
northwest
35
5
4
6
4
2
2
has
2
have
4
2
2
is
2
are
4
2
2
was
2
were
4
2
2
's
2
've
4
2
2
can
2
can
4
2
2
lies
2
lie
35
5
4
8
2
south
2
southwest
2
west
2
northwest
2
north
2
northeast
2
east
2
southeast
35
5
1
194
35
5
1
109
35
5
1
149
35
5
3
4
199
1
4
4
2
all
2
everything
2
everyone
2
everybody
35
5
5
35
5
5
35
4
4
2
2
English Utilities
2
English
35
5
4
13
2
An object for storing verbs and properties dealing with the english language.
2

2
Properties:
2

2
vowels                     {"a", "e", "i", "o", "u"}
2

2
[non]vowel_exceptions      A list of prefixes which start with vowels [consonants] but don't take 'an' ['a'] as indefinite articles.
2

2
Verbs:
2

2
@add-[non]vowel-exc*eption <exception> to $english
2
    Add an exception to the 'an' before vowel ['a' before consonant] rule.
2
    Sends mail to *English if the player is not a wizard.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
20196
0
913134427
200
1
#73
Generic Pronoun_sub Translator

16
35
-1
-1
-1
1
77
105
12
parse*_message
35
165
-1
parse_segment
35
165
-1
parse_verbose
35
173
-1
unparse*_message
35
173
-1
unparse_segment
35
165
-1
unparse_verbose
35
165
-1
parse_string
35
173
-1
nominate_for_core
35
173
-1
parse_delimited_string
35
173
-1
parse_delimited_message
35
165
-1
unparse_delimited_message
35
173
-1
unparse_delimited_segment
35
165
-1
0
14
5
35
4
4
2
2
Generic Pronoun_sub Translator
2
GPST
35
5
4
2
2
%% becomes %
2
%{type:arg1:arg2:etc} becomes {"type", @args}
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
8736
0
913134427
200
1
#74
integration utils

16
199
-1
-1
-1
156
-1
13
7
integrate
199
173
-1
parse
199
173
-1
ok
199
173
-1
visible_of
199
173
-1
ok_room ok_container ok_player
199
173
-1
_parse
199
173
-1
integrate_new
199
173
-1
1
default_separator
16
2
  
199
1
5
199
5
5
199
4
4
1
2
integration utils
199
5
4
15
2
Utilities for implementing integrating descriptions.  (See `help integration' for a basic explanation.)
2

2
Useful Verbs
2
2-----------
2

2
  :integrate  -- Make text out of .integrate_paragraphs lists
2
  :visible_of -- Which of a given set of objects are visible?
2

2
Related verbs on $room
2
2---------------------
2

2
  :look_self       -- Decides what to integrate, calls :description.
2
  :description     -- Integrates objects using $integration_utils.
2
  :ok_to_integrate -- Can a given object be integrated?
2
  :visible         -- Should a given object be seen in `look'?
199
5
5
35
1
2
Integration utils is sitting here.
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5388
0
913134427
200
1
#75
Generic BigList Resident

144
35
-1
-1
-1
1
-1
79
7
_make
199
173
-1
_kill
199
173
-1
_get
35
173
-1
_put
35
173
-1
_genname
35
173
-1
_ord
35
173
-1
init_for_core
199
173
-1
4
mowner
_mgr
_genname
_list_of_a_s
18
1
35
35
5
1
13
35
5
2

35
1
2
aaaa
35
1
0
0
35
4
4
3
2
biglist
2
resident
2
gblr
35
5
4
1
2
This is the object you want to use as a parent in order to @create a place for your biglists to live.  Suitably sick souls may wish to reimplement :_genprop and :_kill to reclaim unused properties (this :_kill just throws them away and this :_genprop just relentlessly advances....  who cares).  Anyway, you'll need to look at $biglist before this will make sense.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
4141
0
913134427
200
1
#76
login watcher options

16
35
-1
-1
-1
60
-1
-1
6
show_message
35
173
-1
parse_message
35
173
-1
sub_message
35
173
-1
check_message
35
173
-1
show_idle
35
173
-1
parse_idle
35
173
-1
4
show_on
show_interest
default_message
type_idle
25
4
2
2
You will not get notified of connections and disconnections.
2
You will get notified of connections and disconnections.
35
5
4
2
2
Showing all connections and disconnections.
2
Only notifying you when interesting people connect or disconnect.
35
5
2
< %Ni %n:(has) %m.  Total: %c >
35
1
4
1
0
0
35
1
4
4
2
on
2
interest
2
message
2
idle
35
1
2
!on!interest!message!idle!
35
1
4
0
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
4
4
1
2
login watcher options
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
4478
0
913134427
200
1
#77
LambdaCore Pronoun_sub Translator

16
35
-1
-1
-1
73
-1
78
11
parse_segment
35
165
-1
unparse_name
35
173
-1
unparse_verb
35
173
-1
unparse_pronoun
35
173
-1
unparse_property
35
173
-1
unparse_objnum
35
173
-1
unparse_object
35
165
-1
unparse_iname
199
173
-1
unparse_dname
199
173
-1
parse_string
199
173
-1
nominate_for_core
35
173
-1
0
14
5
35
4
4
2
2
LambdaCore Pronoun_sub Translator
2
LCPST
35
5
4
1
2
Translate messages in the format of LambdaCore's $string_utils:pronoun_sub into list-based stuff that can be handled by $pronoun_sub, and vice versa.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
8448
0
913134427
200
1
#78
Two-Letter Pronoun_Sub Translator

16
35
-1
-1
-1
73
-1
-1
24
parse_segment
35
165
-1
parse_.
35
165
-1
parse_: parse_'
35
165
-1
unparse_verb
35
13
-1
obj_unparse
35
165
-1
extension_unparse
35
165
-1
unparse_name unparse_dname unparse_iname
35
173
-1
unparse_objnum
35
165
-1
unparse_pronoun
35
165
-1
unparse_noun
35
13
-1
unparse_property
35
13
-1
unparse_string
35
173
-1
unparse_name(alt)
35
173
-1
unparse_name(new)
35
173
-1
unparse_allcaps
35
165
-1
unparse_pick
35
165
-1
unparse_special
35
13
-1
parse_obj
35
173
-1
parse_string
35
173
-1
parse_delimited_string
35
173
-1
unparse_delimited_message
35
173
-1
unparse*_message
35
173
-1
unparse_delimited_segment
35
165
-1
unparse_segment
35
165
-1
0
14
5
35
4
4
2
2
Two-Letter Pronoun_Sub Translator
2
TLPST
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
21896
0
913134427
200
1
#79
New Generic Database

144
35
-1
-1
-1
1
37
210
29
init_for_core
199
173
-1
make_node
199
173
-1
kill_node
199
173
-1
get_node
35
173
-1
set_node
35
173
-1
data_equal
35
173
-1
accum_data
35
173
-1
accum_keys
35
173
-1
accum_entries
35
173
-1
prohibit_lookup
35
173
-1
prohibit_modify
35
173
-1
find find_key
35
173
-1
find_exact
35
173
-1
find_all find_all_keys find_all_entries
35
173
-1
_only
35
173
-1
_only_key
35
173
-1
_every
35
173
-1
insert
35
173
-1
delete
35
173
-1
delete2
35
173
-1
_delete2
35
173
-1
clearall
199
173
-1
clearall_big
35
173
-1
_kill_subtrees
35
173
-1
_common
35
173
-1
depth
35
173
-1
count_entries
35
173
-1
count_chars
35
173
-1
count
35
153
3
7
node_info
key_case
no_data
failed
ambiguous
last_modify
 
21
4
2
1
35
2

35
5
0
0
35
1
0
0
35
1
1
-3
35
5
1
-2
35
5
4
2
0
0
0
0
35
1
4
4
2

2

4
0
4
0
35
1
0
0
35
4
4
4
2
New Generic Database
2
Generic Database
2
Generic Dictionary
2
Generic Trie
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
21777
0
913134427
200
1
#80
generic sittable object

144
35
3
-1
81
5
81
83
14
event_exit event_leave_by_exit event_move_by_exit_attempted event_door_open_attempted event_move_within_room
35
173
-1
sitting_string
35
173
-1
empty_msg
35
173
-1
initialize
35
165
-1
sitting_msg on_msg sub_on sub_sitting desc_along_with_msg sub_desc_along_with
35
173
-1
moveto
35
165
-1
insert_sitting
35
173
-1
delete_sitting
35
173
-1
event_sit
35
173
-1
g*et t*ake
35
41
-1
sitting
35
173
-1
set_sitting
35
173
-1
obj_sitting
35
173
-1
seat_for
35
173
-1
5
sitting
empty_msg
sitting_msg
on_msg
desc_along_with_msg
27
4
0
35
1
4
2
1
69
2
do
35
5
4
3
1
69
2
do
2
sitting
35
5
4
3
1
69
2
do
2
on
35
5
4
3
1
69
2
do
2
along with
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
4
4
2
2
generic sittable object
2
sittable
35
5
2
Generic Sittable Object---parent for $furniture. Original from yduJ@LambdaMOO.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
7745
0
913134427
200
1
#81
generic piece of furniture

144
35
3
-1
52
80
-1
-1
16
sit
35
157
4
stand get
35
157
5
sh*ove pu*sh
35
157
5
description
35
173
-1
integrate_room_msg
35
173
-1
stand_msg squeeze_msg sit_msg fall_msg shove_msg also_shove_msg next_to_msg already_sitting_msg
35
173
-1
next_to
35
165
-1
delete_sitting
35
165
-1
insert_sitting
35
173
-1
moveto
35
173
-1
visible
35
173
-1
empty_message_integrate_room
35
173
-1
help_verbs
199
173
-1
obvious_seat_for
199
173
-1
move_next_to
35
173
-1
shoving_policy
35
173
-1
16
squeeze
seats
sit_msg
squeeze_msg
stand_msg
fall_msg
shove_msg
also_shove_msg
nosqueeze_msg
integrate_empty_msg
integrate_sitters_msg
next_to_msg
obvious_seat
already_sitting_msg
move_next_to_msg
help_text
43
0
0
35
5
0
0
35
5
4
9
1
69
2
do
4
3
2
name
2
player
4
1
2
dc
2
 
4
3
2
verb
2
player
2
sits
2
 
4
4
2
special
2
thing
2
on
4
0
2
 
4
3
2
name
2
thing
4
1
2
d
35
5
4
8
1
69
2
do
4
3
2
name
2
dobj
4
1
2
dc
2
 
4
3
2
verb
2
dobj
2
is
2
 squeezed off 
4
3
2
name
2
thing
4
1
2
d
2
.
35
5
4
8
1
69
2
do
4
3
2
name
2
dobj
4
1
2
dc
2
 
4
3
2
verb
2
dobj
2
stands
2
 up from 
4
3
2
name
2
thing
4
1
2
d
2
.
35
5
4
8
1
69
2
do
4
3
2
name
2
dobj
4
1
2
dc
2
 
4
3
2
verb
2
dobj
2
falls
2
 off 
4
3
2
name
2
thing
4
1
2
d
2
.
35
5
4
10
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
shoves
2
 
4
3
2
dname
2
dobj
0
0
2
 off 
4
3
2
dname
2
thing
0
0
2
!
35
5
4
10
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
takes
2
 
4
3
2
dname
2
dobj
0
0
2
 with 
4
3
2
pronoun
2
player
2
po
2
.
35
5
4
7
1
69
2
do
2
There's no room 
4
4
2
special
2
thing
2
on
4
0
2
 
4
3
2
name
2
thing
4
1
2
d
2
.
35
5
4
6
1
69
2
do
4
3
2
name
2
thing
4
1
2
ic
2
 
4
3
2
verb
2
thing
2
is
2
 here.
35
5
4
13
1
69
2
do
2
  
4
3
2
name
2
player
4
1
2
ic
2
 
4
3
2
verb
2
player
2
is
2
 
4
4
2
special
2
thing
2
sitting
4
0
2
 
4
4
2
special
2
thing
2
on
4
0
2
 
4
3
2
pronoun
2
thing
2
po
2
.
35
5
4
6
1
69
2
do
2
 
4
3
2
noun
2
dobj
2
beside/between
2
 
4
3
2
name
2
dobj
4
1
2
d
35
5
0
1
35
5
4
9
1
69
2
do
2
You are already 
4
4
2
special
2
thing
2
sitting
4
0
2
 
4
4
2
special
2
thing
2
on
4
0
2
 
4
3
2
name
2
thing
4
1
2
d
2
.
35
5
4
12
1
69
2
do
4
3
2
name
2
player
4
1
2
dc
2
 
4
3
2
verb
2
player
2
crawls
2
 over 
4
3
2
name
2
iobj
4
1
2
d
2
 on 
4
3
2
pronoun
2
player
2
pp
2
 way to 
4
3
2
name
2
dobj
4
1
2
d
2
.
35
5
4
41
2
Generic Furniture.
2

2
Users can sit on, shove someone else from, and get up from furniture.
2

2
The size and shape of your new couch, recliner or floor should be reflected in the number of people that can sit on it.  The "seats" property controls how many people can easily fit on the furniture.  For example:
2

2
        @set couch.seats to 3
2

2
makes it possible for three people to sit on your couch without being squeezed off (see `help @set' and `help properties' for more information).  If your furniture might fit more people, set the "squeeze" property to the number of *additional* people who might fit:
2

2
        @set couch.squeeze to 1
2

2
In combination with .seats=3, you now have a couch that fits 3 or 4 people, and squeezes the rest off.  If your furniture is of fixed size (an armchair, for example) and is not likely to fit additional people or allow other people to squeeze in and take over, set .squeeze to -1.
2

2
Integration:
2

2
If the furniture is empty, its @integrate_empty message is used.  This defaults to something like "A couch is here."
2

2
If anyone is sitting on the furniture, and it has a non-null @integrate_room message, that is used.  The default is null.
2

2
If anyone is sitting on the furniture, and it has a null @integrate_room message, then @integrate_empty and @integrate_sitters messages are concatenated.  The latter message defaults to something like "  You, Jay, and the System Object are sitting on it."
2

2
In all messages, %N stands for the list of sitters.
2

2
Other messages for builders to know about:
2

2
@sitting ("sitting"):  verb for what people are doing on it.
2
@on ("on"):  preposition for what people are, uh, figure it out.
2
@empty (""):  message for description, if no one is sitting on it.
2
              (if anyone is, the string is hard-coded.)
2

2
@sit ("Foo sits on the couch"):  obvious.  may have extra text tacked on.
2
@squeeze ("Bar is squeezed off the couch."):  not enough room!
2
@nosqueeze ("There's no room on the couch."):  really not enough room.
2
@stand ("Foo stands up from the couch."):  obvious again.
2

2
@fall ("Foo falls off the couch."):  when furniture gets moved.
2

2
@shove ("Foo shoves Bar off the couch!"):  obvious.
2
@also_shove ("Bar takes Baz with her."):  innocent bystanders.
2

35
5
4
5
1
31
1
6
1
4
1
52
1
51
35
1
4
2
1
69
2
do
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
4
4
3
2
generic piece of furniture
2
piece of furniture
2
furniture
35
5
2
A generic piece of furniture. See `help $furniture' for more information.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
17554
0
913134427
200
1
#82
generic module

144
35
-1
-1
-1
5
211
-1
75
set_components set_objects set_object_names set_made_for_unpack set_made_for_update set_props_for_update set_todo_for_update
35
165
-1
add_object
199
173
-1
duplicate_for_pack
199
173
-1
@pack
199
105
1
pack
199
173
-1
is_packing pack_task is_unpacking unpacking_task
35
173
-1
pack_start
35
173
-1
pack_warning
199
173
-1
pack_fatal_error
35
173
-1
pack_finish
199
173
-1
pack_abort
35
173
-1
nuke_duplicated_objects
199
173
-1
@unpack
199
109
5
unpack_from
199
173
-1
resolve_name
35
173
-1
resolve_owner
35
173
-1
object_unpack_from
199
173
-1
unsub_references
35
173
-1
duplicate_object
199
173
-1
substitute_references
35
173
-1
_substitute_references
35
173
-1
duplicate_object_finalize
199
173
-1
pack_line
199
173
-1
pack_header
199
173
-1
pack_object_info
199
173
-1
pack_object_verbs
199
173
-1
pack_object_properties
199
173
-1
pack_object_inherited_properties
199
173
-1
owner_name
35
173
-1
object_flags
35
173
-1
pack_lines
199
173
-1
obj_ref
35
173
-1
unpack_object
199
173
-1
unpack_property-defined
199
173
-1
unpack_property-inherited
199
173
-1
unpack_verb
199
173
-1
match_sink match_source
35
173
-1
match_sink_failed match_source_failed
35
173
-1
set_duplicated_objects
35
165
-1
@add
35
153
1
look_self
199
173
-1
nominate_for_core
199
173
-1
remove_object
199
173
-1
@remove
199
153
5
pack_description
199
173
-1
unpack_description
199
173
-1
add_property add_verb
35
173
-1
remove_property remove_verb
35
173
-1
pack_verb
199
173
-1
pack_property
199
173
-1
finish_unpack
199
173
-1
set_defines set_local_names set_locals
35
173
-1
@define @undefine @force-define
199
153
4
report_status
199
173
-1
calculate_dependencies
199
173
-1
dependencies_for_object
199
173
-1
dependencies_for_value
199
173
-1
obj_name
199
173
-1
@check-dependencies
199
41
-1
display_dependencies
199
173
-1
@update
199
105
5
update_from
199
173
-1
made_for_update todo_for_update
199
173
-1
update_object
199
173
-1
update_verb
199
173
-1
update_property-defined
199
173
-1
update_property-inherited
199
173
-1
finish_update
199
173
-1
init_for_module
199
173
-1
module_finish_unpack
199
173
-1
@add-local
35
153
1
add_local
199
173
-1
remove_local
199
173
-1
@remove-local
199
153
5
local_name
199
173
-1
19
components
objects
object_names
operation_in_progress
pack_error
duplicated_objects
core_ref_cache
pack_target
made_for_unpack
sinks
ignored_properties
sources
format_description
defines
made_for_update
todo_for_update
props_for_update
locals
local_names
41
4
0
35
1
4
0
35
1
4
0
35
1
0
0
35
1
2
extraction error
35
5
4
0
35
1
4
0
35
1
1
-1
35
1
4
0
35
1
4
2
1
195
1
140
35
1
4
2
2
object_size
2
offered
35
1
4
2
1
219
1
223
35
1
2
standard/1.0
199
1
4
0
35
1
4
0
35
1
4
0
35
1
4
0
35
1
4
0
35
1
4
0
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
4
4
1
2
generic module
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
77127
0
913134427
200
1
#83
network utils

16
199
-1
-1
-1
5
-1
114
23
open
199
165
-1
is_connected
199
165
-1
close
199
165
-1
sendmail
199
21
-1
parse_address
199
173
-1
local_domain
199
165
-1
sendmail_ok
35
173
-1
incoming_connection
199
165
-1
clean_up
199
165
-1
invalid_email_address
199
173
-1
email_will_fail
199
173
-1
listen
199
173
-1
unlisten
199
173
-1
init_for_core
199
165
-1
reply_address
199
173
-1
listening_objects
199
173
-1
listening_ports
199
173
-1
notify read set_connection_option connection_option connection_options
199
173
-1
open_connect_to
199
165
-1
controls
199
173
-1
connection_closed
199
173
-1
trusts
199
173
-1
raw_sendmail
199
173
-1
17
open_connections
connect_connections_to
postmaster
site
port
MOO_name
valid_host_regexp
sendmail_ok
invalid_userids
active
valid_email_regexp
ignore
mail_domain
httpd_prefix
connection_owners
maildrop
debug_sendmail
39
4
0
199
0
4
0
199
0
2
postmastername@yourhost
199
5
2
yoursite
199
5
0
7777
199
5
2
YourMOO
199
5
2
^%([-a-z0-9]+%.%)+%(gov%|edu%|com%|org%|int%|mil%|net%|%nato%|arpa%|[a-z][a-z]%)$
199
5
4
0
35
1
4
7
2

2
sysadmin
2
root
2
postmaster
2
system
2
operator
2
bin
199
5
0
0
199
1
2
^[-+a-z0-9_!.]+$
199
1
4
0
199
1
2
not-a-valid-domain.com
199
1
2
not-a-url:////
199
5
4
0
199
0
2
localhost
199
5
0
0
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
4
4
2
2
network utils
2
network
199
5
4
73
2
Utilities for dealing with network connections
2
---------------
2
Creating & tracking hosts:
2

2
:open(host, port [, connect-connection-to]) => {connection, object}
2
    open a network connection (using open_network_connection), optionally
2
    allows for it to be connected to another object.
2
    (see #0:do_login_command for details).
2

2
:close(connection)
2
     closes the connection & cleans up data
2

2
------------------
2
Parsing network things:
2

2
:invalid_email_address(email)
2
     return "" or string saying why 'email' is invalid.
2
     uses .valid_email_regexp
2

2
:invalid_hostname(host)
2
     return "" or string saying why 'host' doesn't look
2
     like a valid internet host name
2

2
:local_domain(host)
2
     returns the 'important' part of a host name, e.g.
2
     golden.parc.xerox.com => parc.xerox.com
2

2
-------------------
2
Sending mail
2

2
:sendmail(to, subject, @lines)
2
     send mail to the email address 'to' with indicated subject.
2
     header fields like 'from', 'date', etc. are filled in.
2
     lines can start with additional header lines.
2

2
:raw_sendmail(to, @lines)
2
     used by :sendmail. Send mail to given user at host, just
2
     as specified, no error checking.
2

2
================================================================
2
Parameters:
2

2
.active If 0, disabled sending of mail.
2

2
.site   Where does this MOO run?
2
        (Maybe MOOnet will use it later).
2

2
.port   The network port this MOO listens on.
2

2
.large_domains 
2
        A list of sites where more than 2 levels of host name are
2
        significant, e.g., if you want 'parc.xerox.com' to be
2
        different than 'cinops.xerox.com', put "xerox.com" as an
2
        element in .large_domains.
2

2
.postmaster
2
        Email address to which problems with MOO mail should
2
        go. This should be a real email address that someone reads.
2

2
.maildrop
2
        Hostname to connect to for dropping off mail. Usually can
2
        just be "localhost".
2

2
.reply_address
2
        If a MOO character sends email, where does a reply go?
2
        Inserted in 'From:' for mail from characters without
2
        registration addresses.        
2

2
.trusts
2
        List of (non-wizard) programmers who can call
2
        :open, :sendmail, :close
2

2
                
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
19915
0
913134427
200
1
#84
SMTP

16
199
-1
-1
-1
1
-1
157
18
trusts
199
173
-1
connect
199
173
-1
get_reply
199
173
-1
send_RCPT
199
173
-1
send_MAIL
199
173
-1
send_DATA
199
173
-1
send_HELO
199
173
-1
send_VRFY
199
173
-1
send_RSET
199
173
-1
_get_multiline_reply
199
173
-1
send_EXPN
199
173
-1
send_NOOP
199
173
-1
send_QUIT
199
173
-1
raw_sendmail
199
173
-1
send
199
173
-1
sendmail
199
173
-1
send_queued_mail
199
173
-1
add_queued_message
199
173
-1
8
trusts
error
error_code
debugging
time_format
queued_mail_task
queued_messages
queue_interval
22
4
0
199
1
2
SMTP error
199
1
2
SMTP wrong response
199
1
0
0
199
1
2
$d, $T $n $Y $H:$M:$S $Z
199
5
0
1596990599
199
1
4
0
199
0
0
600
199
1
5
199
4
4
1
2
SMTP
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
12144
0
913134427
200
1
#85
login watcher mark 2

16
35
-1
-1
-1
65
-1
-1
20
@login-o*ptions @Logino*ptions
35
93
-2
get_login_options login_option
35
173
-1
set_login_option
35
173
-1
add_user
35
173
-1
remove_user
35
173
-1
set_interesting
35
173
-1
feature_add
35
173
-1
feature_remove
35
173
-1
interesting
35
173
-1
options_propname
35
173
-1
@inter*esting
35
93
-2
@uninter*esting
35
93
-2
hello
35
173
-1
goodbye
35
173
-1
announce2
35
173
-1
@login
35
93
-2
feature_ok
35
173
-1
nominate_for_core
35
173
-1
init_for_core
35
173
-1
@wwho
35
93
-2
3
options
users
uninteresting
22
1
76
35
1
4
0
35
1
4
0
35
1
4
4
2
@login-o
2
@interesting
2
@uninteresting
2
@login
35
5
0
1
35
1
4
5
2
@login-o*ptions @Logino*ptions
2
@inter*esting
2
@uninter*esting
2
@login
2
@wwho
35
1
5
35
1
2
This feature can notify you when people connect and disconnect.   Try typing `@add-feature login' and `@login' to get started.
35
5
5
35
4
4
1
2
login watcher mark 2
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
16125
0
913134427
200
1
#86
String Tag

0
190
-1
-1
-1
94
-1
-1
5
to_linemode
190
173
-1
to_tkmoo
190
173
-1
to_lemoo
35
173
-1
to_html
190
173
-1
nominate_for_core
199
173
-1
0
15
5
35
1
5
190
4
4
1
2
String Tag
190
5
2
The string tag is used when the jtext-form conversion code finds a raw string.  There is no need to use it directly.
190
5
5
35
1
5
190
5
5
190
5
5
190
5
0
0
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2485
0
913134427
200
1
#87
Boot-Log

0
199
42
-1
-1
41
-1
-1
1
is_usable_by is_readable_by
199
173
-1
0
26
5
199
5
5
35
1
4
0
35
0
0
1
199
5
5
199
5
5
199
5
5
199
5
4
1
1
2
35
1
4
0
35
1
5
199
5
5
35
1
4
0
35
0
5
199
4
4
3
2
Boot-Log
2
bootlog
2
boot log
35
1
2
A log of bootings.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
997
0
913134427
200
1
#88
List Of Strings Tag

0
190
-1
-1
-1
94
-1
86
3
to_linemode
190
173
-1
to_lemoo
190
173
-1
to_tkmoo
190
173
-1
0
15
5
35
1
5
190
4
4
1
2
List Of Strings Tag
190
5
5
190
5
5
35
1
5
190
5
5
190
5
5
190
5
0
0
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1841
0
913134427
200
1
#89
http

16
35
-1
-1
-1
1
-1
222
20
get_now
199
173
-1
parse
35
173
-1
show_text
35
165
-1
type
35
173
-1
summary
35
173
-1
get get_cache
35
173
-1
clear_cache
35
173
-1
unparse
35
173
-1
interpret_error
35
173
-1
trusted
35
173
-1
init_for_core
35
173
-1
parse_url
35
173
-1
escape_for_url
35
173
-1
unescape_for_url
35
173
-1
nominate_for_core
35
173
-1
init_for_module
35
173
-1
module_finish_unpack
199
173
-1
post_now
199
173
-1
post_binary
199
173
-1
encode_fields
35
173
-1
7
cache_requests
cache_times
cache_values
limit
valid_host_regexp
cache_timeout
error
21
4
0
35
1
4
0
35
1
4
0
35
1
0
20000
35
5
2
^%([0-9]+%.[0-9]+%.[0-9]+%.[0-9]+%|[-a-z0-9.]+%.%(gov%|edu%|com%|org%|int%|mil%|net%|%nato%|arpa%|[a-z][a-z]%)%)$
35
5
0
900
35
1
2
HTTP error
35
1
5
35
4
4
1
2
http
35
5
2
An interface to HTTP 0.9 services. HTTP 1.0 maybe coming soon.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
18498
0
913134427
200
1
#90
Generic Quota Utils Parent

16
200
-1
-1
-1
156
119
197
8
nominate_for_core
200
173
-1
initialize_quota
200
173
-1
adjust_quota_for_programmer
200
173
-1
own_object
200
165
-1
disown_object
200
165
-1
verify_owned_objects
199
165
-1
all_characters
200
173
-1
can_peek
200
173
-1
7
fascist
byte
object
default_player_quota
default_programmer_quota
report_recipients
byte_based
22
0
0
200
5
1
188
199
1
1
119
199
1
0
0
200
5
0
0
200
5
4
1
1
2
200
1
0
0
200
5
5
200
5
5
200
4
4
1
2
Generic Quota Utils Parent
200
5
4
5
2
This is the parental unit of the quota utility objects.
2

2
Properties defined here:
2
.byte_based tells @audit etc. how to display.
2
.fascist defines whether we are doing fascist quota scam enforcement.
200
5
5
35
1
5
200
5
5
200
5
5
200
5
5
200
5
5
200
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5248
0
913134427
200
1
#91
generic cord type

144
199
-1
-1
-1
179
-1
-1
2
set_cord_name set_messages_out set_messages_in set_cord_class
199
165
-1
set_parent_package
199
173
-1
3
parent_package
cord_name
cord_class
19
1
-1
199
1
2

199
1
1
220
199
1
5
199
1
5
199
1
5
199
4
4
3
2
generic cord type
2
cord type
2
type
199
5
2
A cord type.  Part of a protocol; has a name within that protocol; defines certain messages (sent and received).
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2331
0
913134427
200
1
#92
Who Options

16
35
-1
-1
-1
60
-1
76
33
show_columns
35
173
-1
parse_columns
35
173
-1
show_who_listing
199
165
-1
parse_order
35
173
-1
show_order
35
173
-1
show_ascending
35
173
-1
_get_name
35
173
-1
_get_idle
35
165
-1
_get_connected
35
173
-1
_get_doing
35
173
-1
_get_location
35
165
-1
_get_last_disconnect
35
173
-1
_build_title
35
165
-1
_sort_by_idle
35
173
-1
_sort_by_connected _sort_by_last_disconnect _sort_by_name _sort_by_location
35
173
-1
_get_columns
35
165
-1
left_just
35
173
-1
check_columns
35
173
-1
_format_column
35
173
-1
_format_idle_short _format_connected_short
35
173
-1
right_just
35
173
-1
_columns
35
165
-1
_get_line
35
165
-1
_format_name _format_doing
35
173
-1
_format_idle_long _format_connected_long
35
173
-1
_get_column
35
173
-1
sin
35
173
-1
_format_last_disconnect
35
173
-1
_format_location
35
173
-1
_get_party
35
165
-1
_sort_by_party
35
173
-1
_party_size
35
165
-1
_party_rank
35
165
-1
9
valid_columns
type_columns
type_order
valid_sorts
show_ascending
column_titles
csep
column_widths
show_oldwho
30
4
6
2
name
2
idle
2
connected
2
location
2
doing
2
last_disconnect
35
1
4
1
4
1
0
2
35
5
4
1
0
2
35
5
4
5
2
name
2
idle
2
connected
2
location
2
party
35
5
4
2
2
Sort in descending order.
2
Sort in ascending order.
35
5
4
6
2
Name
2
Idle Time
2
Connected
2
Location
2
Doing/Idle
2
Last Disconnected
35
1
2
  
35
5
4
6
0
12
2
long
2
long
0
28
0
30
0
28
35
1
4
2
2
Use the BRAND NEW @who command.
2
Use the old @who technology.
35
5
4
3
2
order
2
columns
2
ascending
35
1
2
!order!columns!ascending!
35
1
4
0
35
1
5
35
5
2
who
35
5
2
@who-options
35
5
2
the @who command
35
5
5
35
4
4
1
2
Who Options
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
20575
0
913134427
200
1
#93
Browser Object Address

16
190
-1
-1
-1
115
-1
153
5
send_to
199
165
-1
make_link
190
173
-1
make_core_link
190
165
-1
make_named_link
190
165
-1
nominate_for_core
199
173
-1
0
15
2
browser_object
190
5
5
190
4
4
1
2
Browser Object Address
190
5
5
190
5
5
35
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
7558
0
913134427
200
1
#94
Tag Root

144
190
-1
-1
-1
1
96
115
7
to_tkmoo
190
173
-1
to_lemoo
35
173
-1
to_linemode
35
173
-1
recycle
190
165
-1
nominate_for_core
199
173
-1
to_mcp
35
173
-1
help_text
35
173
-1
1
tag_type
15
2
root
35
1
5
190
4
4
1
2
Tag Root
190
5
2
The tag root.  See `help $jtext'.  This is the parent of all the tags and should be the parent of any new tags.
190
5
5
35
1
5
190
5
5
190
5
5
190
5
0
1
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2765
0
913134427
200
1
#95
Jtext Dispatcher

16
190
-1
-1
-1
1
-1
94
7
convert_to
190
173
-1
tag_class
190
173
-1
make_vgroup
35
173
-1
english_list
199
173
-1
nominate_for_core
199
173
-1
help_text
35
173
-1
make_obj_link
35
173
-1
18
hgroup
link
root
vgroup
header
title
string
list_of_strings
paragraph
dlist
plaintext
list
index
itemize
itemize_unnumbered
itemize_numbered
isindex
hr
32
1
96
190
5
1
130
190
5
1
94
190
5
1
102
190
5
1
108
190
5
1
100
190
5
1
86
190
5
1
88
190
5
1
107
190
5
1
103
190
5
1
135
190
5
1
136
190
5
1
127
190
1
1
148
190
5
1
120
190
5
1
132
190
5
1
125
190
5
1
144
190
1
5
190
4
4
1
2
Jtext Dispatcher
190
5
5
190
5
5
35
1
5
190
5
5
190
5
5
190
5
0
1
190
5
0
0
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5910
0
913134427
200
1
#96
Hgroup Tag

16
190
-1
-1
-1
94
-1
110
4
to_linemode
190
173
-1
to_tkmoo
190
173
-1
to_lemoo
190
173
-1
to_html
190
173
-1
0
15
5
35
1
5
190
4
4
1
2
Hgroup Tag
190
5
4
5
2
Hgroups combine a set of hboxes `horizontally' to form a single hbox.  The form of the list is:
2

2
  {$jtext.hgroup, <hbox>, <hbox>, ...}
2

2
It is a (currently) unchecked error to have a vbox inside an hgroup.  I'd stay away from hgroups with zero contained hboxes for the moment.
190
5
5
35
1
5
190
5
5
190
5
5
190
5
0
0
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3062
0
913134427
200
1
#97
Country Database

16
199
-1
-1
-1
79
-1
-1
7
get_info
199
173
-1
set_info
199
173
-1
delete_info
199
173
-1
get_guest
199
173
-1
get_aliases
199
13
-1
nominate_for_core
199
173
-1
init_for_core
199
173
-1
8
 .
 .f
 .d
 .b
 .n
 .c
cartographers
 .u
29
4
4
2

2
fdbncu
4
6
2
.xerox.com
2
.io.com
2
.au
2
.tr
2
.mankato.msus.edu
2
.se
4
6
4
2
4
2
2
adjective
2
ubiquitous
4
2
2
description-tag
2
This one seems to have a COMPUTER WRISTWATCH, however.
4
2
4
2
2
adjective
2
illuminated
4
2
2
description-tag
2
This one glows faintly in the dark, however.
4
1
4
2
2
adjective
2
Australian
4
1
4
2
2
adjective
2
Turkish
4
1
4
2
2
adjective
2
Maverick
4
1
4
2
2
adjective
2
Swedish
35
1
4
4
2

2

4
2
2
.fi
2
.fr
4
2
4
1
4
2
2
adjective
2
Finnish
4
1
4
2
2
adjective
2
French
35
1
4
4
2

2

4
2
2
.dk
2
.de
4
2
4
1
4
2
2
adjective
2
Danish
4
1
4
2
2
adjective
2
German
35
1
4
4
2

2

4
2
2
.be
2
.br
4
2
4
1
4
2
2
adjective
2
Belgian
4
1
4
2
2
adjective
2
Brazilian
35
1
4
4
2

2

4
2
2
.no
2
.nl
4
2
4
1
4
2
2
adjective
2
Norwegian
4
1
4
2
2
adjective
2
Dutch
35
1
4
4
2

2

4
3
2
.com
2
.ca
2
.ch
4
3
4
2
4
2
2
adjective
2
corporate
4
2
2
description-tag
2
This one is wearing a nondescript grey suit.
4
1
4
2
2
adjective
2
Canadian
4
1
4
2
2
adjective
2
Swiss
35
1
4
0
199
1
4
4
2

2

4
2
2
.us
2
.uk
4
2
4
1
4
2
2
adjective
2
American
4
1
4
2
2
adjective
2
British
35
1
4
2
1
199
2

199
5
5
35
1
0
0
35
1
5
199
5
5
199
5
5
35
1
4
4
2

2
.
4
0
4
0
35
1
5
199
4
4
1
2
Country Database
199
5
4
36
2
A database containing information about different domains.  Each domain is listed by suffix (e.g., ".fr" for the country of France, or "theory.cs.mankato.msus.edu" for a particular machine).  Associated with each domain is an alist of {property, value} pairs, for example {"adjective", "French"}.
2

2
Useful verbs:
2

2
  :set_info(str <domain>, str <info>, <value>)
2
    Set the given type of information about the country marked by <domain>.
2
    Add the domain if it doesn't exist already.
2

2
  :get_info(str <domain>, str <info>)
2
    Return the requested information about the country marked by <domain>.
2
    Return E_PROPNF if domain doesn't exist, E_INVARG if domain has no info.
2

2
  :delete_info(str <domain>, str <info>)
2
    Remove the given information from the db.
2
    Return E_PROPNF if no such domain.
2
    Return E_INVARG if no such information.
2
    Else return a true value.
2

2
  :get_guest()
2
    Check the connection site of player and match it up with a guest.
2
    Return a guest object if the domain is known.
2
    Return $ambiguous_match if blacklisted.
2
    Return another invalid object if all guests are in use.
2
    A guest is created from the info in $country_db if it needs to be.
2

2
Useful "properties" of domains:
2

2
  adjective
2
    Used for guest names.
2

2
  accent
2
    Used in the default default description.  Defaults to <adjective>.
2

2
  description-tag
2
    Used in the default decription.  Defaults to
2
      This one has a slight <accent> accent, however.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
9421
0
913134427
200
1
#98
mcp-cord

16
35
-1
-1
-1
224
-1
221
9
next_id
35
173
-1
cord_send
199
173
-1
cord_closed
199
173
-1
handle_
35
173
-1
handle_closed
35
173
-1
find_type
35
173
-1
send_open
35
173
-1
finalize_connection
199
173
-1
type_name
35
173
-1
2
next_id
cords
20
0
1
35
1
4
0
35
1
5
35
1
4
0
35
1
4
3
4
2
2
open
4
2
2
_id
2
_type
4
2
2

4
2
2
_id
2
_message
4
2
2
closed
4
1
2
_id
199
1
4
3
4
2
2
open
4
2
2
_id
2
_type
4
2
2

4
2
2
_id
2
_message
4
2
2
closed
4
1
2
_id
199
1
5
35
4
4
1
2
mcp-cord
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5667
0
913134427
200
1
#99
Browser Link Tag

16
190
-1
-1
-1
110
-1
152
4
to_linemode
190
173
-1
to_tkmoo
190
169
-1
to_lemoo
35
173
-1
to_html
190
173
-1
0
18
5
190
5
5
190
5
5
35
1
5
35
1
5
190
4
4
1
2
Browser Link Tag
190
5
5
190
5
5
35
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2454
0
913134427
200
1
#100
Title Tag

16
190
-1
-1
-1
94
-1
107
4
to_linemode
190
173
-1
to_tkmoo
190
173
-1
to_lemoo
35
173
-1
to_html
190
173
-1
0
15
5
35
1
5
190
4
4
1
2
Title Tag
190
5
4
3
2
A title is a string that serves to identify a document. Jtext clients don't present titles to users. Usage:
2

2
     {$jtext.title, <string>}
190
5
5
35
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2547
0
913134427
200
1
#101
generic daemon listener with flood control

0
199
-1
-1
-1
143
-1
-1
5
redlisted
199
165
-1
allow_connection
199
173
-1
redlist_add
199
173
-1
redlist_remove
199
173
-1
check_flood
199
173
-1
2
redlist
floodcounters
18
4
2
4
0
4
0
199
1
4
0
199
1
5
199
5
5
199
5
5
199
4
4
1
2
generic daemon listener with flood control
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
4040
0
913134427
200
1
#102
Vgroup Tag

16
190
-1
-1
-1
94
-1
108
6
to_linemode
190
173
-1
to_tkmoo
190
173
-1
to_lemoo
35
173
-1
to_html
190
173
-1
nominate_for_core
199
173
-1
to_mcp
35
173
-1
0
15
5
35
1
5
190
4
4
1
2
Vgroup Tag
190
5
4
3
2
A vgroup is a vertical series of vboxes.
2

2
{$jtext.vgroup, <vbox>, <vbox>, ...}
190
5
5
35
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2927
0
913134427
200
1
#103
Definition List Tag

16
190
190
-1
-1
94
-1
135
4
to_linemode
190
173
-1
to_lemoo
35
173
-1
to_html
190
173
-1
to_tkmoo
190
173
-1
0
15
5
35
1
5
190
4
4
2
2
Definition List Tag
2
dlist tag
190
5
4
3
2
Definition list tag:
2

2
{$jtext.dlist, {word, desc}, {word, desc}, {word, desc}, ...}
190
5
5
35
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3011
0
913134427
200
1
#104
generic testing player

16
199
3
-1
-1
52
-1
-1
4
disfunc
199
173
-1
@programmer!
199
25
-1
description
199
173
-1
@quit
199
9
-1
1
public_identity
107
1
-1
199
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
199
0
5
199
5
5
199
0
5
199
1
0
0
199
5
5
35
1
5
35
1
5
35
0
5
35
1
5
199
5
5
200
1
5
35
1
5
199
4
5
199
0
5
199
0
5
199
5
5
199
4
5
199
4
5
199
4
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
4
5
199
4
5
199
1
5
200
0
5
199
5
5
35
1
5
199
0
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
199
1
5
199
1
5
35
1
5
35
1
5
199
5
5
199
5
5
199
1
5
199
1
5
199
5
5
199
0
5
199
5
5
35
1
5
35
1
5
199
5
4
0
35
1
5
199
0
5
199
0
5
199
0
5
199
1
5
199
1
5
35
1
5
199
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
1
5
199
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
199
1
5
199
1
5
199
0
5
199
1
5
200
0
5
199
5
5
199
0
5
35
1
5
199
1
5
199
1
5
199
5
5
199
1
5
199
4
4
2
2
generic testing player
2
gtp
199
1
2
A battered test character.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5392
0
913134427
200
1
#105
HTML

16
35
-1
-1
-1
1
-1
95
1
from_literal_text
199
173
-1
2
tags_implying_paragraph_mark
tags_handled
16
4
19
2
H1
2
H2
2
H3
2
H4
2
H5
2
H6
2
UL
2
OL
2
LI
2
DL
2
/OL
2
/UL
2
/DL
2
DT
2
PRE
2
/PRE
2
XMP
2
/XMP
2
/BODY
35
5
4
9
2
P
2
H1
2
H2
2
H3
2
H4
2
H5
2
H6
2
TITLE
2
A
35
5
5
35
4
4
1
2
HTML
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1259
0
913134427
200
1
#106
feature object help database

16
199
-1
-1
-1
20
-1
-1
2
find_topics
199
165
-1
index
35
173
-1
0
15
4
0
35
1
5
199
4
4
2
2
feature object help database
2
feature help database
199
5
4
3
2
The Feature Verb Help Database searches your list of features ($player.features) and returns help documentation on matching verbs. It does not do partial matches.
2

2
This object is not itself a Feature Object. To use it, add this object to your .help property until someone adds it to a Featureful player class' .help property.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
0
0
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2385
0
913134427
200
1
#107
Paragraph Tag

16
190
-1
-1
-1
94
-1
103
4
to_linemode
190
173
-1
to_tkmoo
190
173
-1
to_lemoo
35
173
-1
to_html
190
173
-1
0
15
5
35
1
5
190
4
4
1
2
Paragraph Tag
190
5
4
5
2
Paragraphs combine a list of hboxes into a paragraph, producing a vbox.
2

2
  {$jtext.paragraph, <hbox>, <hbox>, ...}
2

2
The hboxes that make up this list are built into a paragraph by joining them horizontally.
190
5
5
35
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3079
0
913134427
200
1
#108
Header Tag

16
190
-1
-1
-1
94
-1
100
4
to_linemode
190
173
-1
to_tkmoo
190
173
-1
to_lemoo
35
173
-1
to_html
190
173
-1
0
15
5
35
1
5
190
4
4
1
2
Header Tag
190
5
4
5
2
A header is set above some text, and indicates something about that text--typically what the text is about.  Headers are often arranged hierarchically; for example, a document might have sections, which have subsections, and so on.
2

2
{$jtext.header, <size>, <hbox>}
2

2
<hbox> is set as a header.  Size is an integer from 1 to 6.  Size 1 is a top-level header; size 6 is as small a header as we get.
190
5
5
35
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3085
0
913134427
200
1
#109
all

16
35
-1
-1
-1
1
-1
5
0
0
14
5
35
4
4
1
2
all
35
5
2
Yep.  All.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
0
1
35
5
5
35
1
5
35
1
1
109
35
1
1
-1
199
1
4
2
0
347
0
913134427
200
1
#110
Link Tag

144
190
-1
-1
-1
94
99
102
6
to_linemode
190
173
-1
to_tkmoo
190
173
-1
to_lemoo
190
173
-1
to_html
190
173
-1
nominate_for_core
199
173
-1
to_mcp
35
173
-1
3
preceding
root
link_type
18
1
152
190
5
1
110
190
5
2
root
35
1
2
link
35
1
5
190
4
4
1
2
Link Tag
190
5
4
7
2
A link indicates an opportunity for the user to navigate to a different document.  The form is:
2

2
{$jtext.link.root, <jaddress>, <presented text>}
2

2
The jaddress, of course, identifies the document pointed to by this link.  The presented text is shown to the user (typically underlined, highlighted, or marked with a number), who can then somehow select that text to follow the link.
2

2
The generic link tag should have several children, each of which may indicate a different way of presenting the presented text.  The different link tag objects would each provide presentation methods for the linemode client and any other in-MOO output drivers.  For example, we might have a `menu link' which would present a link as "3. A big flat rock" (as opposed to the generic presentation "A big flat rock[3]").  Specialized clients would have other ways of presenting different kinds of links.  Unfortunately, none of this is implemented.
190
5
5
35
1
5
190
5
5
190
5
5
190
5
0
0
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
4863
0
913134427
200
1
#111
security

0
199
42
-1
112
113
-1
33
0
0
29
1
159
199
5
4
1
1
147
199
5
5
199
5
5
199
5
5
35
1
4
0
35
0
0
1
199
5
5
199
5
5
199
5
5
199
5
4
1
1
2
35
1
4
0
35
1
5
199
5
5
35
1
4
0
35
0
5
199
4
4
1
2
security
35
1
2
This mailing list is readable only by members of the DB Group, and should be used to send reports of possible security holes.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
768
0
913134427
200
1
#112
Community-Group

0
199
42
-1
87
113
-1
150
0
0
29
1
176
199
5
4
1
1
147
199
5
5
199
5
5
199
5
5
35
1
4
0
35
0
0
1
199
5
5
199
5
5
199
5
5
199
5
4
1
1
2
35
1
4
0
35
1
5
199
5
5
35
1
4
0
35
0
5
199
4
4
2
2
Community-Group
2
CG
35
1
2
The Community Group is responsible for handling social problems and promoting a sense of community.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
767
0
913134427
200
1
#113
Generic Administrators Mailing List

16
199
-1
-1
-1
41
111
87
2
is_readable_by is_usable_by
199
173
-1
is_writable_by
199
173
-1
3
owner_group
reader_groups
moderator_groups
29
1
-1
199
5
4
0
199
5
4
0
199
5
5
199
5
5
35
1
5
35
0
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
4
0
35
1
5
199
5
5
35
1
5
35
0
5
199
4
4
1
2
Generic Administrators Mailing List
35
1
2
The Generic Administrators Mailing List.  Members of .owner_group (assumed to be a descendent of $admin_group) have write-privilege; members of any of the .reader_groups have read-privileges; members of any of the .moderator_groups have send-privileges.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1761
0
913134427
200
1
#114
Registrar

16
199
-1
-1
-1
5
-1
162
15
trust
199
173
-1
init_for_core
199
173
-1
add_relation
199
173
-1
add_log
199
173
-1
clean_relations
199
173
-1
primary_char*acter
199
173
-1
other_chars all_chars all_shared_chars other_shared_chars all_second_chars other_second_chars
199
173
-1
all_relations
199
173
-1
delete_relation
199
173
-1
same_as
199
173
-1
is_primary_char is_second_char
199
173
-1
add_shared
199
173
-1
is_registered_primary_char
199
173
-1
delete_shared
199
173
-1
delete_player
199
9
-1
8
primary
secondary
trusted
no_log_objects
shared
shared_owners
log
help_text
30
4
0
199
0
4
0
199
0
4
1
1
200
199
1
4
0
199
1
4
0
199
0
4
0
199
0
4
1
4
2
4
5
1
2
2
mcd_2
1
2
1
51
1
2
4
5
1
2
2
mcd_2
1
2
1
51
1
2
199
0
4
58
2
The Registrar
2
=============
2

2
The Registrar is the object which handles the second-character registration for the MOO.  Its information is stored in a set of parallel lists.
2

2
Rules for Registration
2
----------------------
2
Any player may be registered to any other player, with the following exceptions:
2
+ Links may not be recursive.  In other words, a player may not be a second character -of- a second character.
2
+ Guest characters may not be registered.
2

2

2
Properties on the Registrar
2
---------------------------
2

2
.primary / .secondary
2
  -- These parallel lists store the character registration information.
2

2
.trusted
2
  -- Working in connection with the :trust verb, this property stores people whose perms should allow access to the registry information.
2

2
.log
2
  -- This property, maintained by :add_log, stores a complete log of all calls to this object.  Its information is in the form: { {callers(), time(), @args} [...] }
2

2
.help_msg
2
  -- This help text.
2

2

2
Verbs on the Registrar
2
======================
2

2
:add_relation ( master, slave )
2
  -- Adds a second-character link.
2

2
:delete_relation ( [former] master, [former] slave )
2
  -- Delete a second-character link.
2

2
:clean_relations ()
2
  -- Clean out fluff (e.g. toaded/reaped players) from the links.
2

2
:primary_char ( dude )
2
  -- Returns the primary character to whom 'dude' is registered, if any.
2

2
:other_chars ( dude )
2
  -- Returns a list (possibly empty) of all other characters related to 'dude'.
2

2
:all_chars ( dude )
2
  -- Returns a list (never empty) of all characters related to 'dude'.
2

2
:same_as ( dude1, dude2 )
2
  -- Returns a true/false for whether they're linked as the same.
2

2
:is_primary_char ( dude )
2
:is_secondary_char ( dude )
2
  -- Returns a true/false for whether 'dude' is a primary/secondary character.
2

2
:all_relations()
2
  -- Returns a list of all relations.
199
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
4
4
1
2
Registrar
199
5
2
You see the registration device.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
14565
0
913134427
200
1
#115
Address Root

144
190
-1
-1
-1
1
124
117
14
to_lemoo to_mcp
35
173
-1
to_tkmoo
190
173
-1
to_url
199
173
-1
keyword_and_value_to_url
35
173
-1
keyword_or_value_to_url
199
173
-1
escape_to_url
35
173
-1
from_url
35
173
-1
default_*
35
173
-1
optional_attributes
199
173
-1
recycle
190
165
-1
nominate_for_core
199
173
-1
from_user
35
173
-1
init_for_core
199
165
-1
help_text
35
173
-1
1
address_type
15
2
root
190
5
5
190
4
4
1
2
Address Root
190
5
4
8
2
This is an abstract superclass for jaddress type objects.  It provides the following verbs and properties for conversion from and to URLs and to jtext output formats:
2

2
  :to_*         -- default ways to print jaddresses in a given output format
2
  :to_url       -- default way to print as a URL (called in HTML conversion)
2
  :from_url     -- conversion from the keyword!value part of a jhcore URL
2
  .address_type -- the name of the address type.
2

2
.. plus various auxiliary verbs.  Each new jaddress type object should get a distinct .address_type, which should correspond to the name of the property on $jaddress that points to it.  (This property is used in constructing URLs and other print formats.)
190
5
5
35
1
5
190
5
5
190
5
5
190
5
0
1
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
7837
0
913134427
200
1
#116
generic event dispatching object

144
199
-1
-1
-1
1
155
156
8
add_dependent
199
173
-1
remove_dependent
199
173
-1
@rm-dep*endent @rmdep*endent
199
153
5
@add-dep*endent @adddep*endent
199
153
1
initialize
199
173
-1
broadcast_event_*
35
165
-1
dependents dependents_event_*
35
173
-1
init_for_core
199
173
-1
1
dependents
15
4
0
199
1
5
199
4
4
3
2
generic event dispatching object
2
event_dispatcher
2
gedo
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5197
0
913134427
200
1
#117
Jaddress Dispatcher

16
190
-1
-1
-1
1
-1
130
14
send_me
190
165
-1
extract
35
173
-1
convert_to
190
173
-1
keyword_or_value_to_url
199
173
-1
keyword_and_value_to_url
35
173
-1
escape_for_url
199
173
-1
from_url
199
173
-1
unescape_for_url
35
173
-1
from_pseudocommand
199
173
-1
nominate_for_core
199
173
-1
init_for_core
199
165
-1
match_address_type
199
165
-1
address_type_match_failed
199
165
-1
help_text
35
173
-1
9
root
help
browser_object
note
browser_verb
browser_inherited_properties
resolved_help
home_page_jaddress
pseudocommand_candidates
23
1
115
190
5
1
139
190
5
1
93
190
1
1
124
190
5
1
153
190
1
1
154
190
1
1
185
190
5
3
13
190
5
4
1
1
139
199
1
5
190
4
4
1
2
Jaddress Dispatcher
190
5
5
190
5
5
35
1
5
190
5
5
190
5
5
190
5
0
1
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
8644
0
913134427
200
1
#118
Miscellaneous Options

16
199
-1
-1
-1
60
-1
122
6
actual
35
173
-1
show
35
173
-1
show_tstamp
35
173
-1
show_fstamp
35
173
-1
parse_fstamp
35
173
-1
parse_tstamp
35
173
-1
9
show_public_email
show_expert_user
show_brief
show_verbose
show_page_dwim
show_third_person
type_tstamp
type_fstamp
show_public_connect
30
4
2
2
Your email address is known only to you and administrators
2
Your email address is publically available
199
5
4
2
2
You will receive normal prompting and usage information
2
You will receive shortened prompts and usage information
199
5
4
2
2
You will receive long descriptions upon entering a room.
2
You will receive short descriptions upon entering a room.
199
5
4
2
2
You will receive long descriptions upon entering a room.
2
You will receive short descriptions upon entering a room.
199
5
4
2
2
The ' command won't do automagic whispering.
2
The ' command will whisper if the recipient is in the current room.
199
5
4
2
2
You will see `you' when other people see your name.
2
You will see your name instead of `you'.
199
5
4
1
0
0
199
5
4
1
0
2
199
5
4
2
2
Your total connection time is private.
2
Your total connection time is public.
199
5
4
8
2
public_email
2
expert_user
2
brief
2
page_dwim
2
third_person
2
tstamp
2
fstamp
2
public_connect
35
1
2
!public_email!expert_user!brief!verbose!page_dwim!third_person!tstamp!fstamp!public_connect!
35
1
4
1
2
verbose
35
1
5
199
5
2
misc
199
5
2
@misc-options
199
5
2
assorted preferences
199
5
5
199
4
4
1
2
Miscellaneous Options
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5090
0
913134427
200
1
#119
Object Quota Utilities

144
200
-1
-1
-1
90
-1
188
14
bi_create
199
165
-1
creation_permitted
200
173
-1
verb_addition_permitted property_addition_permitted
200
173
-1
display_quota
200
173
-1
get_quota quota_remaining
200
173
-1
charge_quota
200
173
-1
reimburse_quota
200
173
-1
set_quota
200
173
-1
preliminary_reimburse_quota
200
173
-1
can_peek
200
173
-1
can_touch
200
173
-1
get_quota_total
200
173
-1
set_quota_total
200
173
-1
get_quota_usage
200
173
-1
0
22
5
200
5
5
199
1
5
199
1
0
0
200
5
0
20
200
5
5
200
1
0
0
200
5
5
200
5
5
200
4
4
1
2
Object Quota Utilities
200
5
4
1
2
This is the default package that interfaces to the $player/$prog quota manipulation verbs.
200
5
5
35
1
5
200
5
5
200
5
5
200
5
0
0
200
5
5
200
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
7192
0
913134427
200
1
#120
Unnumbered Itemization Tag

16
190
-1
-1
-1
148
132
-1
4
to_linemode
190
173
-1
to_html
190
173
-1
to_lemoo
190
173
-1
to_tkmoo
190
173
-1
0
15
5
35
1
5
190
4
4
1
2
Unnumbered Itemization Tag
190
5
4
3
2
Usage:  {$jtext.itemize_unnumbered, hbox, hbox, hbox ...}
2

2
Unnumbered itemizations are subtypes of itemizations (which see).  The child hboxes are presented to the user without sequencing.
190
5
5
35
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3014
0
913134427
200
1
#121
generic personal help database

144
35
-1
-1
-1
30
-1
134
6
initialize
199
173
-1
recycle
199
173
-1
moveto
35
173
-1
find_topics
199
173
-1
topic_text
35
173
-1
nominate_for_core
199
165
-1
1
possessor
16
1
-1
199
1
4
0
35
1
5
35
4
4
2
2
generic personal help database
2
gphdb
35
5
4
5
2
The personal help database is intended mainly for people who are helping to maintain the JHM help system.  The idea is that people will write their own help topics, using the documentation feature (which see, #2455) and a personal database, and these topics can then be edited and copied to one of the public help databases.
2

2
If you've created a personal help database, then it's already crawled into your help db list.  This means that when you use the `help' command, it'll be searched for help topics; it also means that, in using the help text editing commands, you'll often be able to refer to the topic name alone.
2

2
The one tricky part is that, when you do have to specify a help database, you'll need to refer to it as "~<my-name>", as if you were referring to yourself.  Trust me.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3588
0
913134427
200
1
#122
Client Options

16
199
-1
-1
-1
60
-1
129
0
6
show_edit
show_gopher
show_ftp
show_urls
show_help_urls
show_mcp_edit
27
4
2
2
Your client can't do local editing.
2
Your client can do local editing.
199
5
4
2
2
Your client can't handle gopher requests locally.
2
Your client can handle gopher requests locally.
199
5
4
2
2
Your client can't fetch FTP documents.
2
Your client can fetch FTP documents.
199
5
4
2
2
Your client won't fetch and display URLs locally.
2
Your client will fetch URLs and display them locally.
199
5
4
2
2
The help system should display help as jtext.
2
The help system should display help using local URLs if possible.
199
5
4
2
2
Your client can't do MCP standard editing.
2
Your client can do MCP standard editing.
199
5
4
6
2
edit
2
ftp
2
gopher
2
help_urls
2
urls
2
mcp_edit
35
1
2
!edit!ftp!gopher!help_urls!urls!mcp_edit!
35
1
4
0
35
1
5
199
5
2
client
199
5
2
@client-options
199
5
2
client capabilities
199
5
5
199
4
4
1
2
Client Options
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1561
0
913134427
200
1
#123
Parsing Utilities

16
35
-1
-1
-1
156
-1
90
3
parse_object_args
35
165
-1
init_for_core
199
165
-1
nominate_for_core
35
173
-1
1
quota_hungry
16
0
1
35
5
5
35
5
5
35
4
4
5
2
Parsing Utilities
2
parse utilities
2
parsing utils
2
parse utils
2
$parse_utils
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
4181
0
913134427
200
1
#124
Note Address

16
190
-1
-1
-1
115
-1
139
2
send_to
190
173
-1
from_object
199
173
-1
0
15
2
note
190
5
5
190
4
4
1
2
Note Address
190
5
4
4
2
Note jaddresses have the following tags:
2

2
  objnum:       the object number of a note
2

190
5
5
35
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1584
0
913134427
200
1
#125
ISINDEX Tag

16
35
-1
-1
-1
94
-1
144
1
to_html
35
173
-1
0
15
2
isindex
35
1
5
35
4
4
1
2
ISINDEX Tag
35
5
4
6
2
The ISINDEX tag is a kludge that works like an html isindex tag. It works, but it's not pretty. Usage:
2

2
     {$jtext.isindex, ""}
2

2
An ISINDEX tag can appear anywhere in a document.
2

35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
965
0
913134427
200
1
#126
generic source

144
35
-1
-1
-1
210
219
203
5
read_headers
35
173
-1
read_body
35
173
-1
parse_headers
35
173
-1
read_line
35
173
-1
set_data
35
173
-1
3
data
error
line
19
4
0
35
1
2
reading error
35
1
0
1
35
1
5
35
1
5
35
1
5
35
4
4
2
2
generic source
2
source
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3569
0
913134427
200
1
#127
Index Tag

16
199
-1
-1
-1
94
-1
148
3
to_linemode
35
173
-1
to_html
35
173
-1
to_lemoo
190
173
-1
0
15
5
35
1
5
199
4
4
2
2
Index Tag
2
Index
199
5
4
5
2
A big list of undescribed items with short names. A good representation of this would be a columnized listing. Unfortunately, HTML has nothing we can use for this (as far as I know).
2

2
Example:
2

2
{$jtext.index, "foo", "bar", "baz"}
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2653
0
913134427
200
1
#128
generic door

144
35
-1
-1
-1
7
193
-1
30
open
35
45
-1
close shut
35
45
-1
opens_msg closes_msg locks_msg unlocks_msg knocks_msg opened_msg closed_msg locked_msg unlocked_msg no_key_msg
35
173
-1
description
35
173
-1
move
35
173
-1
trusted
35
173
-1
lock
35
45
-1
unlock
35
45
-1
is_locked
35
173
-1
has_key
35
173
-1
find_other_side
35
173
-1
setup_connection
35
173
-1
other_side obj_other_side
35
173
-1
knock
35
141
4
set_user_key set_close_delay
35
173
-1
knock_msg open_msg close_msg lock_msg unlock_msg self_closes_msg
35
173
-1
set_locked set_other_side set_opaque set_automatic set_closing_task
35
173
-1
self_close
35
173
-1
set_open
35
173
-1
close_delay
35
173
-1
event_directed_speech
35
173
-1
is_unlocked_for
35
173
-1
help_text
35
173
-1
through_msg
35
173
-1
sub_open
199
173
-1
init_for_core
199
165
-1
pull
35
157
7
walking_cost
35
173
-1
obj_user_key
35
173
-1
modname_open
35
173
-1
26
open_msg
close_msg
open
opened_msg
closed_msg
automatic
locked_msg
other_side
opens_msg
closes_msg
user_key
unlocked_msg
lock_msg
unlock_msg
locked
locks_msg
unlocks_msg
knock_msg
knocks_msg
opaque
help
no_key_msg
close_delay
closing_task
self_closes_msg
pull_msg
55
4
8
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
opens
2
 
4
3
2
dname
2
thing
0
0
2
.
35
5
4
8
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
closes
2
 
4
3
2
dname
2
thing
0
0
2
.
35
5
0
0
35
1
4
6
1
69
2
do
4
3
2
dname
2
thing
0
1
2
 
4
3
2
verb
2
thing
2
is
2
 open.
35
5
4
6
1
69
2
do
4
3
2
dname
2
thing
0
1
2
 
4
3
2
verb
2
thing
2
is
2
 closed.
35
5
0
1
35
1
4
6
1
69
2
do
4
3
2
dname
2
thing
0
1
2
 
4
3
2
verb
2
thing
2
is
2
 locked.
35
5
1
-1
35
1
4
6
1
69
2
do
4
3
2
dname
2
thing
0
1
2
 
4
3
2
verb
2
thing
2
opens
2
 from the other side.
35
5
4
6
1
69
2
do
4
3
2
dname
2
thing
0
1
2
 
4
3
2
verb
2
thing
2
closes
2
 from the other side.
35
5
1
-1
35
1
4
6
1
69
2
do
4
3
2
dname
2
thing
0
1
2
 
4
3
2
verb
2
thing
2
is
2
 unlocked.
35
5
4
8
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
locks
2
 
4
3
2
dname
2
thing
0
0
2
.
35
5
4
8
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
unlocks
2
 
4
3
2
dname
2
thing
0
0
2
.
35
5
0
0
35
1
4
5
1
69
2
do
2
You hear the click of 
4
3
2
dname
2
thing
0
0
2
 being locked from the other side.
35
5
4
5
1
69
2
do
2
You hear the click of 
4
3
2
dname
2
thing
0
0
2
 being unlocked from the other side.
35
5
4
8
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
knocks
2
 on 
4
3
2
dname
2
thing
0
0
2
.
35
5
4
5
1
69
2
do
2
Someone knocks on 
4
3
2
dname
2
thing
0
0
2
.
35
5
0
1
35
1
4
29
2
A Generic Door which can be opened and closed.
2
To make a door, simply @chparent your exit to this object.  Actually, you'll want to @chparent the returning exit to this object too.  Then try opening or closing or something and it will automatically set the other_side property.
2

2
Properties:
2
@set <door>.user_key to <object || #-1>
2
 - Use this to set the object which is used as the key for locking and unlocking the door.  This is very different from the @lock commands.  A $door key should be some object which you wish to use as the key.  You might @create a $thing called "key" or just use something you already have.  You have to be holding it to use it.  (This means if your key is in your backpack and you are holding your backpack, it won't work.)  If you set the door itself as its key, then anyone may lock it (from that side), i.e. it has a regular bolt attached to it.  The default is #-1 (no key).
2
@set <door>.automatic to <0 || 1>
2
 - Set this to 0 for non-automatic or 1 for automatic.  An Automatic door is one which automatically tries to be opened when someone tries to walk through it.  In effect, it's like typing `open door' (and even `unlock door' if necessary), before exiting.  The default is 1 (automatic).
2
@set <door>.close_delay to <seconds || -1>
2
 - Set this to the number of seconds before the door closes by itself after being opened, or -1 if it doesn't close by itself.  The default is -1 (don't close).
2

2
Messages:
2
@open/@close/@lock/@unlock/@knock
2
 - Displayed to the room and player when the action is performed on the door.
2
@opens/@closes/@locks/@unlocks/@knocks
2
 - Displayed to the other side of the door when the action is performed.
2
@self_closes
2
 - Displayed to the room when the door closes by itself.
2
@opened/@closed/@locked/@unlocked
2
 - Says the door is currently open/closed/etc.  Displayed in the door's description and when someone tries to open/close/etc it when it is already open/closed/etc.
2
@no_key
2
 - Displayed to the player when the player tries to unlock/open/go through a locked door without having the key.
2
@integrate_room
2
 - The message integrated into the room's description. Use [Open] or [open] to be substituted with "Open"/"Closed" depending on the state of the door.
2
@whos_there
2
 - A message saying who's visible through the door when open. (In the message, [Dest] or [dest] is the destination and [Who] or [who] is a list of people on the other side.)
2
@no_one
2
 - Used in @whos_there when no one is visible.
2
Most of the default messages on the door will work well enough.  Type `@messages $door' for examples of good messages.
35
5
4
5
1
69
2
do
2
You don't have the key for 
4
3
2
dname
2
thing
0
0
2
.
35
5
0
-1
35
1
0
0
35
1
4
6
1
69
2
do
4
3
2
dname
2
thing
0
1
2
 
4
3
2
verb
2
thing
2
closes
2
.
35
5
4
14
1
69
2
do
4
3
2
name
2
player
4
1
2
ic
2
 
4
3
2
verb
2
player
2
reaches
2
 through 
4
3
2
name
2
thing
4
1
2
d
2
 and 
4
3
2
verb
2
player
2
pulls
2
 
4
3
2
name
2
dobj
4
1
2
d
2
 into 
4
3
2
name
4
2
2
location
2
player
4
1
2
d
2
.
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
199
1
5
35
4
4
1
2
generic door
35
5
4
0
35
5
5
35
1
4
9
1
69
2
do
4
3
2
name
2
thing
4
1
2
dc
2
 to 
4
3
2
name
0
2
4
1
2
d
2
 
4
3
2
verb
2
thing
2
is
2
 
2
{special thing open {0%}}.
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
25842
0
913134427
200
1
#129
building options

16
199
-1
-1
-1
60
-1
146
2
show_room show_exit
35
173
-1
parse_room parse_exit
199
173
-1
5
type_room
type_exit
show_furnishing
show_create_set_co
show_bi_create
26
4
1
0
1
199
5
4
1
0
1
199
5
4
2
2
@created objects are carried by you, unlocked
2
@created objects are locked in the room you're in
199
1
4
2
2
@create doesn't change your current object.
2
@create sets your current object to the one created.
199
5
4
2
2
@create/@recycle re-use object numbers.
2
@create/@recycle call create()/recycle() directly.
199
5
4
5
2
room
2
exit
2
furnishing
2
create_set_co
2
bi_create
35
1
2
!dig_room!dig_exit!furnishing!create_set_co!bi_create!
35
1
4
0
35
1
5
199
5
2
building
199
5
2
@building-options
199
5
2
options for building
199
5
5
199
4
4
2
2
building options
2
bopt
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2618
0
913134427
200
1
#130
link dispatcher tag

16
190
-1
-1
-1
1
-1
174
2
nominate_for_core
199
173
-1
help_text
35
173
-1
3
preceding
root
browser
17
1
152
190
5
1
110
190
5
1
99
190
5
5
190
4
4
1
2
link dispatcher tag
190
5
4
2
2

2
This is the root of an unimplemented but potentially cool set of link types.  For now, see `help $jtext.link.root'.
190
5
5
35
1
5
190
5
5
190
5
5
190
5
0
1
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1285
0
913134427
200
1
#131
Programming Group

0
35
-1
-1
-1
175
-1
-1
1
add_member
199
173
-1
0
21
5
35
1
5
35
1
4
0
35
1
1
150
35
1
5
35
1
5
35
1
5
35
1
5
35
4
4
4
2
Programming Group
2
programmers
2
prog group
2
prog_group
35
5
2
People who program verbs.  See `help programmer-policy' for what it takes to become a programmer.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1068
0
913134427
200
1
#132
Numbered Itemization Tag

16
190
-1
-1
-1
120
-1
-1
1
to_html
190
173
-1
0
15
5
35
1
5
190
4
4
1
2
Numbered Itemization Tag
190
5
4
5
2
Usage:  {$jtext.itemize_numbered, hbox, hbox, hbox ...}
2

2
Numbered itemizations are subtypes of itemizations (which see).  The child hboxes are presented to the user with some sort of sequencing implying a temporal order.
2

2
This tag is currently only supported fully by the HTML jtext driver.  Other drivers will render it as an unnumbered list.
190
5
5
35
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1312
0
913134427
200
1
#133
generic container

144
35
8
155
-1
155
8
-1
11
p*ut in*sert d*rop
199
157
3
re*move ta*ke g*et
199
157
5
put_msg remove_msg
35
173
-1
description
35
173
-1
put_fail_msg remove_fail_msg empty_msg
35
173
-1
can_put_in
199
173
-1
can_reach
35
173
-1
moveto
199
173
-1
acceptable
35
173
-1
sub_in
35
173
-1
can_get_from
35
173
-1
7
put_fail_msg
remove_fail_msg
remove_msg
put_msg
empty_msg
integrate_sep_msg
integration_enabled
23
4
7
1
69
2
do
2
You can't put 
4
3
2
name
2
dobj
4
1
2
d
2
 
4
4
2
special
2
thing
2
in
4
0
2
 that.
35
1
4
3
1
69
2
do
2
You can't remove that.
35
1
4
10
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
removes
2
 
4
3
2
iname
2
dobj
0
0
2
 from 
4
3
2
dname
2
iobj
0
0
2
.
35
1
4
12
1
69
2
do
4
3
2
name
2
player
4
1
2
dc
2
 
4
3
2
verb
2
player
2
puts
2
 
4
3
2
name
2
dobj
4
1
2
d
2
 
4
4
2
special
2
thing
2
in
4
0
2
 
4
3
2
name
2
iobj
4
1
2
d
2
.
35
1
4
6
1
69
2
do
4
3
2
pronoun
2
thing
2
Psc
2
 
4
3
2
verb
2
thing
2
is
2
 empty.
35
1
4
3
1
69
2
do
2
  
35
1
0
1
35
1
5
35
5
5
199
1
5
35
4
4
2
2
generic container
2
container
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
7903
0
913134427
200
1
#134
last resort help database

16
199
-1
-1
-1
30
-1
166
3
find_topics
35
173
-1
get_topic
35
165
-1
init_for_core
35
173
-1
1
help_recipient
16
1
150
35
1
4
0
35
1
5
199
4
4
2
2
last resort help database
2
$last_help
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
0
0
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2516
0
913134427
200
1
#135
Plaintext Tag

16
190
-1
-1
-1
94
-1
136
5
to_linemode
190
173
-1
to_tkmoo
190
173
-1
to_lemoo
190
173
-1
to_html
190
173
-1
to_mcp
190
173
-1
0
15
5
35
1
5
190
4
4
1
2
Plaintext Tag
190
5
4
3
2
The plaintext text works just like <PRE></PRE> in HTML.   It normally means a monospaced font in smart clients and from the web gateway.
2

2
Usage:  {$jtext.plaintext, vbox, vbox, ...}
190
5
5
35
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3105
0
913134427
200
1
#136
List Tag

16
35
-1
-1
-1
94
-1
127
4
to_linemode
35
173
-1
to_lemoo
35
173
-1
to_html
35
173
-1
to_tkmoo
35
173
-1
0
15
5
35
1
5
35
4
4
1
2
List Tag
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2370
0
913134427
200
1
#137
permissions utilities

16
35
-1
-1
-1
156
-1
19
5
controls
199
173
-1
apply
35
173
-1
controls_verb
199
173
-1
controls_prop controls_property
199
173
-1
invoked_by_function
35
173
-1
0
15
5
35
5
5
35
4
5
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3569
0
913134427
200
1
#138
module registry

16
199
-1
-1
-1
222
-1
-1
0
5
module
mcp
simpleedit
listeners
core_features
20
1
211
199
1
1
214
199
1
1
212
199
1
1
187
199
1
1
208
199
1
4
1
2
extended_features
199
1
5
199
4
4
2
2
module registry
2
registry
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
593
0
913134427
200
1
#139
Help Address

16
190
-1
-1
-1
115
-1
93
6
send_to
190
165
-1
make_link
190
173
-1
make_formatted_link
190
173
-1
from_db_and_topic
199
173
-1
from_topic
199
173
-1
from_pseudocommand
199
173
-1
1
default_player_class
16
1
51
190
5
2
help
190
5
5
190
4
4
1
2
Help Address
190
5
4
7
2
Help addresses have the following tags:
2

2
   subject:    topic to search for
2
        db:    specify the db (this apparently passes to .resolved_help
2

2
This also defines a from_psuedocommand that defines `help foo' to be a link to help topic foo.
2

190
5
5
35
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
4567
0
913134427
200
1
#140
generate note sink

144
35
-1
-1
-1
205
-1
-1
2
parse_commandline
199
173
-1
finish
199
173
-1
0
17
5
35
0
5
35
1
5
35
1
5
35
4
4
2
2
generate note sink
2
note
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1609
0
913134427
200
1
#141
name-subbing dummy object

16
35
-1
-1
-1
1
-1
209
1
modname_spoof
35
165
-1
0
14
5
35
4
4
2
2
name-subbing dummy object
2
nsdo
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
0
1
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
669
0
913134427
200
1
#142
generic event handler

144
35
-1
-1
-1
1
-1
149
6
handle_event_*
35
165
-1
set_handler_ok
35
165
-1
initialize
35
173
-1
help_verbs
199
173
-1
help_text
35
165
-1
match_type_object
35
173
-1
1
handler_ok
15
0
0
35
1
5
35
4
4
2
2
generic event handler
2
handler
35
5
4
3
2
This is an object that can handle events, with :handle_event_* verbs.  It's meant to be put in objects' .handlers lists.
2

2
Each handle verb takes three arguments: the calling object; the list of non-zero return values from previous handlers; and the list of arguments.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
1
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
4237
0
913134427
200
1
#143
generic daemon listener

16
199
-1
-1
-1
204
101
-1
10
do_login_command
199
173
-1
make_handler
199
173
-1
allow_connection
199
173
-1
start stop
199
41
-1
service_stop
199
173
-1
open_connections
199
173
-1
abort_connection
199
173
-1
user_disconnected user_client_disconnected
199
173
-1
timeout_connection
199
173
-1
user_connected user_created
199
173
-1
1
handler
16
1
-1
199
5
5
199
5
5
199
4
4
1
2
generic daemon listener
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5737
0
913134427
200
1
#144
Horizontal Rule Tag

16
190
-1
-1
-1
94
-1
88
2
to_linemode to_tkmoo to_lemoo
190
173
-1
to_html
190
173
-1
0
15
2
hr
35
1
5
190
4
4
1
2
Horizontal Rule Tag
190
5
2
A horizontal rule is simply a  line across the page.
190
5
5
35
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1321
0
913134427
200
1
#145
Steering-Committee

0
199
42
-1
111
113
-1
112
0
0
29
1
147
199
5
5
199
5
5
199
5
5
199
5
5
35
1
4
0
35
0
0
1
199
5
5
199
5
5
199
5
5
199
5
4
1
1
2
35
1
4
0
35
1
5
199
5
5
35
1
4
0
35
0
5
199
4
4
3
2
Steering-Committee
2
SC
2
Steering
35
1
2
The Steering Committee is responsible for defining and maintaining this MOO's goals and objectives.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
774
0
913134427
200
1
#146
Eval Options

16
35
-1
-1
-1
60
-1
92
8
show_ticks
35
173
-1
show_env
35
173
-1
show_subs
35
173
-1
parse_ticks
35
173
-1
parse_env
35
173
-1
parse_subs
35
173
-1
get
35
173
-1
actual
35
173
-1
7
show_no_debug
show_time
type_ticks
type_env
type_subs
show_print_names
show_nodebug
28
4
2
2
`;' runs `eval' (i.e. with debug flag).
2
`;' runs `eval-d' (i.e. no debug flag).
35
5
4
2
2
Do not show ticks and seconds after each eval command.
2
Show ticks and seconds after each eval command.
35
5
4
1
0
0
35
5
4
1
0
2
35
5
4
2
4
1
4
1
0
2
4
1
0
2
35
5
4
2
2
Don't show names of objects in lists.
2
Show names of objects in lists.
35
5
4
2
2
`;' runs `eval' (i.e. with debug flag).
2
`;' runs `eval-d' (i.e. no debug flag).
35
1
4
6
2
no_debug
2
time
2
env
2
ticks
2
subs
2
print_names
35
1
2
!nodebug!no_debug!time!env!ticks!subs!print_names!
35
1
4
1
2
nodebug
35
1
5
35
5
2
eval
35
5
2
@eval-options
35
5
2
the eval command
35
5
5
35
4
4
1
2
Eval Options
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5604
0
913134427
200
1
#147
Steering Committee

16
199
-1
-1
-1
174
-1
175
0
0
21
5
35
1
5
35
1
5
35
1
1
145
35
1
5
35
1
5
35
1
5
35
1
5
199
4
4
2
2
Steering Committee
2
SC
199
5
4
60
2
This document details the role of the JaysHouseMOO Steering Committee (or SC).  The SC defines the goals and objectives of JHM, and makes decisions related to the accomplishment of those goals.
2

2
ORGANIZATION
2
------------
2

2
The Steering Committee members may be reached via the *sc mailing list.  All of the requests below should be mailed to this list.
2

2
The Steering Committee meets at least once a month to discuss any issues from the mailing list that could not (or should not) be resolved in email, and to deal with any other issues related to JHM that should be discussed interactively.
2

2
The leader of the Steering Committee plans the agenda for the SC meetings, resolves disputes between members of the SC, and makes any `hard' decisions that can not be reached by consensus.
2

2
DECISIONS
2
---------
2

2
The following decisions must be made by the Steering Committee.  Others should be added to this list as necessary.
2

2
Approve new programmers
2
=======================
2

2
A request to make someone a programmer should be be submitted to the SC by a metaproject leader.  (The prospective programmer must be planning to work on a project within that leader's metaproject.)  The leader must get verbal or email approval of all active SC members.  Once this has happened, the leader may make the person a programmer, and the fact shall be recorded in the programmer-log.
2

2
Approve new metaprojects and metaproject leaders
2
================================================
2

2
A metaproject (or area of interest) should be submitted to the SC, which should discuss it at an SC meeting.  It must be approved by a full consensus of SC members, either at the meeting, or via mail if any member of the SC was not present at the meeting.  If the person who has submitted the metaproject is not already a metaproject leader, e must also be approved to become a metaproject leader.
2

2
Discontinue metaprojects
2
========================
2

2
A metaproject may be discontinued simply by the choice of the metaproject leader, or by full consensus of the SC.
2

2
Approve new members of the Steering Committee
2
=============================================
2

2
A request should be submitted to the SC.  All active members of the SC and the potential new member must approve the request.  MOOmail is sufficient.
2

2
Approve new members of the DB group
2
===================================
2

2
A request should be submitted to the SC.  All active members of the SC and the potential new member must approve the request.  MOOmail is sufficient.
2

2
Approve new members of the Unix group
2
=====================================
2

2
A request should be submitted to the SC.  All active members of the SC must approve the request.  The Unix systems administrator of the computer that JHM is running on must also approve the addition.
2

2
Approve new members of the Community group
2
==========================================
2

2
A request should be submitted to the SC.  All active members of the SC and the CG, along with the potential new member, must approve the request.
2

2
Dismissals
2
==========
2

2
All dismissals from the above posts are approved by consensus of the members of the SC, minus the person in question.  Requests to resign should be handled in the best and most polite manner.
2

2
THEORY OF OPERATION
2
-------------------
2

2
Many details in the above decisions have not been explicitly stated, and it is assumed they will simply be handled reasonably.  If this turns out not to be the case, or if some method of decision-making is ambiguous, this document should be modified by the leader of the steering committee.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
4532
0
913134427
200
1
#148
Itemize Tag

16
190
-1
-1
-1
94
120
125
0
0
15
5
35
1
5
190
4
4
1
2
Itemize Tag
190
5
4
1
2
This is an abstract superclass of itemized lists, in the LaTeX sense.  Itemized lists consist of one or more hboxes that are displayed together with some mark in front of them.
190
5
5
35
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
561
0
913134427
200
1
#149
everything

16
35
-1
-1
-1
1
-1
109
0
0
14
5
35
4
4
1
2
everything
35
5
2
Yep, you betcha, everything.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
0
1
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
379
0
913134427
200
1
#150
Bug-reports

0
35
42
-1
151
113
-1
-1
1
init_for_core
199
173
-1
0
29
1
131
35
5
5
35
5
5
35
5
5
35
5
5
35
1
4
0
35
0
0
0
35
5
5
35
5
5
35
5
0
1
35
5
4
1
1
2
35
1
4
0
35
1
5
35
5
5
35
1
4
0
35
0
5
35
4
4
2
2
Bug-reports
2
bugs
35
1
2
Bug reports here.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1029
0
913134427
200
1
#151
News

16
199
42
-1
145
41
-1
113
9
news_display_seq_full
199
173
-1
to_text
199
173
-1
@set-newspaper
199
105
1
is_writable_by
199
173
-1
check
199
173
-1
receive_message
199
173
-1
touch
199
173
-1
init_for_core
199
173
-1
description
199
173
-1
1
current_newspaper
27
4
1
2
1-$
199
5
5
199
5
5
35
1
4
0
35
0
4
3
1
147
1
175
1
176
199
5
5
199
5
5
199
5
0
1
199
5
4
1
1
2
35
1
4
0
35
1
5
199
5
5
35
1
4
0
35
0
5
199
4
4
1
2
News
35
1
2
It's the current issue of the News, dated %d.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5611
0
913134427
200
1
#152
generic preceding link tag

16
190
-1
-1
-1
110
-1
-1
1
to_linemode
190
173
-1
0
18
5
190
5
5
190
5
2
preceding
35
1
5
35
1
5
190
4
4
4
2
generic preceding link tag
2
preceding link tag
2
gplt
2
plt
190
5
5
190
5
5
35
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1002
0
913134427
200
1
#153
Browser Verb Address

16
199
-1
-1
-1
115
-1
154
4
make_link
35
173
-1
send_to
199
165
-1
make_named_link
35
173
-1
nominate_for_core
199
173
-1
0
15
2
browser_verb
199
5
5
199
4
4
4
2
Browser Verb Address
2
Verb Address
2
Address
2
BVA
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5148
0
913134427
200
1
#154
Browser Inherited Properties Address

0
199
-1
-1
-1
115
-1
185
3
make_link
199
165
-1
send_to
199
165
-1
nominate_for_core
199
173
-1
0
15
2
browser_inherited_properties
199
5
5
199
4
4
5
2
Browser Inherited Properties Address
2
Inherited Properties Address
2
Properties Address
2
Address
2
BIPA
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3624
0
913134427
200
1
#155
generic containing object

144
35
133
156
-1
116
3
7
14
enterfunc
199
173
-1
exitfunc
199
173
-1
tell_contents
35
173
-1
empty_msg in_msg
35
165
-1
in_name
35
173
-1
dependents dependents_event_*
35
173
-1
look_self
35
173
-1
look_in
35
173
-1
visible_of
35
173
-1
contents_for_integration
35
173
-1
ok_to_integrate
35
173
-1
acceptable
35
173
-1
in_namec
35
173
-1
modname_in
35
173
-1
1
in_msg
16
4
3
1
69
2
do
2
in
35
5
5
199
1
5
35
4
4
3
2
generic containing object
2
gco
2
containing_object
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
7160
0
913134427
200
1
#156
generic utilities

144
35
155
-1
-1
1
24
142
0
1
indef_art
15
2
some
35
5
5
35
4
4
2
2
generic utilities
2
utilities
35
5
2
A set of related utilities.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
0
2
35
1
1
-1
199
1
4
2
0
449
0
913134427
200
1
#157
MCP 2.1

16
35
-1
-1
-1
1
-1
217
15
create_session
35
173
-1
destroy_session
35
173
-1
initialize_connection
199
173
-1
finalize_connection
199
173
-1
parse_version
199
173
-1
compare_version_range
199
173
-1
compare_version
199
173
-1
unparse_version
199
173
-1
session_for
199
173
-1
user_created user_connected user_reconnected
199
173
-1
user_disconnected user_client_disconnected
199
173
-1
do_out_of_band_command
199
173
-1
package_name match_package packges
35
173
-1
handles_package wait_for_package
35
173
-1
nominate_for_core
199
173
-1
7
parser
session
version
package
negotiate
registry
cord
21
1
217
35
5
1
216
35
1
4
2
0
2
0
1
35
5
1
224
199
1
1
213
199
1
1
218
35
5
1
98
35
1
5
35
4
4
1
2
MCP 2.1
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
8563
0
913134427
200
1
#158
Unix Group

0
35
-1
-1
-1
175
-1
131
0
0
21
5
35
1
5
35
1
4
0
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
4
4
3
2
Unix Group
2
unix-group
2
ug
35
5
2
The set of people who deal with unix-level administration of the MOO. They deal with startup, shutdowns, backups, memory usage, and management of auxiliary processes.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
667
0
913134427
200
1
#159
DB Group

0
35
-1
-1
-1
175
-1
158
0
0
21
5
35
1
5
35
1
4
0
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
4
4
4
2
DB Group
2
db-group
2
dbg
2
dg
35
5
4
3
2
People who have superuser (wheel) access to the MOO database for maintenance purposes. This is to be used for hacking on core objects, fixing bugs, or making global changes related to programming projects, and should not be used for non-technical purposes.
2

2
If you have questions, comments, or suggestions about the core database, send mail to the *DB-Issues mailing list.  If you think you've found a bug in the core database, send mail to the *Bug-Reports mailing list.  If you think you've found a security hole, send mail to the *Security mailing list, which is only readable to members of the DB group.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1146
0
913134427
200
1
#160
Community Group Commands

16
199
-1
-1
-1
161
-1
-1
8
@boot*!
199
93
11
@newt
199
85
-2
@unnewt @denewt @get-better
199
85
-2
@net-who @@who @@who-cg
199
85
-2
@new-password @newpassword @new-password-cg
199
93
12
@ungag
199
93
5
@make-player @make-player-cg
199
93
-2
newt_msg
35
173
-1
2
booted_msg
newt_msg
22
4
9
1
69
2
do
2
You've been booted by 
4
3
2
name
2
player
4
1
2
d
2
.  
4
3
2
pronoun
2
player
2
Psc
2
 gave the reason:  
4
3
2
string
2
iobjstr
0
0
2
.
199
5
4
10
1
69
2
do
4
3
2
name
2
player
4
1
2
dc
2
 
4
3
2
verb
2
player
2
@newts
2
 
4
3
2
name
2
dobj
4
1
2
d
2
 (
4
2
2
objnum
2
dobj
2
).
199
5
1
176
199
1
4
6
2
@boot
2
@newt
2
@denewt
2
@corral
2
@uncage
2
@@who
199
5
0
1
35
1
4
7
2
@boot*!
2
@newt
2
@unnewt @denewt @get-better
2
@net-who @@who @@who-cg
2
@new-password @newpassword @new-password-cg
2
@ungag
2
@make-player @make-player-cg
35
1
5
35
1
2
Some tools to help community group members
199
5
5
199
4
4
2
2
Community Group Commands
2
CG
199
5
2
Some tools to help community group members
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
12290
0
913134427
200
1
#161
generic secure group-command-feature

144
199
-1
-1
-1
65
160
182
5
set_group
199
173
-1
@set-group
199
105
1
trusts
199
173
-1
is_trusted
199
173
-1
feature_ok
199
173
-1
1
group
20
1
174
199
1
5
199
5
0
1
35
1
5
35
1
5
35
1
5
199
5
5
199
4
4
1
2
generic secure group-command-feature
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2944
0
913134427
200
1
#162
feature object registry

144
35
-1
-1
-1
5
-1
180
22
features
35
173
-1
feature_list
35
173
-1
add_feature
35
173
-1
remove_feature
35
173
-1
closed_msg empty_msg already_open_msg already_closed_msg index_msg contents_msg feature_msg no_contents_msg contents_header_msg what_page_msg
35
173
-1
open
35
45
-1
close
35
45
-1
open_msg close_msg turn_to_contents_msg turn_to_index_msg turn_to_empty_msg turn_to_feature_msg write_msg
35
173
-1
contents
35
173
-1
index
35
173
-1
feature
35
173
-1
look_self
35
173
-1
write enter
35
157
3
turn open
35
109
1
match_feature
35
165
-1
feature_match_failed
199
173
-1
empty closed
35
173
-1
feature_list_lines
35
173
-1
read
35
45
-1
read lookup
35
157
3
set_open
35
173
-1
init_for_core
35
173
-1
19
features
open
closed_msg
contents_msg
index_msg
empty_msg
feature_msg
open_msg
close_msg
already_open_msg
already_closed_msg
no_contents_msg
contents_header_msg
what_page_msg
turn_to_contents_msg
turn_to_index_msg
turn_to_empty_msg
turn_to_feature_msg
write_msg
41
4
5
1
67
1
68
1
160
1
182
1
85
35
1
2
closed
35
1
4
6
1
69
2
do
4
3
2
dname
2
thing
0
1
2
 
4
3
2
verb
2
thing
2
is
2
 closed.
35
5
4
6
1
69
2
do
4
3
2
dname
2
thing
0
1
2
 
4
3
2
verb
2
thing
2
is
2
 open to the contents page.
35
5
4
6
1
69
2
do
4
3
2
dname
2
thing
0
1
2
 
4
3
2
verb
2
thing
2
is
2
 open to the index.
35
5
4
6
1
69
2
do
4
3
2
dname
2
thing
0
1
2
 
4
3
2
verb
2
thing
2
is
2
 open to an empty page.
35
5
4
8
1
69
2
do
4
3
2
dname
2
thing
0
1
2
 
4
3
2
verb
2
thing
2
is
2
 open to a page entitled "
4
3
2
iname
2
dobj
0
1
2
".
35
5
4
8
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
opens
2
 
4
3
2
dname
2
thing
0
0
2
.
35
5
4
8
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
closes
2
 
4
3
2
dname
2
thing
0
0
2
.
35
5
4
6
1
69
2
do
4
3
2
dname
2
thing
0
1
2
 
4
3
2
verb
2
thing
2
is
2
 already open.
35
5
4
6
1
69
2
do
4
3
2
dname
2
thing
0
1
2
 
4
3
2
verb
2
thing
2
is
2
 already closed.
35
5
4
3
1
69
2
do
2
(There are no registered features.)
35
5
4
3
1
69
2
do
2
Table of Contents
35
5
4
5
1
69
2
do
2
What page do you want to turn 
4
3
2
dname
2
thing
0
0
2
 to?
35
5
4
8
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
turns
2
 
4
3
2
dname
2
thing
0
0
2
 to the table of contents page.
35
5
4
8
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
turns
2
 
4
3
2
dname
2
thing
0
0
2
 to the index.
35
5
4
8
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
turns
2
 
4
3
2
dname
2
thing
0
0
2
 to an empty page.
35
5
4
10
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
turns
2
 
4
3
2
dname
2
thing
0
0
2
 to a page entitled "
4
3
2
iname
2
dobj
0
3
2
".
35
5
4
14
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
picks
2
 up the pen attached to 
4
3
2
dname
2
thing
0
0
2
 and 
4
3
2
verb
2
player
2
enters
2
 
4
3
2
dname
2
dobj
0
0
2
 into 
4
3
2
pronoun
2
thing
2
po
2
.
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
4
4
3
2
feature object registry
2
registry
2
book
35
5
2
The feature object registry is a large thick leather-bound book with gilt-edged pages.  There's a gold-plated ball-point pen attached to it by a length of thin chain.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
1
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
18038
0
913134427
200
1
#163
jtext help database

16
35
-1
-1
-1
30
-1
20
1
init_for_core
35
165
-1
16
jtext-index
jaddresses
jtext-forms
targets
jaddress-types
coordinators
jtext-intro
jtext-timeline
tagged-lists
dispatchers
jtext-addressing
jtext-output
jtext-document
jtext-construction
jtext-input
jtext
31
4
2
2
*index*
2
Jtext Help Topics
35
5
4
7
2
A jaddress is a pointer to a jdocument. Formally a jaddress is a tagged list (`help tagged-lists') with one element. The list's class is the address type (see `help jaddress-types'). The second element is an alist of property/value pairs.
2

2
For example, here is a jaddress that refers to a document available via ftp:
2

2
    {$jaddress.ftp, {{"site","parcftp.xerox.com"}, {"dir","/pub/MOO/papers"}
2
                     {"filename", "MUDsGrowUp.txt"}}}
2

35
5
4
17
2
JTEXT FORMS
2
===========
2

2
Jtext forms are print representations of jtext.  For example, jtext can be rendered into HTML, tkmoo tagged text, or a `linemode' style easy to read for users without smart clients.
2

2
Each player object has a prefered form to present jtext in.  It can be set with `@set-jtext-form form-name'; use `@set-jtext-form' by itself to get a list of available forms.
2

2
FOR DEVELOPERS
2
--------------
2

2
In order for a string, <jform>, to name a jtext form, each jaddress and each tag (referred to collectively as jtext elements) MUST define a verb named to_<jform>.
2

2
For each element, <e>, the verb <e>:to_<jform> MUST accept the following two arguments in order: <element>, a jtext element, and <target>, a target (see `help targets').
2

2
Each such verb MUST arrange with the player object to display the jtext element.  
2

2
For convenience, $player defines :contribute_partial (which adds tostr(@args) to the pending line) and :contribute_newline (which sends the pending line, capitalized if it is given an argument).
35
5
4
15
2
A target is an object on which are defined the following VERBs that accept the following ARGS, return the specified VALues, and have the specified ACTions and properties that have the specified values:
2

2
        VERB:   receive_document
2
        ARGS:   {<jaddress>};
2
        VAL :   any
2
        ACT :   fetches the document specified by <jaddress>
2

2
        VERB:   receive_vbox
2
        ARGS:   {<vbox>[,<shep>]}
2
        VAL :   any
2
        ACT :   send <vbox> to target in the form specified
2
                by the target's jtext form.
2

2
************* I'll finish this later, R.T.              
2
 
35
5
4
49
2
JADDRESS TYPES
2
==============
2

2
FOR JTEXT WRITERS
2
-----------------
2

2
A list of all jaddress-types can be displayed with the command
2

2
        @jtext-jaddress-types
2

2
Look at a particular jaddress type for a list of the tags it requires/supports.
2

2
(This doesn't work yet.)
2

2
FOR IMPLEMENTORS OF JADDRESS TYPES
2
----------------------------------
2

2
A jaddress type is any object subject to the following restrictions:
2

2
A jaddress type MUST have the following VERBs, accept the specified ARGS, return the specified VALue, and SHOULD perform the specified ACTion:
2

2
        VERB:   send_to
2
        ARGS:   {<target>,<jaddress>}
2
        VAL :   any
2
        ACT :   deliver the document that <jaddress> points to to <target>
2
                possibly after negotiating the method of transfer
2

2
        VERB:   to_<jtext form>
2
        ARGS:   {<jaddress>,<target>}
2
        VAL :   any
2
        ACT :   call <target>:contribute_partial in order to contribute
2
                a string that a <jtext form> conforming client
2
                will recognize as a reference to a document.
2

2
where <target> is a target object (see `help targets'), <jaddress> is a jaddress (see `help jaddresses'), and <jtext form> is a jtext form (see `help jtext-forms').
2

2
A jaddress type MUST have the following PROPerties which MUST hold the specified MOO VALues and comply with the following restrictions:
2

2
        PROP:   jaddress_type
2
        VAL :   <string1>
2

2
        PROP:   subjaddress_types
2
        VAL :   <list>
2

2
where <string> is a string such that EITHER (#0.jaddress).(<string>) evaluates to the given jaddress type OR there exists a jaddress type, <a>, such that <a>.(<string>) evaluates to the given jaddress type and <list> is a possibly empty list of objects.
2

2
If <list> is non-empty, then each element of <list> must be an jaddress type for each element, <i>,  of <list> the jaddress type MUST have a property named <i>.jaddress_type and the value of the property MUST be <i>.
2

2
For example, suppose that foo has the verbs send_to and to_* defined appropriately. The object foo is an jaddress type if and only if either $jaddress.(foo.jaddress_type) == foo or there exists a jaddress type bar such that bar.(foo.jaddress_type) == foo and foo is in bar.subjaddress_types.
35
5
4
30
2
A coordinator is an object that manages viewing and editing of jdocuments.  In its simplest form, this means an object that works with a single user's client, receiving viewing and editing instructions, and telling the client the results of those operations. However, the strength of the coordinator (and the source of its name) is that it can take input from and send output to multiple targets, keeping them in sync with each other.
2

2
State
2
-----
2

2
current document -- What are we looking at?
2
viewer list      -- Who's looking?
2

2
I'm not sure, but I _think_ the current document needs to be cached on the coordinator, so that in the case of documents generated on the fly, we still have an agreed-upon base for things like annotation  positions and link identifiers (see below).  This does not fit in with the target:receive_document paradigm.
2

2
Eeeeg, in fact, we need a stack of current documents, right?
2

2
Exported Methods
2
----------------
2

2
Follow link (destination address, link identifier):
2

2
See if the identified link, from the current document, leads to the destination address.  If so, follow it (broadcast the fact that it's been followed to all viewers).  If not, inform the caller that it's out of sync (maybe).
2

2
A link identifier uniquely identifies a link _within a given document_.  Typical link identifiers are numbers (corresponding to the ordinary links in a document) and symbols such as `pop' (return to the previous document) or (other examples?).
2

2
The document address alone is enough to specify where the client (user) wants to go, of course; or, given a document, the link identifier is enough to indicate a destination.  The reason we send both is to provide a sort of synchronization: If the user wants to follow link 3 to Tennessee, and the current document's link 3 does go to Tennessee, then we can go ahead to Tennessee,  even if the user may actually have been following from a different document.  That's weird, maybe we should do something more definite.  I suppose we could have the client send (current address, 
2
link identifier).
2

2
 o O ( document versions )
2

2
Needed methods
2
--------------
2

2
Uncertain; we need to determine whether the document source or the coordinator supplies the document.
35
5
4
16
2
INTRODUCTION TO JTEXT
2
=====================
2

2
Jtext is a text abstraction for use in interactive sessions (like connecting to muds).  JHM people have been developing it since early in 1993.
2

2
The purpose of this document (or set of documents) is to provide an overview of jtext, past, present and future.  It's directed mostly towards people who are new to jtext and would just like to understand it, but it may also be useful if you're interested in programming jtext clients and generators (or just in writing jtext documents).
2

2
The jtext introduction is divided into the following sections:
2

2
jtext-intent -- What were we thinking?
2
jtext-timeline -- A rough idea of how we got where we are.
2
jtext-construction -- The components of a jtext document.
2
jtext-addressing -- References between documents.
2
jtext-output -- Different ways to view jtext.
2
jtext-input -- Different ways to enter jtext into the database.
2
jtext-future -- Plans, hopes, and speculations.
35
5
4
6
2
Unfortunately, the earliest development of jtext was pretty spontaneous, and most of the discussion of it took place only in conversation, so we don't have very good records of what was done when.  Actually, we don't even have good records of progress after we started using mailing lists.
2

2
Here are rough ideas of the chronology:
2

2
  *  On January 14 1993, the web entrance (see `help web-entrance') was up and providing an HTML object browser.
2
  *  On January 25, an alpha release of tkmoo (see `help tkmoo') was released for anonymous ftp at parcftp.xerox.com.  It included display of richtext.
35
5
4
16
2
TAGGED LISTS
2
============
2

2
MOO's only fundamental aggregate data type is the list.  Objects (which are pointed to by objnums) are aggregates, but they do not enjoy value semantics---that is, they must be explicitly created and destroyed, and may be mutated.  Often, data structures are best implemented with lists.
2

2
Lists of the form
2

2
  {object, element, element, element, element}
2

2
are tagged lists.  The first element names an object called the list's class or type.  The class object usually has methods for manipulating lists of that class.
2

2
Often, there is only one element in the tagged list, which is an association list:
2

2
  {object, {{"keyword", "value"}, {"keyword", "value"}}}
2

2
`help dispatchers' describes the canonical way to perform operations on tagged lists.
35
5
4
12
2
DISPATCHERS
2
===========
2

2
Packages of code such as jtext and jaddresses are canonically organized by an object called a `dispatcher'.
2

2
The dispatcher object is corified---it has a name like $package.
2

2
Notable objects associated with the package (such as generics and other utilities) are named in properties on the object.  For example, if the package has a class of tagged lists associated with it, it would be named by $package.root.
2

2
The dispatcher has top-level verbs related to the package on it; the naming of these verbs assumes the corified object name---for example, $jaddress:to_url is a verb that converts a jaddress to a URL.
2

2
One set of verbs on dispatchers deserves special mention.  If there's a class of tagged lists (see `help tagged-lists') associated with the project, some verbs on the dispatcher will call verbs on the class of lists passed to them.
35
5
4
6
2

2
Jtext documents are addressed using jaddresses.  A jaddress is an object as described in `help jaddress-types'.  A document's address using a jaddress is of the form {jaddress, assocs} where assocs tell jaddress which document it is.  Assocs is an alist of the form {{attribute, value}, ...} where the attributes vary per jaddress.
2

2
You can get a list of jaddresses from `help $jaddress' and read about what assocs each jaddress requires by reading its help topic or looking at it.
2

2

35
5
4
5
2

2
Jtext is never output to a user or client in its native form of MOO lists.  Instead, it is output to the client in a jtext-form (see `help jtext-forms').  A jtext form is a representation of jtext.
2

2
Verbs on jtext addresses and tags are responsbile for generating a jtext form.  Specifically, {tag or address}:to_<form>(target, assocs) is resposible for calling target:contribute_{newline, line, ..} to output the form the the client.
2

35
5
4
3
2

2
Jtext is stored and generated in the MOO as a tagged list (see `help tagged-lists').  A jtext document consists of a list of tagged lists.
2

35
5
4
18
2
Jtext documents contain many of the same elements as an HTML document.  At a minimum, a jtext document should contain a title and a body.  A short jtext document (in its native format) might look like this:
2

2
{{$jtext.title, "This is a title"}, {$jtext.header, 1, "Header"}, {$jtext.paragraph, "Foo bar baz.  See `", {$jtext.link.root, {$jaddress.help. {"subject", "foo"}, "help foo"}}, "' for some information about foo."}}
2

2
This document contains a title, a header, and a short paragraph including a link to a help document.  It is composed of several tagged lists (see `help tagged-lists').   Jtags have a tagged list of the form: 
2

2
{$jtext.tag, arg1, arg2, ...}
2

2
The arguments vary by tag, and are listed in the help about the tag or by looking at the tag.  The first tag in the document is a simple title tag, which has just the "title" argument.  The next one is a header, corrosponding to an html <H1>Header</H1>.   The link tag has a jaddress and anchor text as arguments.  In this case, the jaddress is a help address.
2

2
Tagged lists like the above jtext document are generated in one of three ways.
2

2
   o Help-format conversion to jtext
2
   o manually
2
   o dynamically
2

2
Help-format and manually are covered in `help jtext-input'.  Dynamic jtext generation is simply the construction of a jtext document in a verb.  An example of this would be an object browser or a jaddress that returned a @who style listing.
2

35
5
4
1
2
There are to ways to get a static jtext document into the database.  The first is to use the property editor and write the jtext by hand, constructing the tagged lists.   The second, more common, way is to write a help text or note in help format (see `help help-format').   Help-format is a flat text representation of jtext that can be converted into jtext.  Most of the help texts and jtext notes in the db are in help-format.
35
5
4
2
2
*forward*
2
jtext-intro
35
5
4
0
35
1
5
35
4
4
2
2
jtext help database
2
jtext documentation database
35
5
2
HELP ABOUT JTEXT, GEEZ, you people need EVERYTHING SPELLED OUT FOR YOU.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
16036
0
913134427
200
1
#164
builtin function definitions

16
199
-1
-1
-1
222
0
138
19
bf_add_verb
199
173
-1
bf_set_verb_info
199
173
-1
bf_set_verb_code
199
173
-1
bf_verb_code
199
173
-1
verb_metadata
199
173
-1
set_verb_metadata
199
173
-1
metadata_to_code
35
173
-1
metadata_from_code
35
173
-1
metadata_begin_index
35
173
-1
metadata_item
35
173
-1
set_metadata_item
35
173
-1
fixup_lastmodify
199
169
-1
bf_recycle bf_chparent
199
173
-1
_protect_recycle
199
173
-1
_protect_chparent
199
173
-1
set_verb_metadata_item
199
173
-1
verb_metadata_item
199
173
-1
delete_metadata_item
199
173
-1
delete_verb_metadata_item
199
173
-1
1
builtins
16
1
164
199
1
5
199
1
5
199
4
4
1
2
builtin function definitions
199
5
4
19
2
This object has the builtin function wrappers on it.  As such, it is a parent for #0.
2

2
bf_add_verb and bf_set_verb_info are wrappers that prevent writable verbs.
2

2
bf_set_verb_code, bf_verb_code, verb_metadata, and set_verb_metadata all implement a metadata-in-verb-code system. 
2

2
Metadata in this context is an list of {name, value} where name is a string containing no colons and value is an arbitrary MOO value.  The only metadata item automatically updated by the system is Last-Modify which is a list of {last modify time, last modified by}.
2

2
Details:
2

2
In this system, the metadata is stored as comments at the end of the verb code.  It looks like:
2

2
verb code;
2
verb code;
2
return;
2
// Metadata 2021026
2
// Item: "Value";
2

2
Where // signifies comment syntax.  return; is put there by set_verb_code if the verb does not end in return;
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
13244
0
913134427
200
1
#165
it

144
35
-1
-1
-1
64
167
171
0
0
27
1
165
35
5
2
Its
35
5
2
its
35
5
2
Its
35
5
2
its
35
5
2
Itself
35
5
2
itself
35
5
2
It
35
5
2
it
35
5
2
It
35
5
2
it
35
5
5
35
5
2
neuter
35
5
5
35
4
4
3
2
neuter
2
it
2
it
35
5
2
Third person singular, neuter.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
648
0
913134427
200
1
#166
Builder Help

16
199
-1
-1
-1
30
-1
181
0
17
@who
pronoun-example
pronoun-substitution
substitution
two-letter-qr
two-letter-spec
two-letter
@dig
@create
@recreate
@building-options
@buildingoptions
furniture
@measure
@newmessage
@unmessage
naming
32
4
4
2
*pass*
2
@who
2
*subst*
2
Note for builders:  @who refers to the @who_location message (see 'help messages') on each player's location in order to determine what should be printed in the location column.  Pronoun substitutions are done on this string in the usual manner (see 'help pronouns').  The default value is "%[player:unparse_message($room.who_location_msg)]" (i.e., the room name).
199
5
4
121
2
This text is intended to tell you something about how to use pronoun substitution in programming.  If you don't know anything about pronoun substitution, you should probably read `help pronouns' before you start.
2

2
Note that, at least for now, the text assumes two-letter pronoun substitution (see `help two-letter' for a full specification).
2

2
Step One
2
2-------
2

2
Okay.  For our example, let's say you've been creating a duck.  It's a pretty independent duck, and you don't want people to be able to pick it up, so you write a simple verb:
2

2
   >@verb duck:take this
2
   >@program duck:take
2
   >player:tell("You try to pick the duck up, but it swiftly dodges away.");
2
   >.
2

2
Here's a sample interaction with that code:
2

2
   >take duck
2
   You try to pick the duck up, but it swiftly dodges away.
2

2
About what you'd expect.
2

2
Step Two
2
2-------
2

2
So now you have a duck that won't let itself be picked up.  But if you change the duck's name, or if you make a generic ornery bird, then "the duck" won't be correct.
2

2
One way to deal with this would be to have the `take' verb call this:dname().  This verb will return "the duck" (or "the mallard" or "Bob the Wonder Pigeon").  This is an improvement, but it still lacks flexibility for the generic case.  You can provide this flexibility by using a pronoun-sub message.
2

2
   >@newmessage @dodge on duck
2
   @dodge on duck is now "".
2

2
This command just creates a message called `@dodge' on the duck.  Actually, it creates a property named `dodge_msg'.  This is all a message is:  A property with a special name (`something_msg') and a special format (more complicated).
2

2
   >@dodge duck is "You try to pick up %td, but %ts swiftly %t:(dodges) away."
2

2
This command sets the contents of the .dodge_msg property on the duck.  Notice that a lot of words related to the duck have been replaced with strange expressions that begin with `%t'.  This is NOT A COINCIDENCE.
2

2
The `%t' means that some piece of information about `this' should be used.  (`This' is usually the object on which the message is located.)  The particular piece of information is then specified:
2

2
   %td         -- the object's name, with definite article if needed
2
                  ("the duck", "Bob the Wonder Pigeon", "the goslings")
2
   %ts         -- the subjective pronoun
2
                  ("it",       "he",                    "they")
2
   %t:(dodges) -- a verb, conjugated for the object
2
                  ("dodges",   "dodges",                "dodge")
2

2
The last of these may be a little bit tricky.  `%t' means `substitute information about "this"'.  Following that, `:' means `use a conjugated verb'.  Then `(dodges)' specifies what verb to use.
2

2
Now, reprogram the verb to actually use this new message:
2

2
   >@program duck:take
2
   >player:tell($string_utils:pronoun_sub(this.dodge_msg));
2
   >.
2

2
$string_utils:pronoun_sub is a verb that takes a message in the standard format, looks at information in the state of the world (knowing `player' and the object that called it) or passed in explicitly, and returns a string with the appropriate substitutions made.  So a sample interaction might go:
2

2
   >take duck
2
   You try to pick the duck up, but it swiftly dodges away.
2

2
.. exactly the same!  But if you change the duck's name, make it plural, or change its gender, the message will change automatically.
2

2
Step Three
2
2---------
2

2
Now, suppose you're not satisfied with having your duck reject players' advances; you want everyone around to know what the player is doing, too.  (This is a perfectly reasonable goal, by the way; that way if the player says `That pesky duck!', everyone will know why.)
2

2

2
So, you make a new message:
2

2
   >@newmessage @ododge on duck
2
   @ododge on duck is now "".
2
   >@ododge duck is "%nD %n:(tries) to pick up %td, but %ts swiftly %t:(dodges) away."
2

2
In this message, `%n' works just the way `%t' does, but it refers to `player' (the one who typed the command), not to `this'.
2

2
   >@program duck:take
2
   >player:tell($string_utils:pronoun_sub(this.dodge_msg));
2
   >player:room_announce($string_utils:pronoun_sub(this.ododge_msg));
2
   >.
2

2
The :room_announce verb tells its arguments to everyone in the room with `player', except for `player'.  So:
2

2
Munchkin types:
2
   >take duck
2
Munchkin sees:
2
   You try to pick the duck up, but it swiftly dodges away.
2
Everyone else in the room sees:
2
   Munchkin tries to pick the duck up, but it swiftly dodges away.
2

2
Two messages and two lines take care of all of this.
2

2
Step Four
2
2--------
2

2
Actually, when the message is essentially the same for all parties involved (as in this example), you can simplify it still more.
2

2
   >@dodge duck is "%nD %n:(tries) to pick up %td, but %ts swiftly %t:(dodges) away."
2

2
Notice that this sets the `@dodge' to just the same thing `@ododge' was.
2

2
   >@program duck:take
2
   >$you:say_action(this.dodge_msg);
2
   >.
2

2
$you:say_action is a very handy verb that takes a message in the standard format, and substitutes appropriately for everyone involved (or nearby).  So, with this one message (you could `@rmprop duck.ododge_msg') and one line of code, you get this:
2

2
Munchkin types:
2
   >take duck
2
Munchkin sees:
2
   You try to pick the duck up, but it swiftly dodges away.
2
The duck sees:
2
   Munchkin tries to pick you up, but you swiftly dodge away.
2
Everyone else sees:
2
   Munchkin tries to pick the duck up, but it swiftly dodges away.
2

2
Munchkin gets treated as `you' in his own message; so does the duck.  Everyone else sees it in third person.
2

2
Step Zero
2
2--------
2

2
This is a basic use of pronoun substitution, but if you understand it well it will cover many cases.  To find out more, you might want to read `help two-letter' for more description of the capabilities of pronoun substitution; search through existing code that does things that interest you; and don't hesitate to ask other people for help.
199
5
4
61
2
This text describes the basic pronoun substitution model.  An introduction to the pronoun substitution concept can be found in `help pronouns', and a detailed example can be find in `help pronoun-example'.  Detailed information on the format of a two-letter substitution is in `help two-letter'.
2

2
The Evaluation Context
2
----------------------
2

2
Pronoun substition is done on a string by calling:
2

2
  $string_utils:pronoun_sub(string);
2
 
2

2
This will return a new string that is based on the format of string and the context in which pronoun_sub:() was called.
2

2
For example, if an object named `sun' calls this code:
2

2
  $string_utils:pronoun_sub("%td sets in the west.");
2
 
2

2
the returned string will be:
2

2
  "The sun sets in the west."
2
 
2

2
The information that :pronoun_sub() can use to try to decipher the string includes, (along with they way they can be referred to in the format string):
2

2
  - The player who caused the code to execute. (%n)
2
  - The object where the code is stored, usually referred to as `this'. (%t)
2
  - The location of the player. (%l)
2
  - The direct object of the command line that was typed by the player. (%d)
2
  - The indirect object of the command line that was typed by the player. (%i)
2

2
These values, which :pronoun_sub() figures out by itself, can be set explicitly by passing them in as arguments:
2

2
   $string_utils:pronoun_sub(string, player, this, location, dobj, iobj)
2
 
2

2
All of these (except the string) are optional, but order is important, so if you want to change the location, you also need to specifically mention the string, player, and this.
2

2
Each of these arguments can be:
2

2
  - an object (as you'd expect)
2
  - a list of objects
2
  - a list like you'd pass to $string_utils:english_list, i.e: :pronoun_sub("The %n", {{#0, #1, #2}, "nothing", " or "})
2

2
Multiple Similar Contexts
2
-------------------------
2

2
It often turns out that you want to say nearly the same thing to several different targets.  For example, if Munchkin picks up a duck, three messages will probably be printed:
2

2
  To Munchkin: You pick the duck up.
2
  To the duck: Munchkin picks you up.
2
  To everyone else in the room: Munchkin picks the duck up.
2

2
If you want to use :pronoun_sub() to create these messages, you'll either have to call it three times with three different format messages, or you'll have to call it three times with one format while tweaking the arguments to :pronoun_sub().
2

2
This happens so often that a special command has been created to do this for you.  The verb $you:say_action(string) takes the string and then substitutes in appropriately for everything involved or nearby.  In the above situation, you would simply call:
2

2
  $you:say_action("%nD %n:(picks) up %td.")
2

2
This will print the message according to how each person should see it, exactly like the three messages above.
2

2
A detailed example using $you:say_action() can be found in `help pronoun-example'.
199
5
4
2
2
*forward*
2
pronoun-substitution
199
5
4
35
2
This help text is a quick reference for pronoun_sub format strings.  For explanation, see `help two-letter'.
2

2
General format:   %<object specifier><information specifier>
2

2
Object Specifiers
2
  %t - this           %d - the dobj     %l - the player location
2
  %n - the player     %i - the iobj
2
  Append `l' to refer to that object's location, or `L' for the outermost.
2
  Append `c' for the contents.
2
  %x<object specifier>{foo arg1 arg2 ...} is an object specifier meaning
2
  "call :obj_foo on the specified object, with the given arguments."
2

2
Information Specifiers (capitalizable)
2
  n - the object's name         ("Susan",   "duck",    "king and queen")
2
  d - definite-article          ("Susan",   "the duck","the king and queen")
2
  i - indefinite-article        ("Susan",   "a duck",  "a king and queen")
2
  o  - the objective pronoun    ("her",     "him",     "them")
2
  p  - possessive adjective     ("her",     "his",     "their")
2
  q  - possessive pronoun       ("hers",    "his",     "theirs")
2
  r  - reflexive pronoun        ("herself", "himself", "themselves")
2
  s  - subjective pronoun       ("she",     "he,       "they")
2
  'i - indefinite possessive    ("a duck's")
2
  'd - definite possessive      ("the duck's")
2
  'n - possessive title         ("duck's")
2
  #  - the object's number 
2
  <object specifier>x{foo arg1 arg2 ...} is an information specifier
2
  meaning "call :sub_foo on the object, with the given arguments."
2

2
Verbs, Nouns, Properties (capitalizable)
2
  :(foos) - conjugate the verb `to foo'
2
  '(foo)  - pluralize the noun `foo' if necessary
2
  .(foo)  - the object's `foo' property
2

2
Other Substitutions
2
  %${a,d,i,p} - the {arg,dobj,iobj,prep}str 
199
5
4
2
2
*forward*
2
two-letter-qr
199
5
4
200
2
This help text describes the format of pronoun-substitution messages.  Other help texts on pronoun_substitution include:
2

2
pronouns -- An overview.
2
pronoun-substitution -- How to call pronoun_sub and how it works.
2
pronoun-example -- A detailed example.
2
two-letter-qr -- A quick reference guide to the format string.
2
pronoun-style -- Dunno yet.
2

2
Pronoun-substitution messages are entered as strings, but are usually converted to an arcane list format before they're used.  This document is about the format of the string entry; the list format may be discussed elsewhere.
2

2
The information passed to :pronoun_sub() can have subsitution specifiers in it that are replaced by values that depend on the context.  The string that results after all the substitutions are made is returned.
2

2
In the format string that :pronoun_sub() parses, a `%' indicates that a substition should take place.  The characters after it specify the type of substition.  Take a look at this string:
2

2
  "%Td %t:(looks) at %dd oddly."
2

2
It has three substitutions to be made in it:  `%Td', `%t:(looks)', and `%dd'.
2

2
In most cases, a substition consists of three elements:
2

2
  *  The % sign, indicating that a substitution should be made.
2
  *  A symbol indicating the object that should be considered.
2
  *  A symbol indicating what about that object to substitute.
2

2

2
The Object Specifier
2
====================
2

2
The object specifier is the letter that follows the % sign.  Consider the string:
2

2
    "%Td %t:(looks) at %dd oddly."
2
 
2

2
In `%Td' and in `%t:', the object specifer is `t'.  In `%dd', the object specifer is `d'.
2

2
The possible letters and the objects that they correspond to include:
2

2
  t - represents the thing where the verb calling :pronoun_sub() is stored
2
  n - represents the player that started the command
2
  d - represents the direct object of the initial command
2
  i - represents the indirect object of the initial command
2
  l - represents the location of the player
2

2
As an example, If Munchkin typed this command:
2

2
  put duck in pond
2
 
2

2
and the duck had this as a @put_in_water message:
2

2
  "%Nd %n:(puts) %td in %id."
2
 
2

2
Then:
2

2
  %Nd would get the `D' information on Munchkin.
2

2
  %n:(puts) would conjugate `puts' according to Munchkin's gender.
2

2
  %td would get the `d' information from the duck.
2

2
  %id would get the `d' information from the pond.
2

2

2
The Information Specifier
2
=========================
2

2
The character following the object specifier is the information specifier.  It specifies what sort of information to get about the object.  It can be any of these characters:
2

2
  n - the object's name        ("Susan",   "duck",    "king and queen")
2
  d - definite-article         ("Susan",   "the duck","the king and queen")
2
  i - indefinite-article       ("Susan",   "a duck",  "a king and queen")
2
  o - the objective pronoun    ("her",     "him",     "them")
2
  p - possessive adjective     ("her",     "his",     "their")
2
  q - possessive pronoun       ("hers",    "his",     "theirs")
2
  r - reflexive pronoun        ("herself", "himself", "themselves")
2
  s - subjective pronoun       ("she",     "he,       "they")
2

2
Sometimes, several characters follow the object specifier:
2

2
  'i  - indefinite possessive  ("a duck's")
2
  'd  - definite possessive    ("the duck's")
2
  'n  - possessive name        ("duck's")
2

2
In using any of these, you can capitalize the object specifier to have the phrase capitalize--for example, `%Nd' might become "The duck".  If you capitalize both the object specifier and the information specifier (`%ND'), you'll get all caps--"THE DUCK".
2

2
Information Specifiers for Verb Conjugation
2
===========================================
2

2
You often want to insert a verb into a sentence, but the conjugation depends on the context that it's used in.  Give this as the information specifer in order to have pronoun_sub conjugate a verb:
2

2
  :(foos) - conjugate the verb `to foo'
2

2
For example:
2
  "%ns %n:(runs)"
2

2
will result in "he runs", "she runs", or "they run", depending on the gender of the player who called the verb.
2

2
If you capitalize the verb, the result will be capitalized:
2

2
  "%ns %n:(Runs)"
2
  => "he Runs"
2

2

2
Information Specifiers for Pluralizing Nouns
2
============================================
2

2
If you want to make a noun singular or plural depending on context, you can give the noun as an information specifier, and it will be pluralized if the object you specified is plural.  This is the format of the information specification:
2

2
  '(noun)
2
 
2

2
For example, this string:
2
  "%nD %n:(shakes) %d'd %d'(hand)."
2

2
may result in either:
2
  "Munchkin shakes Susan's hand."
2
or
2
  "Munchkin shakes the king and queen's hands."
2

2
Again, capitalizing the noun will result in a capitalized substitution.
2

2

2
Information Specifiers for Object Data
2
======================================
2

2
You can also obtain information about the actual object by using these information specifiers.
2

2
  #      - the object's number 
2
  .(foo) - the object's "foo" property
2

2
For example:
2
  %t.(mood)
2
would return the string in the object's `mood' property.
2

2
If the proprety doesn't exist, the string `Property not found' will be returned.
2

2
If you capitalize the property name, the resulting string will be capitalized.
2

2

2
Information Specifiers for Odd Cases
2
====================================
2

2
Information enclosed in curly brackets can be used as an information specifier, e.g.:
2

2
  %n{u}
2
 
2

2
which produces the same result as `%NN'.  The information within the brackets will be transformed into a list in a way that will be described near the end of this document, and will be used during substitution as the arguments to :name().  Some forms of name, such as the 'in' name, can only be obtained in this manner.  See `help #1:name' for more information.
2

2

2
Object Locations and Contents
2
=============================
2

2
Sometimes you want information about an object's location.  This can be retrieved by appending an `l' to the object specifier.  For example:
2

2
  %tln - Get the `thing' this code is on, find out where it's located, and then get the name of that location. 
2

2
This can be done to every kind of object specifier: %tl, %nl, %il, %dl, and %ll.
2

2
Moreover, `L' and `c' may be used in the same fashion, to refer to the object's outermost location and contents, respectively.
2

2
Object Callbacks
2
================
2

2
The letter `x' can be used in two ways to call back to an object for specialized information.
2

2
Firstly, it can be used as an object specifier, followed by another object specifier and some data in curly brackets, thusly:
2

2
  %xn{foo arg1 arg2 ...}d
2

2
The data in curly brackets is changed into a list, in the way specified in the next section.  Then, during substitution, the substitution mechanism will call the verb `:obj_foo(arg1, arg2, ...)' on the object(s) specified by `%n' to obtain some objects to get the `d' information of.
2

2
Secondly, it can be used as an information specifier, like this:
2

2
  %nx{foo arg1 arg2 ...}
2

2
This case is similar to the above, only the verb called is `:sub_foo' and the mechanism expects text rather than objects.  The returned text is inserted literally for single objects, and turned into an English-style list otherwise.
2

2
Other Substitutions
2
===================
2

2
In addition to the format of %(object specifier)(information specifier), you can also use these substituions:
2

2
  %% - A percent sign.   That is:  %
2
 
2
  %$a - the argstr to the verb that called :pronoun_sub().
2
  %$d - the dobjstr to the verb that called :pronoun_sub().
2
  %$i - the iobjstr to the verb that called :pronoun_sub().
2
  %$p - the prepstr to the verb that called :pronoun_sub().
2
 
2
  %{information} 
2
 
2

2
%{information} allows one to insert an arbitrary list into the data structure used for pronoun substitution.  The list is (nominally) space-separated, and can include both literal MOO values, and unquoted strings (any sequence of characters not including a space, not beginning with `"' or `{', and not interpretable as an object, number, or error value).  Note that lists included in this list are literal MOO lists, not this type of list, and also that any `%' or `}' character included must be quoted with a preceding `%', even inside strings or lists.
2

2
Errata
2
======
2

2
If the information following the % isn't a valid substitution string, the % will remain a %.  This is a clue that the format string needs some work.
199
5
4
35
2
Syntax:  @dig <exit-spec> to <new-room-names>
2
         @dig <exit-spec> to <old-room-object-number>
2

2
This is the basic building tool.  It creates an exit leading from your current location to another room, and optionally another exit leading back.  The destination room may already exist, or it may be created by the @dig command.
2

2
An <exit-spec> has one of the following two forms:
2
        <names>
2
        <names>|<names>
2
where the first form is used when you only want to create one exit, from your current room to the destination room, and the second form when you also want an exit back.
2

2
The <names> piece is just a list of names for the exit, as described in `help @rename':
2
        <name-and-alias>,<alias>,...,<alias> [preferred]
2
        <name>:<alias>,...,<alias>           [not preferred]
2
These are the names of the commands people can type to use the exit.  It is usually a good idea to include explicitly the standard abbreviations for direction names (e.g., 'n' for 'north', 'se' for 'southeast', etc.).  DO NOT put spaces in the names of exits; they are useless in MOO.
2

2
If you give an object number for the indirect object of the command (<old-room-object-number>), then that object will be used as the destination room.  Otherwise, that part of the command is another set of names and aliases, which will be used to create a new room.
2

2
NOTE: You must own the room at one end or the other of the exits you create.  If you own both, everything is fine.  If you own only one end, then after creating the exits you should write down their object numbers.  You must then get the owner of the other room to use @add-exit and @add-entrance to link your new exits to their room.
2

2
You can determine what objects will be used as the parents of new exits and rooms that @dig creates by setting your building options:
2
    @options building room=<parent class>
2
and
2
    @options building exit=<parent class>
2
will set your defaults.  You can also specify "-room" or "-exit" to use the global defaults ($room and $exit).
2

2
Examples:
2

2
    @dig north,n to "The North Pole"
2
creates a new room and also an exit linking the player's current location to the new room; players would say either 'north' or 'n' to get from here to the new room.  No way to get back from that room is created.
2

2
    @dig west,w|east,e,out to "The Department of Auto-Musicology"
2
creates a new room and two exits, one taking players from here to the new room (via the commands 'west' or 'w') and one taking them from the new room to here (via 'east', 'e', or 'out').
2

2
    @dig up,u to #7164
2
creates an exit leading from the player's current room to #7164, which must be an existing room.
199
5
4
13
2
Syntax:  @create <parent-object> named "<names>"
2

2
The main command for creating objects other than rooms and exits (for them, see 'help @dig'; it's much more convenient).
2

2
The first argument specifies the 'parent' of the new object: loosely speaking, the 'kind' of object you're creating.  You can use a `class name' such as $note, $letter, $thing, or $container; you can also specify a parent object by object number or just by name, if it's nearby.
2

2
The <names> are given in the same format as in the @rename command:
2

2
        <name-and-alias>,<alias>,...,<alias> [preferred]
2
        <name>:<alias>,...,<alias>           [not preferred]
2

2
See `help @rename' for a discussion of the difference between a name and an alias.
2
See `help naming' for a discussion of names and articles.
199
5
4
5
2
Usage: @recreate <object> as <parent> named <name spec>
2

2
This is a combination of @create and @chparent.  It takes an existing object, completely strips it of any verbs, properties, and values for inherited properties.  This object is then reshaped into a child of the parent specified, as though @create had been called, but retaining the same object number as the original.
2

2
The <parent> and <name spec> arguments are as in @create.  See `help @create' and `help @chparent'.
199
5
4
29
2
Usage:  @building-options
2
        @building-option <option>
2

2
These options (see `help options') allow you to customize your building environment.
2

2
The first form displays all of your building options.  The second shows or sets a given option.  Current building options are:
2

2
  room=<object>	Type of object the @dig command should create.
2
 !room		Default ($room).
2

2
  exit=<object> Type of exit the @dig command should create.
2
 !exit		Default ($exit).
2

2
The @dig command (see `help @dig') creates rooms and/or exits when you're building.  These two options let you determine what parent classes are used for the objects created.
2

2
 +furnishing	@created objects are locked in the room you're in
2
 -furnishing	@created objects are carried by you, unlocked
2

2
By default, when @create makes an object, the object is placed in your inventory.  While this is handy for programming, it's often not what you want when you're furnishing rooms.  If you have this option set, the objects created will be placed in the room you're in, and locked down.
2

2
 +create-set-co @create sets your current object to the one created.
2
 -create-set-co @create doesn't change your current object.
2

2
When this option is set, a @create command will set your current object (see `help current-object') to the newly created object.  This is useful primarily for porting (an object creation script can start with a @create, then refer to _ in the rest of the script), but can also be handy for general creation.
2

2
 +bi-create     @create/@recycle call create()/recycle() directly.
2
 -bi-create     @create/@recycle re-use object numbers.
2

2
This option determines whether @create and other commands will use the server's create() and recycle() primitives or the database's $recycle.
199
5
4
2
2
*forward*
2
@building-options
199
5
4
2
2
*objectdoc*
2
$furniture
199
5
4
15
2
Syntax:
2
  @measure object <object name>
2
  @measure summary [player]
2
  @measure new [player]
2
  @measure breakdown <object name>
2

2
When the MOO is under byte-quota, objects in the MOO are measured approximately once a week, and the usage tally as reported by @quota is updated.  You may wish to measure an object specially, however, without waiting for the automatic measurement to take place, or if the MOO is under object-quota.  @measure has some subcommands to handle this.
2

2
@measure object will measure an individual object right now, update the usage of that object in your usage as reported by @quota, and update the date of that object's measurement.
2

2
@measure summary will go through your or another player's objects and produce the summary information that is printed by @quota.  Normally this will be just the same as @quota prints out, but occasionally the addition/subtraction done to keep @quota in sync will get out of date, and @measure summary will be needed.
2

2
@measure new will go through all your or another player's objects, measuring exactly those which have never been measured before (that is, are too newly @created to have any measurement data).  This is necessary as any player is only permitted to own 10 unmeasured objects, or object creation will not be permitted.
2

2
@measure breakdown will give you full information on where an object's size is coming from.  It will offer to moomail you the result.  Caution: don't forget to delete this message, as it is large and takes up a lot of space!
199
5
4
5
2
Usage:  @newmessage <message-name> [<message>] [on <object>]
2

2
Add a message property to an object (default is player), and optionally set its value.  For use by non-programmers, who aren't allowed to add properties generally.
2

2
To undo the effects of this, use @unmessage.
199
5
4
3
2
Usage:  @unmessage <message-name> [from <object>]
2

2
Remove a message property from an object (default is player).
199
5
4
122
2
Naming an object can be a tricky business.  The first thing to know is that there are two sorts of names; there is the "base" name of an object, i.e. the value of the .name property, and the "modified" name, i.e. the value returned by the :name(...) verb.  When you specify the name when you @create an object (see `help @create') or @rename it (see `help @rename'), you are only setting the base name (as well as the object's aliases, which don't enter into this discussion).  The modified name is automatically generated from the base name depending on the particular situation.  Usually this just adds an article in front of the name, like "a" or "the", e.g. if an object is listed in a player's inventory.  You can control how the modified name is generated with a number of auxiliary properties.  Let's try a few examples.
2

2
Suppose Janet wants to create a football.  She types:
2

2
        >@create $thing named "football"
2

2
then does an inventory and sees:
2

2
        Carrying:
2
         a football
2

2
Notice that she did not include "a" in the name when creating the football; it gets dynamically added to the name whenever she does an inventory.  Now if she were to drop the football, she would see:
2

2
        You drop the football.
2

2
while other people in the room would see:
2

2
        Janet drops a football.
2

2
Notice that the modified name is different depending on who sees the message.  The difference is that the first message uses the definite-article modified name, while the second message uses the indefinite-article modified name; in pronoun-substitution lingo (see `help pronouns'), the first one uses %dd, while the second uses %di.
2

2
Possessor
2
---------
2

2
Now suppose there are a bunch of footballs around, and Janet wants to distinguish hers from the others by adding her name to it; in other words, she wants the modified name to be "Janet's football".  She can do this by adding a .possessor property to the football and setting it to point to herself:
2

2
        >@prop football.possessor
2
        >@set football.possessor to me
2

2
Now when she picks up the football, she sees:
2

2
        You pick up Janet's football.
2

2
while others in the room see:
2

2
        Janet picks up Janet's football.
2

2
and when she does an inventory she sees:
2

2
        Carrying:
2
         Janet's football
2

2
Unique
2
------
2

2
Now suppose Janet wants to create a famous painting:
2

2
        >@create $thing named "Mona Lisa"
2

2
Her inventory now looks like this:
2

2
        Carrying:
2
         Janet's football                a Mona Lisa
2

2
This isn't quite right, though; there's only *one* Mona Lisa, so it doesn't make much sense to call it "a" Mona Lisa!  Janet can fix this by setting the .unique property:
2

2
        >@set mona.unique to 1
2

2
(Any non-zero value will do, but 1 is the best non-zero value.)  Now her inventory looks like this:
2

2
        Carrying:
2
         Janet's football                the Mona Lisa
2

2
And now when she drops the Mona Lisa, other people in the room see:
2

2
        Janet drops the Mona Lisa.
2

2

2
Number
2
------
2

2
All this has made Janet hungry, so she makes some peanuts:
2

2
        >@create $thing named "peanuts"
2

2
Now her inventory looks like this:
2

2
        Carrying:
2
         Janet's football                a peanuts
2

2
Oops.  "Peanuts" is plural, so it shouldn't use the singular article "a".  She can fix this by setting the .number property:
2

2
        >@set peanuts.number to 20
2

2
(The exact number doesn't really matter, since there's no way to drop, say, 3 peanuts.  Just as long as .number is not 1.)  Now her inventory is:
2

2
        Carrying:
2
         Janet's football                some peanuts
2

2
Proper
2
------
2

2
Finally, Janet decides to create a room called "Janetland":
2

2
        >@dig "north,n|south,s,out" to "Janetland"   (see `help @dig')
2

2
But when she goes there, there's something amiss:
2

2
        >north
2
        The Janetland
2
        You see nothing special.
2

2
"Janetland" is a proper noun, so there shouldn't be any article!  This is easily fixed:
2

2
        >@set here.proper to 1
2

2
(Again, any non-zero value will work.)  Now when she looks at the room, she sees:
2

2
        Janetland
2
        You see nothing special.
2

2
Summary
2
-------
2

2
In summary, here are the various properties you can set on an object to control how the modified name gets generated:
2

2
        * .possessor -- Set this to another object (e.g. a player) to have that object's name appear in the object's name.
2
        * .unique -- Set this to 1 to have an object always use the definite article "the".
2
        * .number -- Set this to something other than 1 to have a collective object use the indefinite article "some".
2
        * .proper -- Set this to 1 to have an object not use any article at all.
2

2
If you need more particular control, you can use the .def_art and .indef_art properties to specify the exact definite and indefinite article to use, respectively.  Also, you can use verbs in place of any of these properties; for example, you might write a :possessor verb which returns the current owner of the object, so that the name of the object will automatically change if the ownership changes.
35
1
4
0
35
1
5
199
4
4
2
2
Builder Help
2
$builder_help
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
1
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
38222
0
913134427
200
1
#167
he

16
35
-1
-1
-1
165
-1
168
0
0
27
1
167
35
5
2
His
35
5
2
his
35
5
2
His
35
5
2
his
35
5
2
Himself
35
5
2
himself
35
5
2
Him
35
5
2
him
35
5
2
He
35
5
2
he
35
5
5
35
5
2
male
35
5
5
35
4
4
3
2
male
2
he
2
him
35
5
2
Third person singular, male.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
647
0
913134427
200
1
#168
she

16
35
-1
-1
-1
165
-1
169
0
0
27
1
168
35
5
2
Hers
35
5
2
hers
35
5
2
Her
35
5
2
her
35
5
2
Herself
35
5
2
herself
35
5
2
Her
35
5
2
her
35
5
2
She
35
5
2
she
35
5
5
35
5
2
female
35
5
5
35
4
4
3
2
female
2
she
2
her
35
5
2
Third person singular, female.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
659
0
913134427
200
1
#169
e

16
35
-1
-1
-1
165
-1
173
0
0
27
1
169
35
5
2
Eirs
35
5
2
eirs
35
5
2
Eir
35
5
2
eir
35
5
2
Emself
35
5
2
emself
35
5
2
Em
35
5
2
em
35
5
2
E
35
5
2
e
35
5
5
35
5
2
Spivak
35
5
5
35
4
4
3
2
Spivak
2
e
2
em
35
5
2
Third person singular, indefinite gender.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
657
0
913134427
200
1
#170
we

16
35
-1
-1
-1
64
-1
-1
0
0
27
1
170
35
5
2
Ours
35
5
2
ours
35
5
2
Our
35
5
2
our
35
5
2
Ourselves
35
5
2
ourselves
35
5
2
Us
35
5
2
us
35
5
2
We
35
5
2
we
35
5
0
1
35
5
2
royal
35
5
5
35
4
4
3
2
royal
2
we
2
us
35
5
2
First person plural.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
1
170
35
1
1
-1
199
1
4
2
0
644
0
913134427
200
1
#171
I

16
35
-1
-1
-1
64
-1
71
1
verb_sub noun_sub
35
173
-1
1
verb_exceptions
28
4
5
4
2
2
is
2
am
4
2
2
Is
2
Am
4
2
2
isn't
2
am not
4
2
2
Isn't
2
Am Not
4
2
2
's
2
'm
35
5
1
171
35
5
2
Mine
35
5
2
mine
35
5
2
My
35
5
2
my
35
5
2
Myself
35
5
2
myself
35
5
2
Me
35
5
2
me
35
5
2
I
35
5
2
I
35
5
5
35
5
2
egotistical
35
5
5
35
4
4
3
2
egotistical
2
I
2
me
35
5
2
First person singular.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1464
0
913134427
200
1
#172
you

16
35
-1
-1
-1
34
-1
-1
0
0
28
5
35
5
1
172
35
5
5
35
5
5
35
5
5
35
5
5
35
5
2
Yourselves
35
5
2
yourselves
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
2
second_plural
35
5
5
35
4
4
3
2
2nd
2
you
2
you
35
5
2
Second person plural.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
1
172
35
1
1
-1
199
1
4
2
0
642
0
913134427
200
1
#173
s/he

16
35
-1
-1
-1
165
-1
177
0
0
27
1
173
35
5
2
His/Hers
35
5
2
his/hers
35
5
2
His/Her
35
5
2
his/her
35
5
2
(Him/Her)self
35
5
2
(him/her)self
35
5
2
Him/Her
35
5
2
him/her
35
5
2
S/He
35
5
2
s/he
35
5
5
35
5
2
either
35
5
5
35
4
4
3
2
either
2
s/he
2
him/her
35
5
2
Third person singular, either gender.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
710
0
913134427
200
1
#174
generic admin group

16
35
-1
-1
-1
1
147
116
25
is_member
199
173
-1
add_member
199
173
-1
remove_member
199
173
-1
@add*member
199
157
1
@rm*member
199
149
5
description
35
173
-1
all_members
35
173
-1
connected_members
35
173
-1
member_list
35
173
-1
members
199
173
-1
match_group
35
173
-1
member_set
35
173
-1
initialize
35
173
-1
group_match_failed
199
173
-1
new_member_voters
199
173
-1
@vacation
35
157
5
@return
35
157
1
@incognito
35
157
11
@reveal
35
157
11
controls
35
173
-1
help_text
35
173
-1
init_for_core
199
173
-1
nominate_for_core
35
173
-1
member_term members_term
35
173
-1
help_topic
35
173
-1
7
members
leader
subgroups
mail_recipient
new_member_voter_groups
vacationers
incognito
21
4
1
1
2
35
1
1
2
35
1
4
0
35
1
1
-1
35
1
4
0
35
1
4
0
35
1
4
0
35
1
5
35
4
4
1
2
generic admin group
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
1
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
17920
0
913134427
200
1
#175
Tech Group

0
35
-1
-1
-1
174
159
176
0
0
21
5
35
1
5
35
1
4
3
1
159
1
158
1
131
35
1
1
111
35
1
5
35
1
5
35
1
5
35
1
5
35
4
4
3
2
Tech Group
2
tech-group
2
tg
35
5
2
The JaysHouseMOO `Tech' Group are people who deal with the technical operation of the MOO.  This consists of three types of jobs:
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
654
0
913134427
200
1
#176
Community Group

16
35
-1
-1
-1
174
-1
-1
1
nominate_for_core
35
173
-1
1
feature
22
1
160
199
1
5
35
1
5
35
1
5
35
1
1
112
35
1
4
2
1
147
1
176
35
1
5
35
1
5
35
1
5
35
4
4
3
2
Community Group
2
community-group
2
cg
35
5
4
9
2
Members of the community group handle any social problems that may arise, mediate disputes, assist new players in becoming acclimated to JH, and promote community harmony.  New members are selected by existing members of the community group and the steering committee (see `help steering-committee').
2

2
They have the ability to boot characters from the MOO, limit access from remote sites, check network connections, and use related commands that may be useful when dealing with problematic situations.
2

2
They can also help if you forget your password, need to change your email address, or have a problem using the MOO.
2

2
@911 (also called @helpme) pages members of the CG who are on-line.  Please use this when problems occur.  (There is help on this command.)  If you have a complaint about a member of the CG and don't feel comfortable bringing it directly to that person or to the group as a whole yourself, please talk to another member of the CG.  If even that would be uncomfortable, speak to one of the steering committee members.
2

2
If there are no members of the CG on-line, you can send mail to the *Community-Group mailing list (*cg).
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2089
0
913134427
200
1
#177
*e

16
35
-1
-1
-1
165
-1
178
0
0
27
1
177
35
5
2
H*s
35
5
2
h*s
35
5
2
H*
35
5
2
h*
35
5
2
H*self
35
5
2
h*self
35
5
2
H*
35
5
2
h*
35
5
2
*E
35
5
2
*e
35
5
5
35
5
2
splat
35
5
5
35
4
4
3
2
splat
2
*e
2
h*
35
5
2
Third person singular, wildcard gender.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
653
0
913134427
200
1
#178
none

16
35
-1
-1
-1
165
-1
-1
1
ps*c po*c pr*c pp*c pq*c
35
165
-1
0
27
1
178
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
2
none
35
5
5
35
4
4
1
2
none
35
5
2
Never use pronouns, always use the name.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1258
0
913134427
200
1
#179
generic message dispatch object

16
199
-1
-1
-1
1
224
-1
3
parse_send_args
199
173
-1
parse_receive_args
199
173
-1
set_messages_in set_messages_out
199
173
-1
2
messages_in
messages_out
16
4
0
199
1
4
0
199
1
5
199
4
4
1
2
generic message dispatch object
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
4414
0
913134427
200
1
#180
FTP

0
35
-1
-1
-1
5
-1
192
1
get
199
165
-1
1
port
23
0
21
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
4
4
1
2
FTP
35
5
2
An object with properties and verbs for interacting with FTP servers.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2250
0
913134427
200
1
#181
guest help database

16
199
-1
-1
-1
30
-1
201
0
8
@request
@register
guest-index
character
@name
(core-character)
(core-@request)
 name
23
4
3
2
Usage:  @request <player-name> for <email-address>
2

2
The @request command creates a new character with your email address, and sends you your password in the mail.  You should use your primary address for this, as various MOO programs may send you mail.  (For example, the MOO gopher client allows you to request that a document be mailed to you instead of printed at you through the MOO.)
199
5
4
2
2
*forward*
2
@request
199
5
4
2
2
*index*
2
Guest Help Topics
199
5
4
6
2
*pass*
2
character
2
GETTING A CHARACTER
2
-------------------
2

2
If you haven't got a character yet, and you'd like one, you need to use the `@request' command (see `help @request').
199
5
4
10
2
Syntax: @name <name>
2
        @name <name> <password>
2

2
@name is used by guests to designate their name.  The name you pick doesn't override the guests's default name, but is added to it.  For example, if you type:
2
        @name Munchkin
2
and you're the French Guest, then your name will be:
2
        The French Guest (Munchkin)
2
for the duration of the connection.
2

2
You can't use a name that is already being used by an existing player, unless you supply that player's password.  The assumption here is that you _are_ that player, using a guest character for some reason.
199
5
4
6
2
*pass*
2
character
2
GETTING A CHARACTER
2
-------------------
2

2
If you haven't got a character yet, and you'd like one, you need to use the `@request' command (see `help @request').
199
5
4
3
2
Usage:  @request <player-name> for <email-address>
2

2
The @request command creates a new character with your email address, and sends you your password in the mail.  You should use your primary address for this, as various MOO programs may send you mail.  (For example, the MOO gopher client allows you to request that a document be mailed to you instead of printed at you through the MOO.)
199
5
4
3
2
*pass*
2
name
2
Guests can use the @name command to identify themselves while they're logged in; see `help @name' for more information.
199
5
4
0
35
1
5
199
4
4
3
2
guest help database
2
$guest_help
2
$guest.help
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2929
0
913134427
200
1
#182
documentation feature

16
35
-1
-1
-1
65
-1
85
17
@gethelp
199
21
-1
@copyhelp
199
85
1
@sethelp @sethelp-text @sethelp-string @sethelp-value
199
21
-1
@helpedit @edithelp
199
29
-1
match_help_topic
35
173
-1
name_help_topic
35
173
-1
verify_db
35
173
-1
match_help_dbs
35
173
-1
help_dbs_match_failed
199
165
-1
help_topic_match_failed
199
173
-1
name_help_db_or_topic
35
173
-1
@addhelp
199
29
-1
@rmhelp
199
29
-1
@linkhelp
199
93
1
@findhelp
199
29
-1
@showhelp
199
21
-1
nominate_for_core
35
165
-1
1
generic_personal_db
20
1
121
199
1
4
8
2
@gethelp
2
@sethelp
2
@copyhelp
2
@edithelp
2
@addhelp
2
@rmhelp
2
@linkhelp
2
@findhelp
35
5
0
1
35
1
4
9
2
@gethelp
2
@copyhelp
2
@sethelp @sethelp-text @sethelp-string @sethelp-value
2
@helpedit @edithelp
2
@addhelp
2
@rmhelp
2
@linkhelp
2
@findhelp
2
@showhelp
35
1
5
35
1
2
A feature object for help text authors.
35
5
5
35
4
4
5
2
documentation feature
2
df
2
feature
2
doc
2
help
35
5
4
1
2
This feature makes it easier for people to write help texts, hopefully.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
22567
0
913134427
200
1
#183
administration

16
35
-1
-1
-1
1
-1
207
8
groups_helpme groups_register_other groups_register_other_as groups_check_registration_other groups_set_registration_other
35
173
-1
ok_gag_announce
35
173
-1
ok_check_registration_other ok_set_registration_other
35
173
-1
groups_shout
35
173
-1
ok_shout
35
173
-1
in_any_group
35
173
-1
members_groups
35
173
-1
member_term members_term
35
173
-1
0
14
5
35
4
4
1
2
administration
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
4002
0
913134427
200
1
#184
help text editor

16
35
35
-1
-1
44
-1
-1
4
set_note_text
199
165
-1
note_match_failed
35
173
-1
note_text
199
165
-1
local_editing_info
35
173
-1
0
60
4
0
35
5
4
0
35
1
4
0
35
1
4
0
35
1
5
35
5
5
35
5
5
35
5
5
35
5
0
0
35
1
5
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
4
0
35
0
4
0
35
1
4
0
35
0
4
0
35
0
4
0
35
1
4
3
1
69
2
do
2
[editing notes] 
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
4
5
35
5
5
35
5
5
35
4
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
196
1
5
35
5
5
35
1
5
35
5
5
199
1
5
35
4
4
5
2
help text editor
2
help editor
2
editor
2
hte
2
he
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3638
0
913134427
200
1
#185
Resolved Help Address

16
199
-1
-1
-1
115
-1
-1
5
send_to
35
165
-1
make_link
35
173
-1
make_formatted_link
35
173
-1
from_db_and_topic
35
173
-1
from_db_and_topic_and_dblist
35
173
-1
0
15
2
resolved_help
199
5
5
199
4
4
2
2
Resolved Help Address
2
rha
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
4059
0
913134427
200
1
#186
Object Help Database

16
199
-1
-1
-1
30
-1
121
4
find_topics
35
173
-1
get_topic
35
173
-1
index
35
173
-1
nominate_for_core
35
173
-1
0
15
4
0
35
1
5
199
4
4
2
2
Object Help Database
2
ohd
199
5
2
Directly inspired by the Verb Help Database, this help database `contains' help topics for all stringified objnums (#1234) that have .help_msgs defined on them.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3844
0
913134427
200
1
#187
listeners module

16
199
-1
-1
-1
82
-1
214
0
4
obj_listener
obj_daemon
obj_daemon_handler
obj_redlist_daemon
45
1
204
199
1
1
143
199
1
1
206
199
1
1
101
199
1
5
35
1
4
4
1
204
1
143
1
206
1
101
35
1
4
4
2
listener
2
daemon
2
daemon_handler
2
redlist_daemon
35
1
0
0
35
1
5
199
5
4
0
35
1
4
2
4
0
4
0
35
1
1
-1
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
199
1
4
1
4
2
1
1
2
$root_class
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
4
4
1
2
listeners module
199
5
2
Useful objects for the creation of listeners and daemons based on listeners.
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1255
0
913134427
200
1
#188
Byte Quota Utilities

16
200
-1
-1
-1
90
-1
-1
29
:initialize_quota(old)
200
173
-1
adjust_quota_for_programmer
200
173
-1
parse_create_args
200
173
-1
creation_permitted verb_addition_permitted property_addition_permitted
200
173
-1
display_quota
200
173
-1
get_quota get_quota_total
200
173
-1
charge_quota
200
173
-1
reimburse_quota
200
173
-1
set_quota set_quota_total
200
173
-1
get_size_quota
200
173
-1
display_quota_summary
200
173
-1
quota_remaining
200
173
-1
preliminary_reimburse_quota
200
173
-1
do_summary
200
157
0
can_touch
200
173
-1
object_overhead_bytes
200
173
-1
summarize_one_user
200
173
-1
init_for_core
199
173
-1
enable_create
199
173
-1
disable_create
199
173
-1
value_bytes
199
173
-1
object_bytes object_size
199
173
-1
do_breakdown
199
173
-1
verb_overhead_bytes
199
173
-1
property_overhead_bytes
199
173
-1
bi_create
199
165
-1
recent_object_bytes
199
165
-1
measurement_task
199
173
-1
add_owned_object(old)
199
173
-1
9
default_quota
large_negative_number
max_unmeasured
unmeasured_multiplier
working
cycle_days
task_time_limit
exempted
measurement_task
31
4
4
0
50000
0
0
0
0
0
1
200
5
0
-10000
200
5
0
10
200
5
0
100
200
5
1
2
200
5
0
1
200
5
0
500
200
5
4
0
200
5
0
1877742644
200
5
5
200
5
5
199
1
5
199
1
0
0
200
5
0
100000
200
5
4
1
1
2
200
1
0
1
200
5
5
200
5
0
0
200
4
4
1
2
Byte Quota Utilities
200
5
4
42
2
Verbs a user might want to call from a program:
2
 :bi_create -- built-in create() call, takes same args.
2

2
 :get_quota(who) -- just get the raw size_quota property
2
 :display_quota(who) -- prints to player the quota of who.  If caller_perms() controls who, include any secondary characters.  Called by @quota.
2
 :get_size_quota(who [allchars]) -- return the quota of who, if allchars flag set, add info from all secondary chars, if caller_perms() permits.
2

2
 :value_bytes(value) -- computes the size of the value.
2
 :object_bytes(object) -- computes the size of the object and caches it.
2
 :recent_object_bytes(object, days) -- computes and caches the size of object only if cached value more than days old.  Returns cached value.
2
 :do_summary(user) -- prints out the results of summarize-one-user.
2
 :summarize_one_user(user) -- summarizes and caches space usage for user.  See verb help for details.
2

2
Verbs the system calls:
2
 :"creation_permitted verb_addition_permitted property_addition_permitted"(who) -- returns true if who is permitted to build.
2
 :initialize_quota(who) -- sets quota for newly created players
2
 :adjust_quota_for_programmer(who) -- empty; might add more quota to newly @progged player.
2
 :enable_create(who) -- sets .ownership_quota to 1
2
 :disable_create(who) -- sets .ownership_quota back to -1000 to prohibit create()
2
 :charge_quota(who, object) -- subtract the size of object from who's quota.  Manipulates the #-unmeasured if what is not currently measured.  Called by $wiz_utils:set_owner.
2
 :reimburse_quota(who, object) -- add the size of object to who's quota.  Ditto.
2
 :preliminary_reimburse_quota(who, object) -- Because the set_owner is done *after* an object has been turned into $garbage, ordinary reimbursement fails.  So we use this verb in the $recycler.
2
 :set_quota(who, howmuch)
2
 :quota_remaining(who) 
2
 :display_quota_summary -- internal, called by display quota
2

2
The measurement task:
2

2
 :measurement_task() -- runs once every 24 hours measuring stuff.
2
 .task_time_limit -- integer number of seconds indicating for how long it should run each day.
2
 .working -- object indicating the player whom it is either working on now (or if not running) will pick up working on when it commences tonight.
2
 .cycle_days -- integer numbers indicating how long ago an object must have been measured before it will be remeasured.  If, in .task_time_limit seconds, it measures all objects not measured in cycle_days, it will run again measuring those objects which have not been measured in cycle_days - 1, repeating until it has used up its seconds.  ("Doing some of tomorrow's work.")
2
 .exempted -- list of objects to never measure (useful if there are huge objects)
2
 .fascist -- if 1, sends mail indicating possible quota violations. (May spam you unnecessarily, so be cautious.)
2
 .measurement_task -- indicates the task_id() of the most recent measurement task -- used to prevent duplicate invocation.
2
 .report_recipients -- list of players to receive status report mail.
2

2
Suggestion: modify .task_time_limit so that it just completes one full run each day.  Too many and it will spin its wheels when it gets to (cycle_days - cycle_days = 0).  (It should be smarter.  My kingdom for a break statement.)
2

2
See help @measure and help @quota for the command line verbs.
2

2

200
5
5
35
1
5
200
5
5
200
5
5
200
5
0
1
200
5
5
200
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
26733
0
913134427
200
1
#189
checkpointer

16
199
-1
-1
-1
1
-1
198
9
schedule_checkpoint
199
173
-1
next_checkpoint
199
173
-1
description
199
173
-1
checkpoint_started
199
173
-1
checkpoint_finished
199
173
-1
ftime
199
173
-1
init_for_core
199
173
-1
log
199
173
-1
server_started
199
173
-1
5
checkpoint_times
last_failure
last_success
in_progress
last_success_time
19
4
0
199
1
0
0
199
1
0
0
199
1
0
0
199
1
0
172
199
1
0
0
199
4
4
1
2
checkpointer
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
7067
0
913134427
200
1
#190
Text

1
190
15
103
196
6
-1
196
1
name
190
173
-1
0
101
5
35
1
5
199
0
5
190
5
2
nop@ccs.neu.edu
199
0
0
767037213
199
1
0
0
190
5
5
35
1
5
35
1
5
35
0
5
35
1
5
190
5
4
25
1
190
1
95
1
96
1
110
1
102
1
108
1
100
1
99
1
86
1
88
1
107
1
103
1
115
1
117
1
124
1
135
1
94
1
93
1
148
1
120
1
132
1
130
1
144
1
152
1
139
200
1
5
35
1
5
190
4
5
199
0
5
199
0
5
190
5
5
190
4
5
190
4
5
190
4
5
190
5
5
190
5
5
190
5
5
190
5
2
The %t isn't a real person.  You may want to mail an admin group instead.
190
5
5
190
5
5
190
5
5
190
4
5
190
4
0
2147483647
199
1
0
100
200
0
5
190
5
1
70
35
1
5
199
0
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
199
1
5
199
1
5
35
1
5
35
1
5
190
5
5
190
5
5
199
1
5
199
1
5
190
5
5
199
0
5
190
5
5
35
1
5
35
1
5
190
5
4
0
35
1
5
199
0
5
199
0
5
199
0
5
199
1
5
199
1
5
35
1
5
199
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
199
1
5
199
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
199
1
5
199
1
5
199
0
5
199
1
4
4
0
992259
0
108729
0
873878439
0
0
200
0
5
190
5
5
199
0
5
35
1
5
199
1
5
199
1
5
190
5
5
199
1
5
190
4
4
4
2
Text
2
J. Text
2
jtext
2
J.
199
1
2
Oh, weird, e must have fallen asleep with eir head resting on a newspaper; there's text in various sizes and styles on eir forehead and various other parts of eir face.
190
5
5
35
1
5
190
5
5
190
5
5
190
5
5
190
5
5
190
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2594
0
913134427
200
1
#191
Name Utilities

16
35
-1
-1
-1
156
-1
123
7
find_name_flag
35
165
-1
remove_name_flag
35
165
-1
find_pronomial_equivalent
35
165
-1
collapse_to_string
35
165
-1
fake_proper_name
35
165
-1
nominate_for_core
35
173
-1
sep_name_flags_final
35
165
-1
1
dummy
16
1
141
35
5
5
35
5
5
35
4
4
2
2
Name Utilities
2
$name_utils
35
5
2
A set of utilities dealing with name flags.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
1
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3716
0
913134427
200
1
#192
topic sign

16
196
-1
-1
-1
5
-1
82
25
set_default_topic set_current_topic
196
173
-1
write
196
153
4
cover
196
109
0
look_self
196
173
-1
uncover peal peel
196
41
-1
event_exit
196
165
-1
moveto
199
173
-1
make_topic_sign
196
173
-1
destroy_topic_sign
196
173
-1
topic_msg
196
173
-1
initialize
196
173
-1
recycle
196
165
-1
remove kick
196
41
-1
integrate_room_msg
196
173
-1
carve
196
153
4
pop
196
173
-1
push
196
173
-1
set_topic
196
173
-1
shake
196
41
-1
nominate_for_core
196
173
-1
initial_message
196
173
-1
show_topic
196
173
-1
matches_private
199
165
-1
set_private_from_topic
199
173
-1
help_verbs
199
173
-1
9
current_topic
default_topic
write_msg
uncover_msg
stack
remove_msg
carve_msg
is_private
private
31
2

196
1
2

196
1
4
6
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
writes
2
 `
196
1
4
10
1
69
2
do
4
3
2
name
2
player
4
1
2
c
2
 
4
3
2
verb
2
player
2
pulls
2
 the top sheet off 
4
3
2
name
2
thing
4
1
2
d
2
, revealing `
4
3
2
string
2
argstr
0
0
2
'.
196
1
4
0
196
1
4
10
1
69
2
do
4
3
2
name
2
player
0
1
2
 
4
3
2
verb
2
player
2
kicks
2
 
4
3
2
dname
2
thing
0
0
2
 down. The groundskeeper whisks 
4
3
2
pronoun
2
thing
2
ps
2
 away.
196
1
4
10
1
69
2
do
4
3
2
dname
2
player
0
1
2
 
4
3
2
verb
2
player
2
carves
2
 `
4
4
2
property
2
thing
2
default_topic
0
0
2
' on 
4
3
2
dname
2
thing
0
0
2
.
196
1
0
0
196
5
0
0
196
5
5
196
5
5
196
5
5
196
5
5
196
5
5
196
5
5
196
5
5
196
5
5
196
5
5
196
4
4
2
2
topic sign
2
sign
196
5
2
A well crafted sign on a polished brass stand.
196
5
0
0
35
1
5
196
5
5
196
5
5
196
5
0
0
196
5
5
196
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
15089
0
913134427
200
1
#193
generic window

144
35
-1
-1
-1
128
-1
-1
0
0
55
5
35
5
5
35
5
0
0
35
1
5
35
5
5
35
5
0
0
35
1
5
35
5
5
35
1
4
8
1
69
2
do
4
3
2
name
2
player
4
1
2
dc
2
 
4
3
2
verb
2
player
2
opens
2
 
4
3
2
name
2
thing
4
1
2
d
2
 from the other side.
35
5
4
8
1
69
2
do
4
3
2
name
2
player
4
1
2
dc
2
 
4
3
2
verb
2
player
2
closes
2
 
4
3
2
name
2
thing
4
1
2
d
2
 from the other side.
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
1
4
6
1
69
2
do
4
3
2
name
2
player
4
1
2
dc
2
 locks 
4
3
2
name
2
thing
4
1
2
d
2
 from the other side.
35
5
4
6
1
69
2
do
4
3
2
name
2
player
4
1
2
dc
2
 unlocks 
4
3
2
name
2
thing
4
1
2
d
2
 from the other side.
35
5
5
35
5
4
8
1
69
2
do
4
3
2
name
2
player
4
1
2
dc
2
 
4
3
2
verb
2
player
2
knocks
2
 on 
4
3
2
name
2
thing
4
1
2
d
2
.
35
5
0
0
35
1
4
1
2
Windows are just like doors, except that they're transparent (.opaque == 0) by default.  See `help $door'.
35
5
5
35
5
5
35
1
5
35
1
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
199
1
5
35
4
4
2
2
generic window
2
window
35
5
5
35
5
5
35
1
4
9
1
69
2
do
4
3
2
name
2
thing
4
1
2
dc
2
 to 
4
3
2
name
0
2
4
1
2
d
2
 
4
3
2
verb
2
thing
2
is
2
 
2
{special thing open {0%}}.
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2672
0
913134427
200
1
#194
here

16
35
-1
-1
-1
1
-1
183
2
modname_in
35
173
-1
look_self
35
165
-1
0
14
5
35
4
4
1
2
here
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
0
1
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
923
0
913134427
200
1
#195
spam-player sink

144
35
-1
-1
-1
205
-1
140
4
parse_commandline
199
173
-1
finish
199
173
-1
receive_warning
199
173
-1
set_target
35
173
-1
1
target
18
1
-1
35
1
5
35
0
5
35
1
5
35
1
5
35
4
4
3
2
spam-player sink
2
player
2
me
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2629
0
913134427
200
1
#196
topic-owner

1
196
15
-1
200
6
-1
200
0
0
101
5
35
1
5
199
0
5
196
5
5
199
0
0
779247935
199
1
0
0
196
5
5
35
1
5
35
1
5
35
0
5
35
1
5
196
5
4
2
1
196
1
192
200
1
5
35
1
5
196
4
5
199
0
5
199
0
5
196
5
5
196
4
5
196
4
5
196
4
5
196
5
5
196
5
5
196
5
5
196
5
2
The %t isn't a real person.  You may want to mail an admin group instead.
196
5
5
196
5
5
196
5
5
196
4
5
196
4
0
2147483647
199
1
0
47
200
0
5
196
5
1
70
35
1
2
H8cTI5jJfbhDU
199
0
5
196
5
5
196
5
5
196
5
5
196
5
5
35
1
5
199
1
5
199
1
5
35
1
5
35
1
5
196
5
5
196
5
5
199
1
5
199
1
5
196
5
5
199
0
5
196
5
5
35
1
5
35
1
5
196
5
4
0
35
1
5
199
0
5
199
0
5
199
0
5
199
1
5
199
1
5
35
1
5
199
1
5
196
5
5
196
5
5
196
5
5
196
5
5
196
5
5
196
5
5
199
1
5
199
1
5
196
5
5
196
5
5
196
5
5
196
5
5
196
5
5
35
1
5
199
1
5
199
1
5
199
0
5
199
1
4
4
0
478094
0
16993
0
873878439
0
0
200
0
5
196
5
5
199
0
5
35
1
5
199
1
5
199
1
5
196
5
5
199
1
5
196
4
4
1
2
topic-owner
199
1
5
196
5
5
35
1
5
196
5
5
196
5
5
196
5
5
196
5
5
196
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1904
0
913134427
200
1
#197
walking utils

16
35
-1
-1
-1
156
-1
-1
9
find_path
35
173
-1
walkto walkto_blocking
35
173
-1
move_by_exits
35
173
-1
dijkstra
199
173
-1
decode_space
35
173
-1
_display_crooms
35
173
-1
find_path_and_cost
35
173
-1
bfs
199
173
-1
nominate_for_core
199
173
-1
0
15
5
35
5
5
35
4
4
1
2
walking utils
35
5
4
17
2
A set of utility verbs related to walking.
2
interface as follows:
2
make foo walk from room it's in to some other room:
2

2
   $walking_utils:walkto(foo, whereto)
2

2
foo is expected to define:
2
   foo.walking_task  - current taskid of walking task (ew, kinda gross)
2
   foo:move_by_exits(path, dest)
2
     (this can just call $walking_utils:move_by_exits(who, path, dest) if it wishes)
2
      this is called to move by exits along path to dest
2
   foo:begin_walking(path, dest)
2
      this is called just before walking begins
2
   foo:end_walking(dest)
2
      this gets called when you stop walking for any reason
2
   foo:penultimate_step_in_walking(dest)
2
      this is called just before the last exit is transited
35
5
5
35
1
5
35
5
5
35
5
5
35
5
5
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
8373
0
913134427
200
1
#198
syslog

16
199
-1
-1
-1
1
-1
204
8
log
199
173
-1
can_write
199
173
-1
_log
199
173
-1
watch unwatch ignore
199
153
3
can_read
199
173
-1
announce
199
173
-1
read
199
153
3
init_for_core
199
173
-1
9
names
_startup_log
_checkpoint_log
_startup_size
_checkpoint_size
_watchers_startup
_watchers_checkpoint
_checkpoint_watchers
_startup_watchers
23
4
2
4
2
2
startup
4
1
4
2
2
caller
1
209
4
2
2
checkpoint
4
1
4
2
2
caller
1
189
199
1
4
0
199
0
4
0
199
0
0
50
199
5
0
50
199
5
4
0
199
5
4
0
199
5
4
0
199
5
4
0
199
5
0
0
199
4
4
1
2
syslog
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5574
0
913134427
200
1
#199
Core-Wizard

7
199
15
-1
190
51
-1
-1
1
init_for_core
199
173
-1
0
115
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
4
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
199
0
5
199
5
5
199
0
0
784166604
199
1
0
0
199
5
5
35
1
5
35
1
5
35
0
5
35
1
5
199
5
4
25
1
199
1
22
1
1
1
0
1
4
1
52
1
7
1
51
1
64
1
15
1
12
1
5
1
9
1
11
1
10
1
66
1
24
1
47
1
48
1
50
1
19
1
18
1
3
1
6
1
31
200
1
5
35
1
5
199
4
5
199
0
5
199
0
5
199
5
5
199
4
5
199
4
5
199
4
5
199
5
5
199
5
5
199
5
5
199
5
2
The %t isn't a real person.  You may want to mail an admin group instead.
199
5
5
199
5
5
199
5
4
2
0
0
0
0
199
4
5
199
4
0
2147483647
199
1
0
10001
200
0
5
199
5
1
70
35
1
5
199
0
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
199
1
5
199
1
5
35
1
5
35
1
5
199
5
5
199
5
5
199
1
5
199
1
5
199
5
5
199
0
5
199
5
5
35
1
5
35
1
5
199
5
4
0
35
1
5
199
0
5
199
0
5
199
0
5
199
1
5
199
1
5
35
1
5
199
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
1
5
199
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
199
1
5
199
1
5
199
0
5
199
1
4
4
0
100512138
0
40599
0
873878439
0
0
200
0
5
199
5
5
199
0
5
35
1
5
199
1
5
199
1
5
199
5
5
199
1
5
199
4
4
4
2
Core-Wizard
2
core_wizard
2
$core_wizard
2
Core
199
1
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2769
0
913134427
200
1
#200
Quota

1
200
15
-1
-1
6
-1
31
1
nominate_for_core
200
173
-1
0
101
5
35
1
5
199
0
5
200
5
2

199
0
0
785307223
199
1
0
0
200
5
5
35
1
5
35
1
5
35
0
5
35
1
5
200
5
4
4
1
200
1
188
1
90
1
119
200
1
5
35
1
5
200
4
5
199
0
5
199
0
5
200
5
5
200
4
5
200
4
5
200
4
5
200
5
5
200
5
5
200
5
5
200
5
2
The %t isn't a real person.  You may want to mail an admin group instead.
200
5
5
200
5
5
200
5
4
2
0
0
0
0
200
4
5
200
4
0
2147483647
199
1
0
-3
200
0
5
200
5
1
70
35
1
5
199
0
5
200
5
5
200
5
5
200
5
5
200
5
5
35
1
5
199
1
5
199
1
5
35
1
5
35
1
5
200
5
5
200
5
5
199
1
5
199
1
5
200
5
5
199
0
5
200
5
5
35
1
5
35
1
5
200
5
4
0
35
1
5
199
0
5
199
0
5
199
0
5
199
1
5
199
1
5
35
1
5
199
1
5
200
5
5
200
5
5
200
5
5
200
5
5
200
5
5
200
5
5
199
1
5
199
1
5
200
5
5
200
5
5
200
5
5
200
5
5
200
5
5
35
1
5
199
1
5
199
1
5
199
0
5
199
1
4
4
0
100000
0
41401
0
873878439
0
0
200
0
5
200
5
5
199
0
5
35
1
5
199
1
5
199
1
5
200
5
5
199
1
5
200
4
4
1
2
Quota
199
1
5
200
5
5
35
1
5
200
5
5
200
5
5
200
5
5
200
5
5
200
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2228
0
913134427
200
1
#201
MCP help

16
199
-1
-1
-1
30
-1
202
0
13
mcp
mcp-authentication
mcp-display-url
mcp-edit
mcp-ftp
mcp-goals
mcp-gopher
mcp-requests
mcp-semantics
mcp-spec
mcp-syntax
mcp-x-audio
mcp-index
28
4
11
2
MCP
2
1==
2

2
Rewritten from `help mcp-spec' June 7, 1994 by Jay Carlson.  Comments solicited; send mail to the `@proj usability client MCP' mailing list, moo-cows, or private email to j@io.com.
2

2
The MOO Client Protocol is a standard for transmitting structured "requests" between MUD clients and servers.  The original specification is available as `help mcp-spec', but the skeleton of a rewrite is here:
2

2
mcp-goals -- History and goals of the MCP protocol
2
mcp-syntax -- Syntactic description of MCP requests
2
mcp-semantics -- Semantics of MCP requests
2
mcp-requests -- Documented request types
199
5
4
15
2
MCP authentication
2
1=================
2

2
When an MCP client connects to an MCP server, it may set an `authentication key' for the duration of the connection.  The key is simply a string (in the format described in `help mcp-syntax') that must be sent by the server with every MCP request for the request to be authenticated.  The authentication key is a secret shared by the client and the MCP subsystem of the server.
2

2
The syntax for setting the authentication key is:
2

2
  #$#authentication-key <auth-key>
2
 
2

2
Clients may accept, reject, or query the user about MCP requests that are either missing an authentication key or have the incorrect key.
2

2
Typically, clients will generate a random number or string on connection and use it as their key.
2

2
Proposal: eliminate reference to client and server, and make the authentication scheme symmetric.
199
5
4
22
2
MCP display-url request
2
1======================
2

2
This is a simplification of the display-url request specification used by AstroVR.  It is intended to represent baseline functionality that most clients can implement.
2

2
display-url presents a document specified by a URL to the user.
2

2
Keywords
2
2=======
2

2
url:
2
3===
2

2
Required.  Must be an absolute URL, starting with a name scheme (such as "http:").
2

2
Example
2
2======
2

2
Present the JHM home page:
2

2
  #$#display-url 12345678 url: "http://jhm.ccs.neu.edu:7043/"
2
 
199
5
4
47
2
MCP edit* requests
2
1=================
2

2
A multi-line request for the client to invoke a local editor on the attached text, and later send the edited text to the server.
2

2
Keywords
2
2=======
2

2
type:
2
3====
2

2
Optional.  Suggested values include program, text, string, value and mail.  The client may be able to use this information to provide a specialized editing mode.
2

2
name:
2
3====
2

2
Optional.  A human-readable descriptive name for what's being edited.
2

2
In existing Emacs clients, this is used for the buffer name.  In tkmoo, name: is used as the window and icon title for the workspace editing the text.
2

2
upload:
2
3======
2

2
Required.  Specifies a line to prefix the edited text with before sending to the server.  When the text is sent to the server, it is followed by a line of a single period.
2

2
Notes
2
2====
2

2
Typically the upload: value will be a command that starts reading input from the player, like @paste or @qsend; the MOO will use $command_utils:read_lines() to consume the uploaded lines.
2

2
Proposal: Every known use of #$#edit requires "." as its final line sent back to the server.  Worse, it's not defined that dot-quoting should be performed on the uploaded text, which $command_utils:read_lines() expects.  We should either explicitly state these things, or we should use multi-line MCP requests to send the text back to the server.
2

2
Examples
2
2=======
2

2
A request to provide an editing session for a property:
2

2
  #$#edit* 12345678 type: text name: Dave.test upload: "@set-note-text #75.test"
2
  @@@This is a test.
2
  @@@
2
  #$#END
2

2
A request to provide an editing session for a verb:
2

2
  #$#edit* 12345678 type: program name: Dave:test upload: "@program #75:test none none none"
2
  @@@player:tell("This is a test.");
2
  #$#END
199
5
4
42
2
MCP ftp request
2
1==============
2

2
I don't understand the semantics of the ftp request fully.  AstroVR uses this protocol to move remote files into users' directories.
2

2
This specification does not specify whether anonymous ftp or authenticated ftp is used.
2

2
Keywords
2
2=======
2

2
host:
2
3====
2

2
Required.  Name of the FTP host containing the document.
2

2
directory:
2
3=========
2

2
Required.  The directory containing the document.
2

2
file:
2
3====
2

2
Required.  The filename of the document.
2

2
type:
2
3====
2

2
Required.  File type of the document.  Possible values are "ascii", "binary", "fits", "plaintext", "postscript", "compressed", and "tar".
2

2
destination:
2
3===========
2

2
Required.  Directory on the local machine the document is to be placed in.
2

2
Example
2
2======
2

2
Upload a file from an anonymous ftp site:
2

2
  #$#ftp 12345678 host: ipac.caltech.edu directory: /pub/deposit file: mud.el.Z type: "compressed ascii" destination: clients/mud.el.Z
2
 
199
5
4
12
2
MCP goals
2
1========
2

2
Most MUD clients and servers communicate by an informal protocol: lines of printable ASCII characters separated by newlines.  This is closely related to the TELNET protocol, which will in fact interoperate with MUD servers.  But TELNET clients don't provide user interface support for even the simplest of MUD tasks.  Specialized MUD clients provided these tools.
2

2
Once people started to use clients, several realized that intelligence and user interface closer to the user could be exploited by the MUD.  
2

2
In particular, the LambdaCore editor system allows an option to ship whole documents (to a mud.el variant) to be edited locally, then returned.  The $gopher system can direct clients to fetch Gopher text directly (presenting a better UI and avoiding funneling texts through the MOO).
2

2
Such "requests" are out of band; that is, they can occur at any point in the interaction between client and server.  In particular, client requests may occur while a MOO is using the read() primitive.  The MOO server has a compile-time option to process lines starting with a given prefix as if they were truly out of band.
2

2
MCP is a simple protocol for transmitting structured messages between client and server.  It traffics in "requests", which have types, properties, and optional multi-line extensions.  Standard requests exist for local editing, URL display and gopher access.
199
5
4
39
2
MCP gopher request
2
1=================
2

2
The original MOO $gopher system used a private protocol.  This is a rendering of it into MCP.  It has become deprecated in favor of display-url.
2

2
Keywords
2
2=======
2

2
host:
2
3====
2

2
Required.  Name of the gopher host to connect to.
2

2
port:
2
3====
2

2
Optional.  Port number to connect to on gopher host; defaults to 70.
2

2
path:
2
3====
2

2
Optional.  Pathname to send to the gopher host.  Defaults to the empty string.
2

2
description:
2
3===========
2

2
Required.  Description string; first character is the gopher type character, rest of characters are the human-readable name of the object.
2

2
Proposal: separate type from description.
2

2
Example
2
2======
2

2
A request to retrive Gustavus Adolphus College's root menu:
2

2
  #$#gopher 12345678 host: gopher.gac.edu description: "1Gustavus Adolphus College"
2
 
2

2
Note that port and path default to 70 and "" respectively.
199
5
4
9
2
MCP requests
2
1===========
2

2
There are several standard MCP request types.  If clients provide functionality similar to those described below, they should implement the MCP request as documented.
2

2
mcp-edit -- Perform local editing on a text
2
mcp-gopher -- Display a gopher text to the user
2
mcp-ftp -- Use ftp to download a file to the user's machine
2
mcp-display-url -- Display a URL to the user
199
5
4
19
2
MCP request semantics
2
1====================
2

2
An MCP request is of the general form:
2

2
  #$#<request-name>[*][ <auth-key>][ <keyword>: <value>]...
2
 
2

2
<request-name> identifies the `type' of the request.  The type of the request can be thought of as a function that processes the structures in the rest of the line.
2

2
<auth-key> is an optional authentication key.  See `help mcp-authentication'.
2

2
The set of <keyword>: <value> pairs are a function mapping <keyword>s onto <value>s.  <keyword>s are names of arguments to the request; <value>s are the corresponding values of arguments.
2

2
Duplicate <keyword>s are not allowed.  Arbitrary order of <keyword>: <value> pairs is allowed.
2

2
Specific <keyword>s may be optional for a given request type, with default <value>s assumed.
2

2
<keyword>s may be unrecognized by a request type.  A suggested strategy for request handlers is to ignore any <keyword>s they don't understand.
199
5
4
131
2
MOO CLIENT REQUEST PROTOCOL
2
===========================
2

2
Erik Ostrom, Dave Van Buren, Pavel Curtis, David Nichols, Jay Carlson
2

2
22 Feb 1993
2

2
Conversion to `help help-format' 21 May 1994 by Jay Carlson.
2

2
(Any first-person pronouns refer to Erik.)
2

2
AUTHENTICATION
2
--------------
2

2
An authentication key is generated and uploaded by the client to the server at login.  Proposed method:
2

2
  #$#authentication-key <key>
2
 
2

2
This requires that the server be compiled with OUT_OF_BAND_PREFIX set to "#$#".  (It's actually possible to do it as a verb on $player, but many MOOs have a programmer command "#" which does something very different.)  The authentication key may not contain spaces, double-quotes, colons, or backslashes.
2

2
GENERAL RULES
2
-------------
2

2
String sent by server:
2

2
  #$#<request-name><*-if-data-follows> [<auth-key>] <keyword>: <value> ...
2
 
2

2
An identifying tag #$# is placed at the beginning of the request line.
2

2
  o  <request-name> is the type of request, e.g. "edit" or "ftp".
2
  o  <request-name> cannot contain spaces, double-quotes, backslashes, or asterisks.
2
  o  <*-if-data-follows> is either "*" if this request is followed by additional lines of data, or the empty string.
2
  o  <keyword> is the name of an argument to the request.  Keywords cannot contain spaces, double-quotes, colons, or backslashes.
2
  o  <value> is the value of the corresponding argument.  A <value> can contain any printing ASCII character (including space), but must be quoted in the standard MOO style (with double quotes, using backslash to escape quotes and backslashes) if it either contains spaces or is the empty string.  Arbitrary order of keyword: value pairs is allowed.
2

2
Keyword: value pairs may be optional.  We haven't discussed what clients should do with unrecognized keywords; my plan is for the client simply to ignore them and work with the keywords it recognizes.
2

2
REQUEST DEFINITIONS
2
-------------------
2

2
edit
2
====
2

2
type: <program|text|string|value|mail|...>
2

2
Type of information.  Clients may or may not be able to use a special editing mode depending on type.
2

2
name: <name>
2

2
User-friendly indication of what's in this.  Good for buffer name.
2

2
upload: <command>
2

2
Command to use to begin upload of this text.
2

2
ftp
2
===
2

2
host: <name>
2

2
Name of the host that has the document.
2

2
directory: <dir>
2

2
Name of the directory that the document is in.
2

2
file: <name>
2

2
Name of the file within the directory.
2

2
type: <type>
2

2
Type of document.  Current types include ($ftp.recognized_types):
2

2
      {"ascii", "binary", "fits", "plaintext", "postscript",
2
       "compressed", "tar"}
2

2
destination: <dir>
2

2
Place to store the document locally.  I don't fully understand this.
2

2
gopher
2
======
2

2
(Larry has a local-gopher protocol--this is my attempt to render it into the New Protocol.)
2

2
host: <name>
2

2
Name of the host to connect to.
2

2
port: <number>
2

2
Port to connect to.  Default is 70.
2

2
path: <string>
2

2
String to send to the gopher server.  Default is the empty string.
2

2
description:
2

2
Description string.  First character is the type character, rest of characters are human-readable name of object.  This seems unnecessarily arcane to me--why not separate these?
2

2
EXAMPLES
2
--------
2

2
A request to provide an editing session for a property.  The request is followed by lines of data, prepended with a special tag (@@@, unless someone has strong feelings about changing it), and marked at the end with another tag (#$#END, again subject to change).  The advantage of this over dot-quoting is that it should be fairly easy for the client to figure out if there's been some breakage on the server end and the transmission has terminated early.  (A line that doesn't start with @@@ and isn't #$#END should set off warning bells.)
2

2
  #$#edit* 12345678 type: text name: Dave.test upload: "@set-note-text #75.test"
2
  @@@This is a test.
2
  @@@
2
  #$#END
2

2
A request to provide an editing session for a verb.
2

2
  #$#edit* 12345678 type: program name: Dave:test upload: "@program #75:test none none none"
2
  @@@player:tell("This is a test.");
2
  #$#END
2

2
A request to upload a file from an anonymous ftp site.  The request line contains all the data necessary.
2

2
  #$#ftp 12345678 host: ipac.caltech.edu directory: /pub/deposit file: mud.el.Z type: "compressed ascii" destination: clients/mud.el.Z
2
 
2

2
A request to retrieve a document from a gopher server.
2

2
  #$#gopher 12345678 host: gopher.gac.edu description: "1Gustavus Adolphus College"
2
 
2

2
In this case, we take advantage of default values for the port: and path: arguments ("70" and "", respectively).
199
5
4
34
2
MCP request syntax
2
1=================
2

2
Input and output to and from client and server is presumed to be divided into records (called `lines' hereafter).
2

2
An MCP request is of the general form:
2

2
  #$#<request-name>[*][ <auth-key>][ <keyword>: <value>]...
2
 
2

2
"#$#" must be the first three characters in the line.
2

2
<request-name> is a string of one or more printable ASCII characters.  It may not contain spaces, double-quotes, backslashes or asterisks.
2

2
Proposal: require <request-name> to start with a letter.  This reserves a substantial part of the namespace for private out-of-band protocols, or other protocol extensions.
2

2
<auth-key> is a string of one or more printable ASCII characters.  It may not contain spaces, double-quotes, colons or backslashes.
2

2
<keyword> is a string of one or more printable ASCII characters.  It may not contain spaces, double-quotes, colons, or backslashes.
2

2
<value> is 
2

2
  o  a string of one or more printable ASCII characters not containing spaces, double-quotes or backslashes 
2
  o  OR is a string of zero or more printable ASCII characters, enclosed in double-quotes, with double-quotes and backslashes inside the string `quoted' by preceding them with a backslash.
2

2
Note that the empty string must be quoted.  Also, the ASCII TAB character is not allowed in <value>s, at all.
2

2
Proposal: disallow unquoted <value>s with colons in them.  This is not strictly necessary, but it could detect some bugs in MCP request generators.
2

2
If the asterisk after <request-name> is present, this line starts a `multi-line request'.  Zero or more lines of data associated with the request follow, prepended with "@@@".  These lines are terminated with a single line of the form "#$#END".
2

2
Note that the 1.7.7 LambdaMOO server cannot properly handle multi-line requests sent by a client if OUT_OF_BAND_PREFIX is defined as "#$#".
2

2
Proposal: multi-line requests, in their current form, should be deprecated in favor of some other construct providing this functionality.
199
5
4
30
2
MCP x-audio request
2
1==================
2

2
This is a description of the interim protocol tkmoo uses to provide a single channel of multicast audio.  New requests change the channel.
2

2
Keywords
2
2=======
2

2
my-name
2
3======
2

2
Optional.  Name to identify the user inside a multicast conference.  Defaults to username.
2

2
key
2
3==
2

2
Optional.  Encryption key for the channel.  Defaults to "", which is no key.
2

2
address
2
3======
2

2
Required.  Multicast address 
2

2
ttl
2

2

2
port
2

2
title
2

199
5
4
2
2
*index*
2
MCP help topics
199
5
4
0
35
1
5
199
4
4
1
2
MCP help
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
19411
0
913134427
200
1
#202
tkmoo help

0
199
-1
-1
-1
30
-1
-1
0
8
tkmoo
tkmoo-goals
tkmoo-installation
tkmoo-internals
tkmoo-setup
tkmoo-usage
tkmoo-versions
tkmoo-index
23
4
11
2
tkmoo
2
1====
2

2
tkmoo is a MOO client for the X window system, written in Tcl and using the Tk widget kit.  The tkmoo documentation is divided into the following sections:
2

2
tkmoo-goals -- What's the point?
2
tkmoo-installation -- How to get tkmoo built on your system
2
tkmoo-setup -- How to set up and configure tkmoo
2
tkmoo-usage -- Using tkmoo
2
tkmoo-internals -- Extending tkmoo 
2
tkmoo-versions -- Release history
199
5
4
12
2
Goals of tkmoo
2
1=============
2

2
Early in January of 1993, the jtext system (see `help jtext-intro') was getting off the ground.  From the beginning, we were planning to build clients that could display richtext---text with attributes such as boldfacing, links and headers.  
2

2
At the time, emacs couldn't do this very well, and I didn't want to relearn elisp.  I found the Tk kit's excellent text widget, which seemed a good fit to what I wanted to do.
2

2
tkmoo was originally my squash court for playing with jtext, and that continues to be its primary charter.  The future direction of this part has changed from the display of styled text (which mostly works) to display of documents, however.
2

2
tkmoo is also a testbed for MCP, the MOO Client Protocol (see `help mcp').
2

2
Finally, tkmoo is a vehicle for experimenting with GUIs for other MOO resources.  Other Tk applications can interact with the MOO session.  I have blue-sky plans to implement MOO building tools in a direct manipulation style rather than the clunky TinyMUD-style @-commands.
199
5
4
22
2
Installing tkmoo
2
1===============
2

2
This document assumes familiarity with Unix software installation tools.
2

2
Usually, the latest release of tkmoo will be available at ftp://ftp.ccs.neu.edu/pub/mud/clients/tkmoo/tkmoo-0.4.1.tar.gz, but this is subject to change.
2

2
Tcl and Tk
2
2=========
2

2
tkmoo is written in Tcl, a simple language designed to be incorporated in applications.  The most important extension to Tcl is Tk, an excellent X toolkit.  They're available at ftp://harbor.ecn.purdue.edu/pub/tcl/sprite-mirror/.  I use Tcl 7.3 and Tk 3.6.1, but any Tcl version past 7.0 and any Tk version past 3.3 should work.  You need to have them installed before you can start building tkmoo.
2

2
Building tkmoo itself
2
2====================
2

2
tkmoo is a Tk/Tcl script that runs in a modified interpreter called moowish, which provides some low-level support for interacting with processes.  The script uses a library of other scripts and resource files.
2

2
Once you've unpacked the tkmoo archive, you need to modify the Makefile to specify a number of file paths.  Most of the comments should be self-explanatory.  The trickiest part is specifying where your local X, Tcl and Tk libraries and include files are; be careful to get this right.  Also make sure that the TKMOO_LIB_DIR, TKMOO_BIN_DIR and TKMOO_SCRIPT_DIR directories actually exist.
2

2
You should be able to run `make' at this point.  If it doesn't work, you're probably on your own.
2

2
`make install' will place the tkmoo files in the directories you've specified.
199
5
4
30
2
tkmoo internals
2
1==============
2

2
A warning: I can, have and will drastically change how tkmoo works.  You shouldn't expect your code to work from release to release especially if you haven't talked to me lately.  The rest of this document describes how tkmoo works as of version 0.4, and may interest you, but it's primarily so I can think out loud.
2

2
MCP
2
---
2

2
tkmoo correctly parses all MCP requests (see `help mcp').  When a request is received, the keywords and values are placed in a global array MCPParam, and a tcl procedure named mcp_request_request-name is called (where request-name is taken from the request itself).
2

2
mcp_request procedures can use the function mcp_authenticate, which returns true if the current request has properly authenticated.
2

2
If the request had text associated with it (was #$$foo*), the lines are available in MCPParam(_lines).
2

2
Remote control
2
2-------------
2

2
Other Tk applications on the same X display can request that a tkmoo process send a command to the MOO via Tk's send function.
2

2
tkmoo responds to this protocol:
2

2
  remoteCommand <this application's name> 
2
                <name of function to call upon completion>
2
                <tcl value to include as "client data">
2
                <line of text to send as command>
2

2
The tkmoo process uses the MOO PREFIX/SUFFIX directives to collect the results of the command, and calls back when the command is complete:
2

2
  <callback name> <client-data> <list of lines of text>
2
 
199
5
4
20
2
Setting up tkmoo
2
1===============
2

2
Most user-configurable options in tkmoo are controlled by X resources.  When tkmoo starts up, it reads its app defaults file ($TKMOO_LIB_DIR/tkmoo.res) and the per-user configuration file ($HOME/.tkmoorc).  I suggest you look at tkmoo.res to see what resources are available, and make your changes in your .tkmoorc.
2

2
MUD definitions
2
2==============
2

2
The Tk.hostList resource contains the definitions of how to connect to MUD `worlds'.  The resource is a Tcl list of pairs; the first element of the pair is the name to be displayed in the Session menu; the second element is the shell command to connect.  The second element can be any valid command, so if you need to go through a firewall, you can put your modified telnet command there:
2

2
Tk.hostList:  {\
2
    {JaysHouseMOO {termtelnet jhm.ccs.neu.edu 1709}} \
2
    {LambdaMOO {termtelnet lambda.xerox.com 8888}} \
2
    {Any {trsh}} \
2
}
2

2
Fonts
2
2====
2

2
Some people really don't like the default, proportional font that tkmoo uses by default for its interaction window.  You can override this in your .tkmoorc, but right now I'm too lazy to write about this other than to refer you to the tkmoo.res file for the available resources.
199
5
4
85
2
Using tkmoo
2
1==========
2

2
tkmoo takes no command line arguments.
2

2
The Interaction window
2
2=====================
2

2
tkmoo's main window is used for typescript interaction with the MUD.  The majority of the window is the typescript window proper, and its scrollbar.  At the top of the main window is a menu bar; at the bottom is the `minibuffer', which evaluates tcl commands typed in it.
2

2
The Session menu
2
================
2

2
The Session menu contains entries for the MUD world definitions you have (see `help tkmoo-setup'), and a Quit item.  
2

2
The Edit menu
2
=============
2

2
The Edit menu has commands for manipulating the X selection.  Insert Selection places it into the interaction window.  Paste Selection sends it to the connection.  Workspace From Selection opens a new workspace window containing the selection, ready to be edited and sent to the connection.
2

2
The Remote menu
2
===============
2

2
The Remote menu needs to be renamed.  Right now, it's a dumping ground for new menu items.
2

2
Edit Verb uses a special feature object on JHM to do local editing of verbs.  It's obsolete.
2

2
Fetch Command Output executes a single command, and places the output of that command in a new Workspace window.  It uses the MOO server's PREFIX/SUFFIX directives, and it does not account for the command suspending.
2

2
Send Auth Key generates an MCP authentication key (see `help mcp') and sends it to the MOO.  In general, this allows the MOO to send MCP requests to tkmoo for the life of your connection.
2

2
Text in the interaction window
2
==============================
2

2
The interaction window displays both flat text sent to it, and tkmoo tagged text lines (see `help jtext-forms').  On JHM, you can request that jtext (see `help jtext-intro') be sent to your client in tkmoo tagged text form:
2

2
  @set-jtext-form tkmoo
2
 
2

2
or in the standard `linemode' form:
2

2
  @set-jtext-form linemode
2
 
2

2
In tkmoo tagged text, links are rendered in raised relief, and in white on a color display; the third mouse button follows those links using an undocumented MCP request.
2

2
Workspace windows
2
2----------------
2

2
Local editing in tkmoo is done through Workspace windows.  You can have many Workspaces on your screen; they are independent, although associated with the tkmoo you started them from.
2

2
Most of a Workspace window is a text widget and its associated scrollbar.  At the bottom are a minibuffer and two buttons.  The Send button sends the contents of the window to the MOO; the Close button removes the Workspace.
2

2
Key bindings
2
2-----------
2

2
The interaction window and workspace windows share a set of emacs-like key bindings in addition to the mouse and arrow key bindings provided by Tk.
2

2
  o  Control-a moves to the beginning of a line
2
  o  Control-e moves to the end of a line
2
  o  Control-p moves the cursor up a line
2
  o  Control-n moves the cursor down a line
2
  o  Control-d deletes the character after the cursor
2
  o  Control-k deletes to the end of the line, and sets the `kill buffer' to what was deleted
2
  o  Control-y inserts the latest contents of the `kill buffer'
2
  o  Meta-< moves to the start of the whole text
2
  o  Meta-> moves to the end of the whole text
2
  o  Meta-f moves forward a word
2
  o  Meta-b moves backwards a word
2
  o  Meta-d deletes the word to the right of the cursor
2
  o  Meta-Delete deletes the word to the left of the cursor
2
  o  Meta-x moves the cursor to the `minibuffer' associated with the window
2
  o  Meta-y inserts the X selection at the cursor
2

2
There are a few bindings that don't quite work:
2

2
  o  Control-v mostly moves down a page.
2
  o  Meta-v mostly moves up a page.
2

2
MCP
2
---
2

2
Although tkmoo has an easily extensible MCP handler (see `help tkmoo-internals'), it only responds to two MCP requests: #$#display-url and #$#edit.  #$#edit uses a workspace to edit the text sent by the MOO.
2

2
When tkmoo receives a request to display a URL, it will attempt to launch Mosaic in such a way it can use remote control to show documents for later requests.
199
5
4
9
2
TKMOO VERSION HISTORY
2
=====================
2

2
VERSION 0.4.0
2
-------------
2

2
Unreleased.  
2

2
Started this version log May 21, 1994.
199
5
4
2
2
*index*
2
tkmoo help topics
199
5
4
0
35
1
5
199
4
4
1
2
tkmoo help
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
12020
0
913134427
200
1
#203
generic sink

144
35
-1
-1
-1
210
205
-1
6
receive_line
35
173
-1
parse_commandline
35
173
-1
abnormal_termination
35
173
-1
finish
35
173
-1
receive_warning
35
173
-1
receive_lines
35
173
-1
0
16
5
35
1
5
35
1
5
35
4
4
2
2
generic sink
2
sink
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3077
0
913134427
200
1
#204
generic listener

16
199
-1
-1
-1
1
143
206
8
do_login_command
199
173
-1
user_disconnected user_client_disconnected
199
173
-1
user_connected user_created
199
173
-1
service_start
199
173
-1
service_stop
199
173
-1
allow_connection
199
173
-1
do_command
199
173
-1
listening_ports
199
173
-1
1
port
15
0
0
199
5
5
199
4
4
1
2
generic listener
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3816
0
913134427
200
1
#205
generic gather-then-spam sink

144
35
-1
-1
-1
203
195
-1
3
receive_line
35
173
-1
receive_lines
35
173
-1
get_lines
35
173
-1
1
lines
17
4
0
35
0
5
35
1
5
35
1
5
35
4
4
4
2
generic gather-then-spam sink
2
gather-then-spam sink
2
gtss
2
ggtss
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1977
0
913134427
200
1
#206
generic daemon service handler

16
35
-1
-1
-1
1
-1
75
11
initialize
35
173
-1
abort timeout
199
173
-1
connected
35
173
-1
check_timeout
35
173
-1
disconnected
35
173
-1
notify
199
165
-1
notify_safe
199
173
-1
close close_safe
199
173
-1
read
199
173
-1
notify_lines
199
173
-1
notify_lines_safe
199
173
-1
2
daemon
timeout
16
1
-1
35
1
0
0
35
1
5
35
4
4
1
2
generic daemon service handler
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5163
0
913134427
200
1
#207
server options

16
199
-1
-1
-1
1
-1
141
1
set_message
199
165
-1
17
fg_ticks
bg_ticks
support_numeric_verbname_strings
boot_msg
timeout_msg
recycle_msg
redirect_from_msg
redirect_to_msg
connect_msg
create_msg
permit_writable_verbs
protect_set_verb_info
protect_add_verb
protect_verb_code
protect_set_verb_code
protect_recycle
protect_chparent
31
0
300000
199
1
0
100000
199
1
0
1
199
5
2
*** Disconnected ***
199
5
2
*** Timed-out waiting for login. ***
199
5
2
*** Recycled ***
199
5
2
*** Redirecting connection to new port ***
199
5
2
*** Redirecting old connection to this port ***
199
5
2
*** Connected ***
199
5
2
*** Created ***
199
5
0
0
199
1
0
1
199
1
0
1
199
1
0
1
199
1
0
1
199
1
0
0
199
1
0
0
199
1
5
199
4
4
3
2
server options
2
$server_options
2
options
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2403
0
913134427
200
1
#208
core feature objects

16
199
-1
-1
-1
82
-1
-1
0
2
obj_stage_talk
obj_thinking
43
1
67
199
1
1
68
199
1
5
35
1
4
2
1
67
1
68
35
1
4
2
2
stage_talk
2
thinking
35
1
5
35
1
5
199
5
5
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
199
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
4
4
1
2
core feature objects
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
946
0
913134427
200
1
#209
startup handler

16
199
-1
-1
-1
1
-1
189
8
schedule_checkpoint
199
173
-1
start_services
199
173
-1
server_started
199
173
-1
add_service
199
173
-1
remove_service
199
173
-1
set_services
199
173
-1
log
199
173
-1
init_for_core
199
173
-1
1
services
15
4
0
199
1
0
0
199
4
4
1
2
startup handler
199
5
4
5
2
startup handler
2
---------------
2

2
#0 calls this on server startup.  It is responsible for starting all of the services in $startup.services and telling $checkpointer that the server just started (so it can schedule the checkpoint and all).
2

199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
4403
0
913134427
200
1
#210
generic source or sink

144
35
-1
-1
-1
1
126
89
3
new
199
173
-1
setup
35
173
-1
set_module set_creator
35
173
-1
2
module
creator
16
1
-1
35
1
1
-1
35
1
5
35
4
4
1
2
generic source or sink
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2580
0
913134427
200
1
#211
module system module

0
199
-1
-1
-1
82
-1
187
4
dump
199
105
1
dump_dump
199
165
-1
dumped_ref
199
173
-1
first_install
199
173
-1
10
obj_sink_player
obj_sink_note
obj_source_generic
obj_source_http
obj_source_player
obj_sink_generic_spam
obj_sink_generic
obj_generic_module
obj_generic_source_or_sink
obj_registry
51
1
195
199
1
1
140
199
1
1
126
199
1
1
219
199
1
1
223
199
1
1
205
199
1
1
203
199
1
1
-1
199
1
1
210
199
1
1
138
199
1
5
35
1
4
10
1
195
1
140
1
126
1
219
1
223
1
205
1
203
1
82
1
210
1
138
35
1
4
10
2
sink_player
2
sink_note
2
source_generic
2
source_http
2
source_player
2
sink_generic_spam
2
sink_generic
2
generic_module
2
generic_source_or_sink
2
registry
35
1
0
0
35
1
5
199
5
4
0
35
1
4
2
4
0
4
0
35
1
1
-1
35
1
4
8
1
210
1
195
1
140
1
126
1
219
1
223
1
205
1
203
35
1
5
35
1
5
35
1
5
35
1
5
199
1
4
5
4
2
1
1
2
$root_class
4
2
1
155
2
$containing_object
4
2
1
69
2
$pronoun_sub
4
2
1
5
2
$thing
4
2
1
222
2
$property_farm
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
4
4
1
2
module system module
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
8235
0
913134427
200
1
#212
simpleedit module

0
199
-1
-1
-1
82
-1
208
0
1
obj_simpleedit_package
42
1
221
199
1
5
35
1
4
1
1
221
35
1
4
1
2
simpleedit_package
35
1
5
35
1
5
199
5
5
35
1
5
35
1
5
35
1
4
1
1
221
35
1
5
35
1
5
35
1
5
35
1
5
199
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
4
5
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
876
0
913134427
200
1
#213
mcp-negotiate

16
199
-1
-1
-1
224
-1
98
3
do_negotiation
199
173
-1
handle_can
199
173
-1
handle_end
199
173
-1
0
18
4
2
2
1.0
2
2.0
35
1
5
35
1
4
2
4
2
2
can
4
3
2
package
2
min-version
2
max-version
4
2
2
end
4
0
199
1
4
2
4
2
2
can
4
3
2
package
2
min-version
2
max-version
4
2
2
end
4
0
199
1
5
199
4
4
1
2
mcp-negotiate
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2503
0
913134427
200
1
#214
MCP 2.1 module

0
199
-1
-1
-1
82
-1
212
0
11
obj_mcp
obj_mcp_parser
obj_mcp_session
obj_mcp_package
obj_mcp_negotiate
obj_mcp_registry
obj_mcp_cord
obj_cord
obj_cord_stub
obj_type_root
obj_message_dispatcher
52
1
157
199
1
1
217
199
1
1
216
199
1
1
224
199
1
1
213
199
1
1
218
199
1
1
98
199
1
1
215
199
1
1
220
199
1
1
91
199
1
1
179
199
1
5
35
1
4
11
1
157
1
217
1
216
1
224
1
213
1
218
1
98
1
215
1
220
1
91
1
179
35
1
4
11
2
mcp
2
mcp_parser
2
mcp_session
2
mcp_package
2
mcp_negotiate
2
mcp_registry
2
mcp_cord
2
cord
2
cord_stub
2
type_root
2
message_dispatcher
35
1
5
35
1
5
199
5
5
35
1
5
35
1
5
35
1
4
11
1
157
1
217
1
216
1
224
1
213
1
218
1
98
1
215
1
220
1
91
1
179
35
1
5
35
1
5
35
1
5
35
1
5
199
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
199
4
5
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
5
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
1593
0
913134427
200
1
#215
Cord Dispatcher

16
199
-1
-1
-1
1
-1
220
11
open
199
173
-1
lookup
199
173
-1
mcp_receive
199
173
-1
mcp_closed
199
173
-1
closed
199
173
-1
cleanup
199
165
-1
connection
199
173
-1
module_finish_unpack
199
173
-1
init_for_module
199
173
-1
nominate_for_core
199
173
-1
init_for_core
199
173
-1
5
root
next_id
registry
type_root
registry_ids
19
1
220
199
5
0
1
199
0
4
0
199
1
1
91
199
1
4
0
199
1
5
199
4
4
1
2
Cord Dispatcher
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
1
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
8117
0
913134427
200
1
#216
generic MCP 2.1 session

16
35
-1
-1
-1
1
-1
218
22
set_connection
35
165
-1
multiline_begin
35
173
-1
multiline_finish
35
173
-1
multiline_add_value
35
173
-1
do_out_of_band_command
199
173
-1
finish
35
173
-1
initialize_connection
35
173
-1
set_packages set_authentication_key set_phase
35
173
-1
add_package
35
173
-1
handles_package
35
173
-1
register_handlers
35
173
-1
dispatch
199
173
-1
send
199
173
-1
find_handler
35
173
-1
connection
35
173
-1
package_name
35
173
-1
message_fullname
35
173
-1
strip_prefix
35
173
-1
end_negotiation
35
173
-1
_add_package_waiter
35
173
-1
_signal_package_waiter
35
173
-1
wait_for_package
35
173
-1
7
connection
pending_multilines
packages
authentication_key
phase
package_waiters
message_handlers
21
1
-1
35
1
4
0
35
0
4
0
35
1
3
0
35
0
0
0
35
1
4
0
35
0
4
2
4
0
4
0
35
1
5
35
4
4
1
2
generic MCP 2.1 session
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
4
2
4
0
4
0
35
1
5
35
1
1
-1
199
1
4
2
0
14344
0
913134427
200
1
#217
MCP 2.1 parser

16
35
-1
-1
-1
1
-1
216
6
parse_mcp_alist
35
173
-1
parse_mcp
35
173
-1
parse_mcp_continuation
35
173
-1
parse
35
173
-1
unparse
35
173
-1
next_datakey
35
173
-1
2
next_datakey
unquoted_string
16
0
200
35
1
2
^[]a-zA-Z0-9-%~`!@#$^&()=+{}[|';?/><.,]+$
199
1
5
35
4
4
1
2
MCP 2.1 parser
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5467
0
913134427
200
1
#218
MCP package registry

16
35
-1
-1
-1
1
-1
215
8
add_package
35
173
-1
remove_package
35
173
-1
match_package
35
173
-1
package_name
35
173
-1
packages
35
173
-1
init_for_module init_for_core
35
173
-1
nominate_for_core
199
173
-1
@add-package @remove-package
199
153
1
3
package_names
packages
core_package_names
17
4
3
2
mcp-negotiate
2
mcp-cord
2
dns-org-mud-moo-simpleedit
35
1
4
3
1
213
1
98
1
221
35
1
4
3
2
mcp-negotiate
2
mcp-cord
2
dns-org-mud-moo-simpleedit
35
1
0
0
35
4
4
1
2
MCP package registry
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5872
0
913134427
200
1
#219
http source

144
35
-1
-1
-1
126
-1
223
3
parse_commandline
199
173
-1
fetch
199
173
-1
set_source
35
173
-1
1
source
20
1
-1
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
4
4
1
2
http source
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2240
0
913134427
200
1
#220
generic cord stub

144
199
-1
-1
-1
1
-1
179
4
recycle
199
165
-1
closed
199
173
-1
send
199
173
-1
close
199
173
-1
5
id
our_side
connection
type
session
19
0
0
199
0
1
-1
199
1
0
0
199
5
1
-1
199
1
1
-1
199
1
5
199
4
4
2
2
generic cord stub
2
gcs
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
1
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3111
0
913134427
200
1
#221
dns-org-mud-moo-simpleedit

16
199
-1
-1
-1
224
-1
-1
2
send_content
199
173
-1
handle_set
199
173
-1
0
18
4
2
2
1.0
2
1.0
35
1
5
35
1
4
1
4
2
2
set
4
3
2
reference
2
type
2
content
199
1
4
1
4
2
2
content
4
4
2
reference
2
name
2
type
2
content
199
1
5
199
4
4
1
2
dns-org-mud-moo-simpleedit
199
5
4
52
2
A very simple local editing protocol.
2

2
S->C #$#dns-org-mud-moo-simpleedit-content (reference, content*, name, type)
2

2
Reference is the tag used when sending back to the server.
2
User may be allowed to edit it (i.e., save this same text into a
2
different property), with possibility of disastrous results.
2

2
Content is the content interpreted according to the type
2
info given.  It's multiline (hence the *).
2

2
name is a human-readable name for the info, suitable for window title,
2
buffer name, etc.
2

2
type is one of the following (for version 1.0):
2
 * string
2
 * string-list
2
 * moo-code
2

2
clients that don't provide special support for moo-code editing can
2
treat moo-code identically to string-list.
2

2
C->S #$#dns-org-mud-moo-simpleedit-set (reference, content*, type)
2

2
reference, content, and type are as above.  This is the message sent by the client to set when the user 'saves' the value.  Note this does not necessarily save the value.  Errors such as lack of permission to set the given reference or moo-code compliation errors may prevent it.  It is expected that the server will tell the user this (in the in-band text stream).  
2

2
Clients will probably want to provide a way to just send without closing the window, buffer, etc for this reason.
2

2

2
JHCore implementation notes
2

2
JHCore currently understands several different kinds of (local) editing sessions:
2
handled by $verb_editor:
2
   * verb editing
2
handled by $note_editor:
2
   * list of strings editing for notes and properties
2
handled by both $note_editor and $list_editor(?):
2
   * value editing for properties
2
handled by $mail_editor:
2
   * sending a mail message
2

2
@edit uses a semi-complicated system to determine (a) what the user is trying to edit and (b) how to edit it.   
2

2
So it looks like the critical things to modify are $generic_editor:invoke_local_editor, $note_editor:local_editing_info, $verb_editor:local_editing_info, and $mail_editor:local_editing_info.  
2

2
The current return path for locally edited stuff (in the core) appears to be:
2
  @program
2
  @set-note-text
2
  @set-note-value
2
  @@sendmail
2

2

199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
4620
0
913134427
200
1
#222
generic property farm

16
199
-1
-1
-1
1
164
84
4
nominate_for_core
199
173
-1
core_objects
199
165
-1
module_finish_unpack
199
173
-1
init_for_core
199
173
-1
1
non_core_props
15
4
0
199
1
5
199
4
4
3
2
generic property farm
2
property farm
2
farm
199
5
5
199
5
5
35
1
5
199
5
5
199
5
5
199
5
0
0
199
5
5
199
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
3154
0
913134427
200
1
#223
read() player source

144
35
-1
-1
-1
126
-1
-1
3
parse_commandline
199
173
-1
fetch
199
173
-1
set_source
35
173
-1
1
source
20
1
-1
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
1
5
35
4
4
2
2
read() player source
2
me
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
2574
0
913134427
200
1
#224
generic MCP package

16
35
-1
-1
-1
179
213
91
9
set_version_range
35
165
-1
dispatch
199
173
-1
match_request
35
173
-1
initialize_connection
35
173
-1
message_name_to_verbname
35
173
-1
finalize_connection
35
173
-1
add_cord_type
35
173
-1
remove_cord_type
35
173
-1
send_*
35
173
-1
2
version_range
cord_types
18
4
2
2
1.0
2
1.0
35
1
4
0
35
1
5
199
1
5
199
1
5
35
4
4
1
2
generic MCP package
35
5
5
35
5
5
35
1
5
35
5
5
35
5
5
35
5
0
0
35
5
5
35
5
5
35
1
5
35
1
5
35
1
1
-1
199
1
4
2
0
5721
0
913134427
200
1
#0:0
"...This code should only be run as a server task...";
if (callers())
return E_PERM;
elseif ((conn = `connection_name(player) ! E_INVARG => ""') && $login:redlisted($string_utils:connection_hostname(conn)))
boot_player(player);
return 0;
else
if (typeof(h = $network:incoming_connection(player)) == OBJ)
return h;
elseif (h)
return;
endif
args = $login:parse_command(@args);
return $login:(args[1])(@listdelete(args, 1));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835984989, \"Erik\", #74, \"JHM\"}";
.
#0:1
if (callers())
return;
else
$last_restart_time = time();
return $startup:server_started();
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {865375525, \"Ken\", #75, \"JHM\"}";
.
#0:2
if ($__core_init_phase)
pass(@args);
$shutdown_message = "";
$shutdown_time = 0;
$dump_interval = 3600;
$gripe_recipients = {player};
this.description = "The Known Universe aka The, Known, Universe, and #0.";
`delete_verb(this, "do_command") ! E_VERBNF';
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895717681, \"Ken\", #75, \"JHM\"}";
.
#0:3
if (callers())
return E_PERM;
elseif (!valid(player))
return;
endif
return $mcp:(verb)(@args);
"Metadata 202106";
"Last-Modify: {895462327, \"Ken\", #75, \"JHM\"}";
.
#0:4
user = args[1];
if (callers())
raise(E_PERM);
endif
if (args[1] < #0)
return;
endif
$mcp:(verb)(@args);
fork (0)
`user:confunc() ! E_VERBNF';
endfork
fork (0)
$login_watcher:hello(user);
endfork
`user.location:confunc(user) ! E_VERBNF';
`user:anyconfunc() ! E_VERBNF';
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895261319, \"Ken\", #75, \"JHM\"}";
.
#0:5
if (callers())
return;
endif
user = args[1];
if (user < #0)
return $network:connection_closed(user);
elseif ($login:is_tester(user))
$login:tester_disconnecting(user);
endif
$mcp:(verb)(@args);
"...";
"... record disconnect time...";
user.last_disconnect_time = time();
user.total_connect_time = user.total_connect_time + (user.last_disconnect_time - user.last_connect_time);
if (user.started_keeping_total == 0)
user.started_keeping_total = user.last_connect_time;
endif
"...";
fork (0)
`user.location:disfunc(user) ! E_INVIND, E_VERBNF';
endfork
fork (0)
$login_watcher:goodbye(user);
endfork
`user:disfunc() ! E_VERBNF';
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895261287, \"Ken\", #75, \"JHM\"}";
.
#0:6
user = args[1];
if (user < #0)
return;
endif
$mcp:(verb)(@args);
fork (0)
`user.location:reconfunc(user) ! E_INVIND, E_VERBNF';
endfork
fork (0)
`user:reconfunc() ! E_VERBNF';
endfork
`user:anyconfunc() ! E_VERBNF';
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895261338, \"Ken\", #75, \"JHM\"}";
.
#0:7
"Usage:  :checkpoint_finished(success)";
"";
success = args[1];
if (callers())
return;
endif
$checkpointer:checkpoint_finished(success);
.
#0:8
"Usage:  :checkpoint_started()";
"";
(!callers()) && $checkpointer:checkpoint_started();
.
#1:0
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
this.key = 0;
$quota_utils.byte_based && $quota_utils.byte:object_size(this);
owned = `this.owner.owned_objects ! E_INVIND, E_PROPNF';
if (typeof(owned) == LIST)
this.owner.owned_objects = setadd(owned, this);
elseif (typeof(owned) == NUM)
this.owner.owned_objects = owned + 1;
endif
if (parent(this) == $root_class)
this.unique = 0;
endif
this.offered = #-1;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866487626, \"Erik\", #74, \"JHM\"}";
.
#1:1
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
owned = `this.owner.owned_objects ! E_INVIND, E_PROPNF';
if (typeof(owned) == LIST)
this.owner.owned_objects = setremove(owned, this);
elseif (typeof(owned) == NUM)
this.owner.owned_objects = owned - 1;
endif
else
raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866487617, \"Erik\", #74, \"JHM\"}";
.
#1:2
"set_name(newname) attempts to change this.name to newname";
"  => E_PERM   if you don't own this or aren't its parent, or are a player trying to do an end-run around $player_db...";
"  => E_TYPE   if newname isn't a string";
if ((!caller_perms().wizard) && (is_player(this) || ((caller_perms() != this.owner) && (this != caller))))
return E_PERM;
elseif (typeof(args[1]) != STR)
return E_TYPE;
else
try
this.name = args[1];
return 1;
except e (ANY)
"RAISEME";
return e[1];
endtry
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866487959, \"Erik\", #74, \"JHM\"}";
.
#1:3
"Usage: <obj>:name([<flagstring> | {<flag>, @<flag_args>}]*)";
"";
"Returns the name of <obj>, modified as indicated by the flags.";
"";
"<flagstring> - single-character flags run together as a string";
"<flag> - character or string flag, associated with the verb";
"         obj:modname_<flag>()";
"<flag_args> - args for obj:modname_<flag>()";
"";
"Valid flags are:";
"  p - possessive";
"  d - definite article";
"  i - indefinite article";
"  s - add idle suffix if any";
"  c - capitalize";
name = this:base_name();
for i in (args)
if (typeof(i) == LIST)
name = `this:("modname_" + i[1])(name, @listdelete(i, 1)) ! ANY => name';
"'ANY => name' is an ugly but cheap way of handling malfunctioning or nonexistent modname_foo verbs.";
elseif (typeof(i) == STR)
for j in [1..length(i)]
name = this:("modname_" + i[j])(name) || name;
endfor
endif
endfor
return name;
"Copied from naming test object (#373):name by Erik (#74) Thu Sep 22 17:45:37 1994 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866488025, \"Erik\", #74, \"JHM\"}";
.
#1:4
return this:name("c");
.
#1:5
"set_aliases(alias_list) attempts to change this.aliases to alias_list";
"  => E_PERM   if you don't own this or aren't its parent";
"  => E_TYPE   if alias_list is not a list";
"  => E_INVARG if any element of alias_list is not a string";
if (!($perm_utils:controls(caller_perms(), this) || (this == caller)))
return E_PERM;
elseif (typeof(aliases = args[1]) != LIST)
return E_TYPE;
else
for s in (aliases)
if (typeof(s) != STR)
return E_INVARG;
endif
endfor
this.aliases = aliases;
return 1;
endif
.
#1:6
string = args[1];
whose = #-1;
first = string[1..(space = index(string, " ")) - 1];
if (rindex(first, "'s") == (length(first) - 1))
whose = this:match(first[1..length(first) - 2]);
elseif (rindex(first, "s'") == (length(first) - 1))
whose = this:match(first[1..length(first) - 1]);
endif
if (valid(whose))
return whose:match(string[space + 1..length(string)]);
else
objects = this:objects_for_match();
return $string_utils:match(string, objects, "name", objects, "aliases");
endif
.
#1:7
":match_object(string [,who])";
args[2..1] = {this};
return $string_utils:match_object(@args);
.
#1:8
"set_description(newdesc) attempts to change this.description to newdesc";
"  => E_PERM   if you don't own this or aren't its parent";
if (!($perm_utils:controls(caller_perms(), this) || (this == caller)))
return E_PERM;
elseif (typeof(desc = args[1]) in {LIST, STR})
this.description = desc;
return 1;
else
return E_TYPE;
endif
.
#1:9
"Always return a list.";
"(I've been wanting to do this for a long time.)";
if (desc = this.description)
if (typeof(desc) == LIST)
return desc;
else
return {desc};
endif
else
return {};
endif
.
#1:10
desc = this:description(@args);
if (desc)
player:tell_lines(desc);
else
player:tell("You see nothing special.");
endif
.
#1:11
if (is_player(this))
return notify(this, @args);
endif
return 1;
.
#1:12
this:notify(tostr(@args));
.
#1:13
lines = args[1];
if (typeof(lines) != LIST)
lines = {lines};
endif
for line in (lines)
this:tell(line);
endfor
.
#1:14
return this:acceptable(@args);
.
#1:15
"you'd think this would have a security check";
set_task_perms(this.owner);
"RAISEME";
return `move(this, args[1]) ! ANY';
"Metadata 202106";
"Last-Modify: {866489114, \"Erik\", #74, \"JHM\"}";
.
#1:16
"eject(victim) --- usable by the owner of this to remove victim from this.contents.  victim goes to its home if different from here, or $nothing or $player_start according as victim is a player.";
what = args[1];
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
elseif ((!(what in this.contents)) || what.wizard)
return 0;
endif
if (($object_utils:has_property(what, "home") && (typeof(what.home) == OBJ)) && (what.home != this))
where = what.home;
else
where = is_player(what) ? $player_start | $nothing;
endif
fork (0)
"...Some objects like to know they've been moved...";
"... this is the best we can do.  Remember :moveto() may be broken.";
if ($object_utils:has_verb(what, "moveto"))
what:moveto(where);
endif
endfork
"RAISEME";
return `move(what, where) ! ANY';
"Metadata 202106";
"Last-Modify: {866489219, \"Erik\", #74, \"JHM\"}";
.
#1:17
return (this.key == 0) || $lock_utils:eval_key(this.key, args[1]);
.
#1:18
cp = caller_perms();
if (!valid(cp))
if (cp == $nothing)
cp = player;
else
$error:raise(E_PERM);
endif
endif
set_task_perms(cp);
$command_utils:do_huh(verb, args);
.
#1:19
":set_message(msg_name,new_value, pronoun_sub style)";
"Does the actual dirty work of @<msg_name> object is <new_value>";
"changing the raw value of the message msg_name to be new_value.";
"Both msg_name and new_value should be strings, though their interpretation is up to the object itself.";
" => error value (use E_PROPNF if msg_name isn't recognized)";
" => string error message if something else goes wrong.";
" => 1 (true non-string) if the message is successfully set";
" => 0 (false non-error) if the message is successfully `cleared'";
if (!((caller == this) || $perm_utils:controls(caller_perms(), this)))
return E_PERM;
else
"by default, messages get pronoun_subbed";
"RAISEME";
"also, is 0 ever returned?";
msg = args[3]:parse(args[2]);
return `this.(args[1] + "_msg") = msg ! ANY' && 1;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866489409, \"Erik\", #74, \"JHM\"}";
.
#1:20
"do_examine(examiner)";
"the guts of examine";
"call a series of verbs and report their return values to the player";
"if (caller == this || caller == who)";
who = args[1];
if (caller == who)
who:notify_lines(this:examine_names(who) || {});
"this:examine_names(who);";
who:notify_lines(this:examine_owner(who) || {});
"this:examine_owner(who);";
who:notify_lines(this:examine_desc(who) || {});
"this:examine_desc(who);";
who:notify_lines(this:examine_key(who) || {});
"this:examine_key(who);";
who:notify_lines(this:examine_contents(who) || {});
who:notify_lines(this:examine_verbs(who) || {});
else
"RAISEME";
return E_PERM;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866489452, \"Erik\", #74, \"JHM\"}";
.
#1:21
"examine_key(examiner)";
"return a list of strings to be told to the player, indicating what the key on this type of object means, and what this object's key is set to.";
"the default will only tell the key to a wizard or this object's owner.";
who = args[1];
if (((caller == this) && $perm_utils:controls(who, this)) && (this.key != 0))
return {tostr("Key:  ", $lock_utils:unparse_key(this.key))};
endif
.
#1:22
"examine_names(examiner)";
"Return a list of strings to be told to the player, indicating the name and aliases (and, by default, the object number) of this.";
return {tostr(name = this:name(), " (aka ", $string_utils:english_list(args[1].programmer ? {tostr(this), @setremove(this.aliases, name)} | this.aliases), ")")};
.
#1:23
"examine_desc(who) - return the description, probably";
"who is the player examining";
"this should probably go away";
return this:description() || {"(No description set.)"};
.
#1:24
"examine_contents(examiner)";
"by default, calls :tell_contents.";
who = args[1];
if ((caller == this) && $object_utils:has_verb(this, "tell_contents"))
this:tell_contents(this.contents);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866489511, \"Erik\", #74, \"JHM\"}";
.
#1:25
"Return a list of strings to be told to the player.  Standard format says \"Obvious verbs:\" followed by a series of lines explaining syntax for each usable verb.";
if (caller != this)
"RAISEME";
return E_PERM;
endif
who = args[1];
name = dobjstr;
vrbs = {};
commands_ok = `this:examine_commands_ok(who) ! E_VERBNF => 0';
dull_classes = {$root_class, $room, $player, $prog, $builder, $event_dispatcher};
what = this;
hidden_verbs = this:hidden_verbs(who);
while (what != $nothing)
if (!(what in dull_classes))
for i in [1..length(verbs(what))]
info = verb_info(what, i);
syntax = verb_args(what, i);
if (this:examine_verb_ok(what, i, info, syntax, commands_ok, hidden_verbs))
dobj = syntax[1];
prep = syntax[2];
iobj = syntax[3];
if (syntax == {"any", "any", "any"})
prep = "none";
endif
if (prep != "none")
for x in ($string_utils:explode(prep, "/"))
if (length(x) <= length(prep))
prep = x;
endif
endfor
endif
"This is the correct way to handle verbs ending in *";
vname = info[3];
while (i = index(vname, "* "))
vname[i..i] = "<anything>";
endwhile
if (vname[$] == "*")
vname[$..$] = "<anything>";
endif
vname = strsub(vname, " ", "/");
rest = "";
if (prep != "none")
rest = " " + ((prep == "any") ? "<anything>" | prep);
if (iobj != "none")
rest = tostr(rest, " ", (iobj == "this") ? name | "<anything>");
endif
endif
if (dobj != "none")
rest = tostr(" ", (dobj == "this") ? name | "<anything>", rest);
endif
vrbs = setadd(vrbs, ("  " + vname) + rest);
endif
$command_utils:suspend_if_needed(0);
endfor
endif
what = parent(what);
endwhile
if (vrbs)
return {"Obvious verbs:", @vrbs};
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866489714, \"Erik\", #74, \"JHM\"}";
.
#1:26
`this.location:(verb)(@args) ! E_INVIND, E_VERBNF';
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866490034, \"Erik\", #74, \"JHM\"}";
.
#1:27
if ($__core_init_phase)
vnum = 1;
coreverbs = {};
while (vnum <= length(verbs(this)))
$command_utils:suspend_if_needed(0);
info = verb_info(this, vnum)[3];
if (index(info, "(old)"))
delete_verb(this, vnum);
elseif (s = match(info, "^%(.+%)(core)$"))
set_verb_code(this, substitute("%1", s), verb_code(this, vnum));
delete_verb(this, vnum);
else
vnum = vnum + 1;
endif
endwhile
if (this == $root_class)
delete_property(this, "property_help_entries");
for v in ({"find_property_help_for", "get_property_help_for", "set_property_help_for", "add_property_help_for", "delete_property_help_for"})
delete_verb(this, v);
endfor
else
clear_property(this, "key");
this.offered = $nothing;
endif
else
$error:raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {865522736, \"Ken\", #75, \"JHM\"}";
.
#1:28
"Returns a list of the objects that are apparently inside this one.  Don't confuse this with .contents, which is a property kept consistent with .location by the server.  This verb should be used in `VR' situations, for instance when looking in a room, and does not necessarily have anything to do with the value of .contents (although the default implementation does).  `Non-VR' commands (like @contents) should look directly at .contents.";
return this.contents;
.
#1:29
"examine_verb_ok(loc, index, info, syntax, commands_ok, hidden_verbs)";
"loc is the object that defines the verb; index (1-based) is which verb on the object; info is verb_info; syntax is verb_args; commands_ok is determined by this:commands_ok, probably, but passed in so we don't have to calculate it for each verb.";
"hidden_verbs is passed in for the same reasons.  It should be a list, each of whose entries is either a string with the full verb name to be hidden (e.g., \"d*rop th*row\") or a list of the form {verb location, full verb name, args}.";
{loc, vindex, info, syntax, commands_ok, hidden_verbs} = args;
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
raise(E_PERM);
endif
vname = info[3];
return (((((syntax[2..3] != {"none", "this"}) && (!index(vname, "("))) && (commands_ok || ("this" in syntax))) && verb_code(loc, vindex)) && (!(vname in hidden_verbs))) && (!({loc, vname, syntax} in hidden_verbs));
.
#1:30
"return 1 if the object can hear a :tell, or cares. Useful for active objects that want to stop when nothing is listening.";
return 0;
.
#1:31
"hidden/obvious_verbs(who)";
"Returns a list of verbs on this that should be hidden/not hidden from examine.";
"The player who's examining is passed in, so objects can hide verbs from specific players.";
"Verbs are returned as {location, full_verb_name, args} or just full_verb_name.  full_verb name is what shows up in verb_info(object, verb)[3], for example \"dr*op th*row\".";
{who} = args;
obvious = verb != "hidden_verbs";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
raise(E_PERM);
endif
vbs = {};
what = this;
while (what != $nothing)
for i in [1..length(verbs(what))]
if ((!what:is_hidden_verb(i, who)) == obvious)
info = verb_info(what, i);
vbs = setadd(vbs, {what, info[3], verb_args(what, i)});
endif
endfor
what = parent(what);
endwhile
return vbs;
"Metadata 202106";
"Last-Modify: {878248227, \"Erik\", #74, \"JHM\"}";
.
#1:32
"examine_owner(examiner)";
"Return a list of strings to be told to the player, indicating who owns this.";
if (`is_player(this.offered) ! E_INVARG')
return {tostr("Owned by ", this.owner:dname(), ", who has offered ", this:po(), " to ", this.offered:dname(), ".")};
else
return {tostr("Owned by ", this.owner:name(), ".")};
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866526936, \"Ken\", #75, \"JHM\"}";
.
#1:33
return;
.
#1:34
return this.visible;
.
#1:35
"If this.(verb) is not set, return this:integrate_msg(@args).";
"If this.(verb) is not an alist, pronoun_sub it.";
"If this.location has an entry, pronoun sub it.";
"If $nowhere (#-1) has an entry, pronoun sub it.";
if ((typeof(alist = this.(verb)) == LIST) && (typeof(alist[1]) == LIST))
if (assoc = $list_utils:assoc(this.location, alist) || $list_utils:assoc($nowhere, alist))
msg = assoc[2];
endif
else
msg = alist;
endif
return $string_utils:pronoun_sub(msg, @args);
.
#1:36
cap = (length(verb) == 6) ? {"c"} | {};
article = verb[1];
return this:name({article, @args}, @cap);
return this:name(@args ? {{verb[1], @args}, @(length(verb) == 6) ? {"c"} | {}} | {verb[1] + ((length(verb) == 6) ? "c" | "")});
"Copied from naming test object (#373):dname by Erik (#74) Thu Sep 22 17:45:31 1994 EDT";
.
#1:37
return valid(this.location) ? this.location:where_am_i(@args) | this.location;
.
#1:38
what = args[1];
for i in (this.(verb))
if ($object_utils:isa(what, i))
return this in what:contents();
endif
endfor
return 0;
.
#1:39
return this:name((verb[5] == "c") ? "dc" | "d");
.
#1:40
player:tell("You can't look ", prepstr, " that.");
.
#1:41
return $string_utils:pronoun_sub(this.(verb), this.location, @args);
.
#1:42
"this verb works with the new @edit syntax.";
"it is passed the string which was used to call @edit, and it is expected to return an {editor object, string} pair.";
"the default properties, at this writing, are: .text for $note objects, and .description for others.";
"Should be called by player for permissions to work correctly.";
if ($perm_utils:controls(caller, this))
return {$note_editor, {{this, "description"}, this.description}};
else
return E_PERM;
endif
.
#1:43
return this:name();
.
#1:44
return this:name((verb[5] == "c") ? "ivc" | "iv");
.
#1:45
"Copied from Root Class (#1):tell_lines by Jay (#2) Wed Aug 11 21:21:07 1993 EDT";
lines = args[1];
if (typeof(lines) != LIST)
lines = {lines};
endif
for line in (lines)
this:tell(line);
$command_utils:suspend_if_needed(0);
endfor
.
#1:46
"is_hidden_verb(index, who)";
"Is the (1-based) <index>th verb hidden from <who>?";
index = args[1];
who = args[2];
if (!$object_utils:isa(caller, this))
raise(E_PERM);
endif
info = verb_info(this, index);
vargs = verb_args(this, index);
return ((!index(info[2], "r")) || (vargs[2..3] == {"none", "this"})) || match(info[3], "(.*)$");
"Metadata 202106";
"Last-Modify: {866490194, \"Erik\", #74, \"JHM\"}";
.
#1:47
return this:gender_obj():(verb)(@args);
.
#1:48
return;
.
#1:49
"editing_for(prop[, <who>])";
"This is a fallback answer to `what editor should be used to edit `prop'?' if there's no :editing_for_<prop> verb.";
"Returns {editor, prop}";
"Second arg is used for permissions only if provided and called from this; otherwise, caller (which should be player) will be used.";
{prop, ?who = caller} = args;
if (caller != this)
who = caller;
endif
if ($object_utils:has_property(this, prop))
if ($perm_utils:controls_prop(who, this, prop) || index(property_info(this, prop)[2], "r"))
val = $object_utils:has_callable_verb(this, getter = "get_" + prop) ? this:(getter)() | this.(prop);
if (typeof(val) in {LIST, STR})
return {$note_editor, {{this, prop}, val}};
else
return {"Sorry... expecting a list-valued property.", tostr("Anyway, you don't need an editor to edit ", $string_utils:print(val), ".")};
endif
if (typeof(val) == LIST)
if ($list_utils:check_type(val, STR))
return {$note_editor, {{this, prop}, val}};
else
return {$note_editor, {{this, prop}, $note_editor:explode_list(0, val)}};
endif
elseif (typeof(val) == STR)
return {$note_editor, {{this, prop}, val}};
else
return {"Sorry... expecting a list-valued property.", tostr("Anyway, you don't need an editor to edit ", $string_utils:print(val), ".")};
endif
else
return E_PERM;
endif
else
return tostr(this:dname(), " has no `", prop, "' to edit.");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835804070, \"Erik\", #74, \"JHM\"}";
.
#1:50
retlist = {};
for handler in (`this:("handlers_" + verb)(@args) ! ANY => {}')
ret = `handler:("handle_" + verb)(this, retlist, args) ! ANY';
if (ret)
retlist = {@retlist, ret};
endif
endfor
if ((typeof(args[1]) == LIST) && (typeof(args[1][1]) == LIST))
"it's an alist, get the default handler and call it";
default_handler = $list_utils:assoc("default_handler", args[1]);
if (default_handler)
ret = `default_handler[2]:("handle_" + verb)(this, retlist, args) ! E_VERBNF';
if (ret)
retlist = {@retlist, ret};
endif
endif
endif
return retlist;
"Metadata 202106";
"Last-Modify: {873849486, \"Ken\", #75, \"JHM\"}";
.
#1:51
"name_and_number() => \"name (#number)\"";
nameverb = verb[1..index(verb, "_") - 1];
return tostr(this:(nameverb)(), " (", this, ")");
.
#1:52
"Usage:  :add_handler(handler)";
"";
"Add an event handler to this object.  Only this may add a handler to itself.";
if (caller != this)
$error:raise(E_PERM);
endif
if (length(args))
return_value = $code_utils:verb_or_property(args[1], "handler_ok");
if (return_value)
this:_add_handler(args[1]);
endif
return return_value;
else
return E_ARGS;
endif
"Copied from event dispatcher (#1751):add_handler by Doug (#444) Tue Apr 26 02:29:07 1994 EDT";
.
#1:53
"Copied from event dispatcher (#1751):_add_handler by Doug (#444) Tue Apr 26 02:29:15 1994 EDT";
this.handlers = setadd(this.handlers, args[1]);
.
#1:54
"Usage:  :remove_handler(handler)";
"";
"Remove an event handler to this object.  An object may remove handlers from itself, and a handler may remove itself from an object's handlers list.";
handler = args[1];
if (!(caller in {this, handler}))
$error:raise(E_PERM);
endif
this.handlers = setremove(this.handlers, handler);
"Copied from event dispatcher (#1751):remove_handler by Doug (#444) Tue Apr 26 02:29:19 1994 EDT";
.
#1:55
"Copied from event dispatcher (#1751):handlers by Doug (#444) Tue Apr 26 02:28:39 1994 EDT";
return this.handlers;
.
#1:56
"Get the appropriate pronoun.";
"ps == subjective     e.g. they";
"po == objective           them";
"pr == reflexive           themselves";
"pp == possessive          their";
"pq == possessive noun     theirs";
"c means capitalize.";
return `this.(verb) ! E_PROPNF => $code_utils:verb_or_property(this:gender_obj(), verb, this)';
"OLD VERSION";
return ((this.(verb) || this:gender_obj():(verb)(this, @args)) || $gender_utils.(verb)[this:gender_name() in $gender_utils.genders]) || (this:is_plural() ? $they.(verb) | $it.(verb));
"Metadata 202106";
"Last-Modify: {885576296, \"Erik\", #74, \"JHM\"}";
.
#1:57
"The name of the gender of this object.";
return `this.gender_name ! E_PROPNF => this:gender_obj().gender_name';
"OLD VERSION";
return ((this.gender_name || this.gender:(verb)(@args)) || this.gender) || (this:is_plural() ? $they | $it):(verb)(@args);
"Metadata 202106";
"Last-Modify: {866491911, \"Erik\", #74, \"JHM\"}";
.
#1:58
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
"Copied from root class (#1):set_visible by Doug (#107) Sun May 15 19:50:55 1994 EDT";
.
#1:59
"Non-numeric values imply that the object is plural but of an unspecified number.";
return this.number;
.
#1:60
return this:number() != 1;
.
#1:61
try
g = this.gender;
if (typeof(g) == OBJ)
return g;
else
return $gender_utils:match_gender_obj(g);
endif
except (E_PROPNF)
if (this:is_plural(@args))
return $they;
else
return $it;
endif
endtry
"OLD VERSION";
if (typeof(g = this.gender) == OBJ)
return g;
elseif (typeof(g) == STR)
return $gender_utils:match_gender_obj(g);
elseif (this:is_plural(@args))
return $they;
endif
return $it;
"Metadata 202106";
"Last-Modify: {866492555, \"Erik\", #74, \"JHM\"}";
.
#1:62
"What should this object be replaced with when printing messages about itself to itself?";
return $recycler:valid(you = `this.you ! E_PROPNF => $nothing') ? you | this;
"Metadata 202106";
"Last-Modify: {866493813, \"Erik\", #74, \"JHM\"}";
.
#1:63
"Usage:  :modname_d(<oldname>[,<adj>])";
"";
"Returns <name> prepended with proper definite article (e.g. \"the\").";
"";
"The (optional) <adj>ective will be inserted between the article and the name.";
"";
"Relevant properties on this:";
"";
"  * .proper == 1 if this is a proper noun (e.g. John or Pittsburgh)";
"  * .possessor == <possessing object> if this's name includes a possessive (e.g. John's car)";
"  * :possessor() will be used, if it exists.";
"  * .def_art == definite article to use instead of \"the\" (e.g. \"lots of\" money)";
"  * :def_art() will be used, if it exists.";
{name, ?adj = ""} = args;
if (adj)
adj = this:adj_sub(adj);
endif
if (this.proper && (!adj))
art = "";
elseif ($recycler:valid(pos = `$code_utils:verb_or_property(this, "possessor") ! E_PROPNF => #-1') && (art = `pos:name("pi") ! E_VERBNF => ""'))
"elseif ((typeof(art = this:def_art()) == STR) || (typeof(art = this.def_art) == STR))";
elseif (typeof(art = `$code_utils:verb_or_property(this, "def_art") ! E_PROPNF') == STR)
else
art = "the";
endif
return (((art + (art ? " " | "")) + adj) + (adj ? " " | "")) + name;
"Copied from naming test object (#373):modname_d by Erik (#74) Thu Sep 22 17:35:33 1994 EDT";
.
#1:64
"Usage:  :modname_i(<name>,[<adj>])";
"";
"Returns <name> prepended with proper indefinite article (e.g. \"a\").";
"";
"The (optional) <adj>ective will be inserted between the article and the name.";
"";
"Relevant properties on this:";
"";
"  * .proper == 1 if this is a proper noun (e.g. John or Pittsburgh)";
"  * .possessor == <possessing object> if this's name includes a possessive (e.g. John's car)";
"  * :possessor() will be used, if it exists.";
"  * .indef_art == definite article to use instead of \"a\" (e.g. \"lots of\" money)";
"  * :indef_art() will be used, if it exists.";
"";
"Also, if this.unique == 1 then this:modname_d(@args) will be returned instead (e.g. \"the\" System Object instead of \"a\" System Object).";
{name, ?adj = ""} = args;
if (this.unique)
return this:modname_d(@args);
endif
if (adj)
adj = this:adj_sub(adj);
endif
if (this.proper && (!adj))
art = "";
elseif ($recycler:valid(pos = `$code_utils:verb_or_property(this, "possessor") ! E_PROPNF => #-1') && (art = `pos:name("ip") ! E_VERBNF => ""'))
"elseif ((typeof(art = this:indef_art()) == STR) || (typeof(art = this.indef_art) == STR))";
elseif (typeof(art = `$code_utils:verb_or_property(this, "indef_art") ! E_PROPNF') == STR)
else
art = $english:indef_article(adj || name, this:is_plural());
endif
return (((art + (art ? " " | "")) + adj) + (adj ? " " | "")) + name;
"Copied from root class (#1):dname by James (#2142) Sun Sep  4 21:25:07 1994 EDT";
"Copied from naming test object (#373):modname_i by Erik (#74) Thu Sep 22 17:35:36 1994 EDT";
.
#1:65
return $string_utils:capitalize(args[1]);
"Copied from naming test object (#373):modname_c by Erik (#74) Thu Sep 22 17:35:37 1994 EDT";
.
#1:66
return args[1] + "'s";
"Copied from naming test object (#373):modname_p by Erik (#74) Thu Sep 22 17:35:40 1994 EDT";
.
#1:67
return this.name;
"Copied from naming test object (#373):base_name by Verb not found (#74) Thu Sep 22 17:49:39 1994 EDT";
.
#1:68
if (valid(parent(this)))
return {parent(this)};
else
return {};
endif
.
#1:69
return `$string_utils:pronoun_sub(this.(verb)) ! E_PROPNF => ""';
"!D VERSION";
return (msg = this.(verb)) ? $string_utils:pronoun_sub(msg) | "";
"Copied from root class (#1):help_msg by Erik (#74) Sun Oct 23 18:13:47 1994 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866494293, \"Erik\", #74, \"JHM\"}";
.
#1:70
return $string_utils:uppercase(args[1]);
"Copied from naming test object (#373):modname_c by Erik (#74) Thu Sep 22 17:35:37 1994 EDT";
"Copied from root class (#1):modname_c by Erik (#74) Wed Oct 26 19:57:29 1994 EDT";
.
#1:71
return $language:titleize(args[1]);
"Copied from naming test object (#373):modname_c by Erik (#74) Thu Sep 22 17:35:37 1994 EDT";
"Copied from root class (#1):modname_c by Erik (#74) Wed Oct 26 19:59:15 1994 EDT";
.
#1:72
"Usage:  :empty_msg(msgname)";
"";
"Returns true iff the given message (e.g., \"integrate_room\") on this is empty.";
"";
"May be wrong if there's no such message.";
"sigh, `foo ! ANY' kinda sucks, but that's the current behavior and finding";
"all the calls to this would take too long.";
set_task_perms(caller_perms());
if (`e = this:((verb + "_") + args[1])() ! ANY' == E_VERBNF)
return `$pronoun_sub:message_empty(this.(args[1]) + "_msg") ! ANY';
else
return e;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {837468099, \"Ken\", #75, \"JHM\"}";
.
#1:73
"If this.(verb) is not set, return this:integrate_msg(@args).";
"If this.(verb) is not an alist, pronoun_sub it.";
"If this.location has an entry, pronoun sub it.";
"If $nowhere (#-1) has an entry, pronoun sub it.";
if ((typeof(alist = this.(strsub(verb, "empty_message_", "") + "_msg")) == LIST) && (typeof(alist[1]) == LIST))
if (assoc = $list_utils:assoc(this.location, alist) || $list_utils:assoc($nowhere, alist))
msg = assoc[2];
endif
else
msg = alist;
endif
return $pronoun_sub:message_empty(msg);
.
#1:74
if ($code_utils:get_prep(@$string_utils:explode(args[1]))[1])
return args[1];
else
return tostr("in ", args[1]);
endif
"Copied from James (#2142):modname_in Thu Nov 17 01:52:30 1994 EST";
.
#1:75
return 0;
"Copied from root class (#1):accept by Erik (#74) Sun Nov 20 21:16:08 1994 EST";
.
#1:76
return args[1];
.
#1:77
return tostr(args[1], " (", this, ")");
.
#1:78
return $string_utils:lowercase(args[1]);
.
#1:79
"Usage:  get_messages()";
" ";
"Returns an alist of {{message name, message value}, ...}";
"for all messages for this object.";
"Catches and returns the exceptions get_message might throw for value.";
"";
"Raises an error if caller can't get a list of all properties.";
"This verb may suspend.";
{?pronoun_sub = player.pronoun_sub_style} = args;
set_task_perms(caller_perms());
props = $object_utils:all_properties(this);
if (typeof(props) == ERR)
raise(props, "You can't read the messages on that.");
endif
messages = {};
for pname in (props)
$command_utils:suspend_if_needed(1);
len = length(pname);
if ((len > 4) && (pname[len - 3..len] == "_msg"))
msg = pname[1..$ - 4];
value = `this:get_message(msg, pronoun_sub) ! E_PERM, E_INVARG, E_PROPNF';
messages = {@messages, {pname[1..$ - 4], value}};
endif
endfor
return messages;
"Metadata 202106";
"Last-Modify: {833414096, \"Ken\", #75, \"JHM\"}";
.
#1:80
"Usage:  get_message(msg, [pronoun_sub style])";
":get_message(msg name)";
"        Return value for message.";
"        value is a string suitable for passing to set_message";
"        This verb might raise:";
"                     E_PERM, meaning you can't read that.";
"                     E_INVARG, meaning this isn't a message";
"                     E_PROPNF, meaning this message does not exist.";
"calls `player.pronoun_sub_style:unparse'";
set_task_perms(caller_perms());
{message, ?pronoun_sub = player.pronoun_sub_style} = args;
pname = message + "_msg";
val = this.(pname);
if (!val)
value = "";
elseif (typeof(val) == LIST)
value = $string_utils:print(pronoun_sub:unparse(val));
elseif (typeof(val) != STR)
raise(E_INVARG);
else
value = $string_utils:print(pronoun_sub:unparse($pronoun_sub.lambdacore:parse(val)));
endif
return value;
.
#2:0
if ($__core_init_phase)
pass(@args);
j = 1;
for v in (verbs(this))
if (verb_info(this, j)[3] == "init_for_core")
j = j + 1;
else
delete_verb(this, j);
endif
endfor
for p in (properties(this))
delete_property(this, p);
endfor
for p in ($object_utils:all_properties(this))
clear_property(this, p);
endfor
this:set_name("Wizard");
this.aliases = {"Wizard"};
this.password = 0;
this.gender = $e;
this.home = $player_start;
for o in (this.contents)
this:eject(o);
endfor
clear_property(this, "mail_forward");
$quota_utils.byte:set_quota_total(this, 0);
else
$error:raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863649351, \"Erik\", #74, \"JHM\"}";
.
#3:0
if (!(caller in {#0, this}))
return E_PERM;
endif
this:look_self(player:brief());
this:announce(player:inamec(), " ", player:verb_sub("has"), " connected.");
.
#3:1
this:announce(args[1]:dnamec(), " ", args[1]:verb_sub("has"), " disconnected.");
.
#3:2
"OLDER VERSION";
"player:tell(\"You say, \\\"\", argstr, \"\\\"\");";
"return this:announce($string_utils:pronoun_sub(this.say_msg, player, this));";
"OLD VERSION";
"this:announce(player:dnamec(), \" says, \\\"\", argstr, \"\\\"\");";
"thd = player:misc_option(\"third_person\");";
"player:tell(thd ? player:dnamec() | \"You\", \" \", thd ? \"says\" | \"say\", \", \\\"\", argstr, \"\\\"\");";
"return;";
"i miss pronouns";
return $you:say_action(this.say_msg, player, this, this, $nothing, $nothing);
"DOUG'S VERSION";
event = {{"speaker", player}, {"speech", argstr}, {"default_handler", this.default_handler_event_speech}};
return this:broadcast_event_speech(event);
.
#3:3
name = `player:dnamec() ! ANY';
if ((argstr != "") && (argstr[1] == ":"))
this:announce_all(name, text = argstr[2..$]);
else
this:announce_all(name, " ", text = argstr);
endif
this:broadcast_event_emote(argstr);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869623859, \"Erik\", #74, \"JHM\"}";
.
#3:4
for dude in (setremove(this.contents, player))
dude:tell(@args);
endfor
.
#3:5
"Only full matches are successful.  This is so, for example, \"s\" doesn't match \"secret door\".";
what = args[1];
if (what)
yes = $failed_match;
for e in (this:((verb == "match_exit") ? "exits" | "entrances")())
if (valid(e) && (what in {e.name, @e.aliases}))
if (yes == $failed_match)
yes = e;
elseif (yes != e)
return $ambiguous_match;
endif
endif
endfor
return yes;
else
return $nothing;
endif
.
#3:6
set_task_perms(caller_perms());
prop = verb[5..$] + "s";
try
this.(prop) = setadd(this.(prop), args[1]);
return 1;
except (E_PERM)
return 0;
endtry
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869624163, \"Erik\", #74, \"JHM\"}";
.
#3:7
"Copied from generic room (#3):tell_contents by r'm (#2) Mon Oct 31 22:28:40 1994 EST";
contents = args[1];
if ((!this.dark) && (contents != {}))
people = asleep = things = types = sorts = {};
" if (players = $set_utils:intersection(contents, $code_utils:connected_players()))";
if (players = $set_utils:intersection(contents, connected_players()))
"all the awake ones, anyway";
contents = $set_utils:diff(contents, players);
for x in (players)
if (i = (type = x:idle_suffix()) in types)
people[i] = {@people[i], x};
else
types = {@types, type};
sorts = {@sorts, idle_seconds(x)};
people = {@people, {x}};
endif
endfor
endif
for x in (contents)
if (is_player(x))
asleep = {@asleep, x};
else
things = {@things, x};
endif
endfor
if (asleep)
people = {@people, asleep};
types = {@types, "asleep"};
sorts = {@sorts, $maxint};
endif
if (things)
player:tell("You see ", $string_utils:name_for_tell_contents_list(things), " here.");
endif
if (people)
people = $list_utils:sort(people, sorts);
types = $list_utils:sort(types, sorts);
if (types[1] == "alert")
string = tostr($string_utils:namec_for_tell_contents_list(people[1]), " ", (length(people[1]) == 1) ? "is" | "are", " here.  ");
else
string = tostr($string_utils:namec_for_tell_contents_list(people[1]), " ", (length(people[1]) == 1) ? "is" | "are", " here, ", types[1], ".  ");
endif
for i in [2..l = length(types)]
string = (((((string + $string_utils:namec_for_tell_contents_list(people[i])) + " ") + ((length(people[i]) == 1) ? "is" | "are")) + " ") + types[i]) + ".  ";
endfor
string[length(string) - 1..length(string)] = "";
player:tell(string);
endif
endif
.
#3:8
if (!$perm_utils:controls(valid(caller_perms()) ? caller_perms() | player, this))
exits = this:obvious_exits();
player:tell("Obvious exits:  ", $string_utils:dname_list(exits, "none"), ".");
"player:tell(\"Sorry, only the owner of a room may list its exits.\");";
elseif (!(exits = this:exits()))
player:tell("This room has no conventional exits.");
else
for exit in (exits)
player:tell($string_utils:dnamec_and_number(exit), " leads to ", $string_utils:dname_and_number(`exit.dest ! E_INVIND => "nowhere"'), " via {", $string_utils:from_list(exit.aliases, ", "), "}.");
endfor
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869624104, \"Erik\", #74, \"JHM\"}";
.
#3:9
if (((caller in this.entrances) || $object_utils:isa(caller, $generic_editor)) || ((callers()[1][4] == $room) && (callers()[1][2] in {"disfunc", "event_really_disconnected"})))
this.blessed_object = args[1];
this.blessed_task = task_id();
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869624687, \"Erik\", #74, \"JHM\"}";
.
#3:10
if (!$perm_utils:controls(valid(caller_perms()) ? caller_perms() | player, this))
player:tell("Sorry, only the owner of a room may list its entrances.");
elseif (this.entrances == {})
player:tell("This room has no conventional entrances.");
else
for exit in (this.entrances)
player:tell($string_utils:dnamec_and_number(exit), " comes from ", $string_utils:dname_and_number(`exit.source ! E_INVIND => "nowhere"'), " via {", $string_utils:from_list(exit.aliases, ", "), "}.");
endfor
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869624483, \"Erik\", #74, \"JHM\"}";
.
#3:11
if ((!args) || (!(dir = args[1])))
player:tell("You need to specify a direction.");
return E_INVARG;
elseif (valid(exit = this:match_exit(dir)))
exit:invoke();
if (length(args) > 1)
old_room = player.location;
"Now give objects in the room we just entered a chance to act.";
fork (1)
if (player.location == old_room)
"player didn't move or get moved while we were suspended";
player.location:go(@listdelete(args, 1));
endif
endfork
endif
elseif (exit == $failed_match)
player:tell("You can't go that way (", dir, ").");
else
player:tell("There's more than one way to go in the direction `", dir, "'.");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869625024, \"Erik\", #74, \"JHM\"}";
.
#3:12
if ((dobjstr == "") && (!prepstr))
this:look_self();
elseif (($code_utils:short_prep(prepstr) != "in") && (prepstr != "on"))
if ((!dobjstr) && (prepstr == "at"))
dobjstr = iobjstr;
iobjstr = "";
else
dobjstr = dobjstr + (prepstr && ((dobjstr && " ") + prepstr));
dobjstr = dobjstr + (iobjstr && ((dobjstr && " ") + iobjstr));
endif
dobj = this:match_object(dobjstr);
if (!$command_utils:object_match_failed(dobj, dobjstr))
dobj:look_self();
endif
elseif (!iobjstr)
player:tell(verb, " ", prepstr, " what?");
else
iobj = this:match_object(iobjstr);
if (!$command_utils:object_match_failed(iobj, iobjstr))
iobj:look_in(dobjstr);
endif
endif
.
#3:13
for dude in (this.contents)
dude:tell(@args);
endfor
.
#3:14
text = listdelete(args, 1);
contents = this.contents;
for l in (args[1])
contents = setremove(contents, l);
endfor
for listener in (contents)
listener:tell(@text);
endfor
.
#3:15
{object} = args;
if (!$perm_utils:invoked_by_function("move"))
raise(E_PERM);
endif
if (is_player(object))
player = object;
this:look_self(player:brief());
endif
if (object == this.blessed_object)
this.blessed_object = #-1;
endif
pass(@args);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {913134420, \"core initialization\", #3023, \"core\"}";
.
#3:16
exit = args[1];
if (caller != exit)
set_task_perms(caller_perms());
endif
return (this.exits = setremove(this.exits, exit)) != E_PERM;
.
#3:17
exit = args[1];
if (caller != exit)
set_task_perms(caller_perms());
endif
return (this.entrances = setremove(this.entrances, exit)) != E_PERM;
.
#3:18
set_task_perms(player);
if (!dobjstr)
player:tell("Usage:  @add-exit <exit-number>");
return;
endif
exit = this:match_object(dobjstr);
if ($command_utils:object_match_failed(exit, dobjstr))
return;
endif
if (!($exit in $object_utils:ancestors(exit)))
player:tell("That doesn't look like an exit object to me...");
return;
endif
try
dest = exit.dest;
except (E_PERM)
player:tell("You can't read the exit's destination to check that it's consistent!");
return;
endtry
try
source = exit.source;
except (E_PERM)
player:tell("You can't read that exit's source to check that it's consistent!");
return;
endtry
if (source != this)
player:tell("That exit wasn't made to be attached here; it was made as an exit from ", source:name(), " (", source, ").");
return;
elseif (((typeof(dest) != OBJ) || (!valid(dest))) || (!($room in $object_utils:ancestors(dest))))
player:tell("That exit doesn't lead to a room!");
return;
endif
if (!this:add_exit(exit))
player:tell("Sorry, but you must not have permission to add exits to this room.");
else
player:tell("You have added ", exit, " as an exit that goes to ", exit.dest:name(), " (", exit.dest, ") via ", $string_utils:english_list(setadd(exit.aliases, exit:name())), ".");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869625205, \"Erik\", #74, \"JHM\"}";
.
#3:19
set_task_perms(player);
if (!dobjstr)
player:tell("Usage:  @add-entrance <exit-number>");
return;
endif
exit = this:match_object(dobjstr);
if ($command_utils:object_match_failed(exit, dobjstr))
return;
endif
if (!($exit in $object_utils:ancestors(exit)))
player:tell("That doesn't look like an exit object to me...");
return;
endif
try
dest = exit.dest;
except (E_PERM)
player:tell("You can't read the exit's destination to check that it's consistent!");
return;
endtry
if (dest != this)
player:tell("That exit doesn't lead here!");
return;
endif
if (!this:add_entrance(exit))
player:tell("Sorry, but you must not have permission to add entrances to this room.");
else
player:tell("You have added ", exit, " as an entrance that gets here via ", $string_utils:english_list(setadd(exit.aliases, exit:name())), ".");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869625254, \"Erik\", #74, \"JHM\"}";
.
#3:20
"Make a mild attempt to keep people and objects from ending up in #-1 when people recycle a room";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
"... first try spilling them out onto the floor of enclosing room if any";
if (valid(this.location))
for x in (this.contents)
x:moveto(this.location);
endfor
endif
"... try sending them home...";
for x in (this.contents)
if (is_player(x))
if ((typeof(x.home) == OBJ) && valid(x.home))
x:moveto(x.home);
endif
if (x.location == this)
move(x, $player_start);
endif
elseif (valid(x.owner))
x:moveto(x.owner);
endif
endfor
pass(@args);
else
return E_PERM;
endif
.
#3:21
if (dobjstr != "")
dobj = player:my_match_player(dobjstr);
if (!valid(dobj))
player:tell("Who?");
return;
endif
folks = {dobj};
else
folks = players();
endif
day = week = month = ever = never = {};
a_day = (24 * 60) * 60;
a_week = 7 * a_day;
a_month = 30 * a_day;
now = time();
for x in (folks)
if ($object_utils:isa(x, $player))
when = x.last_connect_time;
how_long = now - when;
if ((when == 0) || (when == $maxint))
never = {@never, x};
elseif (how_long < a_day)
day = {@day, x};
elseif (how_long < a_week)
week = {@week, x};
elseif (how_long < a_month)
month = {@month, x};
else
ever = {@ever, x};
endif
endif
endfor
for entry in ({{day, "the last day"}, {week, "the last week"}, {month, "the last 30 days"}, {ever, "recorded history"}})
if (entry[1])
player:tell("Players who have connected within ", entry[2], ":");
for x in (entry[1])
player:tell("  ", x:name(), " last connected ", ctime(x.last_connect_time), ".");
endfor
endif
endfor
if (never)
player:tell("Players who have never connected:");
player:tell("  ", $string_utils:english_list($list_utils:map_prop(never, "name")));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863667928, \"Erik\", #74, \"JHM\"}";
.
#3:22
exit_name = verb;
if (args)
exit_name = tostr(exit_name, " ", argstr);
endif
exit = this:match_exit(exit_name);
if (valid(exit))
exit:invoke();
elseif (exit == $failed_match)
player:tell("You can't go that way.");
else
player:tell("There's more than one way to go in the direction `", verb, "'.");
endif
.
#3:23
set_task_perms(player);
if ($command_utils:object_match_failed(dobj, dobjstr))
return;
elseif (dobj.location != this)
player:tell(dobj:name(), "(", dobj, ") is not here.");
return;
elseif (!$perm_utils:controls(player, this))
player:tell("You are not the owner of this room.");
return;
elseif (dobj.wizard)
player:tell("Sorry, you can't ", verb, " a wizard.");
dobj:tell(player:dnamec(), " tried to ", verb, " you.");
return;
endif
iobj = this;
player:tell(this:ejection_msg());
this:eject(dobj);
dobj:tell(this:victim_ejection_msg());
this:announce_all_but({player, dobj}, this:oejection_msg());
.
#3:24
return $string_utils:pronoun_sub(this.(verb));
.
#3:25
who = args[1];
return (valid(who) && ((this.free_home || $perm_utils:controls(who, this)) || (who in this.residents))) && this:accept(who);
.
#3:26
if (!$perm_utils:controls(player, this))
player:tell("You must own this room to manipulate the legal residents list.  Try contacting ", this.owner:name(), ".");
else
if (!dobjstr)
player:tell("Allowable residents in this room:  ", $string_utils:english_list($list_utils:map_prop(this.residents, "name"), "no one"), ".");
return;
elseif (dobjstr[1] == "!")
notflag = 1;
dobjstr = dobjstr[2..length(dobjstr)];
else
notflag = 0;
endif
result = $string_utils:match_player_or_object(dobjstr);
if (!result)
return;
else
"a one element list was returned to us if it won.";
result = result[1];
if (notflag)
if (!(result in this.residents))
player:tell(result:name(), " doesn't appear to be in the residents list of ", this:name(), ".");
else
this.residents = setremove(this.residents, result);
player:tell(result:name(), " removed from the residents list of ", this:name(), ".");
endif
else
if (result in this.residents)
player:tell(result:name(), " is already an allowed resident of ", this:name(), ".");
else
this.residents = {@this.residents, result};
player:tell(result:name(), " added to the residents list of ", this:name(), ".");
endif
endif
endif
endif
.
#3:27
set_task_perms(player);
if (!dobjstr)
player:tell("Usage:  @remove-exit <exit>");
return;
endif
exit = this:match_object(dobjstr);
if (!(exit in this.exits))
if ($command_utils:object_match_failed(exit, dobjstr))
return;
endif
player:tell("Couldn't find \"", dobjstr, "\" in the exits list of ", this:name(), ".");
return;
elseif (!this:remove_exit(exit))
player:tell("Sorry, but you do not have permission to remove exits from this room.");
else
name = valid(exit) ? exit:name() | "<recycled>";
player:tell("Exit ", exit, " (", name, ") removed from exit list of ", this:name(), " (", this, ").");
endif
.
#3:28
set_task_perms(player);
if (!dobjstr)
player:tell("Usage:  @remove-entrance <entrance>");
return;
endif
entrance = $string_utils:match(dobjstr, this.entrances, "name", this.entrances, "aliases");
if (!valid(entrance))
"Try again to parse it.  Maybe they gave object number.  Don't complain if it's invalid though; maybe it's been recycled in some nefarious way.";
entrance = this:match_object(dobjstr);
endif
if (!(entrance in this.entrances))
player:tell("Couldn't find \"", dobjstr, "\" in the entrances list of ", this:name(), ".");
return;
elseif (!this:remove_entrance(entrance))
player:tell("Sorry, but you do not have permission to remove entrances from this room.");
else
name = valid(entrance) ? entrance:name() | "<recycled>";
player:tell("Entrance ", entrance, " (", name, ") removed from entrance list of ", this:name(), " (", this, ").");
endif
.
#3:29
if ((caller in {this, this.owner}) || $perm_utils:controls(caller_perms(), this))
return pass(@args);
else
return E_PERM;
endif
.
#3:30
if (this:is_public_location())
return (msg = this.(verb)) ? $string_utils:pronoun_sub(msg, args[1]) | "";
else
return "";
endif
.
#3:31
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb);
else
return E_PERM;
endif
.
#3:32
":obvious_exits([who]) -> return all the exits in the room that are obvious [to who]";
exits = {};
for exit in (this:exits())
if (`exit:obvious(@args) ! E_INVIND, E_VERBNF')
exits = setadd(exits, exit);
endif
endfor
return exits;
"Metadata 202106";
"Last-Modify: {869625394, \"Erik\", #74, \"JHM\"}";
.
#3:33
":here_huh(verb,args)  -- room-specific :huh processing.  This should return 1 if it finds something interesting to do and 0 otherwise; see $command_utils:do_huh.";
"For the generic room, we check for the case of the caller specifying an exit for which a corresponding verb was never defined.";
"If that fails, we check to see if the verb is defined on an exit or other object which can match with player:my_match_object() in the room.";
set_task_perms(caller_perms());
verb = args[1];
if (args[2] || ($failed_match == (exit = this:match_exit(verb))))
"... okay, it's not an exit.  see if it's a verb defined on an exit ...";
if ($server_options.support_numeric_verbname_strings && $string_utils:is_numeric(verb))
"don't allow numeric verbs below, cuz it's not matching on verb name";
player:tell("That is not a valid command.");
return 1;
endif
dobj = this:match_object(dobjstr);
iobj = this:match_object(iobjstr);
for o in ({this, dobj, iobj})
if (valid(o) && (where = $object_utils:has_callable_verb(o, verb)))
vargs = verb_args(where[1], verb);
if (((vargs[2] == "any") || ((!prepstr) && (vargs[2] == "none"))) || index(("/" + vargs[2]) + "/", ("/" + prepstr) + "/"))
if (((vargs[1] == "any") || ((!dobjstr) && (vargs[1] == "none"))) || ((dobj == o) && (vargs[1] == "this")))
if (((vargs[3] == "any") || ((!iobjstr) && (vargs[3] == "none"))) || ((iobj == o) && (vargs[3] == "this")))
o:(verb)(@args);
return 1;
endif
endif
endif
endif
endfor
"... okay it's not a verb defined on an exit either.  we give up ...";
return 0;
elseif (valid(exit))
exit:invoke();
else
"... ambiguous exit ...";
player:tell("Which direction `", verb, "' do you mean?");
endif
return 1;
.
#3:34
this:(verb[6..length(verb)])(@args);
.
#3:35
return this == args[1].location;
.
#3:36
"examine_key(examiner)";
"return a list of strings to be told to the player, indicating what the key on this type of object means, and what this object's key is set to.";
"the default will only tell the key to a wizard or this object's owner.";
who = args[1];
if (((caller == this) && $perm_utils:controls(who, this)) && (this.key != 0))
return {tostr(this:dnamec(), " will accept only objects matching the following key:"), tostr("  ", $lock_utils:unparse_key(this.key))};
endif
.
#3:37
"examine_contents(who)";
if (caller == this)
"this:tell_contents(this.contents, this.ctype);";
this:tell_contents(this.contents);
endif
.
#3:38
if (typeof(what = args[1]) == LIST)
r = {};
for thing in (args[1])
this:(verb)(thing) && (r = {@r, thing});
endfor
return r;
else
if (this.integrate_unknown_objects && (!is_player(args[1])))
return (args[1]:integrate_in(this) && (!args[1]:empty_message("integrate_room"))) && "args[1]:integrate_room_msg()";
endif
endif
.
#3:39
brief = args && args[1];
player:tell(this:namec_for_look_self(brief));
things = this:visible_of(setremove(this:contents(), player));
integrate = {};
if (this.integration_enabled)
for i in (things)
if (this:ok_to_integrate(i) && ((!brief) || (!is_player(i))))
integrate = {@integrate, i};
things = setremove(things, i);
endif
endfor
"for i in (this:obvious_exits(player))";
for i in (this:exits())
if (this:ok_to_integrate(i))
integrate = setadd(integrate, i);
"changed so prevent exits from being integrated twice in the case of doors and the like";
endif
endfor
endif
if (!brief)
desc = this:description(integrate);
if (desc)
player:tell_lines(desc);
else
player:tell("You see nothing special.");
endif
endif
"there's got to be a better way to do this, but.";
if (topic = this:topic_msg())
if (0)
this.topic_sign:show_topic();
else
player:tell(this.topic_sign:integrate_room_msg());
endif
endif
"this:tell_contents(things, this.ctype);";
this:tell_contents(things);
.
#3:40
"If there's more than one paragraph of description, then add integration messages to the end as a separate paragraph(s).";
"If there are more than one paragraph of integration messages, add them as separate paragraphs to the end of the description.";
"Otherwise, append the integration message to the end of the description paragraph.";
desc = pass(@args);
if (!this.integrate_paragraphs)
integrate = $integration_utils:integrate(args, "integrate_room_msg");
integrate_sep = $string_utils:pronoun_sub(this.integrate_sep_msg);
if (integrate)
if ((((sep_is_list = typeof(integrate_sep) == LIST) || (!integrate_sep)) || (length(desc) > 1)) || (length(integrate) > 1))
if (sep_is_list)
desc = {@desc, @integrate_sep, @integrate};
else
desc = {@desc, @integrate};
endif
else
if (desc)
desc = {(desc[1] + integrate_sep) + integrate[1]};
else
desc = integrate;
endif
endif
endif
return desc;
else
if (!desc)
desc = {""};
endif
explicit = $list_utils:flatten(integrate = this.integrate_paragraphs);
integrate_objects = {@this:contents(), @this:exits()};
for i in [1..length(integrate)]
ni = {};
for elt in (integrate[i])
if ((elt in integrate_objects) || (typeof(elt) in {LIST, STR}))
ni = {@ni, elt};
endif
endfor
integrate[i] = ni;
endfor
"integrate[i] = $set_utils:intersection(integrate[i], $set_utils:union(this:contents(), this:exits()));";
if (misc = $set_utils:diff($list_utils:flatten(args), explicit))
integrate[l = length(integrate)] = {@integrate[l], @misc};
endif
if (length(desc) == 1)
integrate[1] = {@desc, @integrate[1]};
else
for i in [1..length(desc) - 1]
integrate = {{desc[i]}, @integrate};
endfor
integrate[l = length(desc)] = {desc[l], @integrate[l]};
endif
return $integration_utils:integrate(integrate, "integrate_room_msg");
endif
.
#3:41
return this;
.
#3:42
return $string_utils:pronoun_sub(this.(verb), @args);
.
#3:43
{name} = args;
if (!name)
this:look_self();
else
pass(@args);
endif
.
#3:44
"Capitalize all words according to english title rules.";
return $english:titleize(pass(@args));
.
#3:45
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
.
#3:46
"Usage:  :here_find_path(destination, randomness)";
"randomness is a fraction {num, den} determining in a vague way how much randomness will be added to the shortest-length path.";
path = this:_here_find_path(args[1], {0}, args[2]);
if (path[1])
return path[2];
else
return 0;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869625552, \"Erik\", #74, \"JHM\"}";
.
#3:47
"Usage:  :_here_find_path(destination, {0, @rooms checked}, randomness)";
"Look for path from here to destination without going through rooms already checked.";
"randomness is a fraction {num, den} determining in a vague way how much randomness will be added to the shortest-length path.";
"Returns {1, list of {exit name, exit source} pairs, {0, @list of rooms looked at}} or {0, @list of rooms looked at}.";
destination = args[1];
checked = args[2];
randomness = args[3];
numerator = randomness[1];
denominator = randomness[2];
path = 0;
if (this == destination)
return {1, {}, setadd(checked, this)};
endif
for exit in (this:exits())
if (exit:obvious())
exitdest = exit.dest;
if (!(exitdest in checked))
newpath = `exitdest:(verb)(destination, setadd(checked, this), randomness) ! E_VERBNF => {}';
if (newpath)
if (newpath[1])
if (random(denominator) > numerator)
if ((!path) || (length(path) > length(newpath[2])))
path = {{exit.name, this}, @newpath[2]};
endif
checked = newpath[3];
else
return {1, {{exit.name, this}, @newpath[2]}, newpath[3]};
endif
else
checked = newpath;
endif
endif
endif
endif
endfor
if (path)
return {1, path, setadd(checked, this)};
else
return checked;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869625862, \"Erik\", #74, \"JHM\"}";
.
#3:48
return {@pass(@args), @this:exits()};
.
#3:49
if (caller in {this, #0})
this:look_self(player:brief());
this:announce(player:dnamec(), " has reconnected.");
endif
.
#3:50
"Usage:  sit";
"";
"Looks for an open seat.";
if (valid(caller_perms()) && (caller_perms() != player))
return E_PERM;
endif
furniture = {};
seat = $nothing;
for o in ($list_utils:randomly_permute(this.contents))
if ($object_utils:isa(o, $furniture) && o:obvious_seat_for(player))
if (player in o.sitting)
return player:tell("You are already sitting ", $string_utils:pronoun_sub(o:on_msg()), " ", o:dname(), ".");
elseif (length(o.sitting) < o.seats)
seat = o;
endif
furniture = {@furniture, o};
endif
endfor
if (valid(seat))
return seat:sit();
elseif (furniture)
player:tell("There are no open seats.");
else
player:tell("There's nowhere to sit.");
endif
.
#3:51
"Usage:  stand";
"";
"Looks for furniture you're on.";
for o in (this.contents)
if ($object_utils:isa(o, $furniture))
if (player in o.sitting)
return o:stand();
endif
endif
endfor
player:tell("You're not sitting down.");
.
#3:52
return $recycler:valid(this.topic_sign) ? this.topic_sign:topic_msg() | "";
.
#3:53
"Usage: topic <topic>";
if (player.location != this)
player:Tell("You can't set a room's topic from afar.  How would you know what's being talked about there?");
return;
elseif (!argstr)
return player:tell("Usage: ", verb, " <topic>");
else
topic = argstr;
endif
if (!$recycler:valid(this.topic_sign))
if (typeof(sign = $topic:make_topic_sign()) == OBJ)
sign:push(topic);
sign:initial_message(topic);
else
player:tell("Error creating topic sign: ", sign);
endif
else
iobjstr = topic;
this.topic_sign:cover();
return;
player:tell("use: cover sign with ", topic);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863369878, \"Ken\", #75, \"JHM\"}";
.
#3:54
":set_topic_sign([sign])";
"sets or clears current topic sign";
"only allows itself to be called by $topic and the sign itself";
if ((!$recycler:valid(this.topic_sign)) && (caller == $topic))
this.topic_sign = sign = args[1];
this:add_dependent(sign);
elseif ((caller == this.topic_sign) && (!args))
this:remove_dependent(sign = this.topic_sign);
this.topic_sign = $nothing;
else
return E_INVARG;
endif
.
#3:55
"Usage:  :event_really_disconnected()";
"";
"Do housekeeper stuff if applicable.";
home = caller.home;
if ((this == caller.location) && (this != home))
fork (0)
"This is forked so that it's protected from aborts due to errors in the player's :moveto verb.";
if (!((caller in connected_players()) || (caller.location == home)))
move(caller, $player_start);
endif
endfork
home:bless_for_entry(caller);
caller:moveto(home);
if (caller.location != this)
this:announce(this:housekeeper_msg());
endif
if (caller.location == home)
home:announce(home:housekeeper_deposit_msg());
endif
endif
.
#3:56
if (this == $room)
return $string_utils:match(args[1], d = $object_utils:leaves(this), "name", d, "aliases");
else
return this:match(args[1]);
endif
.
#3:57
"room used by who for party sorting.  sigh.";
return this;
.
#3:58
if (!$__core_init_phase)
raise(E_PERM);
endif
pass(@args);
if (this == $room)
for verb in ({"get_idescription", "set_idescription", "editing_for"})
delete_verb(this, verb);
endfor
delete_verb(this, "area");
delete_property(this, "boxology");
delete_verb(this, "exitfunc");
delete_property(this, "person_seconds");
info = verb_info(this, "set_person_seconds");
if (info[3] == "set_person_seconds")
delete_verb(this, "set_person_seconds");
else
names = info[3];
if (i = index(names, " set_person_seconds "))
names[i..i + 18] = "";
elseif (index(names, "set_person_seconds ") == 1)
names[1..19] = "";
else
names[$ - 18..$] = "";
endif
info[3] = names;
set_verb_info(this, "set_person_seconds", info);
endif
delete_property(this, "default_handler_event_speech");
delete_property(this, "integrate_exit_msg");
delete_verb(this, "integrate_exit_msg");
else
clear_property(this, "blessed_object");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {873849661, \"Ken\", #75, \"JHM\"}";
.
#3:59
what = args[1];
return this:is_unlocked_for(what) && ((((this.free_entry || ((what == this.blessed_object) && (task_id() == this.blessed_task))) || (what.owner == this.owner)) || ((typeof(this.residents) == LIST) && (what in this.residents))) || $object_utils:contains(this, what));
.
#3:61
if (valid(this.topic_sign))
return !this.topic_sign.private;
else
return 1;
endif
.
#3:62
return $set_utils:union(this:contents(), this:exits());
.
#3:63
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
.
#3:64
"Usage:  :walking_cost(who)";
"";
{who, source, dest, source_exit} = args;
cost = this.walking_cost;
for i in (this:contents())
if (i:is_listening())
cost = cost + 1;
endif
endfor
return cost;
"Metadata 202106";
"Last-Modify: {912965374, \"Ken\", #75, \"JHM\"}";
.
#4:0
set_task_perms(player);
if (dobjstr == "")
dobj = player;
else
dobj = player:my_match_player(dobjstr);
endif
if ($command_utils:player_match_failed(dobj, dobjstr))
return;
endif
$quota_utils:display_quota(dobj);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866494599, \"Erik\", #74, \"JHM\"}";
.
#4:1
if (player != this)
player:tell("Sorry, you can't do that.");
return;
endif
set_task_perms(player);
nargs = length(args);
pos = "named" in args;
if ((pos <= 1) || (pos == nargs))
pos = "called" in args;
endif
if ((pos <= 1) || (pos == nargs))
player:notify("Usage:  @create <parent-class> named [name:]alias,...,alias");
player:notify("   or:  @create <parent-class> named name-and-alias,alias,...,alias");
return;
endif
parentstr = $string_utils:from_list(args[1..pos - 1], " ");
namestr = $string_utils:from_list(args[pos + 1..nargs], " ");
parent = player:my_match_object(parentstr);
if ((parentstr[1] == "$") && (!valid(parent)))
player:notify(tostr("\"", parentstr, "\" does not name an object."));
return;
elseif ($command_utils:object_match_failed(parent, parentstr))
return;
endif
object = player:_create(parent);
if (typeof(object) == ERR)
player:notify(tostr(object));
return;
endif
object.r = 1;
`move(object, player) ! E_NACC';
$building_utils:set_names(object, namestr);
if (other_names = setremove(object.aliases, object.name))
aka = (" (aka " + $string_utils:english_list(other_names)) + ")";
else
aka = "";
endif
if (player:building_option("furnishing"))
`object:moveto(this.location) ! E_NACC';
if ($object_utils:isa(object, $thing))
object.key = this.location;
if (object.location == this.location)
player:notify(tostr("Created ", object.name, aka, " with object number ", object, " and parent ", parent:name(), " (", parent, "), and locked it to ", this.location:dname(), "."));
else
player:notify(tostr("Created ", object.name, aka, " with object number ", object, " and parent ", parent:name(), " (", parent, "), and locked it to ", this.location:dname(), ".  (However, it couldn't be moved there.)"));
endif
else
if (object.location == this.location)
player:notify(tostr("Created ", object.name, aka, " with object number ", object, " and parent ", parent:name(), " (", parent, "), and moved it to ", this.location:dname(), "."));
else
player:notify(tostr("Created ", object.name, aka, " with object number ", object, " and parent ", parent:name(), " (", parent, "), but couldn't move it to ", this.location:dname(), "."));
endif
endif
else
player:notify(tostr("You now have ", object.name, aka, " with object number ", object, " and parent ", parent:name(), " (", parent, ")."));
endif
if (this:building_option("create_set_co"))
co = {object, object.name};
if (stack = this.current_object_stack)
stack[length(stack)] = co;
this:set_current_object_stack(stack);
endif
this:set_current_object(co);
player:tell("The created object is now your current object.");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866495473, \"Erik\", #74, \"JHM\"}";
.
#4:2
set_task_perms(player);
dobj = player:my_match_object(dobjstr);
if (dobj == $nothing)
player:notify(tostr("Usage:  ", verb, " <object>"));
elseif ($command_utils:object_match_failed(dobj, dobjstr))
"...bogus object...";
elseif (player == dobj)
player:notify("I'll bet that you don't *really* want to commit suicide, do you?  If so, then get a wizard to kill you or program it yourself; there will be no state-sanctioned self-destruction on *this* MOO...");
elseif ((dobj.owner != player) && (!player.wizard))
player:notify("It would be rude to recycle that, it's not yours.");
elseif (is_player(dobj))
player:notify("Players cannot be @recycled; they must be @toaded first.");
else
nv = length(verbs(dobj));
if (nv)
prompt = tostr(dobj:dnamec(), " (", dobj, ") has ", nv, " verb", (nv == 1) ? "" | "s", ".  Sure you want to recycle it?");
else
prompt = tostr("Sure you want to recycle ", dobj:dname(), " (", dobj, ")?");
endif
if ($command_utils:yes_or_no(prompt))
name = dobj:dnamec();
result = `player:_recycle(dobj) ! ANY';
if (typeof(result) == ERR)
player:notify(tostr(result));
else
player:notify(tostr(name, " (", dobj, ") recycled."));
endif
else
player:notify("Okay.");
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866496008, \"Erik\", #74, \"JHM\"}";
.
#4:3
"@recreate <object> as <parent-class> named [name:]alias,alias,...";
"  effectively recycles and creates <object> all over again.";
if (player != this)
player:tell("Sorry, you can't do that.");
return E_PERM;
endif
set_task_perms(player);
named = "named" in args;
as = prepstr in args;
if ((named <= (as + 1)) || (named == length(args)))
player:notify(tostr("Usage:  ", verb, " <object> as <parent-class> named [name:]alias,...,alias"));
return;
elseif ($command_utils:object_match_failed(dobj = player:my_match_object(dobjstr), dobjstr))
return;
elseif (is_player(dobj))
player:notify("You really *don't* want to do that!");
return;
endif
parentstr = $string_utils:from_list(args[as + 1..named - 1], " ");
namestr = $string_utils:from_list(args[named + 1..length(args)], " ");
parent = player:my_match_object(parentstr);
if ((parentstr[1] == "$") && (!valid(parent)))
player:notify(tostr("\"", parentstr, "\" does not name an object."));
return;
elseif ($command_utils:object_match_failed(parent, parentstr))
return;
endif
if (!(e = $building_utils:recreate(dobj, parent)))
player:notify(tostr(e));
return;
endif
move(dobj, player);
$building_utils:set_names(dobj, namestr);
if ((other_names = setremove(dobj.aliases, dobj.name)) != {})
aka = (" (aka " + $string_utils:english_list(other_names)) + ")";
else
aka = "";
endif
player:notify(tostr("Object number ", dobj, " is now ", dobj.name, aka, " with parent ", parent:name(), " (", parent, ")."));
.
#4:4
set_task_perms(player);
if (!(dobjstr && iobjstr))
player:notify(tostr("Usage:  ", verb, " <exit-description> to <new-room-name-or-old-room-object-number>"));
return;
endif
exit_spec = dobjstr;
room_spec = iobjstr;
other_room = $code_utils:toobj(room_spec);
if (typeof(other_room) == ERR)
other_room = player:_create((typeof(parent = player:building_option("room")) == OBJ) ? parent | $room);
if (typeof(other_room) == ERR)
player:notify(tostr(other_room));
return;
endif
$building_utils:set_names(other_room, room_spec);
move(other_room, $nothing);
if (other_room.aliases == {other_room.name})
player:notify(tostr(other_room.name, " (", other_room, ") created."));
else
player:notify(tostr(other_room.name, " (", other_room, ") created, with aliases ", $string_utils:english_list(other_room.aliases), "."));
endif
elseif ((!valid(player.location)) || (!($room in $object_utils:ancestors(player.location))))
player:notify(tostr("You may only use the ", verb, " command from inside a room."));
return;
elseif ((!valid(other_room)) || (!($room in $object_utils:ancestors(other_room))))
player:notify(tostr(other_room, " doesn't look like a room to me..."));
return;
endif
if (exit_spec)
exits = $string_utils:explode(exit_spec, "|");
if (!(length(exits) in {1, 2}))
player:notify("The exit-description must have the form");
player:notify("     [name:]alias,...,alias");
player:notify("or   [name:]alias,...,alias|[name:]alias,...,alias");
return;
endif
exit_parent = (typeof(parent = this:building_option("exit")) == OBJ) ? parent | $exit;
to_ok = $building_utils:make_exit(exits[1], player.location, other_room, player, exit_parent);
if (to_ok && (length(exits) == 2))
$building_utils:make_exit(exits[2], other_room, player.location, player, exit_parent);
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866496344, \"Erik\", #74, \"JHM\"}";
.
#4:5
"Usage:  @auditDB [player] [from <start>] [to <end>] [for <matching string>]";
set_task_perms(player);
dobj = player:my_match_player(dobjstr);
if (!dobjstr)
dobj = player;
elseif ($command_utils:player_match_failed(dobj, dobjstr) && (!(valid(dobj = $string_utils:literal_object(dobjstr)) && $command_utils:yes_or_no("Continue?"))))
return;
endif
dobjwords = $string_utils:words(dobjstr);
if (args[1..length(dobjwords)] == dobjwords)
args = args[length(dobjwords) + 1..length(args)];
endif
if (!(parse_result = $code_utils:_parse_audit_args(@args)))
player:notify(tostr("Usage:  ", verb, " [player] [from <start>] [to <end>] [for <match>]"));
return;
endif
start = parse_result[1];
end = parse_result[2];
match = parse_result[3];
player:notify(tostr("Objects owned by ", valid(dobj) ? dobj:name() | dobj, ((" (from #" + tostr(start)) + " to #") + tostr(end), match ? " matching " + match | "", ")", ":"));
player:notify("");
count = 0;
"Only print every third suspension";
do_print = 0;
for i in [start..end]
o = toobj(i);
if ($command_utils:running_out_of_time())
(do_print = (do_print + 1) % 3) || player:notify(tostr("... ", o));
suspend(5);
endif
if (valid(o) && (o.owner == dobj))
found = 0;
names = {o:name(), @o.aliases};
while (names && (!found))
if (index(names[1], match) == 1)
found = 1;
endif
names = listdelete(names, 1);
endwhile
if (found)
player:notify(tostr(o:name(), " (", o, ")"));
count = count + 1;
do_print = 0;
endif
endif
endfor
if (count)
player:notify("");
endif
player:notify(tostr("Total: ", count, " object", (count == 1) ? "." | "s."));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863668034, \"Erik\", #74, \"JHM\"}";
.
#4:6
if (!dobjstr)
dobj = player;
elseif ($command_utils:player_match_result(dobj = player:my_match_player(dobjstr), dobjstr)[1])
return;
endif
set_task_perms(player);
count = length(dobj.owned_objects);
player:notify(tostr(dobj:name(), " currently owns ", count, " object", (count == 1) ? "." | "s."));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863668176, \"Erik\", #74, \"JHM\"}";
.
#4:7
if (!dobjstr)
dobj = player;
elseif ($command_utils:player_match_result(dobj = player:my_match_player(dobjstr), dobjstr)[1])
return;
endif
set_task_perms(player);
count = 0;
for i in [1..tonum(max_object())]
if ($command_utils:running_out_of_time())
player:notify("Counting...");
suspend(5);
endif
o = toobj(i);
if (valid(o) && (o.owner == dobj))
count = count + 1;
endif
endfor
player:notify(tostr(dobj:name(), " currently owns ", count, " object", (count == 1) ? "." | "s."));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863668180, \"Erik\", #74, \"JHM\"}";
.
#4:8
"$player:owned_objects -- sorts a players .owned_objects property in ascending";
"order so it looks nice on @audit.";
if (player != this)
return E_PERM;
endif
if (typeof(player.owned_objects) == LIST)
ret = $list_utils:sort(player.owned_objects);
if (typeof(ret) == LIST)
player.owned_objects = ret;
player:tell("Your .owned_objects list has been sorted.");
return 1;
else
player:tell("Something went wrong. .owned_obejcts not sorted.");
return 0;
endif
else
player:tell("You are not enrolled in .owned_objects scheme, sorry.");
endif
.
#4:9
if (player != this)
player:tell("Permission Denied");
return E_PERM;
endif
if (!valid(dobj))
player:tell("Don't understand `", dobjstr, "' as an object to add.");
elseif (dobj.owner != player)
player:tell("You don't own ", dobj:name(), ".");
elseif (dobj in player.owned_objects)
player:tell(dobj:name(), " is already recorded in your .owned_objects.");
else
player.owned_objects = setadd(player.owned_objects, dobj);
player:tell("Added ", dobj, " to your .owned_objects.");
endif
.
#4:10
for x in (player.owned_objects)
if ((!valid(x)) || (x.owner != player))
player.owned_objects = setremove(player.owned_objects, x);
if (valid(x))
player:tell("Removing ", x:name(), "(", x, "), owned by ", valid(x.owner) ? x.owner:name() | "<recycled player>", " from your .owned_objects property.");
else
player:tell("Removing invalid object ", x, " from your .owned_objects property.");
endif
endif
endfor
player:tell(".owned_objects property verified.");
.
#4:11
set_task_perms(player);
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
return;
endif
try
dobj.key = 0;
except e (ANY)
player:notify(e[2]);
return;
endtry
player:notify(tostr("Unlocked ", dobj:name(), "."));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866496501, \"Erik\", #74, \"JHM\"}";
.
#4:12
set_task_perms(player);
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
return;
endif
key = $lock_utils:parse_keyexp(iobjstr, player);
if (typeof(key) == STR)
player:notify("That key expression is malformed:");
player:notify(tostr("  ", key));
else
try
dobj.key = key;
except e (ANY)
player:notify(e[2]);
return;
endtry
player:notify(tostr("Locked ", dobj:name(), " to this key:"));
player:notify(tostr("  ", $lock_utils:unparse_key(key)));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866496558, \"Erik\", #74, \"JHM\"}";
.
#4:13
"Usage:  @message <message-name> [<message>] [on <object>]";
"Add a message property to an object (default is player), and optionally";
"set its value.  For use by non-programmers, who aren't allowed to add";
"properties generally.";
"To undo the effects of this, use @unmessage.";
set_task_perms(player);
dobjwords = $string_utils:words(dobjstr);
if ((!dobjwords) || index(dobjwords[1], "."))
player:notify(tostr("Usage:  ", verb, " <message-name> [<message>] [on <object>]"));
return;
endif
if (!iobjstr)
object = player;
elseif ($command_utils:object_match_failed(object = player:my_match_object(iobjstr), iobjstr))
return;
endif
name = this:_messagify(dobjwords[1]);
value = player:parse_message(dobjstr[length(dobjwords[1]) + 2..length(dobjstr)]);
nickname = "@" + name[1..length(name) - 4];
try
add_property(object, name, value, {player, "rc"});
except (E_INVARG)
if ($object_utils:has_property(object, name))
"object already has property";
player:notify(tostr(object:dnamec(), " already has a ", nickname, " message."));
elseif (olist = $object_utils:descendants_with_property_suspended(object, name))
player:notify(tostr("The following descendents of ", object:dname(), " have the ", nickname, " message defined:"));
player:notify("  " + $string_utils:from_list(olist, " "));
else
player:notify(tostr("Unable to add ", nickname, " message to ", object:dname(), ": ", E_INVARG));
endif
return;
except e (ANY)
player:notify(e[2]);
return;
endtry
player:notify(tostr(nickname, " on ", object:dname(), " is now \"", player:unparse_message(object.(name)), "\"."));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866497290, \"Erik\", #74, \"JHM\"}";
.
#4:14
"Usage:  @unmessage <message-name> [from <object>]";
"Remove a message property from an object (default is player).";
set_task_perms(player);
if ((!dobjstr) || (length($string_utils:words(dobjstr)) > 1))
player:notify(tostr("Usage:  ", verb, " <message-name> [from <object>]"));
return;
endif
if (!iobjstr)
object = player;
elseif (valid(iobj))
object = iobj;
elseif ($command_utils:object_match_failed(object = player:my_match_object(iobjstr), iobjstr))
return;
endif
name = this:_messagify(dobjstr);
nickname = "@" + name[1..length(name) - 4];
try
delete_property(object, name);
except (E_PROPNF)
player:notify(tostr("No ", nickname, " message found on ", object:name(), "."));
return;
except e (ANY)
player:notify(e[2]);
return;
endtry
player:notify(tostr(nickname, " message removed from ", object:name(), "."));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866497219, \"Erik\", #74, \"JHM\"}";
.
#4:15
"Given any of several formats people are likely to use for a @message";
"property, return the canonical form (\"foobar_msg\").";
name = args[1];
if (name[1] == "@")
name = name[2..length(name)];
endif
if ((length(name) < 4) || (name[length(name) - 3..length(name)] != "_msg"))
name = name + "_msg";
endif
return name;
.
#4:16
"'@kids <obj>' - List the children of an object. This is handy for seeing whether anybody's actually using your carefully-wrought public objects.";
thing = player:my_match_object(dobjstr);
if (!$command_utils:object_match_failed(thing, dobjstr))
kids = children(thing);
if (kids)
player:notify(tostr(thing:dnamec(), "(", thing, ") has ", length(kids), " kid", (length(kids) == 1) ? "" | "s", "."));
player:notify(tostr($string_utils:names_of(kids)));
else
player:notify(tostr(thing:dnamec(), "(", thing, ") has no kids."));
endif
endif
.
#4:17
"'@contents <obj> - list the contents of an object, with object numbers. This verb is by yduJ, hacked on by Doug.";
set_task_perms(player);
if (!dobjstr)
dobj = player.location;
else
dobj = player:my_match_object(dobjstr);
endif
if ($command_utils:object_match_failed(dobj, dobjstr))
else
contents = dobj.contents;
contents_str = contents ? $string_utils:iname_and_number_list(contents) | "nothing";
player:notify(tostr(dobj:dnamec_and_number(), " ", dobj:verb_sub("contains"), " ", contents_str, "."));
endif
.
#4:18
"'@parents <thing>' - List <thing> and its ancestors, all the way back to the Root Class (#1).";
set_task_perms(player);
if (!dobjstr)
player:notify(tostr("Usage:  ", verb, " <object>"));
return;
else
o = player:my_match_object(dobjstr);
endif
if (!$command_utils:object_match_failed(o, dobjstr))
player:notify($string_utils:names_of({o, @$object_utils:ancestors(o)}));
endif
.
#4:19
"@locations <thing> - List <thing> and its containers, all the way back to the outermost one.";
set_task_perms(player);
if (!dobjstr)
what = player;
elseif ((!valid(what = player:my_match_object(dobjstr))) && (!valid(what = player:my_match_player(dobjstr))))
$command_utils:object_match_failed(dobj, dobjstr);
return;
endif
player:notify($string_utils:names_of({what, @$object_utils:locations(what)}));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863668189, \"Erik\", #74, \"JHM\"}";
.
#4:20
set_task_perms(caller_perms());
if (this:building_option("bi_create"))
return $quota_utils:bi_create(@args);
else
return $recycler:(verb)(@args);
endif
.
#4:21
set_task_perms(caller_perms());
if (this:building_option("bi_create"))
return recycle(@args);
else
return $recycler:(verb)(@args);
endif
.
#4:22
"Usage:  @audit [player] [from <start>] [to <end>] [for <matching string>]";
dobj = player:my_match_player(dobjstr);
if (!dobjstr)
dobj = player;
elseif ($command_utils:player_match_result(dobj, dobjstr)[1])
return;
endif
dobjwords = $string_utils:words(dobjstr);
if (args[1..length(dobjwords)] == dobjwords)
args = args[length(dobjwords) + 1..length(args)];
endif
if (!(parse_result = $code_utils:_parse_audit_args(@args)))
player:notify(tostr("Usage:  ", verb, " [player] [from <start>] [to <end>] [for <match>]"));
return;
endif
return $building_utils:do_audit(dobj, @parse_result);
"Metadata 202106";
"Last-Modify: {863668195, \"Erik\", #74, \"JHM\"}";
.
#4:23
"@<what>-option <option> [is] <value>   sets <option> to <value>";
"@<what>-option <option>=<value>        sets <option> to <value>";
"@<what>-option +<option>     sets <option>   (usually equiv. to <option>=1";
"@<what>-option -<option>     resets <option> (equiv. to <option>=0)";
"@<what>-option !<option>     resets <option> (equiv. to <option>=0)";
"@<what>-option <option>      displays value of <option>";
set_task_perms(player);
if ((i = index(verb, "optio")) && (verb[i - 1] != "-"))
what = verb[2..i - 1];
else
what = substitute("%1", match(verb, "^@%([^-]*%)%(o%|opt?i?o?n?s?%|-o?p?t?i?o?n?s?%)$"));
endif
options = what + "_options";
option_pkg = $string_utils:match(("@" + what) + "-options", this:options_packages(), "command");
set_option = ("set_" + what) + "_option";
if (!args)
player:notify_lines({("Current " + what) + " options:", "", @option_pkg:show(this.(options), option_pkg.names)});
return;
elseif (typeof(presult = option_pkg:parse(args)) == STR)
player:notify(presult);
return;
else
if (length(presult) > 1)
if (typeof(sresult = this:(set_option)(@presult)) == STR)
player:notify(sresult);
return;
elseif (!sresult)
player:notify("No change.");
return;
endif
endif
player:notify_lines(option_pkg:show(this.(options), presult[1]));
endif
.
#4:24
":set_edit_option(oname,value)";
":set_display_option(oname,value)";
":set_mail_option(oname,value)";
"Changes the value of the named option.";
"Returns a string error if something goes wrong.";
if (!((caller == this) || $perm_utils:controls(caller_perms(), this)))
return tostr(E_PERM);
endif
"...this is kludgy, but it saves me from writing the same verb 3 times.";
"...there's got to be a better way to do this...";
verb[1..4] = "";
foo_options = verb + "s";
"...";
if (typeof(s = #0.(foo_options):set(this.(foo_options), @args)) == STR)
return s;
elseif (s == this.(foo_options))
return 0;
else
this.(foo_options) = s;
return 1;
endif
.
#4:25
"misc_option(name)";
"client_option(name)";
"return the value of the specified option";
return #0.(verb + "s"):get(this.(verb + "s"), args[1]);
.
#4:26
return {@pass(args), $building_options};
.
#4:27
set_task_perms(player);
dobj = player:my_match_object(dobjstr);
if (dobj == $nothing)
player:notify(tostr("Usage:  ", verb, " <object>"));
elseif ($command_utils:object_match_failed(dobj, dobjstr))
"...bogus object...";
elseif (player == dobj)
player:notify("I'll bet that you don't *really* want to commit suicide, do you?  If so, then get a wizard to kill you or program it yourself; there will be no state-sanctioned self-destruction on *this* MOO...");
else
name = dobj:name();
result = player:_recycle(dobj);
if (typeof(result) == ERR)
player:notify(tostr(result));
else
player:notify(tostr(name, " (", dobj, ") recycled."));
endif
endif
.
#4:28
set_task_perms((caller == this) ? this | $no_one);
dobj = this:my_match_object(dobjstr);
iobj = this:my_match_object(iobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr) || ((iobj != $nothing) && $command_utils:object_match_failed(iobj, iobjstr)))
return;
endif
if ((!$perm_utils:controls(this, dobj)) && (this != dobj))
player:tell("You may only @move your own things.");
return;
endif
old_loc = dobj.location;
if (old_loc == iobj)
player:tell(dobj:dnamec(), " ", dobj:verb_sub("is"), " already ", valid(iobj) ? "in " + iobj:dname() | "nowhere", ".");
return;
endif
if (dobj != player)
dobj:tell("You are moved by ", player:dname(), ".");
endif
dobj:moveto(iobj);
if (dobj.location != old_loc)
if (valid(old_loc))
old_loc:announce_all_but({dobj}, dobj:dnamec(), " ", dobj:verb_sub("disappears"), " suddenly for parts unknown", (player == dobj) ? "." | ((", moved by " + player:dname()) + "."));
endif
if (valid(dobj.location))
dobj.location:announce_all_but({dobj}, dobj:inamec(), " ", dobj:verb_sub("materializes"), " out of thin air", (player == dobj) ? "." | ((", moved by " + player:dname()) + "."));
endif
if (dobj.location == iobj)
player:tell("Moved.");
elseif (dobj == player)
player:tell("You have been deflected from your original destination.");
else
player:tell($string_utils:pronoun_sub($pronoun_sub.("two-letter"):parse("%dD %d:(has) been deflected from %dp original destination.")));
endif
else
if ($object_utils:contains(dobj, iobj))
player:tell(iobj:dnamec(), " is inside of ", dobj:dname(), "!");
else
player:tell($string_utils:pronoun_sub($pronoun_sub.("two-letter"):parse("Either %dd %d:(doesn't) want to go, or %id %i:(doesn't) want to accept %do.")));
if (dobj != player)
dobj:tell("Well, ", player:ps(), " tried, but you didn't actually go anywhere.");
endif
endif
endif
.
#4:29
"Syntax:";
"  @measure object <object name>";
"  @measure summary [player]";
"  @measure new [player]";
"  @measure breakdown <object name>";
if (length(args) < 1)
player:tell_lines($code_utils:verb_documentation());
return;
endif
if (index("object", args[1]) == 1)
"Object.";
what = player.location:match_object(name = $string_utils:from_list(args[2..length(args)], " "));
if (!valid(what))
player:tell("Sorry, I didn't understand `", name, "'");
else
player:tell("Checking size of ", what:dname(), " (", what, ")...");
player:tell("Size of ", what:dname(), " (", what, ") is ", $quota_utils.byte:object_bytes(what), " bytes.");
endif
elseif (index("summary", args[1]) == 1)
"Summarize player.";
if (length(args) == 1)
what = player;
else
what = player:my_match_player(name = $string_utils:from_list(args[2..length(args)], " "));
endif
if (!valid(what))
player:tell("Sorry, I don't know who you mean by `", name, "'");
else
$quota_utils.byte:do_summary(what);
endif
elseif (index("new", args[1]) == 1)
if (length(args) == 1)
what = player;
elseif (!valid(what = player:my_match_player(name = $string_utils:from_list(args[2..length(args)], " "))))
return $command_utils:player_match_failed(what, name);
endif
player:tell("Measuring the sizes of ", what:dname(), "'s recently created objects...");
total = 0;
unmeasured_index = 4;
unmeasured_multiplier = 100;
nunmeasured = 0;
if (typeof(what.owned_objects) == LIST)
for x in (what.owned_objects)
if (!$object_utils:has_property(x, "object_size"))
nunmeasured = nunmeasured + 1;
elseif (!x.object_size[1])
player:tell("Measured ", $string_utils:nn(x), ":  ", size = $quota_utils.byte:object_bytes(x), " bytes.");
total = total + size;
endif
$command_utils:suspend_if_needed(5);
endfor
if (nunmeasured && (what.size_quota[unmeasured_index] < (unmeasured_multiplier * nunmeasured)))
what.size_quota[unmeasured_index] = (what.size_quota[unmeasured_index] % unmeasured_multiplier) + (nunmeasured * unmeasured_multiplier);
endif
player:tell("Total bytes used in new creations: ", total, ".", nunmeasured ? tostr("There were a total of ", nunmeasured, " object(s) found with no .object_size property.  This will prevent additional building.") | "");
else
player:tell("Sorry, ", what:dname(), " is immune to such study.");
endif
elseif (index("breakdown", args[1]) == 1)
what = player.location:match_object(name = $string_utils:from_list(args[2..length(args)], " "));
if (!valid(what))
player:tell("Sorry, I didn't understand `", name, "'");
elseif (!$quota_utils.byte:can_peek(player, what.owner))
return player:tell("Sorry, you don't control ", what:dname(), " (", what, ")");
else
if (mail = $command_utils:yes_or_no("This might be kinda long.  Want me to mail you the result?"))
player:tell("Result will be mailed.");
endif
info = $quota_utils.byte:do_breakdown(what);
if (typeof(info) == ERR)
player:tell(info);
endif
if (mail)
$mail_agent:send_message($quota_utils.byte.owner, {player}, tostr("Object breakdown of ", what:dname(), " (", what, ")"), info);
else
player:tell_lines_suspended(info);
endif
endif
else
player:tell("Not a sub-command of @measure: ", args[1]);
player:tell_lines($code_utils:verb_documentation());
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863668340, \"Erik\", #74, \"JHM\"}";
.
#4:30
"Usage:  @grant <object> to <recipient> (used by owner)";
"        @grant <object> to me          (used by recipient)";
"";
"Change the ownership of an object, including all properties and verbs owned by the owner.";
"";
"Shouldn't transfer (at least verbs) between wizards and non-wizards.";
if (player != this)
player:notify("Sorry.");
return;
elseif ((!dobjstr) || (!iobjstr))
player:notify_lines($code_utils:verb_usage());
return;
endif
dobj = player:my_match_object(dobjstr);
iobj = player:my_match_player(iobjstr, player);
if (!($command_utils:player_match_failed(iobj, iobjstr) && (!$command_utils:object_match_failed(dobj, dobjstr))))
if (is_player(dobj))
player:tell("Sorry, you can't @grant players.");
elseif (dobj.owner == player)
if (is_player(dobj.offered))
revoke = 1;
endif
if (iobj in {player, $no_one})
if (revoke)
player:notify(tostr("Okay, revoking offer to ", dobj.offered:dname(), "."));
else
player:notify(tostr(dobj:dnamec(), " wasn't offered to anybody."));
endif
dobj.offered = $nothing;
else
if (revoke)
player:notify(tostr("Okay, revoking offer to ", dobj.offered:dname(), " and offering ", dobj:dname(), " to ", iobj:dname(), "."));
else
player:notify(tostr("Okay, offering ", dobj:dname(), " to ", iobj:dname(), "."));
endif
dobj.offered = iobj;
endif
elseif (iobj == player)
if (dobj.offered == player)
dobj.offered = $nothing;
if (!$quota_utils:creation_permitted(player))
player:notify(tostr("You don't have enough quota to do the transfer."));
else
player:notify(tostr(`$wiz_utils:do_grant(dobj, iobj, 1) ! ANY'));
endif
else
player:notify(tostr(dobj.owner:dnamec(), " hasn't offered ", dobj:dname(), " to you."));
endif
else
player:notify(tostr("You don't own ", dobj:dname(), "."));
endif
endif
"Copied from generic player (#6):@grant by Nosredna (#72) Wed Apr 10 14:53:33 1996 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866504401, \"Erik\", #74, \"JHM\"}";
.
#5:0
"Usage: get/take <thing>";
"  This allows you to pick up things.";
set_task_perms(callers() ? caller_perms() | player);
if (this.location == player)
player:tell("You already have that!");
elseif (this.location != player.location)
player:tell("That's not here.");
else
this:moveto(player);
if (this.location == player)
player:tell_lines(this:take_succeeded_msg() || "Taken.");
if (msg = this:otake_succeeded_msg())
player.location:announce(player:dnamec(), " ", msg);
endif
else
player:tell_lines(this:take_failed_msg() || "You can't pick that up.");
if (msg = this:otake_failed_msg())
player.location:announce(player:dnamec(), " ", msg);
endif
endif
endif
.
#5:1
"Usage: drop <thing>";
" This drops things in the room the dropper is in.";
set_task_perms(callers() ? caller_perms() | player);
if (this.location != player)
player:tell("You don't have that.");
elseif (!player.location:accept(this))
player:tell("You can't drop that here.");
else
this:moveto(player.location);
if (this.location == player.location)
player:tell_lines(this:drop_succeeded_msg() || "Dropped.");
if (msg = this:odrop_succeeded_msg())
player.location:announce(player:dnamec(), " ", msg);
endif
else
player:tell_lines(this:drop_failed_msg() || "You can't seem to drop that here.");
if (msg = this:odrop_failed_msg())
player.location:announce(player:dnamec(), " ", msg);
endif
endif
endif
.
#5:2
where = args[1];
if ((!valid(where)) || this:is_unlocked_for(where))
pass(where);
endif
.
#5:3
set_task_perms(caller_perms());
return $string_utils:pronoun_sub(this.(verb));
.
#5:4
"Usage: give <thing> to <person>";
"This lets you experience the joy of giving things to others.";
set_task_perms(callers() ? caller_perms() | player);
action = (verb[1] == "g") ? "give" | "hand";
if (this.location != player)
player:tell("You don't have ", this:dname(), "!");
elseif (!valid(player.location))
player:tell("You see no \"", iobjstr, "\" here.");
elseif ($command_utils:object_match_failed(who = player.location:match_object(iobjstr), iobjstr))
elseif (who.location != player.location)
player:tell("You see no \"", iobjstr, "\" here.");
elseif (who == player)
player:tell("You already have ", this:dname(), ".");
else
this:moveto(who);
if (this.location == who)
player:tell("You ", action, " ", this:dname(), " to ", who:dname(), ".");
who:tell(player:dnamec(), " ", player:verb_sub(action + "s"), " you ", this:iname(), ".");
player.location:announce_all_but({player, who}, player:dnamec(), " ", player:verb_sub(action + "s"), " ", who:dname(), " ", this:iname(), ".");
else
player:tell(who:dnamec(), " ", who:verb_sub("does"), " not want that item.");
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869628335, \"Erik\", #74, \"JHM\"}";
.
#5:5
"examine_key(examiner)";
"return a list of strings to be told to the player, indicating what the key on this type of object means, and what this object's key is set to.";
"the default will only tell the key to a wizard or this object's owner.";
who = args[1];
if (((caller == this) && $perm_utils:controls(who, this)) && (this.key != 0))
return {tostr(this:dnamec(), " can only be moved to locations matching this key:"), tostr("  ", $lock_utils:unparse_key(this.key))};
endif
.
#5:6
pass(@args);
if (parent(this) == $thing)
this.unique = 0;
endif
.
#5:7
all_help = `this.help_text ! E_PROPNF => {}';
if (typeof(all_help) == STR)
all_help = {all_help};
endif
helpless = {};
for vrb in (this:help_verbs(player))
if (typeof(vrb) == LIST)
vrb = vrb[2];
if (space = index(vrb, " "))
vrb = vrb[1..space - 1];
endif
vrb = strsub(vrb, "*", "");
endif
if (loc = $object_utils:has_verb(this, vrb))
$command_utils:suspend_if_needed(0);
help = $code_utils:verb_documentation(loc[1], vrb);
if (help)
try
msg = verb_info(loc[1], vrb)[3];
except v (ANY)
msg = v[2];
endtry
underline = $string_utils:space(length(msg), "=");
if (all_help)
"all_help = {@all_help, \"\", tostr(this) + \":\" + verb_info(this, vrb)[3], @help};";
all_help = {@all_help, "", msg, underline, "", @help};
else
"all_help = {tostr(this) + \":\" + verb_info(this, vrb)[3], @help};";
all_help = {msg, underline, "", @help};
endif
else
helpless = {@helpless, vrb};
endif
endif
endfor
if (helpless)
if (all_help)
all_help = {@all_help, "", ("No help found on " + $string_utils:english_list(helpless, "nothing", " or ")) + "."};
else
return {tostr("Sorry, but no help is avilable on ", name = this:name(), "(", this, ").", "  Try `examine ", name, "'.")};
endif
endif
return all_help ? {@all_help, "", "----"} | {};
"Metadata 202106";
"Last-Modify: {869628524, \"Erik\", #74, \"JHM\"}";
.
#6:0
if (!$__core_init_phase)
raise(E_PERM);
endif
pass(@args);
if (is_player(this))
this.home = $player_start;
move(this, $player_start);
else
move(this, $room);
endif
if (this == $player)
this.help = {$object_help, $feature_help};
this.followers = {};
this.current_folder = 1;
this.responsible = {};
`delete_property(this, "client_type") ! ANY';
`delete_property(this, "use_do_command") ! ANY';
`delete_verb(this, "client_type") ! ANY';
`delete_property(this, "projects") ! ANY';
`delete_verb(this, "@projects") ! ANY';
`delete_verb(this, "last_entrance_time") ! ANY';
`delete_verb(this, "@programmer") ! ANY';
`delete_verb(this, "@tutorial") ! ANY';
else
for p in ({"all_connect_places", "last_connect_place", "gaglist", "messages", "messages_going", "features", "responsible", "current_folder", "mail_forward", "followers"})
clear_property(this, p);
endfor
if (is_player(this) && (this != $first_wizard))
this.mail_forward = "The %t isn't a real person.  You may want to mail an admin group instead.";
endif
$first_wizard.features = setadd($first_wizard.features, $community_group.feature);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {880319835, \"Ken\", #75, \"JHM\"}";
.
#6:1
if ((valid(caller_perms()) && (caller != this)) && (caller != #0))
return E_PERM;
endif
$news:check();
nm = (cm = this:get_current_message(this)) ? this:length_all_msgs() - this:length_date_le(cm[2]) | E_NONE;
if (nm)
this:notify(tostr("You have new mail (", nm, " message", (nm == 1) ? "" | "s", ").", this:mail_option("expert") ? "" | "  Type 'help mail' for info on reading it."));
endif
this:mail_catch_up();
this:check_mail_lists();
if (is_clear_property(this, "description"))
this:notify("Before going anywhere, you might want to describe yourself; type `help describe' for information." + (is_clear_property(this, "gender") ? "  You should probably also set a gender (`help gender')." | ""));
endif
"This is forked off to protect :confunc from buggy :player_connected verbs.";
fork (0)
for feature in (this:features())
if ($object_utils:has_callable_verb(feature, "player_connected"))
feature:player_connected(player, @args);
endif
endfor
endfork
.
#6:2
if (!(caller in {#0, this}))
return E_PERM;
endif
"Part 1:  really_disconnected event.";
this:maybe_really_disconnected();
"Part 2:  Notify features of disconnection.";
"This is forked off to protect :disfunc from buggy :player_disconnected verbs.";
fork (0)
for feature in (this:features())
if ($object_utils:has_callable_verb(feature, "player_disconnected"))
feature:player_disconnected(player, @args);
endif
endfor
endfork
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869628807, \"Erik\", #74, \"JHM\"}";
.
#6:3
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
this.help = 0;
return pass(@args);
else
return E_PERM;
endif
.
#6:4
set_task_perms(caller_perms());
if ((!is_player(this)) || caller_perms().wizard)
pass(@args);
return;
endif
"...start off with a wizard shout...";
for p in (connected_players())
if (p.wizard)
p:tell($string_utils:pronoun_sub($pronoun_sub.lambdacore:parse("%N (%#) is currently trying to recycle %t (%[#t])")));
endif
endfor
"...Okay here's the fun part.";
"...Doing kill_task(task_id()) doesn't work because the server can";
"...figure out that it's okay to go ahead and recycle once the task finishes.";
"...Evidently, suspend() confuses the server sufficiently that it forgets to";
"...do the recycle once the task finishes or dies.  Now of course, we don't";
"...want suspended tasks hanging around indefinitely, so we fork something";
"...off to kill it.  This seems to work...";
t = task_id();
fork (1)
kill_task(t);
endfork
"...let him think he succeeded (should we do this ?)...no.";
"...boot_player(this)";
"...emergency life support...";
suspend(1073741823);
"...code not reached --- the patient lives...";
"...keep this around for posterity...";
if (is_player(this))
for a in (this.aliases)
$player_db:delete(a);
endfor
$player_db:delete(this.name);
endif
pass(@args);
.
#6:5
"Extra parsing of player commands.  Called by $command_utils:do_huh.";
"This version of my_huh just handles features.";
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
endif
"verb - obvious                 pass - would be args";
"plist - list of prepspecs that this command matches";
"dlist and ilist - likewise for dobjspecs, iobjspecs";
verb = args[1];
if ($string_utils:is_numeric(verb))
return 0;
endif
pass = args[2];
plist = {"any", prepstr ? $code_utils:full_prep(prepstr) | "none"};
dlist = dobjstr ? {"any"} | {"none", "any"};
ilist = iobjstr ? {"any"} | {"none", "any"};
focus = this.focus_object;
if (`focus:match_command(verb, dlist, plist, ilist) ! E_VERBNF, E_INVIND')
set_task_perms(this);
focus:(verb)(@pass);
return 1;
endif
for fobj in (this:features())
try
match = fobj:match_command(verb, dlist, plist, ilist);
except (E_VERBNF, E_INVIND)
this:remove_feature(fobj);
endtry
if (match)
set_task_perms(this);
fobj:(verb)(@pass);
return 1;
endif
if ($command_utils:running_out_of_time())
player:tell("You have too many features.  Parsing your command runs out of ticks while checking ", fobj:name(), " (", fobj, ").");
return 1;
endif
endfor
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869629514, \"Erik\", #74, \"JHM\"}";
.
#6:6
":last_huh(verb,args)  final attempt to parse a command...";
set_task_perms(caller_perms());
{verb, args} = args;
if ((verb[1] == "@") && (prepstr == "is"))
"... set or show _msg property ...";
set_task_perms(player);
$last_huh:(verb)(@args);
elseif ((verb[1] != "@") && `verb_info($last_huh, verb) ! E_VERBNF')
$last_huh:(verb)(@args);
elseif (verb[length(verb)] == ",")
if (!valid(stage = $feature:match_type_object("stage")))
return 0;
endif
call = "`" + verb[1..length(verb) - 1];
call = strsub(call, ",", " ");
stage:(call)(argstr);
else
return 0;
endif
return 1;
"Copied from r'm (#1100):last_huh by r'm (#1100) Thu Sep  8 22:39:02 1994 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869629777, \"Erik\", #74, \"JHM\"}";
.
#6:7
":my_match_object(string [,location])";
return $string_utils:match_object(@{@args, this.location}[1..2], this);
.
#6:8
c = args[1];
if (c)
longear = {};
gear = {};
width = player:linelen();
half = width / 2;
player:tell("Carrying:");
for thing in (c)
cx = tostr(" ", thing:name_for_tell_contents());
if (length(cx) > half)
longear = {@longear, cx};
else
gear = {@gear, cx};
endif
endfor
player:tell_lines($string_utils:columnize(gear, 2, width));
player:tell_lines(longear);
endif
.
#6:9
"now properly understands extra args, even if it ignores them, and returns 1 on success like notify() does in 1.8.0";
"6/13/96: extra args are now passed through in the case where linewrap and pagination are turned off. --Rog";
"11/18/97: fixed OOB quoting --Rog";
{line, @xargs} = args;
if (match(line, "^#%$[#\"]"))
line = "#$\"" + line;
endif
"... WAS:";
"if ((length(line) >= 3) && (line[1..3] == \"#$#\"))";
"  line = \" \" + line;";
"endif";
"";
if (this.pagelen)
if (!(this in connected_players()))
"...drop it on the floor...";
return 1;
elseif ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
"...need wizard perms if this and this.owner are different, since...";
"...only this can notify() and only this.owner can read .linebuffer...";
if (((player == this) && this.linetask) && (this.linetask[2] != task_id()))
"...player has started a new task...";
"....linetask[2] is the taskid of the most recent player task...";
if (this.linetask[2] != this.linetask[1])
this.linesleft = this.pagelen - 2;
endif
this.linetask[2] = task_id();
endif
"... digest the current line...";
if (this.linelen > 0)
lbuf = {@this.linebuffer, @this:linesplit(line, this.linelen)};
else
lbuf = {@this.linebuffer, line};
endif
"... print out what we can...";
if (this.linesleft)
howmany = min(this.linesleft, length(lbuf));
for l in (lbuf[1..howmany])
notify(this, l);
endfor
this.linesleft = this.linesleft - howmany;
lbuf[1..howmany] = {};
endif
if (lbuf)
"...need we say ***More***?         ---suggested by Ford";
if (this.linetask[1] != this.linetask[2])
"....linetask[1] is the taskid of the most recent player task";
"...   for which ***More*** was printed...";
this.linetask[1] = this.linetask[2];
fork (0)
notify(this, this:more_string(length(this.linebuffer)));
endfork
endif
llen = length(lbuf);
if (llen > 500)
"...way too much saved text, flush some of it...";
lbuf[1..llen - 100] = {"*** buffer overflow, lines flushed ***"};
endif
endif
this.linebuffer = lbuf;
else
if (caller != this)
set_task_perms(caller_perms());
endif
if (this.linelen > 0)
for l in (this:linesplit(line, this.linelen))
notify(this, l);
endfor
else
notify(this, line, @xargs);
endif
endif
return 1;
"Metadata 202106";
"Last-Modify: {879907454, \"Rog\", #145, \"JHM\"}";
.
#6:10
if ($perm_utils:controls(caller_perms(), this) || (caller == this))
set_task_perms(caller_perms());
for line in ((typeof(lines = args[1]) != LIST) ? {lines} | lines)
this:notify(line);
endfor
else
return E_PERM;
endif
.
#6:11
"Copied from Joe (#73):linesplit Tue Apr 26 17:55:58 1994 EDT";
":linesplit(line,len) => list of substrings of line";
"used by :notify to split up long lines if .linelen>0";
line = args[1];
len = args[2];
cline = {};
while (length(line) > len)
cutoff = rindex(line[1..len], " ");
if (nospace = cutoff < ((4 * len) / 5))
cutoff = len + 1;
nospace = line[cutoff] != " ";
endif
cline = {@cline, line[1..cutoff - 1]};
line = " " + $string_utils:triml(line[cutoff..length(line)]);
endwhile
return {@cline, line};
.
#6:12
return abs(this.linelen);
.
#6:13
if (player != this)
"... somebody's being sneaky...";
"... Can't do set_task_perms(player) since we need to be `this'...";
"... to notify and `this.owner' to change +c properties...";
return;
elseif (!(lbuf = this.linebuffer))
this.linesleft = this.pagelen - 2;
notify(this, "*** No more ***");
elseif (index("flush", dobjstr || "x") == 1)
this.linesleft = this.pagelen - 2;
notify(this, tostr("*** Flushed ***  ", length(lbuf), " lines"));
this.linebuffer = {};
elseif ((index("rest", dobjstr || "x") == 1) || (!this.pagelen))
this.linesleft = this.pagelen - 2;
for l in (lbuf)
notify(this, l);
endfor
this.linebuffer = {};
else
howmany = min(this.pagelen - 2, llen = length(lbuf = this.linebuffer));
for l in (lbuf[1..howmany])
notify(this, l);
endfor
this.linesleft = (this.pagelen - 2) - howmany;
this.linebuffer = lbuf[howmany + 1..llen];
if (howmany < llen)
notify(this, this:more_string(llen - howmany));
this.linetask[1] = task_id();
endif
endif
this.linetask[2] = task_id();
.
#6:14
if (player != this)
"... someone is being sneaky...";
"... Can't do set_task_perms(player) since we need to be `this'...";
"... to notify and `this.owner' to change +c properties...";
return;
endif
linelen = player.linelen;
if (!(prepstr in {"on", "off"}))
player:notify("Usage:  @wrap on|off");
player:notify(tostr("Word wrap is currently ", (linelen > 0) ? "on" | "off", "."));
return;
endif
player.linelen = abs(linelen) * ((prepstr == "on") ? 1 | -1);
player:notify(tostr("Word wrap is now ", prepstr, "."));
.
#6:15
if (player != this)
"... somebody is being sneaky ...";
return;
endif
curlen = player.linelen;
wrap = curlen > 0;
wrapstr = wrap ? "on" | "off";
if (!dobjstr)
player:notify(tostr("Usage:  ", verb, " <number>"));
player:notify(tostr("Current line length is ", abs(curlen), ".  Word wrapping is ", wrapstr, "."));
return;
endif
newlen = tonum(dobjstr);
if (newlen < 0)
player:notify("Line length can't be a negative number.");
return;
elseif (newlen < 10)
player:notify("You don't want your linelength that small.  Setting it to 10.");
newlen = 10;
endif
player.linelen = wrap ? newlen | (-newlen);
player:notify(tostr("Line length is now ", abs(player.linelen), ".  Word wrapping is ", wrapstr, "."));
if (!wrap)
player:notify("To enable word wrapping, type `@wrap on'.");
endif
.
#6:16
"@pagelength number  -- sets page buffering to that many lines (or 0 to turn off page buffering)";
if (player != this)
"... somebody is being sneaky ...";
"... Can't do set_task_perms(player) since we need to be `this'...";
"... to notify and `this.owner' to change +c properties...";
return;
elseif (!dobjstr)
notify(player, tostr("Usage:  ", verb, " <number>"));
notify(player, tostr("Current page length is ", player.pagelen, "."));
return;
elseif (0 > (newlen = tonum(dobjstr)))
notify(player, "Page length can't be a negative number.");
return;
elseif (newlen == 0)
player.pagelen = 0;
notify(player, "Page buffering off.");
elseif (newlen < 5)
player.pagelen = 5;
notify(player, "Too small.  Setting it to 5.");
else
notify(player, tostr("Page length is now ", player.pagelen = newlen, "."));
endif
if (this.linebuffer)
notify(this, this:more_string(length(this.linebuffer)));
player.linetask = {task_id(), task_id()};
player.linesleft = 0;
else
player.linetask = {0, task_id()};
player.linesleft = player.pagelen - 2;
endif
.
#6:17
if (!$network:is_connected(this))
return;
endif
if (this.gaglist || this.paranoid)
"Check the above first, default case, to save ticks.  Paranoid gaggers are cost an extra three or so ticks by this, probably a net savings.";
if (g = this:gag_p())
return;
endif
if (this.paranoid == 2)
z = this:whodunnit({@callers(), {player, "", player}}, {this, $no_one}, {})[3];
args = {"(", z:name(), " ", z, ") ", @args};
elseif (this.paranoid == 1)
if (this in connected_players())
this.responsible = {@this.responsible, {{@callers(), {player, "<cmd-line>", player}}, {tostr(@args)}}};
if (((len = length(this.responsible)) * 2) > (this.lines * 3))
this.responsible = this.responsible[(len - this.lines) + 1..len];
endif
endif
else
this.responsible = {};
endif
endif
if ((v = this:misc_option("tstamp")) < 0)
if (((time() - this.last_timestamp) > abs(v * 60)) && (!($login_watcher in $list_utils:slice(callers(), 4))))
this:notify(this:timestamp_string());
endif
endif
pass(@args);
if (v > 0)
if (((time() - this.last_timestamp) > (v * 60)) && (!($login_watcher in $list_utils:slice(callers(), 4))))
this:notify(this:timestamp_string());
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {877532579, \"Ken\", #75, \"JHM\"}";
.
#6:18
gag = this.gaglist;
if (player in gag)
return 1;
elseif (gag)
for x in (callers())
if (valid(x[1]) && (!is_player(x[1])))
if ((x[1] in gag) || (x[4] in gag))
return 1;
endif
endif
endfor
endif
return 0;
.
#6:19
":set_gaglist(@newlist) => this.gaglist = newlist";
verb[1..4] = "";
if (!((caller == this) || $perm_utils:controls(caller_perms(), this)))
return E_PERM;
else
return this.(verb) = args;
endif
.
#6:20
set_task_perms(player);
if (player != this)
player:notify("Permission denied.");
return;
endif
if (!args)
player:notify(tostr("Usage:  ", verb, " <player or object> [<player or object>...]"));
return;
endif
victims = $string_utils:match_player_or_object(@args);
changed = 0;
for p in (victims)
if (p in player.gaglist)
player:notify(tostr("You are already gagging ", p:name(), "."));
elseif (p == player)
player:notify("Gagging yourself is a bad idea.");
else
changed = 1;
player:set_gaglist(@setadd(this.gaglist, p));
endif
endfor
if (changed)
fork (0)
"No running out of time for *me*!";
this:("@listgag")();
endfork
endif
.
#6:21
set_task_perms(valid(caller_perms()) ? caller_perms() | player);
if (!this.gaglist)
player:notify(tostr("You are ", callers() ? "no longer gagging anything." | "not gagging anything right now."));
else
player:notify(tostr("You are ", callers() ? "now" | "currently", " gagging ", $string_utils:name_and_number_list(this.gaglist), "."));
endif
gl = {};
for p in (players())
if ((typeof(`p.gaglist ! E_PROPNF') == LIST) && (this in p.gaglist))
gl = {@gl, p};
endif
$command_utils:suspend_if_needed(0);
endfor
if (gl || (!callers()))
player:notify(tostr($string_utils:name_and_number_list(gl, "No one"), " appear", (length(gl) <= 1) ? "s" | "", " to be gagging you."));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869631298, \"Erik\", #74, \"JHM\"}";
.
#6:22
set_task_perms(player);
if (player != this)
player:notify("Permission denied.");
elseif (dobjstr == "")
player:notify(tostr("Usage:  ", verb, " <player>  or  ", verb, " everyone"));
elseif (dobjstr == "everyone")
this:set_gaglist();
player:notify("You are no longer gagging anyone or anything.");
else
victims = $string_utils:match_player_or_object(@args);
for match in (victims)
if (match in this.gaglist)
this:set_gaglist(@setremove(this.gaglist, match));
player:notify(tostr(valid(match) ? match:name() | match, " removed from gag list."));
else
player:notify(tostr(valid(match) ? match:name() | match, " doesn't seem to be in your gag list."));
endif
endfor
this:("@listgag")();
endif
.
#6:23
record = args[1];
trust = args[2];
mistrust = args[3];
s = {this, "???", this};
for w in (record)
if ((!valid(w[3])) && (!valid(w[1])))
continue;
endif
if (valid(s[3]) && (((s[3].wizard || (s[3] in trust)) && (!(s[3] in mistrust))) || (s[1] == this)))
s = w;
else
return s;
endif
endfor
return s;
.
#6:24
if ((args == {}) || ((typ = args[1]) == ""))
this.lines = 20;
this.paranoid = 1;
this:notify("Anti-spoofer on and keeping 20 lines.");
elseif (index("immediate", typ))
this.lines = 0;
this.paranoid = 2;
this:notify("Anti-spoofer now in immediate mode.");
elseif (index("off", typ) || (typ == "0"))
this.paranoid = 0;
this.lines = 0;
this:notify("Anti-spoofer off.");
elseif ((tostr(y = tonum(typ)) != typ) || (y < 0))
this:notify(tostr("Usage: ", verb, " <lines to be kept>     to turn on your anti-spoofer."));
this:notify(tostr("       ", verb, " off                    to turn it off."));
this:notify(tostr("       ", verb, " immediate              to use immediate mode."));
else
this.paranoid = 1;
this.lines = y;
this:notify(tostr("Anti-spoofer on and keeping ", y, " lines."));
endif
.
#6:25
buggers = 1;
found_listener = 0;
here = this.location;
for thing in (setremove(here.contents, this))
tellwhere = $object_utils:has_verb(thing, "tell");
notifywhere = $object_utils:has_verb(thing, "notify");
if (thing in connected_players())
this:notify(tostr(thing:name(), " (", thing, ") is listening."));
found_listener = 1;
elseif ($object_utils:has_callable_verb(thing, "sweep_msg") && (typeof(msg = thing:sweep_msg()) == STR))
this:notify(tostr(thing:name(), " (", thing, ") ", msg, "."));
found_listener = 1;
elseif (tellwhere && (((owner = verb_info(tellwhere[1], "tell")[1]) != this) && (!owner.wizard)))
this:notify(tostr(thing:name(), " (", thing, ") has been taught to listen by ", owner:name(), " (", owner, ")"));
found_listener = 1;
elseif (notifywhere && (((owner = verb_info(notifywhere[1], "notify")[1]) != this) && (!owner.wizard)))
this:notify(tostr(thing:name(), " (", thing, ") has been taught to listen by ", owner:name(), " (", owner, ")"));
found_listener = 1;
endif
endfor
buggers = {};
for v in ({"announce", "announce_all", "announce_all_but", "say", "emote", "huh", "here_huh", "huh2", "whisper"})
vwhere = $object_utils:has_verb(here, v);
if ((vwhere && (((owner = verb_info(vwhere[1], v)[1]) != this) && (!owner.wizard))) && (owner != $hacker))
buggers = setadd(buggers, owner);
endif
endfor
if (buggers != {})
if ($object_utils:has_verb(here, "sweep_msg") && (typeof(msg = here:sweep_msg()) == STR))
this:notify(tostr(here:name(), " (", here, ") ", msg, "."));
else
this:notify(tostr(here:name(), " (", here, ") may have been bugged by ", $string_utils:english_list($list_utils:map_prop(buggers, "name")), "."));
endif
elseif (!found_listener)
this:notify("Communications look secure.");
endif
.
#6:26
if (this.location != player.location)
player:tell(this:dnamec(), " isn't here.");
elseif (this in connected_players())
msg = $pronoun_sub.("two-letter"):parse("%nD %n:(whispers) to %td, \"%$d\"");
parties = $pronoun_sub:parse_parties({}, this);
for o in (setadd({player}, this))
$you:say(msg, o, parties);
endfor
return;
this:tell(player:dnamec(), " whispers, \"", dobjstr, "\"");
player:tell("You whisper, \"", dobjstr, "\" to ", this:dname(), ".");
else
player:tell("You begin to whisper to ", this:dname(), ", but then notice that ", this:ps(), this:verb_sub("'s/'re"), " asleep.");
endif
"Copied from generic player (#6):whisper by the folding couch (#437) Mon Jun 12 15:58:27 1995 EDT";
.
#6:27
"called by $player:page.  Two args, the page header and the text, all pre-processed by the page command.  Could be extended to provide haven abilities, multiline pages, etc.  Indeed, at the moment it just does :tell_lines, so we already do have multiline pages, if someone wants to take advantage of it.";
"Return 1 to say we successfully received the page.  If a specialization wants to refuse a page, it should return 0 to say it was refused.  If it uses pass(@args) it should propagate back up the return value.  It is possible that this code should interact with gagging and return 0 if the page was gagged.";
this:tell_lines(args);
return 1;
.
#6:28
"set_task_perms(this.owner)";
return (msg = this.(verb)) ? $string_utils:pronoun_sub(this.(verb), this) | "";
.
#6:29
if (player.contents)
this:tell_contents(player.contents);
else
player:tell("You are empty-handed.");
endif
.
#6:30
integrate = {};
dontintegrate = {};
if (visible = this:visible_of(this.contents))
for i in (visible)
if (this:ok_to_integrate(i))
integrate = {@integrate, i};
else
dontintegrate = {@dontintegrate, i};
endif
endfor
endif
desc = this:description(integrate);
if (desc)
player:tell_lines(desc);
else
player:Tell("You see nothing special.");
endif
player:tell(this:desc_idle_msg());
if (dontintegrate)
this:tell_contents(dontintegrate);
endif
.
#6:31
start = this.location;
home = this.home;
if (start == home)
player:tell("You're already home!");
return;
elseif (typeof(home) != OBJ)
player:tell("You've got a weird home, pal.  I've reset it to the default one.");
this.home = $player_start;
elseif (!$recycler:valid(home))
player:tell("Oh no!  Your home's been recycled.  Time to look around for a new one.");
this.home = $player_start;
endif
"Lose all followers.  --Doug, 5/4/94";
for p in (this.followers)
p.following = setremove(p.following, this);
endfor
this.followers = {};
home = this.home;
if (!this:walkto(home))
player:tell("You click your heels three times.");
this:moveto(home);
if (!valid(start))
elseif (start == this.location)
start:announce(player:name(), " learns that you can never go home...");
else
start:announce(player:name(), " goes home.");
endif
if (this.location == home)
this.location:announce(player:name(), " comes home.");
elseif (this.location == start)
player:tell("Either home doesn't want you, or you don't really want to go.");
else
player:tell("Wait a minute!  This isn't your home...");
if (valid(this.location))
this.location:announce(player:name(), " arrives, looking quite bewildered.");
endif
endif
endif
.
#6:32
set_task_perms(this);
here = this.location;
if (typeof(ERR = this:set_home(here)) == OBJ)
player:notify(tostr(ERR:dnamec(), " is your new home."));
elseif (ERR == E_INVARG)
player:notify("This is a pretty odd place.  You should make your home in an actual room.");
elseif (ERR == E_NACC)
player:notify(tostr("This place doesn't want to be your home.  Contact ", here.owner:iname(), " to be added to the residents list of this place, or choose another place as your home."));
else
player:notify(tostr("Error setting home: ", ERR));
endif
.
#6:33
player:tell("This is not a pick-up joint!");
this:tell(player:dnamec(), " tried to pick you up!");
.
#6:34
set_task_perms(player);
if (iobjstr == "here")
iobj = player.location;
elseif (iobjstr == "me")
iobj = player;
elseif ($command_utils:object_match_failed(iobj, iobjstr))
return;
endif
if (!$perm_utils:controls(player, iobj))
player:notify(tostr("You are not the owner of ", iobj:name(), "."));
return;
endif
if (dobjstr == "me")
dobj = player;
elseif (($failed_match == (dobj = $string_utils:literal_object(dobjstr))) && $command_utils:object_match_failed(dobj = iobj:match(dobjstr), dobjstr))
return;
endif
if (dobj.location != iobj)
player:notify(tostr(dobj:name(), "(", dobj, ") is not in ", iobj:name(), "(", iobj, ")."));
return;
endif
if (dobj.wizard)
player:notify(tostr("Sorry, you can't ", verb, " a wizard."));
dobj:tell(player:name(), " tried to ", verb, " you.");
return;
endif
iobj:eject(dobj);
player:notify($object_utils:has_callable_verb(iobj, "ejection_msg") ? iobj:ejection_msg() | $room:ejection_msg());
dobj:tell($object_utils:has_callable_verb(iobj, "victim_ejection_msg") ? iobj:victim_ejection_msg() | $room:victim_ejection_msg());
iobj:announce_all_but({player, dobj}, $object_utils:has_callable_verb(iobj, "oejection_msg") ? iobj:oejection_msg() | $room:oejection_msg());
.
#6:35
if (!args)
them = $code_utils:connected_players();
else
who = $command_utils:player_match_result(player:my_match_player(args), args);
if (length(who) <= 1)
if (!who[1])
player:notify("Where is who?");
endif
return;
elseif (who[1])
player:notify("");
endif
them = listdelete(who, 1);
endif
lmax = rmax = 0;
for p in (them)
if (valid(p.location))
if (p.location:is_public_location())
player:notify(tostr(p:inamec_and_number(), " ", p:verb_sub("is"), " ", p.location:name("d", {"in"}), " (", p.location, ")."));
else
player:notify(tostr(p:inamec_and_number(), " ", p:verb_sub("is"), " in a private location."));
endif
else
player:notify(tostr(p:inamec_and_number(), " ", p:verb_sub("is"), " nowhere.  Silly ", p:inamec_and_number(), "."));
endif
endfor
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863668528, \"Erik\", #74, \"JHM\"}";
.
#6:36
"@wizards [all]";
if (caller != player)
return E_PERM;
endif
player:tell("There are no `wizards' on this MOO.  See `help @admins' to find out how to list the members of the different administrative groups.");
.
#6:37
if (typeof(mf = this.(verb)) == STR)
return $string_utils:pronoun_sub(mf, @args);
else
return mf;
endif
.
#6:38
":display_message(preamble,msg) --- prints msg to player.";
vb = ((this._mail_task == task_id()) || (caller == $mail_editor)) ? "notify_lines" | "tell_lines";
preamble = args[1];
player:(vb)({@(typeof(preamble) == LIST) ? preamble | {preamble}, @args[2], "--------------------------"});
.
#6:39
"parse_message_seq(strings,cur)         => msg_seq";
"messages_in_seq(msg_seq);              => text of messages in msg_seq";
"display_seq_headers(msg_seq[,current]) :displays summary lines of those msgs";
"rmm_message_seq(msg_seq)               => string giving msg numbers removed";
"undo_rmm()    => msg_seq of restored messages";
"expunge_rmm() => number of messages expunged";
"list_rmm()    => number of messages awaiting expunge";
"renumber(cur) => {number of messages in folder, new_cur}";
"";
"See the corresponding routines on $mail_agent.";
if ((caller == $mail_agent) || $perm_utils:controls(caller_perms(), this))
set_task_perms(this.owner);
return $mail_agent:(verb)(@args);
else
return E_PERM;
endif
.
#6:40
return $mail_agent:msg_summary_line(@args);
.
#6:41
return $mail_agent:to_text(@args);
.
#6:42
":notify_mail(from,recipients[,msgnums])";
" used by $mail_agent:raw_send to notify this player about mail being sent";
" from <from> to <recipients>.  <msgnums> if given gives the message number(s) assigned (in the event that the corresponding recipient actually kept the mail)";
if (!((caller in {this, $mail_agent}) || $perm_utils:controls(caller_perms(), this)))
return E_PERM;
else
from = args[1];
recipients = args[2];
msgnums = {@args, {}}[3];
if ((length(args) >= 4) && (!this:mail_option("nosubj_notify")))
text = args[4];
subject = text[4];
if ((length(subject) == 0) || (subject == " "))
subject = (typeof(text[6]) == LIST) ? text[6][1] | text[6];
endif
else
subject = 0;
endif
"... msgnums may be shorter than recipients or may have some slots filled";
"... with 0's if msg numbers are not available for some recipients.";
if ((t = this in recipients) && ((length(msgnums) >= t) && msgnums[t]))
"... you are getting the mail and moreover your :receive_message kept it.";
namelist = $string_utils:english_list($list_utils:map_arg($mail_agent, "name", setremove(recipients, this)), "");
if (!subject)
this:notify(tostr("You have new mail (", msgnums[t], ") from ", from:name(), namelist ? " which was also sent to " + namelist | "", "."));
else
msg = tostr("You have mail (", msgnums[t], ": %) from ", from:name(), namelist ? " which was also sent to " + namelist | "", ".");
if (((maxlen = (this:linelen() - length(msg)) + 1) >= 10) && (length(subject) > maxlen))
subject = subject[1..maxlen];
elseif (length(subject) > 40)
subject = subject[1..40];
endif
msg[index(msg, "%")..index(msg, "%")] = subject;
this:notify(msg);
endif
if (!this:mail_option("expert"))
this:notify(tostr("Type `help mail' for info on reading it."));
endif
else
"... vanilla notification; somebody got sent mail and you're finding out.";
namelist = $string_utils:english_list({@t ? {"You"} | {}, @$list_utils:map_arg($mail_agent, "name", setremove(recipients, this))}, "");
fromname = `from:dname() ! ANY';
if (!subject)
this:notify(tostr(namelist, (length(recipients) == 1) ? " has" | " have", " just been sent new mail by ", fromname, "."));
else
msg = tostr((length(recipients) == 1) ? " has" | " have", " just been sent new mail (re: %) by ", fromname, ".");
if ((length(subject) >= 4) && (subject[1..4] == "Re: "))
subject[1..4] = "";
endif
if (((maxlen = (this:linelen() - length(msg)) + 1) >= 10) && (length(subject) > maxlen))
subject = subject[1..maxlen];
elseif (length(subject) > 40)
subject = subject[1..40];
endif
msg[index(msg, "%")..index(msg, "%")] = subject;
this:notify(tostr(namelist, msg));
endif
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869631967, \"Erik\", #74, \"JHM\"}";
.
#6:43
"runs the old->new format conversion on every message in this.messages.";
" => 1 if successful";
" => 0 if anything toward happened during a suspension";
"      (e.g., new message received, someone deleted stuff) ";
"      in which case this.messages is left as if this routine were never run.";
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
endif
msgs = {};
i = 1;
for m in (oldmsgs = this.messages)
msgs = {@msgs, {m[1], $mail_agent:__convert_new(@m[2])}};
if ($command_utils:running_out_of_time())
player:notify(tostr("...", i, " ", this));
suspend(0);
if (oldmsgs != this.messages)
return 0;
endif
endif
i = i + 1;
endfor
this.messages = msgs;
return 1;
.
#6:44
":current_message([recipient])";
" => current message number for the given recipient (defaults to this).";
" => 0 if we have no record of that recipient.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
raise(E_PERM);
elseif ((!args) || (args[1] == this))
return this.current_message[1];
elseif (a = $list_utils:assoc(args[1], this.current_message))
return a[2];
else
return 0;
endif
.
#6:45
":get_current_message([recipient])";
" => {msg_num, last_read_date} for the given recipient.";
" => 0 if we have no record of that recipient.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
$error:raise(E_PERM);
elseif ((!args) || (args[1] == this))
return this.current_message[1..2];
elseif (a = $list_utils:assoc(args[1], this.current_message))
return a[2..3];
else
return 0;
endif
.
#6:46
":set_current_message(recipient[,number[,date]])";
"Returns the new {number,last-read-date} pair for recipient.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return raise(E_PERM);
endif
recip = args[1];
number = {@args, E_NONE}[2];
date = {@args, 0, 0}[3];
cm = this.current_message;
if (recip == this)
this.current_message[2] = max(date, cm[2]);
if (number != E_NONE)
this.current_message[1] = number;
endif
return this.current_message[1..2];
elseif (i = $list_utils:iassoc(recip, cm))
return (this.current_message[i] = {recip, (number == E_NONE) ? cm[i][2] | number, max(date, cm[i][3])})[2..3];
else
entry = {recip, (number != E_NONE) && number, date};
this.current_message = {@cm, entry};
return entry[2..3];
endif
.
#6:47
":make_current_message(recipient[,index])";
"starts a new current_message record for recipient.";
"index, if given, indicates where recipient is to be";
"  placed (n = at or after nth entry in .current_message).";
{recip, ?i = 0} = args;
cm = this.current_message;
if (length(args) > 1)
i = max(2, min(i, length(cm)));
endif
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return $error:raise(E_PERM);
elseif (recip == this)
"...self...";
elseif (j = $list_utils:iassoc(recip, cm))
"...already present...";
if (i)
if (j < i)
this.current_message = {@cm[1..j - 1], @cm[j + 1..i], cm[j], @cm[i + 1..length(cm)]};
elseif (j > (i + 1))
this.current_message = {@cm[1..i], cm[j], @cm[i + 1..j - 1], @cm[j + 1..length(cm)]};
endif
endif
else
this.current_message = listappend(cm, {recip, 0, 0}, @i ? {i} | {});
return 1;
endif
.
#6:48
":kill_current_message(recipient)";
"entirely forgets current message for this recipient...";
"Returns true iff successful.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return $error:raise(E_PERM);
else
return ((recip = args[1]) != this) && ((i = $list_utils:iassoc(recip, cm = this.current_message)) && (this.current_message = listdelete(cm, i)));
endif
.
#6:49
":current_folder() => default folder to use, always an object, usually `this'";
set_task_perms((caller == this) ? this.owner | caller_perms());
return ((!this:mail_option("sticky")) || this.current_folder) && this;
.
#6:50
set_task_perms((caller == this) ? this.owner | caller_perms());
return this.current_folder = args[1];
.
#6:51
":parse_folder_spec(verb,args,expected_preposition[,allow_trailing_args_p])";
" => {folder, msg_seq_args, trailing_args}";
set_task_perms(caller_perms());
folder = this:current_folder();
if (!prepstr)
return {folder, args[2], {}};
endif
verb = args[1];
prep = args[3];
extra = {@args, 0}[4];
args = args[2];
p = prepstr in args;
if (prepstr != prep)
"...unexpected preposition...";
if (extra && (!index(prepstr, " ")))
return {folder, args[1..p - 1], args[p..length(args)]};
else
player:tell("Usage:  ", verb, " [<message numbers>] [", prep, " <folder/list-name>]");
endif
elseif (!((p < length(args)) && (fname = args[p + 1])))
"...preposition but no iobj...";
player:tell(verb, " ", $string_utils:from_list(args, " "), " WHAT?");
elseif ($mail_agent:match_failed(folder = $mail_agent:match_recipient(fname), fname))
"...bogus mail folder...";
else
return {folder, args[1..p - 1], args[p + 2..length(args)]};
endif
return 0;
.
#6:52
":parse_mailread_cmd(verb,args,default,prep[,trailer])";
"  handles anything of the form  `VERB message_seq [PREP folder ...]'";
"    default is the default msg-seq to use if none given";
"    prep is the expected prepstr (assumes prepstr is set), usually `on'";
"    trailer, if present and true, indicates trailing args are permitted.";
"  returns {recipient object, message_seq, current_msg,\"...\"} or 0";
set_task_perms(caller_perms());
if (!(pfs = this:parse_folder_spec(@listdelete(args, 3))))
return 0;
endif
verb = args[1];
default = args[3];
extra = {@args, 0}[5];
folder = pfs[1];
cur = this:get_current_message(folder) || {0};
if (typeof(pms = folder:parse_message_seq(pfs[2], @cur)) == LIST)
rest = {@listdelete(pms, 1), @pfs[3]};
if ((!extra) && rest)
"...everything should have been gobbled by :parse_message_seq...";
player:tell("This makes no sense: `", rest[1], "'");
return 0;
elseif (pms[1])
"...we have a nonempty message sequence...";
return {folder, pms[1], cur, rest};
elseif (used = (length(pfs[2]) + 1) - length(pms))
"...:parse_message_seq used some words, but didn't get anything out of it";
pms = ("%f %<has> no `" + $string_utils:from_list(pfs[2][1..used], " ")) + "' messages.";
elseif (typeof(pms = folder:parse_message_seq(default, @cur)) == LIST)
"...:parse_message_seq used nothing, try the default; wow it worked";
return {folder, pms[1], cur, rest};
endif
elseif (typeof(pms) == ERR)
player:tell($mail_agent:name(folder), " is not readable by you.");
if (!$object_utils:isa(folder, $mail_recipient))
player:tell("Use * to indicate a non-player mail recipient.");
endif
return 0;
endif
if (folder == this)
subst = {{"%f's", "Your"}, {"%f", "You"}, {"%<has>", "have"}};
elseif (is_player(folder))
subst = {{"%f", folder:name()}, {"%<has>", folder:verb_sub("has")}};
else
subst = {{"%f", "*" + folder:name()}, {"%<has>", "has"}};
endif
player:tell($string_utils:substitute(pms, {@subst, {"%%", "%"}}));
return 0;
.
#6:53
"@mail <msg-sequence>                --- as in help @mail";
"@mail <msg-sequence> on <recipient> --- shows mail on mailing list or player.";
set_task_perms(valid(caller_perms()) ? caller_perms() | player);
if (p = this:parse_mailread_cmd("@mail", args, this:mail_option("@mail") || "last:15", "on"))
this:set_current_folder(folder = p[1]);
msg_seq = p[2];
seq_size = $seq_utils:size(msg_seq);
if ((lim = player:mail_option("manymsgs")) && ((lim <= seq_size) && (!$command_utils:yes_or_no(tostr("You are about to see ", seq_size, " message headers.  Continue?")))))
player:notify(tostr("Aborted.  @mailoption manymsgs=", lim));
return;
endif
if (1 != seq_size)
player:notify(tostr(seq_size, " messages", (folder == this) ? "" | (" on " + $mail_agent:name(folder)), ":"));
endif
folder:display_seq_headers(msg_seq, @p[3]);
endif
.
#6:54
"@read <msg>...                  -- as in help @read";
"@read <msg>... on *<recipient>  -- reads messages on recipient.";
"@peek ...                       -- like @read, but don't set current message";
set_task_perms(valid(caller_perms()) ? caller_perms() | player);
"... wrong!  guests are owned by HACKER...";
"set_task_perms((valid(caller_perms()) ? caller_perms() | player).owner)";
"...";
if (p = this:parse_mailread_cmd("@read", args, "", "on"))
this:set_current_folder(folder = p[1]);
msg_seq = p[2];
if ((lim = player:mail_option("manymsgs")) && ((lim <= (seq_size = $seq_utils:size(msg_seq))) && (!$command_utils:yes_or_no(tostr("You are about to see ", seq_size, " messages.  Continue?")))))
player:notify(tostr("Aborted.  @mailoption manymsgs=", lim));
return;
endif
this:_set_mail_task();
if ((cur = folder:display_seq_full(msg_seq, tostr("Message %d", (folder == this) ? "" | (" on " + $mail_agent:name(folder)), ":"))) && (verb != "@peek"))
if (verb != "@peek")
this:set_current_message(folder, @cur);
endif
endif
endif
.
#6:55
set_task_perms(player);
if (dobjstr && (!(n = tonum(dobjstr))))
player:notify(tostr("Usage:  ", verb, "[<number>] [on <recipient>]"));
elseif (dobjstr)
this:("@read")(tostr(verb[2..5], n), @listdelete(args, 1));
else
this:("@read")(verb[2..5], @args);
endif
.
#6:56
"@rmm <message-sequence> [from <recipient>].   Use @unrmm if you screw up.";
" Beware, though.  @unrmm can only undo the most recent @rmm.";
set_task_perms(player);
if (!(p = this:parse_mailread_cmd("@rmm", args, "cur", "from")))
"...parse failed, we've already complained...";
elseif ((!prepstr) && ((p[1] != this) && (!$command_utils:yes_or_no(("@rmmail from " + $mail_agent:name(p[1])) + ".  Continue?"))))
"...wasn't the folder player was expecting...";
player:notify("@rmmail aborted.");
else
this:set_current_folder(folder = p[1]);
e = folder:rm_message_seq(p[2]);
if (typeof(e) == ERR)
player:notify(tostr(e));
else
count = ((n = $seq_utils:size(p[2])) == 1) ? "." | tostr(" (", n, " messages).");
fname = (folder == this) ? "" | (" from " + $mail_agent:name(folder));
player:notify(tostr("Deleted ", e, fname, count));
endif
endif
.
#6:57
set_task_perms(player);
if (!dobjstr)
folder = this:current_folder();
elseif ($mail_agent:match_failed(folder = $mail_agent:match_recipient(dobjstr), dobjstr))
return;
endif
cur = this:current_message(folder);
fname = $mail_agent:name(folder);
if (typeof(h = folder:renumber(cur)) == ERR)
player:notify(tostr(h));
else
if (!h[1])
player:notify(tostr("No messages on ", fname, "."));
else
player:notify(tostr("Messages on ", fname, " renumbered 1-", h[1], "."));
this:set_current_folder(folder);
if (h[2] && this:set_current_message(folder, h[2]))
player:notify(tostr("Current message is now ", h[2], "."));
endif
endif
endif
.
#6:58
"@unrmm [on <recipient>]  -- undoes the previous @rmm on that recipient.";
set_task_perms(player);
if (!(p = this:parse_folder_spec("@unrmm", args, "on")))
return;
endif
dobjstr = $string_utils:from_list(p[2], " ");
if (!dobjstr)
do = "undo_rmm";
elseif (index("expunge", dobjstr) == 1)
do = "expunge_rmm";
elseif (index("list", dobjstr) == 1)
do = "list_rmm";
else
player:notify(tostr("Usage:  ", verb, " [expunge|list] [on <recipient>]"));
return;
endif
this:set_current_folder(folder = p[1]);
if (msg_seq = folder:(do)())
if (do == "undo_rmm")
player:notify(tostr($seq_utils:size(msg_seq), " messages restored to ", $mail_agent:name(folder), "."));
folder:display_seq_headers(msg_seq, 0);
else
player:notify(tostr(msg_seq, " zombie message", (msg_seq == 1) ? " " | "s ", (do == "expunge_rmm") ? "expunged from " | "on ", $mail_agent:name(folder), "."));
endif
elseif (typeof(msg_seq) == ERR)
player:notify(tostr(msg_seq));
else
player:notify(tostr("No messages to ", (do == "expunge_rmm") ? "expunge from " | "restore to ", $mail_agent:name(folder)));
endif
.
#6:59
if (args && (args[1] == "to"))
args = listdelete(args, 1);
endif
subject = {};
for a in (args)
if (((i = index(a, "=")) > 3) && (index("subject", a[1..i - 1]) == 1))
args = setremove(args, a);
a[1..i] = "";
subject = {a};
endif
endfor
$mail_editor:invoke(args, verb, @subject);
.
#6:60
"@answer <msg> [on *<recipient>] [<flags>...]";
set_task_perms(who = valid(caller_perms()) ? caller_perms() | player);
if (p = this:parse_mailread_cmd(verb, args, "", "on", 1))
if ($seq_utils:size(p[2]) != 1)
player:notify("You can only answer *one* message at a time.");
elseif (LIST != typeof(flags_replytos = $mail_editor:check_answer_flags(@p[4])))
player:notify_lines({tostr("Usage:  ", verb, " [message-# [on <recipient>]] [flags...]"), "where flags include any of:", "  all        reply to everyone", "  sender     reply to sender only", "  include    include the original message in your reply", "  noinclude  don't include the original in your reply"});
else
this:set_current_folder(p[1]);
$mail_editor:invoke(2, verb, p[1]:messages_in_seq(p[2])[1][2], @flags_replytos);
endif
endif
.
#6:61
"@forward <msg> [on *<recipient>] to <recipient> [<recipient>...]";
set_task_perms(valid(caller_perms()) ? caller_perms() | player);
if (!(p = this:parse_mailread_cmd(verb, args, "", "on", 1)))
"...lose...";
return;
elseif ($seq_utils:size(sequence = p[2]) != 1)
player:notify("You can only forward *one* message at a time.");
return;
elseif ((length(p[4]) < 2) || (p[4][1] != "to"))
player:notify(tostr("Usage:  ", verb, " [<message>] [on <folder>] to <recip>..."));
return;
endif
recips = {};
for rs in (listdelete(p[4], 1))
if ($mail_agent:match_failed(r = $mail_agent:match_recipient(rs), rs))
return;
endif
recips = {@recips, r};
endfor
this:set_current_folder(folder = p[1]);
m = folder:messages_in_seq(sequence)[1];
msgnum = m[1];
msgtxt = m[2];
from = msgtxt[2];
if (msgtxt[4] != " ")
subject = tostr("[", from, ":  ", msgtxt[4], "]");
elseif ((h = "" in msgtxt) && (h < length(msgtxt)))
line = msgtxt[h + 1];
subject = tostr("[", from, ":  `", line[1..min(length(line), 20)], "']");
else
subject = tostr("[", from, "]");
endif
result = $mail_agent:send_message(player, recips, subject, $mail_agent:to_text(@msgtxt));
if (!result)
player:notify(tostr(result));
elseif (result[1])
player:notify(tostr("Message ", msgnum, @(folder == this) ? {} | {" on ", $mail_agent:name(folder)}, " @forwarded to ", $mail_agent:name_list(@listdelete(result, 1)), "."));
else
player:notify("Message not sent.");
endif
.
#6:62
$mail_editor:invoke($gripe_recipients, "@gripe", "@gripe: " + argstr);
.
#6:63
subject = tostr($string_utils:capitalize(verb[2..length(verb)]), ":  ", (loc = this.location):name(), "(", loc, ")");
if (this != player)
return E_PERM;
elseif (argstr)
result = $mail_agent:send_message(this, {loc.owner}, subject, argstr);
if (result && result[1])
player:notify(tostr("Your ", verb, " sent to ", $mail_agent:name_list(@listdelete(result, 1)), ".  Input is appreciated, as always."));
else
player:notify(tostr("Huh?  This room's owner (", loc.owner, ") is invalid?  Tell a wizard..."));
endif
return;
elseif (!($object_utils:isa(loc, $room) && loc.free_entry))
player:notify_lines({tostr("You need to make it a one-liner, i.e., `", verb, " something or other'."), "This room may not let you back in if you go to the Mail Room."});
elseif ($object_utils:isa(loc, $generic_editor))
player:notify_lines({tostr("You need to make it a one-liner, i.e., `", verb, " something or other'."), "Sending you to the Mail Room from an editor is usually a bad idea."});
else
$mail_editor:invoke({tostr(loc.owner)}, verb, subject);
endif
if (verb == "@bug")
player:notify("For a @bug report, be sure to mention exactly what it was you typed to trigger the error...");
endif
.
#6:64
"@skip [*<folder/mailing_list>...]";
"  sets your last-read time for the given list to now, indicating your";
"  disinterest in any new messages that might have appeared recently.";
"@unsubscribe *<folder/mailing_list>";
"  entirely removes the record of your current message for this folder,";
"  indicating your disinterest in anything that might appear in the future...";
set_task_perms(player);
forget = verb != "@skip";
folderlist = {};
for a in (args || {0})
if (a ? $mail_agent:match_failed(folder = $mail_agent:match_recipient(a), a) | (folder = this:current_folder()))
"...bogus folder name, done...";
elseif (forget && (folder == this))
player:notify(tostr("You can't ", verb, " yourself."));
elseif (forget)
if (!this:kill_current_message(folder))
player:notify(tostr("You weren't subscribed to ", $mail_agent:name(folder)));
else
folderlist = {@folderlist, folder};
if ($object_utils:isa(folder, $mail_recipient))
folder:delete_notify(this);
endif
endif
else
lseq = folder:length_all_msgs();
this:set_current_message(folder, lseq && folder:messages_in_seq({lseq, lseq + 1})[1][1], time());
folderlist = {@folderlist, folder};
endif
endfor
if (folderlist)
player:notify(tostr(forget ? "Forgetting about " | "Ignoring unread messages on ", $string_utils:english_list($list_utils:map_arg($mail_agent, "name", folderlist))));
"... if we've skipped or unsubscribed our current folder, switch back...";
if (this:current_folder() in folderlist)
this:set_current_folder(this);
endif
endif
.
#6:65
"@subscribe *<folder/mailing_list> [with notification] [before|after *<folder>]";
"  causes you to be notified when new mail arrives on this list";
"@subscribe";
"   just lists available mailing lists.";
set_task_perms(player);
fname = {@args, 0}[1];
if (!fname)
for c in ({@$mail_agent.contents, @this.mail_lists})
$command_utils:suspend_if_needed(0);
if ((c:is_usable_by(this) || c:is_readable_by(this)) && (!c:subscribed(this)))
c:look_self();
endif
endfor
player:notify(tostr("-------- end of ", verb, " -------"));
return;
elseif ($mail_agent:match_failed(folder = $mail_agent:match_recipient(fname), fname))
return;
elseif (folder == this)
player:notify("You don't need to @subscribe to yourself");
return;
elseif ($object_utils:isa(folder, $mail_recipient) ? !folder:is_readable_by(this) | (!$perm_utils:controls(this, folder)))
player:notify("That mailing list is not readable by you.");
return;
endif
notification = this in folder.mail_notify;
beforeafter = nchanged = 0;
i = 0;
while (length(args) >= 2)
if (length(args) < 3)
player:notify(args[2] + " what?");
return;
elseif (args[2] in {"with", "without"})
with = args[2] == "with";
if (index("notification", args[3]) != 1)
player:notify(tostr("with ", args[3], "?"));
return;
elseif (!$object_utils:isa(folder, $mail_recipient))
player:notify(tostr("You cannot use ", verb, " to change mail notification from a non-$mail_recipient."));
elseif ((!with) == (!notification))
"... nothing to do...";
elseif (with)
if (this in folder:add_notify(this))
notification = nchanged = 1;
else
player:notify("This mail recipient does not allow immediate notification.");
endif
else
folder:delete_notify(this);
notification = 0;
nchanged = 1;
endif
elseif (args[2] in {"before", "after"})
if (beforeafter)
player:notify((args[2] == beforeafter) ? tostr("two `", beforeafter, "'s?") | "Only use one of `before' or `after'");
return;
elseif ($mail_agent:match_failed(other = $mail_agent:match_recipient(args[3]), args[3]))
return;
elseif (other == this)
i = 2;
elseif (!(i = $list_utils:iassoc(other, this.current_message)))
player:notify(tostr("You aren't subscribed to ", $mail_agent:name(other), "."));
return;
endif
beforeafter = args[2];
i = i - (beforeafter == "before");
if (this:mail_option("rn_order") != "fixed")
player:notify("Warning:  Do `@mail-option rn_order=fixed' if you do not want your @rn listing reordered when you next login.");
endif
endif
args[2..3] = {};
endwhile
new = this:make_current_message(folder, @i ? {i} | {});
if (new)
len = folder:length_all_msgs();
player:notify(tostr($mail_agent:name(folder), " has ", len, " message", (len == 1) ? "" | "s", "."));
elseif (beforeafter)
player:notify("Ordering changed.");
elseif (!nchanged)
player:notify(tostr("You are already subscribed to ", $mail_agent:name(folder), "."));
endif
if (nchanged)
player:notify(notification ? "You will be notified immediately when new messages are posted." | "Notification of new messages will be printed when you connect.");
endif
this:set_current_folder(folder);
.
#6:66
set_task_perms((caller == this) ? this.owner | caller_perms());
dates = new_cm = head = {};
sort = this:mail_option("rn_order") || "read";
for n in (this.current_message)
$command_utils:suspend_if_needed(0);
if (typeof(n) != LIST)
head = {@head, n};
elseif ($object_utils:isa(folder = n[1], $mail_recipient) && folder:is_readable_by(this))
"...set current msg to be the last one you could possibly have read.";
if (n[3] < folder.last_msg_date)
i = folder:length_date_le(n[3]);
n[2] = i && folder:messages_in_seq(i)[1];
endif
if (sort == "fixed")
new_cm = {n, @new_cm};
elseif (sort == "send")
j = $list_utils:find_insert(dates, folder.last_msg_date - 1);
dates = listinsert(dates, folder.last_msg_date, j);
new_cm = listinsert(new_cm, n, j);
else
new_cm = listappend(new_cm, n, $list_utils:iassoc_sorted(n[3] - 1, new_cm, 3));
endif
endif
endfor
this.current_message = {@head, @$list_utils:reverse(new_cm)};
this:set_current_folder(this);
.
#6:67
set_task_perms((caller == this) ? this.owner | caller_perms());
which = {};
cm = this.current_message;
cm[1..2] = ((verb == "@rn") || (player != this)) ? {{this, @cm[1..2]}} | {};
all = verb == "@subscribed";
for n in (cm)
if ((nmsgs = n[1]:length_date_gt(n[3])) || all)
which = {@which, {n[1], nmsgs}};
endif
$command_utils:suspend_if_needed(0);
endfor
if (which)
player:notify((verb == "@subscribed") ? "You are subscribed to the following lists:" | "There is new activity on the following lists:");
for w in (which)
name = (w[1] == this) ? (player == this) ? " me" | (" " + this.name) | (((w[1] in $mail_agent.contents) || (w[1] in player.mail_lists)) ? "*" + w[1]:name() | tostr(w[1]));
player:notify(tostr($string_utils:left("    " + name, 31), " ", w[2], " new message", (w[2] == 1) ? "" | "s"));
endfor
elseif (verb == "@rn")
player:notify("No new activity on any of your lists.");
elseif (verb == "@subscribed")
player:notify("You aren't subscribed to any mailing lists.");
endif
return which;
.
#6:68
":mail_option(name)";
"Returns the value of the specified mail option";
if ((caller in {this, $mail_editor, $mail_agent}) || $perm_utils:controls(caller_perms(), this))
return $mail_options:get(this.mail_options, args[1]);
else
return E_PERM;
endif
.
#6:69
":edit_option(name) => returns the value of the specified edit option";
if ((caller == this) || ($object_utils:isa(caller, $generic_editor) || $perm_utils:controls(caller_perms(), this)))
return $edit_options:get(this.edit_options, args[1]);
else
return E_PERM;
endif
.
#6:70
":set_edit_option(oname,value)";
":set_display_option(oname,value)";
":set_mail_option(oname,value)";
"Changes the value of the named option.";
"Returns a string error if something goes wrong.";
if (!((caller == this) || $perm_utils:controls(caller_perms(), this)))
return tostr(E_PERM);
endif
"...this is kludgy, but it saves me from writing the same verb 3 times.";
"...there's got to be a better way to do this...";
verb[1..4] = "";
foo_options = verb + "s";
"...";
if (typeof(s = #0.(foo_options):set(this.(foo_options), @args)) == STR)
return s;
elseif (equal(s, this.(foo_options)))
return 0;
else
this.(foo_options) = s;
return 1;
endif
return;
"Metadata 202106";
"Last-Modify: {832018252, \"Ken\", #75, \"JHM\"}";
.
#6:71
"@<what>-option <option> [is] <value>   sets <option> to <value>";
"@<what>-option <option>=<value>        sets <option> to <value>";
"@<what>-option +<option>     sets <option>   (usually equiv. to <option>=1";
"@<what>-option -<option>     resets <option> (equiv. to <option>=0)";
"@<what>-option !<option>     resets <option> (equiv. to <option>=0)";
"@<what>-option <option>      displays value of <option>";
set_task_perms(player);
if ((i = index(verb, "optio")) && (verb[i - 1] != "-"))
what = verb[2..i - 1];
else
what = substitute("%1", match(verb, "^@%([^-]*%)%(o%|opt?i?o?n?s?%|-o?p?t?i?o?n?s?%)$"));
endif
"what = substitute(\"%1\", match(verb, \"^@%([^-o]*o?%)-?o?p?t?i?o?n?s?$\"));";
"what = substitute(\"%1\", match(verb, \"^@%([^-]*%)-?o%(p%(t%(i%(o%(n%(s%)?%)?%)%)?%)?%)??$\"));";
"what = {\"mail\", \"edit\", \"display\", \"list\"}[index(\"medl\", verb[2])];";
options = what + "_options";
option_pkg = #0.(options);
set_option = ("set_" + what) + "_option";
if (!args)
player:notify_lines({("Current " + what) + " options:", "", @option_pkg:show(this.(options), option_pkg.names)});
return;
elseif (typeof(presult = option_pkg:parse(args)) == STR)
player:notify(presult);
return;
else
if (length(presult) > 1)
if (typeof(sresult = this:(set_option)(@presult)) == STR)
player:notify(sresult);
return;
elseif (!sresult)
player:notify("No change.");
return;
endif
endif
player:notify_lines(option_pkg:show(this.(options), presult[1]));
endif
.
#6:72
"set_name(newname) attempts to change this.name to newname";
"  => E_TYPE   if newname is not a string";
"  => E_PERM   if you don't own this";
"  => E_INVARG if the name is already taken or prohibited for some reason";
"  => E_NACC   if the player database is not taking new names right now.";
{name} = args;
if (typeof(name) != STR)
return E_TYPE;
elseif (!($perm_utils:controls(caller_perms(), this) || (this == caller)))
return E_PERM;
elseif (!is_player(this))
"we don't worry about the names of player classes.";
set_task_perms(caller_perms());
return pass(@args);
elseif ($player_db.frozen)
return E_NACC;
elseif (!$player_db:name_ok(name))
return E_INVARG;
elseif (index(name, "_Guest") && (!$object_utils:isa(this, $guest)))
return E_INVARG;
else
old = this.name;
this.name = name;
if ((name != old) && (!(old in this.aliases)))
$player_db:delete(old);
endif
$player_db:insert(name, this);
return 1;
endif
"Copied from generic player (#6):set_name by Doug (#107) Thu Aug 15 16:20:01 1996 EDT";
"Copied from Doug (#107):set_name by Doug (#107) Thu Aug 15 16:22:25 1996 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {840140545, \"Doug\", #107, \"JHM\"}";
.
#6:73
"set_aliases(alias_list)";
"For changing player aliases, we check to make sure that none of the aliases match existing player names/aliases.  Aliases containing spaces are not entered in the $player_db and so are not subject to this restriction ($string_utils:match_player will not match on them, however, so they only match if used in the immediate room, e.g., with match_object() or somesuch).";
"Also we make sure that the .name is included in the .alias list.  In any situation where .name and .aliases are both being changed, do the name change first.";
"  => E_PERM   if you don't own this";
"  => E_NACC   if the player database is not taking new aliases right now.";
"  => E_TYPE   if alias_list is not a list";
"  => E_INVARG if any element of alias_list is not a string";
"  => E_INVARG if any element of alias_list is not a valid alias";
{aliases} = args;
if (!($perm_utils:controls(caller_perms(), this) || (this == caller)))
return E_PERM;
elseif (!is_player(this))
"we don't worry about the names of player classes.";
return pass(@args);
elseif ($player_db.frozen)
return E_NACC;
elseif (typeof(aliases) != LIST)
return E_TYPE;
else
for a in (aliases)
if (typeof(a) != STR)
return E_INVARG;
endif
if (!$player_db:alias_ok(a))
"aliases = setremove(aliases, a);";
return E_INVARG;
endif
endfor
aliases = setadd(aliases, this.name);
old = this.aliases;
this.aliases = aliases;
for a in (old)
if (!(a in aliases))
$player_db:delete2(a, this);
endif
endfor
for a in (aliases)
if (!$player_db:alias_ignored(a))
"... was if (!index(a, \" \"))...";
$player_db:insert(a, this);
endif
endfor
return 1;
endif
.
#6:74
if ((player != caller) || (player != this))
return;
endif
set_task_perms(player);
if (!iobjstr)
player:notify("You must specify a new name.");
return;
endif
bynumber = verb == "@rename#";
spec = $code_utils:parse_verbref(dobjstr);
if (spec)
object = this:my_match_object(spec[1]);
if (!$command_utils:object_match_failed(object, spec[1]))
vname = spec[2];
if (bynumber && ((E_TYPE == (vname = $code_utils:tonum(vname))) || (vname < 1)))
player:notify("Verb number expected.");
return;
endif
try
info = verb_info(object, vname);
except (E_VERBNF)
player:notify(bynumber ? "Verb number out of range." | "That object does not define that verb.");
return;
except e (ANY)
player:notify(e[2]);
return;
endtry
if ((spec2 = $code_utils:parse_verbref(iobjstr)) && (spec2[1] == spec[1]))
player:notify(tostr("(You probably meant `", verb, " ", dobjstr, " ", prepstr, " ", iobjstr = spec2[2], "'.)"));
endif
try
set_verb_info(object, vname, listset(info, iobjstr, 3));
except e (ANY)
player:notify(e[2]);
return;
endtry
player:notify("Verb name changed.");
endif
elseif (bynumber)
player:notify(tostr(verb, " is only for verbs."));
else
spec = $code_utils:parse_propref(dobjstr);
if (spec && ((spec[1] != "#0") || (dobjstr[1] != "$")))
object = this:my_match_object(spec[1]);
if ($command_utils:object_match_failed(object, spec[1]))
return;
endif
property = spec[2];
if ((!player.programmer) && (!(match(iobjstr, "_msg$") && match(property, "_msg$"))))
player:tell("Sorry, but as a nonprogrammer you cannot rename properties arbitrarily.");
return;
endif
result = $code_utils:rename_property(object, property, iobjstr);
if (result == E_PROPNF)
player:notify("That object does not define that property.");
elseif (result == E_INVARG)
player:notify(tostr("That object or one of its ancestors or descendants already has a property named \"", iobjstr, "\"."));
elseif (typeof(result) == ERR)
player:notify(tostr(result));
else
player:notify("Property name changed.");
endif
else
object = this:my_match_object(dobjstr);
if (!$command_utils:object_match_failed(object, dobjstr))
old_name = object.name;
old_dnamec = object:dnamec();
old_aliases = object.aliases;
"The \"none\" gender uses the name in pronouns.  --Doug, 1/23/98";
old_pp = object:pp();
if (e = $building_utils:set_names(object, iobjstr))
if (strcmp(object.name, old_name) == 0)
name_message = tostr("Name of ", object, " (", old_name, ") is unchanged");
else
name_message = tostr("Name of ", object, " changed to \"", object.name, "\"");
"Prevent silent player name changes.";
if (object == player)
player.location:announce(old_dnamec, " ", player:verb_sub("changes"), " ", old_pp, " name to ", player:dname(), ".");
endif
endif
aliases = $string_utils:from_value(object.aliases, 1);
if (object.aliases == old_aliases)
alias_message = tostr(".  Aliases are unchanged (", aliases, ").");
else
alias_message = tostr(", with aliases ", aliases, ".");
endif
player:notify(name_message + alias_message);
elseif (e == E_INVARG)
player:notify("That particular name change is not allowed (see help @rename).");
elseif (e == E_NACC)
player:notify("Oops.  You can't update that name right now; try again in a few minutes.");
else
player:notify(tostr(e));
endif
endif
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {885575456, \"Doug\", #107, \"JHM\"}";
.
#6:75
"Usage: @addalias <alias>[,...,<alias>] to <object>";
"       @addalias <alias>[,...,<alias>] to <object>:<verb>";
"";
"The first form is used to add aliases to an object's list of aliases.  You can separate multiple aliases with commas.  The aliases will be checked against the object's current aliases and all aliases not already in the object's list of aliases will be added.";
"";
"Example:";
"Muchkin wants to add new aliases to Rover the Wonder Dog:";
"  @addalias Dog,Wonder Dog to Rover";
"Since Rover the Wonder Dog already has the alias \"Dog\" but does not have the alias \"Wonder Dog\", Munchkin sees:";
"  Rover the Wonder Dog(#4237) already has the alias Dog.";
"  Alias Wonder Dog added to Rover the Wonder Dog(#4237).";
"";
"If the object is a player, spaces will also be assumed to be separations between aliases and each alias will be checked against the Player Name Database to make sure no one else is using it. Any already used aliases will be identified.";
"";
"Example:";
"Munchkin wants to add his nicknames to his own list of aliases:";
"  @addalias Foobar Davey to me";
"@Addalias recognizes that Munchkin is trying to add an alias to a valid player and checks the aliases against the Player Name Database.  Unfortunately, DaveTheMan is already using the alias \"Davey\" so Munchkin sees:";
"  DaveTheMan(#5432) is already using the alias Davey";
"  Alias Foobar added to Munchkin(#1523).";
"";
"The second form of the @addalias command is for use by programmers, to add aliases to a verb they own.  All commas and spaces are assumed to be separations between aliases.";
if (player != this)
return;
elseif (!dobjstr)
player:tell_lines($code_utils:verb_usage());
endif
set_task_perms(player);
spec = $code_utils:parse_verbref(iobjstr);
bynumber = verb[$] == "#";
if (spec)
object = player:my_match_object(spec[1]);
if (!$command_utils:object_match_failed(object, spec[1]))
vname = spec[2];
if (bynumber && ((E_TYPE == (vname = $code_utils:tonum(vname))) || (vname < 1)))
player:notify("Verb number expected.");
return;
endif
try
info = verb_info(object, vname);
except (E_VERBNF)
player:notify(bynumber ? "Verb number out of range" | "That object does not define that verb.");
return;
except e (ANY)
player:notify(e[2]);
return;
endtry
old_aliases = $string_utils:explode(info[3]);
used = {};
for alias in (new_aliases = $list_utils:remove_duplicates($string_utils:explode(strsub(dobjstr, ",", " "))))
if (alias in old_aliases)
used = {@used, alias};
new_aliases = setremove(new_aliases, alias);
endif
endfor
if (used)
player:notify(tostr(object.name, "(", object, "):", vname, " already has the alias", (length(used) > 1) ? "es" | "", " ", $string_utils:english_list(used), "."));
endif
if (new_aliases)
info = listset(info, aliases = $string_utils:from_list({@old_aliases, @new_aliases}, " "), 3);
try
result = set_verb_info(object, vname, info);
except e (ANY)
player:notify(e[2]);
return;
endtry
player:notify(tostr("Alias", (length(new_aliases) > 1) ? "es" | "", " ", $string_utils:english_list(new_aliases), " added to verb ", object.name, "(", object, "):", vname));
player:notify(tostr("Verbname is now ", object.name, "(", object, "):\"", aliases, "\""));
endif
endif
elseif (bynumber)
player:notify(tostr(verb, " can only be used for verbs."));
else
object = player:my_match_object(iobjstr);
if (!$command_utils:object_match_failed(object, iobjstr))
old_aliases = object.aliases;
used = {};
for alias in (new_aliases = $list_utils:remove_duplicates($list_utils:map_arg($string_utils, "trim", $string_utils:explode(dobjstr, ","))))
if (alias in old_aliases)
used = {@used, alias};
new_aliases = setremove(new_aliases, alias);
elseif (is_player(object) && valid(someone = $player_db:find_exact(alias)))
player:notify(tostr(someone.name, "(", someone, ") is already using the alias ", alias, "."));
new_aliases = setremove(new_aliases, alias);
endif
endfor
if (used)
player:notify(tostr(object.name, "(", object, ") already has the alias", (length(used) > 1) ? "es" | "", " ", $string_utils:english_list(used), "."));
endif
if (new_aliases)
if (e = object:set_aliases(aliases = {@old_aliases, @new_aliases}))
player:notify(tostr("Alias", (length(new_aliases) > 1) ? "es" | "", " ", $string_utils:english_list($list_utils:map_arg($string_utils, "print", new_aliases)), " added to ", object.name, "(", object, ")."));
player:notify(tostr("Aliases for ", object.name, "(", object, ") are now ", $string_utils:from_value(aliases, 1)));
elseif (e == E_INVARG)
player:notify("That particular name change not allowed (see help @rename or help @addalias).");
elseif (e == E_NACC)
player:notify("Oops.  You can't update that object's aliases right now; try again in a few minutes.");
else
player:notify(tostr(e));
endif
endif
endif
endif
.
#6:76
"Syntax: @rmalias <alias>[,...,<alias>] from <object>";
"        @rmalias <alias>[,...,<alias>] from <object>:<verb>";
"";
"The first form is used to remove aliases from an object.  If the object is a valid player, space and commas will be assumed to be separations between unwanted aliases.  Otherwise, only commas will be assumed to be separations.";
"Note that @rmalias will not affect the object's name, only its aliases.";
"";
"The second form is for use by programmers, to remove aliases from a verb they own.  All spaces and commas are assumed to be separations between unwanted aliases.";
if (player != this)
return;
endif
set_task_perms(player);
spec = $code_utils:parse_verbref(iobjstr);
bynumber = verb[$] == "#";
if (spec)
object = player:my_match_object(spec[1]);
if (!$command_utils:object_match_failed(object, spec[1]))
vname = spec[2];
if (bynumber && ((E_TYPE == (vname = $code_utils:tonum(vname))) || (vname < 1)))
player:notify("Verb number expected.");
return;
endif
try
info = verb_info(object, vname);
except (E_VERBNF)
player:notify(bynumber ? "Verb number out of range." | "That object does not define that verb.");
return;
except e (ANY)
player:notify(e[2]);
return;
endtry
old_aliases = $string_utils:explode(info[3]);
not_used = {};
for alias in (bad_aliases = $list_utils:remove_duplicates($string_utils:explode(strsub(dobjstr, ",", " "))))
if (!(alias in old_aliases))
not_used = {@not_used, alias};
bad_aliases = setremove(bad_aliases, alias);
else
old_aliases = setremove(old_aliases, alias);
endif
endfor
if (not_used)
player:notify(tostr(object:name(), "(", object, "):", vname, " does not have the alias", (length(not_used) > 1) ? "es" | "", " ", $string_utils:english_list(not_used), "."));
endif
if (bad_aliases && old_aliases)
info = listset(info, aliases = $string_utils:from_list(old_aliases, " "), 3);
try
set_verb_info(object, vname, info);
except e (ANY)
player:notify(e[2]);
return;
endtry
player:notify(tostr("Alias", (length(bad_aliases) > 1) ? "es" | "", " ", $string_utils:english_list(bad_aliases), " removed from verb ", object:name(), "(", object, "):", vname));
player:notify(tostr("Verbname is now ", object:name(), "(", object, "):", toliteral(aliases)));
elseif (!old_aliases)
player:notify("You have to leave a verb with at least one alias.");
endif
endif
else
object = player:my_match_object(iobjstr);
if (!$command_utils:object_match_failed(object, iobjstr))
old_aliases = object.aliases;
not_used = {};
"the next line useta check is_player(object), and if so convert spaces in dobjstr to commas.  i removed that cuz players can now have aliases with spaces -- davidsan";
for alias in (bad_aliases = $list_utils:remove_duplicates($list_utils:map_arg($string_utils, "trim", $string_utils:explode(dobjstr, ","))))
if (!(alias in old_aliases))
not_used = {@not_used, alias};
bad_aliases = setremove(bad_aliases, alias);
else
old_aliases = setremove(old_aliases, alias);
endif
endfor
if (not_used)
player:notify(tostr(object:name(), "(", object, ") does not have the alias", (length(not_used) > 1) ? "es" | "", " ", $string_utils:english_list(not_used), "."));
endif
if (bad_aliases)
if (e = object:set_aliases(old_aliases))
player:notify(tostr("Alias", (length(bad_aliases) > 1) ? "es" | "", " ", $string_utils:english_list($list_utils:map_arg($string_utils, "print", bad_aliases)), " removed from ", object:name(), "(", object, ")."));
player:notify(tostr("Aliases for ", object:name(), "(", object, ") are now ", $string_utils:from_value(old_aliases, 1)));
elseif (e == E_INVARG)
player:notify("That particular name change not allowed (see help @rename or help @rmalias).");
elseif (e == E_NACC)
player:notify("Oops.  You can't update that object's aliases right now; try again in a few minutes.");
else
player:notify(tostr(e));
endif
endif
endif
endif
.
#6:77
"Usage:  @describe <object> as <description>";
set_task_perms(player);
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
"...lose...";
elseif (e = dobj:set_description(iobjstr))
player:notify("Description set.");
else
player:notify(tostr(e));
endif
.
#6:78
set_task_perms(player);
if (dobjstr == "")
player:notify(tostr("Usage:  ", verb, " <object>"));
return;
endif
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
return;
endif
if (`messages = dobj:get_messages() ! E_PERM' == E_PERM)
player:notify("You can't read the messages on that.");
elseif (messages)
for message in (messages)
$command_utils:suspend_if_needed(1);
{name, value} = message;
if (value == E_PERM)
player:notify(tostr("@", name, " ", dobjstr, " isn't readable by you."));
elseif (value == E_INVARG)
player:notify(tostr("@", name, " ", dobjstr, " appears to be corrupted!"));
elseif (!value)
player:notify(tostr("@", name, " ", dobjstr, " isn't set."));
else
player:notify(tostr("@", name, " ", dobjstr, " is ", value));
endif
endfor
else
player:notify("That object doesn't have any messages to set.");
endif
return;
"Metadata 202106";
"Last-Modify: {831879113, \"splat\", #3024, \"JHM\"}";
.
#6:79
$note_editor:invoke(dobjstr, verb);
.
#6:80
if (typeof(player.password) != STR)
if (length(args) != 1)
return player:notify(tostr("Usage:  ", verb, " <new-password>"));
else
new_password = args[1];
endif
elseif (length(args) != 2)
player:notify(tostr("Usage:  ", verb, " <old-password> <new-password>"));
return;
elseif (player.password != crypt(tostr(args[1]), player.password))
player:notify("That's not your old password.");
return;
else
new_password = args[2];
endif
player.password = crypt(tostr(new_password));
player:notify("New password set.");
.
#6:81
"@last-c           reports when and from where you last connected.";
"@last-c all       adds the 10 most recent places you connected from.";
"@last-c confunc   is like `@last-c' but is silent on first login.";
opts = {"all", "confunc"};
i = 0;
if (caller != this)
return E_PERM;
elseif (args && ((length(args) > 1) || (!(i = $string_utils:find_prefix(args[1], opts)))))
this:notify(tostr("Usage:  ", verb, " [all]"));
return;
endif
opt_all = i && (opts[i] == "all");
opt_confunc = i && (opts[i] == "confunc");
if (!(prev = this.previous_connection))
this:notify("Something was broken when you logged in; tell a wizard.");
elseif (prev[1] == 0)
opt_confunc || this:notify("Your previous connection was before we started keeping track.");
elseif (prev[1] > time())
this:notify("This is your first time connected.");
else
this:notify(tostr("Last connected ", ctime(prev[1]), " from ", prev[2]));
if (opt_all)
this:notify("Previous connections have been from the following sites:");
for l in (this.all_connect_places)
this:notify("   " + l);
endfor
endif
endif
.
#6:82
"set_gender(newgender) attempts to change this.gender to newgender";
"  => E_PERM   if you don't own this or aren't its parent";
"  => Other return values as from $gender_utils:match_gender.";
if (!($perm_utils:controls(caller_perms(), this) || (this == caller)))
return E_PERM;
endif
result = $gender_utils:match_gender(args[1], $gender_utils:player_gender_objs());
if (result || valid(result))
this.gender = result;
endif
return result;
.
#6:83
set_task_perms(valid(caller_perms()) ? caller_perms() | player);
if (!args)
player:notify(tostr("Your gender is currently ", this:gender_name(), "."));
player:notify($string_utils:pronoun_sub($pronoun_sub.lambdacore:parse("Your pronouns:  %s, %o, %p, %q, %r, %S, %O, %P, %Q, %R")));
player:notify(tostr("Available genders:  ", $string_utils:english_list($gender_utils:player_gender_names(), "", " or ")));
else
result = this:set_gender(gender = args[1]);
if (typeof(result) == ERR)
player:notify(tostr(result));
elseif ($gender_utils:gender_match_failed(result, gender))
player:notify(tostr("Available genders:  ", $string_utils:english_list($gender_utils:player_gender_names(), "", " or ")));
else
player:notify(tostr("Gender set to \"", this:gender_name(), "\"."));
player:notify($string_utils:pronoun_sub($pronoun_sub.lambdacore:parse("Your pronouns:  %s, %o, %p, %q, %r, %S, %O, %P, %Q, %R")));
endif
endif
.
#6:84
"This verb should probably go away once 'examine' is in place.";
if (dobjstr == "")
player:notify(tostr("Usage:  ", verb, " <object>"));
return;
endif
what = $string_utils:match_object(dobjstr, player.location);
if ($command_utils:object_match_failed(what, dobjstr))
return;
endif
player:notify(tostr(what:name(), " (", what, ") is owned by ", valid(what.owner) ? what.owner:name() | "a recycled player", " (", what.owner, ")."));
player:notify(tostr("Aliases:  ", $string_utils:english_list(what.aliases)));
desc = what:description();
if (desc)
player:notify_lines(desc);
else
player:notify("(No description set.)");
endif
if ($perm_utils:controls(player, what))
player:notify(tostr("Key:  ", $lock_utils:unparse_key(what.key)));
endif
contents = what.contents;
if (contents)
player:notify("Contents:");
for item in (contents)
player:notify(tostr("  ", item:name(), " (", item, ")"));
endfor
endif
"Use dobjstr, not shortest alias.";
name = dobjstr;
"name = what:name();";
"if (typeof(what.aliases) == LIST && what.aliases != {})";
"for alias in (what.aliases)";
"if (length(alias) <= length(name))";
"name = alias;";
"endif";
"endfor";
"endif";
vrbs = {};
commands_ok = what in {player, player.location};
dull_classes = {$root_class, $room, $player, $builder, $prog};
what = what;
while (what != $nothing)
if (!(what in dull_classes))
for i in [1..length(verbs(what))]
info = verb_info(what, i);
syntax = verb_args(what, i);
if ((index(info[2], "r") && ((syntax[2..3] != {"none", "this"}) && (commands_ok || ("this" in syntax)))) && verb_code(what, i))
dobj = syntax[1];
prep = syntax[2];
iobj = syntax[3];
if (syntax == {"any", "any", "any"})
prep = "none";
endif
if (prep != "none")
for x in ($string_utils:explode(prep, "/"))
if (length(x) <= length(prep))
prep = x;
endif
endfor
endif
"This is the correct way to handle verbs ending in *";
vname = info[3];
while (i = index(vname, "* "))
vname[i..i] = "<anything>";
endwhile
if (vname[$] == "*")
vname[$..$] = "<anything>";
endif
vname = strsub(vname, " ", "/");
rest = "";
if (prep != "none")
rest = " " + ((prep == "any") ? "<anything>" | prep);
if (iobj != "none")
rest = tostr(rest, " ", (iobj == "this") ? name | "<anything>");
endif
endif
if (dobj != "none")
rest = tostr(" ", (dobj == "this") ? name | "<anything>", rest);
endif
vrbs = setadd(vrbs, ("  " + vname) + rest);
endif
endfor
endif
what = parent(what);
endwhile
if (vrbs)
player:notify("Obvious Verbs:");
player:notify_lines(vrbs);
endif
.
#6:85
set_task_perms(player);
if (!dobjstr)
player:notify(tostr("Usage:  ", verb, " <object>"));
return E_INVARG;
endif
what = player.location:match_object(dobjstr);
if ($command_utils:object_match_failed(what, dobjstr))
return;
endif
what:do_examine(player);
.
#6:86
"Add a feature to this player's features list.  Caller must be this or have suitable permissions (this or wizardly).";
"If this is a nonprogrammer, then ask feature if it is feature_ok (that is, if it has a verb :feature_ok which returns a true value, or a property .feature_ok which is true).";
"After adding feature, call feature:feature_add(this).";
"Returns true if successful, E_INVARG if not a valid object, and E_PERM if !feature_ok or if caller doesn't have permission.";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
feature = args[1];
if ((typeof(feature) != OBJ) || (!valid(feature)))
return E_INVARG;
"Not a valid object.";
endif
if ($code_utils:verb_or_property(feature, "feature_ok", this))
"The object is willing to be a feature.";
if (typeof(this.features) == LIST)
"If list, we can simply setadd the feature.";
this.features = setadd(this.features, feature);
else
"If not, we erase the old value and create a new list.";
this.features = {feature};
endif
"Tell the feature it's just been added.";
`feature:feature_add(this) ! E_VERBNF';
return 1;
"We're done.";
else
return E_PERM;
"Feature isn't feature_ok.";
endif
else
return E_PERM;
"Caller doesn't have permission.";
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874510359, \"Erik\", #74, \"JHM\"}";
.
#6:87
"Remove a feature from this player's features list.  Caller must be this, or have permissions of this, a wizard, or feature.owner.";
"Returns true if successful, E_PERM if caller didn't have permission.";
feature = args[1];
if (((caller == this) || $perm_utils:controls(caller_perms(), this)) || (caller_perms() == feature.owner))
if (typeof(this.features) == LIST)
"If this is a list, we can just setremove...";
this.features = setremove(this.features, feature);
"Otherwise, we leave it alone.";
endif
"Let the feature know it's been removed.";
`feature:feature_remove(this) ! E_VERBNF';
return 1;
"We're done.";
else
return E_PERM;
"Caller didn't have permission.";
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874510380, \"Erik\", #74, \"JHM\"}";
.
#6:88
"Usage:  @add-feature <feature object>";
"Add an object to your features list.";
set_task_perms(player);
if (dobjstr)
if (dobj == $failed_match)
"dobj = $feature.warehouse:match_object(dobjstr);";
if ($feature.registry:feature_match_failed(dobj = $feature.registry:match_feature(dobjstr), dobjstr))
return;
endif
endif
if (!$command_utils:object_match_failed(dobj, dobjstr))
if (dobj in player:features())
player:tell($string_utils:dnamec_and_number(dobj), " is already one of your features.");
elseif (player:add_feature(dobj))
player:tell($string_utils:dnamec_and_number(dobj), " added as a feature.");
else
player:tell("You can't seem to add ", $string_utils:dname_and_number(dobj), " to your features list.");
endif
endif
else
player:tell("Usage:  @add-feature <object>");
features = {};
player_features = player:features();
for f in ($feature.registry:features())
if ((!(f in player_features)) && $code_utils:verb_or_property(f, "feature_ok", player))
features = {@features, f};
endif
endfor
feature_list = $feature.registry:feature_list_lines(player, features);
"fe = {};";
"long = {};";
"width = abs(player.linelen);";
"half = width / 2;";
"\"for c in ($feature.warehouse:contents())\";";
"\"name = c in player:features() ? c:name() + \\\" (*)\\\" | c:name();\";";
"if (!(c in player:features()) && $code_utils:verb_or_property(c, \"feature_ok\", player))";
"name = c:name();";
"if (length(name) > half)";
"long = {@long, name};";
"else";
"fe = {@fe, name};";
"endif";
"endif";
"endfor";
"if (fe || long)";
if (feature_list)
player:tell("Available features not already in your feature list include:");
"player:tell_lines($string_utils:columnize(fe, 2, width));";
"player:tell_lines(long);";
player:tell_lines(feature_list);
else
player:tell("There are no available features that you don't already have.");
endif
endif
.
#6:89
"Usage:  @remove-feature <feature object>";
"Remove an object from your .features list.";
set_task_perms(player);
if (dobjstr)
features = player:features();
if (!valid(dobj))
dobj = $string_utils:match(dobjstr, features, "name", features, "aliases");
endif
if (!$command_utils:object_match_failed(dobj, dobjstr))
if (dobj in features)
player:remove_feature(dobj);
player:tell(dobj, " (", dobj:name(), ") removed from your features list.");
else
player:tell(dobjstr, " is not one of your features.");
endif
endif
else
player:tell("Usage:  @remove-feature <object>");
endif
.
#6:90
"Usage:  @features [<name>] [for <player>]";
"List the feature objects matching <name> used by <player>.";
if (!iobjstr)
player:tell("Usage: @features [<name>] for <player>");
return;
elseif ($command_utils:player_match_failed(whose = player:my_match_player(iobjstr), iobjstr))
return;
endif
features = {};
for feature in (whose:features())
if (`valid(feature) ! E_TYPE' && ((!dobjstr) || ((dobjstr in feature.aliases) || ((pref = $string_utils:find_prefix(dobjstr, feature.aliases)) || (pref == $ambiguous_match)))))
features = listappend(features, feature);
endif
endfor
if (features)
len = max(length("Feature"), length(tostr(max_object()))) + 1;
player:tell($string_utils:left("Feature", len), "Name");
player:tell($string_utils:left("-------", len), "----");
for feature in (features)
player:tell($string_utils:left(tostr(feature), len), feature:name());
endfor
player:tell($string_utils:left("-------", len), "----");
elseif (dobjstr)
player:tell("No features found on ", whose:name(), " (", whose, ") matching \"", dobjstr, "\".");
else
player:tell("No features found on ", whose:name(), " (", whose, ").");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874511236, \"Erik\", #74, \"JHM\"}";
.
#6:91
"Usage:  @features [<name>]";
"List the feature objects matching <name> used by player.";
iobjstr = "me";
iobj = player;
this:("@features")();
.
#6:92
stats = memory_usage();
if (!stats)
player:notify("Sorry, but no memory-usage statistics are available for this server.");
return;
endif
su = $string_utils;
player:notify("Block Size   # In Use    # Free    Bytes In Use   Bytes Free");
player:notify("----------   --------   --------   ------------   ----------");
nused = nfree = bytesused = bytesfree = 0;
kilo = 1024;
meg = kilo * kilo;
for x in (stats)
if (x[2..3] != {0, 0})
bsize = x[1];
if ((bsize % meg) == 0)
bsize = tostr(bsize / meg, " M");
elseif ((bsize % kilo) == 0)
bsize = tostr(bsize / kilo, " K");
endif
bused = x[1] * x[2];
bfree = x[1] * x[3];
player:notify(tostr(su:left(bsize, 10), "   ", su:right(su:group_number(x[2]), 8), "   ", su:right(su:group_number(x[3]), 8), "   ", su:right(su:group_number(bused), 12), "   ", su:right(su:group_number(bfree), 10)));
nused = nused + x[2];
nfree = nfree + x[3];
bytesused = bytesused + bused;
bytesfree = bytesfree + bfree;
endif
endfor
player:notify("");
player:notify(tostr(su:left("Totals:", 10), "   ", su:right(su:group_number(nused), 8), "   ", su:right(su:group_number(nfree), 8), "   ", su:right(su:group_number(bytesused), 12), "   ", su:right(su:group_number(bytesfree), 10)));
player:notify("");
player:notify(tostr("Total Memory Size: ", su:group_number(bytesused + bytesfree), " bytes."));
.
#6:93
player:notify(tostr("The MOO is currently running version ", server_version(), " of the LambdaMOO server code."));
if ($core_extraction_time)
player:notify(tostr("The core is derived from a JHCore extracted on ", $time_utils:time_sub("$n $t, $Y", $core_extraction_time), "."));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863369727, \"Ken\", #75, \"JHM\"}";
.
#6:94
player:notify(tostr("The server has been up for ", $time_utils:english_time(time() - $last_restart_time), "."));
.
#6:95
boot_player(player);
"-- argh, let the player decide; #3:disfunc() takes care of this --Rog";
"player:moveto(player.home)";
.
#6:96
if (strcmp(verb, "QUIT"))
"If the player is in a room that has a quit verb, e.g. editor, call it.";
if ($object_utils:has_verb(player.location, verb))
player.location:(verb)(@args);
else
player:notify("I don't understand that.");
endif
else
player:notify("Use `@quit' to logout.");
endif
.
#6:97
line = args[1];
if (this.pagelen && (this in connected_players()))
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
"...need wizard perms if this and this.owner are different, since...";
"...only this can notify() and only this.owner can read .linebuffer...";
if ((player == this) && (this.linetask[2] != task_id()))
"...player has started a new task...";
"....linetask[2] is the taskid of the most recent player task...";
if (this.linetask[2] != this.linetask[1])
this.linesleft = this.pagelen - 2;
endif
this.linetask[2] = task_id();
endif
"... digest the current line...";
if (this.linelen > 0)
lbuf = {@this.linebuffer, @this:linesplit(line, this.linelen)};
else
lbuf = {@this.linebuffer, line};
endif
llen = length(lbuf);
"... print out what we can...";
if (this.linesleft)
howmany = min(this.linesleft, llen);
for l in (lbuf[1..howmany])
notify(this, l);
endfor
this.linesleft = this.linesleft - howmany;
lbuf = lbuf[howmany + 1..llen];
llen = llen - howmany;
endif
if (llen)
"...see if we need to say ***More***";
if (this.linetask[1] != this.linetask[2])
"....linetask[1] is the taskid of the most recent player task";
"...   for which ***More*** was printed...";
this.linetask[1] = this.linetask[2];
fork (0)
notify(this, strsub(this.more_msg, "%n", tostr(length(this.linebuffer))));
endfork
endif
if (llen > 500)
"...way too much saved text, flush some of it...";
lbuf = {"*** buffer overflow, lines flushed ***", @lbuf[llen - 99..llen]};
endif
endif
this.linebuffer = lbuf;
elseif (this.linelen > 0)
set_task_perms(((caller == this) && is_player(this)) ? this | caller_perms());
for l in (this:linesplit(line, this.linelen))
notify(this, l);
endfor
else
set_task_perms(((caller == this) && is_player(this)) ? this | caller_perms());
notify(this, line);
endif
.
#6:98
return this == args[1];
.
#6:99
"return true if player is active. This verb is !d";
try
idle_seconds(this);
return 1;
except (ANY)
return 0;
endtry
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874526409, \"Erik\", #74, \"JHM\"}";
.
#6:100
"Syntax: @quicksend <recipients(s)> [subj=<text>] [<message>]";
"";
"Sends the recipients(s) a quick message, without having to go to the mail room. If there is more than one recipients, place them all in quotes. If the subj contains spaces, place it in quotes.";
"";
"To put line breaks in the message, use a caret (^).";
"";
"If no message is given, prompt for lines of message.";
"";
"Examples:";
"";
"        @quicksend Alice subj=\"Wonderland is neat!\" Have you checked out the Wonderland scenario yet? I think you'd like it!";
"        @quicksend \"Ethel Fred\" Have you seen Lucy around?^--Ricky";
set_task_perms(player);
if (!args)
player:notify(tostr("Usage: ", verb, " <recipients(s)> [subj=<text>] [<message>]"));
return E_INVARG;
elseif (this != player)
player:notify(tostr("You can't use ", this:pp(), " @quicksend verb."));
return E_PERM;
elseif (!(recipients = $mail_editor:parse_recipients({}, $string_utils:explode(args[1]))))
return;
else
if ((length(args) > 1) && ((eq = index(args[2], "=")) && (index("subject", args[2][1..eq - 1]) == 1)))
subject = $string_utils:trim(args[2][eq + 1..length(args[2])]);
ws = $string_utils:word_start(argstr);
argstr = argstr[1..ws[1][2]] + argstr[ws[2][2] + 1..length(argstr)];
args = listdelete(args, 2);
else
subject = "";
endif
if (length(args) > 1)
unbroken = argstr[(argstr[1] == "\"") ? length(args[1]) + 4 | (length(args[1]) + 2)..length(argstr)] + "^";
message = {};
while (unbroken)
if (i = index(unbroken, "^"))
message = {@message, unbroken[1..i - 1]};
endif
unbroken = unbroken[i + 1..length(unbroken)];
endwhile
else
if (!(subject || player:mail_option("nosubject")))
player:notify("Subject:");
subject = $command_utils:read();
endif
player:notify("Enter lines of message:");
message = $command_utils:read_lines_escape((active = player in $mail_editor.active) ? {} | {"@edit"}, {tostr("You are composing mail to ", $mail_agent:name_list(@recipients), "."), @active ? {} | {"Type `@edit' to take this into the mail editor."}});
if (typeof(message) == ERR)
player:notify(tostr(message));
return;
elseif (message[1] == "@edit")
$mail_editor:invoke(1, verb, recipients, subject, {}, message[2]);
return;
elseif (!(message[2] || subject))
player:notify("Blank message not sent.");
return;
endif
message = message[2];
endif
while (!((result = $mail_agent:send_message(this, recipients, subject, message)) && result[1]))
recipients = 0;
while (!recipients)
player:notify("Message not sent.  Specify alternate recipients, `@edit', or `@abort':");
line = $string_utils:trim($command_utils:read());
if (line == "@edit")
$mail_editor:invoke(1, verb, {}, subject, {}, message[2]);
return;
endif
recipients = $mail_editor:parse_recipients({}, $string_utils:explode(line));
endwhile
endwhile
player:notify(tostr("Message sent to ", $mail_agent:name_list(@listdelete(result, 1)), "."));
endif
.
#6:101
"This is the polite spoof verb.  `!<string>'  announces an arbitrary string to the current location, except that if the string doesn't contain the player name somewhere as a distinct word, or commences with the name of another player present at that location, the player's .spoof_attribution property (with the usual pronoun substitutions) is appended and if the resulting strings *still* doesn't contain player:name(), we append it.";
if (player != this)
player:tell(("Sorry, you can't use " + this:name()) + "'s ! verb");
return;
elseif (!valid(this.location))
player:tell("You can't spoof here.");
return;
endif
argstr = $code_utils:argstr(verb, args);
if (length(verb) > 1)
msg = (verb[2..$] + " ") + argstr;
else
msg = argstr;
endif
ok = 1;
for o in (setremove(this.location.contents, this))
if (is_player(o) && (index(msg, o.name) == 1))
ok = 0;
endif
endfor
if (!(ok && (index(msg, player:name()) || index(msg, player.name))))
attrib = $string_utils:pronoun_sub(player.spoof_attribution_msg);
if (!$string_utils:index_delimited(attrib, player:name()))
attrib = attrib + player:name();
endif
msg = msg + attrib;
endif
player.location:announce_all(msg);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874527110, \"Erik\", #74, \"JHM\"}";
.
#6:102
" [EZ-Page]             Page: '<player> [<message>]";
"                    Respond: ' <message>";
"";
" EZ-Page responds to the player last contacted with \"'\"";
if ((verb == "'") && (!argstr))
"player:tell_lines($code_utils:verb_documentation())";
player:tell("Last player paged:  ", this.last_player_paged);
else
who = verb[2..length(verb)] || this.last_player_paged;
if ((valid(p = player:my_match_player(who)) && (p.location == this.location)) && this:misc_option("page_dwim"))
iobj = p;
dobjstr = argstr;
this.last_player_paged = who;
p:whisper(argstr);
else
"this:page(this.last_player_paged = (verb[2..length(verb)] || this.last_player_paged), $string_utils:from_list(args, \" \"));";
this:page(this.last_player_paged = verb[2..length(verb)] || this.last_player_paged, argstr);
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874527238, \"Erik\", #74, \"JHM\"}";
.
#6:103
"Usage:  @pronoun_sub with <style>";
styles = properties($pronoun_sub);
available = {"The following pronoun_sub styles are available: ", "  " + $string_utils:english_list(styles, "none")};
if (!iobjstr)
player:tell_lines({@$code_utils:verb_usage(), @available});
player:tell("Your current style is ", player.pronoun_sub_style:dname(), ".");
return;
endif
style = $string_utils:find_prefix(iobjstr, styles);
if (style)
player.pronoun_sub_style = $pronoun_sub.(styles[style]);
player:tell("Set your pronoun_sub style to \"", styles[style], "\".");
elseif (style == $ambiguous_match)
player:tell_lines({"I couldn't tell which pronoun_sub style you meant.", @available});
else
player:tell_lines({"There is no such pronoun_sub style.", @available});
endif
.
#6:104
"Usage:  @set-note-{string | text} {#xx | #xx.pname}";
"        ...lines of text...";
"        .";
"";
"For use by clients' local editors, to save new text for a note or object property.  See $note_editor:local_editing_info() for details.";
set_task_perms(player);
text = $command_utils:read_lines(1);
if ((verb == "@set-note-string") && (length(text) <= 1))
text = text ? text[1] | "";
elseif (verb == "@set-note-value")
value_list = $note_editor:to_value(@text);
if (value_list[1])
player:tell("Error on line ", value_list[1], ":  ", value_list[2]);
player:tell("Value not saved.");
return;
elseif (typeof(value_list) != LIST)
player:tell("Error parsing value:  ", value_list);
player:tell("Value not saved.");
return;
else
text = value_list[2];
endif
endif
if (spec = $code_utils:parse_propref(argstr))
o = $code_utils:toobj(spec[1]);
p = spec[2];
if (typeof(o) == OBJ)
if ($object_utils:has_callable_verb(o, setter = "set_" + p))
e = `o:(setter)(text) ! ANY';
else
e = `o.(p) = text ! ANY';
endif
else
player:tell("You must supply an object number.  Isn't that lame?");
return;
endif
"if (e != text)";
if (typeof(e) == ERR)
player:tell("Error: ", e);
else
player:tell("Set ", p, " property of ", o:dname(), " (", o, ").");
endif
elseif (typeof(note = $code_utils:toobj(argstr)) == OBJ)
o = note;
e = `note:set_text(text) ! ANY';
if (typeof(e) == ERR)
player:tell("Error: ", e);
else
player:tell("Set text of ", o:dname(), ".");
endif
else
player:tell("Error: Malformed argument to ", verb, ": ", argstr);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874527497, \"Erik\", #74, \"JHM\"}";
.
#6:105
desc = pass(@args);
if (!desc)
return desc;
endif
integrate = $integration_utils:integrate(args, "integrate_player_msg");
if (integrate && (typeof(desc) == LIST))
desc[$] = (desc[$] + $string_utils:pronoun_sub(this.integrate_sep_msg)) + integrate[1];
if (length(integrate) > 1)
desc = {@desc, @integrate[2..length(integrate)]};
endif
endif
return desc;
"Metadata 202106";
"Last-Modify: {874527592, \"Erik\", #74, \"JHM\"}";
.
#6:106
"@nn  -- reads the first new message on the first mail_recipient (in .current_message) where new mail exists.";
set_task_perms(player);
cm = (who = `valid(this.mail_identity) ! E_PROPNF' ? this.mail_identity | this).current_message;
cm[1..2] = {{who, @cm[1..2]}};
for n in (cm)
if (new = n[1]:length_date_gt(n[3]))
next = (n[1]:length_all_msgs() - new) + 1;
who:set_current_folder(folder = n[1]);
who:_set_mail_task();
cur = folder:display_seq_full({next, next + 1}, tostr("Message %d", " on ", $mail_agent:name(folder), ":"));
who:set_current_message(folder, @cur);
return;
endif
endfor
player:tell("No News (is news)");
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {837468467, \"Ken\", #75, \"JHM\"}";
.
#6:107
if (length(args) < 2)
player:tell("EZ-Whisper Usage: mu <player> <message>");
return;
endif
who = this.location:match_object(args[1]);
if ((!valid(who)) || (!$object_utils:has_callable_verb(who, "whisper")))
player:tell("\"", args[1], "\"", " is either ambiguous, or that person isn't here.");
return;
endif
"dobjstr = $string_utils:from_list(listdelete(args, 1), \" \");";
dobjstr = argstr[index(argstr, " ") + 1..length(argstr)];
iobj = who;
who:whisper(argstr);
.
#6:108
if ((!dobjstr) && ($code_utils:short_prep(prepstr) == "in"))
player:tell("You can't look ", prepstr, " ", this:dname(), "!");
return;
endif
name = args[1];
if (!name)
this:look_self();
else
pass(@args);
endif
.
#6:109
"Usage:  @options";
"        @options <category>";
"        @options all";
"        @options <category> <option>";
"";
"Get information about \"preferences\".  The first form lists the categories available, the second lists the options associated with a given category, the fourth lists the options from all categories, and the fourth either shows or sets the value of a given option.";
1;
"Considering \"@options <option>\" to search all over for an option that matches.  This might be more trouble than it's worth, though.";
packages = this:options_packages();
if (!args)
catlen = $list_utils:max_length($list_utils:map_property(packages, "category"), length("Category")) + 2;
comlen = $list_utils:max_length($list_utils:map_property(packages, "command"), length("Command")) + 2;
uselen = min(((player:linelen() - catlen) - comlen) - 4, $list_utils:max_length($list_utils:map_property(packages, "used_for"), length("Used for")));
player:tell($string_utils:left("Category", catlen), $string_utils:left("Command", comlen), "Used for"[1..min(8, uselen)]);
player:tell($string_utils:left("--------", catlen), $string_utils:left("-------", comlen), "--------"[1..min(8, uselen)]);
for package in (packages)
player:tell($string_utils:left(package.category, catlen), $string_utils:left(package.command, comlen), package.used_for[1..min(length(package.used_for), uselen)]);
endfor
player:tell($string_utils:left("--------", catlen), $string_utils:left("-------", comlen), "--------"[1..min(8, uselen)]);
elseif (args[1] == "all")
if (extra = listdelete(args, 1))
player:notify_lines($code_utils:verb_usage());
else
for o in (this:options_packages())
this:options_command(o.category, {});
this:notify("");
endfor
endif
else
argstr = $string_utils:triml(argstr[length(args[1]) + 2..$]);
result = this:options_command(args[1], listdelete(args, 1));
if (typeof(result) == OBJ)
if (valid(result))
command = result.command || tostr("@options ", args[1], " <option>");
player:tell_lines({"", tostr("(You can use the `", command, "' command to manipulate these options.)")});
else
player:tell("That is not a valid category.  Type `@options' for a list.");
endif
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835805006, \"Erik\", #74, \"JHM\"}";
.
#6:110
":options_packages()";
"Returns a list of options packages usable by this player.";
return {$edit_options, $mail_options, $who_options, $misc_options, $client_options};
.
#6:111
":options_command(category, extra-args)";
"Returns an object ($nothing, $failed_match, $ambiguous_match) if category doesn't uniquely match an options package usable by this player.  (Matching is done with the package's .category property.";
"With no extra-args, show options in <category> (a string).";
"With extra-args, have the package associated with <category> parse them and do whatever's appropriate (setting or displaying an option.";
if (!((caller == this) || $perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
category = args[1];
extra = args[2];
package = $string_utils:match(category, this:options_packages(), "category");
if (!valid(package))
return package;
endif
category = package.category;
options = category + "_options";
set_option = ("set_" + category) + "_option";
if (!extra)
this:notify_lines({("Current " + category) + " options:", "", @package:show(this.(options), package.names)});
return package;
elseif (typeof(presult = package:parse(extra)) == STR)
this:notify(presult);
return;
else
if (length(presult) > 1)
if (typeof(sresult = this:(set_option)(@presult)) == STR)
player:notify(sresult);
return;
elseif (!sresult)
player:notify("No change.");
return;
endif
endif
player:notify_lines(package:show(this.(options), presult[1]));
endif
.
#6:112
this.contributed_line = this.contributed_line + tostr(@args);
.
#6:113
if (this.jtext_form != "mcp")
this:notify("-----");
endif
this.contributed_line = "";
this.contributed_links = {};
.
#6:114
jaddress = args[1][2];
jaddress = {@jaddress, this.link_shepherd};
this.contributed_links = {@this.contributed_links, jaddress};
this:contribute_partial("[", length(this.contributed_links), "]");
.
#6:115
if (length(args) > 1)
this.link_shepherd = args[2];
else
this.link_shepherd = $nothing;
endif
$jtext:convert_to(this.jtext_form, args[1], this);
return args[1];
.
#6:116
return;
.
#6:117
if (args[1] == #-1)
return E_INVARG;
this:notify("You are now in #-1, The Void.  Type `home' to get back.");
endif
set_task_perms(caller_perms());
pass(@args);
.
#6:118
return args[1]:integrate_in(this) && args[1]:integrate_player_msg();
.
#6:119
return $integration_utils:visible_of(@args);
.
#6:120
"@set-jtext-form linemode|tkmoo|lemoo|html|mcp";
if (this != player)
player:tell("That's not a form I know.");
return;
endif
forms = {"linemode", "tkmoo", "lemoo", "html", "mcp"};
if (length(args) != 1)
player:tell("I only know about the forms ", $string_utils:english_list(forms), ".");
return;
endif
if (!(args[1] in forms))
player:tell("That's not a form I know.");
return;
endif
this.jtext_form = args[1];
player:tell("OK, jtext boxes will now be sent to you in ", args[1], " form.");
.
#6:121
"Returns the string used in the description in a room, i.e. (dozing).";
"This verb checks idle time to see if any special idle message should";
"be returned, otherwise it returns a string indicating how long someone";
"has been dozing.";
if (this:is_idle())
return "idling";
else
try
z = idle_seconds(this);
except (E_INVARG)
return "asleep";
endtry
if (z > 3600)
return "off in another world";
return ("out on " + this:pp()) + " feet";
elseif (z > 600)
return "dozing";
elseif (z > 180)
return "daydreaming";
elseif (z > 60)
return "distracted";
else
return "alert";
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {875332364, \"Erik\", #74, \"JHM\"}";
.
#6:122
return pass(@args);
return (suffix = this:idle_suffix()) ? tostr(pass(@args), " (", suffix, ")") | pass(@args);
.
#6:123
"@edit <thing-to-edit>";
"The specified thing-to-edit will be parsed in the following way:";
"+ If it appears to specify a verb, the verb editor will be invoked appropriately.";
"+ If it appears to specify a property which is a string or list of strings, the note editor will be invoked appropriately.";
"+ If it appears to specify a property which is a list including non-strings, the property editor will be invoked.";
"+ If only an object is specified, we will call that object's :default_editing verb passing it dobjstr. This is expected to return a pair {editor, what-to-edit} of values. Thus, when we are editing a note object, we will pass 'Note' and we will get back {$note_editor, 'Note.text'}. The default for other objects, defined on $root_class, is {$note_editor, 'object.description'}.";
"+ If we can't at all figure out what we want, we'll say so. <grin>";
if (player != this)
player:tell("You can't use ", this:dname(), "'s ", verb, " verb.");
return;
endif
if (!dobjstr)
active = {};
for editor in (children($generic_editor))
if (player in editor.active)
active = setadd(active, editor);
endif
endfor
if (length(active) == 1)
active[1]:invoke(dobjstr, verb);
return;
elseif (length(active) == 0)
player:tell("Syntax: ", verb, " <object>:<verb> or <object>.<property> or <object>");
return;
else
player:tell("You have more than one unfinished editing session at the moment. You have work in ", $string_utils:iname_and_number_list(active), ". Type the name of which editor you would like to enter.");
desire = $command_utils:read();
editor = $string_utils:match(desire, active, "aliases");
if (editor == $failed_match)
player:tell("I don't understand that.");
return;
elseif (editor == $ambiguous_match)
player:tell("I don't understand which `", desire, "' you mean.");
return;
elseif (editor == $nothing)
player:tell("Um, thanks.");
return;
else
editor:invoke(dobjstr, verb);
return;
endif
return;
endif
endif
if ($code_utils:parse_verbref(dobjstr))
if (!player.programmer)
player:tell("Only programmers may edit verb code.");
return;
endif
$verb_editor:invoke(argstr, verb);
else
if (spec = $code_utils:parse_propref(dobjstr))
if ($command_utils:object_match_failed(object = player:my_match_object(spec[1]), spec[1]))
editing = 0;
elseif ($object_utils:has_callable_verb(object, ev = "editing_for_" + spec[2]))
editing = object:(ev)();
elseif ($object_utils:has_callable_verb(object, "editing_for"))
editing = object:editing_for(spec[2]);
else
editing = tostr(object:dnamec(), " can't be edited.");
endif
else
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
editing = 0;
else
editing = dobj:default_editing();
endif
endif
if ((typeof(editing) == LIST) && (typeof(editing[1]) == OBJ))
editing[1]:invoke_parsed(@editing[2]);
elseif (editing)
player:tell_lines(editing);
elseif (typeof(editing) == ERR)
player:tell(editing);
endif
endif
.
#6:124
"set_focus_object(object)";
"return E_PERM unless called with usual permissions or by <object>.";
"return E_INVARG unless <object> has a callable match_command verb.";
"else set focus_object and return the result.";
if ((args[1] == $nothing) && (caller in {this, this.focus_object}))
return this.focus_object = args[1];
elseif ((caller in {this, args[1]}) || $perm_utils:controls(caller_perms(), this))
if ($object_utils:has_callable_verb(args[1], "match_command"))
return this.focus_object = args[1];
else
return E_INVARG;
endif
else
return E_PERM;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874605271, \"Erik\", #74, \"JHM\"}";
.
#6:125
"email_address()";
"return this.email_address to authorized personnel only.";
if ($perm_utils:controls(caller_perms(), this) || this:misc_option("public_email"))
return this.email_address;
else
return E_PERM;
endif
.
#6:126
"misc_option(name)";
"client_option(name)";
"return the value of the specified option";
return #0.(verb + "s"):get(this.(verb + "s"), args[1]);
.
#6:127
"@forward-me <msg> [on *<recipient>]";
"send mail to my real email address";
if (player != this)
player:tell("Sorry.");
endif
if (!player:email_address())
player:tell("Sorry, you don't have a registered email address.");
return;
endif
if (!(p = this:parse_mailread_cmd(verb, args, "", "on", 1)))
"...lose...";
return;
elseif ($seq_utils:size(sequence = p[2]) != 1)
player:notify("You can only forward *one* message at a time.");
return;
elseif (p[4])
player:notify(tostr("Usage:  ", verb, " [<message>] [on <folder>]"));
return;
endif
this:set_current_folder(folder = p[1]);
for m in (folder:messages_in_seq(sequence))
msgnum = m[1];
msgtxt = m[2];
from = msgtxt[2];
if (msgtxt[4] != " ")
subject = tostr("[", from, ":  ", msgtxt[4], " (fwd)]");
elseif ((h = "" in msgtxt) && (h < length(msgtxt)))
subject = tostr("[", from, ":  `", msgtxt[h + 1][1..20], "' (fwd)]");
else
subject = tostr("[", from, " (fwd)]");
endif
player:notify(tostr("Forwarding ", msgnum, " ", subject, " to ", player:email_address(), "."));
player:notify(tostr("... ", length(text = $mail_agent:to_text(@msgtxt)), " lines ..."));
suspend(0);
$network:sendmail(player:email_address(), subject, "", @text);
endfor
.
#6:128
if ($perm_utils:controls(caller_perms(), this))
return this.ownership_quota;
else
return E_PERM;
endif
.
#6:129
"Returns the length of the previous line for the sake of the linemode client.";
"Further hackery: if there's an arg, captitalize the line.";
"SUSPEND.  This may be horribly broken, but I don't care, I'm tired of waiting around for someone to figure out the right fix.";
$command_utils:suspend_if_needed(0);
length = length(this.contributed_line);
if ((length(args) > 0) && args[1])
this.contributed_line = $string_utils:uppercase(this.contributed_line);
endif
if ((this.jtext_form == "tkmoo") && this.contributed_line)
notify(this, "tkmootag: " + this.contributed_line);
elseif ((this.jtext_form == "lemoo") && this.contributed_line)
notify(this, "lemootag: " + this.contributed_line);
elseif (this.jtext_form == "mcp")
if (typeof(this.contributed_document) == LIST)
this.contributed_document = {@this.contributed_document, this.contributed_line || "()"};
else
this:client_notify("jtext-line", {{"line", this.contributed_line || "()"}});
endif
else
this:notify(this.contributed_line);
endif
this.contributed_line = "";
return length;
.
#6:130
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863369889, \"Ken\", #75, \"JHM\"}";
.
#6:131
"client_notify(request type, keyword/value alist[, lines of data])";
"request type is a string identifying the type of request, e.g. \"ftp\" or \"edit\".  request type must contain no spaces, double-quotes, backslashes, or asterisks.";
"keyword/value alist is a list of pairs {keyword, value}.  Both keyword and value must be strings; keyword must contain no spaces, colons, double-quotes, or backslashes.";
"lines of data is included if this request is to be followed with, yes, lines of data.  Note that not including the data is different from including an empty list as data.";
"caller_perms() must have control over this object, or permission denied.";
"return E_INVIND if this player doesn't appear to have a smart client (i.e., doesn't have an authentication key set).";
"return E_QUOTA if the text is too long to translate and send out.";
"return E_INVARG if any of the keywords or the request type is bogus.";
{request, kvlist, ?data = 0} = args;
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
elseif (this.authentication_key == 0)
return E_INVIND;
elseif (match(request, "[ \\\"*]"))
return E_INVARG;
endif
keyvals = "";
for keyval in (kvlist)
{key, val} = keyval;
if (match(key, "[ \\\":]"))
return E_INVARG;
else
keyvals = tostr(keyvals, " ", key, ": ", val ? match(val, "[:\" ]") ? toliteral(val) | val | "\"\"");
endif
endfor
if (data != 0)
if (ticks_left() < (12 + (5 * length(args[3]))))
return E_QUOTA;
endif
notify(this, tostr("#$#", request, "* ", this.authentication_key, keyvals));
for line in (data)
notify(this, tostr("@@@", line));
endfor
notify(this, "#$#END");
else
notify(this, tostr("#$#", request, " ", this.authentication_key, keyvals));
endif
return 1;
.
#6:132
":brief()";
"return whether or not the player wants brief descriptions or not.";
return this:misc_option("brief") || (this.walking_task && (this.walking_dest != this.location));
.
#6:133
number = tonum(argstr);
if ((number < 1) || (number > length(this.contributed_links)))
if (length(this.contributed_links) == 0)
player:tell("There are no links for you to pick currently.");
return;
else
player:tell("The current range of link numbers is 1 through ", length(this.contributed_links), ".");
return;
endif
endif
$jaddress:send_me(this.contributed_links[number]);
.
#6:134
"Copied from PathFinder Feature Object (#975):walk by Morpheus (#811) Tue Apr 13 22:53:40 1993 CDT";
start = player.location;
if ($object_utils:isa(start, $generic_editor))
return player:tell("You must leave the editor manually before you can walk anywhere.");
endif
dest = this:my_match_room(deststr = iobjstr);
if ($command_utils:room_match_failed(dest, deststr))
return;
endif
if (dest == $nowhere)
player:tell("You can't walk to nowhere.");
return;
endif
dest = is_player(dest) ? dest.location | dest;
if (dest == start)
player:tell("You are already there.");
else
if (!this:walkto(dest))
player:tell("You can't walk from here to ", dest:dname(), ".");
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835799415, \"Erik\", #74, \"JHM\"}";
.
#6:135
"Copied from Erik (#74):_find_path Tue Apr 13 23:04:21 1993 CDT";
destination = args[2];
rooms = {args[1]};
paths = {{}};
which = 1;
while (which <= length(rooms))
check = rooms[which];
path = paths[which];
which = which + 1;
exits = `check.exits ! E_PROPNF => {}';
exits = (typeof(exits) == LIST) ? exits | {};
for exit in (exits)
"if (exit:obvious(this, destination) && exit:is_unlocked_for(this))";
"now don't worry about locked exits.  lame temporary solution.";
if (`exit:obvious(this, destination) ! E_VERBNF => 0')
try
dest = exit.dest;
except (E_PROPNF)
continue exit;
endtry
if (dest == destination)
return {@path, {exit.name, check}};
elseif (!(dest in rooms))
rooms = {@rooms, dest};
paths = {@paths, {@path, {exit.name, check}}};
endif
endif
endfor
endwhile
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874605756, \"Erik\", #74, \"JHM\"}";
.
#6:136
l = args[1];
if (this.walking_task && (this.walking_task != task_id()))
`kill_task(this.walking_task) ! ANY';
this.walking_task = 0;
endif
"The old version did this:";
"We're at our destination, turn off brief so the player sees the desc.";
"if (length(l) == 1)";
"  this:set_misc_option(\"brief\", this.temp_brief);";
"endif";
if (l && (this.location == (pair = l[1])[2]))
if (valid(exit = this.location:match_exit(pair[1])))
exit:move(this);
this.walking_task = 0;
fork f (1)
this:(verb)(listdelete(l, 1));
endfork
this.walking_task = f;
else
this:tell("Hm, you can't figure out how to get any closer to where you wanted to go.");
this:stopped_moving();
endif
else
if (l)
"We may be able to keep going if we are further along on the path.";
this:(verb)(listdelete(l, 1));
else
this.walking_task = 0;
this:stopped_moving();
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874605983, \"Erik\", #74, \"JHM\"}";
.
#6:137
start = this.location;
if ($object_utils:isa(start, $generic_editor))
return player:tell("You must leave the editor manually before you can join anyone.");
endif
who = player:my_match_player(dobjstr);
if ($command_utils:player_match_failed(who, dobjstr))
return;
endif
realdest = who.location;
if ($object_utils:isa(realdest, $generic_editor))
dest = realdest.original[who in realdest.active];
else
dest = realdest;
endif
if (dest == start)
if (realdest == dest)
player:tell("You are already there.");
else
player:tell($string_utils:pronoun_sub("The %n %<is> editing at the moment.  You can wait here until %s%<'s/'re> done.", who));
endif
elseif (!dest:is_public_location())
player:tell(who:dnamec(), " is not in a public location.");
else
"path = this:find_path(start, dest);";
"if (path)";
"this:move_by_exits(path);";
"else";
if (!this:walkto(dest))
player:tell("There is no walkable path from here to ", dest:dname(), ".");
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863668553, \"Erik\", #74, \"JHM\"}";
.
#6:138
if (player != this)
player:tell("That is not a valid command.");
return;
endif
number = tonum(dobjstr);
if ((number < 1) || (number > length(this.contributed_links)))
if (length(this.contributed_links) == 0)
player:tell("There are currently no links for you to pick.");
return;
else
player:tell("The current range of link numbers is 1 through ", length(this.contributed_links), ".");
return;
endif
endif
new_address = this.contributed_links[number];
this:receive_document(new_address);
"[1..min($, 20)] limits the list to 20 entries";
this.jaddress_stack = {new_address, @this.jaddress_stack}[1..min($, 20)];
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874606278, \"Erik\", #74, \"JHM\"}";
.
#6:139
if (player != this)
player:tell("That is not a valid command.");
return;
endif
if (length(this.jaddress_stack) <= 1)
this:tell("There are no documents on your stack.");
return;
endif
this.jaddress_stack = this.jaddress_stack[2..$];
$jaddress:send_me(this.jaddress_stack[1]);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874606372, \"Erik\", #74, \"JHM\"}";
.
#6:140
"Copied from Gopher Slate (#683):match_command by Joe (#73) Fri Apr 30 11:42:52 1993 CDT";
"match_command(vrb, dlist, plist, ilist)";
"return true if this object can handle the command, false otherwise";
"vrb - name of the verb the player typed";
"dlist - list of objspecs that this command matches";
"plist and ilist - likewise for prepspecs, iobjspecs";
if ((player == this) && (player.focus_object == this))
vrb = args[1];
dlist = args[2];
plist = args[3];
ilist = args[4];
if (((vrb in {"pick"}) && ("none" in plist)) && ("none" in ilist))
return 1;
elseif (((vrb in {"pop"}) && ("none" in dlist)) && (("none" in plist) || ("by" in plist)))
return 1;
endif
endif
"return pass(@args);";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {879923220, \"William_Lee\", #811, \"JHM\"}";
.
#6:141
return this.features;
.
#6:142
"receive_document(jaddress)";
"";
"temporary version created because Jay's been stalling.";
"temporary version seems to be living way too long.";
jaddress = args[1];
if (this:gag_p())
return;
endif
show_urls = this:client_option("urls");
if (show_urls)
url = $jaddress:convert_to("url", jaddress, this);
if (url[1] == "/")
url = $network.httpd_prefix + url;
endif
this:client_notify("display-url", {{"url", url}, {"command", "goto"}});
"  this:tell(\"URL: \", url);";
else
if (this.jtext_form == "mcp")
this.contributed_document = {};
endif
this:set_focus_object(this);
$jaddress:send_me(jaddress);
if (((this.jtext_form == "mcp") && (typeof(this.contributed_document) == LIST)) && this.contributed_document)
this:client_notify("jtext-document", {}, this.contributed_document);
this.contributed_document = E_NONE;
endif
endif
.
#6:143
"Usage:  @co [objspec]";
"Change (or just view, with no argument) your `current object'.";
set_task_perms(player);
if (player != this)
player:notify("You should get your own current object verb.");
return E_PERM;
endif
co = this.current_object;
if (dobjstr)
co = this:parse_current_object(dobjstr, co);
if (typeof(co) == LIST)
if (valid(co[1]))
if (stack = this.current_object_stack)
stack[length(stack)] = co;
this:set_current_object_stack(stack);
endif
this:set_current_object(co);
if (tostr(co[1]) == co[2])
player:notify(tostr("Your current object is now ", co[1]:iname(), " (", co[1], ")."));
else
player:notify(tostr("Your current object is now ", co[2], ", ", co[1]:iname(), " (", co[1], ")."));
endif
else
$command_utils:object_match_failed(@co);
endif
else
player:notify(tostr(co));
endif
else
if (tostr(co[1]) == co[2])
player:notify(tostr("Your current object is ", co[1]:iname(), " (", co[2], ")."));
else
player:notify(tostr("Your current object is ", co[2], ", ", co[1]:iname(), " (", co[1], ")."));
endif
endif
.
#6:144
"parse_current_object(string, current)";
"Figure out what the new current object should be from the given string.";
"set_task_perms(caller_perms());";
"Hey, how did it get a set_task_perms and yet be owned by hacker?!  --Ken";
parts = $string_utils:to_list(args[1], ".");
if (!parts[1])
parts[1..1] = $string_utils:to_list(args[2][2], ".");
endif
if ((parts[1] == "#0") && (length(parts) > 1))
parts[1..2] = {"$" + parts[2]};
endif
part = parts[1];
base = this:my_match_object(part);
if (!valid(base))
return {base, part};
endif
stack = {base};
i = 2;
while (i <= length(parts))
part = parts[i];
if (part == "^")
base = parent(base);
stack = listappend(stack, base);
i = i + 1;
elseif (part == "<")
if (i == 2)
if (parts[1][1] == "$")
base = #0;
stack = {#0};
parts[1..2] = {"#0"};
else
return tostr("Can't back up past ", parts[1] || co[2], ".");
endif
else
parts[i - 1..i] = {};
stack = listdelete(stack, length(stack));
base = stack[length(stack)];
i = i - 1;
endif
elseif ($object_utils:has_property(base, part))
base = base.(part);
if (typeof(base) != OBJ)
return tostr("The value of ", $string_utils:from_list(parts[1..i - 1], "."), " is either unreadable or not an object.");
else
stack = listappend(stack, base);
if ((i == 2) && (parts[1] == "#0"))
parts[1..2] = {"$" + part};
else
i = i + 1;
endif
endif
else
return tostr("There is no ", $string_utils:print(part), " property on ", $string_utils:from_list(parts[1..i - 1]), ".");
endif
endwhile
return {base, $string_utils:from_list(parts[1..i - 1], ".")};
.
#6:145
"Usage:  @pusho <objspec>";
"Push current object onto the stack, select a new current object.";
set_task_perms(player);
if (player != this)
player:notify("You should get your own ", verb, " verb.");
return E_PERM;
endif
co = this.current_object;
if (dobjstr)
try
co = this:parse_current_object(dobjstr, co);
except e (ANY)
return player:notify(e[2]);
endtry
if (valid(co[1]))
this:set_current_object_stack({@this.current_object_stack, this.current_object});
this:set_current_object(co);
this:display_current_object(@co);
else
$command_utils:object_match_failed(@co);
endif
elseif (stack = this.current_object_stack)
player:notify("Your current object stack:");
for co in (stack)
player:notify(tostr("  ", this:current_object_string(@co)));
endfor
this:display_current_object(@this.current_object);
else
player:notify("You have no current object stack.");
this:display_current_object(@this.current_object);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874608618, \"Erik\", #74, \"JHM\"}";
.
#6:146
"Usage:  @popo";
"Pop an object off the stack.";
set_task_perms(player);
if (player != this)
player:notify("You should get your own ", verb, " verb.");
return E_PERM;
endif
if (stack = this.current_object_stack)
co = stack[$];
this:set_current_object(co);
this:set_current_object_stack(stack[1..$ - 1]);
this:display_current_object(@co);
else
player:tell("Your current object stack is empty.");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874608559, \"Erik\", #74, \"JHM\"}";
.
#6:147
"Usage:  @swapo";
"Switch between the current object and the previous object on the stack.  Using this repeatedly alternates between two current objects.";
set_task_perms(player);
if (player != this)
player:notify("You should get your own ", verb, " verb.");
return E_PERM;
endif
if (stack = this.current_object_stack)
co = this.current_object;
this:set_current_object(stack[length(stack)]);
stack[length(stack)] = co;
this:set_current_object_stack(stack);
if (tostr(co[1]) == co[2])
player:notify(tostr("Your current object is now ", co[2], "."));
else
player:notify(tostr("Your current object is now ", co[2], " (", co[1], ")."));
endif
else
player:tell("Your current object stack is empty.");
endif
.
#6:148
if (player != this)
player:tell("You're trying to get details on ", this.name, ".");
return;
endif
number = tonum(dobjstr);
if ((number < 1) || (number > length(this.contributed_links)))
if (length(this.contributed_links) == 0)
player:tell("You have no contributed links.");
return;
else
player:tell("The current range of link numbers is 1 through ", length(this.contributed_links), ".");
return;
endif
endif
address = this.contributed_links[number];
player:tell((((address[1].name + " (") + "$jaddress.") + address[1].address_type) + ")");
for pair in (address[2])
player:tell((("  " + pair[1]) + ": ") + pair[2]);
endfor
.
#6:149
"Syntax: @@sendmail";
"This is intended for use with client editors.  You probably don't want to try using this command manually.";
"Reads a formatted mail message, extracts recipients, subject line and/or reply-to header and sends message without going to the mailroom.  Example:";
"";
"@@send";
"To: Rog (#145)";
"Subject: random";
"";
"first line";
"second line";
".";
"";
"Currently, header lines must have the same format as in an actual message.";
set_task_perms(player);
if (args)
player:notify(tostr("Usage: ", verb, " <recipients(s)> [subj=<text>] [<message>]"));
return;
elseif (this != player)
player:notify(tostr("You can't use ", this:pp(), " ", verb, " verb."));
return;
endif
msg = $command_utils:read_lines();
end_head = ("" in msg) || (length(msg) + 1);
subject = "";
replyto = "";
rcpts = {};
body = msg[end_head + 1..length(msg)];
for i in [1..end_head - 1]
line = msg[i];
if (index(line, "Subject:") == 1)
subject = $string_utils:trim(line[9..length(line)]);
elseif (index(line, "To:") == 1)
if (!(rcpts = $mail_agent:parse_address_field(line)))
player:notify("No recipients found in To: line");
return;
endif
elseif (index(line, "Reply-to:") == 1)
if ((!(replyto = $mail_agent:parse_address_field(line))) && $string_utils:trim(line[10..length(line)]))
player:notify("No address found in Reply-to: line");
return;
endif
elseif (i = index(line, ":"))
player:notify(tostr("Unknown header \"", line[1..i], "\""));
return;
else
player:notify("Blank line must separate headers from body.");
return;
endif
endfor
if (!rcpts)
player:notify("No To: line found.");
elseif (!(subject || body))
player:notify("Blank message not sent.");
else
player:notify("Sending...");
result = $mail_agent:send_message(this, rcpts, replyto ? subject | {subject, replyto}, body);
if (e = result && result[1])
if (length(result) == 1)
player:notify("Mail actually went to no one.");
else
player:notify(tostr("Mail actually went to ", $mail_agent:name_list(@listdelete(result, 1)), "."));
endif
else
player:notify(tostr((typeof(e) == ERR) ? e | ("Bogus recipients:  " + $string_utils:from_list(result[2]))));
player:notify("Mail not sent.");
endif
endif
.
#6:150
"Copied from Craig (#339):follow Tue Oct 12 23:35:31 1993 EDT";
"follow <player> will cause you to follow <player> everywhere <player> goes, as long as you can go there too, and <player> uses a valid exit.";
"";
"unfollow <player> will stop you from following <player>.";
"";
"lose <player> will stop a player from following you, if you're in the same room.";
"";
if (this.location != player.location)
player:tell("There is no \"", dobjstr, "\" here.");
elseif (player in this.followers)
player:tell(("You are already following " + this:dname()) + "!");
elseif (player == this)
player:tell("Ever hear of a dog chasing its own tail?");
elseif (!this.followable)
player:tell(this:dnamec(), " does not want to be followed right now.");
else
this.followers = listappend(this.followers, player);
player.following = listappend(player.following, this);
what = $pronoun_sub.("two-letter"):parse("%nD %n:(starts) to follow %dd.");
$you:say_action(what);
endif
.
#6:151
"Copied from Craig (#339):unfollow Tue Oct 12 23:35:34 1993 EDT";
"follow <player> will cause you to follow <player> everywhere <player>";
"goes, as long as you can go there too, and <player> uses a valid exit.";
"";
"unfollow <player> will stop you from following <player>.";
if (!args)
this:tell("Stop following whom?");
return;
endif
successful = {};
unsuccessful = {};
for name in (args)
who = player:my_match_object(name);
if (!valid(who))
who = player:my_match_player(name);
endif
if (!$command_utils:player_match_failed(who, name))
if ($object_utils:has_property(who, "followers"))
index = this in who.followers;
index2 = who in this.following;
if (index)
who.followers = listdelete(who.followers, index);
if (index2)
this.following = listdelete(this.following, index2);
endif
successful = listappend(successful, who);
else
unsuccessful = listappend(unsuccessful, who);
endif
else
unsuccessful = listappend(unsuccessful, who);
endif
endif
endfor
if (successful)
dobj = {successful, "", " or "};
what = $pronoun_sub.("two-letter"):parse("%nD no longer %n:(follows) %dd.");
$you:say_action(what);
endif
if (unsuccessful)
this:tell("But you weren't following ", $string_utils:dname_list(unsuccessful, "", " or "), "!");
endif
.
#6:152
"Usage: followers [on|off]";
"";
"Followers alone will tell you who's following you, and if you can be followed.";
"";
"Followers on will allow others to follow you, followers off will not allow";
"others to follow you.";
"";
if (player != this)
raise(E_PERM);
endif
if (!args)
if (!this.followers)
this:tell("You are not being followed.");
else
this:tell("Your followers are ", $string_utils:dname_and_number_list(this.followers), ".");
endif
if (this.followable)
this:tell("People can currently follow you.");
else
this:tell("People cannot currently follow you.");
endif
return;
endif
if ($string_utils:lowercase(args[1]) == "on")
this.followable = 1;
what = $pronoun_sub.("two-letter"):parse("People can now follow %nd.");
$you:say_action(what);
elseif ($string_utils:lowercase(args[1]) == "off")
this.followable = 0;
for x in (this.followers)
`x.following = setremove(x.following, this) ! E_INVIND, E_PROPNF';
endfor
this.followers = {};
what = $pronoun_sub.("two-letter"):parse("People can no longer follow %nd.");
$you:say_action(what);
else
player:tell_lines($code_utils:verb_usage());
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874610216, \"Erik\", #74, \"JHM\"}";
.
#6:153
"\"follow <player>\" will cause you to follow <player> everywhere <player> goes, as long as you can go there too, and <player> uses a valid exit.";
"";
"\"unfollow <player>\" will stop you from following <player> if <player> is in the room.";
"";
"\"lose <player>\" will stop a player from following you, if you're in the same room.";
"";
"\"following\" will show you who you are currently following, should they be in the room, or happen to come into the room.";
"";
"\"followers\" will tell you who's following you, and if you can be followed.";
"";
"\"followers on\" will allow others to follow you, and announce it to the room.";
"";
"\"followers off\" will not allow others to follow you, and announce it to the room.";
if (!this.following)
this:tell("You are not following anybody.");
else
this:tell(("You are now following " + $string_utils:dname_and_number_list(this.following)) + ".");
endif
"Copied from Craig (#339):following Tue Oct 12 23:54:40 1993 EDT";
.
#6:154
return $string_utils:match_player(@args);
"Metadata 202106";
"Last-Modify: {863668687, \"Erik\", #74, \"JHM\"}";
.
#6:155
"Usage: news [new] [contents] [all] [<articles>]";
"";
"Common uses:";
"news new      -- display articles you haven't seen yet";
"news all      -- display all current articles";
"news contents -- display headers of current news";
"";
"`news' with no arguments defaults to `news new'.";
set_task_perms(player);
cur = this:get_current_message($news) || {};
if (!args)
args = {"new"};
endif
if (typeof($news:parse_message_seq("1-$")) == STR)
player:tell("There is NO NEWS at all.");
elseif (args[1] == "contents")
"Do the mail contents list";
$news:display_seq_headers(@$news:parse_message_seq(args[2..length(args)] || $news.current_newspaper, @cur));
else
"Display news";
if ((args[1] == "all") || ((args[1] == "new") && (!player:current_message($news))))
"Display current news, as set by news:@set-newspaper to msg-seq";
args = $news.current_newspaper;
endif
msg_seq = $news:parse_message_seq(args, @cur);
if (typeof(msg_seq) == STR)
player:tell(args ? (args == {"new"}) ? "No new news." | "No matching news.  [Use `news contents' to get a table of contents]" | "No news.");
return;
endif
player:set_current_message($news, @$news:news_display_seq_full(@msg_seq));
endif
.
#6:156
"Page all the currently connected members of the appropriate group(s).  Use the @admins command (`@admins <group> [all]') to list the connected [and disconnected] members.";
groups = $admin:groups_helpme();
members = $admin:members_groups(groups);
gterms = $admin:member_term(groups);
gtermp = $admin:members_term(groups);
mailrs = $list_utils:map_prop(groups, "mail_recipient");
if (!$set_utils:intersection(members, connected_players()))
result = $mail_agent:send_message(player, mailrs, "forward from @911/@helpme", argstr);
if (result && result[1])
player:notify(tostr("No ", gtermp, " are currently online.  Your message was sent to the mailing list instead; the next ", gterms, " to log in should respond."));
else
player:notify(tostr("No ", gtermp, " are currently online, and there was an error while sending mail to the mailing list(s).  Please try to @send mail to the group(s) manually.  The lists are as follows:"));
for g in (groups)
player:notify(tostr("  ", $mail_agent:addr_name(g.mail_recipient)));
endfor
endif
else
if (!argstr)
argstr = "<no message>";
player:notify("If you have a particular question, please type it after @911 but before pressing return.");
endif
for p in (members)
p:receive_page((("[911] " + player:dnamec()) + ": ") + argstr);
endfor
if (!(player in members))
player:notify(tostr("Your message has been sent to all ", gtermp, " currently online.  (You can use `@admins <group name> [all]' to list the connected [and disconnected] ", gtermp, ".)"));
endif
endif
.
#6:157
"@administrators/@admins group [all]";
"";
"List all connected [all] members of the administrative group.";
"";
"See `help admin' for information about the admin groups on this MOO.";
if ((caller != player) && (!$perm_utils:controls(caller_perms(), player)))
return E_PERM;
endif
admin_groups = $object_utils:descendants($admin_group);
if (args)
group_name = args[1];
args = listdelete(args, 1);
else
player:notify(("The names of the admin groups are " + $string_utils:name_list(admin_groups)) + ".  List the people of which group?");
return E_ARGS;
endif
admin_group = $string_utils:match(group_name, admin_groups, "name", admin_groups, "aliases");
if (!valid(admin_group))
player:notify(((("There is no admin group named \"" + group_name) + "\".  The names of the admin groups are ") + $string_utils:name_list(admin_groups)) + ".");
return E_INVARG;
endif
player:notify(admin_group:dnamec() + ":");
player:notify("");
if (args)
$code_utils:show_who_listing(admin_group:all_members());
else
$code_utils:show_who_listing(admin_group:connected_members()) || player:notify(tostr("No members of ", admin_group:dname(), " currently logged in."));
endif
.
#6:158
return this.pronoun_sub_style:(verb)(@args);
.
#6:159
if (!argstr)
this:tell("Usage: @url pathname");
this:tell("");
this:tell("Try: @url /help/subject!summary");
return;
endif
jaddress = $jaddress:from_url(argstr);
if (!jaddress)
this:tell("`", argstr, "' doesn't seem to map onto a jaddress; can't fetch the document.");
return;
endif
this:tell("Raw jaddress: ", $string_utils:print(jaddress));
this:receive_document(jaddress);
.
#6:160
"@read-all-new-mail";
" Prints all new mail on every mail-recipient mentioned in .current_message";
" Generally this will spam you into next Tuesday.";
set_task_perms(valid(caller_perms()) ? caller_perms() | player);
noconfirm = args && ("yes" in args);
cm = this.current_message;
cm[1..2] = {{this, @cm[1..2]}};
this:_set_mail_task();
nomail = 1;
new_cms = {};
for f in (cm)
if (!($object_utils:isa(folder = f[1], $player) || $object_utils:isa(folder, $mail_recipient)))
player:notify(tostr(folder, " is neither a $player nor a $mail_recipient"));
elseif (typeof(flen = folder:length_all_msgs()) == ERR)
player:notify(tostr($mail_agent:name(folder), " ", flen));
elseif (msg_seq = $seq_utils:range(folder:length_date_le(f[3]) + 1, flen))
nomail = 0;
player:notify("===== " + $string_utils:left(tostr($mail_agent:name(folder), " (", s = $seq_utils:size(msg_seq), " message", (s == 1) ? ") " | "s) "), 40, "="));
player:notify("");
if (cur = folder:display_seq_full(msg_seq, tostr("Message %d", (folder == this) ? "" | (" on " + $mail_agent:name(folder)), ":")))
new_cms = {@new_cms, {folder, @cur}};
player:notify("");
endif
$command_utils:suspend_if_needed(2);
endif
endfor
if (nomail)
player:notify("You don't have any new mail anywhere.");
elseif (player:notify("===== " + $string_utils:left("End of new mail ", 40, "=")) || (noconfirm || $command_utils:yes_or_no("Did you get all of that?")))
for n in (new_cms)
this:set_current_message(@n);
endfor
player:notify("Last-read-dates updated");
else
player:notify("Last-read-dates not updated");
endif
.
#6:161
":send_self_netmail(msg [ ,from ])";
"return 0 if successful, otherwise error.";
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
elseif (error = $network:invalid_email_address(this.email_address))
return "Invalid email address: " + error;
else
msg = args[1];
if (length(args) > 1)
from = args[2];
this:notify(tostr("Receiving mail from ", from:title(), " (", from, ") and forwarding it to your .email_address."));
endif
oplayer = player;
player = this;
error = $network:sendmail(this.email_address, @msg);
if (error && (length(args) > 1))
this:notify(tostr("Mail sending failed: ", error));
endif
player = oplayer;
return error;
endif
.
#6:162
"Usage:  @add-help-db <name>";
"";
"Add a help database to the list of objects searched by the `help' command.";
if (player != this)
player:notify("Hey, how'd you do that?");
return E_PERM;
elseif (!dobjstr)
player:notify_lines($code_utils:verb_usage());
dblist = $code_utils:help_db_list(player);
if (dblist)
player:notify(tostr("You're currently using ", $string_utils:dname_list(dblist), "."));
endif
if (dblist = $set_utils:difference($help.known_dbs, dblist))
player:notify(tostr("Others available include ", $string_utils:dname_list(dblist), "."));
endif
return E_ARGS;
endif
if (dobj == $failed_match)
dobj = $help:match_help_db(dobjstr, $help.known_dbs);
endif
if ($help:help_db_match_failed(dobj, dobjstr))
return E_INVARG;
elseif (dobj in (dblist = $code_utils:help_db_list(player)))
player:notify(tostr("You can already use ", dobj:dname(), "."));
return E_QUOTA;
else
help = this.help;
if (typeof(help) == LIST)
this.help = listappend(help, dobj);
elseif (typeof(help) == OBJ)
this.help = {this.help, dobj};
else
this.help = {dobj};
endif
player:notify(tostr("Your help database list now contains ", $string_utils:dname_list(this.help), " (and, by ancestry, ", $string_utils:dname_list($set_utils:difference(dblist, this.help)), ")."));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {868907059, \"Erik\", #74, \"JHM\"}";
.
#6:163
"Usage:  @rm-help-db <name>";
"";
"Add a help database to the list of objects searched by the `help' command.";
if (player != this)
player:notify("Hey, how'd you do that?");
return E_PERM;
elseif (!dobjstr)
player:notify_lines($code_utils:verb_usage());
dblist = $code_utils:help_db_list(player);
if (dblist)
player:notify(tostr("You're currently using ", $string_utils:dname_list(dblist), "."));
endif
return E_ARGS;
endif
dblist = $code_utils:help_db_list(player);
if (dobj == $failed_match)
dobj = $help:match_help_db(dobjstr, dblist);
if (dobj == $failed_match)
dobj = $help:match_help_db(dobjstr, $help.known_dbs);
if (valid(dobj))
player:notify(tostr("You have no help database named `", dobjstr, "'.  Perhaps you meant ", dobj:dname(), ", but it's not currently on your list."));
else
player:notify(tostr("You have no help database named `", dobjstr, "'."));
endif
return E_INVARG;
elseif ($help:help_db_match_failed(dobj, dobjstr))
return E_INVARG;
endif
endif
help = player.help;
if ((((typeof(help) == OBJ) && (help != dobj)) || (typeof(help == LIST) && (!(help in dobj)))) || (!(typeof(help) in {OBJ, LIST})))
player:tell(dobj:dnamec(), " comes as part of your ancestry, sorry.");
elseif ((typeof(help) == LIST) && (length(help) > 1))
player.help = setremove(help, dobj);
player:notify(tostr("Removed ", dobj:dname(), ".  Your help database list now contains ", $string_utils:dname_list(this.help), " (and, by ancestry, ", $string_utils:dname_list($set_utils:difference(dblist, this.help)), ")."));
else
player.help = 0;
player:notify(tostr("Removed ", dobj:dname(), ".  Your help database list is now empty, although by ancestry you have ", $string_utils:dname_list(setremove(dblist, dobj)), "."));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {868907059, \"Erik\", #74, \"JHM\"}";
.
#6:164
set_task_perms(this.owner);
which = {};
folders = $mail_agent.contents;
cm = $list_utils:slice(this.current_message[3..$]);
for folder in (folders)
if (!(folder in cm))
which = {@which, {folder, folder:length_all_msgs()}};
endif
$command_utils:suspend_if_needed(0);
endfor
if (which)
player:notify("Here are some lists to which you aren't subscribed:");
for w in (which)
name = $mail_agent:name(w[1]);
player:notify(tostr($string_utils:left("    " + name, 31), " ", @(typeof(w[2]) == NUM) ? {w[2], " message", (w[2] == 1) ? "" | "s"} | {"Unreadable"}));
endfor
else
player:notify("There are no known mailing lists to which you aren't subscribed.  See a doctor.");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874610566, \"Erik\", #74, \"JHM\"}";
.
#6:165
"Handle reconnects.";
if (!(caller in {#0, this}))
return E_PERM;
endif
"Treat a redirected connection like a disconnect and reconnect, for total connect time purposes.";
this.last_disconnect_time = time();
"can't use this.last_connect_time because it's already been set when we get here.";
last_connect_time = this.previous_connection[1];
this.total_connect_time = this.total_connect_time + (this.last_disconnect_time - last_connect_time);
if (this.started_keeping_total == 0)
this.started_keeping_total = last_connect_time;
endif
.
#6:166
"@resend <msg> [on *<recipient>] to <recipient> [<recipient>...]";
set_task_perms(valid(caller_perms()) ? caller_perms() | player);
"...";
"... parse command...";
"...";
if (!(p = this:parse_mailread_cmd(verb, args, "", "on", 1)))
"...lose...";
return;
elseif ($seq_utils:size(sequence = p[2]) != 1)
player:notify("You can only resend *one* message at a time.");
return;
elseif ((length(p[4]) < 2) || (p[4][1] != "to"))
player:notify(tostr("Usage:  ", verb, " [<message>] [on <folder>] to <recip>..."));
return;
endif
recips = {};
for rs in (listdelete(p[4], 1))
if ($mail_agent:match_failed(r = $mail_agent:match_recipient(rs), rs))
return;
endif
recips = {@recips, r};
endfor
this:set_current_folder(folder = p[1]);
"...";
"... retrieve original message...";
"...";
m = folder:messages_in_seq(sequence)[1];
msgnum = m[1];
msgtxt = m[2];
if (forward_style = this:mail_option("resend_forw"))
"...message will be from player...";
pmh = $mail_agent:parse_misc_headers(msgtxt, "Reply-To", "Original-Date", "Original-From");
orig_from = pmh[3][3] || msgtxt[2];
else
"...message will be from author...";
pmh = $mail_agent:parse_misc_headers(msgtxt, "Reply-To", "Original-Date", "Original-From", "Resent-By", "Resent-To");
orig_from = pmh[3][3];
from = $mail_agent:parse_address_field(msgtxt[2])[1];
to = $mail_agent:parse_address_field(msgtxt[3]);
endif
"...";
"... report bogus headers...";
"...";
if (bogus = pmh[2])
player:notify("Bogus headers stripped from original message:");
for b in (bogus)
player:notify("  " + b);
endfor
if (!$command_utils:yes_or_no("Continue?"))
player:notify("Message not resent.");
return;
endif
endif
"...";
"... subject, replyto, original-date, original-from ...";
"...";
hdrs = {msgtxt[4], pmh[3][1], {"Original-Date", pmh[3][2] || ctime(msgtxt[1])}, @orig_from ? {{"Original-From", orig_from}} | {}, @pmh[1]};
"...";
"... send it ...";
"...";
if (forward_style)
result = $mail_agent:send_message(player, recips, hdrs, pmh[4]);
else
"... resend inserts resent-to and resent-by...";
result = $mail_agent:resend_message(player, recips, from, to, hdrs, pmh[4]);
endif
"...";
"... report outcome...";
"...";
if (!result)
player:notify(tostr(result));
elseif (result[1])
player:notify(tostr("Message ", msgnum, @(folder == this) ? {} | {" on ", $mail_agent:name(folder)}, " @resent to ", $mail_agent:name_list(@listdelete(result, 1)), "."));
else
player:notify("Message not resent.");
endif
.
#6:167
set_task_perms(callers() ? caller_perms() | player);
"...this code explicitly relies on being !d in several places...";
if ((index(verb, "?") != 1) || (length(verb) <= 1))
what = $string_utils:trimr(argstr);
elseif (argstr)
what = tostr(verb[2..$], " ", $string_utils:trimr(argstr));
else
what = verb[2..length(verb)];
endif
nstr = "";
if (match(what, "^[0-9]+$"))
nstr = what;
elseif (s = match(what, "%[%([0-9]+%)%]$"))
nstr = substitute("%1", s);
endif
if (nstr)
n = tonum(nstr);
if ((n >= 1) && (n <= (l = length(player.contributed_links))))
jaddress = player.contributed_links[n];
else
player:notify(l ? tostr("Current links range from 1 to ", l, ".") | "There are no help links to follow right now.");
endif
else
"...find a db that claims to know about `what'...";
dblist = $code_utils:help_db_list();
result = $code_utils:help_db_search(what, dblist);
if ((result[1] == $failed_help) && (result[2][1..7] == "failed "))
"... note: all of the last-resort stuff...";
"... is now located on $help:find_topics/get_topic...";
"... Ew, no, it's not, it means that $help has to go mucking about with player:match_object making our nice database into a huge monstrosity.";
o = this:match_help_object(what);
if (valid(o))
result = {$object_help, tostr(o)};
endif
endif
if (player.focus_object != player)
if (valid(player.focus_object))
player:notify("[Changing your focus object to yourself.]");
endif
player:set_focus_object(player);
endif
help = result[1];
topic = result[2];
if (typeof(topic) == STR)
jaddress = $jaddress.resolved_help:from_db_and_topic_and_dblist(help, topic, dblist);
else
jaddress = topic[2];
endif
endif
if (jaddress)
this:receive_document(jaddress, this);
endif
return;
"Metadata 202106";
"Last-Modify: {874610834, \"Erik\", #74, \"JHM\"}";
.
#6:168
"Copied from main help database (#60):match_help_object by Jay (#73) Fri Apr  8 22:51:49 1994 EDT";
what = args[1];
object = $string_utils:match_object(what, this.location);
if (object == $failed_match)
features = $set_utils:union(this:features(), $feature.registry:features());
"features = $set_utils:union(this:features(), $feature.warehouse:contents());";
object = $string_utils:match(what, features, "name", features, "aliases");
endif
return object;
.
#6:169
"@aliases [object]";
"Lists your aliases, or the aliases of the given object.";
if (!dobjstr)
dobj = this;
elseif (!valid(dobj))
if ($command_utils:object_match_failed(dobj = player:my_match_object(dobjstr), dobjstr))
return;
endif
endif
NUM = length(aliases = dobj.aliases);
if (dobj == player)
dobj = $you;
endif
player:tell(dobj:dnamec(), " ", dobj:verb_sub("has"), " ", $string_utils:english_number(NUM, "no"), " alias", (NUM == 1) ? "" | "es", NUM ? (": " + $string_utils:english_list(aliases)) + "." | ".");
return NUM;
.
#6:170
"Copied from generic player (#6):contribute_newline by Erik (#2) Mon Apr 18 18:58:01 1994 EDT";
$command_utils:suspend_if_needed(0);
if (this.contributed_line)
return this:contribute_newline(@args);
else
return 0;
endif
.
#6:171
this:notify($string_utils:space(this:linelen(), "-"));
.
#6:172
"This is not the standard :set_foo verb.  It allows features to call it.  Good thing we trust programmers.";
if (((caller == this) || (caller in this:features())) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
.
#6:173
return 1;
.
#6:174
"Players can only be singular.";
if (args[1] != 1)
return E_INVARG;
endif
return pass(@args);
.
#6:175
"What should this object be replaced with when printing messages about itself to itself?";
"Use $you, unless third_person option is set.";
return this:misc_option("third_person") ? this | $you;
.
#6:176
if (((nargs = length(args)) == 2) && (!args[2]))
nargs = 1;
endif
if (!args)
player:notify(tostr("Usage: ", verb, " <player> [with] [<message>]"));
return;
endif
who = player:my_match_player(args[1]);
if ($command_utils:player_match_result(who, args[1])[1])
return;
elseif (!$object_utils:connected(who))
"player:notify(tostr($string_utils:pronoun_sub((typeof(msg = who:page_absent_msg()) == STR) ? msg | \"%N is not currently logged in.\", who)));";
player:notify(tostr($string_utils:pronoun_sub(who:page_absent_msg(), who)));
return;
endif
header = player:page_origin_msg();
text = "";
if (nargs > 1)
if ((args[2] == "with") && (nargs > 2))
msg_start = 3;
else
msg_start = 2;
endif
msg = $string_utils:from_list(args[msg_start..nargs], " ");
text = tostr($string_utils:index_delimited(header, player:name()) ? player:psc() | player:inamec(), " ", player:verb_sub("pages"), ", \"", msg, "\"");
elseif (header == "")
text = tostr(player:inamec(), " ", player:verb_sub("pages"), " you.");
endif
who:receive_page(@setremove({header, text}, ""));
player:notify(tostr(who:page_echo_msg()));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {913134419, \"core initialization\", #3023, \"core\"}";
.
#6:177
"If no activity has happened during the time since the player ";
"indicated that they would be idle, then return true.";
if (this in connected_players())
return ((time() - this.idle_start_time) - 5) <= idle_seconds(this);
else
return 0;
endif
.
#6:178
player.dozing_string = args[1];
.
#6:179
player.idle_string = args[1];
player.idle_start_time = time();
.
#6:180
"Syntax: idle";
"        idle message";
"        reidle";
"        reidle message";
"";
"reidle with no argument uses the last idle message set.";
"";
if ((verb == "reidle") && (argstr == ""))
idle_msg = player:idle_msg("previous");
else
idle_msg = argstr;
endif
player:start_idling(idle_msg);
dozing_msg = player:dozing_msg();
idle_msg = player:idle_msg();
ctime = ctime();
if (matched = match(ctime, "%(%w+%)%W+%(%w+%)%W+%(%w+%)"))
r = matched[3];
day = ctime[r[1][1]..r[1][2]];
mon = ctime[r[2][1]..r[2][2]];
date = ctime[r[3][1]..r[3][2]];
time = $time_utils:ampm(ctime);
datestr = ((((((" at " + time) + " on ") + day) + ", ") + mon) + " ") + date;
else
datestr = "";
endif
if (verb == "idle")
player.location:announce_all(player:dnamec(), " idles: ", idle_msg);
endif
if (dozing_msg == "idling")
player:tell("[Idling", datestr, ": ", idle_msg, "]");
else
player:tell("[", dozing_msg, datestr, ": ", idle_msg, "]");
endif
.
#6:181
"If the player isn't idle, return a blank string, unless an argument is";
"given, in which case return what would otherwise have been returned.";
if ((!length(args)) && (!this:is_idle()))
return "";
elseif (this.idle_string == "")
return $string_utils:pronoun_sub(this.idle_msg);
else
return this.idle_string;
endif
.
#6:182
"If the player isn't idle, return a blank string, unless an argument is";
"given, in which case return what would otherwise have been returned.";
if ((!length(args)) && (!this:is_idle()))
return "";
elseif (this.dozing_string == "")
return $string_utils:pronoun_sub(this.dozing_msg);
else
return this.dozing_string;
endif
.
#6:183
"set the doing string";
"verbname based on standard set by start_idling and start_dozing";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
this.doing_string = args[1];
else
raise(E_PERM);
endif
.
#6:184
"return the player's current doing message: either as currently set or a default (if any is there)";
"return this.doing_string || `$string_utils:pronoun_sub(this.doing_msg) ! ANY => \"broken\"';";
return this.doing_string;
.
#6:185
"Syntax: doing message";
"";
doing_msg = argstr;
if (verb == "undoing")
player:start_doing("");
player:tell("[reset doing to null]");
elseif (!argstr)
player:tell("doing/undoing [message]");
else
player:start_doing(doing_msg);
player:tell("[doing: ", doing_msg, "]");
endif
.
#6:186
if ((caller != player) || (player != this))
return E_PERM;
endif
if ((!dobjstr) && (prepstr == "in"))
room = player:my_match_room(iobjstr);
if ($command_utils:room_match_failed(room, iobjstr))
return;
endif
if (!(plyrs = $set_utils:intersection(room:contents(), $code_utils:players())))
player:notify(tostr(room:dnamec(), " is empty."));
return;
endif
elseif ((!dobjstr) && (prepstr == "with"))
if (!$command_utils:player_match_failed(who = player:my_match_player(iobjstr), iobjstr))
if (!valid(room = who.location))
return player:tell(who:dnamec(), " is not in a valid location.");
endif
plyrs = $set_utils:intersection(room:contents(), $code_utils:players()) || {who};
else
return;
endif
else
if (i = "by" in args)
prepstr = "by";
iobjstr = $string_utils:from_list(args[i + 1..$], " ");
args = args[1..i - 1];
endif
plyrs = args ? listdelete($command_utils:player_match_result(player:my_match_player(args), args), 1) | $code_utils:connected_players();
endif
if (!plyrs)
return;
endif
if (prepstr == "by")
sort = $who_options:parse_order(1, iobjstr);
if (typeof(sort) == STR)
return player:tell(sort);
else
sort = sort[2];
endif
usualsort = this:who_option("order");
this:set_who_option("order", sort);
$who_utils:show_who_listing(plyrs);
this:set_who_option("order", usualsort);
else
$who_utils:show_who_listing(plyrs);
endif
"Copied from generic player (#6):@who by r'm (#1100) Thu Sep  1 22:45:38 1994 EDT";
"Copied from r'm (#1100):@w by r'm (#1100) Thu Sep  1 22:51:34 1994 EDT";
"Copied from generic player (#6):@who by Ken (#75) Mon Sep 12 22:07:42 1994 EDT";
"Copied from Ken (#75):@who by Ken (#75) Tue Sep 13 10:41:21 1994 EDT";
"Copied from generic player (#6):@who by Ken (#75) Wed Sep 14 17:09:05 1994 EDT";
"Copied from Ken (#75):@who by Ken (#75) Wed Sep 14 20:19:55 1994 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874611300, \"Erik\", #74, \"JHM\"}";
.
#6:187
"Usage:  :debug_if_player(value, @message)";
"";
"Just like :debug, but will return without printing if `player' is not `this'.  This can be handy when you're trying to debug something that gets called often, and you only want to get spammed by it when you're triggering it.";
value = args[1];
message = args[2..length(args)];
if (player == this)
set_task_perms(caller_perms());
return this:debug(value, @message || {caller});
else
return value;
endif
.
#6:188
if (this == $player)
return $string_utils:match_player(args[1]);
elseif (typeof(owned = this.owned_objects) == LIST)
return $string_utils:match(args[1], owned, "name", owned, "aliases");
else
return $failed_match;
endif
.
#6:189
return {this, this.location};
.
#6:190
"Usage:  :event_really_disconnected()";
"";
"Clear doing message and .responsible, and lose followers.";
if (caller == this)
this:start_doing("");
for p in (this.followers)
`p.following = setremove(p.following, this) ! ANY';
endfor
this.followers = {};
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {875333194, \"Erik\", #74, \"JHM\"}";
.
#6:191
"Usage:  :maybe_really_disconnected()";
"";
"This verb is called when the user disconnects.  If e stays disconnected for five minutes, a really_disconnected event is broadcast.";
if (caller != this)
return E_PERM;
endif
if (this.disconnect_task)
`kill_task(this.disconnect_task) ! ANY';
endif
fork task (300)
if (!$object_utils:connected(this))
try
this.disconnect_task = 0;
this:broadcast_event_really_disconnected();
except (ANY)
"probably got chparented/recycled/etc.";
endtry
endif
endfork
this.disconnect_task = task;
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {875333234, \"Erik\", #74, \"JHM\"}";
.
#6:192
return (index(args[1], this.name) == 1) ? args[1] | $string_utils:capitalize(args[1]);
"Copied from James (#2142):player_modname_c Thu Sep 22 17:43:26 1994 EDT";
.
#6:193
if (player != this)
player:tell("You can't use ", this:dname(), "'s ", verb, " verb.");
return;
endif
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
"...lose...";
elseif (player:edit_option("local"))
if ($object_utils:has_callable_verb(dobj, "editing_for_description"))
editing = dobj:editing_for_description();
elseif ($object_utils:has_callable_verb(dobj, "editing_for"))
editing = dobj:editing_for("description");
else
editing = tostr(dobj:dnamec(), " can't be edited.");
endif
if ((typeof(editing) == LIST) && (typeof(editing[1]) == OBJ))
editing[1]:invoke_parsed(@editing[2]);
elseif (editing || (typeof(editing) == ERR))
player:tell_lines(editing);
endif
else
player:tell("Usage:  @describe ", dobjstr, " as <anything>");
player:tell("        @edit ", index(dobjstr, " ") ? $string_utils:print(dobjstr) | dobjstr, ".description");
endif
.
#6:194
"ported from #6@lm by michele on 4 oct 94";
"@netforward <msg>...                  -- as in help on @netforward";
"@netforward <msg>... on *<recipient>  -- netforwards messages on recipient.";
"This command forwards mail-messages to your registered email-address.";
if (player != this)
return player:tell(E_PERM);
endif
if (reason = $network:email_will_fail(email = player.email_address))
return player:notify(tostr("Cannot forward mail to your email address: ", reason));
endif
set_task_perms(valid(caller_perms()) ? caller_perms() | player);
if (p = player:parse_mailread_cmd(verb, args, "", "on"))
player:set_current_folder(folder = p[1]);
msg_seq = p[2];
folderstr = (folder == player) ? "" | tostr(" from ", $mail_agent:name(folder));
if (!$command_utils:yes_or_no(tostr("You are about to forward ", seq_size = $seq_utils:size(msg_seq), " message(s)", folderstr, " to your registered email-address, ", email, ".  Continue?")))
player:notify(tostr("@Netforward cancelled."));
return;
endif
player:_set_mail_task();
netmail = {};
linelen = player:linelen();
maxmsg = minmsg = 0;
for msg in (folder:messages_in_seq(msg_seq))
minmsg = minmsg ? min(msg[1], minmsg) | msg[1];
maxmsg = maxmsg ? max(msg[1], maxmsg) | msg[1];
lines = {tostr("Message ", msg[1], folderstr, ":"), tostr("Date:     ", ctime(msg[2][1])), "From:     " + msg[2][2], "To:       " + msg[2][3], @(length(subj = msg[2][4]) > 1) ? {"Subject:  " + subj} | {}};
for line in (msg[2][5..length(msg[2])])
lines = {@lines, @$generic_editor:fill_string(line, linelen)};
$command_utils:suspend_if_needed(0);
endfor
netmail = {@netmail, @lines, "", "--------------------------", "", ""};
endfor
reason = player:send_self_netmail({header = tostr($network.MOO_name, " Message(s) ", minmsg, @(minmsg != maxmsg) ? {" - ", maxmsg} | {}, folderstr), @netmail});
player:notify((reason == 0) ? tostr("@netforward of ", header, " completed.") | tostr("@netforward failed: ", reason, "."));
endif
.
#6:195
"Usage:  @register [<someone>]";
"        @register <someone> as <email-address>[ <commentary>]";
"";
"Check or update an email address.";
"";
"The first form just tells you the person's email address (if you are that person or are in the appropriate admin group).";
"";
"The second form will change the database entry, assign a new password, and mail the new password to the given email address.";
"";
"Most people can only register themselves, and can't provide commentary (additional text that's stored with the email address).  Some admins can register others and add comments.";
if (player != this)
return player:notify(tostr(E_PERM));
endif
who = player:my_match_player(dobjstr, this);
groups = $admin:groups_set_registration_other();
member = $admin:ok_set_registration_other(this);
if ($command_utils:player_match_failed(who, dobjstr))
return;
elseif ((who != this) && (!member))
return player:tell("You can only register yourself.");
elseif ($object_utils:isa(who, $guest))
this:notify((who == this) ? "Use the '@request' command to request a character." | "Guests don't have email addresses.");
return;
endif
msgchar = (who == this) ? $you | who;
connection = $string_utils:connection_hostname(who.last_connect_place);
if (s = match(iobjstr, "%([^ ]+%)%( +%(.+%)%)?$"))
email = substitute("%1", s);
commentary = substitute("%3", s);
else
player:tell_lines($code_utils:verb_usage());
return;
endif
if (commentary && (!member))
return this:notify("Sorry, you can't include commentary.  If it's important that you do so, just talk to a ", $admin:member_term(groups), " (see `help <group name>').");
elseif (email == who.email_address)
this:notify(tostr("That is ", msgchar:name("p"), " current address.  Not changed."));
return;
elseif (!(check = $registration_db:check_address_request(email, connection, who))[1])
this:notify_lines(check[2]);
if (check[1] == E_INVARG)
return this:notify("Please try again.");
else
if (member)
if (!$command_utils:yes_or_no("Automatic registration not allowed.  Register anyway?"))
return this:notify("Okay.");
endif
else
return this:notify(tostr("Automatic registration not allowed.  Talk to a ", $admin:member_term(groups), " (see `help <group-name>') if you want to register for this address anyway."));
endif
endif
else
email = check[2];
endif
if (commentary)
commentary = ": " + commentary;
endif
old = who.email_address || "unregistered";
if ($network.active && (!(member && (who == this))))
if (!$command_utils:yes_or_no(tostr("If you continue, ", msgchar:name("p"), " password will be changed, and the new password mailed to `", email, "'.  Do you want to continue?")))
return this:notify("Registration terminated.");
endif
password = $wiz_utils:random_password(5);
this:notify(tostr("Registering ", msgchar:dname(), ", changing ", msgchar:pp(), " password, and mailing it to `", email, "'."));
result = $network:sendmail(email, tostr("Your ", $network.MOO_Name, " character, ", who.name), "Reply-to: " + $login.registration_address, @$generic_editor:fill_string(tostr("Your ", $network.MOO_name, " character, ", $string_utils:nn(who), " has been registered to this email address (", email, "), and a new password assigned.  The new password is `", password, "'. Please keep your password secure. You can change your password with the @password command."), 75));
if (result != 0)
this:notify(tostr("Mail sending did not work: ", reason, ". Reregistration terminated."));
return;
endif
who.password = crypt(password);
who.email_address = email;
who.last_password_time = time();
byname = (who == this) ? "" | (" by " + $string_utils:nn(this));
$mail_agent:send_message(member ? this | $new_player_log, $new_player_log, ("reg " + $string_utils:nn(who)) + byname, {email, tostr("formerly ", old), @commentary ? {commentary} | {}});
$registration_db:add(who, email, (("Reregistered at " + ctime()) + byname) + commentary);
who:notify(tostr("Mail with your new password forwarded", (who == this) ? "" | (" as part of reregistration initiated by " + this:dname()), ".  If you do not get it, send regular email to ", $login.registration_address, " with your character name."));
elseif (member)
if ((!$network.active) && (!$command_utils:yes_or_no(tostr("The network is down and mail cannot be sent.  Are you sure you want to register", (who == this) ? "" | (" " + who:dname()), "?"))))
return this:notify("Okay.");
endif
who.email_address = email;
byname = (who == this) ? "" | (" by " + $string_utils:nn(this));
$mail_agent:send_message(member ? this | $new_player_log, $new_player_log, ("reg " + $string_utils:nn(who)) + byname, {email, tostr("formerly ", old), @commentary ? {commentary} | {}});
$registration_db:add(who, email, (("Reregistered at " + ctime()) + byname) + commentary);
this:notify(tostr("Okay, set ", msgchar:name("p"), " address to `", email, "'."));
else
this:notify(tostr("Sorry, automatic reregistration is impossible right now.  Talk to a ", $admin:member_term(groups), " (see `help <group-name>')."));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863668775, \"Erik\", #74, \"JHM\"}";
.
#6:196
"Usage:  @register [player]";
"        @register <player> as <email-address>[ <commentary>]";
"";
"See the other $player:@register for more detail.";
if (player != this)
return player:notify(tostr(E_PERM));
endif
if (!dobjstr)
dobjstr = "me";
endif
who = player:my_match_player(dobjstr, this);
member = $admin:ok_check_registration_other(this);
if ($command_utils:player_match_failed(who, dobjstr))
return;
elseif (((who != this) && (!member)) && (!who:misc_option("public_email")))
return this:notify(tostr(who:name("pc"), " email address is not available to you."));
elseif ($object_utils:isa(who, $guest))
return this:notify("Guests don't have email addresses.");
else
email = who.email_address;
if ((who == this) || member)
all = $registration_db:find(email);
thisone = $list_utils:assoc(who, all);
this:notify(tostr(((who == this) ? $you | who):name("pc"), " email address is ", email, (length(thisone) > 1) ? (" (" + thisone[2]) + ")" | "", "."));
if (all = setremove(all, thisone))
previous = {};
for one in (all)
if (`one[1].email_address != email ! ANY => 1')
previous = setadd(previous, one[1]);
all = setremove(all, one);
endif
endfor
if (all)
this:notify("");
this:notify("Also registered to this address:");
for one in (all)
this:notify(tostr(one[1]:iname(), " (", one[1], ")", (length(one) > 1) ? (" (" + one[2]) + ")" | ""));
endfor
endif
if (previous)
for o in (previous)
if (!is_player(o))
"wonder what this is for.";
previous = listappend(setremove(previous, o), o);
endif
endfor
this:notify("");
this:notify($string_utils:pronoun_sub($pronoun_sub.lambdacore:parse("A %n %<has> previously been registered to this address."), {previous}));
endif
endif
this:notify("");
if ((who == this) && all)
this:notify(tostr("(Use `@register me as <address>' to set your email address.)"));
else
this:notify(tostr("(Use `@register <someone> as <address>' to set an email address.)"));
endif
else
this:notify(tostr(who:name("pc"), " email address is ", email, "."));
all = $registration_db:find(email);
thisone = $list_utils:assoc(who, all);
all = setremove(all, thisone);
for one in (all)
if (!`(one[1].email_address == email) && one[1]:misc_option("public_email") ! ANY => 0')
all = setremove(all, one);
endif
endfor
if (all)
all = $list_utils:slice(all);
this:notify("");
this:notify($string_utils:pronoun_sub($pronoun_sub.lambdacore:parse("A %n %<is> also registered to this address."), {all}));
endif
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874612610, \"Erik\", #74, \"JHM\"}";
.
#6:197
"Usage:";
"";
lines = args[1];
return tostr("*** More ***  ", lines, " lines left.  Do @more [rest|flush] for more.");
.
#6:198
if (!(this in connected_players()))
return $string_utils:pronoun_sub($pronoun_sub.lambdacore:parse("%S %<is> sleeping."), this);
else
idle = idle_seconds(this);
if (idle < 60)
return $string_utils:pronoun_sub($pronoun_sub.lambdacore:parse("%S %<is> awake and %<looks> alert."), this);
else
time = $string_utils:from_seconds(idle);
if (this:is_idle())
return tostr($string_utils:pronoun_sub($pronoun_sub.lambdacore:parse("%S %<is> awake, but %<has> been idle for "), this), time, ": ", this:idle_msg());
else
return tostr($string_utils:pronoun_sub($pronoun_sub.lambdacore:parse("%S %<is> awake, but %<has> been staring off into space for "), this), time, ".");
endif
endif
endif
.
#6:199
return {@pass(@args), @$player.features, $player_start, $object_help, $feature_help, $news, $object_utils, $perm_utils, $string_utils, $pronoun_sub, $pronoun_sub.lambdacore, $player_db, $command_utils, $code_utils, $recycler, $last_huh, $feature, $room, $no_one, $login_watcher, $list_utils, $time_utils, $hacker, $you, $help, $wiz_utils, $jtext, $jtext.title, $jtext.vgroup, $jtext.header, $network, $generic_editor, $mail_agent, $mail_editor, $error, $mail_recipient, $seq_utils, $mail_options, $edit_options, $guest, $building_utils, $note_editor, $gender_utils, $lock_utils, $root_class, $builder, $prog, $feature.registry, $exit, $set_utils, $integration_utils, $who_options, $misc_options, $client_options, $verb_editor, $jaddress, $admin, $admin_group, this.pronoun_sub_style, $failed_help, $jaddress.resolved_help, $who_utils, $login, $new_player_log, $registration_db, $walking_utils};
"Metadata 202106";
"Last-Modify: {835804145, \"Erik\", #74, \"JHM\"}";
.
#6:200
"Usage:  sit with <someone>";
"        sit on <seat>";
"        sit";
"";
"Tries to sit down next to this person.";
for o in (this.location.contents)
if ($object_utils:isa(o, $furniture) && (this in o.sitting))
return o:sit();
endif
endfor
player:tell(this:dnamec(), " doesn't seem to be sitting down.");
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835729785, \"Erik\", #74, \"JHM\"}";
.
#6:201
return !is_player(args[1]);
.
#6:202
if ((length(args) == 1) && (argstr[1] == "\""))
argstr = args[1];
endif
if (!$admin:ok_shout(player))
player:tell("Only administrators can shout; try `help communication' for more information.");
return;
endif
msg = $string_utils:pronoun_sub(this.shout_msg);
for person in (setremove(connected_players(), player))
person:notify(msg);
endfor
if (!player:misc_option("third_person"))
msg = $string_utils:pronoun_sub(this.shout_msg, $you);
endif
player:notify(msg);
.
#6:203
newhome = args[1];
if (!$object_utils:isa(newhome, $room))
return E_INVARG;
elseif (!`newhome:accept_for_abode(this) ! E_VERBNF => 0')
return E_NACC;
elseif ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.home = newhome;
else
return E_PERM;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874612741, \"Erik\", #74, \"JHM\"}";
.
#6:204
if (((valid(caller_perms()) && (caller != this)) && (caller != #0)) && (caller != $login))
return E_PERM;
endif
this:set_authentication_key(0);
clear_property(this, "jtext_form");
"notify(this, \"#$#mcp version: 1.0\");";
this:("@last-connection")();
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895261709, \"Ken\", #75, \"JHM\"}";
.
#6:205
if (typeof(this.responsible) != LIST)
this:notify("Hey, someone was mucking about with your .responsible property, and it was probably you.  Don't do it again.");
this.responsible = {};
return;
endif
if (length(verb) <= 6)
"@check, not @check-full";
n = 5;
trust = {this, $no_one, $hacker};
"... trust no one, my friend.... no one....  --Herod";
mistrust = {};
for k in (args)
if (z = $code_utils:tonum(k))
n = z;
elseif (k[1] == "!")
mistrust = listappend(mistrust, player:my_match_player(k[2..length(k)]));
else
trust = listappend(trust, player:my_match_player(k));
endif
endfor
msg_width = player:linelen() - 60;
for q in ((n > (y = length(z = this.responsible))) ? z | z[(y - n) + 1..y])
msg = tostr(@q[2]);
if (length(msg) > msg_width)
msg = msg[1..msg_width];
endif
s = this:whodunnit(q[1], trust, mistrust);
text = valid(s[1]) ? s[1].name | "** NONE **";
this:notify(tostr($string_utils:left(tostr((length(text) > 13) ? text[1..13] | text, " (", s[1], ")"), 20), $string_utils:left(s[2], 15), $string_utils:left(tostr((length(s[3].name) > 13) ? s[3].name[1..13] | s[3].name, " (", s[3], ")"), 20), msg));
endfor
this:notify("*** finished ***");
this.responsible = z;
else
"@check-full, from @traceback by APHiD";
matches = {};
if (length(match = argstr) == 0)
player:notify(tostr("Usage: ", verb, " <string> --or-- ", verb, " <number>"));
return;
endif
if (!(responsible = player.responsible))
player:notify("No text has been saved by the monitor.  (See `help @paranoid').");
else
if (typeof(x = $code_utils:tonum(argstr)) == ERR)
for line in (responsible)
if (index(tostr(@line[length(line)]), argstr))
matches = {@matches, line};
endif
endfor
else
matches = this.responsible[max(1, ((l = length(this.responsible)) - x) + 1)..l];
endif
if (matches)
linelen = player:linelen();
string_utils = $string_utils;
list_utils = $list_utils;
for match in (matches)
$command_utils:suspend_if_needed(3);
text = tostr(@match[length(match)]);
player:notify("Traceback for:");
player:notify(text);
verbwidth = 0;
for verbitem in (list_utils:slice(mm = listdelete(mm = match[1], length(mm)), 2))
verbwidth = max(verbwidth, length(verbitem));
endfor
numwidth = ((linelen - verbwidth) / 4) - 1;
widths = {numwidth, verbwidth, numwidth, numwidth, numwidth};
top = l = between = "";
for x in [1..5]
top = (top + between) + string_utils:left({"This", "Verb", "Programmer", "VerbLocation", "Player"}[x], -widths[x]);
l = (l + between) + string_utils:space(widths[x], "-");
between = " ";
endfor
player:notify(top);
player:notify(l);
for line in (listdelete(mm = match[1], length(mm)))
output = {};
for bit in [1..5]
output = {@output, string_utils:left((typeof(word = line[bit]) == STR) ? word | tostr(word, "(", valid(word) ? list_utils:shortest({word.name, @word.aliases}) | ((word == $nothing) ? "invalid" | ((word == $ambiguous_match) ? "ambiguous match" | "Error")), ")"), -widths[bit]), " "};
endfor
player:notify(string_utils:trimr(tostr(@output)));
endfor
player:notify(l);
endfor
player:notify("**** finished ****");
else
player:notify(tostr("No matches for \"", argstr, "\" found."));
endif
endif
endif
"Copied from yduJ (#68):@check-full Sun Feb 26 18:40:24 1995 EST";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863668839, \"Erik\", #74, \"JHM\"}";
.
#6:206
"Usage:  @add-handler <handler object>";
"Add an object to your event handlers list.";
set_task_perms(player);
if (dobjstr)
if (dobj == $failed_match)
dobj = $event_handler:match_type_object(dobjstr);
"  if ($event_handler.registry:handler_match_failed(dobj = $event_handler.registry:match_handler(dobjstr), dobjstr))";
"    return;";
"  endif";
endif
if (!$command_utils:object_match_failed(dobj, dobjstr))
if (dobj in player:handlers())
player:tell($string_utils:dnamec_and_number(dobj), " is already one of your event handlers.");
elseif (player:add_handler(dobj))
player:tell($string_utils:dnamec_and_number(dobj), " added as an event handler.");
else
player:tell("You can't seem to add ", $string_utils:dname_and_number(dobj), " to your event handlers list.");
endif
endif
else
player:tell("Usage:  @add-handler <object>");
handlers = {};
player_handlers = player:handlers();
" for f in ($handler.registry:handlers())";
for f in ($object_utils:descendants($event_handler))
if ((!(f in player_handlers)) && $code_utils:verb_or_property(f, "handler_ok", player))
handlers = {@handlers, f};
endif
endfor
" handler_list = $handler.registry:handler_list_lines(player, handlers);";
handler_list = $list_utils:map_verb(handlers, "dname");
if (handler_list)
player:tell("Available event handlers not already in your event handler list include:");
player:tell_lines(handler_list);
else
player:tell("There are no available event handlers that you don't already have.");
endif
endif
"Copied from generic player (#6):@addfeature by Doug (#107) Thu Mar 16 04:47:26 1995 EST";
.
#6:207
return this.handlers;
"Copied from generic player (#6):features by Doug (#107) Thu Mar 16 04:50:26 1995 EST";
.
#6:208
"Usage:  @remove-handler <handler object>";
"Remove an object from your .handlers list.";
set_task_perms(player);
if (dobjstr)
handlers = player:handlers();
if (!valid(dobj))
dobj = $string_utils:match(dobjstr, handlers, "name", handlers, "aliases");
endif
if (!$command_utils:object_match_failed(dobj, dobjstr))
if (dobj in handlers)
player:remove_handler(dobj);
player:tell(dobj, " (", dobj:name(), ") removed from your event handlers list.");
else
player:tell(dobjstr, " is not one of your event handlers.");
endif
endif
else
player:tell("Usage:  @remove-handler <object>");
endif
"Copied from generic player (#6):@remove-feature by Doug (#107) Thu Mar 16 04:52:08 1995 EST";
.
#6:209
"Usage:  @handlers [<name>] [for <player>]";
"List the event handler objects matching <name> used by <player>.";
if (!iobjstr)
player:tell("Usage: @handlers [<name>] for <player>");
return;
elseif ($command_utils:player_match_failed(whose = player:my_match_player(iobjstr), iobjstr))
return;
endif
handlers = {};
for handler in (whose:handlers())
if (!valid(handler))
"do nothing";
elseif ((!dobjstr) || ((dobjstr in handler.aliases) || ((pref = $string_utils:find_prefix(dobjstr, handler.aliases)) || (pref == $ambiguous_match))))
handlers = listappend(handlers, handler);
endif
endfor
if (handlers)
len = max(length("Handler"), length(tostr(max_object()))) + 1;
player:tell($string_utils:left("Handler", len), "Name");
player:tell($string_utils:left("-------", len), "----");
for handler in (handlers)
player:tell($string_utils:left(tostr(handler), len), handler:name());
endfor
player:tell($string_utils:left("-------", len), "----");
elseif (dobjstr)
player:tell("No handlers found on ", whose:name(), " (", whose, ") matching \"", dobjstr, "\".");
else
player:tell("No handlers found on ", whose:name(), " (", whose, ").");
endif
"Copied from generic player (#6):@features by Doug (#107) Thu Mar 16 04:55:23 1995 EST";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874613106, \"Erik\", #74, \"JHM\"}";
.
#6:210
"Usage:  @features [<name>]";
"List the event handler objects matching <name> used by player.";
iobjstr = "me";
iobj = player;
this:("@handlers")();
.
#6:211
if (((caller == this) || $perm_utils:controls(caller_perms(), this)) || this:misc_option("public_connect"))
return {this.total_connect_time, this.started_keeping_total};
else
return E_PERM;
endif
.
#6:212
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
raise(E_PERM);
else
this._mail_task = task_id();
endif
.
#6:213
"Usage:  :add_handler(handler)";
"";
"Add an event handler to this object.  $player lets its features add handlers.";
if (caller in {this, @this:features()})
return pass(@args);
else
$error:raise(E_PERM);
endif
"Copied from root class (#1):add_handler by Erik (#74) Tue Mar 26 14:52:13 1996 EST";
.
#6:214
"Usage:  :remove_handler(handler)";
"";
"Remove an event handler to this object.  $player lets its features remove handlers.";
if (caller in {this, args[1], @this:features()})
return pass(@args);
else
$error:raise(E_PERM);
endif
"Copied from event dispatcher (#1751):remove_handler by Doug (#444) Tue Apr 26 02:29:19 1994 EDT";
"Copied from root class (#1):remove_handler by Erik (#74) Tue Mar 26 14:57:04 1996 EST";
.
#6:215
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
.
#6:216
"Usage:  :move_by_exits(path, dest)";
"";
{path, dest} = args;
return $walking_utils:move_by_exits(this, @args);
"Copied from generic walking object (#414):move_by_exits by Ken (#75) Mon Apr 15 23:12:40 1996 EDT";
.
#6:217
"Usage:  :walking_step(step, path, dest)";
"";
if (caller in {this, $walking_utils})
{step, path, dest} = args;
{exit, room} = step;
if (room == this.location)
if (!valid(e = room:match_exit(exit)))
return 0;
else
e:move(this);
endif
endif
if ((where = dest:where_am_i()) != this.walking_dest)
"stay on tarrrget";
if (where == this.location)
if ($code_utils:task_valid(`dest.walking_task ! ANY'))
return this:find_path(this.location, dest.walking_dest);
else
return 0;
endif
elseif (!path)
"ok, time to retarget or we'll miss the target";
if (path = this:find_path(this.location, dest))
return path;
else
this:tell("Can't figure out how to get to ", where:dname(), ".");
return 0;
endif
endif
endif
endif
return path;
"Copied from generic walking object (#414):walking_step by Ken (#75) Mon Apr 15 23:12:53 1996 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {856040520, \"Ken\", #75, \"JHM\"}";
.
#6:218
"Usage:  :begin_walking(path, dest)";
"";
{path, dest} = args;
if (caller in {this, $walking_utils})
this:set_walking_dest(dest:where_am_i());
this:set_walking_task(task_id());
endif
"Copied from generic walking object (#414):begin_walking by Ken (#75) Mon Apr 15 23:13:03 1996 EDT";
.
#6:219
"Usage:  :end_walking(path, dest)";
"";
{?path = {}, ?dest = this.walking_dest} = args;
message = {$pronoun_sub, "do", {"name", "player", {"dc"}}, " ", {"verb", "player", "has"}, " arrived."};
if (caller in {this, $walking_utils})
if (this.walking_task && (task_id() != this.walking_task))
message = {$pronoun_sub, "do", {"name", "player", {"dc"}}, " ", {"verb", "player", "has"}, " stopped walking."};
"Following line added with inexcusably little understanding by splat";
`kill_task(this.walking_task) ! E_INVARG, E_PERM';
endif
this:set_walking_task(0);
this:tell($string_utils:pronoun_sub(message, this:misc_option("third_person") ? this | $you));
endif
"Copied from generic walking object (#414):end_walking by Ken (#75) Mon Apr 15 23:13:06 1996 EDT";
return;
"Metadata 202106";
"Last-Modify: {855984276, \"splat\", #3024, \"JHM\"}";
.
#6:220
"Usage:  :walkto(dest)";
"";
dest = args[1];
return $walking_utils:walkto(this, dest);
"Copied from generic walking object (#414):walkto by Ken (#75) Mon Apr 15 23:16:27 1996 EDT";
.
#6:221
"Usage:  :find_path(from, dest, ?who)";
"";
{from, dest, ?oksuspend = 1} = args;
return $walking_utils:find_path(from, dest, this, oksuspend);
"Copied from generic walking object (#414):find_path by Ken (#75) Mon Apr 15 23:17:30 1996 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {894559797, \"Ken\", #75, \"JHM\"}";
.
#6:222
this:end_walking({}, this.walking_dest);
"this:stopped_moving(this.temp_brief);";
"Copied from Ken (#75):stop by Ken (#75) Mon Apr 15 22:30:55 1996 EDT";
"Copied from generic walking object (#414):stop by Ken (#75) Mon Apr 15 23:18:13 1996 EDT";
.
#6:223
{?format = this:misc_option("fstamp") || $player:misc_option("fstamp"), ?stime = this.last_timestamp} = args;
day = ($time_utils:day(stime) != $time_utils:day(time())) ? ", on $D" | "";
parsed = $pronoun_sub.("two-letter"):parse(strsub(format, "%D", day));
if (stime == this.last_timestamp)
this.last_timestamp = time();
endif
zot = $string_utils:pronoun_sub(parsed, this);
return $time_utils:time_sub(zot, time());
.
#6:224
if ((dobjstr == "") || (dobj == player))
dobj = player;
else
dobj = player:my_match_player(dobjstr);
if (!valid(dobj))
$command_utils:player_match_failed(dobj, dobjstr);
return;
endif
endif
time = dobj.first_connect_time;
if (time == $maxint)
duration = time() - dobj.last_disconnect_time;
if (duration < 86400)
notice = $string_utils:from_seconds(duration);
else
notice = $time_utils:english_time((duration / 86400) * 86400);
endif
player:notify(tostr(dobj.name, " has never connected.  It was created ", notice, " ago."));
elseif (time == 0)
player:notify(tostr(dobj.name, " first connected before initial connections were being recorded."));
else
player:notify(tostr(dobj.name, " first connected on ", ctime(time)));
duration = time() - time;
if (duration < 86400)
notice = $string_utils:from_seconds(duration);
else
notice = $time_utils:english_time((duration / 86400) * 86400);
endif
player:notify(tostr($string_utils:pronoun_sub("%S %<is> ", dobj), notice, " old."));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863668850, \"Erik\", #74, \"JHM\"}";
.
#6:225
"Usage:  :my_match_room(name)";
"";
name = args[1];
if (name == "home")
return this.home;
elseif (name == "here")
return this.location;
else
return $string_utils:match_room(name);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863659173, \"Erik\", #74, \"JHM\"}";
.
#6:226
"Lose <player> will stop that player from following you.  You must both be in the same room.";
if (!args)
return $you:say_action($pronoun_sub.("two-letter"):parse("%nD %n:(loses)."));
endif
successful = {};
for name in (args)
pest = player:my_match_player(name);
if (valid(pest))
index = pest in this.followers;
index2 = this in pest.following;
if (index)
this.followers = listdelete(this.followers, index);
if (index2)
pest.following = listdelete(pest.following, index2);
endif
successful = listappend(successful, pest);
else
this:tell("But ", pest:dname(), " isn't following you!");
return;
endif
else
this:tell("You see no \"", name, "\" here.  You can't lose something you've already lost.");
return;
endif
endfor
dobj = {successful, "", " and "};
what = $pronoun_sub.("two-letter"):parse("%nD %n:(stops) %dd from following %no.");
$you:say_action(what);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {865051470, \"Ken\", #75, \"JHM\"}";
.
#6:227
"Usage:  :display_current_object(o, name)";
"";
"Print a message to this player about eir current object, given the object and the name used to identify it.";
{o, name} = args;
(caller == this) || raise(E_PERM);
this:notify(tostr("Your current object is now ", this:current_object_string(o, name), "."));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874607845, \"Erik\", #74, \"JHM\"}";
.
#6:228
"Usage:  :current_object_string(o, name)";
"";
"Return a string suitable for telling a player about eir current object, given the object and the name used to identify it.  The string will be of the form \"#NNN\" or \"name (#NNN)\".";
{o, name} = args;
if (tostr(o) == name)
return name;
else
return tostr(name, " (", o, ")");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874607859, \"Erik\", #74, \"JHM\"}";
.
#6:229
set_task_perms(caller_perms());
{reference, type, content} = args;
"reference format == [str|val]:#xx[.pname]";
if (!match(reference, "^%(str%|val%):.+"))
return {"Malformed reference: " + reference};
else
vtype = reference[1..3];
reference = reference[5..$];
if (((vtype == "str") && (type == "string")) && (length(content) <= 1))
text = content ? content[1] | "";
elseif (vtype == "val")
value_list = $note_editor:to_value(@content);
if (value_list[1])
return {tostr("Error on line ", value_list[1], ":  ", value_list[2]), "Value not saved."};
elseif (typeof(value_list) != LIST)
return {"Error parsing value:  " + value_list, "Value not saved."};
else
text = value_list[2];
endif
else
text = content;
endif
endif
if (spec = $code_utils:parse_propref(reference))
o = $code_utils:toobj(spec[1]);
p = spec[2];
if (typeof(o) == OBJ)
if ($object_utils:has_callable_verb(o, setter = "set_" + p))
e = o:(setter)(text);
else
e = o.(p) = text;
endif
else
return {"Malformed reference: You must supply an object number."};
endif
if (typeof(e) == ERR)
raise(e, tostr("Error: ", e));
else
return tostr("Set ", p, " property of ", o:dname(), " (", o, ").");
endif
elseif (typeof(note = $code_utils:toobj(argstr)) == OBJ)
o = note;
e = note:set_text(text);
if (typeof(e) == ERR)
return {tostr("Error: ", e)};
else
return tostr("Set text of ", o:dname(), ".");
endif
else
raise(E_INVARG, tostr("Error: Malformed argument to ", verb, ": ", argstr));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895598838, \"Ken\", #75, \"JHM\"}";
.
#6:230
"See $player:@@sendmail";
set_task_perms(caller_perms());
{reference, msg} = args;
end_head = ("" in msg) || (length(msg) + 1);
subject = "";
replyto = "";
rcpts = {};
body = msg[end_head + 1..length(msg)];
for i in [1..end_head - 1]
line = msg[i];
if (index(line, "Subject:") == 1)
subject = $string_utils:trim(line[9..length(line)]);
elseif (index(line, "To:") == 1)
if (!(rcpts = $mail_agent:parse_address_field(line)))
player:notify("No recipients found in To: line");
return;
endif
elseif (index(line, "Reply-to:") == 1)
if ((!(replyto = $mail_agent:parse_address_field(line))) && $string_utils:trim(line[10..length(line)]))
player:notify("No address found in Reply-to: line");
return;
endif
elseif (i = index(line, ":"))
player:notify(tostr("Unknown header \"", line[1..i], "\""));
return;
else
player:notify("Blank line must separate headers from body.");
return;
endif
endfor
if (!rcpts)
player:notify("No To: line found.");
elseif (!(subject || body))
player:notify("Blank message not sent.");
else
player:notify("Sending...");
result = $mail_agent:send_message(this, rcpts, replyto ? subject | {subject, replyto}, body);
if (e = result && result[1])
if (length(result) == 1)
player:notify("Mail actually went to no one.");
else
player:notify(tostr("Mail actually went to ", $mail_agent:name_list(@listdelete(result, 1)), "."));
endif
else
player:notify(tostr((typeof(e) == ERR) ? e | ("Bogus recipients:  " + $string_utils:from_list(result[2]))));
player:notify("Mail not sent.");
endif
endif
return {};
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895598886, \"Ken\", #75, \"JHM\"}";
.
#6:231
"Usage: +<who> <emote>";
"   or: ++<who> <emote>";
"";
"Like the emote command, but works from afar (and privately).";
"++ works the same as +, but leaves no space between the name and the text.";
"";
"Example:";
" Munchkin, in Munchkin's Home, types:";
"   +Fred can't think of a good example of + usage.";
" Fred sees:";
"   (from Munchkin's Home) Munchkin can't think of a good example of + usage.";
if (verb in {"+", "++"})
sf = player.last_player_paged;
else
s = (verb[2] == "+") ? 3 | 2;
sf = verb[s..length(verb)];
endif
if (sf[length(sf) - 1..length(sf)] == "'s")
sf = sf[1..length(sf) - 2];
ms_str = "'s " + argstr;
else
ms_str = argstr;
endif
who = player:my_match_player(sf);
if ($command_utils:player_match_result(who, sf)[1])
return;
elseif (!(who in connected_players()))
player:tell(who:dnamec(), " ", who:verb_sub("isn't"), " connected.");
else
player:set_last_player_paged(sf);
if (player.location:is_public_location())
pr = $string_utils:pronoun_sub(player.remote_emote_prefix_msg || "(from %l)");
else
pr = "(from a private location)";
endif
msg = tostr(player:dnamec(), (index(verb, "++") || (ms_str[1..2] == "'s")) ? "" | " ", ms_str);
if (who == player)
player:tell("(to ", player.gender.pr, ") ", msg);
elseif (who:receive_page(tostr(pr, " ", msg)))
player:tell("(to ", who:dname(), ") ", msg);
else
player:tell(who:dnamec(), " ", who:verb_sub("has"), " refused your emote.");
endif
endif
"Copied from Non-VR Feature (#341):+ by Ken (#75) Tue May 19 20:19:52 1998 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895623592, \"Ken\", #75, \"JHM\"}";
.
#6:232
{msg, fromobj} = args;
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
elseif (error = $network:invalid_email_address(this.email_address))
return "Invalid email address: " + error;
else
{when, ofrm, oto, subject, dummy, @raw_body} = msg;
body = {};
for line in (raw_body)
body = {@body, @$generic_editor:fill_string(line, this:linelen())};
endfor
forwarded_message = {"--- Forwarded Message ---", "Date: " + ctime(when), "From: " + ofrm, "To: " + oto, "Subject: " + subject, "", @body};
headers = {{"Subject", "Forwarded: " + subject}};
this:notify(tostr("Receiving mail from ", fromobj:dname(), " (", fromobj, ") and forwarding it to your .email_address."));
from = $network:reply_address(fromobj);
from_name = ((((fromobj:name() + " at ") + $network.moo_name) + "<") + from) + ">";
if (index(from, "not-a-valid"))
from = $network.postmaster;
endif
to_name = ((((this.name + " at ") + $network.moo_name) + " <") + this.email_address) + ">";
if ($smtp:sendmail(from, from_name, to_name, {this.email_address}, headers, forwarded_message))
return 1;
else
return 0;
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {913065087, \"Ken\", #75, \"JHM\"}";
.
#6:233
":receive_message(msg,from)";
if ((!$perm_utils:controls(caller_perms(), this)) && (caller != this))
return E_PERM;
endif
if (this:mail_option("netmail"))
if (this:send_forwarded_moomail(args[1], args[2]))
return 0;
endif
endif
set_task_perms(this.owner);
new = this:new_message_num();
ncur = (new <= 1) ? 0 | min(this:current_message(this), new);
this:set_current_message(this, ncur);
new = max(new, ncur + 1);
this.messages = {@this.messages, {new, args[1]}};
"... new-mail notification is now done directly by $mail_agent:raw_send";
"... see :notify_mail...";
return new;
"Copied from generic player (#6):receive_message by Ken (#75) Thu Jul  2 13:47:05 1998 EDT";
"Copied from Ken (#75):receive_message by Ken (#75) Tue Dec  8 11:06:42 1998 EST";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {913133202, \"Ken\", #75, \"JHM\"}";
.
#7:0
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
"... relies on -d...";
`this.source:remove_exit(this) ! ANY';
`this.dest:remove_entrance(this) ! ANY';
return pass(@args);
else
return E_PERM;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874613305, \"Erik\", #74, \"JHM\"}";
.
#7:1
"'description (...)', etc. - Return messages for the transparent exit.";
text = $string_utils:pronoun_sub(this.(verb), args[1]);
if (typeof(text) == STR)
return this:substitute(text);
elseif (typeof(text) == LIST)
"The list must be a list of strings. This is not checked.";
result = {};
for item in (text)
result = {@result, this:substitute(item)};
endfor
return result;
else
return text;
endif
.
#7:2
name = args[1];
if (!($perm_utils:controls(cp = caller_perms(), this) || (valid(this.source) && (this.source.owner == cp))))
return E_PERM;
endif
old_is_dir = $english:is_direction_name(this.name);
new_is_dir = $english:is_direction_name(name);
if ((!old_is_dir) && new_is_dir)
"Changing name from non-direction to direction.";
if (is_clear_property(this, "proper"))
"Avoid \"the north\".";
this.proper = 1;
endif
elseif (old_is_dir && (!new_is_dir))
"Changing name from direction to non-direction.";
clear_property(this, "proper");
endif
return (typeof(e = this.name = name) != ERR) || e;
.
#7:3
if ($perm_utils:controls(cp = caller_perms(), this) || (valid(this.source) && (this.source.owner == cp)))
return (typeof(e = this.aliases = args[1]) != ERR) || e;
else
return E_PERM;
endif
.
#7:4
name = this:name();
dir = this:direction();
if (dir in $english.cardinal_directions)
msg = tostr("%n:(goes) ", dir, ".");
elseif (name in {"leave", "out", "exit"})
msg = "%n:(leaves).";
else
msg = "%n:(leaves) for %td.";
endif
if (!this.prefix_name)
msg = "%nI " + msg;
endif
return $string_utils:pronoun_sub($pronoun_sub.("two-letter"):parse(msg), @args);
.
#7:5
if ((caller in {this, this.owner}) || $perm_utils:controls(caller_perms(), this))
return pass(@args);
else
return E_PERM;
endif
.
#7:6
"examine_key(examiner)";
"return a list of strings to be told to the player, indicating what the key on this type of object means, and what this object's key is set to.";
"the default will only tell the key to a wizard or this object's owner.";
who = args[1];
if (((caller == this) && $perm_utils:controls(who, this)) && (this.key != 0))
return {tostr(this:dnamec(), " will only transport objects matching this key:"), tostr("  ", $lock_utils:unparse_key(this.key))};
endif
.
#7:7
"'substitute (<string>)' - Do %-substitutions on the given string.";
text = args[1];
if (index(text, "%"))
text = $string_utils:substitute(text, {{"%%", "%"}, {"%w", this:dest_who()}, {"%c", this:dest_contents()}, {"%l", this:dest_description()}}, 1);
endif
return text;
.
#7:8
"'dest_who ()' - Return a string indicating who is in the destination.";
if ((typeof(dest = this.dest) == OBJ) && valid(dest))
who = {};
for item in (dest.contents)
if (is_player(item))
who = {@who, item};
endif
endfor
if (who)
return $string_utils:english_list($list_utils:map_verb(who, "dname"));
else
return this:no_one_msg(this);
endif
else
return this:no_one_msg(this);
endif
.
#7:9
"'dest_contents ()' - Return a string indicating the contents of the destination.";
if ((typeof(dest = this.dest) == OBJ) && valid(dest))
return $string_utils:english_list($list_utils:map_prop(dest.contents, "name"));
else
return this.nothing_msg;
endif
.
#7:10
"'dest_description ()' - Return a string indicating the description of the destination. If the destination's description is a list, you only get the first paragraph.";
if ((typeof(dest = this.dest) == OBJ) && valid(dest))
desc = dest:description();
"Try to return a string, even if the description is a list.";
if (typeof(desc) == LIST)
if (desc)
return desc[1];
else
return "";
endif
else
return desc;
endif
else
return "You see nothing.";
endif
.
#7:11
":set_message(msg_name,new_value, pronoun_sub style)";
"@nothing and @no_one shouldn't be pronoun_subbed.";
if (!((caller == this) || $perm_utils:controls(caller_perms(), this)))
return E_PERM;
elseif (args[2] in {"nothing", "no_one"})
this.(args[1] + "_msg") = args[2];
return 1;
else
return pass(@args);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874613413, \"Erik\", #74, \"JHM\"}";
.
#7:12
"set_description(newdesc) attempts to change this.description to newdesc";
"  => E_PERM   if you don't own this or aren't its parent";
if (!($perm_utils:controls(caller_perms(), this) || (this == caller)))
return E_PERM;
elseif (typeof(desc = args[1]) in {LIST, STR})
this.description = desc;
return 1;
else
return E_TYPE;
endif
.
#7:13
if (typeof(this.obvious) == LIST)
this.obvious = setadd(this.obvious, player);
endif
this:move(player);
.
#7:14
try
return (this in args[1].exits) || (this in args[1].entrances);
except (E_PROPNF)
return 0;
endtry
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {875332230, \"Erik\", #74, \"JHM\"}";
.
#7:15
"Return the canonical direction name of this exit.";
"It does this by finding the first alias which matches a member of the set $english:cardinal_directions (q.v.).";
for dir in (this.aliases)
if (dir in $english.cardinal_directions)
"adapted from #1:name()...";
for i in (args)
if (typeof(i) == LIST)
dir = this:("moddir_" + i[1])(dir, @listdelete(i, 1)) || dir;
"'|| dir' is an ugly but cheap way of handling malfunctioning or nonexistent moddir_foo verbs.";
elseif (typeof(i) == STR)
for j in [1..length(i)]
dir = this:("moddir_" + i[j])(dir) || dir;
endfor
endif
endfor
return dir;
endif
endfor
return this:name(@args);
.
#7:16
"'description (...)', etc. - Return messages for the transparent exit.";
text = pass(@args);
result = {};
for item in (text)
result = {@result, this:substitute(item)};
endfor
if (typeof(through = this:through_msg(this)) == LIST)
result = {@result, @through};
elseif (through)
result = {@result, through};
endif
return result;
.
#7:17
"Copied from generic exit (#7):dest_who by Doug (#74) Mon Sep 27 02:15:26 1993 EDT";
"'sub_who ()' - Return a string indicating who is in the destination.";
if ((typeof(dest = this.dest) == OBJ) && valid(dest))
who = {};
for item in (dest:contents())
if (is_player(item))
who = {@who, item};
endif
endfor
if (who)
return $string_utils:namec_for_tell_contents_list();
else
return this.no_one_msg;
endif
else
return this.no_one_msg;
endif
.
#7:18
what = args[1];
start = what.location;
if (start != this.source)
what:tell("You can't go that way.");
return;
endif
unlocked = this:is_unlocked_for(what);
if (typeof(dest = this.dest) == LIST)
dest = dest[1]:create(listdelete(dest, 1));
endif
if (unlocked)
dest:bless_for_entry(what);
endif
start:broadcast_event_move_by_exit_attempted(what, this);
dest:broadcast_event_move_by_exit_attempted(what, this);
if (unlocked && dest:accept(what))
if (msg = this:leave_msg(what))
what:tell_lines(msg);
endif
what:moveto(dest);
start:broadcast_event_move_by_exit_completed(what, this);
what.location:broadcast_event_move_by_exit_completed(what, this);
if ((what.location != start) || (dest == start))
"Don't print oleave messages if WHAT didn't actually go anywhere...";
"Unless the exit is actually a loop...";
msg = this:oleave_msg(what) || this:defaulting_oleave_msg(what);
start:announce_all_but({what}, this.prefix_name ? what:dnamec() + " " | "", msg || (what:verb_sub("has") + " left."));
endif
if (what.location == dest)
"Don't print arrive messages if WHAT didn't really end up there...";
if (msg = this:arrive_msg(what))
what:tell_lines(msg);
endif
msg = this:oarrive_msg(what) || this:defaulting_oarrive_msg(what);
what.location:announce_all_but({what}, this.prefix_name ? what:inamec() + " " | "", msg || (what:verb_sub("has") + " arrived."));
this:sweep_for_followers(what);
endif
else
start:broadcast_event_move_by_exit_failed(what, this);
what.location:broadcast_event_move_by_exit_failed(what, this);
if (msg = this:nogo_msg(what))
what:tell_lines(msg);
else
what:tell("You can't go that way.");
endif
if (msg = this:onogo_msg(what))
what.location:announce_all_but({what}, this.prefix_name ? what:dnamec() + " " | "", msg);
endif
endif
"Copied from generic exit (#7):move by Ken (#75) Tue Nov  7 19:12:28 1995 CST";
"Copied from generic exit (#7):move(old) by Ken (#75) Tue Nov  7 19:32:34 1995 CST";
.
#7:19
leader = args[1];
room = this.source;
for x in (`leader.followers ! E_PROPNF' || {})
fork (0)
if ((leader.location != room) && (x in room.contents))
dobj = x;
msg = {$pronoun_sub, "do", {"name", "dobj", {"dc"}}, " ", {"verb", "dobj", "follows"}, " ", {"name", "player", {"i"}}, "."};
"     $you:say_action(msg, leader);";
this:move(x);
endif
endfork
endfor
return;
room = this.source;
followers = {};
for x in (`leader.followers ! E_PROPNF' || {})
if ((leader.location != room) && (x in room.contents))
"  if ((leader.location != room) && ((x in connected_players()) && (x in room.contents)))";
followers = listappend(followers, x);
endif
endfor
return followers;
"Metadata 202106";
"Last-Modify: {867866900, \"Ken\", #75, \"JHM\"}";
.
#7:20
":obvious([person]) Is this exit obvious [for someone]?";
if (this.obvious == 1)
return 1;
elseif (!this.obvious)
return 0;
elseif (typeof(this.obvious) != LIST)
return 1;
elseif (args && (args[1] in this.obvious))
return 1;
else
return 0;
endif
"The above could be compressed into an ugly but single line, but that would reduce readability, imho --Ken";
return;
"Metadata 202106";
"Last-Modify: {912965290, \"Ken\", #75, \"JHM\"}";
.
#7:21
msg = tostr("%n:(arrives) from ", this.source:dname(), ".");
if (!this.prefix_name)
msg = "%nI " + msg;
endif
return $string_utils:pronoun_sub($pronoun_sub.("two-letter"):parse(msg), @args);
.
#7:22
newargs = {player, this, player.location, dobj, iobj, this.source, this.dest};
newargs[1..length(args)] = args;
return pass(@newargs);
.
#7:23
return {this, this.location};
.
#7:24
"Find the exit in this.source.entrances whose .source is this.dest.";
0;
"if (caller != this && !$perm_utils:controls(caller_perms(), this))";
" return E_PERM;";
"endif";
for entr in (this.source:entrances())
if (entr.source == this.dest)
return entr;
endif
endfor
return $failed_match;
.
#7:25
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
.
#7:26
"Usage:  :walking_cost(who)";
"";
{who, source, dest, source_exit} = args;
cost = this.walking_cost;
if (!this:is_unlocked_for(who))
cost = cost + 20;
endif
return cost + `this.dest:walking_cost(who, source, dest, source_exit) ! ANY => 0';
"Metadata 202106";
"Last-Modify: {912965345, \"Ken\", #75, \"JHM\"}";
.
#7:27
return this.(verb[5..$]);
.
#7:28
dir = args[1];
return "the " + dir;
.
#7:29
return this:("modname_" + verb[8..$])(@args);
.
#7:30
"'description (...)', etc. - Return messages for the transparent exit.";
"";
"If the destination is .dark, we return null.  This may be the wrong thing.";
if (`this.dest.dark ! E_INVIND => 0')
return {};
endif
text = $string_utils:pronoun_sub(this.(verb), args[1]);
if (typeof(text) == STR)
return this:substitute(text);
elseif (typeof(text) == LIST)
"The list must be a list of strings. This is not checked.";
result = {};
for item in (text)
result = {@result, this:substitute(item)};
endfor
return result;
else
return text;
endif
"Copied from generic exit (#7):through_msg by Erik (#74) Fri Jun 28 17:56:06 1996 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {836267874, \"Jay\", #2, \"JHM\"}";
.
#7:31
return this.source;
"Metadata 202106";
"Last-Modify: {863586929, \"Ken\", #75, \"JHM\"}";
.
#8:0
if (!this.dark)
pass(@args);
else
desc = this:description(@args);
if (desc)
player:tell_lines(desc);
else
player:tell("You see nothing special.");
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {861772885, \"Ben\", #3373, \"JHM\"}";
.
#8:1
if (this.opened)
player:tell("It's already open.");
elseif (this:is_openable_by(player))
this:set_opened(1);
$you:say_action(this:open_msg());
endif
.
#8:2
if (!$perm_utils:controls(valid(cp = caller_perms()) ? cp | player, this))
return player:tell("Permission denied.");
endif
key = $lock_utils:parse_keyexp(iobjstr, player);
if (typeof(key) == STR)
player:tell("That key expression is malformed:");
player:tell("  ", key);
else
this:set_open_key(key);
player:tell("Locked opening of ", this:name(), " with this key:");
player:tell("  ", $lock_utils:unparse_key(key));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {875330318, \"Ben\", #3373, \"JHM\"}";
.
#8:3
if (!this:can_reach(@args))
elseif ((this.open_key != 0) && (!$lock_utils:eval_key(this.open_key, args[1])))
player:tell($string_utils:pronoun_sub(this:open_fail_msg()));
else
return 1;
endif
return 0;
.
#8:4
if (!this.opened)
player:tell("It's already closed.");
elseif (this:is_closable_by(player))
this:set_opened(0);
$you:say_action(this:close_msg());
endif
.
#8:5
if (!$perm_utils:controls(valid(cp = caller_perms()) ? cp | player, this))
return player:tell("Permission denied.");
endif
this:set_open_key(0);
player:tell("Unlocked ", dobj:name(), " for opening.");
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {875330130, \"Ben\", #3373, \"JHM\"}";
.
#8:6
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
this.opened = opened = !(!args[1]);
this.dark = this.opaque > opened;
return opened;
else
raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {875330655, \"Ben\", #3373, \"JHM\"}";
.
#8:7
if (!$perm_utils:controls(player, this))
player:tell("Can't set opacity of something you don't own.");
elseif ((iobjstr != "0") && (!tonum(iobjstr)))
player:tell("Opacity must be an integer (0, 1, 2).");
else
player:tell("Opacity changed:  Now " + {"transparent.", "opaque.", "a black hole."}[1 + this:set_opaque(tonum(iobjstr))]);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {875329464, \"Erik\", #74, \"JHM\"}";
.
#8:8
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
if (typeof(number = args[1]) != NUM)
raise(E_INVARG);
else
number = (number < 0) ? 0 | ((number > 2) ? 2 | number);
this.dark = number > this.opened;
return this.opaque = number;
endif
else
raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {875330582, \"Ben\", #3373, \"JHM\"}";
.
#8:9
return this.(verb);
.
#8:10
if (this.opaque == 2)
player:tell("You can't see inside ", this:dname(), ".");
elseif ((this.opaque == 1) && (!this.opened))
player:tell(this:dnamec(), " is closed.");
else
pass(@args);
endif
.
#8:11
return (msg = this.(verb)) ? $string_utils:pronoun_sub(msg) | "";
.
#8:12
"Check for openness.";
if (!pass(@args))
return 0;
endif
if (this.automatic && (!this.opened))
this:open();
if (!this.opened)
return 0;
endif
endif
if (!this.opened)
player:tell(this:dnamec(), " ", this:verb_sub("is"), " closed.");
return 0;
else
return 1;
endif
.
#8:13
"Check for openness.";
if (this.dark && (this.automatic < 2))
player:tell("You can't see into ", this:dname(), " to remove anything.");
return 0;
endif
if (this.automatic && (!this.opened))
this:open();
if (!this.opened)
return 0;
endif
endif
if (!this.opened)
player:tell(this:dnamec(), " ", this:verb_sub("is"), " closed.");
return 0;
else
return pass(@args);
endif
.
#8:14
return this:can_reach(@args);
.
#8:15
return `args[1] ! E_RANGE' ? this.opened ? "Open" | "Closed" | (this.opened ? "open" | "closed");
"Metadata 202106";
"Last-Modify: {857913478, \"Ben\", #3373, \"JHM\"}";
.
#8:16
"Use %t{{\"open\"%} \"ic\"} to get \"an open foo\", for example (don't forget to escape the quotes)";
return (this.opened ? "open " | "closed ") + args[1];
"Metadata 202106";
"Last-Modify: {863737017, \"Ben\", #3373, \"JHM\"}";
.
#8:17
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {875330046, \"Ben\", #3373, \"JHM\"}";
.
#9:0
if ((caller != this) && (!this:is_readable_by(valid(caller_perms()) ? caller_perms() | player)))
player:tell("Sorry, but it seems to be written in some code that you can't read.");
else
this:do_read(player);
endif
.
#9:1
if (this:is_writable_by(valid(caller_perms()) ? caller_perms() | player))
this:set_text({});
player:tell("Note erased.");
return 1;
else
player:tell("You can't erase this note.");
return E_PERM;
endif
.
#9:2
if (this:is_writable_by(valid(caller_perms()) ? caller_perms() | player))
this:set_text({@this.text, dobjstr});
player:tell("Line added to note.");
else
player:tell("You can't write on this note.");
endif
.
#9:3
if (!this:is_writable_by(player))
player:tell("You can't modify this note.");
elseif (!dobjstr)
player:tell("You must tell me which line to delete.");
else
line = tonum(dobjstr);
if (line < 0)
line = (line + length(this.text)) + 1;
endif
if ((line <= 0) || (line > length(this.text)))
player:tell("Line out of range.");
else
this:set_text(listdelete(this.text, line));
player:tell("Line deleted.");
endif
endif
.
#9:4
set_task_perms(player);
key = $lock_utils:parse_keyexp(iobjstr, player);
if (typeof(key) == STR)
player:tell("That key expression is malformed:");
player:tell("  ", key);
else
try
this.encryption_key = key;
player:tell("Encrypted ", this:name(), " with this key:");
player:tell("  ", $lock_utils:unparse_key(key));
except (E_PERM)
player:tell(E_PERM);
endtry
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {875329736, \"Erik\", #74, \"JHM\"}";
.
#9:5
set_task_perms(player);
try
dobj.encryption_key = 0;
player:tell("Decrypted ", dobj:name(), ".");
except (E_PERM)
player:tell(E_PERM);
endtry
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {875329791, \"Erik\", #74, \"JHM\"}";
.
#9:6
return ($perm_utils:controls(caller_perms(), this) || this:is_readable_by(caller_perms())) ? this.text | E_PERM;
.
#9:7
key = this.encryption_key;
return (key == 0) || $lock_utils:eval_key(key, args[1]);
.
#9:8
return ($perm_utils:controls(caller_perms(), this) || this:is_writable_by(caller_perms())) ? (typeof(newtext = args[1]) == LIST) ? this.text = newtext | E_TYPE | E_PERM;
.
#9:9
return (this.writers && (typeof(this.writers) != LIST)) || ($perm_utils:controls(who = args[1], this) || (who in this.writers));
.
#9:10
"Usage:  mailme <note>";
"  uses the 'internet mail' object";
"  sends the text of this note to your REAL internet email address.";
"  This only works for players who have a valid registration .email_address";
set_task_perms(player);
if (!(address = player:email_address()))
player:tell("Sorry, you don't have a registered email address.");
return;
elseif (!(text = this:plaintext()))
player:tell(this:dnamec(), " is empty--there wouldn't be any point to mailing it.");
return;
endif
player:tell("Mailing ", this:dname(), " to ", address, ".");
player:tell("... ", length(text), " lines ...");
suspend(0);
player:send_self_netmail({this:name(), "", @text});
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {856754548, \"Ken\", #75, \"JHM\"}";
.
#9:11
if (!(this.location in {player, player.location}))
player:tell("I see no \"", dobjstr, "\" here.");
return;
elseif ($command_utils:object_match_failed(iobj = player.location:match_object(iobjstr), iobjstr))
return;
endif
"player:tell(\"Unfortunately, due to a security bug, showing of notes does not work.  Please tell \", iobj:dname(), \" to read \", this:name(), \".\");";
"return;";
$you:say_action("%N %<shows> %t to %i.");
this:do_read(iobj);
.
#9:12
"this verb works with the new @edit syntax.";
"it is passed the string which was used to call @edit, and it is expected to return an {editor object, string} pair.";
"the default properties, at this writing, are: .text for $note objects, and .description for others.";
"Should be called by player for permissions to work correctly.";
if (this:is_writable_by(caller))
return {$note_editor, {{this, "text"}, this.text}};
else
return E_PERM;
endif
.
#9:13
target = args[1];
if ((!is_player(target)) || (!this:is_readable_by(target)))
"if (is_player(target) && !this:is_readable_by(valid(caller_perms()) ? caller_perms() | target))";
target:receive_vbox({$jtext.paragraph, "Sorry, but it seems to be written in some code that you can't read."});
"target:tell(\"Sorry, but it seems to be written in some code that you can't read.\");";
else
"this:look_self();";
target:receive_vbox({$jtext.title, tostr("A note: ", this:namec())});
hdesc = $jtext:make_vgroup(this:description());
target:receive_vbox(hdesc);
target:receive_vbox({$jtext.header, 1, this:namec()});
for vbox in ($generic_help:to_jtext(this:text()))
target:receive_vbox(vbox);
endfor
"target:receive_vbox({$jtext.plaintext, @this:text()});";
endif
.
#9:14
"Copied from jtext note (#701):read by Jay (#88) Wed Apr 20 13:04:10 1994 EDT";
if (this.text)
player:receive_document({$jaddress.note, {{"objnum", tostr(this)}}, this});
"player:receive_vbox(this.text);";
else
player:tell("This note hasn't been written yet.");
endif
.
#9:15
if (this:text())
return pass(@args);
else
return {"It appears to be blank."};
endif
.
#9:16
return $string_utils:match(args[1], d = $object_utils:leaves(this), "name", d, "aliases");
.
#9:17
return {@pass(@args), $jaddress, $jaddress.note, $lock_utils, $perm_utils, $network, $command_utils, $you, $note, $note_editor, $jtext, $jtext.paragraph, $jtext.title, $jtext.header, $generic_help, $jtext.plaintext, $string_utils, $object_utils};
.
#9:18
if (caller != this)
return E_PERM;
else
showto = args[1];
jaddress = $jaddress.note:from_object(this);
showto:receive_document(jaddress);
showto:tell("(You finish reading.)");
"this:look_self();";
"if (text = this:text())";
" showto:tell_lines_suspended({\"\", @text, \"\", \"(You finish reading.)\"});";
"endif";
endif
.
#10:0
if (caller != #0)
raise(E_PERM);
endif
clist = {};
for i in [1..length(verbs(this))]
if ((verb_args(this, i) == {"any", "none", "any"}) && index((info = verb_info(this, i))[2], "x"))
vname = $string_utils:explode(info[3])[1];
star = index(vname + "*", "*");
clist = {@clist, $string_utils:uppercase(vname[1..star - 1]) + strsub(vname[star..length(vname)], "*", "")};
endif
endfor
notify(player, "I don't understand that.  Valid commands at this point are");
notify(player, "   " + $string_utils:english_list(setremove(clist, "?"), "", " or "));
return 0;
.
#10:1
if (caller != #0)
return E_PERM;
else
msg = this.welcome_message;
for line in ((typeof(msg) == LIST) ? msg | {msg})
if (typeof(line) == STR)
notify(player, line);
endif
endfor
this:check_player_db();
this:check_for_shutdown();
"this:maybe_print_idle();";
return 0;
endif
.
#10:2
if (caller != #0)
return E_PERM;
elseif (!args)
players = $code_utils:connected_players();
idles = {};
for p in (players)
idles = {@idles, idle_seconds(p)};
endfor
players = $list_utils:sort(players, idles);
l = length(players);
max = 15;
if (l > max)
notify(player, tostr("(Showing only the ", max, " most active users, of ", l, "...)"));
notify(player, "");
players[max + 1..l] = {};
endif
$who_utils:show_who_listing(players) || this:notify("No one logged in.");
else
plyrs = listdelete($command_utils:player_match_result($string_utils:match_player(args), args), 1);
$who_utils:show_who_listing(plyrs);
endif
return 0;
"Metadata 202106";
"Last-Modify: {865119481, \"Ken\", #75, \"JHM\"}";
.
#10:3
you_lose_msg = "Either that person does not exist, or has a different password.";
if (!(caller in {#0, this}))
return E_PERM;
"...caller isn't :do_login_command...";
elseif (args && (args[1] == "test"))
return this:test(@listdelete(args, 1));
elseif (!(length(args) in {1, 2}))
notify(player, tostr("Usage:  ", verb, " <existing-player-name> <password>"));
elseif (!valid(candidate = this:_match_player(name = strsub(args[1], " ", "_"))))
notify(player, you_lose_msg);
"...unknown player...";
elseif (is_clear_property(candidate, "password") || ((typeof(candidate.password) == STR) && ((length(candidate.password) < 2) || (crypt({@args, ""}[2], candidate.password) != candidate.password))))
notify(player, you_lose_msg);
"...bad password...";
server_log(tostr("FAILED CONNECT: ", args[1], " (", candidate, ") on ", connection_name(player), ($string_utils:connection_hostname(connection_name(player)) in candidate.all_connect_places) ? "" | "******"));
elseif (((candidate.name == "guest") && this.sitematch_guests) && valid(foreigner = $country_db:get_guest()))
notify(player, tostr("Okay,...  Logging you in as `", foreigner:name(), "'"));
this:record_connection(foreigner);
return foreigner;
elseif ((parent(candidate) == $guest) && (!valid(candidate = candidate:defer())))
if (candidate == #-3)
notify(player, "Sorry, guest characters are not allowed from your site right now.");
elseif (candidate == #-2)
notify(player, tostr("Sorry, guest characters are not allowed from your site.  Send email to ", this.registration_address, " to request a character."));
else
notify(player, "Sorry, all of our guest characters are in use right now.");
endif
else
if ((!(name in candidate.aliases)) && (name != tostr(candidate)))
notify(player, tostr("Okay,... ", name, " is in use.  Logging you in as `", candidate:name(), "'"));
endif
if (this:is_newted(candidate))
notify(player, "");
notify(player, this:newt_message_for(candidate));
notify(player, "");
else
this:record_connection(candidate);
if (verb[1] == "s")
candidate.use_do_command = 0;
endif
return candidate;
endif
endif
return 0;
"Metadata 202106";
"Last-Modify: {895718006, \"Ken\", #75, \"JHM\"}";
.
#10:4
if (caller != #0)
return E_PERM;
"... caller isn't :do_login_command()...";
elseif (!this:player_creation_enabled(player))
notify(player, this:registration_string());
"... we've disabled player creation ...";
elseif (length(args) != 2)
notify(player, tostr("Usage:  ", verb, " <new-player-name> <new-password>"));
elseif (!(password = args[2]))
notify(player, "You must set a password for your player.");
elseif (!(check = $player_db:check_name_request(args[1], $nothing))[1])
this:notify_lines(check[2]);
elseif (typeof(new = $player_db:create_registered_player(name = check[2], {name}, password, "", "", $new_player_log.autoregistration_player, 0)) != OBJ)
notify(player, tostr(new));
else
if ($player_class.programmer)
$wiz_utils:set_programmer(new, 1);
endif
this:record_connection(new);
return new;
endif
return 0;
.
#10:5
if (caller != #0)
return E_PERM;
else
boot_player(player);
return 0;
endif
.
#10:6
if (caller != #0)
return E_PERM;
else
notify(player, tostr("The server has been up for ", $time_utils:english_time(time() - $last_restart_time), "."));
return 0;
endif
.
#10:7
if (caller != #0)
return E_PERM;
else
notify(player, tostr("The MOO is currently running version ", server_version(), " of the LambdaMOO server code."));
return 0;
endif
.
#10:8
":parse_command(@args) => {verb, args}";
"Given the args from #0:do_login_command,";
"  returns the actual $login verb to call and the args to use.";
"Commands available to not-logged-in users should be located on this object and given the verb_args \"any none any\"";
if (caller != #0)
raise(E_PERM);
endif
if (!args)
return {this.blank_command, @args};
elseif (`(verb = (args = this:unescape_for_url(@args))[1]) && ((verb_args(this, verb) == {"any", "none", "any"}) && index(verb_info(this, verb)[2], "x")) ! E_VERBNF')
return args;
else
return {this.bogus_command, @args};
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878517298, \"Ken\", #75, \"JHM\"}";
.
#10:9
when = $shutdown_time - time();
if (when >= 0)
line = "***************************************************************************";
notify(player, "");
notify(player, "");
notify(player, line);
notify(player, line);
notify(player, "****");
notify(player, ("****  WARNING:  The server will be going down in about " + $string_utils:from_seconds(when)) + ".");
"notify(player, (\"****  WARNING:  The server will be going down in about \" + tostr(when)) + \" seconds.\");";
for piece in ($generic_editor:fill_string($shutdown_message, 60))
notify(player, "****            " + piece);
endfor
notify(player, "****");
notify(player, line);
notify(player, line);
notify(player, "");
notify(player, "");
endif
.
#10:10
if ($player_db.frozen)
line = "***************************************************************************";
notify(player, "");
notify(player, line);
notify(player, "***");
for piece in ($generic_editor:fill_string("The character-name matcher is currently being reloaded.  This means your character name might not be recognized even though it still exists.  Don't panic.  You can either wait for the reload to finish or you can connect using your object number if you remember it (e.g., `connect #1234 yourpassword').", 65))
notify(player, "***       " + piece);
endfor
if (this:player_creation_enabled(player))
notify(player, "***       This also means that character creation is disabled.");
endif
notify(player, "***");
notify(player, line);
notify(player, "");
endif
.
#10:11
":_match_player(name)";
"This is the matching routine used by @connect.";
"returns either a valid player corresponding to name or $failed_match.";
name = args[1];
if (valid(candidate = $string_utils:literal_object(name)) && is_player(candidate))
return candidate;
endif
".....uncomment this to trust $player_db and have `connect' recognize aliases";
if (valid(candidate = $player_db:find_exact(name)) && is_player(candidate))
return candidate;
endif
".....uncomment this if $player_db gets hosed and you want by-name login";
". for candidate in (players())";
".   if (candidate.name == name)";
".     return candidate; ";
".   endif ";
". endfor ";
".....";
return $failed_match;
.
#10:12
set_task_perms(caller_perms());
notify(player, args[1]);
.
#10:13
"keeps bad things from happening if someone brings this object into a room and talks to it.";
return 0;
.
#10:14
"Accepts a player object.  If player creation is enabled for that player object, then return true.  Otherwise, return false.";
"Default implementation checks the player's connecting host via $login:blacklisted to decide.";
if (caller_perms().wizard)
return this.create_enabled && (!this:blacklisted($string_utils:connection_hostname(connection_name(args[1]))));
else
return E_PERM;
endif
.
#10:15
return $string_utils:subst(this.(verb), {{"%e", this.registration_address}, {"%%", "%"}});
.
#10:16
if ($__core_init_phase)
pass(@args);
this.bogus_command = "?";
this.blank_command = "help";
this.create_enabled = 0;
this.registration_address = "";
this.registration_string = "Character creation is disabled.";
this.newt_registration_string = "Your character is temporarily hosed.";
this.welcome_message = {"Welcome to the JHCore database.", ((("  Extracted " + $time_utils:time_sub("$N $t, $Y")) + " (under LambdaMOO ") + server_version()) + ")", "", "Type 'connect wizard' to log in.", "", "You will probably want to change this text, which is stored in $login.welcome_message.", "", "Before you do, though, please read `help core-copyright' (linked to `help copyright') for the exceedingly broad copyright on JHCore.", "", "You will also want to read `help getting-started' for some more information about starting a JHCore MOO."};
this.spooflist = this.redlist = this.blacklist = this.graylist = {{}, {}};
this.bootlist = {};
this.newted = {};
if ("login_monitor" in properties(this))
delete_property(this, "login_monitor");
endif
if ("login_monitor" in verbs(this))
delete_verb(this, "login_monitor");
endif
if ("special_action" in verbs(this))
set_verb_code(this, "special_action", {});
endif
set_verb_args(this, "test", {"this", "none", "this"});
delete_verb(this, "set_welcome_message");
delete_property(this, "log");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878514464, \"Ken\", #75, \"JHM\"}";
.
#10:17
":blacklisted(hostname) => is hostname on the .blacklist";
":graylisted(hostname)  => is hostname on the .graylist";
":redlisted(hostname)   => is hostname on the .redlist";
sitelist = this.(this:listname(verb));
if (!caller_perms().wizard)
return E_PERM;
elseif (((hostname = args[1]) in sitelist[1]) || (hostname in sitelist[2]))
return 1;
elseif ($site_db:domain_literal(hostname))
for lit in (sitelist[1])
if ((index(hostname, lit) == 1) && ((hostname + ".")[length(lit) + 1] == "."))
return 1;
endif
endfor
else
for dom in (sitelist[2])
if (index(dom, "*"))
"...we have a wildcard; let :match_string deal with it...";
if ($string_utils:match_string(hostname, dom))
return 1;
endif
else
"...tail of hostname ...";
if ((r = rindex(hostname, dom)) && ((("." + hostname)[r] == ".") && (((r - 1) + length(dom)) == length(hostname))))
return 1;
endif
endif
endfor
endif
return 0;
.
#10:18
if (!caller_perms().wizard)
return E_PERM;
endif
where = args[1];
lname = this:listname(verb);
which = 1 + (!$site_db:domain_literal(where));
this.(lname)[which] = setadd(this.(lname)[which], where);
return 1;
.
#10:19
if (!caller_perms().wizard)
return E_PERM;
endif
where = args[1];
lname = this:listname(verb);
which = 1 + (!$site_db:domain_literal(where));
if (where in this.(lname)[which])
this.(lname)[which] = setremove(this.(lname)[which], where);
return 1;
else
return E_INVARG;
endif
.
#10:20
return {"???", "blacklist", "graylist", "redlist", "spooflist"}[1 + index("bgrs", (args[1] || "?")[1])];
.
#10:21
":record_connection(plyr) update plyr's connection information";
"to reflect impending login.";
if (!caller_perms().wizard)
return E_PERM;
else
plyr = args[1];
plyr.previous_connection = {plyr.last_connect_time, $string_utils:connection_hostname(plyr.last_connect_place)};
plyr.last_connect_time = time();
plyr.last_connect_place = cn = connection_name(player);
plyr.first_connect_time = min(time(), plyr.first_connect_time);
chost = $string_utils:connection_hostname(cn);
acp = setremove(plyr.all_connect_places, chost);
plyr.all_connect_places = {chost, @acp[1..min(length(acp), 15)]};
if (parent(plyr) != $guest)
$site_db:add(plyr, chost);
endif
endif
.
#10:22
if (caller != #0)
return E_PERM;
else
notify(player, "Please FIRST connect as a Guest; look around for a while before deciding whether or not you want a character.");
this:check_player_db();
this:check_for_shutdown();
return 0;
endif
.
#10:23
":spooflisted(hostname) => is hostname on the .spooflist";
"The return value, if true, is actually a domain (or site) name.";
sitelist = this.(this:listname(verb));
if (!caller_perms().wizard)
return E_PERM;
elseif (((hostname = args[1]) in sitelist[1]) || (hostname in sitelist[2]))
return hostname;
elseif ($site_db:domain_literal(hostname))
for lit in (sitelist[1])
if ((index(hostname, lit) == 1) && ((hostname + ".")[length(lit) + 1] == "."))
return lit;
endif
endfor
else
for dom in (sitelist[2])
if (index(dom, "*"))
"...we have a wildcard; let :match_string deal with it...";
if ($string_utils:match_string(hostname, dom))
return dom;
endif
else
"...tail of hostname ...";
if ((r = rindex(hostname, dom)) && ((("." + hostname)[r] == ".") && (((r - 1) + length(dom)) == length(hostname))))
return dom;
endif
endif
endfor
endif
return 0;
.
#10:24
ret = {};
for arg in (args)
ret = {@ret, @$string_utils:explode($jaddress:unescape_for_url(arg))};
endfor
return ret;
.
#10:25
if (!(caller in {#0, this}))
return E_PERM;
"...caller isn't :do_login_command or another $login verb...";
elseif (args)
notify(player, tostr("Usage:  ", verb));
else
return this:connect("guest");
endif
.
#10:26
"Was this a Guest from this site @booted recently enough not to allow access?";
"This should probably handle sub-domains and all that, and will eventually.  Right now, it just does things on a single-site basis.";
if ((caller != this) && (!caller_perms().wizard))
$error:raise(E_PERM);
endif
where = args[1];
die = 0;
for item in (this.bootlist)
if (time() > item[2])
this.bootlist = setremove(this.bootlist, item);
elseif (item[1] == where)
die = 1;
"We could return here, but then it wouldn't finish cleaning things out.";
elseif ((i = rindex(where, "." + item[1])) && ((i + length(item[1])) == length(where)))
die = 1;
endif
endfor
return die;
.
#10:27
":bootlist(site, time) -> ban all guests from site until time";
"For $guest:@boot!";
if (!caller_perms().wizard)
return E_PERM;
endif
this.bootlist = {@this.bootlist, args};
.
#10:28
":is_newted(person)";
"Is this person newted?  Handles newt-until-time as well as NEWT FOREVER.";
"Format of .newted is:";
"list of {who, ?until, ?special message}";
"";
"";
if ((caller != this) && (!caller_perms().wizard))
$error:raise(E_PERM);
endif
"we take this opportunity to clear out this.newted";
for i in (this.newted)
if (!is_player(i[1]))
this.newted = setremove(this.newted, i);
endif
endfor
if (a = $list_utils:assoc(args[1], this.newted))
{who, ?until = $maxint, ?message = this:newt_registration_string()} = a;
if (time() < until)
"a bit redundant there, but want to make sure message isn't null (and thereby false)";
return message || this:newt_registration_string();
else
this.newted = setremove(this.newted, a);
return 0;
endif
else
return 0;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {836275773, \"Ken\", #75, \"JHM\"}";
.
#10:29
"newt a player";
"calling format is:";
":newt_player(who, ?until, ?special message)";
"where until is time when person becomes unnewted (default: $maxint)";
"and special message is the message sent when they try to connect.";
"the default is the current (at the time of connect) :newt_registration_string";
"check args validity";
{who, ?until = $maxint, ?message = "test"} = args;
if (((!valid(who)) || (!is_player(who))) || $object_utils:isa(who, $guest))
raise(E_INVARG);
elseif (typeof(until) != NUM)
raise(E_INVARG);
elseif ((typeof(message) != STR) || (!message))
raise(E_INVARG);
endif
if (caller_perms().wizard)
this.newted = {@this.newted, args};
if (args[1] in connected_players())
boot_player(args[1]);
"Bye";
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {836275474, \"Ken\", #75, \"JHM\"}";
.
#10:30
if (caller_perms().wizard)
if (i = $list_utils:iassoc(args[1], this.newted))
this.newted = listdelete(this.newted, i);
endif
endif
.
#10:31
you_lose_msg = "Either that player does not exist, or has a different password.";
if (!(caller in {#0, this}))
return E_PERM;
"...caller isn't :do_login_command...";
elseif (!(length(args) in {1, 2}))
notify(player, tostr("Usage:  ", verb, " <existing-player-name> <password>"));
elseif (!valid(candidate = this:_match_player(name = strsub(args[1], " ", "_"))))
notify(player, you_lose_msg);
"...unknown player...";
elseif ((typeof(candidate.password) == STR) && ((length(candidate.password) < 2) || (crypt({@args, ""}[2], candidate.password[1..2]) != candidate.password)))
notify(player, you_lose_msg);
"...bad password...";
server_log(tostr("FAILED CONNECT: ", args[1], " (", candidate, ") on ", connection_name(player), ($string_utils:connection_hostname(connection_name(player)) in candidate.all_connect_places) ? "" | "******"));
elseif (!candidate.programmer)
notify(player, "Sorry, only programmers may create test characters.");
elseif (this:is_newted(candidate))
notify(player, "");
notify(player, this:newt_message_for(candidate));
notify(player, "");
elseif (typeof(new = $player_db:create_player(name = $player_db:gensym_name("Tester"), {name}, $tester, $nothing, crypt(password = $wiz_utils:random_password()))) != OBJ)
notify(player, tostr(new));
else
notify(player, tostr("Okay, logging you in as ", name, ", with password `", password, "'..."));
if ($player_class.programmer)
$wiz_utils:set_programmer(new, 1);
endif
new.public_identity = candidate;
this:record_connection(new);
this.testers = setadd(this.testers, new);
return new;
endif
return 0;
"Metadata 202106";
"Last-Modify: {836276178, \"Ken\", #75, \"JHM\"}";
.
#10:32
idle = this:avg_idle();
idle ? notify(player, tostr("The average idle time is ", idle, "; there are ", length($code_utils:connected_players()), " connected.")) | notify(player, "There is nobody connected.");
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863643908, \"Erik\", #74, \"JHM\"}";
.
#10:33
idle = 0;
plyrs = $code_utils:connected_players();
if (l = length(plyrs))
for x in (plyrs)
idle = idle + idle_seconds(x);
endfor
return $time_utils:english_time(idle / length(connected_players()));
else
return 0;
endif
.
#10:34
if (caller != #0)
return E_PERM;
else
this:maybe_print_idle();
return 0;
endif
.
#10:35
if ($perm_utils:controls(caller_perms(), this) || (caller == this))
set_task_perms(caller_perms());
for line in ((typeof(lines = args[1]) != LIST) ? {lines} | lines)
this:notify(line);
endfor
else
return E_PERM;
endif
"Copied from generic player (#6):notify_lines by Erik (#74) Thu Nov 10 17:25:14 1994 EST";
.
#10:36
{who} = args;
return who in this.testers;
"Metadata 202106";
"Last-Modify: {833591497, \"Ken\", #75, \"JHM\"}";
.
#10:37
{who} = args;
if (caller != #0)
return;
elseif (!this:is_tester(who))
return;
else
for i in [1..length(verbs(who))]
delete_verb(who, i);
endfor
for i in (properties(who))
delete_property(who, i);
endfor
if (parent(who) != $tester)
chparent(who, $tester);
endif
this.testers = setremove(this.testers, who);
endif
return;
"Metadata 202106";
"Last-Modify: {833591766, \"Ken\", #75, \"JHM\"}";
.
#11:0
"{last_huh}  @<msg_name> <object> is [<text>]";
"If <text> is given calls <object>:set_message(<msg_name>,<text>),";
"otherwise prints the value of the specified message property";
set_task_perms(caller_perms());
nargs = length(args);
pos = "is" in args;
if (pos == 1)
player:notify(("Usage:  " + verb) + " <object> is <message>");
return;
endif
dobjstr = $string_utils:from_list(args[1..pos - 1], " ");
message = $string_utils:from_list(args[pos + 1..nargs], " ");
msg_name = verb[2..length(verb)];
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
"... oh well ...";
elseif (pos == nargs)
try
get = dobj:get_message(msg_name);
if (!get)
player:notify("Message is not set.");
else
return player:notify((((("@" + msg_name) + " ") + dobjstr) + " is ") + get);
endif
except (E_PROPNF)
player:notify((($string_utils:dnamec_and_number(dobj) + " has no \"") + msg_name) + "\" message.");
except (E_PERM)
player:notify(tostr(E_PERM));
endtry
else
"clear the way for a 1.8.0ish contract, but don't break what we have now";
set = `dobj:set_message(msg_name, message, player.pronoun_sub_style) ! E_PROPNF, E_PERM';
if (set)
if (typeof(set) == STR)
player:notify(set);
else
player:notify(((("You set the \"" + msg_name) + "\" message of ") + $string_utils:dname_and_number(dobj)) + ".");
endif
elseif (set == E_PROPNF)
player:notify((($string_utils:dnamec_and_number(dobj) + " has no \"") + msg_name) + "\" message to set.");
elseif (typeof(set) == ERR)
player:notify(tostr(set));
else
player:notify(((("You clear the \"" + msg_name) + "\" message of ") + $string_utils:dname_and_number(dobj)) + ".");
endif
endif
return;
"Metadata 202106";
"Last-Modify: {831882879, \"splat\", #3024, \"JHM\"}";
.
#11:1
"{last_huh}  give any to any";
"a give \"verb\" that works for non-$things.";
set_task_perms(caller_perms());
if (dobj == $nothing)
player:tell("What do you want to give?");
elseif (iobj == $nothing)
player:tell("To whom/what do you want to give it?");
elseif ($command_utils:object_match_failed(dobj, dobjstr) || $command_utils:object_match_failed(iobj, iobjstr))
"...lose...";
elseif (dobj.location != player)
player:tell("You don't have that!");
elseif (iobj.location != player.location)
player:tell("You see no ", iobj:name(), " here.");
else
dobj:moveto(iobj);
if (dobj.location == iobj)
player:tell("You give ", dobj:dname(), " to ", iobj:dname(), ".");
iobj:tell(player:dnamec(), " ", player:verb_sub("gives"), " you ", dobj:iname(), ".");
player.location:announce_all_but({player, iobj}, player:dnamec(), " ", player:verb_sub("hands"), " ", iobj:dname(), " ", dobj:iname(), ".");
else
player:tell("Either that doesn't want to be given away or ", iobj:dname(), " ", iobj:verb_sub("doesn't"), " want it.");
endif
endif
.
#11:2
"{last_huh}  get/take any";
"a take \"verb\" that works for non-$things.";
set_task_perms(caller_perms());
if (!valid(dobj))
dobj = player.location:match_object(dobjstr = argstr);
endif
if (prepstr)
if (!valid(dobj))
if (!$command_utils:object_match_failed(iobj, iobjstr))
player:tell("You can't ", verb, " anything ", prepstr, " ", iobj:dname(), ".");
endif
return;
endif
endif
if (dobj == $nothing)
player:tell($string_utils:capitalize(verb), " what?");
elseif ($command_utils:object_match_failed(dobj, dobjstr))
"...lose...";
elseif (dobj.location == player)
player:tell("You already have that!");
elseif (dobj.location != player.location)
player:tell("You don't see that here.");
else
dobj:moveto(player);
if (dobj.location == player)
$you:say_action($pronoun_sub.("two-letter"):parse(("%nD %n:(" + verb) + "s) %dd."));
else
player:tell("You can't pick that up.");
endif
endif
.
#11:3
"{last_huh}  drop/throw any";
"a drop \"verb\" that works for non-$things.";
set_task_perms(caller_perms());
if ((!valid(dobj)) && prepstr)
dobj = player.location:match_object(dobjstr = argstr);
endif
if (dobj == $nothing)
player:tell($string_utils:capitalize(verb), " what?");
elseif ($command_utils:object_match_failed(dobj, dobjstr))
"...lose...";
elseif (dobj.location != player)
player:tell("You don't have that.");
elseif (!player.location:accept(dobj))
player:tell("You can't drop that here.");
else
dobj:moveto(player.location);
if (dobj.location == player.location)
player:tell("You ", (verb[1] == "d") ? "drop " | "throw away ", dobj:dname(), ".");
player.location:announce(player:dnamec(), " ", (verb[1] == "d") ? player:verb_sub("drops") | (player:verb_sub("throws") + " away"), " ", dobj:iname(), ".");
else
player:tell_lines("You can't seem to drop that here.");
endif
endif
.
#11:4
if (strcmp(verb, "QUIT"))
"Not a TinyMU* refugee.";
player:notify("I don't understand that.");
else
"TinyMU* refugee tried to use all-caps bogus server command.  Why don't we just let it work?  Idunno, tradition, maybe.";
player:notify("Use `@quit' to logout.");
endif
.
#12:0
":enter(who,islogin,time,site)";
"adds an entry to the connection log for a given guest (caller).";
if ($object_utils:isa(caller, $guest))
$guest_log.connections = {{caller, @args}, @$guest_log.connections[1..min(99, length($guest_log.connections))]};
else
return E_PERM;
endif
.
#12:1
set_task_perms(caller_perms());
howmany = min({@args, 0}[1] || $maxint, length($guest_log.connections));
if (!caller_perms().wizard)
player:notify("Sorry.");
else
current = {};
listing = {};
last = 0;
for c in ($guest_log.connections[1..howmany])
if (c[2])
"...login...";
if (a = $list_utils:assoc(c[1], current))
listing[a[2]][3] = c[3];
current = setremove(current, a);
else
listing = {@listing, {c[1], c[4], c[3], -idle_seconds(c[1])}};
last = last + 1;
endif
else
"...logout...";
listing = {@listing, {c[1], c[4], 0, c[3]}};
last = last + 1;
if (i = $list_utils:iassoc(c[1], current))
current[i][2] = last;
else
current = {@current, {c[1], last}};
endif
endif
endfor
su = $string_utils;
player:notify(su:left(su:left(su:left("Guest", 20) + "Connected", 36) + "Idle/Disconn.", 52) + "From");
player:notify(su:left(su:left(su:left("-----", 20) + "---------", 36) + "-------------", 52) + "----");
for l in (listing)
on = l[3] ? (ct = ctime(l[3]))[1..3] + ct[9..19] | "earlier";
off = (l[4] > 0) ? (ct = ctime(l[4]))[1..3] + ct[9..19] | ("  " + $string_utils:from_seconds(-l[4]));
player:notify(su:left(su:left(su:right(tostr(strsub(l[1]:name(), "uest", "."), " (", l[1], ")  "), 20) + on, 36) + off, 52) + l[2]);
endfor
endif
.
#12:2
if ($__core_init_phase)
pass(@args);
this.connections = {};
endif
.
#12:3
return {@pass(@args), $object_utils, $guest, $list_utils, $string_utils};
.
#13:0
":length(tree) => number of leaves in tree.";
{tree} = args;
return tree ? tree[2] | 0;
"Metadata 202106";
"Last-Modify: {882334161, \"Rog\", #145, \"JHM\"}";
.
#13:1
":find_nth(tree,n) => nth leaf of tree.  Raises E_RANGE if n is not in [1..tree[2]] or tree is empty. ";
{tree, n} = args;
while height (({_, subtrees} = caller:_get(tree[1]))[1])
for tree in (subtrees)
if (n > tree[2])
n = n - tree[2];
else
continue height;
endif
endfor
raise(E_RANGE);
endwhile
return subtrees[n];
"Metadata 202106";
"Last-Modify: {882333925, \"Rog\", #145, \"JHM\"}";
.
#13:2
":_find_ord(tree,n,comp) ";
" => index of rightmost leaf for which :(comp)(n,:_ord(leaf)) is false.";
"returns 0 if true for all leaves.";
return args[1] ? this:_find_ord(caller, @args) | 0;
.
#13:3
":set_nth(tree,n,value) => tree";
"modifies tree so that nth leaf == value";
{tree, n, value} = args;
if ((n < 1) || ((!tree) || (tree[2] < n)))
raise(E_RANGE);
else
value_ord = caller:_ord(value);
if (n == 1)
tree[3] = value_ord;
endif
node = tree[1];
k = n - 1;
while height (({_, subtrees} = caller:_get(node))[1])
{i, k} = this:_listfind_nth(subtrees, k);
if (k == 0)
subtrees[i][3] = value_ord;
caller:_put(node, height, subtrees);
endif
node = subtrees[i][1];
continue height;
endwhile
subtrees[k + 1] = value;
caller:_put(node, 0, subtrees);
return tree;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {882335659, \"Rog\", #145, \"JHM\"}";
.
#13:4
":kill(tree[,leafverb]) deletes tree and _kills all of the nodes that it uses.";
"if leafverb is given, caller:leafverb is called on all leaves in tree.";
if (tree = args[1])
lverb = {@args, ""}[2];
this:_skill(caller, (typeof(tree) == LIST) ? tree[1] | tree, lverb);
endif
"... otherwise nothing to do...";
.
#13:5
":insert_after(tree,subtree,n)";
":insert_before(tree,subtree,n)";
"  inserts subtree after (before) the nth leaf of tree,";
"  returning the resulting tree.";
subtree = args[2];
if (tree = args[1])
if (subtree)
where = args[3] - (verb == "insert_before");
if (where <= 0)
return this:_merge(caller, subtree, tree);
elseif (where >= tree[2])
return this:_merge(caller, tree, subtree);
else
s = this:_split(caller, caller:_get(tree[1])[1], where, tree);
return this:_merge(caller, this:_merge(caller, s[1], subtree), s[2]);
endif
else
return tree;
endif
else
return subtree;
endif
.
#13:6
":extract_range(tree,first,last) => {newtree,extraction}";
return this:_extract(caller, @args);
.
#13:7
":delete_range(tree,first,last[,leafkill]) => newtree";
extract = this:_extract(caller, @args);
if (die = extract[2])
this:_skill(caller, die[1], {@args, ""}[4]);
endif
return extract[1];
.
#13:8
":keep_range(tree,first,last[,leafkill]) => range";
extract = this:_extract(caller, @args);
if (die = extract[1])
this:_skill(caller, die[1], {@args, ""}[4]);
endif
return extract[2];
.
#13:9
":insert_last(tree,insert) => newtree";
"insert a new leaf to be inserted at the righthand end of the tree";
tree = args[1];
insert = args[2];
if (!tree)
return {caller:_make(0, {insert}), 1, caller:_ord(insert)};
endif
hgt = caller:_get(tree[1]);
rspine = {{tree, plen = length(kids = hgt[2])}};
for i in [1..hgt[1]]
parent = kids[plen];
kids = caller:_get(parent[1])[2];
plen = length(kids);
rspine = {{parent, plen}, @rspine};
endfor
iord = caller:_ord(insert);
for h in [1..length(rspine)]
"... tree is the plen'th (rightmost) child of parent...";
if (rspine[h][2] < this.maxfanout)
parent = rspine[h][1];
hgp = caller:_get(parent[1]);
caller:_put(parent[1], @listset(hgp, {@hgp[2], insert}, 2));
for p in (rspine[h + 1..length(rspine)])
rkid = listset(parent, parent[2] + 1, 2);
parent = p[1];
hgp = caller:_get(parent[1]);
caller:_put(parent[1], @listset(hgp, listset(hgp[2], rkid, p[2]), 2));
endfor
return listset(tree, tree[2] + 1, 2);
endif
insert = {caller:_make(h - 1, {insert}), 1, iord};
endfor
return {caller:_make(length(rspine), {tree, insert}), tree[2] + 1, tree[3]};
.
#13:10
":start(tree,first,last) => {list of leaf nodes, @handle}";
"handle is of the form {{node,next,size}...}";
if (tree = args[1])
before = max(0, args[2] - 1);
howmany = min(args[3], tree[2]) - before;
if (howmany <= 0)
return {};
else
spine = {};
for h in [1..caller:_get(tree[1])[1]]
ik = this:_listfind_nth(kids = caller:_get(tree[1])[2], before);
newh = kids[ik[1]][2] - ik[2];
if (newh < howmany)
spine = {{tree[1], ik[1] + 1, howmany - newh}, @spine};
howmany = newh;
endif
tree = kids[ik[1]];
before = ik[2];
endfor
return {caller:_get(tree[1])[2][before + 1..before + howmany], @spine};
endif
else
return {};
endif
.
#13:11
":next(@handle) => {list of more leaf nodes, @newhandle}";
if (args)
spine = listdelete(args, 1);
node = args[1][1];
n = args[1][2];
size = args[1][3];
for h in [1..caller:_get(node)[1]]
nnode = caller:_get(node)[2][n];
if (size > nnode[2])
spine = {{node, n + 1, size - nnode[2]}, @spine};
size = nnode[2];
endif
n = 1;
node = nnode[1];
endfor
return {caller:_get(node)[2][n..size], @spine};
else
return {};
endif
.
#13:12
":_call(home,verb,@vargs) calls home:verb(@vargs) with $no_one's perms";
set_task_perms($no_one);
if (caller != this)
raise(E_PERM);
endif
{home, vb, @vargs} = args;
return home:(vb)(@vargs);
"Metadata 202106";
"Last-Modify: {882332823, \"Rog\", #145, \"JHM\"}";
.
#13:13
":_find_ord(home,tree,n,less_than) ";
" => index of rightmost leaf for which :(less_than)(n,:_ord(leaf)) is false.";
"returns 0 if true for all leaves.";
if (caller != this)
raise(E_PERM);
endif
{home, tree, n, less_than} = args;
if ((p = home:_get(tree[1]))[1])
sz = tree[2];
for i in [-length(p[2])..-1]
k = p[2][-i];
sz = sz - k[2];
if (!this:_call(home, less_than, n, k[3]))
return sz + this:_find_ord(home, k, n, less_than);
endif
endfor
return 0;
else
for i in [1..r = length(p[2])]
if (this:_call(home, less_than, n, home:_ord(p[2][i])))
return i - 1;
endif
endfor
return r;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {882333222, \"Rog\", #145, \"JHM\"}";
.
#13:14
":_skill(home,node,leafverb)";
"home:_kill's node and all descendants, home:(leafverb)'s all leaves";
{home, node, kill_leaf} = args;
if (caller != this)
raise(E_PERM);
endif
try
{height, subtrees} = home:_get(node);
except (E_PROPNF)
return;
endtry
if (height)
for kid in (subtrees)
this:_skill(home, kid[1], kill_leaf);
endfor
elseif (kill_leaf)
for kid in (subtrees)
this:_call(home, kill_leaf, kid);
endfor
endif
home:_kill(node);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {882332823, \"Rog\", #145, \"JHM\"}";
.
#13:15
":_extract(home,tree,first,last) => {newtree,extraction}";
if (caller != this)
return E_PERM;
endif
home = args[1];
if (!(tree = args[2]))
return {{}, {}};
endif
before = max(0, args[3] - 1);
end = min(tree[2], args[4]);
if ((end <= 0) || (before >= end))
return {tree, {}};
endif
height = home:_get(tree[1])[1];
if (end < tree[2])
r = this:_split(home, height, end, tree);
if (before)
l = this:_split(home, height, before, r[1]);
extract = l[2];
newtree = this:_merge(home, l[1], r[2]);
else
extract = r[1];
newtree = r[2];
endif
elseif (before)
l = this:_split(home, height, before, tree);
extract = l[2];
newtree = l[1];
else
return {{}, tree};
endif
return {this:_scrunch(home, newtree), this:_scrunch(home, extract)};
"Metadata 202106";
"Last-Modify: {882332823, \"Rog\", #145, \"JHM\"}";
.
#13:16
"_merge(home,ltree,rtree) => newtree";
"assumes ltree and rtree to be nonempty.";
if (caller != this)
return E_PERM;
endif
home = args[1];
lnode = args[2];
rnode = args[3];
lh = home:_get(lnode[1])[1];
rh = home:_get(rnode[1])[1];
if (lh > rh)
return this:_rmerge(home, lnode, rnode);
endif
for h in [lh + 1..rh]
lnode[1] = home:_make(h, {lnode});
endfor
m = this:_smerge(home, rh, lnode, rnode);
return (length(m) <= 1) ? m[1] | {home:_make(rh + 1, m), m[1][2] + m[2][2], m[1][3]};
"Metadata 202106";
"Last-Modify: {882332823, \"Rog\", #145, \"JHM\"}";
.
#13:17
"_smerge(home, height, ltree, rtree) =>{ltree[,rtree]}";
"assumes ltree and rtree are at the given height.";
"merges the trees if the combined number of children is <= maxfanout";
"otherwise returns two trees where ltree is guaranteed minfanout children and rtree is guaranteed the minimum of minfanout and however many children it started with.";
if (caller != this)
return E_PERM;
endif
home = args[1];
height = args[2];
ltree = args[3];
rtree = args[4];
llen = length(lkids = home:_get(ltree[1])[2]);
rlen = length(rkids = home:_get(rtree[1])[2]);
if (height)
m = this:_smerge(home, height - 1, lkids[llen], rkids[1]);
mlen = length(mkids = {@listdelete(lkids, llen), @m, @listdelete(rkids, 1)});
if (mlen <= this.maxfanout)
home:_put(ltree[1], height, mkids);
home:_kill(rtree[1]);
ltree[2] = ltree[2] + rtree[2];
return {ltree};
else
S = max(llen - 1, (mlen + 1) / 2);
home:_put(ltree[1], height, mkids[1..S]);
home:_put(rtree[1], height, mkids[S + 1..length(mkids)]);
xfer = -lkids[llen][2];
for k in (mkids[llen..S])
xfer = xfer + k[2];
endfor
ltree[2] = ltree[2] + xfer;
rtree[2] = rtree[2] - xfer;
rtree[3] = mkids[S + 1][3];
return {ltree, rtree};
endif
elseif ((llen * 2) >= this.maxfanout)
return {ltree, rtree};
elseif (this.maxfanout < (llen + rlen))
T = ((rlen - llen) + 1) / 2;
home:_put(ltree[1], 0, {@lkids, @rkids[1..T]});
home:_put(rtree[1], 0, rkids[T + 1..rlen]);
ltree[2] = ltree[2] + T;
rtree[2] = rtree[2] - T;
rtree[3] = home:_ord(rkids[T + 1]);
return {ltree, rtree};
else
home:_put(ltree[1], 0, {@lkids, @rkids});
home:_kill(rtree[1]);
ltree[2] = ltree[2] + rtree[2];
return {ltree};
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {882332823, \"Rog\", #145, \"JHM\"}";
.
#13:18
"_split(home, height,lmax,ltree[,@rtrees]}) => {ltree,[mtree,]@rtrees}";
"ltree is split after the lmax'th leaf, the righthand portion grafted onto the leftmost of the rtrees, if possible.  Otherwise we create a new tree mtree, stealing from rtrees[1] if necessary.";
"Assumes 1<=lmax<ltree[2]";
if (caller != this)
return E_PERM;
endif
home = args[1];
height = args[2];
lmax = args[3];
ltree = args[4];
rtrees = args[5..length(args)];
llen = length(lkids = home:_get(ltree[1])[2]);
rlen = length(rkids = rtrees ? home:_get(rtrees[1][1])[2] | {});
if (height)
ik = this:_listfind_nth(lkids, lmax);
if (ik[2])
llast = ik[1];
m = this:_split(home, height - 1, ik[2], lkids[llast], @lkids[llast + 1..llen], @rkids);
lkids[llast] = m[1];
mkids = listdelete(m, 1);
else
llast = ik[1] - 1;
mkids = {@lkids[ik[1]..llen], @rkids};
endif
home:_put(ltree[1], height, lkids[1..llast]);
mlen = length(mkids);
if ((((mlen - rlen) * 2) >= this.maxfanout) || (!rtrees))
"...residue left over from splitting ltree can stand by itself...";
return {listset(ltree, lmax, 2), {home:_make(height, mkids[1..mlen - rlen]), ltree[2] - lmax, mkids[1][3]}, @rtrees};
elseif (mlen <= this.maxfanout)
"...residue left over from splitting ltree fits in rtrees[1]...";
home:_put(rtrees[1][1], height, mkids);
rtrees[1][2] = (ltree[2] - lmax) + rtrees[1][2];
rtrees[1][3] = mkids[1][3];
return {listset(ltree, lmax, 2), @rtrees};
else
"...need to steal from rtrees[1]...";
if (llast < llen)
msize = ltree[2] - lmax;
R = (mlen - rlen) + 1;
else
msize = 0;
R = 1;
endif
for k in (mkids[R..mlen / 2])
msize = msize + k[2];
endfor
home:_put(rtrees[1][1], height, mkids[(mlen / 2) + 1..mlen]);
rtrees[1][2] = (rtrees[1][2] + ltree[2]) - (lmax + msize);
rtrees[1][3] = mkids[(mlen / 2) + 1][3];
return {listset(ltree, lmax, 2), {home:_make(height, mkids[1..mlen / 2]), msize, mkids[1][3]}, @rtrees};
endif
else
home:_put(ltree[1], 0, lkids[1..lmax]);
if ((((llen - lmax) * 2) >= this.maxfanout) || (!rtrees))
"...residue left over from splitting ltree can stand by itself...";
return {listset(ltree, lmax, 2), {home:_make(0, lkids[lmax + 1..llen]), llen - lmax, home:_ord(lkids[lmax + 1])}, @rtrees};
elseif ((mlen = (rlen + llen) - lmax) <= this.maxfanout)
"...residue left over from splitting ltree fits in rtrees[1]...";
home:_put(rtrees[1][1], 0, {@lkids[lmax + 1..llen], @rkids});
rtrees[1][2] = mlen;
rtrees[1][3] = home:_ord(lkids[lmax + 1]);
return {listset(ltree, lmax, 2), @rtrees};
else
"...need to steal from rtrees[1]...";
home:_put(rtrees[1][1], 0, rkids[(R = ((rlen - llen) + lmax) / 2) + 1..rlen]);
rtrees[1][2] = (mlen + 1) / 2;
rtrees[1][3] = home:_ord(rkids[R + 1]);
return {listset(ltree, lmax, 2), {home:_make(0, {@lkids[lmax + 1..llen], @rkids[1..R]}), mlen / 2, home:_ord(lkids[lmax + 1])}, @rtrees};
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {882332823, \"Rog\", #145, \"JHM\"}";
.
#13:19
":_rmerge(home, tree, insertree) => newtree ";
"(newtree is tree with insertree appended to the right)";
"insertree is assumed to be of height < tree";
if (caller != this)
return E_PERM;
endif
home = args[1];
tree = args[2];
insert = args[3];
if (!tree)
return insert;
elseif (!insert)
return tree;
endif
iheight = home:_get(insert[1])[1];
rspine = {};
for i in [iheight + 1..home:_get(tree[1])[1]]
kids = home:_get(tree[1])[2];
tlen = length(kids);
rspine = {{tree, tlen}, @rspine};
tree = kids[tlen];
endfor
isize = insert[2];
m = this:_smerge(home, iheight, tree, insert);
for h in [1..length(rspine)]
plen = rspine[h][2];
parent = rspine[h][1];
hgp = home:_get(parent[1]);
if (((length(m) - 1) + plen) > this.maxfanout)
home:_put(parent[1], @listset(hgp, listset(hgp[2], m[1], plen), 2));
parent[2] = (parent[2] + isize) - m[2][2];
m = {parent, listset(m[2], home:_make(h + iheight, {m[2]}), 1)};
else
home:_put(parent[1], @listset(hgp, {@hgp[2][1..plen - 1], @m}, 2));
for p in (rspine[h + 1..length(rspine)])
parent[2] = parent[2] + isize;
tree = parent;
parent = p[1];
hgp = home:_get(parent[1]);
home:_put(parent[1], @listset(hgp, listset(hgp[2], tree, p[2]), 2));
endfor
return listset(parent, parent[2] + isize, 2);
endif
endfor
return {home:_make((length(rspine) + iheight) + 1, m), m[1][2] + m[2][2], m[1][3]};
"Metadata 202106";
"Last-Modify: {882332823, \"Rog\", #145, \"JHM\"}";
.
#13:20
":_scrunch(home,tree) => newtree";
"decapitates single-child nodes from the top of the tree, returns new root.";
if (caller != this)
return E_PERM;
endif
if (tree = args[2])
home = args[1];
while ((n = home:_get(tree[1]))[1] && (length(n[2]) == 1))
home:_kill(tree[1]);
tree = n[2][1];
endwhile
endif
return tree;
"Metadata 202106";
"Last-Modify: {882332823, \"Rog\", #145, \"JHM\"}";
.
#13:21
"_listfind_nth(nodelist,key) => {i,k} where i is the smallest i such that the sum of the first i elements of intlist is > key, and k==key - sum(first i-1 elements).";
"1 <= i <= length(intlist)+1";
lst = args[1];
key = args[2];
for i in [1..length(lst)]
key = key - lst[i][2];
if (0 > key)
return {i, key + lst[i][2]};
endif
endfor
return {length(lst) + 1, key};
"Metadata 202106";
"Last-Modify: {882332823, \"Rog\", #145, \"JHM\"}";
.
#13:22
if (caller != this)
return E_PERM;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {882332823, \"Rog\", #145, \"JHM\"}";
.
#13:23
return $perm_utils:controls(caller_perms(), this) ? this:(args[1])(@listdelete(args, 1)) | E_PERM;
"Metadata 202106";
"Last-Modify: {882332823, \"Rog\", #145, \"JHM\"}";
.
#14:0
gp = this._genprop;
ngp = "";
for i in [1..length(gp)]
if (gp[i] != "z")
ngp = (ngp + "bcdefghijklmnopqrstuvwxyz"[index("abcdefghijklmnopqrstuvwxy", gp[i])]) + gp[i + 1..length(gp)];
return " " + (this._genprop = ngp);
endif
ngp = ngp + "a";
endfor
return " " + (this._genprop = ngp + "a");
.
#14:1
":_make(...) => new node with value {...}";
if (!(caller in {this._mgr, this}))
return E_PERM;
endif
prop = this:_genprop();
add_property(this, prop, args, {this.mowner, ""});
return prop;
.
#14:2
":_kill(node) destroys the given node.";
if (!(caller in {this, this._mgr}))
raise(E_PERM);
endif
`delete_property(this, args[1]) ! E_PROPNF => 0';
.
#14:3
return (caller == this._mgr) ? this.(args[1]) | E_PERM;
.
#14:4
return (caller == this._mgr) ? this.(args[1]) = listdelete(args, 1) | E_PERM;
.
#14:5
return args[1][2..3];
.
#14:6
":_makemsg(ord,msg) => leafnode for msg";
"msg = $mail_agent:__convert_new(@args[2])";
msg = args[2];
if (caller != this)
return E_PERM;
elseif (h = "" in msg)
return {this:_make(@msg[h + 1..length(msg)]), args[1], @msg[1..h - 1]};
else
return {0, args[1], @msg};
endif
.
#14:7
if (caller != this._mgr)
return E_PERM;
elseif (node = args[1][1])
this:_kill(node);
endif
.
#14:8
return args[2];
.
#14:9
return args[3];
.
#14:10
return args[3..length(args)];
.
#14:11
return {@args[3..length(args)], @args[1] ? {"", @this.(args[1])} | {}};
.
#14:12
return args[1] < args[2][1];
.
#14:13
return args[1] < args[2][2];
.
#14:14
if (!this:is_writable_by(caller_perms()))
return E_PERM;
else
new = this:new_message_num();
msgtree = this.messages;
for m in (args)
msgtree = this._mgr:insert_last(msgtree, this:_makemsg(new, m[2]));
new = new + 1;
if ($command_utils:running_out_of_time())
this.messages = msgtree;
player:tell("... ", new);
suspend(0);
msgtree = this.messages;
new = this:new_message_num();
endif
endfor
this.messages = msgtree;
this.last_used_time = time();
return 1;
endif
.
#14:15
if (!this:is_writable_by(caller_perms()))
return E_PERM;
else
this.messages = this._mgr:insert_last(this.messages, msg = this:_makemsg(new = this:new_message_num(), args[1]));
this.last_msg_date = this:_message_date(@msg);
this.last_used_time = time();
return new;
endif
.
#14:16
if (!this:ok(caller, caller_perms()))
return E_PERM;
elseif (typeof(seq = args[1]) != LIST)
x = this._mgr:find_nth(this.messages, seq);
return {this:_message_num(@x), this:_message_text(@x)};
else
msgs = {};
while (seq)
handle = this._mgr:start(this.messages, seq[1], seq[2] - 1);
while (handle)
for x in (handle[1])
msgs = {@msgs, {this:_message_num(@x), this:_message_text(@x)}};
endfor
handle = this._mgr:next(@listdelete(handle, 1));
$command_utils:suspend_if_needed(0);
endwhile
seq = seq[3..length(seq)];
endwhile
return msgs;
endif
.
#14:17
":display_seq_headers(msg_seq[,cur[,last_read_date]])";
"This is the default header display routine.";
"Prints a list of headers of messages on this to player.  msg_seq is the handle returned by this:parse_message_seq(...).  cur is the player's current message.  last_read_date is the date of the last of the already-read messages.";
if (!this:ok(caller, caller_perms()))
return E_PERM;
endif
getmsg = this.summary_uses_body ? "_message_text" | "_message_hdr";
seq = args[1];
cur = {@args, 0}[2];
last_old = {@args, $maxint, $maxint}[3];
width = abs(player.linelen || $player.linelen);
while (seq)
handle = this._mgr:start(this.messages, seq[1], seq[2] - 1);
while (handle)
for x in (handle[1])
$command_utils:suspend_if_needed(0);
line = tostr($string_utils:right(x[2], 4, (cur == x[2]) ? ">" | " "), (x[3] > last_old) ? ":+ " | ":  ", this:msg_summary_line(@this:(getmsg)(@x)));
player:tell(line[1..min(width, length(line))]);
endfor
handle = this._mgr:next(@listdelete(handle, 1));
endwhile
seq = seq[3..length(seq)];
endwhile
player:tell("----+");
.
#14:18
":display_seq_full(msg_seq[,preamble]) => {cur}";
"This is the default message display routine.";
"Prints the indicated messages on folder to player.  msg_seq is the handle returned by folder:parse_message_seq(...).  Returns the number of the final message in the sequence (to be the new current message number).";
if (!this:ok(caller, caller_perms()))
return E_PERM;
endif
seq = args[1];
preamble = {@args, ""}[2];
cur = date = 0;
while (seq)
handle = this._mgr:start(this.messages, seq[1], seq[2] - 1);
while (handle)
for x in (handle[1])
cur = this:_message_num(@x);
date = this:_message_date(@x);
player:display_message(preamble ? strsub(preamble, "%d", tostr(cur)) | {}, player:msg_text(@this:_message_text(@x)));
endfor
handle = this._mgr:next(@listdelete(handle, 1));
$command_utils:suspend_if_needed(0);
endwhile
seq = seq[3..length(seq)];
endwhile
return {cur, date};
.
#14:19
if (!this:ok(caller, caller_perms()))
return E_PERM;
endif
len = 0;
getmsg = this.summary_uses_body ? "_message_text" | "_message_hdr";
for s in (this.messages_going)
len = len + s[2][2];
handle = this._mgr:start(s[2], 1, s[2][2]);
while (handle)
for x in (handle[1])
player:tell($string_utils:right(this:_message_num(@x), 4), ":  ", this:msg_summary_line(@this:(getmsg)(@x)));
endfor
handle = this._mgr:next(@listdelete(handle, 1));
endwhile
endfor
if (len)
player:tell("----+");
endif
return len;
.
#14:20
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
msgtree = this.messages;
seq = {};
last = 0;
for s in (this.messages_going)
msgtree = this._mgr:insert_after(msgtree, s[2], last + s[1]);
seq = {@seq, (last + s[1]) + 1, (last = (last + s[1]) + s[2][2]) + 1};
endfor
this.messages = msgtree;
this.messages_going = {};
this:_fix_last_msg_date();
return seq;
.
#14:21
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
len = 0;
for s in (this.messages_going)
len = len + s[2][2];
this._mgr:kill(s[2], "_killmsg");
endfor
this.messages_going = {};
return len;
.
#14:22
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
msgtree = this.messages;
save = nums = {};
onext = 1;
rmmed = 0;
for i in [1..length(seq = args[1]) / 2]
if ($command_utils:suspend_if_needed(0))
player:tell("... rmm ", onext);
suspend(0);
endif
start = seq[(2 * i) - 1];
next = seq[2 * i];
x = this._mgr:extract_range(msgtree, start - rmmed, (next - 1) - rmmed);
msgtree = x[1];
zmsgs = x[2];
save = {@save, {start - onext, zmsgs}};
nums = {@nums, this:_message_num(@this._mgr:find_nth(zmsgs, 1)), this:_message_num(@this._mgr:find_nth(zmsgs, zmsgs[2])) + 1};
onext = next;
rmmed = (rmmed + next) - start;
endfor
tmg = this.messages_going;
this.messages_going = save;
fork (0)
for s in (tmg)
this._mgr:kill(s[2], "_killmsg");
endfor
endfork
this.messages = msgtree;
this:_fix_last_msg_date();
return $seq_utils:tostr(nums);
.
#14:23
":renumber([cur]) renumbers caller.messages, doing a suspend() if necessary.";
"  => {number of messages,new cur}.";
cur = {@args, 0}[1];
this:expunge_rmm();
"... blow away @rmm'ed messages since there's no way to tell what their new numbers should be...";
if (!(msgtree = this.messages))
return {0, 0};
endif
if (cur)
cur = this._mgr:find_ord(msgtree, cur - 1, "_lt_msgnum") + 1;
endif
while (1)
"...find first out-of-sequence message...";
n = 1;
subtree = msgtree;
if (msgtree[3][1] == 1)
while ((node = this.(subtree[1]))[1])
"...subtree[3][1]==n...";
kids = node[2];
n = n + subtree[2];
i = length(kids);
while ((n = n - kids[i][2]) != kids[i][3][1])
i = i - 1;
endwhile
subtree = kids[i];
endwhile
leaves = node[2];
n = ((firstn = n) + length(leaves)) - 1;
while (n != leaves[(n - firstn) + 1][2])
n = n - 1;
endwhile
n = n + 1;
endif
"... n == first out-of-sequence ...";
"...renumber as many messages as we have time for...";
while ((n <= msgtree[2]) && (!$command_utils:running_out_of_time()))
msg = this._mgr:find_nth(msgtree, n);
msgtree = this._mgr:set_nth(msgtree, n, listset(msg, n, 2));
n = n + 1;
endwhile
this.messages = msgtree;
if (n > msgtree[2])
return {n - 1, cur};
endif
player:tell("...(renumbering to ", n - 1, ")");
suspend(0);
"...start over... may have received new mail, rmm'ed stuff, etc...";
"...so who knows what's there now?...";
if (this.messages_going)
player:tell("Renumber aborted.");
return;
endif
msgtree = this.messages;
endwhile
.
#14:24
return this:ok(caller, caller_perms()) ? this.messages ? this.messages[2] | 0 | E_PERM;
.
#14:25
return this:ok(caller, caller_perms()) ? this._mgr:find_ord(this.messages, args[1], "_lt_msgnum") | E_PERM;
.
#14:26
return this:ok(caller, caller_perms()) ? this._mgr:find_ord(this.messages, args[1], "_lt_msgdate") | E_PERM;
.
#14:27
return this:ok(caller, caller_perms()) ? (i = this._mgr:find_ord(this.messages, args[1], "_lt_msgnum")) && ((this:_message_num(@this._mgr:find_nth(this.messages, i)) == args[1]) && i) | E_PERM;
.
#14:28
new = (msgtree = this.messages) ? this:_message_num(@this._mgr:find_nth(msgtree, msgtree[2])) + 1 | 1;
if (rmsgs = this.messages_going)
lbrm = rmsgs[length(rmsgs)][2];
return max(new, this:_message_num(@this._mgr:find_nth(lbrm, lbrm[2])) + 1);
else
return new;
endif
.
#14:29
":from_msg_seq(object or list)";
" => msg_seq of messages from any of these senders";
if (!this:ok(caller, caller_perms()))
return E_PERM;
elseif (!this.messages)
return {};
endif
if (typeof(plist = args[1]) != LIST)
plist = {plist};
endif
mask = {@args, {1, this.messages[2] + 1}}[2];
fseq = {};
for m in [1..length(mask) / 2]
handle = this._mgr:start(this.messages, i = mask[(2 * m) - 1], mask[2 * m] - 1);
while (handle)
for msg in (handle[1])
fromline = msg[4];
if (toobj(fromline[rindex(fromline, "(") + 1..rindex(fromline, ")") - 1]) in plist)
fseq = $seq_utils:add(fseq, i, i);
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
handle = this._mgr:next(@listdelete(handle, 1));
endwhile
endfor
return fseq || ("%f %<has> no messages from " + $string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%n (%#)", plist), "no one", " or "));
.
#14:30
":%from_msg_seq(string or list of strings)";
" => msg_seq of messages with one of these strings in the from line";
if (!this:ok(caller, caller_perms()))
return E_PERM;
elseif (!this.messages)
return {};
endif
if (typeof(nlist = args[1]) != LIST)
nlist = {nlist};
endif
fseq = {};
mask = {@args, {1, this.messages[2] + 1}}[2];
for m in [1..length(mask) / 2]
handle = this._mgr:start(this.messages, i = mask[(2 * m) - 1], mask[2 * m] - 1);
while (handle)
for msg in (handle[1])
fromline = " " + msg[4];
for n in (nlist)
if (index(fromline, n))
fseq = $seq_utils:add(fseq, i, i);
endif
endfor
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
handle = this._mgr:next(@listdelete(handle, 1));
endwhile
endfor
return fseq || ("%f %<has> no messages from " + $string_utils:english_list($list_utils:map_arg($string_utils, "print", nlist), "no one", " or "));
.
#14:31
":to_msg_seq(object or list) => msg_seq of messages to those people";
if (!this:ok(caller, caller_perms()))
return E_PERM;
elseif (!this.messages)
return {};
endif
if (typeof(plist = args[1]) != LIST)
plist = {plist};
endif
seq = {};
mask = {@args, {1, this.messages[2] + 1}}[2];
for m in [1..length(mask) / 2]
handle = this._mgr:start(this.messages, i = mask[(2 * m) - 1], mask[2 * m] - 1);
while (handle)
for msg in (handle[1])
toline = msg[5];
for r in ($mail_agent:parse_address_field(toline))
if (r in plist)
seq = $seq_utils:add(seq, i, i);
endif
endfor
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
handle = this._mgr:next(@listdelete(handle, 1));
endwhile
endfor
return seq || ("%f %<has> no messages to " + $string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%n (%#)", plist), "no one", " or "));
.
#14:32
":%to_msg_seq(string or list of strings)";
" => msg_seq of messages containing one of strings in the to line";
if (!this:ok(caller, caller_perms()))
return E_PERM;
elseif (!this.messages)
return {};
endif
if (typeof(nlist = args[1]) != LIST)
nlist = {nlist};
endif
seq = {};
mask = {@args, {1, this.messages[2] + 1}}[2];
for m in [1..length(mask) / 2]
handle = this._mgr:start(this.messages, i = mask[(2 * m) - 1], mask[2 * m] - 1);
while (handle)
for msg in (handle[1])
toline = " " + msg[5];
for n in (nlist)
if (index(toline, n))
seq = $seq_utils:add(seq, i, i);
endif
endfor
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
handle = this._mgr:next(@listdelete(handle, 1));
endwhile
endfor
return seq || ("%f %<has> no messages to " + $string_utils:english_list($list_utils:map_arg($string_utils, "print", nlist), "no one", " or "));
.
#14:33
":subject_msg_seq(target) => msg_seq of messages with target in the Subject:";
if (!this:ok(caller, caller_perms()))
return E_PERM;
elseif (!this.messages)
return {};
endif
target = args[1];
seq = {};
mask = {@args, {1, this.messages[2] + 1}}[2];
for m in [1..length(mask) / 2]
handle = this._mgr:start(this.messages, i = mask[(2 * m) - 1], mask[2 * m] - 1);
while (handle)
for msg in (handle[1])
if (((subject = msg[6]) != " ") && index(subject, target))
seq = $seq_utils:add(seq, i, i);
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
handle = this._mgr:next(@listdelete(handle, 1));
endwhile
endfor
return seq || (("%f %<has> no messages with subjects containing `" + target) + "'");
.
#14:34
":body_msg_seq(target) => msg_seq of messages with target in the body";
if (!this:ok(caller, caller_perms()))
return E_PERM;
elseif (!this.messages)
return {};
endif
target = args[1];
seq = {};
mask = {@args, {1, this.messages[2] + 1}}[2];
for m in [1..length(mask) / 2]
handle = this._mgr:start(this.messages, i = mask[(2 * m) - 1], mask[2 * m] - 1);
while (handle)
for msg in (handle[1])
if (msg[1] && (body = this.(msg[1])))
l = length(body);
while ((!index(body[l], target)) && (l = l - 1))
$command_utils:suspend_if_needed(0);
endwhile
if (l)
seq = $seq_utils:add(seq, i, i);
endif
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
handle = this._mgr:next(@listdelete(handle, 1));
endwhile
endfor
return seq || tostr("%f %<has> no messages containing `", target, "' in the body.");
.
#14:35
return E_VERBNF;
.
#14:36
msgtree = this.messages;
this.last_msg_date = msgtree && this:_message_hdr(@this._mgr:find_nth(msgtree, msgtree[2]))[1];
.
#14:37
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
doit = args && args[1];
msgtree = this.messages;
for n in [1..msgtree[2]]
msg = this._mgr:find_nth(msgtree, n);
msg = {@msg[1..2], @$mail_agent:__convert_new(@msg[3..length(msg)])};
if (doit)
msgtree = this._mgr:set_nth(msgtree, n, msg);
endif
if ($command_utils:running_out_of_time())
suspend(0);
if (this.messages != msgtree)
player:notify("urk.  someone played with this folder.");
return 0;
endif
endif
endfor
return 1;
.
#14:38
if ($__core_init_phase)
pass(@args);
this._mgr = $biglist;
clear_property(this, "messages_going");
this.mowner = $mail_recipient.owner;
endif
.
#14:39
if (this:ok(caller, caller_perms()))
date = args[1];
return (this.last_msg_date <= date) ? 0 | (this.messages[2] - this._mgr:find_ord(this.messages, args[1], "_lt_msgdate"));
else
return E_PERM;
endif
.
#14:40
if (caller_perms().wizard)
if (0 && "this worked...")
top = 1 + this:length_all_msgs();
i = 1;
while (i < top)
$command_utils:suspend_if_needed(0);
nexti = min(i + 20, top);
this:rm_message_seq({i, nexti});
this:expunge_rmm();
i = nexti;
endwhile
elseif (this != $big_mail_recipient)
clear_property(this, "_genprop");
clear_property(this, "messages");
for p in (properties(this))
$command_utils:suspend_if_needed(0);
if (p[1] == " ")
delete_property(this, p);
endif
endfor
endif
else
$error:raise(E_PERM);
endif
.
#15:0
who = args[1];
move(who, $player_start);
$player_start:announce_all_but({who}, who:inamec(), " has connected.");
.
#15:1
return $player_start:who_location_msg(@args);
.
#15:2
return {};
.
#15:3
what = args[1];
return is_player(what) && (!(what in connected_players()));
.
#15:4
return $player_start:in_name(@args);
.
#15:5
return 1;
.
#15:6
return $player_start:in_name(@args);
.
#16:0
return ((caller == this) || caller_perms().wizard) ? pass(@args) | raise(E_PERM);
.
#16:1
":add(player,email[,comment])";
if (!caller_perms().wizard)
return E_PERM;
endif
who = args[1];
email = args[2];
comment = args[3..length(args)];
l = this:find_exact(email);
if (l == $failed_match)
this:insert(email, {{who, @comment}});
elseif (i = $list_utils:iassoc(who, l))
this:insert(email, listset(l, {who, @comment}, i));
else
this:insert(email, {@l, {who, @comment}});
endif
.
#16:2
if ($__core_init_phase)
for p in (players())
if (p.__init_for_core_flag != p)
return "wait";
endif
endfor
pass(@args);
this:clearall();
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866506433, \"Erik\", #74, \"JHM\"}";
.
#16:3
"suspicious_address(address[, who])";
"Determine whether an address appears to be another player in disguise, and return true or false.";
"";
"If a second argument is given, then if all similar addresses are held by that person, let it pass--they're just switching addresses within the same domain.";
"";
"at the moment,";
"  foo@bar.baz.bing.boo";
"is considered 'similar' to anything matching";
"  foo@*.bing.boo";
if (!caller_perms().wizard)
return E_PERM;
endif
allowed = (length(args) > 1) ? args[2] | #-1;
parsed = $network:parse_address(address = args[1]);
userid = parsed[1];
site = parsed[2];
exact = (!site) && this:find_exact(address);
if (!site)
site = $network.site;
endif
site = $network:local_domain(site);
if (typeof(site) != STR)
"not our job.";
return site;
endif
sitelen = length(site);
others = this:find_all_keys(userid + "@");
for other in (others)
if (other[max(1, (length(other) - sitelen) + 1)..length(other)] != site)
others = setremove(others, other);
endif
endfor
if (exact)
others = listinsert(others, address);
endif
"return others;";
for key in (others)
value = this:find_exact(key);
for who in (value)
if (!((who[1] == allowed) || ((length(who) > 1) && (who[2] == "zapped due to inactivity"))))
"if ((value && (length(value) == 2)) && (value[2] != \"zapped due to inactivity\"))";
return 1;
endif
endfor
endfor
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866506927, \"Erik\", #74, \"JHM\"}";
.
#16:4
"suspicious_userid(userid)";
"Return yes if userid is root or postmaster or something like that.";
return (((args[1] in {"", "sysadmin", "root", "postmaster", "bin", "SYSTEM", "OPERATOR", "guest", "me"}) || match(args[1], "^guest")) || index(args[1], "-owner")) || index(args[1], "owner-");
"Thinking about ruling out hyphenated names, on the grounds that they're probably mailing lists.";
.
#16:5
"Usage:  $registration_db:check_address_request(address, connection, who)";
"";
"See if <address> is an acceptable registration for <who>, connecting from site <connection>.  (Regular users can register an address similar to their current one; guests are assumed to be requesting a new character, and can't have an address similar to anyone's.";
"";
"Return {E_INVARG, reason} if the address is unacceptable; {false, reason} if the address is unacceptable but a registrar can override; or {true, address} if the address is okay.";
"";
"Reason is an explanation you can feed to :tell_lines().";
address = args[1];
connection = args[2];
who = args[3];
if (!caller_perms().wizard)
return E_PERM;
"accesses registration information -- wiz only";
endif
if (match(address, "^<.*>$"))
address = address[2..length(address) - 1];
endif
if ($registration_db:suspicious_address(address, who))
return {0, "There has already been a character with that or a similar email address."};
endif
if (reason = $network:invalid_email_address(address))
return {E_INVARG, reason};
endif
parsed = $network:parse_address(address);
if ($registration_db:suspicious_userid(parsed[1]))
return {0, tostr("Automatic registration from an account named `", parsed[1], "' is not allowed.")};
endif
if (connection)
if ((connection[max(length(connection) - 2, 1)..length(connection)] == ".uk") && (parsed[2][1..3] == "uk."))
return {E_INVARG, tostr("Addresses must be in internet form. Try ", parsed[1], "@", $string_utils:from_list($list_utils:reverse($string_utils:explode(parsed[2], ".")), "."), ".")};
elseif (match(connection, "^[0-9.]+$"))
return {0, "The system needs to compare connect site with address, and can't resolve the connect site."};
elseif ((a = $network:local_domain(connection)) != (b = $network:local_domain(parsed[2])))
return {0, tostr("The connection is from '", a, "' but the mail address is '", b, "'; these don't seem to be the same place.")};
endif
elseif ($login:spooflisted(parsed[2]))
return {0, tostr("Automatic registration is not allowed from ", parsed[2], ".")};
endif
return {1, address};
.
#16:6
{_, _, perms, @_} = callers()[2];
return !perms.wizard;
"Copied from Site DB (#27):prohibit_lookup by Rog (#145) Mon Apr 15 04:56:39 1996 EDT";
.
#17:0
":display_seq_headers(msg_seq[,cur])";
if (!this:ok(caller, caller_perms()))
return E_PERM;
endif
player:tell("       WHEN    BY        WHO                 EMAIL-ADDRESS");
pass(@args);
.
#17:1
when = ctime(args[1])[5..10];
from = args[2];
by = $string_utils:left(from[1..index(from, " (") - 1], -9);
subject = args[4];
who = subject[1..(open = index(subject, " (")) - 1];
if ((close = rindex(subject, ")")) > open)
who = who[1..min(9, length(who))] + subject[open..close];
endif
who = $string_utils:left(who, 18);
line = `args[("" in args) + 1] ! E_RANGE => ""';
email = line[1..index(line + " ", " ") - 1];
if (!index(email, "@"))
email = "??";
endif
return tostr(when, "  ", by, " ", who, "  ", email);
"Metadata 202106";
"Last-Modify: {866507711, \"Erik\", #74, \"JHM\"}";
.
#17:2
return (pass(@args) || (args[1] == this.autoregistration_player)) || $admin:ok_check_registration_other(args[1]);
.
#18:0
"space(len,fill) returns a string of length abs(len) consisting of copies of fill.  If len is negative, fill is anchored on the right instead of the left.";
"len has an upper limit of 100,000.";
{n, ?fill = " "} = args;
if (typeof(n) == STR)
n = length(n);
endif
if (n > 100000)
$error:raise(E_INVARG);
endif
if (fill != " ")
fill = fill + fill;
fill = fill + fill;
fill = fill + fill;
elseif ((n = abs(n)) < 70)
return "                                                                      "[1..n];
else
fill = "                                                                      ";
endif
m = (n - 1) / length(fill);
while (m)
fill = fill + fill;
m = m / 2;
endwhile
return (n > 0) ? fill[1..n] | fill[((f = length(fill)) + 1) + n..f];
.
#18:1
{text, len, ?fill = " "} = args;
abslen = abs(len);
out = tostr(text);
if (length(out) < abslen)
return out + this:space(length(out) - abslen, fill);
else
return (len > 0) ? out | out[1..abslen];
endif
.
#18:2
{text, len, ?fill = " "} = args;
abslen = abs(len);
out = tostr(text);
if (length(out) < abslen)
return this:space(abslen - length(out), fill) + out;
else
return (len > 0) ? out | out[1..abslen];
endif
.
#18:3
{text, len, ?lfill = " ", ?rfill = lfill} = args;
out = tostr(text);
abslen = abs(len);
if (length(out) < abslen)
return (this:space((abslen - length(out)) / 2, lfill) + out) + this:space(((abslen - length(out)) + 1) / -2, rfill);
else
return (len > 0) ? out | out[1..abslen];
endif
.
#18:4
"columnize (items, n [, width]) - Turn a one-column list of items into an n-column list. 'width' is the last character position that may be occupied; it defaults to a standard screen width. Example: To tell the player a list of numbers in three columns, do 'player:tell_lines ($string_utils:columnize ({1, 2, 3, 4, 5, 6, 7}, 3));'.";
{items, n, ?width = 79} = args;
height = ((length(items) + n) - 1) / n;
items = {@items, @$list_utils:make((height * n) - length(items), "")};
colwidths = {};
for col in [1..n - 1]
colwidths = listappend(colwidths, 1 - (((width + 1) * col) / n));
endfor
result = {};
for row in [1..height]
line = tostr(items[row]);
for col in [1..n - 1]
line = tostr(this:left(line, colwidths[col]), " ", items[row + (col * height)]);
endfor
result = listappend(result, line[1..min(length(line), width)]);
endfor
return result;
.
#18:5
"$string_utils:from_list(l [, separator])";
"Return a string being the concatenation of the string representations of the elements of L, each pair separated by the string SEPARATOR, which defaults to the empty string.";
"$string_utils:from_list(     l, separator) is the inverse of";
"$string_utils:to_list  (string, separator)";
{l, ?separator = ""} = args;
if (separator)
if (l)
result = tostr(l[1]);
for elt in (listdelete(l, 1))
result = tostr(result, separator, elt);
endfor
return result;
else
return "";
endif
else
return tostr(@l);
endif
.
#18:6
"Prints the argument (must be a list) as an english list, e.g. {1, 2, 3} is printed as \"1, 2, and 3\", and {1, 2} is printed as \"1 and 2\".";
"Optional arguments are treated as follows:";
"  Second argument is the string to use when the empty list is given.  The default is \"nothing\".";
"  Third argument is the string to use in place of \" and \".  A typical application might be to use \" or \" instead.";
"  Fourth argument is the string to use instead of a comma (and space).  Gary_Severn's deranged mind actually came up with an application for this.  You can ask him.";
"  Fifth argument is a string to use after the penultimate element before the \" and \".  The default is to have a comma without a space.";
{things, ?nothingstr = "nothing", ?andstr = " and ", ?commastr = ", ", ?finalcommastr = ","} = args;
nthings = length(things);
if (nthings == 0)
return nothingstr;
elseif (nthings == 1)
return tostr(things[1]);
elseif (nthings == 2)
return tostr(things[1], andstr, things[2]);
else
ret = "";
for k in [1..nthings - 1]
if (k == (nthings - 1))
commastr = finalcommastr;
endif
ret = tostr(ret, things[k], commastr);
endfor
return tostr(ret, andstr, things[nthings]);
endif
.
#18:7
"Return a string of the names and object numbers of the objects in a list.";
return this:name_and_number_list(args[1], "", "   ", "   ", "");
.
#18:8
minute = 60;
hour = 60 * minute;
day = 24 * hour;
secs = args[1];
if (secs > day)
count = secs / day;
unit = "day";
article = "a";
elseif (secs > hour)
count = secs / hour;
unit = "hour";
article = "an";
elseif (secs > minute)
count = secs / minute;
unit = "minute";
article = "a";
else
count = secs;
unit = "second";
article = "a";
endif
if (count == 1)
time = tostr(article, " ", unit);
else
time = tostr(count, " ", unit, "s");
endif
return time;
.
#18:9
":trim (string [, space]) -- remove leading and trailing spaces";
"";
"`space' should be a character (single-character string); it defaults to \" \".  Returns a copy of string with all leading and trailing copies of that character removed.  For example, $string_utils:trim(\"***foo***\", \"*\") => \"foo\".";
{string, ?space = " "} = args;
m = match(string, tostr("[^", space, "]%(.*[^", space, "]%)?%|$"));
return string[m[1]..m[2]];
.
#18:10
":triml(string [, space]) -- remove leading spaces";
"";
"`space' should be a character (single-character string); it defaults to \" \".  Returns a copy of string with all leading copies of that character removed.  For example, $string_utils:triml(\"***foo***\", \"*\") => \"foo***\".";
{string, ?space = " "} = args;
return string[match(string, tostr("[^", space, "]%|$"))[1]..length(string)];
.
#18:11
":trimr(string [, space]) -- remove trailing spaces";
"";
"`space' should be a character (single-character string); it defaults to \" \".  Returns a copy of string with all trailing copies of that character removed.  For example, $string_utils:trimr(\"***foo***\", \"*\") => \"***foo\".";
{string, ?space = " "} = args;
return string[1..rmatch(string, tostr("[^", space, "]%|^"))[2]];
.
#18:12
":strip_chars(string,chars) => string with chars removed";
subject = args[1];
stripped = args[2];
for i in [1..length(stripped)]
subject = strsub(subject, stripped[i], "");
endfor
return subject;
.
#18:13
":strip_all_but(string,keep) => string with chars not in `keep' removed.";
"`keep' is used in match() so if it includes ], ^, or -,";
"] should be first, ^ should be other from first, and - should be last.";
string = args[1];
keep = args[2];
wanted = tostr("[", keep, "]+");
output = "";
while (m = match(string, wanted))
output = output + string[m[1]..m[2]];
string = string[m[2] + 1..length(string)];
endwhile
return output;
.
#18:14
"lowercase(string) -- returns a lowercase version of the string.";
"uppercase(string) -- returns the uppercase version of the string.";
string = args[1];
from = caps = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
to = lower = "abcdefghijklmnopqrstuvwxyz";
if (verb == "uppercase")
from = lower;
to = caps;
endif
for i in [1..26]
string = strsub(string, from[i], to[i], 1);
endfor
return string;
.
#18:15
"capitalizes its argument.";
if ((string = args[1]) && (i = index("abcdefghijklmnopqrstuvwxyz", string[1], 1)))
string[1] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i];
endif
return string;
.
#18:16
string = args[1];
if (!string)
return $nothing;
elseif ((string[1] == "#") && (E_TYPE != (object = $code_utils:toobj(string))))
return object;
elseif (string[1] == "~")
return this:match_player(string[2..$], #0);
elseif (string[1] == "*")
return $mail_agent:match_recipient(string);
elseif (string == "$")
return $failed_match;
elseif (string[1] == "$")
string = string[2..$];
object = #0;
while properties (1)
dot = index(string, ".");
pn = dot ? string[1..dot - 1] | string;
try
object = object.(pn);
except (ANY)
return $failed_match;
endtry
if (dot)
string = string[dot + 1..$];
else
break properties;
endif
endwhile
if (typeof(object) == OBJ)
return object;
else
return $failed_match;
endif
else
return $failed_match;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {875331719, \"Erik\", #74, \"JHM\"}";
.
#18:17
"$string_utils:match(string [, obj-list, prop-name]*)";
"Each obj-list should be a list of objects or a single object, which is treated as if it were a list of that object.  Each prop-name should be string naming a property on every object in the corresponding obj-list.  The value of that property in each case should be either a string or a list of strings.";
"The argument string is matched against all of the strings in the property values.";
"If it exactly matches exactly one of them, the object containing that property is returned.  If it exactly matches more than one of them, $ambiguous_match is returned.";
"If there are no exact matches, then partial matches are considered, ones in which the given string is a prefix of some property string.  Again, if exactly one match is found, the object with that property is returned, and if there is more than one match, $ambiguous_match is returned.";
"Finally, if there are no exact or partial matches, then $failed_match is returned.";
subject = args[1];
tests = args[2..length(args)];
if (subject == "")
return $nothing;
endif
no_exact_match = no_partial_match = 1;
for i in [1..(length(tests) + 1) / 2]
prop_name = tests[2 * i];
for object in ((typeof(olist = tests[(2 * i) - 1]) == LIST) ? olist | {olist})
if (valid(object))
if (typeof(str_list = object.(prop_name)) != LIST)
str_list = {str_list};
endif
if (subject in str_list)
if (no_exact_match)
no_exact_match = object;
elseif (no_exact_match != object)
return $ambiguous_match;
endif
else
for string in (str_list)
if (index(string, subject) != 1)
elseif (no_partial_match)
no_partial_match = object;
elseif (no_partial_match != object)
no_partial_match = $ambiguous_match;
endif
endfor
endif
endif
endfor
endfor
return no_exact_match && (no_partial_match && $failed_match);
.
#18:18
"* wildcard matching. Returns a list of what the *s actually matched. Won't cath every match, if there are several ways to parse it.";
"Example: $string_utils:match_string(\"Jack waves to Jill\",\"* waves to *\") returns {\"Jack\", \"Jill\"}";
"Optional arguments: numbers are interpreted as case-sensitivity, strings as alternative wildcards.";
wild = "*";
case = ret = {};
what = args[1] + "&^%$";
targ = args[2] + "&^%$";
for y in (args[3..length(args)])
if (typeof(y) == STR)
wild = y;
elseif (typeof(y) == NUM)
case = {y};
endif
endfor
while (targ != "")
if (z = index(targ, wild))
part = targ[1..z - 1];
else
z = length(targ);
part = targ;
endif
n = (part == "") ? 1 | index(what, part, @case);
if (n)
ret = listappend(ret, what[1..n - 1]);
what = what[(z + n) - 1..length(what)];
targ = targ[z + 1..length(targ)];
else
return 0;
endif
endwhile
if (ret == {})
return what == "";
elseif (ret == {""})
return 1;
elseif (ret[1] == "")
return ret[2..length(ret)];
else
return 0;
endif
.
#18:19
":match_object(string,location[,someone])";
"Returns the object matching the given string for someone, on the assumption that s/he is in the given location.  `someone' defaults to player.";
"This first tries :literal_object(string), \"me\"=>someone,\"here\"=>location, then player:match(string) and finally location:match(string) if location is valid.";
"This is the default algorithm for use by room :match_object() and player :my_match_object() verbs.  Player verbs that are calling this directly should probably be calling :my_match_object instead.";
{string, here, ?who = player} = args;
if ($failed_match != (object = this:literal_object(string)))
return object;
elseif ((string == "_") && (typeof(co = `who.current_object[1] ! ANY') == OBJ))
return co;
elseif (string == "me")
return who;
elseif (string == "here")
return here;
elseif ((valid(pobject = who:match(string)) && (string in {@pobject.aliases, pobject:name()})) || (!valid(here)))
"...exact match in player or room is bogus...";
return pobject;
elseif (valid(hobject = here:match(string)) && (string in {@hobject.aliases, hobject:name()}))
"...exact match in room or match in player failed completely...";
return hobject;
elseif (pobject != $failed_match)
return pobject;
elseif (hobject != $failed_match)
return hobject;
elseif (arrow = rindex(string, "->"))
guardian = this:match_object(string[1..arrow - 1], @listdelete(args, 1));
if (valid(guardian))
o = guardian:match_type_object(string[arrow + 2..$]);
return (typeof(o) == OBJ) ? o | $failed_match;
else
return $failed_match;
endif
elseif (typeof(number = $code_utils:tonum(string)) == NUM)
" if (((number > 0) && (number <= length(who.contributed_links))) && ((jaddress = who.contributed_links[number])[1] == $jaddress.object))";
"   return this:literal_object($jaddress:extract(\"objnum\", jaddress)[2]);";
" (now works with other addresses)";
if (((number > 0) && (number <= length(who.contributed_links))) && (jaddress_obj = $jaddress:extract("objnum", who.contributed_links[number])))
return this:literal_object(jaddress_obj[2]);
else
return $failed_match;
endif
elseif ((first = string[1..(space = index(string, " ")) - 1]) == "my")
return who:match(string[space + 1..$]);
elseif (subs = match(string, "%([^ ]*%)%('s%|s'%) +%(.*%)$"))
if (valid(whose = $string_utils:match_object(@listset(args, substitute("%1", subs), 1))))
return whose:match(substitute("%3", subs));
else
return $failed_match;
endif
elseif (subs = match(string, "^%(a%|an%|the%) +%([^ ].*%)$"))
return this:match_object(substitute("%2", subs), here, who);
else
return $failed_match;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {875332080, \"Erik\", #74, \"JHM\"}";
.
#18:20
"match_player(name,name,...)      => {obj,obj,...}";
"match_player(name[,meobj])       => obj";
"match_player({name,...}[,meobj]) => {obj,...}";
"objs returned are either players, $failed_match, $ambiguous_match, or $nothing in the case of an empty string.";
"meobj (what to return for instances of `me') defaults to player; if given and isn't actually a player, `me' => $failed_match";
retstr = 0;
me = player;
if ((length(args) < 2) || (typeof(me = args[2]) == OBJ))
me = (valid(me) && is_player(me)) ? me | $failed_match;
if (typeof(args[1]) == STR)
strings = {args[1]};
retstr = 1;
"return a string, not a list";
else
strings = args[1];
endif
else
strings = args;
me = player;
endif
found = {};
for astr in (strings)
if (!astr)
aobj = $nothing;
elseif (astr == "me")
aobj = me;
elseif (valid(aobj = $string_utils:literal_object(astr)) && is_player(aobj))
"astr is a valid literal object number of some player, so we are done.";
else
aobj = $player_db:find(astr);
endif
found = {@found, aobj};
endfor
return retstr ? found[1] | found;
.
#18:21
"Accepts any number of strings, attempts to match those strings first against objects in the room, and if no objects by those names exist, matches against player names (and \"#xxxx\" style strings regardless of location).  Returns a list of valid objects so found.";
"Unlike $string_utils:match_player, does not include in the list the failed and ambiguous matches; instead has built-in error messages for such objects.  This should probably be improved.  Volunteers?";
if (!args)
return;
endif
unknowns = {};
objs = {};
"We have to do something icky here.  Parallel walk the victims and args lists.  When it's a valid object, then it's a player.  If it's an invalid object, try to get an object match from the room.  If *that* fails, complain.";
for i in [1..length(args)]
if (valid(o = player.location:match_object(args[i])))
objs = {@objs, o};
else
unknowns = {@unknowns, args[i]};
endif
endfor
victims = $string_utils:match_player(unknowns);
for i in [1..length(victims)]
if (!valid(victims[i]))
player:tell("Could not find ", unknowns[i], " as either an object or a player.");
else
objs = {@objs, victims[i]};
endif
endfor
return objs;
.
#18:22
"find_prefix(prefix, string-list) => list index of something starting with prefix, or 0 or $ambiguous_match.";
subject = args[1];
choices = args[2];
answer = 0;
for i in [1..length(choices)]
if (index(choices[i], subject) == 1)
if (answer == 0)
answer = i;
else
answer = $ambiguous_match;
endif
endif
endfor
return answer;
.
#18:23
"index_delimited(string,target[,case_matters]) is just like the corresponding call to the builtin index() but instead only matches on occurences of target delimited by word boundaries (i.e., not preceded or followed by an alphanumeric)";
args[2] = ("%(%W%|^%)" + $string_utils:regexp_quote(args[2])) + "%(%W%|$%)";
return (m = match(@args)) ? m[3][1][2] + 1 | 0;
.
#18:24
"Usage:  is_numeric(string)";
"Is string numeric (composed of one or more digits possibly preceded by a minus sign)?";
"Return true or false.";
return match(args[1], "^ *[-+]?[0-9]+ *$");
digits = "1234567890";
if (!(string = args[1]))
return 0;
endif
if (string[1] == "-")
string = string[2..length(string)];
endif
for i in [1..length(string)]
if (!index(digits, string[i]))
return 0;
endif
endfor
return 1;
.
#18:25
":short_ordinal(1) => \"1st\",:short_ordinal(2) => \"2nd\",etc...";
string = tostr(n = args[1]);
n = abs(n) % 100;
if (((n / 10) != 1) && ((n % 10) in {1, 2, 3}))
return string + {"st", "nd", "rd"}[n % 10];
else
return string + "th";
endif
.
#18:26
"$string_utils:group_number(n [, sep_char])";
"";
"Converts N to a string, inserting commas (or copies of SEP_CHAR, if given) every three digits, counting from the right.  For example, $string_utils:group_number(1234567890) returns the string \"1,234,567,890\".";
{n, ?sep_char = ","} = args;
result = "";
sign = (n < 0) ? "-" | "";
n = tostr(abs(n));
while ((len = length(n)) > 3)
result = (sep_char + n[len - 2..len]) + result;
n = n[1..len - 3];
endwhile
return (sign + n) + result;
.
#18:27
"$string_utils:english_number(n[,zero]) -- convert the number N into English";
"";
"Produces a string containing the English phrase naming the given number.  For example, $string_utils:english_number(-1234) returns the string `negative one thousand two hundred thirty-four'.";
"If zero is supplied, use that string for 0.  Default is \"zero\".";
NUM = tonum(args[1]);
if (NUM == 0)
return (length(args) > 1) ? args[2] | "zero";
endif
labels = {"", " thousand", " million", " billion"};
numstr = "";
mod = abs(NUM);
for n in [1..4]
div = mod % 1000;
if (div)
hun = div / 100;
ten = div % 100;
outstr = this:english_tens(ten) + labels[n];
if (hun)
outstr = ((this:english_ones(hun) + " hundred") + (ten ? " " | "")) + outstr;
endif
if (numstr)
numstr = (outstr + " ") + numstr;
else
numstr = outstr;
endif
endif
mod = mod / 1000;
endfor
return ((NUM < 0) ? "negative " | "") + numstr;
.
#18:28
"$string_utils:english_ordinal(n) -- convert the number N into an english ordinal (1 => \"first\", etc...)";
NUM = tonum(args[1]);
if (NUM == 0)
return "zeroth";
elseif (NUM % 100)
hundreds = (abs(NUM) > 100) ? this:english_number((NUM / 100) * 100) + " " | ((NUM < 0) ? "negative " | "");
NUM = abs(NUM) % 100;
specials = {1, 2, 3, 5, 8, 9, 12, 20, 30, 40, 50, 60, 70, 80, 90};
ordinals = {"first", "second", "third", "fifth", "eighth", "ninth", "twelfth", "twentieth", "thirtieth", "fortieth", "fiftieth", "sixtieth", "seventieth", "eightieth", "ninetieth"};
if (i = NUM in specials)
return hundreds + ordinals[i];
elseif ((NUM > 20) && (i = (NUM % 10) in specials))
return ((hundreds + this:english_tens((NUM / 10) * 10)) + "-") + ordinals[i];
else
return (hundreds + this:english_number(NUM)) + "th";
endif
else
return this:english_number(NUM) + "th";
endif
.
#18:29
NUM = args[1];
ones = {"", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};
return ones[NUM + 1];
.
#18:30
NUM = args[1];
teens = {"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"};
others = {"twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"};
if (NUM < 10)
return this:english_ones(NUM);
elseif (NUM < 20)
return teens[NUM - 9];
else
return (others[(NUM / 10) - 1] + ((NUM % 10) ? "-" | "")) + this:english_ones(NUM % 10);
endif
.
#18:31
"subst(string,{{redex1,repl1},{redex2,repl2},{redex3,repl3}...}[,case])";
"  => returns string with all instances of the strings redex<n> replaced respectively by the strings repl<n>.  If the optional argument `case' is given and nonzero, the search for instances of redex<n> is case sensitive.";
"  Substitutions are done in parallel, i.e., instances of redex<n> that appear in any of the replacement strings are ignored.  In the event that two redexes overlap, whichever is leftmost in `string' takes precedence.  For two redexes beginning at the same position, the longer one takes precedence.";
"";
"subst(\"hoahooaho\",{{\"ho\",\"XhooX\"},{\"hoo\",\"mama\"}}) => \"XhooXamamaaXhooX\"";
"subst(\"Cc: banana\",{{\"a\",\"b\"},{\"b\",\"c\"},{\"c\",\"a\"}},1) => \"Ca: cbnbnb\"";
if (typeof(ostr = args[1]) != STR)
return ostr;
endif
case = {@args, 0}[3];
len = length(ostr);
" - - - find the first instance of each substitution - -";
indices = {};
substs = {};
for s in (args[2])
if (i = index(ostr, s[1], case))
fi = $list_utils:find_insert(indices, i = i - len) - 1;
while (fi && ((indices[fi] == i) && (length(substs[fi][1]) < length(s[1]))))
"...give preference to longer redexes...";
fi = fi - 1;
endwhile
indices = listappend(indices, i, fi);
substs = listappend(substs, s, fi);
endif
endfor
"- - - - - perform substitutions - ";
nstr = "";
while (substs)
ind = len + indices[1];
sub = substs[1];
indices = listdelete(indices, 1);
substs = listdelete(substs, 1);
if (ind > 0)
nstr = (nstr + ostr[1..ind - 1]) + sub[2];
ostr = ostr[ind + length(sub[1])..len];
len = length(ostr);
endif
if (next = index(ostr, sub[1], case))
fi = $list_utils:find_insert(indices, next = next - len) - 1;
while (fi && ((indices[fi] == next) && (length(substs[fi][1]) < length(sub[1]))))
"...give preference to longer redexes...";
fi = fi - 1;
endwhile
indices = listappend(indices, next, fi);
substs = listappend(substs, sub, fi);
endif
endwhile
return nstr + ostr;
.
#18:32
"subst(string,{{redex1,repl1},{redex2,repl2},{redex3,repl3}...}[,case])";
"Just like :substitute() but it uses index_delimited() instead of index()";
if (typeof(ostr = args[1]) != STR)
return ostr;
endif
case = {@args, 0}[3];
len = length(ostr);
" - - - find the first instance of each substitution - -";
indices = {};
substs = {};
for s in (args[2])
if (i = this:index_delimited(ostr, s[1], case))
fi = $list_utils:find_insert(indices, i = i - len) - 1;
while (fi && ((indices[fi] == i) && (length(substs[fi][1]) < length(s[1]))))
"...give preference to longer redexes...";
fi = fi - 1;
endwhile
indices = listappend(indices, i, fi);
substs = listappend(substs, s, fi);
endif
endfor
"- - - - - perform substitutions - ";
nstr = "";
while (substs)
ind = len + indices[1];
sub = substs[1];
indices = listdelete(indices, 1);
substs = listdelete(substs, 1);
if (ind > 0)
nstr = (nstr + ostr[1..ind - 1]) + sub[2];
ostr = ostr[ind + length(sub[1])..len];
len = length(ostr);
endif
if (next = this:index_delimited(ostr, sub[1], case))
fi = $list_utils:find_insert(indices, next = next - len) - 1;
while (fi && ((indices[fi] == next) && (length(substs[fi][1]) < length(sub[1]))))
"...give preference to longer redexes...";
fi = fi - 1;
endwhile
indices = listappend(indices, next, fi);
substs = listappend(substs, sub, fi);
endif
endwhile
return nstr + ostr;
.
#18:33
"cap_property(what,prop[,ucase]) returns what.(prop) but capitalized if either ucase is true or the prop name specified is capitalized.";
"If prop is blank, returns what:name().";
"If prop is bogus or otherwise irretrievable, returns the error.";
"If capitalization is indicated, we return what.(prop+\"c\") if that exists, else we capitalize what.(prop) in the usual fashion.  There is a special exception for is_player(what)&&prop==\"name\" where we just return what.name if no .namec is provided --- ie., a player's .name is never capitalized in the usual fashion.";
set_task_perms(caller_perms());
what = args[1];
prop = args[2];
ucase = (prop && (strcmp(prop, "a") < 0)) || ((length(args) >= 3) && args[3]);
if (!prop)
return valid(what) ? ucase ? what:namec() | what:name() | ((ucase ? "N" | "n") + "othing");
elseif (prop == "dname")
return valid(what) ? ucase ? what:dnamec() | what:dname() | ((ucase ? "N" | "n") + "othing");
elseif (prop == "iname")
return valid(what) ? ucase ? what:inamec() | what:iname() | ((ucase ? "N" | "n") + "othing");
elseif ((!ucase) || (typeof(s = `what.(prop + "c") ! E_PROPNF, E_INVIND') == ERR))
if (prop == "name")
if (valid(what))
if (ucase && $object_utils:has_property(what, "namec"))
s = what.namec;
ucase = 0;
else
s = what.name;
ucase = `what.proper ! ANY';
endif
else
s = "nothing";
endif
else
s = $object_utils:has_property(what, prop) ? what.(prop) | $it.(prop);
endif
if ((((ucase && s) && (typeof(s) == STR)) && ((z = strcmp(s[1], "`")) < 27)) && (z > 0))
s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[z] + s[2..length(s)];
endif
endif
return (typeof(s) == ERR) ? s | tostr(s);
"Metadata 202106";
"Last-Modify: {875334956, \"Erik\", #74, \"JHM\"}";
.
#18:34
"Pronoun (and other things) substitution. See 'help pronouns' for details.";
"syntax:  $string_utils:pronoun_sub(text[,who[,thing[,location[,dobj[,iobj]]]]])";
"msg is either a string, which is parsed according to lambdacore pronoun_sub style, or a raw pronoun_sub list.";
"who, thing, location, dobj, and iobj can all be objects or lists of {<list of objects>, @<args to $string_utils:english_list>}.";
"who defaults to player.";
"thing defaults to caller.";
"location defaults to who.location.";
"dobj and iobj default to dobj and iobj.";
set_task_perms($no_one);
if (!(msg = args[1]))
return (typeof(msg) == LIST) ? {} | "";
elseif ((typeof(msg) != LIST) || (typeof(msg[1]) != OBJ))
if ((typeof(msg) == STR) && (!index(msg, "%")))
return msg;
endif
msg = $pronoun_sub.lambdacore:parse(msg);
elseif ((length(msg) == 3) && (typeof(msg[3]) == STR))
return msg[3];
endif
parties = $pronoun_sub:parse_parties(listdelete(args, 1), caller);
return msg[1]:(msg[2])(msg[3..length(msg)], @parties);
.
#18:35
"$string_utils:pronoun_sub_secure(string[,who[,thing[,location]]], default)";
"Do pronoun_sub on string with the arguments given (see help";
"string_utils:pronoun_sub for more information).  Return pronoun_subbed";
"<default> if the subbed string does not contain <who>:name() (<who>";
"defaults to player).";
len = length(args);
who = (len > 2) ? args[2] | player;
default = args[len];
result = $string_utils:pronoun_sub(@args[1..len - 1]);
return this:index_delimited(result, who:name()) ? result | $string_utils:pronoun_sub(default, @args[2..len - 1]);
.
#18:36
" pronoun_quote(string) => quoted_string";
" pronoun_quote(list of strings) => list of quoted_strings";
" pronoun_quote(list of {key,string} pairs) => list of {key,quoted_string} pairs";
"";
"Here `quoted' means quoted in the sense of $string_utils:pronoun_sub, i.e., given a string X, the corresponding `quoted' string Y is such that pronoun_sub(Y) => X.  For example, pronoun_quote(\"--%Spam%--\") => \"--%%Spam%%--\".  This is for including literal text into a string that will eventually be pronoun_sub'ed, i.e., including it in such a way that the pronoun_sub will not expand anything in the included text.";
"";
"The 3rd form above (with {key,string} pairs) is for use with $string_utils:substitute().  If you have your own set of substitutions to be done in parallel with the pronoun substitutions, do";
"";
"  msg=$string_utils:substitute(msg,$string_utils:pronoun_quote(your_substs));";
"  msg=$string_utils:pronoun_sub(msg);";
if (typeof(what = args[1]) == STR)
return strsub(what, "%", "%%");
else
ret = {};
for w in (what)
if (typeof(w) == LIST)
ret = listappend(ret, listset(w, strsub(w[2], "%", "%%"), 2));
else
ret = listappend(ret, strsub(w, "%", "%%"));
endif
endfor
return ret;
endif
.
#18:37
" $string_utils:explode(subject [, delim])";
" Return a list of those substrings of subject separated by runs of delim[1].";
" delim defaults to space.";
{subject, ?delim = " "} = args;
if (delim)
delim = delim[1];
else
return this:char_list(subject);
endif
subject = subject + delim;
parts = {};
while (subject)
if ((i = index(subject, delim)) > 1)
parts = {@parts, subject[1..i - 1]};
endif
subject = subject[i + 1..$];
endwhile
return parts;
.
#18:38
"This breaks up the argument string into words, the resulting list being obtained exactly the way the command line parser obtains `args' from `argstr'.";
rest = args[1];
"...trim leading blanks...";
rest[1..match(rest, "^ *")[2]] = "";
if (!rest)
return {};
endif
quote = 0;
toklist = {};
token = "";
pattern = " +%|\\.?%|\"";
while (m = match(rest, pattern))
"... find the next occurence of a special character, either";
"... a block of spaces, a quote or a backslash escape sequence...";
"notify(#4292,tostr(\"match(\",#20:print(rest),\",\",#20:print(pattern),\") => \",#20:print(m)))";
char = rest[m[1]];
token = token + rest[1..m[1] - 1];
if (char == " ")
toklist = {@toklist, token};
token = "";
elseif (char == "\"")
"... beginning or end of quoted string...";
"... within a quoted string spaces aren't special...";
pattern = (quote = !quote) ? "\\.?%|\"" | " +%|\\.?%|\"";
elseif (m[1] < m[2])
"... char has to be a backslash...";
"... include next char literally if there is one";
token = token + rest[m[2]];
endif
rest[1..m[2]] = "";
endwhile
return (rest || (char != " ")) ? {@toklist, token + rest} | toklist;
.
#18:39
"This breaks up the argument string into words, returning a list of indices into argstr corresponding to the starting points of each of the arguments.";
rest = args[1];
"... find first nonspace...";
wstart = match(rest, "[^ ]%|$")[1];
wbefore = wstart - 1;
rest[1..wbefore] = "";
if (!rest)
return {};
endif
quote = 0;
wslist = {};
pattern = " +%|\\.?%|\"";
while (m = match(rest, pattern))
"... find the next occurence of a special character, either";
"... a block of spaces, a quote or a backslash escape sequence...";
char = rest[m[1]];
if (char == " ")
wslist = {@wslist, {wstart, (wbefore + m[1]) - 1}};
wstart = (wbefore + m[2]) + 1;
elseif (char == "\"")
"... beginning or end of quoted string...";
"... within a quoted string spaces aren't special...";
pattern = (quote = !quote) ? "\\.?%|\"" | " +%|\\.?%|\"";
endif
rest[1..m[2]] = "";
wbefore = wbefore + m[2];
endwhile
return (rest || (char != " ")) ? {@wslist, {wstart, wbefore + length(rest)}} | wslist;
.
#18:40
":to_value(string) tries to parse string as a value (i.e., object, number, string, error, or list thereof).";
"Returns {1,value} or {0,error_message} according as the attempt was successful or not.";
result = this:_tolist(string = args[1] + "}");
if (result[1] && (result[1] != $string_utils:space(result[1])))
return {0, tostr("after char ", length(string) - result[1], ":  ", result[2])};
elseif (typeof(result[1]) == NUM)
return {0, "missing } or \""};
elseif (length(result[2]) > 1)
return {0, "comma unexpected."};
elseif (result[2])
return {1, result[2][1]};
else
return {0, "missing expression"};
endif
.
#18:41
":prefix_to_value(string) tries to parse string as a value (i.e., object, number, string, error, or list thereof).";
"Returns {rest-of-string,value} or {0,error_message} according as the attempt was successful or not.";
alen = length(args[1]);
slen = length(string = this:triml(args[1]));
if (!string)
return {0, "empty string"};
elseif (w = index("{\"", string[1]))
result = this:({"_tolist", "_unquote"}[w])(string[2..slen]);
if (typeof(result[1]) != NUM)
return result;
elseif (result[1] == 0)
return {0, "missing } or \""};
else
return {0, result[2], (alen - result[1]) + 1};
endif
else
thing = string[1..tlen = index(string + " ", " ") - 1];
if (typeof(s = this:_toscalar(thing)) != STR)
return {string[tlen + 1..slen], s};
else
return {0, s, (alen - slen) + 1};
endif
endif
.
#18:42
"_tolist(string) --- auxiliary for :to_value()";
rest = this:triml(args[1]);
vlist = {};
if (!rest)
return {0, {}};
elseif (rest[1] == "}")
return {rest[2..length(rest)], {}};
endif
while (1)
rlen = length(rest);
if (w = index("{\"", rest[1]))
result = this:({"_tolist", "_unquote"}[w])(rest[2..rlen]);
if (typeof(result[1]) == NUM)
return result;
endif
vlist = {@vlist, result[2]};
rest = result[1];
else
thing = rest[1..tlen = min(index(rest + ",", ","), index(rest + "}", "}")) - 1];
if (typeof(s = this:_toscalar(thing)) == STR)
return {rlen, s};
endif
vlist = {@vlist, s};
rest = rest[tlen + 1..rlen];
endif
if (!rest)
return {0, vlist};
elseif (rest[1] == "}")
return {rest[2..length(rest)], vlist};
elseif (rest[1] == ",")
rest = this:triml(rest[2..length(rest)]);
else
return {length(rest), ", or } expected"};
endif
endwhile
.
#18:43
"_unquote(string)   (auxiliary for :to_value())";
"reads string as if it were preceded by a quote, reading up to the closing quote if any, then returns the corresponding unquoted string.";
" => {0, string unquoted}  if there is no closing quote";
" => {original string beyond closing quote, string unquoted}  otherwise";
rest = args[1];
result = "";
while (m = match(rest, "\\.?%|\""))
"Find the next special character";
if (rest[pos = m[1]] == "\"")
return {rest[pos + 1..length(rest)], result + rest[1..pos - 1]};
endif
result = (result + rest[1..pos - 1]) + rest[pos + 1..m[2]];
rest = rest[m[2] + 1..length(rest)];
endwhile
return {0, result + rest};
.
#18:44
":_toscalar(string)  --- auxiliary for :tovalue";
" => value if string represents a number, object or error";
" => string error message otherwise";
thing = args[1];
if (!thing)
return "missing value";
elseif (match(thing, "^#?[-+]?[0-9]+ *$"))
return (thing[1] == "#") ? toobj(thing) | tonum(thing);
elseif (match(thing, "^[-+]?[0-9]*%.[0-9]*%(e[+-]?[0-9]+%)? *$") && match(thing, "^[^e]*[0-9]"))
return tofloat(thing);
elseif (thing[1] == "$")
o = $string_utils:literal_object(thing);
if (valid(o) || (thing in {"$failed_match", "$ambiguous_match"}))
return o;
else
return tostr("bogus object reference `", thing, "'");
endif
elseif (thing[1] == "E")
return (e = $code_utils:toerr(thing)) ? tostr("unknown error code `", thing, "'") | e;
elseif (thing[1] == "#")
return tostr("bogus objectid `", thing, "'");
else
return tostr("`", thing[1], "' unexpected");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863650469, \"Erik\", #74, \"JHM\"}";
.
#18:45
":parse_command(cmd_line[,who])";
" => {verb, {dobj, dobjstr}, {prep, prepstr}, {iobj, iobjstr}, {args, argstr},";
"     dobjset, prepset, iobjset}";
"This mimics the action of the builtin parser, returning what the values of the builtin variables `verb', `dobj', `dobjstr', `prepstr', `iobj', `iobjstr', `args', and `argstr' would be if `who' had typed `cmd_line'.  ";
"`prep' is the shortened version of the preposition found.";
"";
"`dobjset' and `iobjset' are subsets of {\"any\",\"none\"} and are used to determine possible matching verbs, i.e., the matching verb must either be on `dobj' and have verb_args[1]==\"this\" or else it has verb_args[1] in `dobjset'; likewise for `iobjset' and verb_args[3]; similarly we must have verb_args[2] in `prepset'.";
{cmd_line, ?who = player} = args;
y = $string_utils:words(cmd_line);
if (y == {})
return {};
endif
vrb = y[1];
y = y[2..length(y)];
as = (y == {}) ? "" | cmd_line[length(vrb) + 2..$];
n = 1;
while ((!(gp = $code_utils:get_prep(@y[n..length(y)]))[1]) && (n < length(y)))
n = n + 1;
endwhile
"....";
really = player;
player = who;
loc = who.location;
if (ps = gp[1])
ds = $string_utils:from_list(y[1..n - 1], " ");
is = $string_utils:from_list(listdelete(gp, 1), " ");
io = valid(loc) ? loc:match_object(is) | $string_utils:match_object(is, loc);
else
ds = $string_utils:from_list(y, " ");
is = "";
io = $nothing;
endif
do = valid(loc) ? loc:match_object(ds) | $string_utils:match_object(ds, loc);
player = really;
"....";
dset = {"any", @(ds == "") ? {"none"} | {}};
"\"this\" must be handled manually.";
pset = {"any", @ps ? {$code_utils:full_prep(ps)} | {"none"}};
iset = {"any", @(is == "") ? {"none"} | {}};
return {vrb, {do, ds}, {$code_utils:short_prep(ps), ps}, {io, is}, {y, as}, {dset, pset, iset}};
.
#18:46
"$string_utils:from_value(value [, quote_strings = 0 [, list_depth = 1]])";
"Print the given value into a string.";
{value, ?quote_strings = 0, ?list_depth = 1} = args;
if (typeof(value) == LIST)
if (value)
if (list_depth)
result = "{" + this:from_value(value[1], quote_strings, list_depth - 1);
for v in (listdelete(value, 1))
result = tostr(result, ", ", this:from_value(v, quote_strings, list_depth - 1));
endfor
return result + "}";
else
return "{...}";
endif
else
return "{}";
endif
elseif (quote_strings)
if (typeof(value) == STR)
result = "\"";
while (q = index(value, "\"") || index(value, "\\"))
if (value[q] == "\"")
q = min(q, index(value + "\\", "\\"));
endif
result = ((result + value[1..q - 1]) + "\\") + value[q];
value = value[q + 1..length(value)];
endwhile
return (result + value) + "\"";
elseif (typeof(value) == ERR)
return $code_utils:error_name(value);
else
return tostr(value);
endif
else
return tostr(value);
endif
.
#18:47
"Usage:  :print(value)";
"";
"Print the given value into a string. == from_value(value,1,-1) == toliteral(value).";
value = args[1];
return toliteral(value);
.
#18:48
"$string_utils:print_suspended(value)";
"Identical to $string_utils:print(value) except that we suspend where needed.";
value = args[1];
if (typeof(value) == LIST)
if (value)
result = "{" + this:print_suspended(value[1]);
for val in (listdelete(value, 1))
result = tostr(result, ", ", this:print_suspended(val));
$command_utils:suspend_if_needed(0);
endfor
return result + "}";
else
return "{}";
endif
elseif (typeof(value) == STR)
result = "\"";
while (q = index(value, "\"") || index(value, "\\"))
if (value[q] == "\"")
q = min(q, index(value + "\\", "\\"));
endif
result = tostr(result, value[1..q - 1], "\\", value[q]);
value = value[q + 1..length(value)];
$command_utils:suspend_if_needed(0);
endwhile
return (result + value) + "\"";
elseif (typeof(value) == ERR)
return $code_utils:error_name(value);
else
return tostr(value);
endif
.
#18:49
if ((len = length(args[1])) > 50)
return this:reverse(args[1][(len / 2) + 1..len]) + this:reverse(args[1][1..len / 2]);
endif
index = len;
result = "";
while (index > 0)
result = result + args[1][index];
index = index - 1;
endwhile
return result;
.
#18:50
":char_list(string) => string as a list of characters.";
"   e.g., :char_list(\"abad\") => {\"a\",\"b\",\"a\",\"d\"}";
if (30 < (len = length(string = args[1])))
return {@this:char_list(string[1..len / 2]), @this:char_list(string[(len / 2) + 1..len])};
else
l = {};
for c in [1..len]
l = {@l, string[c]};
endfor
return l;
endif
.
#18:51
":regexp_quote(string)";
" => string with all of the regular expression special characters quoted with %";
string = args[1];
quoted = "";
while (m = rmatch(string, "[][$^%.*+?].*"))
quoted = ("%" + string[m[1]..m[2]]) + quoted;
string = string[1..m[1] - 1];
endwhile
return string + quoted;
.
#18:52
"Takes the output from connection_name() and returns just the host string portion of it.  Assumes you are using bsd_network style connection names.";
s = args[1];
m = match(s, "^.* %(from%|to%) %([^, ]+%)");
return m ? substitute("%2", m) | "";
.
#18:53
"This is the function that should actually be called to get the host name from a connection name.  The archwizard should change _bsd so as to be calling the verb appropriate for his/her network interface.";
return this:connection_hostname_bsd(@args);
.
#18:54
"$string_utils:from_value(value [, quote_strings = 0 [, list_depth = 1]])";
"Print the given value into a string.";
{value, ?quote_strings = 0, ?list_depth = 1} = args;
if (typeof(value) == LIST)
if (value)
if (list_depth)
result = "{" + this:from_value(value[1], quote_strings, list_depth - 1);
for v in (listdelete(value, 1))
$command_utils:suspend_if_needed(0);
result = tostr(result, ", ", this:from_value(v, quote_strings, list_depth - 1));
endfor
return result + "}";
else
return "{...}";
endif
else
return "{}";
endif
elseif (quote_strings)
if (typeof(value) == STR)
result = "\"";
while (q = index(value, "\"") || index(value, "\\"))
$command_utils:suspend_if_needed(0);
if (value[q] == "\"")
q = min(q, index(value + "\\", "\\"));
endif
result = ((result + value[1..q - 1]) + "\\") + value[q];
value = value[q + 1..length(value)];
endwhile
return (result + value) + "\"";
elseif (typeof(value) == ERR)
return $code_utils:error_name(value);
else
return tostr(value);
endif
else
return tostr(value);
endif
.
#18:55
":first_word(string) => {first word, rest of string} or {}";
rest = args[1];
"...trim leading blanks...";
rest[1..match(rest, "^ *")[2]] = "";
if (!rest)
return {};
endif
quote = 0;
token = "";
pattern = " +%|\\.?%|\"";
while (m = match(rest, pattern))
"... find the next occurence of a special character, either";
"... a block of spaces, a quote or a backslash escape sequence...";
char = rest[m[1]];
token = token + rest[1..m[1] - 1];
if (char == " ")
rest[1..m[2]] = "";
return {token, rest};
elseif (char == "\"")
"... beginning or end of quoted string...";
"... within a quoted string spaces aren't special...";
pattern = (quote = !quote) ? "\\.?%|\"" | " +%|\\.?%|\"";
elseif (m[1] < m[2])
"... char has to be a backslash...";
"... include next char literally if there is one";
token = token + rest[m[2]];
endif
rest[1..m[2]] = "";
endwhile
return {token + rest, ""};
.
#18:56
":common(first,second) => length of longest common prefix";
first = args[1];
second = args[2];
r = min(length(first), length(second));
l = 1;
while (r >= l)
h = (r + l) / 2;
if (first[l..h] == second[l..h])
l = h + 1;
else
r = h - 1;
endif
endwhile
return r;
.
#18:57
"name_list/namec_list/etc(<obj-list>[, @<args>)";
"Creates an english list out of the titles of the objects in <obj-list>.  Optional <args> are passed on to $string_utils:english_list.";
"a \"c_\" in the verb name means that the first (and only the first) name in the list will be capitalized.";
{objlist, @listargs} = args;
namspec = {"", "d", "i"}[index("ndi", verb[1])];
if ((i = index(verb, "_")) && (verb[i - 1] == "c"))
namspec = namspec + "c";
endif
return $language:name_list(objlist, listargs, namspec);
.
#18:58
"Pronoun (and other things) substitution. See 'help pronouns' for details.";
"syntax:  $string_utils:pronoun_sub(text[,who[,thing[,location]]])";
"%s,%o,%p,%q,%r    => <who>'s pronouns.  <who> defaults to player.";
"%n,%d,%i,%t,%l,%% => <who>, dobj, iobj, <thing>, location and %";
"<thing> defaults to caller; <location> defaults to who.location";
"%S,%O,%P,%Q,%R, %N,%D,%I,%T,%L have corresponding capitalized substitutions.";
" %[#n], %[#d], ...  =>  <who>, dobj, etc.'s object number";
"%(foo) => <who>.foo and %(Foo) => <who>.foo capitalized. %[dfoo] => dobj.foo, etc..";
"%<foo> -> whatever <who> does when normal people foo. This is determined by calling :verb_sub() on the <who>.";
"%<d:foo> -> whatever <dobj> does when normal people foo.";
who = (length(args) >= 2) ? args[2] | player;
thing = (length(args) >= 3) ? args[3] | caller;
where = (length(args) >= 4) ? args[4] | (valid(who) ? who.location | $nothing);
set_task_perms($no_one);
if (typeof(args[1]) == LIST)
plines = {};
for line in (args[1])
plines = {@plines, this:(verb)(line, who, thing, where)};
endfor
return plines;
endif
old = tostr(args[1]);
new = "";
objspec = "nditl";
objects = {who, (length(args) >= 5) ? args[5] | dobj, (length(args) >= 6) ? args[6] | iobj, thing, where};
prnspec = "sopqrSOPQR";
prprops = {"ps", "po", "pp", "pq", "pr", "Ps", "Po", "Pp", "Pq", "Pr"};
oldlen = length(old);
while ((prcnt = index(old, "%")) && (prcnt < oldlen))
s = old[k = prcnt + 1];
if ((s == "<") && (gt = index(old[k + 2..length(old)], ">")))
"handling %<verb> ";
gt = (gt + k) + 1;
vb = old[k + 1..gt - 1];
vbs = who;
if ((length(vb) > 2) && (vb[2] == ":"))
" %<d:verb>";
vbs = objects[index(objspec, vb[1]) || 1];
vb = vb[3..length(vb)];
endif
vb = $object_utils:has_verb(vbs, "verb_sub") ? vbs:verb_sub(vb) | this:(verb)(vb, vbs);
new = (new + old[1..prcnt - 1]) + vb;
k = gt;
else
cp_args = {};
ostr = old[1..prcnt - 1];
if (brace = index("([", s))
if (!(w = index(old[k + 1..oldlen], ")]"[brace])))
return new + old;
else
p = old[prcnt + 2..(k = k + w) - 1];
if (brace == 1)
"%(property)";
cp_args = {who, p};
elseif (p[1] == "#")
"%[#n] => object number";
s = (o = index(objspec, p[2])) ? tostr(objects[o]) | (("[" + p) + "]");
elseif (!(o = index(objspec, p[1])))
s = ("[" + p) + "]";
else
" %[dproperty] ";
cp_args = {objects[o], p[2..w - 1], strcmp(p[1], "a") < 0};
endif
endif
elseif (o = index(objspec, s))
cp_args = {objects[o], "", strcmp(s, "a") < 0};
if (rmatch(ostr, "%<a $", 1))
ostr = ostr[1..prcnt - 3];
cp_args[2] = "iname";
elseif (rmatch(ostr, "%<A $", 1))
ostr = ostr[1..prcnt - 3];
cp_args[2] = "iname";
cp_args[3] = 1;
elseif (rmatch(ostr, "%<the $", 1))
ostr = ostr[1..prcnt - 5];
cp_args[2] = "dname";
elseif (rmatch(ostr, "%<The $", 1))
ostr = ostr[1..prcnt - 5];
cp_args[2] = "dname";
cp_args[3] = 1;
endif
elseif (w = index(prnspec, s, 1))
cp_args = {who, prprops[w]};
elseif (s == "#")
s = tostr(who);
elseif (s != "%")
s = "%" + s;
endif
new = (new + ostr) + ((!cp_args) ? s | ((typeof(sub = $string_utils:_cap_property(@cp_args)) != ERR) ? sub | (("%(" + tostr(sub)) + ")")));
endif
old = old[k + 1..oldlen];
oldlen = oldlen - k;
endwhile
return new + old;
.
#18:59
"name_and_number_list(<list>[, @<args>])";
"<list> is a list of objects; an english list will be constructed of their names and numbers in the form \"Munchkin (#xxx)\".  <args> are the same as the optional args to $string_utils:english_list.";
return $string_utils:english_list($list_utils:map_arg($string_utils, verb[1..rindex(verb, "_") - 1], args[1]), @args[2..length(args)]);
.
#18:60
"Usage:  $string_utils:to_list(str <subject>[, str <separator>])";
"";
"Returns a list of those substrings of <subject> separated by <separator>.  <separator> defaults to space.";
"";
"Differs from $string_utils:explode in that";
"";
"  * <separator> can effectively be longer than one character.";
"  * runs of <separator> are not treated as single occurrences.";
"";
"$string_utils:to_list  (string, separator) is the inverse of";
"$string_utils:from_list(list  , separator)";
{subject, ?separator = " "} = args;
breaklen = length(separator);
parts = {};
while (i = index(subject, separator))
parts = {@parts, subject[1..i - 1]};
subject = subject[i + breaklen..$];
endwhile
return {@parts, subject};
.
#18:61
"Given a string, return the integer of the ASCII code of the first character in the string.  Returns E_INVARG if passed a character that isn't in $string_utils known character set.";
"$string_utils._character_set* need to be updated when the server's range of valid characters changes.";
return decode_binary(args[1][1], 1)[1];
pos = index(this._character_set, args[1][1], 1);
if (pos)
return this._character_set_in_ascii[pos];
else
return E_INVARG;
endif
.
#18:62
"Given a string, return a two character hexadecimal string of the ASCII code of the first character in the string.  Canonical form capitalizes A-F.  Returns E_INVARG if passed a character that isn't in $string_utils known character set.";
"$string_utils._character_set* need to be updated when the server's range of valid characters changes.";
pos = index(this._character_set, args[1][1], 1);
if (pos)
return this._character_set_in_hex_ascii[pos];
else
return E_INVARG;
endif
.
#18:63
"Given an integer in the MOO subset of the ASCII character set, return a one-character string of that character.  Returns the null string if the integer is not representable in a MOO string.";
pos = args[1] in this._character_set_in_ascii;
if (pos)
return this._character_set[pos];
else
return "";
endif
.
#18:64
"$string_utils:hex_ascii_to_character(hexstring)";
"Where hexstring is a two-character string of characters in hexadecimal.";
"Return a one-character string of the character represented by hexstring.  Returns the null string if hexstring is not representable in a MOO string.";
pos = args[1] in this._character_set_in_hex_ascii;
if (pos)
return this._character_set[pos];
else
return "";
endif
.
#18:65
"$string_utils:print_truncated(value, len, finish-string)";
"Print the given value into a string of length len.";
"If the final string is too long, finish-string will be used to replace the last few characters.  The null string, of course, replaces nothing.";
val = this:print_truncated_recursive(args[1], args[2] + 1);
if (length(val) > args[2])
val[(args[2] - length(args[3])) + 1..args[2] + 1] = args[3];
endif
return val;
return $string_utils:left(this:print_truncated_recursive(args[1], args[2]), args[2])[1..args[2] - length(args[3])] + args[3];
.
#18:66
"$string_utils:print(value)";
"Print the given value into a string. == from_value(value,1,-1)";
value = args[1];
len = args[2];
if (!len)
return "";
endif
if (typeof(value) == LIST)
if (value)
result = "{" + this:(verb)(value[1], len - 1);
while ((value = listdelete(value, 1)) && (length(result) < len))
result = tostr(result, ", ", this:(verb)(value[1], len - length(result)));
endwhile
result = result + "}";
else
result = "{}";
endif
elseif (typeof(value) == STR)
result = "\"";
while (q = index(value, "\"") || index(value, "\\"))
if (value[q] == "\"")
q = min(q, index(value + "\\", "\\"));
endif
result = ((result + value[1..q - 1]) + "\\") + value[q];
value = value[q + 1..length(value)];
endwhile
result = (result + value) + "\"";
elseif (typeof(value) == ERR)
result = $code_utils:error_name(value);
else
result = tostr(value);
endif
return (length(result) > len) ? result[1..len] | result;
.
#18:67
"Usage:  nn(obj <object>)";
if (verb == "nn")
verb = "name_and_number";
endif
if ((!args) || (typeof(args[1]) != OBJ))
return pass(@args);
endif
return valid(what = args[1]) ? what:(verb)() | tostr(`{"nothing", "ambiguous match", "failed match"}[-tonum(what)] ! E_RANGE => "nothing"', " (", what, ")");
"Metadata 202106";
"Last-Modify: {875335192, \"Erik\", #74, \"JHM\"}";
.
#18:68
"next_index(str <str1>, str <str2>, num <previous>[, num <case-matters>])";
"find first occurrence of <str2> in <str1> that begins after <previous>.";
{str1, str2, prev, ?casematters = 0} = args;
str1[1..prev] = "";
return (i = index(str1, str2, casematters)) ? i + prev | 0;
"Metadata 202106";
"Last-Modify: {875335394, \"Erik\", #74, \"JHM\"}";
.
#18:69
if ((valid(lit = $string_utils:literal_object(args[1])) && $object_utils:isa(lit, $room)) || (lit == $nowhere))
return lit;
else
"NEW VERSION";
return $room:match_type_object(args[1]);
endif
"OLD VERSION";
rooms = $object_utils:descendents($room);
matches = $string_utils:match(args[1], rooms, "name", rooms, "aliases");
"See if it matches without the first word (maybe a 'the' or an 'a')";
if (matches == $failed_match)
space = index(args[1], " ");
if (space && (length(args[1]) > (space + 1)))
matches = $string_utils:match(args[1][space + 1..length(args[1])], rooms, "name", rooms, "aliases");
endif
endif
return matches;
"Metadata 202106";
"Last-Modify: {863659433, \"Erik\", #74, \"JHM\"}";
.
#18:70
"is_uppercase(string)";
"return true iff the string contains no lower-case letters";
for i in [1..length(s = args[1])]
if (index("abcdefghijklmnopqrstuvwxyz", s[i], 1))
return 0;
endif
endfor
return 1;
.
#18:71
"index_all(string,target[,delimited]) -- returns list of positions of target in string.";
"Usage: $string_utils:index_all(<string>,<pattern>[,<delimited>])";
"       $string_utils:index_all(\"aaabacadae\",\"a\")";
"       $string_utils:index_all(\"food foo, fools not foo.\", \"foo\", 1)";
if ((length(args) < 2) || (length(args) > 3))
return E_ARGS;
elseif ((typeof(args[1]) != STR) || typeof(args[2] != STR))
return E_TYPE;
else
line = args[1];
pattern = args[2];
delimited = (length(args) == 3) && args[3];
where = {};
place = -1;
length = length(line);
next = 0;
while ((place = delimited ? this:index_d(line[next + 1..length], pattern) | index(line[next + 1..length], pattern)) != 0)
where = {@where, place + next};
next = ((place + next) + length(pattern)) - 1;
endwhile
return where;
endif
.
#18:72
"Usage: columnize_with_headers(headers, fields) => lines";
"Format the m * n array of strings under n headers.";
"Doesn't truncate strings.";
headers = args[1];
fields = args[2];
bars = {};
maxlens = {};
lines = {};
for h in [1..length(headers)]
header = headers[h];
maxlen = length(header);
bars = {@bars, $string_utils:space(maxlen, "-")};
for row in (fields)
maxlen = max(maxlen, length(row[h]));
endfor
maxlens = {@maxlens, maxlen};
endfor
for row in ({headers, bars, @fields, bars})
line = "";
for i in [1..length(headers)]
line = tostr(line, $string_utils:left(row[i], maxlens[i]), "  ");
endfor
lines = {@lines, $string_utils:trimr(line)};
endfor
return lines;
.
#18:73
"columnize (items, n [, width]) - Turn a one-column list of items into an n-column list. 'width' is the last character position that may be occupied; it defaults to a standard screen width. Example: To tell the player a list of numbers in three columns, do 'player:tell_lines ($string_utils:columnize ({1, 2, 3, 4, 5, 6, 7}, 3));'.";
{items, n, ?width = 79} = args;
height = ((length(items) + n) - 1) / n;
items = {@items, @$list_utils:make((height * n) - length(items), "")};
colwidths = {};
for col in [1..n - 1]
colwidths = listappend(colwidths, 1 - (((width + 1) * col) / n));
endfor
result = {};
for row in [1..height]
line = tostr(items[row]);
for col in [1..n - 1]
line = tostr(this:left(line, colwidths[col]), " ", items[row + (col * height)]);
endfor
result = listappend(result, line[1..min(length(line), width)]);
$command_utils:suspend_if_needed(0);
endfor
return result;
.
#18:74
"if args[1] contains any non-whitespace characters, return it (a true value); otherwise, return the empty string.";
return match(args[1], "[^ 	]") ? args[1] | "";
.
#18:75
"is_uppercase(string)";
"return true iff the string contains no upper-case letters";
for i in [1..length(s = args[1])]
if (index("ABCDEFGHIJKLMNOPQRSTUVWXYZ", s[i], 1))
return 0;
endif
endfor
return 1;
.
#18:76
"$string_utils:columnize_no_truncate(items, columns[, linelen])";
"";
"Return a list of strings formed by arranging the given strings into the given number of columns, to fit into <linelen> characters.  (default is 79.)  This version differs from $string_utils:columnize in one way:  Strings that are too long to fit in their columns will be moved to the end of the list and displayed in full, rather than truncated to fit.";
"";
"Original code by Hagbard.";
{items, columns, ?linelen = 79} = args;
short_list = long_list = {};
cut = linelen / columns;
for item in (items)
if (length(item) <= cut)
short_list = {@short_list, item};
else
long_list = {@long_list, item};
endif
endfor
return {@$string_utils:columnize(short_list, columns), @long_list};
.
#18:77
"Usage:  :print_with_names(value)";
"";
"Print the given value into a string, with objects as `#nnn (object name)'.  This makes the result not a valid MOO value, but may be more readable.";
value = args[1];
if (typeof(value) == LIST)
if (value)
result = "{" + this:(verb)(value[1]);
for val in (value[2..$])
result = tostr(result, ", ", this:(verb)(val));
endfor
return result + "}";
else
return "{}";
endif
elseif (typeof(value) == OBJ)
return tostr(value, " ", valid(value) ? ("(" + value:name()) + ")" | ((a = $list_utils:assoc(value, {{#-1, "<$nothing>"}, {#-2, "<$ambiguous_match>"}, {#-3, "<$failed_match>"}})) ? a[2] | "<invalid>"));
else
return toliteral(value);
endif
"Copied from string utilities (#20):print by Erik (#74) Sun Mar 10 16:33:50 1996 EST";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {875335589, \"Erik\", #74, \"JHM\"}";
.
#18:78
"$string_utils:match_stringlist(string, {list of strings})";
"The list of strings should be just that, a list of strings.  The first string is matched against the list of strings.";
"If it exactly matches exactly one of them, the index of the match is returned. If it exactly matches more than one of them, $ambiguous_match is returned.";
"If there are no exact matches, then partial matches are considered, ones in which the given string is a prefix of one of the strings.";
"Again, if exactly one match is found, the index of that string is returned, and if more than one match is found, $ambiguous match is returned.";
"Finally, if there are no exact or partial matches, then $failed_match is returned.";
{subject, stringlist} = args;
if ((!subject) || (!stringlist))
return $nothing;
endif
match = 0;
"First check for exact matches.";
for i in [1..length(stringlist)]
if (subject == stringlist[i])
if (match)
return $ambiguous_match;
else
match = i;
endif
endif
endfor
"Now return a match, or $ambiguous, or check for partial matches.";
if (match)
return match;
endif
for i in [1..length(stringlist)]
if (index(stringlist[i], subject) == 1)
if (match)
return $ambiguous_match;
else
match = i;
endif
endif
endfor
if (match)
return match;
else
return $nothing;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {875336126, \"Erik\", #74, \"JHM\"}";
.
#18:79
"name_list/namec_list/etc(<obj-list>[, @<args>)";
"Creates an english list out of the titles of the objects in <obj-list>.  Optional <args> are passed on to $string_utils:english_list.";
"a \"c_\" in the verb name means that the first (and only the first) name in the list will be capitalized.";
v = verb[1..rindex(verb, "_") - 1];
first = args[1] ? {args[1][1]:(v)()} | {};
if (v[c = (i = index(v, "_")) ? i - 1 | length(v)] == "c")
v[c..c] = "";
endif
return $string_utils:english_list({@first, @$list_utils:map_verb(args[1] ? listdelete(args[1], 1) | {}, v)}, @listdelete(args, 1));
"Copied from string utilities (#20):title_list by splat (#3024) Wed Mar 27 03:09:47 1996 EST";
.
#18:80
":glob_to_regexp(string)";
" => string with all of the regular expression special characters quoted with % and the glob special characters converted to equivalent regexp ones";
string = args[1];
converted = "$";
while (m = rmatch(string, "[]$^%[.+?*\\].*"))
c = string[m[1]];
if (c == "?")
c = ".";
elseif (c == "*")
c = ".*";
elseif (c == "\\")
c = "";
"trailing $ means we can't lose on range here";
if (converted[1] == ".")
if (converted[2] == "*")
converted[1] = "%";
else
converted[1..1] = "%?";
endif
endif
else
c = "%" + c;
endif
converted = (c + string[m[1] + 1..m[2]]) + converted;
string = string[1..m[1] - 1];
endwhile
if (`converted[$ - 2..$ - 1] ! E_RANGE' == ".*")
converted[$ - 2..$] = "";
endif
if ((!string) && (`converted[1..2] ! E_RANGE' == ".*"))
return converted[3..$];
"remove '^.*' -- string is always left null in this case";
endif
return ("^" + string) + converted;
"Copied from string utilities (#20):regexp_quote by splat (#3024) Sat Apr  6 04:16:41 1996 EST";
.
#18:81
"Usage: xglob_to_regexp (string)";
"=> An equivalent regular expression to the extended glob 'string'.  Extended globs allow the \"[]\" construct as well as \"*\" and \"?\".";
"";
"Note: Backslash-quoting within [] doesn't work.  There's no obvious way to create legal MOO regexps from xglobs with quoting between [].  Clever positioning still works, though.";
{string} = args;
(typeof(string) == STR) || raise(E_TYPE);
if (string == "*")
return "";
"handle a simple, common case";
endif
conv = "^";
while result (match(string, "[]^[.$%?*+\\]"))
c = string[p = result[1]];
if (c == "?")
c = ".";
elseif (c == "*")
c = ".*";
elseif (c == "\\")
string[p..p] = "";
c = `string[p] ! E_RANGE => ""';
if (c && index("%[]^$.?*+", c))
c = "%" + c;
endif
elseif (c == "[")
conv = conv + string[1..p];
string[1..p] = "";
try
p = (string[1] == "^") ? 2 | 1;
p = (string[p] == "]") ? p + 1 | p;
if (tmp = index(string[p..$], "]"))
p = (p + tmp) - 1;
c = "]";
else
string[$ + 1];
"get out to the except clause";
endif
except (E_RANGE)
raise(E_INVARG, "xglob: unclosed []");
endtry
else
c = "%" + c;
endif
conv = (conv + string[1..p - 1]) + c;
string[1..p] = "";
endwhile
if (`conv[2..3] ! E_RANGE' == ".*")
conv[1..3] = "";
endif
if ((!string) && (`conv[$ - 1..$] ! E_RANGE' == ".*"))
return conv[1..$ - 2];
"string is null if the last character got converted, and \".*\" can't appear outside closed \"[]\" if the \".\" is quoted";
endif
return (conv + string) + "$";
.
#18:82
"$string_utils:match(string [, obj-list, prop-name]*)";
"Each obj-list should be a list of objects or a single object, which is treated as if it were a list of that object.  Each prop-name should be string naming a property on every object in the corresponding obj-list.  The value of that property in each case should be either a string or a list of strings.";
"The argument string is matched against all of the strings in the property values.";
"If it exactly matches exactly one of them, the object containing that property is returned.  If it exactly matches more than one of them, $ambiguous_match is returned.";
"If there are no exact matches, then partial matches are considered, ones in which the given string is a prefix of some property string.  Again, if exactly one match is found, the object with that property is returned, and if there is more than one match, a list of all of them is returned.";
"Finally, if there are no exact or partial matches, then $failed_match is returned.";
subject = args[1];
tests = args[2..length(args)];
if (subject == "")
return $nothing;
endif
"exact matches first";
exacts = {};
ambigs = {};
for i in [1..length(tests) / 2]
prop_name = tests[2 * i];
for object in ((typeof(olist = tests[(2 * i) - 1]) == LIST) ? olist | {olist})
if (valid(object))
if (typeof(str_list = object.(prop_name)) != LIST)
str_list = {str_list};
endif
if (subject in str_list)
exacts = setadd(exacts, object);
else
for string in (str_list)
if (index(string, subject) != 1)
else
ambigs = setadd(ambigs, object);
endif
endfor
endif
endif
endfor
endfor
allmatches = $list_utils:remove_duplicates({@exacts, @ambigs});
return allmatches || $failed_match;
"Metadata 202106";
"Last-Modify: {881349809, \"Ken\", #75, \"JHM\"}";
.
#18:83
"$string_utils:explode_match(result of match)";
"-> list of strings, one for each matching group in the match in group order.";
"unmatched groups omitted.";
exploded = {};
{result} = args;
if (!result)
return exploded;
else
{begin, end, groups, original} = result;
for group in (groups)
{beg, end} = group;
if (beg != 0)
exploded = {@exploded, original[beg..end]};
endif
endfor
return exploded;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {853875128, \"Ken\", #75, \"JHM\"}";
.
#18:84
"args[1] is a string.  'increments' the string by one. E.g., aaa => aab, aaz => aba.  empty string => a, zzz => aaaa.";
"args[2] is optional alphabet to use instead of $string_utils.alphabet.";
{s, ?alphabet = this.alphabet} = args;
index = length(s);
if (!s)
return alphabet[1];
elseif (s[$] == alphabet[$])
return this:incr_alpha(s[1..index - 1], alphabet) + alphabet[1];
else
t = index(alphabet, s[index]);
return s[1..index - 1] + alphabet[t + 1];
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {908141393, \"Nosredna\", #72, \"JHM\"}";
.
#19:0
"make_exit(spec, source, dest[, owner[, parent]])";
"Calls owner:_create to decide if to use $recycler or use built-in.";
"Returns the object number as a list if successful, 0 if not.";
"if the fourth argument is an object number, we just call its :_create verb to create the exit.";
"if the fifth argument is supplied, it is the object to use as the exit's parent.";
set_task_perms(caller_perms());
spec = args[1];
source = args[2];
dest = args[3];
if (length(args) > 4)
exitparent = args[5];
else
exitparent = $exit;
endif
if (length(args) > 3)
if (typeof(args[4]) == OBJ)
exit = args[4]:_create(exitparent);
else
exit = $quota_utils:bi_create(exitparent);
endif
else
exit = $recycler:_create(exitparent);
endif
if (typeof(exit) == ERR)
player:tell(exit);
return;
endif
$building_utils:set_names(exit, spec);
exit.source = source;
exit.dest = dest;
source_ok = source:add_exit(exit);
dest_ok = dest:add_entrance(exit);
move(exit, $nothing);
via = $string_utils:from_value(setadd(exit.aliases, exit:name()), 1);
if (source_ok)
player:tell("Exit from ", source:name(), " (", source, ") to ", dest:name(), " (", dest, ") via ", via, " created with id ", exit, ".");
if (!dest_ok)
player:tell("However, I couldn't add ", exit, " as a legal entrance to ", dest:name(), ".  You may have to get its owner, ", dest.owner:name(), " to add it for you.");
endif
return {exit};
elseif (dest_ok)
player:tell("Exit to ", dest:name(), " (", dest, ") via ", via, " created with id ", exit, ".  However, I couldn't add ", exit, " as a legal exit from ", source:name(), ".  Get its owner, ", source.owner:name(), " to add it for you.");
return {exit};
elseif (0)
"I considered using this for awhile. -- Lambda";
player:tell("Exit to ", dest:name(), " (", dest, ") via ", via, " created with id ", exit, ".  However, I couldn't add ", exit, " as EITHER a legal exit from ", source:name(), " OR as a legal entrance to ", dest:name(), ".  Get their owners, ", source.owner:name(), " and ", dest.owner:name(), ", respectively, to add it for you.");
return 0;
else
" if (recreate_enabled)";
"   $recycler:_recycle(exit);";
" else";
"   recycle(exit);";
" endif";
player:_recycle(exit);
player:tell("I couldn't add a new exit as EITHER a legal exit from ", source:name(), " OR as a legal entrance to ", dest:name(), ".  Get their owners, ", source.owner:name(), " and ", dest.owner:name(), ", respectively, to add it for you.");
return 0;
endif
.
#19:1
"$building_utils:set_names(object, spec)";
set_task_perms(caller_perms());
object = args[1];
names = this:parse_names(args[2]);
name = names[1] || object.name;
if ("" in {name, names[1], @names[2]})
return E_INVARG;
endif
return object:set_name(name) && object:set_aliases(names[2]);
.
#19:2
":recreate(object,newparent) -- effectively recycle and recreate the specified object as a child of parent.  Returns true iff successful.";
object = args[1];
parent = args[2];
who = caller_perms();
if (!(valid(object) && ((parent == #-1) || valid(parent))))
return E_INVARG;
elseif (who.wizard)
"no problemo";
elseif ((who != object.owner) || (!(((parent == #-1) || parent.f) || (who == parent.owner))))
return E_PERM;
endif
"Chparent any children to their grandparent instead of orphaning them horribly.  Have to do the chparent with wizperms, in case the children are owned by others, so do this before set_task_perms.";
for c in (children(object))
chparent(c, parent(object));
endfor
set_task_perms(who);
if ($object_utils:has_verb(object, "recycle"))
object:recycle();
endif
chparent(object, #-1);
for p in (properties(object))
delete_property(object, p);
endfor
for v in (verbs(object))
delete_verb(object, 1);
endfor
for item in (object.contents)
move(item, #-1);
endfor
chparent(object, parent);
object.name = "";
if ($object_utils:has_verb(parent, "initialize"))
object:initialize();
endif
object.r = 0;
object.f = 0;
object.w = 0;
return 1;
"Metadata 202106";
"Last-Modify: {863646979, \"Erik\", #74, \"JHM\"}";
.
#19:3
"$building_utils:parse_names(spec)";
"Return {name, {alias, alias, ...}} from name,alias,alias or name:alias,alias";
spec = args[1];
if (!(colon = index(spec, ":")))
aliases = $string_utils:explode(spec, ",");
name = aliases ? aliases[1] | "";
else
aliases = $string_utils:explode(spec[colon + 1..length(spec)], ",");
name = spec[1..colon - 1];
endif
return {name, $list_utils:map_arg($string_utils, "trim", aliases)};
.
#19:4
"provide string for audit of object (args[1]), in @prospectus format if there is a second argument and it's true.";
{o, ?pros = 0} = args;
if (pros)
kids = 0;
for k in (children(o))
$command_utils:suspend_if_needed(0);
if (k.owner != o.owner)
kids = 2;
elseif (kids == 0)
kids = 1;
endif
endfor
"don't bother checking permissions--if they really wanted to know the number of verbs on the object, they could find out.";
v = verbs(o);
if (v)
vstr = tostr("[", $string_utils:right(length(v), 3), "] ");
else
vstr = "      ";
endif
if (o.r && o.f)
r = "f";
elseif (o.r)
r = "r";
elseif (o.f)
r = "F";
else
r = " ";
endif
vstr = tostr(" kK"[kids + 1], r, $building_utils:audit_object_category(o), vstr);
else
vstr = "";
endif
if (valid(o.location))
loc = ((((o.location.owner == o.owner) ? " " | "*") + "[") + o.location.name) + "]";
elseif ($object_utils:has_property(o, "dest") && $object_utils:has_property(o, "source"))
if (typeof(o.source) != OBJ)
source = " <non-object> ";
elseif (!valid(o.source))
source = "<invalid>";
else
source = o.source.name;
if (o.source.owner != o.owner)
source = "*" + source;
endif
endif
if (typeof(o.dest) != OBJ)
destin = " <non-object> ";
elseif (!valid(o.dest))
destin = "<invalid>";
else
destin = o.dest.name;
if (o.dest.owner != o.owner)
destin = "*" + destin;
endif
endif
srclen = (length(source) < 20) ? length(source) | 19;
destlen = (length(destin) < 20) ? length(destin) | 19;
loc = ((" " + source[1..srclen]) + "->") + destin[1..destlen];
elseif ($object_utils:isa(o, $room))
loc = "";
for x in (o.entrances)
if (((((typeof(x) == OBJ) && valid(x)) && (x.owner != o.owner)) && $object_utils:has_property(x, "dest")) && (x.dest == o))
loc = ((loc + (loc ? ", " | "")) + "<-*") + x.name;
endif
endfor
else
loc = " [Nowhere]";
endif
if (length(loc) > 41)
loc = loc[1..37] + "..]";
endif
if ($quota_utils == $quota_utils.byte)
vstr = tostr(this:size_string(o.object_size[1]), " ", vstr);
name_field_len = 26;
else
name_field_len = 30;
endif
namelen = (length(o.name) < name_field_len) ? length(o.name) | (name_field_len - 1);
return tostr(vstr, $string_utils:right(o, length(tostr(max_object()))), " ", $string_utils:left(o.name[1..namelen], name_field_len), loc);
"Metadata 202106";
"Last-Modify: {875479347, \"Erik\", #74, \"JHM\"}";
.
#19:5
cl = {$player, $room, $exit, $note, $container, $thing};
cs = {"p", "R", "E", "N", "C", "T"};
if (is_player(OBJ = args[1]))
return "P";
endif
while (valid(OBJ))
if (i = OBJ in cl)
return cs[i];
endif
OBJ = parent(OBJ);
endwhile
return " ";
.
#19:6
size = args[1];
if (!size)
return " ???";
elseif (size < 1000)
return " <1K";
elseif (size < 1000000)
return tostr($string_utils:right(size / 1000, 3), "K");
else
return tostr($string_utils:right(size / 1000000, 3), "M");
endif
.
#19:7
":do_audit(who, start, end, match)";
"audit who, with objects from start to end that match 'match'";
":do_prospectus(...)";
"same, but with verb counts";
"the set_task_perms is to make the task owned by the player. There are no other security aspects";
who = args[1];
start = args[2];
end = args[3];
match = args[4];
set_task_perms(caller_perms());
player:tell(tostr("Objects owned by ", who.name, " (from #", start, " to #", end, match ? " matching " + match | "", ")", ":"));
count = bytes = 0;
pros = verb == "do_prospectus";
if (typeof(who.owned_objects) == LIST)
for o in (who.owned_objects)
$command_utils:suspend_if_needed(0);
if (!player:is_listening())
return;
endif
if ((tonum(o) >= start) && (tonum(o) <= end))
didit = this:do_audit_item(o, match, pros);
count = count + didit;
if ((didit && ($quota_utils == $quota_utils.byte)) && $object_utils:has_property(o, "object_size"))
bytes = bytes + o.object_size[1];
endif
endif
endfor
else
for i in [start..end]
$command_utils:suspend_if_needed(0);
o = toobj(i);
if (valid(o) && (o.owner == who))
didit = this:do_audit_item(o, match, pros);
count = count + didit;
if ((didit && ($quota_utils == $quota_utils.byte)) && $object_utils:has_property(o, "object_size"))
bytes = bytes + o.object_size[1];
endif
endif
endfor
endif
player:tell($string_utils:left(tostr("-- ", count, " object", (count == 1) ? "." | "s.", ($quota_utils == $quota_utils.byte) ? tostr("  Total bytes: ", $string_utils:group_number(bytes), ".") | ""), player:linelen() - 1, "-"));
.
#19:8
o = args[1];
match = args[2];
pros = args[3];
found = match ? 0 | 1;
names = {o.name, @o.aliases};
while (names && (!found))
if (index(names[1], match) == 1)
found = 1;
endif
names = listdelete(names, 1);
endwhile
if (found)
"From Dred---don't wrap long lines.";
len = player:linelen();
line = $building_utils:object_audit_string(o, pros);
player:tell((length(line) >= len) ? line[1..len] | line);
return 1;
endif
return 0;
"Metadata 202106";
"Last-Modify: {875479319, \"Erik\", #74, \"JHM\"}";
.
#20:0
if (args && $code_utils:parse_verbref(what = args[1]))
"... hey wow, I found it!...";
return {what};
else
return {};
endif
.
#20:1
"Help facility for verbs that people have bothered to document.  If the argument is a verb specification, this retrieves the code and prints any documentation lines that might be at the beginning.  Returns true if the arg can actually be interpreted as a verb specification, whether or not it is a correct one.";
set_task_perms(caller_perms());
if (!(spec = $code_utils:parse_verbref(args[1])))
return 0;
elseif ($command_utils:object_match_failed(object = $string_utils:match_object(spec[1], player.location), spec[1]))
return 1;
elseif (!(hv = $object_utils:has_verb(object, spec[2])))
return "That object does not define that verb.";
elseif (typeof(verbdoc = $code_utils:verb_documentation(object = hv[1], spec[2])) == ERR)
return tostr(verbdoc);
elseif (typeof(info = `verb_info(object, spec[2]) ! ANY') == ERR)
return tostr(info);
else
objverb = tostr(object:name(), "(", object, "):", strsub(info[3], " ", "/"));
if (verbdoc)
if ((usage = $code_utils:verb_usage(object, spec[2])) ? (length(verbdoc) > length(usage)) && (!verbdoc[length(usage) + 1]) | (((length(verbdoc) >= 3) && (!verbdoc[2])) || (length(verbdoc) == 1)))
return this:to_jtext(verbdoc, objverb);
endif
"return {tostr(\"Information about \", objverb), \"----\", @verbdoc};";
vgroup = {};
for line in (verbdoc)
para = {$jtext.paragraph, line};
vgroup = {@vgroup, para};
endfor
return vgroup;
else
return {{$jtext.paragraph, "(No documentation.)"}};
"return tostr(\"No information about \", objverb);";
endif
endif
.
#20:2
set_task_perms(caller_perms());
if (!(spec = $code_utils:parse_verbref(args[1])))
return E_INVARG;
elseif ($command_utils:object_match_failed(object = $string_utils:match_object(spec[1], player.location), spec[1]))
return E_INVARG;
elseif (!(hv = $object_utils:has_verb(object, spec[2])))
return E_VERBNF;
else
return $code_utils:verb_documentation(hv[1], spec[2]);
endif
.
#20:3
set_task_perms(caller_perms());
if (!(spec = $code_utils:parse_verbref(args[1])))
return E_INVARG;
elseif ($command_utils:object_match_failed(object = $string_utils:match_object(spec[1], player.location), spec[1]))
return E_INVARG;
elseif (!(hv = $object_utils:has_verb(object, spec[2])))
return E_VERBNF;
elseif (typeof(e = $code_utils:set_verb_documentation(hv[1], spec[2], args[2])) == ERR)
return e;
else
return 1;
endif
.
#20:4
return E_INVARG;
.
#20:5
"Copied from feature object help database (#692):index by Erik (#37) Tue Apr 26 20:14:53 1994 EDT";
"{\"*index*\" [, title]}";
"This produces a columnated list of topics in this help db, headed by title.";
$command_utils:suspend_if_needed(0);
return {};
title = args[1] ? args[1][1] | tostr(this:name(), " (", this, ")");
su = $string_utils;
return {{$jtext.header, 3, title}, {$jtext.paragraph, "This database can't be indexed."}};
return {{$jtext.header, 3, title}, {$jtext.plaintext, @this:columnize(@this:sort_topics(this:find_topics())), ""}};
return {"", title, su:from_list($list_utils:map_arg(su, "space", su:explode(title), "-"), " "), @this:columnize(@this:sort_topics(this:find_topics()))};
.
#21:0
if (!args)
l = {};
for p in (properties(#0))
if ((p[max(1, $ - 5)..$] == "_utils") && `#0.(p).description ! ANY')
l = {@l, "$" + p};
endif
endfor
return {@pass(@args), @l};
elseif (ts = pass(@args))
return ts;
elseif ((what = args[1]) && (what[1] != "$"))
return {};
elseif (ts = pass("$generic_" + what[2..length(what)]))
return ts;
elseif ((r = rindex(w = strsub(what[2..length(what)], "-", "_"), "_utils")) && ((r == (length(w) - 5)) && (valid(`#0.(w) ! ANY => #-1') && #0.(w).description)))
return {what};
else
return {};
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {875480190, \"Erik\", #74, \"JHM\"}";
.
#21:1
{topic, dblist} = args;
if ((topic == (("$" + topic[2..length(topic) - 5]) + "utils")) && (valid(#0.(w = strsub(topic[2..length(topic)], "-", "_"))) && (uhelp = #0.(w):description())))
return {{$jtext.header, 1, {$jtext.hgroup, "General information on ", $jaddress.browser_object:make_core_link(w)}}, @this:to_jtext(uhelp)};
return {tostr("General information on $", w, ":"), "----", @uhelp};
else
return pass(@args);
endif
return;
"Metadata 202106";
"Last-Modify: {832100016, \"Erik\", #74, \"JHM\"}";
.
#21:2
if ((E_PROPNF != (text = pass(@args))) || ((args[1][1] != "$") || ((!((uprop = args[1][2..length(args[1])]) in properties(#0))) || (typeof(uobj = #0.(uprop)) != OBJ))))
return text;
else
return uobj:description();
endif
.
#21:3
set_task_perms(caller_perms());
if ((E_PROPNF != (text = pass(@args))) || ((args[1][1] != "$") || ((!((uprop = args[1][2..length(args[1])]) in properties(#0))) || (typeof(uobj = #0.(uprop)) != OBJ))))
return text;
elseif (typeof(e = uobj:set_description(args[2])) == ERR)
return e;
else
return 1;
endif
.
#21:4
return {@pass(@args), $topic, $jtext, $jtext.header, $jtext.hgroup, $jaddress, $jaddress.browser_object, $command_utils};
.
#22:0
text = args[1];
for i in [1..length($code_utils.error_list)]
text = {@text, tostr("    ", $string_utils:left($code_utils.error_names[i], 15), $code_utils.error_list[i])};
endfor
return text;
.
#22:1
text = args[1];
for p in ($code_utils:prepositions())
text = {@text, tostr($string_utils:space(4), p)};
endfor
return text;
.
#22:2
return {@pass(@args), $code_utils, $string_utils};
.
#24:0
":set_programmer(victim[, nomail])  => 1 or error.";
"Sets victim.programmer, chparents victim to $prog if necessary, and sends mail to $new_prog_log unless requested not to.";
whodunnit = caller_perms();
if (!whodunnit.wizard)
return E_PERM;
elseif (!(valid(victim = args[1]) && (is_player(victim) && $object_utils:isa(victim, $player))))
return E_INVARG;
elseif (victim.programmer)
return E_NONE;
elseif (typeof(e = `victim.programmer = 1 ! ANY') == ERR)
return e;
else
if (!$object_utils:isa(victim, $prog))
p = $prog;
while (!$object_utils:isa(victim, p = parent(p)))
endwhile
"...p is now the common ancestor of victim and $prog...";
$quota_utils:adjust_quota_for_programmer(victim);
try
chparent(victim, $prog);
except e (ANY)
"...this isn't really supposed to happen but it could...";
player:notify(tostr("chparent(", victim, ",", $prog, ") failed:  ", e[2]));
player:notify("Check for common properties.");
endtry
endif
if (args[2..$] && args[2])
"fork to make login still work in case send message suspends";
fork (0)
$mail_agent:send_message(whodunnit, {$new_prog_log, victim}, tostr("@programmer ", victim:name(), " (", victim, ")"), tostr("I just gave ", victim:name(), " a programmer bit."));
endfork
endif
return 1;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {912984424, \"Ken\", #75, \"JHM\"}";
.
#24:1
":set_player(victim[,nochown]) => 1 or error";
"Set victim's player flag, (maybe) chown to itself, add name and aliases to $player_db.";
" E_NONE == already a player,";
" E_NACC == player_db is frozen,";
" E_RECMOVE == name is unavailable";
if (!caller_perms().wizard)
return E_PERM;
elseif ((!(valid(victim = args[1]) && $object_utils:isa(victim, $player))) && 0)
return E_INVARG;
elseif (is_player(victim))
return E_NONE;
elseif ($player_db.frozen)
return E_NACC;
elseif (!$player_db:name_ok(name = victim.name, victim))
return E_RECMOVE;
else
set_player_flag(victim, 1);
victim.programmer = $player.programmer;
if (!{@args, 0}[2])
$wiz_utils:set_owner(victim, victim);
endif
$player_db:insert(name, victim);
for a in (setremove(aliases = victim.aliases, name))
if ($player_db:alias_ok(a, victim))
$player_db:insert(a, victim);
else
aliases = setremove(aliases, a);
endif
endfor
victim.aliases = setadd(aliases, name);
return 1;
endif
.
#24:2
":set_owner(object,newowner[,suspendok])  does object.owner=newowner, taking care of c properties as well.  This should be used anyplace one is contemplating doing object.owner=newowner, since the latter leaves ownership of c properties unchanged.  (--Rog thinks this is a server bug).";
if (!valid(object = args[1]))
return E_INVIND;
elseif (!caller_perms().wizard)
return E_PERM;
elseif (!(valid(newowner = args[2]) && is_player(newowner)))
return E_INVARG;
endif
suspendok = {@args, 0}[3];
oldowner = object.owner;
object.owner = newowner;
for pname in ($object_utils:all_properties(object))
if (suspendok && ((ticks_left() < 5000) || (seconds_left() < 2)))
suspend(0);
endif
perms = property_info(object, pname)[2];
if (index(perms, "c"))
set_property_info(object, pname, {newowner, perms});
endif
endfor
if ($object_utils:isa(oldowner, $player))
if (is_player(oldowner) && (object != oldowner))
$quota_utils:reimburse_quota(oldowner, object);
endif
if (typeof(oldowner.owned_objects) == LIST)
oldowner.owned_objects = setremove(oldowner.owned_objects, object);
endif
endif
if ($object_utils:isa(newowner, $player))
if (object != newowner)
$quota_utils:charge_quota(newowner, object);
endif
if (typeof(newowner.owned_objects) == LIST)
newowner.owned_objects = setadd(newowner.owned_objects, object);
endif
endif
return 1;
.
#24:3
":set_property_owner(object,prop,newowner[,suspendok])  changes the ownership of object.prop to newowner.  If the property is !c, changes the ownership on all of the descendents as well.  Otherwise, we just chown the property on the object itself and give a warning if newowner!=object.owner (--Rog thinks this is a server bug that one is able to do this at all...).";
if (!caller_perms().wizard)
return E_PERM;
elseif (!(info = property_info(object = args[1], pname = args[2])))
"... handles E_PROPNF and invalid object errors...";
return info;
elseif (!is_player(newowner = args[3]))
return E_INVARG;
elseif (index(info[2], "c"))
if ({@args, 0}[4] / 2)
"...(recursive call)...";
"...child property is +c while parent is -c??...RUN AWAY!!";
return E_NONE;
else
set_property_info(object, pname, listset(info, newowner, 1));
return (newowner == object.owner) || E_NONE;
endif
else
set_property_info(object, pname, listset(info, newowner, 1));
if ((suspendok = {@args, 0}[4] % 2) && ((ticks_left() < 10000) || (seconds_left() < 2)))
suspend(0);
endif
suspendok = 2 + suspendok;
for c in (children(object))
this:set_property_owner(c, pname, newowner, suspendok);
endfor
return 1;
endif
.
#24:4
":unset_player(victim[,newowner])  => 1 or error";
"Reset victim's player flag, chown victim to newowner (if given), remove all of victim's names and aliases from $player_db.";
if (!caller_perms().wizard)
return E_PERM;
elseif (!valid(victim = args[1]))
return E_INVARG;
elseif (!is_player(victim))
return E_NONE;
endif
if (length(args) >= 2)
$wiz_utils:set_owner(victim, args[2]);
endif
victim.programmer = 0;
victim.wizard = 0;
set_player_flag(victim, 0);
if ($player_db.frozen)
player:tell("Warning:  player_db is in the middle of a :load().");
endif
$player_db:delete2(victim:name(), victim);
for a in (victim.aliases)
$player_db:delete2(a, victim);
endfor
return 1;
.
#24:5
":set_property_flags(object,prop,flags[,suspendok])  changes the permissions on object.prop to flags.  Unlike a mere set_property_info, this changes the flags on all descendant objects as well.  We also change the ownership on the descendent properties where necessary.";
{object, pname, flags, ?suspendok = 0} = args;
perms = caller_perms();
if (!(info = property_info(object, pname)))
"... handles E_PROPNF and invalid object errors...";
return info;
elseif ($set_utils:difference($string_utils:char_list(flags), {"r", "w", "c"}))
"...not r, w, or c?...";
return E_INVARG;
elseif ((pinfo = property_info(parent(object), pname)) && (flags != pinfo[2]))
"... property doesn't actually live here...";
"... only allowed to correct so that this property matches parent...";
return E_INVARG;
elseif (!(perms.wizard || (info[1] == perms)))
"... you have to own the property...";
return E_PERM;
elseif (!(((!(c = index(flags, "c"))) == (!index(info[2], "c"))) || $perm_utils:controls(perms, object)))
"... if you're changing the c flag, you have to own the object...";
return E_PERM;
else
if (c)
set_property_info(object, pname, {object.owner, kflags = flags});
else
set_property_info(object, pname, kflags = listset(info, flags, 2));
endif
for kid in (children(object))
this:_set_property_flags(kid, pname, kflags, suspendok);
endfor
return 1;
endif
.
#24:6
"_set_property_flags(object, pname, {owner, flags} or something+\"c\", suspendok)";
"auxiliary to :set_property_flags... don't call this directly.";
if (caller != this)
return E_PERM;
endif
if (args[4] && $command_utils:running_out_of_time(0))
suspend(0);
endif
object = args[1];
if (typeof(args[3]) != LIST)
set_property_info(object, args[2], {object.owner, args[3]});
else
set_property_info(@args[1..3]);
endif
for kid in (children(object))
this:_set_property_flags(@listset(args, kid, 1));
endfor
.
#24:7
"Generate a random password of length args[1] (or default 7).  Alternates vowels and consonants, for maximum pronounceability.  Uses its own list of consonants which exclude F and C and K to prevent generating obscene sounding passwords.";
vowels = "aeiouy";
consonants = "bdghjlmnpqrstvwxz";
len = args ? tonum(args[1]) | 7;
if (len)
alt = random(2) - 1;
s = "";
for i in [1..len]
s = s + (alt ? vowels[random(6)] | consonants[random(17)]);
alt = !alt;
endfor
return s;
else
return E_INVARG;
endif
.
#24:8
":queued_tasks(player) => list of queued tasks for that player.";
"shouldn't the server builtin should work this way?  oh well";
set_task_perms(caller_perms());
if (typeof(e = set_task_perms(who = args[1])) == ERR)
return e;
elseif (who.wizard)
tasks = {};
for t in (queued_tasks())
if (t[5] == who)
tasks = {@tasks, t};
endif
endfor
return tasks;
else
return queued_tasks();
endif
.
#24:9
"Return 1 if args[1] is a newted player.  0 if not, or if some error.  Maybe should do better in the error case.  Feel free :-)";
if (!caller_perms().wizard)
return E_PERM;
else
"return verb_code($wiz_utils, \"newt_confunc\") == verb_code(args[1], \"confunc\")";
if (0)
return verb_info(args[1], "confunc")[1].wizard && (!args[1].wizard);
else
return $login:is_newted(args[1]);
endif
endif
.
#24:10
player:notify_lines({"", $login:newt_registration_string(), ""});
set_task_perms(this);
boot_player(player);
.
#24:11
":connected_wizards() => list of currently connected wizards and players mentioned in .public_identity properties as being wizard counterparts.";
wizzes = $object_utils:leaves($wiz);
wlist = {};
for w in (wizzes)
if (w.wizard)
if (connected_seconds(w))
wlist = setadd(wlist, w);
endif
if (connected_seconds(w.public_identity))
wlist = setadd(wlist, w.public_identity);
endif
endif
endfor
return wlist;
.
#24:12
":all_wizards() => list of all wizards and players mentioned in .public_identity properties as being wizard counterparts.";
wizzes = $object_utils:leaves($wiz);
wlist = {};
for w in (wizzes)
if (w.wizard)
if (is_player(w))
wlist = setadd(wlist, w);
endif
if (is_player(w.public_identity))
wlist = setadd(wlist, w.public_identity);
endif
endif
endfor
return wlist;
.
#24:13
":rename_all_instances(object,oldname,newname)";
"Used to rename all instances of an unwanted verb (like recycle or disfunc)";
"if said verb is actually defined on the object itself";
{object, vrb, newname} = args;
if (!caller_perms().wizard)
raise(E_PERM);
endif
found = 0;
while (`info = verb_info(object, vrb) ! E_VERBNF => 0')
set_verb_info(object, vrb, listset(info, newname, 3));
found = 1;
endwhile
return found;
.
#24:14
"first arg is the topic missed; second is true for ambiguous match, false for failed match.";
miss = args[1];
ambiguous = args[2];
if (!(index = miss in this.missed_help_strings))
this.missed_help_strings = {miss, @this.missed_help_strings};
this.missed_help_counters = {{0, 0}, @this.missed_help_counters};
index = 1;
endif
which = ambiguous ? 2 | 1;
this.missed_help_counters[index][which] = this.missed_help_counters[index][which] + 1;
.
#24:15
mhs = this.missed_help_strings;
cnt = this.missed_help_counters;
"save values first, so subsequent changes during suspends wont affect it";
thresh = args ? args[1] | 5;
strs = {};
player:tell("miss ambig word");
for i in [1..length(mhs)]
$command_utils:suspend_if_needed(0);
if ((cnt[i][1] + cnt[i][2]) > thresh)
strs = {@strs, ((($string_utils:right(tostr(cnt[i][1]), 5) + " ") + $string_utils:right(tostr(cnt[i][2]), 5)) + " ") + mhs[i]};
endif
endfor
sorted = $list_utils:sort_suspended(0, strs);
len = length(sorted);
for x in [1..len]
$command_utils:suspend_if_needed(0);
player:tell(sorted[(len - x) + 1]);
endfor
player:tell(" - - - - - - - - -");
.
#24:16
if ($__core_init_phase)
pass(@args);
this.dont_reap = {$housekeeper, $quota_utils.owner, $hacker, $topic.owner, $jtext.owner};
this.missed_help_counters = this.missed_help_strings = {};
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {881442056, \"Ken\", #75, \"JHM\"}";
.
#24:17
"This is essentially @net-who moved into $wiz-utils     r'm, 23 May 1993";
"It's sort of a test of permissions, and because lots of non-wizs have";
"put it on their characters and had to duplicate the code.";
"";
"net-connections prints all connected users and hosts.";
"net-connections player player player prints specified users and current";
"  or most recent connected host.";
"net-connections from hoststring prints all players who have connected from";
"  that host or host substring.  Substring can include *'s, e.g. @net-who";
"  from *.foo.edu.";
whodunnit = caller_perms();
if (!whodunnit.wizard)
return E_PERM;
endif
set_task_perms(caller_perms());
su = $string_utils;
if ((prepstr == "from") && dobjstr)
player:notify(tostr("Usage:  ", verb, " from <host string>"));
elseif (((prepstr != "from") || dobjstr) || (!iobjstr))
"Not parsing 'from' here...  Instead printing connected/recent users.";
footnotes = {};
if (!(pstrs = args))
unsorted = connected_players();
else
unsorted = listdelete($command_utils:player_match_result(su:match_player(pstrs), pstrs), 1);
endif
if (!unsorted)
return;
endif
alist = {};
nwidth = length("Player name");
for u in (unsorted)
pref = u.programmer ? "% " | "  ";
u.programmer && (footnotes = setadd(footnotes, "prog"));
$command_utils:suspend_if_needed(0);
if (u in connected_players())
lctime = ctime(time() - connected_seconds(u));
where = connection_name(u);
else
lctime = ctime(u.last_connect_time);
where = u.last_connect_place;
endif
u3 = {tostr(pref, u:name(), " (", u, ")"), lctime[5..10] + lctime[20..24]};
nwidth = max(length(u3[1]), nwidth);
where = $string_utils:connection_hostname(where);
if ($login:blacklisted(where))
where = "(*) " + where;
footnotes = setadd(footnotes, "black");
elseif ($login:graylisted(where))
where = "(+) " + where;
footnotes = setadd(footnotes, "gray");
endif
alist = {@alist, {@u3, where}};
endfor
alist = $list_utils:sort_alist_suspended(0, alist, 3);
$command_utils:suspend_if_needed(0);
headers = {"Player name", "Last Login", "From Where"};
before = {0, nwidth + 3, (nwidth + length(ctime(0))) - 11};
tell1 = "  " + headers[1];
tell2 = "  " + su:space(headers[1], "-");
for j in [2..3]
tell1 = su:left(tell1, before[j]) + headers[j];
tell2 = su:left(tell2, before[j]) + su:space(headers[j], "-");
endfor
player:notify(tell1);
player:notify(tell2);
active = 0;
for a in (alist)
$command_utils:suspend_if_needed(0);
tell1 = a[1];
for j in [2..3]
tell1 = su:left(tell1, before[j]) + a[j];
endfor
player:notify(tell1[1..min(length(tell1), 79)]);
endfor
if (footnotes)
player:notify("");
if ("prog" in footnotes)
player:notify(" %  == programmer.");
endif
if ("black" in footnotes)
player:notify("(*) == blacklisted site.");
endif
if ("gray" in footnotes)
player:notify("(+) == graylisted site.");
endif
endif
elseif (!index(where = iobjstr, "*"))
"Oh good... search for users from a site... the fast way.  No wild cards.";
nl = 0;
bozos = {};
sites = $site_db:find_all_keys(where);
while (sites)
s = sites;
sites = {};
for domain in (s)
for b in ($site_db:find_exact(domain))
if (typeof(b) == STR)
sites = setadd(sites, (b + ".") + domain);
else
bozos = setadd(bozos, b);
nl = max(length(tostr(b, valid(b) ? b:name() | "")), nl);
endif
endfor
endfor
endwhile
if (bozos)
player:notify(tostr(su:left("  Player", nl + 7), "From"));
player:notify(tostr(su:left("  ------", nl + 7), "----"));
for who in (bozos)
st = su:left(tostr(valid(who) ? (who.programmer ? "% " | "  ") + who:name() | "", " (", who, ")"), nl + 7);
comma = 0;
if ($object_utils:isa(who, $player))
for p in (who.all_connect_places)
if (comma && (length(p) >= (78 - length(st))))
player:notify(tostr(st, ","));
st = su:space(nl + 7) + p;
else
st = tostr(st, comma ? ", " | "", p);
endif
comma = 1;
$command_utils:suspend_if_needed(0);
endfor
else
st = st + (valid(who) ? "*** recreated ***" | "*** recycled ***");
endif
player:notify(st);
endfor
player:notify("");
player:notify(tostr(length(bozos), " player", (length(bozos) == 1) ? "" | "s", " found."));
else
player:notify(tostr("No sites matching `", where, "'"));
endif
else
"User typed 'from'.  Go search for users from this site.  (SLOW!)";
howmany = 0;
for who in (players())
$command_utils:suspend_if_needed(0);
matches = {};
for name in (who.all_connect_places)
if ((index(where, "*") && su:match_string(name, where)) || ((!index(where, "*")) && index(name, where)))
matches = {@matches, name};
endif
endfor
if (matches)
howmany = howmany + 1;
player:notify(tostr(who:name(), " (", who, "): ", su:english_list(matches)));
endif
endfor
player:notify(tostr(howmany || "No", " matches found."));
endif
.
#24:18
"Usage:  :do_grant(object, newowner[, suspendok])";
"";
"Change ownership of object, along with verbs and properties owned by the original owner, and !c properties on descendants.";
{object, newowner, ?suspendok = 0} = args;
if (!valid(object))
raise(E_INVIND);
elseif (!caller_perms().wizard)
raise(E_PERM);
elseif (!(valid(newowner) && is_player(newowner)))
raise(E_INVARG);
endif
same = object.owner == newowner;
for vnum in [0..length(verbs(object)) - 1]
verb = tostr(vnum);
info = verb_info(object, verb);
if (!((info[1] != object.owner) && (valid(info[1]) && is_player(info[1]))))
same = same && (info[1] == newowner);
set_verb_info(object, verb, listset(info, newowner, 1));
endif
endfor
for prop in (properties(object))
if (suspendok && ((ticks_left() < 5000) || (seconds_left() < 2)))
suspend(0);
endif
info = property_info(object, prop);
if (!(index(info[2], "c") || (((info[1] != object.owner) && valid(info[1])) && is_player(info[1]))))
same = same && (info[1] == newowner);
$wiz_utils:set_property_owner(object, prop, newowner, suspendok);
endif
endfor
if (suspendok)
suspend(0);
endif
$wiz_utils:set_owner(object, newowner, suspendok);
if (same)
return tostr(newowner:dnamec(), " already owns everything ", newowner:ps(), " is entitled to on ", object:dname(), ".");
else
return tostr("Ownership changed on object, verb, properties and descendants' properties.");
endif
.
#24:19
"Usage:";
"";
"See if the request for player and email address is valid.  Return {E_INVARG, reason} if the request is invalid and player should try again; {false, reason} if the request is invalid but a registrar can override; {1, name, address} if the request is okay.";
name = args[1];
email = args[2];
who = args[3];
if (!caller_perms().wizard)
return E_PERM;
"accesses registration information -- wiz only";
endif
if (!(checkname = $player_db:check_name_request(name, who))[1])
return checkname;
elseif (!(checkaddr = $registration_db:check_address_request(email, $string_utils:connection_hostname(connection_name(who)), who))[1])
return checkaddr;
else
"  return {1, checkname[1], checkaddr[2]}; but checkname[1] is 1, so changed.";
return {1, checkname[2], checkaddr[2]};
endif
.
#24:20
"Usage:  :do_make_guest(name, aliases)";
"";
"Creates a player owned by $hacker and a child of $guest, with the given name and aliases, plus a numeric alias (like `guest23').";
"";
"Returns the new guest object, or a string explaining why not.";
name = args[1];
aliases = args[2];
if (!caller_perms().wizard)
return tostr(E_PERM);
endif
newguest = $player_db:create_player(name, {@aliases, $player_db:gensym_name("guest")}, $guest, $hacker, $guest.password);
if ((typeof(newguest) == OBJ) && valid(newguest))
"set these things up now.";
newguest.default_aliases = newguest.aliases;
endif
return newguest;
.
#24:21
"do_make_player(name,email,[comment])";
"Common code for @make-player";
"If no password is given, generates a random password for the player.";
"Email-address is stored in $registration_db and on the player object.";
if (!caller_perms().wizard)
return E_PERM;
elseif (!args)
player:tell("Usage:  @make-player name[,aliases] [email-address [password]]");
else
name = (na = $building_utils:parse_names(args[1]))[1];
aliases = setadd(na[2], name);
email = (length(args) >= 2) ? args[2] | "";
password = (length(args) >= 3) ? args[3] | "";
if (!password)
password = $wiz_utils:random_password();
endif
if (index(email, " "))
comments = email[index(email, " ") + 1..length(email)];
email = email[1..index(email, " ") - 1];
else
comments = "";
endif
if (((email && (!index(email, "@"))) || index(password, "@")) && (!$command_utils:yes_or_no(tostr({"email and password were swapped(?)", "email address looks like a password."}[1 + (!password)], "  Continue?"))))
player:notify("Aborted.");
return 0;
endif
if (others = $registration_db:find_exact(email))
player:notify(email + " is the registered address of the following characters:");
for x in (others)
player:notify(tostr(valid(x[1]) ? x[1]:name() | "<recycled>", " (", x[1], ") ", (length(x) > 1) ? ("[" + tostr(@x[2..length(x)])) + "]" | ""));
endfor
if (!$command_utils:yes_or_no("Go ahead and make the new player?"))
player:notify("Aborted.");
return 0;
endif
endif
if (!email)
player:notify("No email address specified, not sending mail with password.");
elseif ($network.active)
sendmail = $command_utils:yes_or_no(("Send email to " + email) + " with password?");
else
player:notify(tostr("Network is not enabled, not sending mail with password to ", email, "."));
endif
new = $player_db:create_registered_player(name, aliases, password, email, comments, player, sendmail);
if (typeof(new) == OBJ)
player:notify(tostr(name, " (", new, ") created with password \"", password, "\""));
else
player:notify(tostr(new));
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {893173592, \"Ken\", #75, \"JHM\"}";
.
#24:22
":send_new_player_mail(preface, name, address, password)";
"  used by $wiz:@make-player and $guest:@request";
preface = args[1];
name = args[2];
address = args[3];
password = args[4];
if (!caller_perms().wizard)
return E_PERM;
endif
msg = {preface};
msg = {@msg, tostr("A character has been created, with name \"", name, "\" and password \"", password, "\".  (Passwords are case sensitive.)")};
msg = {@msg, tostr($network.moo_name, " is at ", $network.site, ", port ", $network.port, ".")};
msg = {@msg, "Read the help if you haven't used MOO before."};
msg = {@msg, "A programmer's manual and various other pieces of documentation are available via ftp from ", "  parcftp.xerox.com, directory /pub/MOO"};
msg = {@msg, "Keep your password secure; do not let anyone else connect as you. Remember, you are responsible for what your character does. If you no longer want your character, do not give it to anyone else. You can change your password after you connect with the @password command."};
msg = {@msg, "After you connect, type `help manners' in order to see the policy on the code of conduct expected."};
msg2 = {};
for x in (msg)
msg2 = {@msg2, "", @$generic_editor:fill_string(x, 75)};
endfor
return $network:sendmail(address, (("Your " + $network.moo_name) + " character, ") + name, "Reply-to: " + $login.registration_address, @msg2);
"Metadata 202106";
"Last-Modify: {878587687, \"Ken\", #75, \"JHM\"}";
.
#24:23
"do_newt(@args)";
"";
"Common code for @newt.  Args as in the command line verb.";
"";
"Turns a player into a news.  It can get better...";
"";
"Parses, calls $login:newt_player to do do the actual newting, sends mail to $newt_log giving .all_connect_places and commentary, then tells the user what's going on.";
if (!caller_perms().wizard)
return E_PERM;
endif
whostr = args[1];
comment = $string_utils:first_word(argstr)[2];
if (!argstr)
player:notify("Who do you want to @newt?");
return;
elseif ($command_utils:player_match_failed(who = player:my_match_player(whostr), whostr))
return;
elseif (((whostr != who:name()) && (!(whostr in who.aliases))) && (whostr != tostr(who)))
player:notify(tostr("Must be a full name or an object number:  ", who:name(), "(", who, ")"));
return;
elseif (who == player)
player:notify("If you want to newt yourself, you have to do it by hand.");
return;
elseif ($wiz_utils:isnewt(who))
player:notify(tostr(who:name(), " appears to already be a newt."));
return;
else
$login:newt_player(who);
if (msg = player:newt_victim_msg())
notify(who, msg);
endif
notify(who, $login:newt_registration_string());
boot_player(who);
player:notify(tostr(who:name(), " (", who, ") has been turned into a newt."));
$mail_agent:send_message(player, $newt_log, tostr("@newt ", who:name(), " (", who, ")"), {$string_utils:from_list(who.all_connect_places, " "), @comment ? {comment} | {}});
dobj = who;
if ($object_utils:isa(who.location, $room) && (msg = caller:newt_msg()))
dobj = who;
who.location:announce_all_but({who}, msg);
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863668930, \"Erik\", #74, \"JHM\"}";
.
#24:24
"do_denewt(@args)";
"";
"Common code for @denewt.  Args as in the command line verb.";
"";
"Undoes the effects of $wiz_utils:do_newt.";
"";
"Parses, calls $login:newt_player to do do the actual newting, sends mail to $newt_log giving .all_connect_places and commentary, then tells the user what's going on.";
if (!caller_perms().wizard)
return E_PERM;
endif
whostr = args[1];
comment = $string_utils:first_word(argstr)[2];
if ($command_utils:player_match_failed(who = player:my_match_player(whostr), whostr))
return;
else
"Should parse email address and register user in some clever way.  Ick.";
if ($login:is_newted(who))
$login:denewt_player(who);
player:notify(tostr(who:name(), " (", who, ") got better."));
$mail_agent:send_message(player, $newt_log, tostr("@denewt ", who:name(), " (", who, ")"), comment ? {comment} | {});
else
player:notify(tostr(who:name(), " does not appear to be a newt."));
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863668936, \"Erik\", #74, \"JHM\"}";
.
#25:0
return ((caller == this) || caller_perms().wizard) ? pass(@args) | raise(E_PERM);
.
#25:1
":add(player,site)";
if (!caller_perms().wizard)
return E_PERM;
endif
who = args[1];
domain = args[2];
if (this:domain_literal(domain))
"... just enter it...";
l = this:find_exact(domain);
if (l == $failed_match)
this:insert(domain, {who});
elseif (!(who in l))
this:insert(domain, setadd(l, who));
endif
else
"...an actual domain name; add player to list for that domain...";
"...then add domain itself to list for the next larger domain; repeat...";
"...  Example:  domain == foo.bar.edu:  ";
"...            enter #who  on foo.bar.edu list";
"...            enter `foo' on bar.edu list";
"...            enter `bar' on edu list";
if (!(dot = index(domain, ".")))
dot = length(domain) + 1;
domain = tostr(domain, ".", this.domain);
endif
prev = who;
while ($failed_match == (l = this:find_exact(domain)))
this:insert(domain, {prev});
if (dot)
prev = domain[1..dot - 1];
domain = domain[dot + 1..length(domain)];
else
return;
endif
dot = index(domain, ".");
endwhile
if (!(prev in l))
this:insert(domain, {@l, prev});
endif
return;
endif
.
#25:2
":load([start]) -- reloads site_db with the connection places of all players.";
"This routine calls suspend() if it runs out of time.";
"WIZARDLY";
"...needs to be able to read .all_connect_places";
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
endif
plist = players();
if (!args)
this:clearall();
elseif (i = args[1] in plist)
plist[1..i - 1] = {};
else
return E_INVARG;
endif
for p in (plist)
if (valid(p) && ((is_player(p) && $object_utils:isa(p, $player)) && (!$object_utils:isa(p, $guest))))
"... player may be recycled or toaded during the suspend(),...";
"... guests login from everywhere...";
for c in (p.all_connect_places)
this:add(p, c);
if ($command_utils:running_out_of_time())
player:tell("...", p);
suspend(0);
endif
endfor
endif
endfor
.
#25:3
":domain_literal(string)";
" => true iff string is a domain literal (i.e., numeric IP address).";
if (10 <= (len = length(hnum = strsub(args[1], ".", ""))))
return tonum(hnum[1..9]) && tonum(hnum[6..len]);
else
return tonum(hnum);
endif
"SLEAZY CODE ALERT";
"... what I wanted to do was return tonum(strsub(args[1],\".\",\"\"))";
"... but on a 32-bit machine, this has a 1 in 4294967296 chance of failing";
"... (e.g., on \"42.94.967.296\", though I'll grant this particular example";
"...  entails some very strange subnetting on net 42, to say the least).";
"... So we do something that is guaranteed to work so long as internet";
"... addresses stay under 32 bits --- a while yet...";
"";
"... As soon as we're sure match() is working, this will become a one-liner:";
return match(args[1], "[0-9]+%.[0-9]+%.[0-9]+%.[0-9]+");
.
#25:4
for p in (players())
if (p.__init_for_core_flag != p)
return "wait";
endif
endfor
if ($__core_init_phase)
pass(@args);
this:load();
this.domain = E_INVARG;
endif
.
#25:5
{_, _, perms, @_} = callers()[2];
return !perms.wizard;
.
#25:6
"Carefully loop through the db and delete items associated with !valid and !is_player objects.  If that results in no objects remaining for a site, delete that site.";
"Attempt to keep memory usage down by only asking for a small number of items at a time.  Should probably have some arguments to control this.";
"Another thing it should do is be clever about string typed items.  Instead, call prune_fixup after prune is all complete, to flush unused intermediate sites.";
"To run: call $site_db:prune_reset() then $site_db:prune().";
if (!caller_perms().wizard)
raise(E_PERM);
endif
this.prune_task = task_id();
probe = this.prune_progress;
while ((probe <= this.prune_stop) && (length(probe) == length(this.prune_stop)))
for sitename in (z = this:find_all_keys(probe))
items = this:find_exact(sitename);
orig = items;
for y in (items)
if ((typeof(y) == OBJ) && (((!valid(y)) || (!is_player(y))) || (!`y.all_connect_places ! E_PROPNF => 0')))
items = setremove(items, y);
endif
$command_utils:suspend_if_needed(0);
endfor
useless = 1;
"If no player has this site in eir .all_connect_places, nuke it anyway.";
for y in (items)
if ((typeof(y) != OBJ) || (sitename in y.all_connect_places))
useless = 0;
break;
"unfortunately this can get kinna O(n^2).";
endif
$command_utils:suspend_if_needed(0);
endfor
if (useless)
items = {};
endif
if (!items)
this:delete(sitename);
this.total_pruned_sites = this.total_pruned_sites + 1;
elseif (items == orig)
else
this:insert(sitename, items);
this.total_pruned_people = (this.total_pruned_people + length(orig)) - length(items);
endif
$command_utils:suspend_if_needed(0);
if (probe >= this.prune_stop)
return player:tell("Prune stopped at ", toliteral(this.prune_progress));
endif
endfor
probe = $string_utils:incr_alpha(probe, this.alphabet);
this.prune_progress = probe;
if ($command_utils:running_out_of_time())
"    set_task_perms($wiz_utils:random_wizard())";
suspend(0);
endif
endwhile
player:tell("Prune stopped at ", toliteral(this.prune_progress));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {908143043, \"Nosredna\", #72, \"JHM\"}";
.
#25:7
player:tell("Prune is up to ", toliteral(this.prune_progress), ".");
mine = 0;
if (typeof(this.prune_progress) == STR)
alphalen = length(this.alphabet);
total = (alphalen * alphalen) * alphalen;
for x in [1..3]
mine = ((mine * alphalen) + index(this.alphabet, this.prune_progress[x])) - 1;
endfor
else
total = 256 * 256;
mine = (this.prune_progress[1] * 256) + this.prune_progress[2];
endif
percent = (100.0 * tofloat(mine)) / tofloat(total);
player:tell("We have processed ", mine, " entries out of ", total, ", or ", toint(percent), ".", toint(10.0 * percent) % 10, "%.");
player:tell("There were ", this.total_pruned_people, " individual list entries removed, and ", this.total_pruned_sites, " whole sites removed.");
if ($code_utils:task_valid(this.prune_task))
player:tell("Prune task is ", this.prune_task, ".  Stacktrace:");
for x in (task_stack(this.prune_task, 1))
if (valid(x[4]))
player:tell(x[4], ":", x[2], " [", x[1], "]  ", x[3].name, "  (", x[6], ")");
endif
endfor
else
player:tell("The recorded task_id is no longer valid.");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {908141393, \"Nosredna\", #72, \"JHM\"}";
.
#25:8
if (!caller_perms().wizard)
raise(E_PERM);
endif
root = args[1];
items = this:find_exact(root);
orig = items;
if (items == #-3)
return 1;
endif
$site_db.prune_progress = root;
$site_db.prune_task = task_id();
for item in (items)
if (typeof(item) == STR)
if (this:prune_fixup((item + ".") + root))
items = setremove(items, item);
endif
endif
if ($command_utils:running_out_of_time())
"$wiz_utils:random_wizard())";
suspend(0);
endif
endfor
if (!items)
this:delete(root);
this.total_pruned_sites = this.total_pruned_sites + 1;
return 1;
elseif (orig == items)
else
this:insert(root, items);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {908143981, \"Nosredna\", #72, \"JHM\"}";
.
#25:9
if (!caller_perms().wizard)
return E_PERM;
endif
day = 24 * 3600;
hour_of_day_GMT = 9;
target = (((hour_of_day_GMT * 60) * 60) + day) - (time() % day);
if (target > 86400)
target = target - 86400;
endif
fork (target)
"Stop at 2am before checkpoint, but only if it's actually running.  It might be done!";
if ($code_utils:task_valid(this.prune_task))
$site_db.prune_stop = "aaa";
"Restart after 3am.  Er, 4am.";
suspend(7500);
this:schedule_prune();
$site_db.prune_stop = "zzz";
"Just in case it didn't actually stop...";
if (!$code_utils:task_valid(this.prune_task))
$site_db:prune();
endif
endif
endfork
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {908141393, \"Nosredna\", #72, \"JHM\"}";
.
#25:10
if (!caller_perms().wizard)
raise(E_PERM);
endif
this:report_prune_progress();
player:tell("Resetting...");
`kill_task(this.prune_task) ! ANY';
this.total_pruned_sites = 0;
this.total_pruned_people = 0;
this.prune_progress = "aaa";
this.prune_stop = "zzz";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {908141393, \"Nosredna\", #72, \"JHM\"}";
.
#26:0
"sin(x) -- given x in degrees, sin(x) will return the value of the sine";
"function at x times 10000";
{x} = args;
return toint(floor((10000.0 * sin((tofloat(x) * this.pi) / 180.0)) + 0.5));
"";
"prior G7 code preserved for posterity:";
"";
x = ((args[1] + 45) % 360) - 45;
if (x < 0)
return -this:sin(-x);
elseif (x > 225)
return -this:xcos(x - 270);
elseif (x > 135)
return -this:xsin(x - 180);
elseif (x > 45)
return this:xcos(x - 90);
else
return this:xsin(x);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {865508963, \"Rog\", #145, \"JHM\"}";
.
#26:1
"cos(x) -- given x in degrees, cos(x) will return cosine evaluated at x";
"times 10000";
{x} = args;
return toint(floor((10000.0 * cos((tofloat(x) * this.pi) / 180.0)) + 0.5));
"";
"prior G7 code preserved for posterity:";
"";
return this:sin(90 - args[1]);
"Metadata 202106";
"Last-Modify: {865509054, \"Rog\", #145, \"JHM\"}";
.
#26:2
"tan(x) -- given x in degrees, tan(x) will calculate the tangent at x";
"times 10000";
{x} = args;
return toint(floor((10000.0 * tan((tofloat(x) * this.pi) / 180.0)) + 0.5));
"";
"prior G7 code preserved for posterity:";
"";
return (this:sin(args[1]) * 10000) / this:cos(args[1]);
"Metadata 202106";
"Last-Modify: {865509209, \"Rog\", #145, \"JHM\"}";
.
#26:3
"xsin(x) -- calculates the taylor approximation for the sine function";
x = args[1];
if ((x * x) > this.taylor)
return ((this:xsin(x / 2) * this:xcos((x + 1) / 2)) + (this:xsin((x + 1) / 2) * this:xcos(x / 2))) / 10000;
else
return (x * (17453000 - ((x * x) * 886))) / 100000;
endif
.
#26:4
"xcos(x) -- calculates the taylor approximation for the cosine function";
x = args[1];
if ((x * x) > this.taylor)
return ((this:xcos(x / 2) * this:xcos((x + 1) / 2)) - (this:xsin(x / 2) * this:xsin((x + 1) / 2))) / 10000;
else
return (1000000000 - ((x * x) * (152309 + ((4 * x) * x)))) / 100000;
endif
.
#26:5
"factorial(n) -- returns n factorial for 0 <= n (<= 12).";
if ((number = args[1]) < 0)
return E_INVARG;
endif
fact = 1;
for i in [2..number]
fact = fact * i;
endfor
return fact;
.
#26:6
"pow(x,n) -- returns x raised to the nth power. n must be >= 0.";
if ((power = args[2]) < 0)
return E_INVARG;
endif
n = args[1];
if (power % 2)
ret = n;
else
ret = 1;
endif
while (power = power / 2)
n = n * n;
if (power % 2)
ret = ret * n;
endif
endwhile
return ret;
.
#26:7
"fibonacci(n) -- calculates the fibonacci numbers to the nth term";
"and returns them in a list. n must be >= 0.";
x0 = 0;
x1 = 1;
if ((n = args[1]) < 0)
return E_INVARG;
elseif (n == 0)
return {x0};
else
x = {x0, x1};
for i in [2..n]
len = length(x);
x = {@x, x[len - 1] + x[len]};
endfor
return x;
endif
.
#26:8
"geometric(x,n) -- calculates the value of the geometric series at x to ";
"the nth term. i.e., approximates 1/(1-x) when |x| < 1. this, of course,";
"is impossible in MOO, but someone may find it useful in some way.";
"n defaults to 5. n must be >= 0.";
{n, ?order = 5} = args;
x = 1;
for i in [1..order]
x = x + this:pow(n, i);
endfor
return x;
.
#26:9
"divmod(n,d) => {q,r} such that n = dq + r";
"  handles negative numbers correctly   0<=r<d if d>0, -d<r<=0 if d<0.";
n = args[1];
d = args[2];
r = ((n % d) + d) % d;
q = (n - r) / d;
return {q, r};
.
#26:10
"combinations(n,r) -- returns the number of ways one can choose r";
"objects from n distinct choices.";
"C(n,r) = n!/[r!(n-r)!]";
"  overflow may occur if n>29...";
n = args[1];
r = args[2];
if (0 > (r = min(r, n - r)))
return 0;
else
c = 1;
n = n + 1;
for i in [1..r]
c = (c * (n - i)) / i;
endfor
return c;
endif
.
#26:11
"permutations(n,r) -- returns the number of ways possible for one to";
"order r objects given n distinct locations.";
"P(n,r) = n!/(n-r)!";
n = args[1];
r = args[2];
return this:factorial(n) / this:factorial(n - r);
.
#26:12
"simpson({a,b},{f(a),f((a+b)/2),f(b)}) -- given two endpoints, a and b,";
"and the functions value at a, (a+b)/2, and b, this will calculate";
"a numerical approximation of the integral using simpson's rule.";
"the answer is returned as {integer,fraction}";
point = args[1];
fcn = args[2];
return this:parts((point[2] - point[1]) * ((fcn[1] + (4 * fcn[2])) + fcn[3]), 6);
.
#26:13
"parts(n,q[,i]) -- returns a decomposition of n by q into integer and";
"floating point parts with i = the number of digits after the decimal.";
"i defaults to 5.";
"warning: it is quite easy to hit maxint which results in unpredictable";
"         results";
parts = {(n = args[1]) / (q = args[2]), n % q};
i = (length(args) > 2) ? args[3] | 5;
return {parts[1], (parts[2] * this:pow(10, i)) / q};
.
#26:14
return sqrt(args[1]);
"sqrt(n) => largest integer <= square root of n.  Uses Newton's method.";
"obsolete now; left for documentation purposes.";
n = args[1];
if (n < 0)
return E_RANGE;
elseif (n)
x1 = n;
while (x1 > (x2 = (x1 + (n / x1)) / 2))
x1 = x2;
endwhile
return x1;
else
return 0;
endif
.
#26:15
"arctan(y/x) == arctan(x,y) => angle in degrees.";
{x, ?y = E_NONE} = args;
if (y == E_NONE)
{x, y} = {10000, x};
endif
return toint(floor(((180.0 * atan(tofloat(y), tofloat(x))) / this.pi) + 0.5));
"";
"prior code preserved for posterity:";
"";
if (length(args) < 2)
sin = args[1];
cos = 10000;
else
sin = args[2];
cos = args[1];
endif
if (sin < 0)
return -this:arctan(cos, -sin);
elseif (cos < 0)
return 180 - this:arctan(-cos, sin);
elseif (sin > cos)
return 90 - this:arctan(sin, cos);
else
tan = (sin * 10000) / cos;
a = $list_utils:find_insert(this.tangents, tan - 1);
if ((this.tangents[a] - tan) < (tan - {0, @this.tangents}[a]))
return a;
else
return a - 1;
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {865510187, \"Rog\", #145, \"JHM\"}";
.
#26:16
"div(n,d) => q such that n = dq + r and  (0<=r<d if d>0, -d<r<=0 if d<0).";
return this:divmod(@args)[1];
.
#26:17
"A correct mod function.";
"mod(n,d) => r such that n = dq + r and (0<=r<d if d>0 or -d<r<=0 if d<0).";
n = args[1];
d = args[2];
return ((n % d) + d) % d;
.
#26:18
"returns 10000 exp (x/10000)";
"The accuracy seems to be ~0.1% for 0<x<4";
x = args[1];
if (x < 0)
z = this:(verb)(-x);
return (100000000 + (z / 2)) / z;
elseif (x > 1000)
z = this:(verb)(x / 2);
if (z > 1073741823)
return $maxint;
"maxint for overflows";
elseif (z > 460000)
z = ((z + 5000) / 10000) * z;
elseif (z > 30000)
z = ((((z + 50) / 100) * z) + 50) / 100;
else
z = ((z * z) + 5000) / 10000;
endif
if (x % 2)
return z + ((z + 5000) / 10000);
else
return z;
endif
else
return ((10000 + x) + (((x * x) + 10000) / 20000)) + ((((x * x) * x) + 300000000) / 600000000);
endif
.
#26:19
"is_prime(number) returns 1 if the number is prime or 0 if it isn't.";
"of course, only positive numbers are candidates for primality.";
number = args[1];
if (number == 2)
return 1;
elseif ((number < 2) || ((number % 2) == 0))
return 0;
else
choice = 3;
while (((denom = choice * choice) <= number) && (denom > 0))
if ((seconds_left() < 2) || (ticks_left() < 25))
suspend(0);
endif
if ((number % choice) == 0)
return 0;
endif
choice = choice + 2;
endwhile
endif
return 1;
.
#26:20
bl1 = this:BLFromInt(args[1]);
bl2 = this:BLFromInt(args[2]);
blOut = {};
for i in [1..32]
blOut = {@blOut, bl1[i] && bl2[i]};
endfor
return this:IntFromBL(blOut);
.
#26:21
bl1 = this:BLFromInt(args[1]);
bl2 = this:BLFromInt(args[2]);
blOut = {};
for i in [1..32]
blOut = {@blOut, bl1[i] || bl2[i]};
endfor
return this:IntFromBL(blOut);
.
#26:22
bl1 = this:BLFromInt(args[1]);
bl2 = this:BLFromInt(args[2]);
blOut = {};
for i in [1..32]
blOut = {@blOut, bl1[i] != bl2[i]};
endfor
return this:IntFromBL(blOut);
.
#26:23
return -(1 + args[1]);
"";
"... here's what it used to be ...";
bl1 = this:BLFromInt(args[1]);
blOut = {};
for i in [1..32]
blOut = {@blOut, !bl1[i]};
endfor
return this:IntFromBL(blOut);
.
#26:24
x = args[1];
l = {};
firstbit = x < 0;
if (firstbit)
x = x + $minint;
endif
for i in [1..31]
l = {x % 2, @l};
x = x / 2;
endfor
return {firstbit, @l};
.
#26:25
bl = args[1];
x = 0;
for l in (bl)
x = x * 2;
x = x + l;
endfor
return x;
.
#26:26
"gcd(num1,num2): find the greatest common divisor of the two numbers";
"using the division algorithm. the absolute values of num1 and num2 are";
"used without loss of generality.";
num1 = abs(args[1]);
num2 = abs(args[2]);
max = max(num1, num2);
min = min(num1, num2);
if (r1 = max % min)
while (r2 = min % r1)
min = r1;
r1 = r2;
endwhile
return r1;
else
return min;
endif
.
#26:27
"lcm(num1,num2): find the least common multiple of the two numbers.";
"we shall use the positive lcm value without loss of generality.";
"since we have gcd already, we'll just use lcm*gcd = num1*num2";
num1 = abs(args[1]);
num2 = abs(args[2]);
return (num1 * num2) / this:gcd(num1, num2);
.
#26:28
"are_rel_prime(num1,num2): returns 1 if num1 and num2 are relatively";
"prime.";
"since we have gcd, this is pretty easy.";
if (this:gcd(args[1], args[2]) == 1)
return 1;
else
return 0;
endif
.
#26:29
"Call with first arg either a number or a string, being the number desired for conversion. capital letters denote values from 10-35; lowercase letters from 36 to 61. Maximal base is 62.";
"";
"You will be unable to use the extra 26 lowercases as separate unless you pass a nonzero fourth argument. Passing zero or none uses the default value, which is to have AAAA=aaaa.";
"";
"The second and third arguments should be the base of the number and the base you want it in, respectively.";
"";
"Any of the arguments can be strings or nums, but high-base numbers will need to be strings. This returns a string.";
"";
"Any problems, talk to Ozymandias.";
sensitive = 0;
if (length(args) < 3)
return E_INVARG;
elseif (length(args) == 4)
sensitive = tonum(args[4]);
endif
result = 0;
thenum = tostr(args[1]);
origbase = tonum(args[2]);
newbase = tonum(args[3]);
if ((((origbase < 2) || (newbase < 2)) || (origbase > 62)) || (newbase > 62))
return E_INVARG;
endif
for which in [1..length(thenum)]
value = index(this.base_alphabet, thenum[which], sensitive);
if ((!value) || (value > origbase))
return E_INVARG;
endif
result = ((result * origbase) + value) - 1;
endfor
thestring = "";
if (result < 0)
return E_INVARG;
endif
while (result)
if ((which = (result % newbase) + 1) <= length(this.base_alphabet))
thestring = this.base_alphabet[which] + thestring;
else
return E_INVARG;
endif
result = result / newbase;
endwhile
return thestring;
"Metadata 202106";
"Last-Modify: {865212680, \"Erik\", #74, \"JHM\"}";
.
#26:30
"exp(x[,n]) -- calculates an nth order taylor approximation for e^x.";
"n defaults to 5. Any n given must be >= 0. you need to divide the result";
"the answer will be returned as {integer part,fractional part}";
{x, ?n = 5} = args;
ex = nfact = 1;
for i in [0..n - 1]
j = n - i;
ex = (ex * x) + (nfact = nfact * j);
endfor
return this:parts(ex, nfact);
"Metadata 202106";
"Last-Modify: {912984214, \"Ken\", #75, \"JHM\"}";
.
#26:31
":norm(a,b,c,d...) => sqrt(a^2+b^2+c^2+...)";
m = max(max(@args), -min(@args));
logm = length(tostr(m));
if (logm <= 4)
s = 0;
for a in (args)
s = s + (a * a);
endfor
return sqrt(s);
else
factor = tonum("1" + "0000000"[1..logm - 4]);
s = 0;
for a in (args)
a = a / factor;
s = s + (a * a);
endfor
return sqrt(s) * factor;
endif
.
#26:32
"Usage:  :percent(n, d)";
"";
"Find a percentage close to the fraction n/d, with integer precision (0-100).";
n = args[1];
d = args[2];
if (n > ($maxint / 100))
return ((n / 100) * 100) / (d / 100);
else
return (n * 100) / d;
endif
.
#26:33
"Usage:  :sqrt_int(n)";
"";
"Return the square root of the integer n, truncated to an integer.";
n = args[1];
return toint(sqrt(tofloat(n)));
.
#27:0
"Returns the set union of all of the lists provided as arguments.";
if (!args)
return {};
endif
set = args[1];
for l in (listdelete(args, 1))
for x in (l)
set = setadd(set, x);
endfor
endfor
return set;
.
#27:1
"Returns the set intersection of all the lists provided as arguments.";
if (!args)
return {};
endif
max = 0;
result = args[1];
for set in (listdelete(args, 1))
if (length(result) < length(set))
set1 = result;
set2 = set;
else
set1 = set;
set2 = result;
endif
for x in (set1)
if (!(x in set2))
set1 = setremove(set1, x);
endif
endfor
result = set1;
endfor
return result;
.
#27:2
"Usage:  diff(set 1, set 2, ..., set n)";
"Returns all elements of set 1 that are not in sets 2..n";
if (!(set = args[1]))
return {};
endif
for l in (listdelete(args, 1))
for x in (l)
set = setremove(set, x);
endfor
endfor
return set;
.
#27:3
"True if the first list given is a superset of all subsequent lists.";
"False otherwise.  {} is a superset of {} and nothing else; anything is";
"a superset of {}.  If only one list is given, return true.";
super = args ? args[1] | {};
for l in (listdelete(args, 1))
for x in (l)
if (!(x in super))
return 0;
endif
endfor
endfor
return 1;
.
#27:4
"Usage:  exclusive_or(set, set, ...)";
"Return the set of all elements that are in exactly one of the input sets";
"For two sets, this is the equivalent of (A u B) - (A n B).";
if (!args)
return {};
endif
set = so_far = args[1];
for l in (listdelete(args, 1))
for x in (l)
if (x in so_far)
set = setremove(set, x);
else
set = setadd(set, x);
endif
endfor
so_far = {@so_far, @l};
endfor
return set;
.
#27:5
"Usage:  diff(set 1, set 2, ..., set n)";
"Returns all elements of set 1 that are not in sets 2..n";
set = args[1];
rest = args[2..length(args)];
for l in (rest)
for x in (l)
set = setremove(set, x);
$command_utils:suspend_if_needed(0);
endfor
endfor
return set;
.
#27:6
"Usage:  $set_utils:equals(set, set, ...)";
if (!(n = length(args)))
return 1;
endif
args = {@args, args[1]};
for i in [1..n]
if (!$set_utils:contains(args[i], args[i + 1]))
return 0;
endif
endfor
return 1;
.
#29:0
who = args[1];
return who.wizard;
.
#29:1
if (!this:is_writable_by(caller_perms()))
return E_PERM;
else
if (msgs = this.messages)
new = msgs[length(msgs)][1] + 1;
else
new = 1;
endif
if (rmsgs = this.messages_going)
lbrm = rmsgs[length(rmsgs)][2];
new = max(new, lbrm[length(lbrm)][1] + 1);
endif
m = args[1];
if (index(m[4], "@programmer ") == 1)
m = {m[1], toobj(args[2]), o = toobj(m[4][index(m[4], "(") + 1..index(m[4], ")") - 1]), o:name()};
endif
this.messages = {@msgs, {new, m}};
this.last_msg_date = m[1];
this.last_used_time = time();
return new;
endif
.
#29:2
":display_seq_headers(msg_seq[,cur])";
":display_seq_full(msg_seq[,cur]) => {cur}";
if (!this:ok(caller, caller_perms()))
return E_PERM;
endif
cur = {@args, 0}[2];
read_date = {@args, $maxint, $maxint}[3];
last = ldate = 0;
player:tell("       WHEN           WHO                           BY");
for x in (msgs = this:messages_in_seq(args[1]))
msgnum = $string_utils:right(last = x[1], 4, (cur == x[1]) ? ">" | " ");
ldate = x[2][1];
if (typeof(x[2][2]) != OBJ)
hdr = this:msg_summary_line(@x[2]);
else
hdr = tostr(ctime(ldate)[5..16], "   ", $string_utils:left(tostr(x[2][4], " (", x[2][3], ")"), 30), valid(w = x[2][2]) ? w:name() | "??", " (", x[2][2], ")");
endif
player:tell(msgnum, (ldate > read_date) ? ":+ " | ":  ", hdr);
$command_utils:suspend_if_needed(0);
endfor
if (verb == "display_seq_full")
return {last, ldate};
else
player:tell("----+");
endif
.
#29:3
":from_msg_seq(object or list[,mask])";
" => msg_seq of messages from any of these senders";
if (!this:ok(caller, caller_perms()))
return E_PERM;
endif
if (typeof(plist = args[1]) != LIST)
plist = {plist};
endif
i = 1;
fseq = {};
mask = {@args, {1}}[2];
for msg in (this.messages)
if ((!mask) || (i < mask[1]))
elseif ((length(mask) < 2) || (i < mask[2]))
if (msg[2][2] in plist)
fseq = $seq_utils:add(fseq, i, i);
endif
else
mask = mask[3..length(mask)];
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return fseq || ("%f %<has> no messages from " + $string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%n (%#)", plist), "no one", " or "));
.
#29:4
":to_msg_seq(object or list[,mask]) => msg_seq of messages to those people";
if (!this:ok(caller, caller_perms()))
return E_PERM;
endif
if (typeof(plist = args[1]) != LIST)
plist = {plist};
endif
i = 1;
fseq = {};
mask = {@args, {1}}[2];
for msg in (this.messages)
if ((!mask) || (i < mask[1]))
elseif ((length(mask) < 2) || (i < mask[2]))
if (msg[2][3] in plist)
fseq = $seq_utils:add(fseq, i, i);
endif
else
mask = mask[3..length(mask)];
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return fseq || ("%f %<has> no messages about @programmer'ing " + $string_utils:english_list(plist, "no one", " or "));
.
#29:5
":%to_msg_seq/subject_msg_seq(string or list of strings[,mask])";
" => msg_seq of messages containing one of strings in the to line";
if (!this:ok(caller, caller_perms()))
return E_PERM;
endif
if (typeof(nlist = args[1]) != LIST)
nlist = {nlist};
endif
i = 1;
fseq = {};
mask = {@args, {1}}[2];
for msg in (this.messages)
if ((!mask) || (i < mask[1]))
elseif ((length(mask) < 2) || (i < mask[2]))
if (msg[2][4] in nlist)
fseq = $seq_utils:add(fseq, i, i);
endif
else
mask = mask[3..length(mask)];
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return fseq || ("%f %<has> no messages about @programmer'ing " + $string_utils:english_list(nlist, "no one", " or "));
.
#29:6
return this:name() + " doesn't understand %%from:";
.
#30:0
"WIZARDLY";
if (args)
"...check for an exact match first...";
search = args[1];
"if (property_info(parent(this), search))";
if (property_info(this, " " + search))
return {search};
"endif";
elseif (search in properties(this))
text = this.(search);
if (((text == {}) || (text && (typeof(text) == LIST))) && (!$list_utils:check_type(text, OBJ)))
return {search};
endif
endif
"...search for partial matches, allowing for";
"...confusion between topics that do and don't start with @, and";
".. confusion between - and _ characters.";
"props = properties(this);";
props = this:find_topics();
topics = {};
if (search[1] == "@")
search = search[2..length(search)];
endif
search = strsub(search, "-", "_");
if (!search)
"...don't try searching for partial matches if the string is empty or @";
"...we'd get *everything*...";
return {};
endif
for prop in (props)
if ((((i = index(strsub(prop, "-", "_"), search)) == 1) || ((i == 2) && index(" @", prop[1]))) && (((text = this.(prop)) == {}) || ((text && (typeof(text) == LIST)) && (!$list_utils:check_type(text, OBJ)))))
topics = {@topics, (prop[1] == " ") ? prop[2..length(prop)] | prop};
endif
endfor
return topics;
else
"...return list of all topics...";
props = setremove(properties(this), "");
topics = {};
for p in (props)
if (p[1] == " ")
p[1..1] = "";
endif
topics = setadd(topics, p);
endfor
return topics;
"...old version...";
for p in ($object_utils:all_properties(parent(this)))
if (i = (" " + p) in props)
props = {p, @listdelete(props, i)};
endif
endfor
return props;
endif
.
#30:1
"WIZARDLY";
{topic, ?dblist = {}} = args;
"text = typeof(this.(topic)) == LIST ? this.(topic) | this.(\" \" + topic);";
text = this:topic_text(topic);
if (typeof(text) == LIST)
if (text && (typeof(text[1]) == STR))
if (text && (text[1] == (("*" + (vb = strsub(text[1], "*", ""))) + "*")))
text = this:(vb)(listdelete(text, 1), dblist);
endif
if (((typeof(text) == LIST) && text) && (typeof(text[1]) == STR))
return this:to_jtext(text, topic);
endif
endif
return text;
elseif (typeof(text) == STR)
return {{$jtext.paragraph, text}};
else
return text;
endif
.
#30:2
":sort_topics(list_of_topics) -- sorts the given list of strings, assuming that they're help-system topic names";
buckets = "(abcdefghijklmnopqrstuvwxyz";
keys = names = $list_utils:make(length(buckets) + 1, {});
for name in (setremove(args[1], ""))
key = index(".@", name[1]) ? name[2..length(name)] + " " | name;
k = index(buckets, key[1]) + 1;
bucket = keys[k];
i = $list_utils:find_insert(bucket, key);
keys[k] = listinsert(bucket, key, i);
names[k] = listinsert(names[k], name, i);
$command_utils:suspend_if_needed(0);
endfor
return $list_utils:append(@listdelete(names, 2));
.
#30:3
":columnize(@list_of_strings) -- prints the given list of strings in 4 columns.";
sorted = {@args, "", "", ""};
n = length(sorted) / 4;
su = $string_utils;
index = {};
for i in [1..n]
index = {@index, tostr(su:left(sorted[i], 20), su:left(sorted[i + n], 20), su:left(sorted[i + (2 * n)], 20), sorted[i + (3 * n)])};
$command_utils:suspend_if_needed(0);
endfor
return index;
.
#30:4
"{\"*forward*\", topic, @rest}  => text for topic from this help db.";
"{\"*pass*\",    topic, @rest}  => text for topic from next help db.";
"{\"*unpass*\",  topic, @rest}  => text for topic from first help db.";
"In both cases the text of @rest is appended.  ";
"@rest may in turn begin with a *<verb>*";
{text, ?dblist = {}} = args;
if (verb == "forward")
first = this:get_topic(text[1], dblist);
elseif (((verb == "unpass") && ((result = $code_utils:help_db_search(text[1], dblist)) && ((db = result[1]) != $ambiguous_match))) || ((verb == "pass") && ((result = $code_utils:help_db_search(text[1], dblist[(this in dblist) + 1..length(dblist)])) && ((db = result[1]) != $ambiguous_match))))
first = db:get_topic(result[2], dblist);
"...first = db:get_topic(result[2], dblist[(db in dblist) + 1..length(dblist)])...";
else
first = {};
endif
if (2 <= (len = length(text)))
if (text[2] == (("*" + (vb = strsub(text[2], "*", ""))) + "*"))
return {@first, @this:to_jtext(this:(vb)(text[3..len], dblist))};
else
return {@first, @this:to_jtext(text[2..len])};
endif
else
return first;
endif
.
#30:5
"{\"*subst*\", @text} => text with the following substitutions:";
"  \"...%[expr]....\" => \"...\"+value of expr (assumed to be a string)+\"....\"";
"  \"%;expr\"         => @(value of expr (assumed to be a list of strings))";
newlines = {};
for old in (args[1])
new = "";
bomb = 0;
while ((prcnt = index(old, "%")) && (prcnt < length(old)))
new = new + old[1..prcnt - 1];
code = old[prcnt + 1];
old = old[prcnt + 2..length(old)];
if (code == "[")
prog = "";
while ((b = index(old + "]", "]")) > (p = index(old + "%", "%")))
prog = (prog + old[1..p - 1]) + old[p + 1];
old = old[p + 2..length(old)];
endwhile
prog = prog + old[1..b - 1];
old = old[b + 1..length(old)];
value = $no_one:eval_d(prog);
if (value[1])
new = tostr(new, value[2]);
else
new = tostr(new, $string_utils:print(value[2]));
bomb = 1;
endif
elseif ((code != ";") || new)
new = (new + "%") + code;
else
value = $no_one:eval_d(old);
if (value[1] && (typeof(r = value[2]) == LIST))
newlines = {@newlines, @r[1..length(r) - 1]};
new = tostr(r[length(r)]);
else
new = tostr(new, $string_utils:print(value[2]));
bomb = 1;
endif
old = "";
endif
endwhile
if (bomb)
newlines = {@newlines, new + old, tostr("@@@ Helpfile alert:  Previous line is messed up; notify ", this.owner.wizard ? "" | tostr(this.owner:name(), " (", this.owner, ") or "), "a wizard. @@@")};
else
newlines = {@newlines, new + old};
endif
endfor
return newlines;
.
#30:6
"{\"*index*\" [, title]}";
"This produces a columnated list of topics in this help db, headed by title.";
$command_utils:suspend_if_needed(0);
title = args[1] ? args[1][1] | tostr(this:name(), " (", this, ")");
su = $string_utils;
return {{$jtext.title, title}, {$jtext.header, 1, title}, {$jtext.index, @$list_utils:map_arg(this, "make_link", this:sort_topics(this:find_topics()))}};
return {{$jtext.header, 3, title}, {$jtext.plaintext, @this:columnize(@this:sort_topics(this:find_topics())), ""}};
return {"", title, su:from_list($list_utils:map_arg(su, "space", su:explode(title), "-"), " "), @this:columnize(@this:sort_topics(this:find_topics()))};
.
#30:7
pass(@args);
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
this.r = 1;
this.f = 0;
endif
.
#30:8
"{\"*verbdoc*\", \"object\", \"verbname\"}  use documentation for this verb";
set_task_perms(this.owner);
if (!valid(object = $string_utils:match_object(args[1][1], player.location)))
return E_INVARG;
elseif (!(hv = $object_utils:has_verb(object, vname = args[1][2])))
return E_VERBNF;
else
return $code_utils:verb_documentation(hv[1], vname);
endif
.
#30:9
if (typeof(text = this:topic_text(fulltopic = args[1])) == ERR)
return text;
elseif ((!text) || $list_utils:check_type(text, STR))
return {tostr("@sethelp-text ", $code_utils:corify_object(this), ".", fulltopic), @$command_utils:dump_lines(text)};
else
return {tostr("@sethelp-value ", $code_utils:corify_object(this), ".", fulltopic), @$note_editor:explode_list(0, text), "."};
endif
.
#30:10
"to_jtext(text, topic)";
"Convert help text to jtext.";
"Doesn't handle lots and lots of things.";
"If it can't figure out how to be smart, it resorts to a plaintext tag.";
"Doesn't make a vgroup, just returns a list of vboxes with no title or header.";
flat = text = args[1];
topic = args[2];
if (typeof(flat) == STR)
return {{$jtext.paragraph, flat}};
elseif ((typeof(flat) != LIST) || (flat && (typeof(flat[1]) == LIST)))
return flat;
endif
new = {};
while (flat)
if ((length(flat) == 1) || (flat[2] == ""))
if (match(line = flat[1], "^---------------------*$"))
new = {@new, {$jtext.hr}};
elseif (match(line, "^ *%(Usage%|Syntax%): "))
new = {@new, {$jtext.plaintext, line}};
else
new = {@new, {$jtext.paragraph, @this:line_to_hboxes(line)}};
endif
flat[1..2] = {};
else
if (blank = "" in flat)
section = flat[1..blank - 1];
flat = flat[blank + 1..length(flat)];
else
section = flat;
flat = {};
endif
new = {@new, this:plaintext_or_dlist(section, topic)};
endif
$command_utils:suspend_if_needed(0);
endwhile
return new;
.
#30:11
"plaintext_or_dlist(some text, from-topic)";
"also handles headers and *-prefixed itemizations";
text = flat = args[1];
if (subs = match(text[1], "^  +[-o*] +"))
prefix = text[1][1..subs[2]];
realstart = subs[2] + 1;
itemize = {$jtext.itemize_unnumbered};
for line in (flat)
if ((index(line, prefix) == 1) && (line[realstart] != " "))
hboxes = this:line_to_hboxes(line[realstart..length(line)]);
if (length(hboxes) == 1)
hbox = hboxes[1];
else
hbox = {$jtext.hgroup, @hboxes};
endif
itemize = {@itemize, hbox};
else
return {$jtext.plaintext, @text};
endif
endfor
return itemize;
elseif ((length(text) == 2) && ((len = length(text[1])) == length(text[2])))
header = 0;
line = $string_utils:space(len, "=");
if (text[2] == line)
header = 1;
elseif (text[2] == (line2 = strsub(line, "=", "-")))
header = 2;
endif
if (header)
if (!$string_utils:is_uppercase(text[1]))
header = header + 2;
endif
return {$jtext.header, header, text[1]};
else
line[1..1] = "";
line2[1..1] = "";
line = tostr("^[1-6]%(", line, "%|", line2, "%)$");
if (match(text[2], line))
return {$jtext.header, tonum(text[2][1]), text[1]};
endif
endif
endif
dlist = {$jtext.dlist};
for line in (flat)
if (subs = match(line, "^ *%([^ ]+%) -- %(.*%)$"))
topic = substitute("%1", subs);
hboxes = this:line_to_hboxes(substitute("%2", subs));
if (length(hboxes) == 1)
hbox = hboxes[1];
else
hbox = {$jtext.hgroup, @hboxes};
endif
dlist = {@dlist, {$jaddress.help:make_formatted_link(topic, topic), hbox}};
else
this.unconvertible_topics = setadd(this.unconvertible_topics, args[2]);
return {$jtext.plaintext, @text};
endif
endfor
return dlist;
.
#30:12
"make_link(subject)";
return {$jtext.link.root, {$jaddress.help, {{"subject", args[1]}}}, args[1]};
.
#30:13
"Usage:  :line_to_hboxes(string) => list of strings";
"Parse line into a list of hboxes.";
"Currently the only hboxes output will be strings and help links.";
line = args[1];
hboxes = {};
string = "";
if (1)
while (subs = match(line, "^%([^`']*%)%([`']%)%([^']*'*%)'%(.*%)"))
addr = $jaddress:from_pseudocommand(substitute("%3", subs));
if (addr)
hboxes = {@hboxes, string + substitute("%1", subs), addr};
string = "";
line = substitute("%4", subs);
else
"     string = string + substitute(\"%1%2%3\", subs);";
"     line = substitute(\"'%4\", subs);";
string = string + substitute("%1%2", subs);
line = substitute("%3'%4", subs);
endif
endwhile
string = string + line;
return string ? {@hboxes, string} | hboxes;
endif
.
#30:14
"{\"*objectdoc*\", \"object\"} => text for topic from object:help_msg";
if (!valid(object = $string_utils:literal_object(args[1][1])))
return E_INVARG;
elseif (!$object_utils:has_verb(object, "help_text"))
return E_VERBNF;
else
return object:help_text();
endif
.
#30:15
topic = args[1];
if ((" " + topic) in properties(this))
return this.(" " + topic);
elseif (topic in properties(this))
text = this.(topic);
if (typeof(text) == LIST)
return text;
else
return E_PROPNF;
endif
else
return E_PROPNF;
endif
.
#30:16
"Usage:  :set_topic_text(topic, text)";
"text should be either a list of strings or jtext";
set_task_perms(caller_perms());
if (!args)
return E_ARGS;
elseif (!(((name = " " + args[1]) in properties(this)) || ((name = args[1]) in properties(this))))
return E_PROPNF;
elseif (typeof(args[2]) != LIST)
return E_INVARG;
elseif (typeof(e = this.(name) = args[2]) == ERR)
return e;
else
return 1;
endif
.
#30:17
return typeof(this:topic_text(args[1])) == LIST;
.
#30:18
"Usage:  :add_topic(string)";
"";
"Adds a new topic with the given name.  If this db already has a topic with that name, or if it's otherwise invalid, return E_INVARG; if caller_perms() are not adequate, return E_PERM; otherwise return 1.";
topic = $string_utils:trim(args[1]);
if (this:has_topic(topic))
return E_INVARG;
elseif (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
endif
if (typeof(e = this:real_add_topic(topic, args[2], caller_perms())) == ERR)
return e;
else
return 1;
endif
.
#30:19
"Usage:  :real_add_topic(string, value, caller_perms)";
"";
"Callable only from this object.  Does the real work of adding a new topic; it's assumed that (a) there is no topic with the given name already, and (b) the caller of the caller had permissions (given in the third argument) to add the topic.  Children of $generic_help may want to customize this.";
if (caller != this)
return E_PERM;
endif
topic = args[1];
if (this.(topic) != E_PROPNF)
topic = " " + topic;
endif
return add_property(this, topic, args[2], {args[3], "rc"});
.
#30:20
"Usage:  :delete_topic(string)";
"";
"Delete a new topic with the given name.  If this db has no topic with that name, or if it otherwise can't be deleted, return E_INVARG; if caller_perms() are not adequate, return E_PERM; otherwise return 1.";
topic = $string_utils:trim(args[1]);
if (!this:has_topic(topic))
return E_INVARG;
elseif (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
endif
if (typeof(e = this:real_delete_topic(topic, args[2], caller_perms())) == ERR)
return e;
else
return 1;
endif
.
#30:21
"Usage:  :real_delete_topic(string, value, caller_perms)";
"";
"Callable only from this object.  Does the real work of deleting a topic; it's assumed that (a) there is a topic with the given name, and (b) the caller of the caller had permissions (given in the third argument) to delete the topic.  Children of $generic_help may want to customize this.";
if (caller != this)
return E_PERM;
endif
topic = args[1];
if (this.(" " + topic) != E_PROPNF)
topic = " " + topic;
endif
return delete_property(this, topic);
.
#30:22
"editing_for(prop)";
"This is a fallback answer to `what editor should be used to edit `prop'?' if there's no :editing_for_<prop> verb.";
"Returns {editor, prop}";
"Should be called by player for permissions to work correctly.";
{prop, ?who = caller} = args;
if (caller != this)
"don't let other objects spoof caller.";
who = caller;
endif
if (this:has_topic(prop))
if ($perm_utils:controls(who, this))
return {$help_editor, {{this, prop}, this:get_topic(prop)}};
else
return E_PERM;
endif
else
return pass(prop, who);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835804365, \"Erik\", #74, \"JHM\"}";
.
#30:23
return {@pass(@args), $object_utils, $jtext, $jtext.paragraph, $list_utils, $command_utils, $string_utils, $code_utils, $no_one, $jtext.title, $jtext.header, $jtext.index, $jtext.plaintext, $perm_utils, $note_editor, $jtext.vgroup, $jaddress, $jaddress.help, $jtext.hr, $jtext.itemize_unnumbered, $jtext.hgroup, $jtext.dlist, $jtext.link, $jtext.link.root, $generic_help, $help_editor};
"Metadata 202106";
"Last-Modify: {868907303, \"Erik\", #74, \"JHM\"}";
.
#30:24
if ($__core_init_phase)
pass(@args);
if (this != $generic_help)
for p in (properties(this))
$command_utils:suspend_if_needed(0);
if (index(p, "(core-") == 1)
this.(p[7..length(p) - 1]) = this.(p);
elseif (index(p, "(old-") == 1)
delete_property(this, p);
endif
endfor
this.unconvertible_topics = {};
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {868907165, \"Erik\", #74, \"JHM\"}";
.
#30:25
set_task_perms(player);
for i in (properties(this))
player:notify("");
player:notify_lines(this:dump_topic(i));
player:notify("");
$command_utils:suspend_if_needed(0);
endfor
player:notify("\"*** finished ***");
.
#31:0
player:tell("Sorry, but guest characters are not allowed to change their passwords.");
.
#31:1
if (!caller_perms().wizard)
return;
endif
player = this;
this:notify(tostr("Sorry, but you've been here for ", $string_utils:from_seconds(connected_seconds(this)), " and someone else wants to be a guest now.  Feel free to come back", @$login:player_creation_enabled(player) ? {" or even create your own character if you want..."} | ($login.registration_address ? {" or send mail to ", $login.registration_address, " to obtain a character of your own."} | {"."})));
fork (0)
this:disfunc();
this:confunc();
oldloc = this.location;
move(this, #-1);
move(this, this.home);
"..force enterfunc to be called so that the newbie gets a room description.";
oldloc:announce("In the distance you hear someone's alarm clock going off.");
if (oldloc != this.location)
oldloc:announce(this:name(), " wavers and vanishes into insubstantial mist.");
this.location:announce(this:name(), " suddenly appears, looking rather disoriented.");
else
oldloc:announce(this:name(), " undergoes a wrenching personality shift.");
endif
endfork
.
#31:2
this:log_disconnect();
fork (10)
this:reset();
endfork
pass(@args);
.
#31:3
"Called by #0:connect_player when this object is about to be used as the next guest character.  Usually returns `this', but if for some reason some other guest character should be used, that player object is returned instead";
if (!caller_perms().wizard)
"...caller is not :do_login_command; doesn't matter what we return...";
return this;
elseif ($login:blacklisted(hostname = $string_utils:connection_hostname(connection_name(player))))
return #-2;
elseif (!$guest.enabled)
return #-10;
elseif ($login:bootlisted(hostname))
return #-3;
elseif (!(this in connected_players()))
"...not logged in, no problemo...";
return this;
endif
longest = 900;
"...guests get 15 minutes before they can be dislodged...";
candidate = #-1;
free = {};
count = 0;
for i in (connected_players())
if ($object_utils:isa(i, $guest))
count = count + 1;
if (count >= $login.max_guests)
return #-10;
endif
endif
endfor
for g in ($list_utils:random_permutation(children($guest)))
if (!is_player(g))
"...a toaded guest?...";
elseif (!(g in connected_players()))
"...yay; found an unused guest...";
free = {@free, g};
elseif ((t = connected_seconds(g)) > longest)
longest = t;
candidate = g;
endif
endfor
if (free)
candidate = free[random(length(free))];
elseif (valid(candidate))
"...someone's getting bumped...  ";
candidate:boot();
endif
candidate:reset();
return candidate;
"Metadata 202106";
"Last-Modify: {834602504, \"Ken\", #75, \"JHM\"}";
.
#31:4
return;
.
#31:5
if ($login:player_creation_enabled(player))
player:tell("First @quit, then connect to the MOO again and, rather than doing `connect guest' do `create <name> <password>'");
else
player:tell($login:registration_string());
endif
.
#31:6
return pass(@args);
.
#31:7
":log(islogin,time,where) adds an entry to the connection log for this guest.";
if (caller != this)
return E_PERM;
elseif (length(this.connect_log) < this.max_connect_log)
this.connect_log = {args, @this.connect_log};
else
this.connect_log = {args, @this.connect_log[1..this.max_connect_log - 1]};
endif
.
#31:8
if (caller != #0)
return E_PERM;
else
$guest_log:enter(1, time(), $string_utils:connection_hostname(connection_name(this)));
return pass(@args);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {913134419, \"core initialization\", #3023, \"core\"}";
.
#31:9
if (caller != this)
return E_PERM;
else
cname = connection_name(this) || this.last_connect_place;
$guest_log:enter(0, time(), $string_utils:connection_hostname(cname));
endif
.
#31:10
if (!valid(caller_perms()))
player:tell("Sorry, that information is not available.");
endif
.
#31:11
"Usage:  @request <name> for <email-address>";
if (player != this)
player:tell("You already have a character.");
return E_PERM;
elseif (!this.registration_ok)
return this:tell($string_utils:pronoun_sub(this.noregister_msg));
elseif (!(iobjstr && dobjstr))
return player:tell_lines($code_utils:verb_usage());
elseif (this.request)
notify(player, "Sorry, you appear to have a character already.");
elseif (!(check = $wiz_utils:check_player_request(dobjstr, iobjstr, this))[1])
this:tell_lines(check[2]);
if (check[1] == E_INVARG)
player:tell("Please try again.");
else
player:tell("To register, send mail to ", $login.registration_address, ".");
endif
return;
elseif (typeof(new = $player_db:create_registered_player(name = check[2], {name}, $wiz_utils:random_password(), address = check[3], "", $new_player_log.autoregistration_player, 1)) == OBJ)
this.request = 1;
player:tell("A character named \"", name, "\" has been created for you.  The password has been e-mailed to your account.  Have a nice day.");
else
player:tell(new);
endif
.
#31:12
if (caller_perms() != this)
return E_PERM;
else
return pass(@args);
endif
.
#31:13
"Boots a guest & logs the event. Don't use this unless you really mean it.";
return this:do_boot(iobjstr, index(verb, "!"));
.
#31:14
p = pass(@args);
"We have to go through and reset any special exits the guest found";
"when e disconencts...";
if ($object_utils:isa(caller, $exit) && (typeof(caller.obvious) == LIST))
this.special_exits = setadd(this.special_exits, caller);
endif
return p;
.
#31:15
desc = pass(@args);
if (this.known_player == $nobody)
if (this.show_site)
if (this in connected_players())
where = connection_name(this);
else
where = this.last_connect_place;
endif
if (where)
where = $string_utils:connection_hostname(where);
mesg = ("%P nametag indicates that %s has logged in from " + where) + ".";
else
mesg = "%P nametag indicates that %s has never logged in.";
endif
endif
else
mesg = ("%P nametag indicates that %s is actually " + this.known_player:name()) + "... in disguise!";
endif
if (mesg)
desc = {@desc, $string_utils:pronoun_sub(mesg, this)};
endif
return desc;
.
#31:16
"Usage: @name <name>";
"Usage: @name <name> <password>";
"  You must supply the correct password to use the name of an existing player.";
"";
"Allow guests to set their name, so that people can talk with them using";
"some name other than 'guest'.  Also useful for people who've lost their";
"characters or are testing things.";
"";
"However, we won't allow existing players names to be added unless the";
"correct password is supplied so that guests can't (accidentally or not)";
"masquerade as existing players.";
"";
"The name_guest is added as an alias to the guest so that people can talk to";
"them semi-normally with stage-talk.";
"";
if (player != this)
return;
elseif ((!argstr) || (length(args) > 2))
player:tell_lines($code_utils:verb_usage());
return;
endif
do_name_change = 0;
become_player = $nobody;
name = args[1];
if (length(args) == 1)
if ((valid(candidate = $string_utils:literal_object(name)) && is_player(candidate)) || valid($player_db:find_exact(name)))
player:notify("Sorry.  That name is already being used by someone here, so is not available.");
player:notify("If you are that person, you may type:");
player:notify(("  @name " + name) + " (your password here)");
player:notify("to use that name for this session as a guest.");
else
do_name_change = 1;
endif
else
"They typed <name> <password";
password = args[2];
candidate = $string_utils:match_player(name);
if (!valid(candidate))
player:notify("That name isn't being used.  You didn't need to supply a password.");
do_name_change = 1;
elseif (is_clear_property(candidate, "password"))
player:notify("Sorry, bad password.");
elseif (typeof(candidate.password) == STR)
if (length(candidate.password) < 2)
player:notify("Sorry, bad password.");
elseif (crypt({@args, ""}[2], candidate.password) != candidate.password)
player:notify("Sorry, bad password.");
else
do_name_change = 1;
become_player = candidate;
endif
else
player:notify("Sorry, bad password.");
endif
endif
if (do_name_change == 1)
message = player:dnamec() + " changes %p name to ";
this.guest_name = name;
this:set_aliases({@this.default_aliases, name + "_guest"});
this.known_player = become_player;
player:notify("For this session, your name will appear as: " + this:name());
message = $string_utils:pronoun_sub((message + player:dname()) + ".", player);
player.location:announce_all_but({player}, message);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895717936, \"Ken\", #75, \"JHM\"}";
.
#31:17
if (this.guest_name == "")
return strsub(pass(@args), "_", " ");
else
return ((strsub(pass(@args), "_", " ") + " (") + this.guest_name) + ")";
endif
.
#31:18
"Boots a guest & logs the event. Don't use this unless you really mean it.";
return this:do_boot("", index(verb, "!"));
.
#31:19
"Usage:";
"";
"Do the guts (everything except parsing) of guest-booting.  Lock out the site if the second arg is true.  If no reason is given, prompt for one.";
reason = args[1];
lock = args[2];
if (caller != this)
$error:raise(E_PERM);
endif
if (!this:is_listening())
player:tell($string_utils:nn(this), " isn't connected, and can't be @boot-ed.");
elseif ($object_utils:isa(player, $guest))
player:tell("Sorry, guests can't boot themselves or other guests.");
else
until = time() + (3600 * 24);
host = $string_utils:connection_hostname(this.last_connect_place);
if (lock)
domain = $network:local_domain(host);
$login:bootlist(domain, until);
l = length(host) - length(domain);
if (l)
host[1..l] = ("(" + host[1..l]) + ")";
endif
endif
$you:say_action(this.booting_msg);
boot_player(this);
subject = tostr($string_utils:nn(player), " @booted ", $string_utils:nn(this));
message = {tostr("Reason:  ", (reason || $command_utils:read("a reason for booting " + this:dname())) || "(None given.)")};
if (lock)
message = {@message, ("All from that site banned until " + ctime(until)) + "."};
endif
if (this.show_site)
$mail_agent:send_log_message($code_utils:verb_perms(), player, {$boot_log, $toad_log}, subject, {@message, tostr("Site:  ", host)});
else
$mail_agent:send_log_message($code_utils:verb_perms(), player, {$boot_log}, subject, message);
$mail_agent:send_log_message($code_utils:verb_perms(), player, {$toad_log}, subject, {@message, tostr("Site:  ", host)});
endif
endif
.
#31:20
return {@pass(@args), $string_utils, $login, $object_utils, $guest, this.known_player, $nobody, $list_utils, $guest_log, $player_db, $code_utils, $registration_db, $network, $player_class, $wiz_utils, $player_start, $generic_editor, $mail_agent, $new_player_log, $new_player_log.autoregistration_player, $exit, $error, $you, $command_utils, $boot_log, $toad_log};
.
#31:21
if ($__core_init_phase)
chparent(this, $player);
this.registration_ok = 0;
this.noregister_msg = $pronoun_sub.generic:parse("Sorry, automatic registration not allowed.");
pass(@args);
endif
.
#31:22
"set_description(newdesc) attempts to change this.description to newdesc";
"  => E_PERM   if you don't own this or aren't its parent";
"";
"guest description also sets .default_description if caller_perms().wizard";
if (!($perm_utils:controls(caller_perms(), this) || (this == caller)))
return E_PERM;
elseif (typeof(desc = args[1]) in {LIST, STR})
this.description = desc;
if (caller_perms().wizard)
this.default_description = desc;
endif
return 1;
else
return E_TYPE;
endif
"Copied from root class (#1):set_description by Erik (#74) Sat Aug 19 12:59:49 1995 EDT";
.
#31:23
"Usage:  @describe <object> as <description>";
set_task_perms(player);
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
"...lose...";
elseif (e = dobj:set_description(iobjstr))
if (player == this)
player:notify("Description set.");
else
player:notify("Description (and default description) set.");
endif
else
player:notify(tostr(e));
endif
"Copied from generic player (#6):@describe by Erik (#74) Sat Aug 19 12:57:08 1995 EDT";
.
#31:24
"set_gender(newgender) attempts to change this.gender to newgender";
"  => E_PERM   if you don't own this or aren't its parent";
"  => Other return values as from $gender_utils:match_gender.";
"";
"on guests, sets the default as well if caller_perms().wizard";
if (!($perm_utils:controls(caller_perms(), this) || (this == caller)))
return E_PERM;
endif
if (args[1] in $gender_utils:player_gender_objs())
result = args[1];
else
result = $gender_utils:match_gender(args[1], $gender_utils:player_gender_objs());
endif
if (result || valid(result))
this.gender = result;
if (caller_perms().wizard)
this.default_gender = result;
endif
endif
return result;
"Copied from generic player (#6):set_gender by Erik (#74) Sat Aug 19 13:03:41 1995 EDT";
.
#31:25
"A wrapper around connection_name() for guests. This makes it easy to get at the guest's site information programmatically. If the guest is not connected, this method returns E_INVARG. If site visibility is turned off, or if the guest is really another player in disguise, it returns E_PERM. Otherwise it returns the connection name.";
if (!(this in connected_players()))
return E_INVARG;
elseif (this.show_site && (this.known_player == $nobody))
return connection_name(this);
else
return E_PERM;
endif
.
#31:26
if (!caller_perms().wizard)
raise(E_PERM);
endif
"if (caller != this)";
"  raise(E_PERM);";
"endif";
if (this.location != this.home)
loc = this.location;
move(this, this.home);
loc:announce(this:dnamec("disconnected"), " ", this:verb_sub("decides"), " ", this:ps(), "'s outstayed ", this:pp(), " welcome and ", this:verb_sub("goes"), " home.");
endif
for x in ({"paranoid", "lines", "responsible", "linelen", "linebuffer", "brief", "gaglist", "rooms", "features", "pagelen", "request", "current_message", "messages", "current_folder", "jtext_form", "pronoun_sub_style"})
if ($object_utils:has_property($guest, x))
clear_property(this, x);
endif
endfor
for package in (this:options_packages())
clear_property(this, package.category + "_options");
endfor
this:set_description(this.default_description);
this:set_gender_obj(this.default_gender);
"Removing any name hacks. -- r'm, 10/1/94";
this:set_aliases(this.default_aliases);
this.guest_name = "";
this.known_player = $nobody;
this.jtext_form = "linemode";
for x in (this.contents)
this:eject(x);
endfor
"Clear up following/followers -- DavidSan, 3/13/94";
for x in (this.following)
x.followers = setremove(x.followers, this);
endfor
this.following = {};
for x in (this.followers)
x.following = setremove(x.following, this);
endfor
this.followers = {};
for x in (this.special_exits)
x.obvious = setremove(x.obvious, this);
endfor
this.special_exits = {};
return;
"Metadata 202106";
"Last-Modify: {832048217, \"Ken\", #75, \"JHM\"}";
.
#32:0
"   add(seq,start,end) => seq with range added.";
"remove(seq,start,end) => seq with range removed.";
"  both assume start<=end.";
seq = args[1];
start = args[2];
end = args[3];
s = (start == $minint) ? 1 | $list_utils:find_insert(seq, start - 1);
e = $list_utils:find_insert(seq, after = end + 1);
remove = verb == "remove";
return {@seq[1..s - 1], @((s + remove) % 2) ? {start} | {}, @((e + remove) % 2) ? {after} | {}, @seq[e..length(seq)]};
.
#32:1
":contains(seq,elt) => true iff elt is in seq.";
return ($list_utils:find_insert(@args) + 1) % 2;
.
#32:2
":complement(seq) => the sequence containing all integers *not* in seq.";
seq = args[1];
if (seq && (seq[1] == $minint))
return listdelete(seq, 1);
else
return {$minint, @seq};
endif
.
#32:3
":union(seq1,seq2,...) => union of all sequences...";
":intersection(seq1,seq2,...) => intersection of all sequences...";
intersect = verb != "union";
if ({} in args)
if (intersect)
return {};
else
args = $list_utils:setremove_all(args, {});
endif
endif
if ((alen = length(args)) <= 1)
return args ? args[1] | (intersect ? {$minint} | {});
else
if (intersect)
args = $list_utils:map_arg(this, "complement", args);
endif
lens = {length(args[1])};
for a in (listdelete(args, 1))
lens = {@lens, length(a)};
endfor
args = $list_utils:sort(args, lens);
lens = $list_utils:sort(lens);
for ll in [-length(lens)..-2]
second = args[2];
for i in [1..length(first = args[1]) / 2]
second = this:add(second, first[(2 * i) - 1], first[2 * i] - 1);
endfor
if (length(first) % 2)
s = $list_utils:find_insert(second, (start = first[length(first)]) - 1);
second = {@second[1..s - 1], @(s % 2) ? {start} | {}};
endif
i = $list_utils:find_insert(lens = lens[3..-ll], slen = length(second));
lens = listinsert(lens, slen, i);
args = listinsert(args[3..-ll], second, i);
endfor
return intersect ? this:complement(second) | second;
endif
.
#32:4
if (!(seq = args[1]))
return "empty";
endif
e = tostr((seq[1] == $minint) ? "" | seq[1]);
for i in [2..length(seq)]
e = e + ((i % 2) ? tostr(", ", seq[i]) | ((seq[i] == (seq[i - 1] + 1)) ? "" | tostr("..", seq[i] - 1)));
endfor
return e + ((length(seq) % 2) ? ".." | "");
.
#32:5
":for([n,]seq,obj,verb,@args) => for s in (seq) obj:verb(s,@args); endfor";
set_task_perms(caller_perms());
if (typeof(n = args[1]) == NUM)
args = listdelete(args, 1);
seq = args[1];
else
seq = n;
n = 1;
endif
if (seq[1] == $minint)
return E_RANGE;
endif
object = args[2];
vname = args[3];
args = args[4..length(args)];
for r in [1..length(seq) / 2]
for i in [seq[(2 * r) - 1]..seq[2 * r] - 1]
if (typeof(object:(vname)(@listinsert(args, i, n))) == ERR)
return;
endif
endfor
endfor
if (length(seq) % 2)
i = seq[length(seq)];
while (1)
if (typeof(object:(vname)(@listinsert(args, i, n))) == ERR)
return;
endif
i = i + 1;
endwhile
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {874510925, \"Ken\", #75, \"JHM\"}";
.
#32:6
"extract(seq,array) => list of elements of array with indices in seq.";
if (alen = length(array = args[2]))
e = $list_utils:find_insert(seq = args[1], 1);
s = $list_utils:find_insert(seq, alen);
seq = {@(e % 2) ? {} | {1}, @seq[e..s - 1], @(s % 2) ? {} | {alen + 1}};
ret = {};
for i in [1..length(seq) / 2]
$command_utils:suspend_if_needed(0);
ret = {@ret, @array[seq[(2 * i) - 1]..seq[2 * i] - 1]};
endfor
return ret;
else
return {};
endif
.
#32:7
seq = args[1];
if (!seq)
return {};
else
if (length(seq) % 2)
seq = {@seq, $minint};
endif
l = {};
for i in [1..length(seq) / 2]
for j in [seq[(2 * i) - 1]..seq[2 * i] - 1]
l = {@l, j};
endfor
endfor
return l;
endif
.
#32:8
":fromlist(list) => corresponding sequence.";
return this:from_sorted_list($list_utils:sort(args[1]));
.
#32:9
":from_sorted_list(sorted_list) => corresponding sequence.";
if (!(lst = args[1]))
return {};
else
seq = {i = lst[1]};
next = i + 1;
for i in (listdelete(lst, 1))
if (i != next)
seq = {@seq, next, i};
endif
next = i + 1;
endfor
return (next == $minint) ? seq | {@seq, next};
endif
.
#32:10
return (seq = args[1]) ? seq[1] | E_NONE;
.
#32:11
return (seq = args[1]) ? ((len = length(seq)) % 2) ? $minint - 1 | (seq[len] - 1) | E_NONE;
.
#32:12
":size(seq) => number of elements in seq";
"  for sequences consisting of more than half of the 4294967298 available integers, this returns a negative number, which can either be interpreted as (cardinality - 4294967298) or -(size of complement sequence)";
n = 0;
for i in (seq = args[1])
n = i - n;
endfor
return (length(seq) % 2) ? $minint - n | n;
.
#32:13
":from_string(string) => corresponding sequence or E_INVARG";
"  string should be a comma separated list of numbers and";
"  number..number ranges";
su = $string_utils;
if (!(words = su:explode(su:strip_chars(args[1], " "), ",")))
return {};
endif
parts = {};
for word in (words)
to = index(word, "..");
if ((!to) && su:is_numeric(word))
part = {tonum(word), tonum(word) + 1};
elseif (to)
if (to == 1)
start = $minint;
elseif (su:is_numeric(start = word[1..to - 1]))
start = tonum(start);
else
return E_INVARG;
endif
end = word[to + 2..length(word)];
if (!end)
part = {start};
elseif (!su:is_numeric(end))
return E_INVARG;
elseif ((end = tonum(end)) >= start)
part = {start, end + 1};
else
part = {};
endif
else
return E_INVARG;
endif
parts = {@parts, part};
endfor
return this:union(@parts);
.
#32:14
":firstn(seq,n) => first n elements of seq as a sequence.";
if ((n = args[2]) <= 0)
return {};
endif
l = length(seq = args[1]);
s = 1;
while (s <= l)
n = n + seq[s];
if ((s >= l) || (n <= seq[s + 1]))
return {@seq[1..s], n};
endif
n = n - seq[s + 1];
s = s + 2;
endwhile
return seq;
.
#32:15
":lastn(seq,n) => last n elements of seq as a sequence.";
n = args[2];
if ((l = length(seq = args[1])) % 2)
return {$minint - n};
else
s = l;
while (s)
n = seq[s] - n;
if (n >= seq[s - 1])
return {n, @seq[s..l]};
endif
n = seq[s - 1] - n;
s = s - 2;
endwhile
return seq;
endif
.
#32:16
":range(start,end) => sequence corresponding to [start..end] range";
return ((start = args[1]) <= (end = args[2])) ? {start, end + 1} | {};
.
#34:0
"$you:say_action(message [,who [, thing[, where[, dobj[, iobj]]]]]).";
"announce 'message' with pronoun substitution as if it were just ";
"  where:announce_all($string_utils:pronoun_sub(message, who, thing, where, dobj, iobj)); ";
"except that all involved parties get modified messages, with the appropriate use of 'you' instead of their name.";
"who   default player";
"thing default object that called this verb";
"where default who.location";
"Any of these may be lists, of the form {<list of objs>, @<args to $string_utils:english_list>}.";
msg = args[1];
p_s_args = args[2..length(args)];
parties = $pronoun_sub:parse_parties(p_s_args, caller);
wheres = parties[3][1];
{msg, parties} = $pronoun_sub:flatten_message(msg, parties);
tell = $string_utils:pronoun_sub(msg, @parties);
party_set = $set_utils:union(@$list_utils:slice(parties));
for where in (wheres)
"if ($object_utils:has_callable_verb(where, \"announce_all_but\"))";
where:announce_all_but(party_set, tell);
"endif";
endfor
for p in (party_set)
this:say(msg, p, parties);
endfor
"Copied from you (#36):say_action(new) by splat (#3024) Sat Apr 27 00:40:34 1996 EDT";
.
#34:1
if (typeof(who = args[1]) == LIST)
l = {};
for p in (who)
l = {@l, this:_you(p)};
endfor
return l;
endif
return `who:you() ! ANY => who';
"return !is_player(who = args[1]) || who:misc_option(\"third_person\") ? who | this;";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {858904328, \"splat\", #3024, \"JHM\"}";
.
#34:2
"say(msg, target, parties)";
msg = args[1];
target = args[2];
parties = args[3];
for i in [1..length(parties)]
for j in [1..length(parties[i][1])]
if (parties[i][1][j] == target)
parties[i][1][j] = this:_you(target);
endif
endfor
endfor
tell = $string_utils:pronoun_sub(msg, @parties);
target:tell(tell);
.
#34:3
return (verb == "verb_sub") ? $english:get_conj(args[1], 1) | $english:get_noun(args[1]);
.
#34:4
return strsub(args[1], "ou", "our");
.
#34:5
"$you:say_action(message [,who [, thing[, where[, dobj[, iobj]]]]]).";
"announce 'message' with pronoun substitution as if it were just ";
"  where:announce_all($string_utils:pronoun_sub(message, who, thing, where, dobj, iobj)); ";
"except that all involved parties get modified messages, with the appropriate use of 'you' instead of their name.";
"who   default player";
"thing default object that called this verb";
"where default who.location";
"Any of these may be lists, of the form {<list of objs>, @<args to $string_utils:english_list>}.";
msg = args[1];
p_s_args = args[2..length(args)];
parties = $pronoun_sub:parse_parties(p_s_args, caller);
{msg, parties} = $pronoun_sub:flatten_message(msg, parties);
tell = $string_utils:pronoun_sub(msg, @parties);
party_set = $set_utils:union(@$list_utils:slice(parties));
for where in (parties[3][1])
"if ($object_utils:has_callable_verb(where, \"announce_all_but\"))";
where:announce_all_but(party_set, tell);
"endif";
endfor
for p in (party_set)
this:say(msg, p, parties);
endfor
.
#35:0
return "unknown " + pass(@args);
.
#36:0
"eval(code)";
"Evaluate code with $no_one's permissions (so you won't damage anything).";
"If code does not begin with a semicolon, set this = caller (in the code to be evaluated) and return the value of the first `line' of code.  This means that subsequent lines will not be evaluated at all.";
"If code begins with a semicolon, set this = caller and let the code decide for itself when to return a value.  This is how to do multi-line evals.";
set_task_perms(this);
exp = args[1];
if (exp[1] != ";")
return eval(tostr("this=", caller, "; return ", exp, ";"));
else
return eval(tostr("this=", caller, ";", exp, ";"));
endif
.
#36:1
return 0;
.
#36:2
":eval_d(code)";
"exactly like :eval except that the d flag is unset";
"Evaluate code with $no_one's permissions (so you won't damage anything).";
"If code does not begin with a semicolon, set this = caller (in the code to be evaluated) and return the value of the first `line' of code.  This means that subsequent lines will not be evaluated at all.";
"If code begins with a semicolon, set this = caller and let the code decide for itself when to return a value.  This is how to do multi-line evals.";
set_task_perms(this);
exp = args[1];
if (exp[1] != ";")
return $code_utils:eval_d(tostr("this=", caller, "; return ", exp, ";"));
else
return $code_utils:eval_d(tostr("this=", caller, ";", exp, ";"));
endif
.
#36:3
"call_verb(object, verb name, args)";
"Call verb with $no_one's permissions (so you won't damage anything).";
"One could do this with $no_one:eval, but ick.";
set_task_perms(this);
if (args[1] != this)
return args[1]:(args[2])(@args[3]);
else
raise(E_PERM);
endif
.
#36:4
return $string_utils:capitalize(args[1]);
.
#37:0
":load() -- reloads the player_db with the names of all existing players.";
"This routine calls suspend() if it runs out of time.";
".frozen is set to 1 while the load is in progress so that other routines are warned and don't try to do any updates.  Sometimes, an update is unavoidable (e.g., player gets recycled) in which case the offending routine should set .frozen to 2, causing the load to start over at the beginning.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
this:clearall();
this.frozen = 1;
for p in (players())
this:suspend_restart(p);
"... note that if a player is recycled or toaded during the suspension,...";
"... it won't be removed from the for loop iteration; thus this test:     ";
if (valid(p) && is_player(p))
if (typeof(po = this:find_exact(p.name)) == ERR)
player:tell(p:name(), ":  ", po);
return;
elseif (po != p)
if (valid(po) && is_player(po))
player:tell("name `", p:name(), "' for ", p, " subsumes alias for ", po:name(), "(", po, ").");
endif
this:insert(p.name, p);
endif
for a in (p.aliases)
this:suspend_restart(p);
if (index(a, " "))
"don't bother";
elseif (typeof(ao = this:find_exact(a)) == ERR)
player:tell(a, ":  ", ao);
return;
elseif (!(valid(ao) && is_player(ao)))
this:insert(a, p);
elseif (ao != p)
player:tell("alias `", a, "' for ", p:name(), "(", p, ") used by ", ao:name(), "(", ao, ").");
endif
endfor
endif
endfor
this.frozen = 0;
.
#37:1
":check() -- checks for recycled and toaded players that managed not to get expunged from the db.";
for p in (properties($player_db))
if ((ticks_left() < 500) || (seconds_left() < 2))
player:tell("...", p);
suspend(0);
endif
if (p[1] == " ")
for o in (this.(p)[4])
if (valid(o) && is_player(o))
else
player:tell(".", p, "==", $string_utils:print(this.(p)), " <= ", o);
endif
endfor
endif
endfor
player:tell("done.");
.
#37:2
for p in (players())
if (p.__init_for_core_flag != p)
return "wait";
endif
endfor
if ($__core_init_phase)
pass(@args);
this:load();
this.stupid_names = $set_utils:union($code_utils._all_preps, $list_utils:map_prop($object_utils:descendants($gender), "name"), {"test", "me", "here"}, $language:selector_words());
endif
.
#37:3
"OBSOLETE";
"";
"Verbs that used this should use :name_ok or :alias_ok (for which see the respective help texts and code) instead.";
name = args[1];
0;
"Usage:  :available(name[, who])";
"";
"Returns 1 if a name is available for use by <who> (default is caller), or the object id of whoever is currently using it, or 0 if the name is otherwise forbidden.";
"";
"If $player_db is not .frozen and :available returns 1, then $player:set_name will succeed.";
if (length(args) > 1)
who = args[2];
else
who = caller;
endif
if (((!name) || (index(name, " ") || ((name in this.stupid_names) || (index(name, "\\") || index("*#()+", name[1]))))) || (length(name) < 2))
return 0;
elseif (index(name, "_guest") && (!$object_utils:isa(who, $guest)))
return 0;
elseif (who = this:alias_taken(name))
return who[1];
else
return 1;
endif
.
#37:4
"used during :load to do the usual out-of-time check.";
"if someone makes a modification during the suspension (indicated by this.frozen being set to 2), we have to restart the entire load.";
if (caller != this)
return E_PERM;
elseif ($command_utils:running_out_of_time())
player:tell("...", args[1]);
suspend(0);
if (this.frozen != 1)
player:tell("...argh... restarting $player_db:load...");
fork (0)
this:load();
endfork
kill_task(task_id());
endif
endif
.
#37:5
"Make a new player, with the given name and aliases, if possible.";
"";
"Return the new player, or an error or string explaining why not.";
"";
"Password must be provided, as a literal (encrypted) value.";
"";
"If owner is $nothing, the new player will own itself.";
name = args[1];
aliases = args[2];
parent = args[3];
owner = args[4];
password = args[5];
if (!caller_perms().wizard)
return E_PERM;
elseif (this.frozen)
return "Sorry, can't create any new players right now.  Try again in a few minutes.";
elseif (index(name, " "))
return "Sorry, spaces aren't allowed in player names.  Consider using a dash (-) or underscore (_) instead.";
elseif (!this:name_ok(name, nameowner = $object_utils:isa(parent, $guest) ? $guest | $nothing))
return tostr("The name ", name, " is not available.");
elseif (badaliases = this:aliases_bad(aliases = setadd(aliases, name), nameowner))
return badaliases;
elseif (typeof(new = $quota_utils:bi_create(parent, owner)) != OBJ)
return tostr("Error creating player object:  ", new);
else
new:set_name(name);
new:set_aliases(aliases);
new.password = password;
if (!(e = $wiz_utils:set_player(new, owner != $nothing)))
$recycler:_recycle(new);
return tostr("Error setting player flag:  ", e);
endif
new.last_connect_time = $maxint;
"Last disconnect time is creation time, until they login.";
new.last_disconnect_time = time();
move(new, $player_start);
return new;
endif
.
#37:6
"Usage:  $player_db:aliases_bad(aliases[, who])";
"";
"If any of the given list of aliases are not available AS ALIASES, return a string explaining the problem.  Otherwise, return false.";
{aliases, ?who = caller} = args;
bad = {};
for alias in (aliases)
if (!this:alias_ok(alias, who))
bad = setadd(bad, alias);
endif
endfor
if (bad)
if (length(bad) == 1)
return tostr("The alias ", bad[1], " is not available.");
else
return tostr("The aliases ", $string_utils:english_list(bad), " are not available.");
endif
else
return 0;
endif
.
#37:7
"Usage:  :alias_taken(alias)";
"";
"Returns true iff a player currently has the given alias.  True value is actually {the person who has it}.";
alias = args[1];
if (valid(who = this:find_exact(alias)))
return {who};
endif
.
#37:8
"Usage:";
"";
"Return true if the alias is of a type that just won't be entered into the database (and hence can't be used to match players).";
"";
"Note:  These shouldn't be allowed for names, but they're okay for aliases.";
{alias} = args;
return (index(alias, " ") || index(alias, "\\")) || index("$*#()+", alias[1]);
.
#37:9
"Usage:";
"";
"Return true if the given string is acceptable as an alias for the given person.";
"Requirements are less stringent than requirements for names.";
{alias, ?who = caller} = args;
return this:alias_ignored(alias) || (!this:alias_bad(alias, who));
.
#37:10
"Usage:";
"";
"Return true if the given string is unacceptable as an alias for the given person.";
"Requirements are less stringent than requirements for names.";
{alias, who} = args;
return ((((taken = this:alias_taken(alias)) && (taken != {who})) || (alias in this.stupid_names)) || (length(alias) < 2)) || (match(alias, "_guest$") && (!$object_utils:isa(who, $guest)));
.
#37:11
"Usage:  :name_ok(alias[, who])";
"";
"Return true if the given string is acceptable as a name for the given person.";
{alias, ?who = caller} = args;
return !(this:alias_ignored(alias) || this:alias_bad(alias, who));
.
#37:12
"Usage:  :gensym_name (base)";
"        :gensym_alias(base)";
"";
"Check base1, base2, base3, etc, until one is found that is an acceptable name/alias.  Return it.";
"";
"Return E_INVARG if we can't get one (without suspending).";
base = args[1];
type = strsub(verb, "gensym_", "");
ok = type + "_ok";
n = 1;
if (!(this:alias_taken(base) || this:(ok)(base)))
return E_INVARG;
endif
while (!$command_utils:running_out_of_time())
if (this:(ok)(name = tostr(base, n)))
return name;
endif
n = n + 1;
endwhile
return E_INVARG;
.
#37:13
"Make a new registered player, with the given name and aliases, if possible.";
"";
"Return the new player, or an error or string explaining why not.";
"";
"Password must be provided, as an unencrypted string.  Email address must be provided, but may be blank.  (If blank, nothing will be entered in the registration database.)  Comments (a string) likewise.";
"";
"Authority is the entity from which mail to $new_player_log should appear to be sent.";
"";
"If sendmail is true, a standard message will be sent to the registered email address.";
name = args[1];
aliases = args[2];
password = args[3];
email = args[4];
comments = args[5];
authority = args[6];
sendmail = args[7];
if (!caller_perms().wizard)
return E_PERM;
elseif (typeof(new = this:create_player(name, aliases, $player_class, $nothing, crypt(password))) != OBJ)
return new;
else
if (email)
if (comments)
$registration_db:add(new, email, comments);
else
$registration_db:add(new, email);
endif
new.email_address = email;
if ((sendmail && email) && $network.active)
result = $wiz_utils:send_new_player_mail(tostr("From ", player.name, "@", $network.moo_name, ":"), name, email, password);
"should probably undo on non-zero result, but bleah";
endif
endif
$quota_utils:initialize_quota(new);
fork (0)
"forked to enable $login:create to return-without-suspending";
"when $login.create_enabled is true.";
$mail_agent:send_message(authority, $new_player_log, tostr(name, " (", new, ")"), tostr(email, comments ? " " + comments | ""));
endfork
return new;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {845599956, \"Ken\", #75, \"JHM\"}";
.
#37:14
"Usage:  $player_db:check_name_request(name, who)";
"";
"See if <name> is an acceptable name for <who> to assume.  If is_player(<who>), e can take one of eir own aliases.";
"";
"Return {E_INVARG, reason} if the name is unacceptable, or {true, name} if the name is okay.";
"";
"Reason is an explanation you can feed to :tell_lines().";
name = args[1];
who = args[2];
if (!caller_perms().wizard)
return E_PERM;
"accesses registration information -- wiz only";
endif
if (match(name, "^<.*>$"))
name = name[2..length(name) - 1];
endif
if (!name)
return {E_INVARG, "You need to give a name."};
elseif (index(name, " "))
return {E_INVARG, "Names with spaces are not allowed.  Use dashes or underscores."};
elseif ($player_db:alias_taken(name) && (!(name in who.aliases)))
return {E_INVARG, tostr("The name `", name, "' is already in use.")};
elseif (!$player_db:name_ok(name))
return {E_INVARG, tostr(("The name `" + name) + "' is not allowed.")};
else
return {1, name};
endif
.
#38:0
"get_verb/noun(spec, object) => verb/noun in number according to object form";
"The object is checked to see whether it is singular or plural.";
"This is here for backwards compatibility only.  See the corresponding verbs on $english.";
return $english:(verb)(args[1], args[2]:is_plural());
.
#38:1
return $list_utils:map_verb(this:player_gender_objs(@args), "gender_name");
.
#38:2
return $list_utils:map_verb(this:gender_objs(@args), "gender_name");
.
#38:3
return $object_utils:descendants($gender);
.
#38:4
"Usage: $gender_utils:match_gender[_obj|_name](string[, genders])";
"";
"Finds a gender in <genders> matching <string>.  <String> can be a literal object specification (see `help $string_utils:literal_object'), or any prefix of a .gender_name property of an item in <genders>.";
"";
"Return values are the traditional ones for a MOO matching verb (matched object, $nothing, $ambiguous_match, or $failed_match).  The corresponding *_failed verb is $gender_utils:gender_match_failed (see `$gender_utils:gender_match_failed).";
"";
"If called as :match_gender_name, will return the result of the :gender_name verb of the matched object rather than the object itself.";
"";
"This verb was more or less rewritten by James on 14 February 1997 to fit 1.8.0 coding standards and allow it to run +d.  Since the documentation (assuming there ever was any) disappeared sometime before that everything in this verb was created by analyzing the old code and core callers, including the charter and documentation.  Needless to say, problems may crop up.";
"";
{string, ?genders = this:gender_objs()} = args;
if (!string)
return $nothing;
endif
"if the following blows up with E_TYPE or something, well, it should.";
if (!((ret = $string_utils:literal_object(string)) in genders))
ret = $string_utils:match(string, genders, "gender_name");
endif
if ((!valid(ret)) || (verb != "match_gender_name"))
return ret;
endif
return ret:gender_name();
"Metadata 202106";
"Last-Modify: {855981553, \"James\", #2142, \"JHM\"}";
.
#38:5
"Usage: gender_match_failed(match_result, string)";
"Prints a message if string does not match the match_result.  Generally used after the match_result is derived from a :match_gender(string).";
match_result = args[1];
string = args[2];
if (typeof(match_result) != OBJ)
return 0;
endif
tell = $perm_utils:controls(caller_perms(), player) ? "notify" | "tell";
if ((index(string, "#") == 1) && ($code_utils:toobj(string) != E_TYPE))
"...avoid the `I don't know which `#-2' you mean' message...";
if (!valid(match_result))
player:(tell)(tostr(string, " does not exist."));
endif
return !valid(match_result);
elseif (match_result == $nothing)
player:(tell)("You must supply a gender.");
elseif (match_result == $failed_match)
player:(tell)(tostr("There are no genders matching \"", string, "\"."));
elseif (match_result == $ambiguous_match)
player:(tell)(tostr("There is more than one gender that matches \"", string, "\"."));
elseif (!valid(match_result))
player:(tell)(tostr(match_result, " does not exist."));
else
return 0;
endif
return 1;
"Copied from command utilities (#56):object_match_failed by Doug (#107) Tue May 17 23:12:53 1994 EDT";
.
#38:6
return this.player_gender_objs;
"Metadata 202106";
"Last-Modify: {879922284, \"William_Lee\", #811, \"JHM\"}";
.
#38:7
if (!$__core_init_phase)
raise(E_PERM);
endif
pass(@args);
if (this == $gender_utils)
for property in ({"is_plural", "have", "be", "pronouns", "genders", "ps", "po", "pp", "pq", "pr", "psc", "poc", "ppc", "pqc", "prc", "c_be", "player_genders"})
delete_property(this, property);
endfor
this.player_gender_objs = {@children($it), $royal};
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {879923968, \"William_Lee\", #811, \"JHM\"}";
.
#39:0
"Given a time() or ctime()-style date, this returns the full name of the day.";
if (typeof(args[1]) == NUM)
time = ctime(args[1]);
elseif (typeof(args[1]) == STR)
time = args[1];
else
return E_TYPE;
endif
dayabbr = $string_utils:explode(time)[1];
return this.days[dayabbr in this.dayabbrs];
.
#39:1
"Given a time() or ctime()-style date, this returns the full name";
"of the month.";
if (typeof(args[1]) == NUM)
time = ctime(args[1]);
elseif (typeof(args[1]) == STR)
time = args[1];
else
return E_TYPE;
endif
monthabbr = $string_utils:explode(time)[2];
return this.months[monthabbr in this.monthabbrs];
.
#39:2
"Return a time in the form [h]h[:mm[:ss]] {a.m.|p.m.}.  Args are";
"[1]   either a time()- or a ctime()-style date, and";
"[2]   (optional) the precision desired--1 for hours, 2 for minutes,";
"        3 for seconds.  If not given, precision defaults to minutes";
"changed to capitalize A.M. and P.M. on 9 Sept 94, by Michele";
if (typeof(args[1]) == NUM)
time = ctime(args[1]);
elseif (typeof(args[1]) == STR)
time = args[1];
else
return E_TYPE;
endif
if (length(args) > 1)
precision = args[2];
else
precision = 2;
endif
time = $string_utils:explode(time)[4];
hour = tonum(time[1..2]);
if (hour == 0)
time = ("12" + time[3..(precision * 3) - 1]) + " A.M.";
elseif (hour == 12)
time = time[1..(precision * 3) - 1] + " p.m.";
elseif (hour > 12)
time = (tostr(hour - 12) + time[3..(precision * 3) - 1]) + " P.M.";
else
time = (tostr(hour) + time[3..(precision * 3) - 1]) + " A.M.";
endif
return time;
.
#39:3
"Given string hh:mm:ss ($string_utils:explode(ctime(time))[4]), this returns";
"the number of seconds elapsed since 00:00:00.  I can't remember why I";
"created this verb, but I'm sure it serves some useful purpose.";
return (((60 * 60) * tonum(args[1][1..2])) + (60 * tonum(args[1][4..5]))) + tonum(args[1][7..8]);
.
#39:4
r = 10000;
h = (r * r) + (r / 2);
time = (args == {}) ? time() | args[1];
t = ((time + 120) % 86400) / 240;
s = (5 * ((time - 14957676) % 31556952)) / 438291;
phi = (s + t) + this.corr;
cs = $math_utils:cos(s);
spss = ((($math_utils:sin(phi) * $math_utils:sin(s)) + h) / r) - r;
cpcs = ((($math_utils:cos(phi) * cs) + h) / r) - r;
return (((((this.stsd * cs) - (this.ctcd * cpcs)) - (this.ct * spss)) + h) / r) - r;
.
#39:5
"Given a string such as returned by ctime(), return the corresponding time-in-seconds-since-1970 time returned by time(), or E_DIV if the format is wrong in some essential way.";
words = $string_utils:explode(args[1]);
if (length(words) == 5)
"Arrgh!  the old ctime() didn't return a time zone, yet it arbitrarily decides whether it's standard or daylight savings time.  URK!!!!!";
words = listappend(words, "PST");
endif
if ((((length(words) != 6) || (length(hms = $string_utils:explode(words[4], ":")) != 3)) || (!(month = words[2] in this.monthabbrs))) || (!(zone = $list_utils:assoc(words[6], this.timezones))))
return E_DIV;
endif
year = tonum(words[5]);
day = ({-1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}[month] + tonum(words[3])) + (year * 366);
zone = zone[2];
return (((((((((((((day - ((day + 1038) / 1464)) - ((day + 672) / 1464)) - ((day + 306) / 1464)) - ((day + 109740) / 146400)) - ((day + 73140) / 146400)) - ((day + 36540) / 146400)) - 719528) * 24) + tonum(hms[1])) + zone) * 60) + tonum(hms[2])) * 60) + tonum(hms[3]);
.
#39:6
s = args[1];
if (s < 0)
return "-" + this:(verb)(-s);
endif
m = s / 60;
s = s % 60;
if (m)
ss = tostr((s < 10) ? ":0" | ":", s);
h = m / 60;
m = m % 60;
if (h)
ss = tostr((m < 10) ? ":0" | ":", m, ss);
d = h / 24;
h = h % 24;
return tostr(@d ? {d, (h < 10) ? ":0" | ":"} | {}, h, ss);
else
return tostr(m, ss);
endif
else
return tostr(s);
endif
.
#39:7
"english_time(time [,reference time]): returns the time as a string of";
"years, months, days, minutes and seconds using the reference time as the";
"start time and incrementing forwards. it can be given in either ctime() or";
"time() format. if a reference time is not given, it is set to time().";
$command_utils:suspend_if_needed(0);
_time = args[1];
reftime = (length(args) > 1) ? args[2] | time();
_ctime = (typeof(reftime) == NUM) ? ctime(reftime) | reftime;
seclist = {60, 60, 24};
units = {"year", "month", "day", "hour", "minute", "second"};
timelist = {};
for unit in (seclist)
timelist = {_time % unit, @timelist};
_time = _time / unit;
endfor
months = 0;
month = _ctime[5..7] in $time_utils.monthabbrs;
year = tonum(_ctime[21..24]);
"the following should really be a verb/property. attribution: the ";
"algorithm used is from the eminently eminent g7.";
monthlen = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
while (_time >= (days = monthlen[month] + (((month == 2) && ((year % 4) == 0)) && (!((year % 400) in {100, 200, 300})))))
_time = _time - days;
months = months + 1;
if ((month = month + 1) > 12)
year = year + 1;
month = 1;
endif
endwhile
timelist = {months / 12, months % 12, _time, @timelist};
for unit in (units)
i = unit in units;
if (timelist[i] > 0)
units[i] = ((tostr(timelist[i]) + " ") + units[i]) + ((timelist[i] == 1) ? "" | "s");
else
units = listdelete(units, i);
timelist = listdelete(timelist, i);
endif
endfor
$command_utils:suspend_if_needed(0);
if (units)
return $string_utils:english_list(units);
else
return "0 seconds";
endif
.
#39:8
"from_day(day_of_week,which)";
"numeric time (seconds since 1970) corresponding to midnight (PST) of the given weekday.  Use either the name of the day or a 1..7 number (1==Sunday,...)";
"  which==-1 => use most recent such day.";
"  which==+1 => use first upcoming such day.";
"  which==0  => use closest such day.";
"larger (absolute) values for which specify a certain number of weeks into the future or past.";
if (!(tonum(day = args[1]) || (day = $string_utils:find_prefix(day, this.days))))
return E_DIV;
endif
delta = {288000, 374400, 460800, 547200, 28800, 115200, 201600}[tonum(day)];
time = time() - delta;
dir = {@args, 0}[2];
if (dir)
time = (time / 604800) + ((dir > 0) ? dir | (dir + 1));
else
time = (time + 302400) / 604800;
endif
return (time * 604800) + delta;
.
#39:9
"from_month(month,which[,d])";
"numeric time (seconds since 1970) corresponding to midnight (PST) of the dth (first) day of the given month.  Use either the month name or a 1..12 number (1==January,...)";
"  which==-1 => use most recent such month.";
"  which==+1 => use first upcoming such month.";
"  which==0  => use closest such month.";
"larger (absolute) values for which specify a certain number of years into the future or past.";
if (!(tonum(month = args[1]) || (month = $string_utils:find_prefix(month, this.months))))
return E_DIV;
endif
delta = ({0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}[month] + {@args, 1}[3]) - 1;
day = (time() - 28800) / 86400;
day = (day - ((day + 672) / 1461)) - delta;
dir = {@args, 0}[2];
if (dir)
day = ((day / 365) + dir) + (dir <= 0);
else
day = ((2 * day) + 365) / 730;
endif
day = (day * 365) + delta;
day = day + ((day + 671) / 1460);
return (day * 86400) + 28800;
.
#39:10
"Takes a time that is midnight PST and converts it to the nearest PDT midnight time if it's during that part of the year where we use PDT.";
time = args[1];
return time - (3600 * (((tonum(ctime(time)[12..13]) + 12) % 24) - 12));
.
#39:11
"Usage:  $time_utils:time_sub(string, time)";
"";
"Works like pronoun substitution, but substitutes time stuff.  Returns a string.";
"";
"time is an optional integer in time() format.  If omitted, time() is used.";
"";
"Macros which are unknown are ignored. $Q -> the empty string.";
"Terminal $ are ignored.";
"";
"$H -> hour #. $M -> min #. $S -> second #. 24-hour format, fixed width.";
"$h, $m, $s same x/c have not-fixed format. 00:03:24 vs. 0:3:24";
"$O/$o -> numeric hour in 12-hour format.";
"$D -> long day name. $d -> short day name.";
"$N -> long month name. $n -> short month name.";
"$Y -> long year # (e.g. '1991'). $y -> short year # (e.g. '91')";
"$Z -> the time zone    (added in by r'm later)";
"$P/$p -> AM/PM, or am/pm.";
"$T -> date number. $t -> date number with no extra whitespace etc.";
"$1 -> Month in fixed-width numeric format (01-12)   (added by dpk)";
"$2 -> Month in nonfixed numeric format (1-12)";
"$3 -> Date in fixed-width format, 0-fill";
"$$ -> $.";
res = "";
thestr = args[1];
if (length(args) > 1)
thetime = tonum(args[2]);
else
thetime = time();
endif
if ((typeof(thestr) != STR) || (typeof(thetime) != NUM))
player:tell("Bad arguments to time_subst.");
return;
endif
itslength = length(thestr);
if (!itslength)
return "";
endif
done = 0;
curchar = 1;
cctime = ctime(thetime);
while (!done)
if (thestr[curchar] != "$")
res = res + thestr[curchar];
else
"Now we begin substitution.";
curchar = curchar + 1;
thechar = thestr[curchar];
if (curchar > length(thestr))
return res;
endif
if (thechar == "$")
res = res + "$";
elseif (!strcmp(thechar, "h"))
res = res + $string_utils:trim(tostr(tonum(cctime[12..13])));
elseif (thechar == "H")
res = res + cctime[12..13];
elseif (!strcmp(thechar, "m"))
res = res + $string_utils:trim(tostr(tonum(cctime[15..16])));
elseif (thechar == "M")
res = res + cctime[15..16];
elseif (!strcmp(thechar, "s"))
res = res + $string_utils:trim(tostr(tonum(cctime[18..19])));
elseif (thechar == "S")
res = res + cctime[18..19];
elseif (!strcmp(thechar, "D"))
res = res + $time_utils:day(thetime);
elseif (thechar == "d")
res = res + cctime[1..3];
elseif (!strcmp(thechar, "N"))
res = res + $time_utils:month(thetime);
elseif (thechar == "n")
res = res + cctime[5..7];
elseif (!strcmp(thechar, "T"))
res = res + cctime[9..10];
elseif (thechar == "t")
res = res + $string_utils:trim(cctime[9..10]);
elseif (thechar == "O")
res = res + tostr(((tonum(cctime[12..13]) + 11) % 12) + 1);
elseif (!strcmp(thechar, "p"))
res = res + ((tonum(cctime[12..13]) >= 12) ? "pm" | "am");
elseif (thechar == "P")
res = res + ((tonum(cctime[12..13]) >= 12) ? "PM" | "AM");
elseif (!strcmp(thechar, "y"))
res = res + cctime[23..24];
elseif (thechar == "Y")
res = res + cctime[21..24];
elseif (thechar == "Z")
res = res + cctime[26..28];
elseif (thechar == "1")
res = res + $string_utils:right(tostr($string_utils:explode(cctime)[2] in this.monthabbrs), 2, "0");
elseif (thechar == "2")
res = res + tostr($string_utils:explode(cctime)[2] in this.monthabbrs);
elseif (thechar == "3")
res = res + $string_utils:subst(cctime[9..10], {{" ", "0"}});
endif
endif
curchar = curchar + 1;
if (curchar > itslength)
done = 1;
endif
endwhile
return res;
.
#39:12
":lunar_phase(time) => STR";
p = (args[1] - this.lunar_phase) % this.lunar_period;
if ((p < 43200) || (p > (this.lunar_period - 43200)))
return "full";
elseif ((p > ((this.lunar_period / 2) - 43200)) && (p < ((this.lunar_period / 2) + 43200)))
return "new";
elseif (((p > ((this.lunar_period / 4) - 129600)) && (p < ((this.lunar_period / 4) + 129600))) || ((p > (((this.lunar_period * 3) / 4) - 129600)) && (p < (((this.lunar_period * 3) / 4) + 129600))))
ret = "half full";
elseif ((p < (this.lunar_period / 4)) || (p > ((this.lunar_period * 3) / 4)))
ret = "gibbous";
else
ret = "crescent";
endif
if (p > (this.lunar_period / 2))
return ret + " and waxing";
else
return ret + " and waning";
endif
.
#39:13
"$time_utils:parse_english_time_interval(n1,u1,n2,u2,...)";
"or $time_utils:parse_english_time_interval(\"n1 u1[,] [and] n2[,] u2 [and] ...\")";
"There must be an even number of arguments, all of which must be strings,";
" or there must be just one argument which is the entire string to be parsed.";
"The n's are are numeric strings, and the u's are unit names.";
"The known units are in $time_utils.time_units,";
" which must be kept sorted with bigger times at the head.";
"Returns the time represented by those words.";
"For example,";
" $time_utils:parse_english_time_interval(\"30\",\"secs\",\"2\",\"minutes\",\"31\",\"seconds\") => 181";
if ((length(args) == 1) && index(args[1], " "))
return $time_utils:parse_english_time_interval(@$string_utils:words(args[1]));
endif
a = $list_utils:setremove_all(args, "and");
nargs = length(a);
if (nargs % 2)
return E_ARGS;
endif
nsec = 0;
n = 0;
for i in [1..nargs]
if ((i % 2) == 1)
if ($string_utils:is_numeric(a[i]))
n = tonum(a[i]);
elseif (a[i] in {"a", "an"})
n = 1;
elseif (a[i] in {"no"})
n = 0;
else
return E_INVARG;
endif
else
unit = a[i];
if (unit[length(unit)] == ",")
unit = unit[1..length(unit) - 1];
endif
ok = 0;
for entry in ($time_utils.time_units)
if ((!ok) && (unit in entry[2..length(entry)]))
nsec = nsec + (entry[1] * n);
ok = 1;
endif
endfor
if (!ok)
return E_INVARG;
endif
endif
endfor
return nsec;
.
#39:14
return {@pass(@args), $string_utils, $math_utils, $list_utils, $command_utils, $time_utils};
.
#41:0
"For changing mailing list aliases, we check to make sure that none of the aliases match existing mailing list aliases.  Aliases containing spaces are not used in addresses and so are not subject to this restriction ($mail_agent:match will not match on them, however, so they only match if used in the immediate room, e.g., with match_object() or somesuch).";
"  => E_PERM   if you don't own this";
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
else
for a in (aliases = args[1])
if ((!index(a, " ")) && (valid(p = $mail_agent:match(a, #-1)) && ((p != this) && (a in p.aliases))))
player:tell("Mailing list name \"", a, "\" in use on ", p:name(), "(", p, ")");
aliases = setremove(aliases, a);
endif
endfor
return pass(aliases);
endif
.
#41:1
if ((!$perm_utils:controls(caller_perms(), player)) && (caller_perms() != $hacker))
return E_PERM;
endif
set_task_perms(caller_perms());
names = {};
for a in (this.aliases)
if (!index(a, " "))
names = setadd(names, strsub(a, "_", "-"));
endif
endfor
namelist = "*" + (names ? $string_utils:from_list(names, ", *") | tostr(this));
flags = this:subscribed(player);
if (this:is_writable_by(player))
flags = {"[Writable]", @flags};
elseif (typeof(this.readers) != LIST)
flags = {"[Public]", @flags};
elseif ((!flags) && this:is_readable_by(player))
flags = {"[Readable]", @flags};
endif
if (!this:is_usable_by($no_one))
if (this:is_usable_by(player))
flags = {@flags, "[Approved]"};
else
flags = {@flags, "[Moderated]"};
endif
endif
player:tell(namelist, "  ", $string_utils:from_list(flags, " "));
"player:tell(namelist, \"  (\", this, \")\", \" \", $string_utils:from_list(flags, \" \"));";
d = this:description();
for line in (d)
player:tell("    ", line);
endfor
.
#41:2
return $perm_utils:controls(who = args[1], this) || (who in this.writers);
.
#41:3
return (typeof(this.readers) != LIST) || (((who = args[1]) in this.readers) || (this:is_writable_by(who) || $mail_agent:sends_to(1, this, who)));
.
#41:4
return (!this.moderated) || ((this:is_writable_by(who = args[1]) || (who in this.moderated)) || who.wizard);
.
#41:5
if ((args && (!this:is_usable_by(args[1]))) && (!args[1].wizard))
return this:moderator_notify(@args);
else
return this.(verb);
endif
.
#41:6
if ((args && (!this:is_usable_by(args[1]))) && (!args[1].wizard))
return this:moderator_forward(@args);
elseif (typeof(mf = this.(verb)) == STR)
return $string_utils:pronoun_sub(mf, @args);
else
return mf;
endif
.
#41:7
if (typeof(mf = this.(verb)) == STR)
return $string_utils:pronoun_sub(mf, args ? args[1] | $player);
else
return mf;
endif
.
#41:8
":add_forward(recip[,recip...]) adds new recipients to this list.  Returns a string error message or a list of results (recip => success, E_PERM => not allowed, E_INVARG => not a valid recipient, string => other kind of failure)";
if (caller == $mail_editor)
perms = player;
else
perms = caller_perms();
endif
result = {};
forward_self = (!this.mail_forward) || (this in this.mail_forward);
for recip in (args)
if ((!valid(recip)) || ((!is_player(recip)) && (!($mail_recipient in $object_utils:ancestors(recip)))))
r = E_INVARG;
elseif ($perm_utils:controls(perms, this) || ((typeof(this.readers) != LIST) && $perm_utils:controls(perms, recip)))
this.mail_forward = setadd(this.mail_forward, recip);
r = recip;
else
r = E_PERM;
endif
result = listappend(result, r);
endfor
if ((length(this.mail_forward) > 1) && ($nothing in this.mail_forward))
this.mail_forward = setremove(this.mail_forward, $nothing);
endif
if (forward_self)
this.mail_forward = setadd(this.mail_forward, this);
endif
return result;
.
#41:9
":delete_forward(recip[,recip...]) removes recipients to this list.  Returns a list of results (E_PERM => not allowed, E_INVARG => not on list)";
if (caller == $mail_editor)
perms = player;
else
perms = caller_perms();
endif
result = {};
forward_self = (!this.mail_forward) || (this in this.mail_forward);
for recip in (args)
if (!(recip in this.mail_forward))
r = E_INVARG;
elseif (((!valid(recip)) || $perm_utils:controls(perms, recip)) || $perm_utils:controls(perms, this))
if (recip == this)
forward_self = 0;
endif
this.mail_forward = setremove(this.mail_forward, recip);
r = recip;
else
r = E_PERM;
endif
result = listappend(result, r);
endfor
if (!(forward_self || this.mail_forward))
this.mail_forward = {$nothing};
elseif (this.mail_forward == {this})
this.mail_forward = {};
endif
return result;
.
#41:10
":add_notify(recip[,recip...]) adds new notifiees to this list.  Returns a list of results (recip => success, E_PERM => not allowed, E_INVARG => not a valid recipient)";
if (caller == $mail_editor)
perms = player;
else
perms = caller_perms();
endif
result = {};
for recip in (args)
if ((!valid(recip)) || (recip == this))
r = E_INVARG;
elseif ($perm_utils:controls(perms, this) || (this:is_readable_by(perms) && $perm_utils:controls(perms, recip)))
this.mail_notify = setadd(this.mail_notify, recip);
r = recip;
else
r = E_PERM;
endif
result = listappend(result, r);
endfor
return result;
.
#41:11
":delete_notify(recip[,recip...]) removes notifiees from this list.  Returns a list of results (E_PERM => not allowed, E_INVARG => not on list)";
if (caller == $mail_editor)
perms = player;
else
perms = caller_perms();
endif
result = {};
rmthis = 0;
for recip in (args)
if (!(recip in this.mail_notify))
r = E_INVARG;
elseif ((!valid(recip)) || ($perm_utils:controls(perms, recip) || $perm_utils:controls(perms, this)))
if (recip == this)
rmthis = 1;
endif
this.mail_notify = setremove(this.mail_notify, recip);
r = recip;
else
r = E_PERM;
endif
result = listappend(result, r);
endfor
return result;
.
#41:12
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
else
this.messages = {@this.messages, {new = this:new_message_num(), args[1]}};
this.last_msg_date = args[1][1];
this.last_used_time = time();
return new;
endif
.
#41:13
":ok(caller,callerperms) => true iff caller can do read operations";
return (args[1] in {this, $mail_agent}) || (args[2].wizard || this:is_readable_by(args[2]));
.
#41:14
":ok_write(caller,callerperms) => true iff caller can do write operations";
return (args[1] in {this, $mail_agent}) || (args[2].wizard || this:is_writable_by(args[2]));
.
#41:15
":parse_message_seq(strings,cur) => msg_seq";
"";
":from_msg_seq(olist)     => msg_seq of messages from those people";
":%from_msg_seq(strings)  => msg_seq of messages with strings in the From: line";
":to_msg_seq(olist)       => msg_seq of messages to those people";
":%to_msg_seq(strings)    => msg_seq of messages with strings in the To: line";
":subject_msg_seq(target) => msg_seq of messages with target in the Subject:";
":body_msg_seq(target)    => msg_seq of messages with target in the body";
":new_message_num()    => number that the next incoming message will receive.";
":length_num_le(num)   => number of messages in folder numbered <= num";
":length_date_le(date) => number of messages in folder dated <= date";
":length_all_msgs()    => number of messages in folder";
":exists_num_eq(num)   => index of message in folder numbered == num, or 0";
"";
":display_seq_headers(msg_seq[,cur])   display message summary lines";
":display_seq_full(msg_seq[,preamble]) display entire messages";
"            => number of final message displayed";
":list_rmm() displays contents of .messages_going.";
"            => the number of messages in .messages_going.";
"";
":messages_in_seq(msg_seq) => list of messages in msg_seq on folder";
"";
"See the corresponding routines on $mail_agent for more detail.";
return this:ok(caller, caller_perms()) ? $mail_agent:(verb)(@args) | E_PERM;
.
#41:16
":rm_message_seq(msg_seq) removes the given sequence of from folder";
"               => string giving msg numbers removed";
":list_rmm()    displays contents of .messages_going.";
"               => number of messages in .messages_going.";
":undo_rmm()    restores previously deleted messages from .messages_going.";
"               => msg_seq of restored messages";
":expunge_rmm() destroys contents of .messages_going once and for all.";
"               => number of messages in .messages_going.";
":renumber([cur])  renumbers all messages";
"               => {number of messages,new cur}.";
"";
"See the corresponding routines on $mail_agent.";
return this:ok_write(caller, caller_perms()) ? $mail_agent:(verb)(@args) | E_PERM;
.
#41:17
"NOTE:  this routine is obsolete, use :messages_in_seq()";
":messages(num) => returns the message numbered num.";
":messages()    => returns the entire list of messages (can be SLOW).";
if (!this:ok(caller, caller_perms()))
return E_PERM;
elseif (!args)
return this:messages_in_seq({1, this:length_all_msgs() + 1});
elseif (!(n = this:exists_num_eq(args[1])))
return E_RANGE;
else
return this:messages_in_seq(n)[2];
endif
.
#41:18
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
date_seq = {};
for msg in (this.messages)
date_seq = {@date_seq, msg[2][1]};
endfor
msg_order = $list_utils:sort($list_utils:range(n = length(msgs = this.messages)), date_seq);
newmsgs = {};
for i in [1..n]
if ($command_utils:suspend_if_needed(0))
player:tell("...", i);
endif
newmsgs = {@newmsgs, {i, msgs[msg_order[i]][2]}};
endfor
if (length(this.messages) != n)
"...shit, new mail received,... start again...";
fork (0)
this:date_sort();
endfork
else
this.messages = newmsgs;
this.last_used_time = newmsgs[length(newmsgs)][2][1];
endif
.
#41:19
mlen = this:length_all_msgs();
this.last_msg_date = mlen && this:messages_in_seq(mlen)[2][1];
.
#41:20
return this.(verb);
.
#41:21
return $mail_agent:msg_summary_line(@args);
.
#41:22
for m in (this.messages)
$mail_agent:__convert_new(@m[2]);
$command_utils:suspend_if_needed(0);
endfor
.
#41:23
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
msgs = {};
i = 1;
for m in (oldmsgs = this.messages)
msgs = {@msgs, {m[1], $mail_agent:__convert_new(@m[2])}};
if ($command_utils:running_out_of_time())
player:notify(tostr("...", i, " ", this));
suspend(0);
if (oldmsgs != this.messages)
return 0;
endif
endif
i = i + 1;
endfor
this.messages = msgs;
return 1;
.
#41:24
if ($__core_init_phase)
pass(@args);
if (index(this.name, "generic ") == 1)
"...generic mail recipients stay in #-1...";
if (this == $mail_recipient)
for v in ({"display_message_as_jtext", "jtext_seq_headers"})
delete_verb(this, v);
endfor
endif
else
move(this, $mail_agent);
this:rmm_for_core();
this.mail_notify = {player};
this.mail_forward = {};
for p in ({"moderator_forward", "moderator_notify", "writers", "readers", "expire_period", "last_used_time", "last_msg_date"})
"this.(p) = $mail_recipient.(p);";
clear_property(this, p);
endfor
this.moderated = 1;
endif
endif
.
#41:25
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
this.mail_forward = {};
return pass(@args);
endif
.
#41:26
":length_date_le(date) => number of messages in folder dated > date";
"";
if (this:ok(caller, caller_perms()))
date = args[1];
return (this.last_msg_date <= date) ? 0 | $mail_agent:(verb)(date);
else
return E_PERM;
endif
.
#41:27
return {@pass(@args), $mail_agent, $perm_utils, $string_utils, $no_one, $player, $mail_editor, $mail_recipient, $object_utils, $list_utils, $command_utils};
.
#41:28
if (caller_perms().wizard)
if (length = this:length_all_msgs())
this:rm_message_seq({1, 1 + length});
endif
this:expunge_rmm();
else
$error:raise(E_PERM);
endif
.
#41:29
"A nicer way than a direct call to player:tell to report helpful error information.";
"In most cases, :handle_error simply *does* call player:tell, but it can do something else with the information depending on the circumstance.";
player:tell($string_utils:from_list(args));
.
#41:30
"Usage: mail_recipient:subscribed(who)";
"Returns {} is who is not subscribed to this, or a list of bracketed strings describing who's subscription info.";
"Needs wizperms for who:current_message.";
"Stolen from $mail_recipient:look_self.";
who = args[1];
fwd = (typeof(fwd = this:mail_forward()) == LIST) && (who in fwd);
ntf = (typeof(ntf = this:mail_notify()) == LIST) && (who in ntf);
set_task_perms(who);
sub = who:current_message(this);
if (fwd)
flags = {"[Subscribed with Forwarding]"};
elseif (ntf)
flags = {"[Subscribed with Notification]"};
elseif (sub)
flags = {"[Subscribed]"};
else
flags = {};
endif
return flags;
.
#42:0
"resolve(name,from,seen,prevrcpts,prevnotifs) => {rcpts,notifs} or E_INVARG";
"resolve(list,from,seen,prevrcpts,prevnotifs) => {bogus,rcpts,notifs}";
"Given either an address (i.e., objectid) or a list of such, traces down all .mail_forward lists and .mail_notify to determine where a message should actually go and who should be told about it.  Both forms take previous lists of recipients/notifications and add only those addresses that weren't there before.  `seen' is the stack of addresses we are currently resolving (for detecting loops).  The first form returns E_INVARG if `name' is invalid.  The second form returns all invalid addresses in the `bogus' list but still does the appropriate search on the remaining addresses.";
{recip, from, ?seen = {}, ?prevrcpts = {}, ?prevnotifs = {}} = args;
sofar = {prevrcpts, prevnotifs};
if (typeof(recip) == LIST)
bogus = {};
for r in (recip)
result = this:resolve_addr(r, from, seen, @sofar);
if (result)
sofar = result;
else
bogus = setadd(bogus, r);
endif
endfor
return {bogus, @sofar};
else
fwd = include_recip = 0;
if ((recip == $nothing) || (recip in seen))
return sofar;
elseif ((!valid(recip)) || ((!(is_player(recip) || $object_utils:isa(recip, $mail_recipient))) || (typeof(fwd = recip:mail_forward(from)) != LIST)))
"recip is a non-player non-mailing-list/folder or forwarding is screwed.";
if (typeof(fwd) == STR)
player:tell(fwd);
endif
return E_INVARG;
elseif (fwd)
if (r = recip in fwd)
include_recip = 1;
fwd = listdelete(fwd, r);
endif
result = this:resolve_addr(fwd, recip, setadd(seen, recip), @sofar);
if (bogus = result[1])
player:tell(recip:name(), "(", recip, ")'s .mail_forward list includes the following bogus entr", (length(bogus) > 1) ? "ies:  " | "y:  ", $string_utils:english_list(bogus));
endif
sofar = result[2..3];
else
include_recip = 1;
endif
if ((ticks_left() < 1000) || (seconds_left() < 2))
suspend(0);
endif
biffs = sofar[2];
for n in (this:mail_notify(recip, from))
if (valid(n))
if (i = $list_utils:iassoc_suspended(n, biffs))
biffs[i] = setadd(biffs[i], recip);
else
biffs = {{n, recip}, @biffs};
endif
endif
if ((ticks_left() < 1000) || (seconds_left() < 2))
suspend(0);
endif
endfor
return {include_recip ? setadd(sofar[1], recip) | sofar[1], biffs};
endif
.
#42:1
"sends_to(from,addr,rcpt[,seen]) ==> true iff mail sent to addr passes through rcpt.";
if ((addr = args[2]) == (rcpt = args[3]))
return 1;
elseif (!(addr in (seen = (length(args) >= 4) ? args[4] | {})))
seen = {@seen, addr};
for a in ((typeof(fwd = this:mail_forward(addr, @args[1] ? {} | {args[1]})) == LIST) ? fwd | {})
if (this:sends_to(addr, a, rcpt, seen))
return 1;
endif
$command_utils:suspend_if_needed(0);
endfor
endif
return 0;
.
#42:2
"send_message(from,rcpt-list,hdrs,msg) -- formats and sends a mail message.  hders is either the text of the subject line, or a {subject,{reply-to,...}} list.";
"Return E_PERM if from isn't owned by the caller.";
"Return {0, @invalid_rcpts} if rcpt-list contains any invalid addresses.  No mail is sent in this case.";
"Return {1, @actual_rcpts} if successful.";
from = args[1];
to = args[2];
orig_hdrs = args[3];
msg = args[4];
"...";
"... remove bogus Resent-To/Resent-By headers...";
"...";
if ((typeof(orig_hdrs) == LIST) && (length(orig_hdrs) > 2))
hdrs = orig_hdrs[1..2];
orig_hdrs[1..2] = {};
strip = {"Resent-To", "Resent-By"};
for h in (orig_hdrs)
(h[1] in strip) || (hdrs = {@hdrs, h});
endfor
else
hdrs = orig_hdrs;
endif
"...";
"... send it...";
"...";
if ($perm_utils:controls(caller_perms(), from))
text = $mail_agent:make_message(from, to, hdrs, msg);
return this:raw_send(text, to, from);
else
return E_PERM;
endif
.
#42:3
"WIZARDLY";
"raw_send(text,rcpts,sender) -- does the actual sending of a message.  Assumes that text has already been formatted correctly.  Decides who to send it to and who wants to be notified about it and does so.";
"Return {E_PERM} if the caller is not entitled to use this verb.";
"Return {0, @invalid_rcpts} if rcpts contains any invalid addresses.  No mail is sent in this case.";
"Return {1, @actual_rcpts} if successful.";
text = args[1];
rcpts = args[2];
from = args[3];
if (typeof(rcpts) != LIST)
rcpts = {rcpts};
endif
if (!(caller in {$mail_agent, $mail_editor}))
return {E_PERM};
elseif (bogus = (resolve = this:resolve_addr(rcpts, from))[1])
return {0, bogus};
else
this:touch(rcpts);
actual_rcpts = resolve[2];
biffs = resolve[3];
results = {};
for recip in (actual_rcpts)
if ((ticks_left() < 1000) || (seconds_left() < 5))
valid(player) && player:notify(tostr("...", recip));
suspend(0);
endif
if (typeof(e = recip:receive_message(text, from)) in {ERR, STR})
"...receive_message bombed...";
player:notify(tostr(recip, ":receive_message:  ", e));
e = 0;
elseif ((!is_player(recip)) || (!e))
"...not a player or receive_message isn't giving out the message number";
"...no need to force a notification...";
elseif (i = $list_utils:iassoc(recip, biffs))
"...player-recipient was already getting a notification...";
"...make sure notification includes a mention of him/her/itself.";
if (!(recip in listdelete(biffs[i], 1)))
biffs[i][2..1] = {recip};
endif
else
"...player-recipient wasn't originally being notified at all...";
biffs = {{recip, recip}, @biffs};
endif
results = {@results, e};
endfor
fork (0)
for b in (biffs)
if ((ticks_left() < 1000) || (seconds_left() < 5))
suspend(0);
endif
if ($object_utils:has_callable_verb(b[1], "notify_mail"))
mnums = {};
for r in (listdelete(b, 1))
mnums = {@mnums, (rn = r in actual_rcpts) && results[rn]};
endfor
b[1]:notify_mail(from, listdelete(b, 1), mnums, text);
endif
endfor
endfork
return {1, @actual_rcpts};
endif
.
#42:4
who = args[1];
rest = args[2..length(args)];
if ($object_utils:has_verb(who, verb))
return who:(verb)(@rest);
else
return {};
endif
.
#42:5
"touch(name or list,seen) => does .last_used_time = time() if we haven't already touched this in the last hour";
{recip, ?seen = {}} = args;
if (typeof(recip) == LIST)
for r in (recip)
result = this:touch(r, seen);
$command_utils:suspend_if_needed(0);
endfor
else
if (((!valid(recip)) || (recip in seen)) || ((!is_player(recip)) && (!($mail_recipient in $object_utils:ancestors(recip)))))
"recip is neither a player nor a mailing list/folder";
else
if (fwd = this:mail_forward(recip))
this:touch(fwd, {@seen, recip});
endif
if (!is_player(recip))
recip.last_used_time = time();
endif
endif
endif
.
#42:6
player:tell_lines(this:description());
for c in (this.contents)
c:look_self();
endfor
.
#42:7
"...make sure the list has at least one usable name.";
"...make sure none of the aliases are already taken.";
what = args[1];
ok = 0;
for a in (what.aliases)
if (index(a, " "))
elseif (valid(p = $mail_agent:match(a, #-1)) && ((p != what) && (a in p.aliases)))
player:tell("Mailing list name \"", a, "\" in use on ", p:name(), "(", p, ")");
return 0;
else
ok = 1;
endif
endfor
return ok;
.
#42:8
":match(string) => mailing list object in here that matches string.";
":match(string,player) => similar but also matches against player's private mailing lists (as kept in .mail_lists).";
if (!(string = args[1]))
return $nothing;
elseif (string[1] == "*")
string = string[2..length(string)];
endif
if (valid(o = $string_utils:literal_object(string)) && ($mail_recipient in $object_utils:ancestors(o)))
return o;
else
if (valid(who = {@args, player}[2]) && (typeof(use = who.mail_lists) == LIST))
use = {@this.contents, @use};
else
use = this.contents;
endif
partial = 1;
string = strsub(string, "_", "-");
for l in (use)
if (string in l.aliases)
return l;
endif
if (partial != $ambiguous_match)
for a in (l.aliases)
if ((index(a, string) == 1) && (!index(a, " ")))
if (partial)
partial = l;
elseif (partial != l)
partial = $ambiguous_match;
endif
endif
endfor
endif
endfor
return partial && $failed_match;
endif
.
#42:9
":match_recipient(string[,meobj]) => $player or $mail_recipient object that matches string.  Optional second argument (defaults to player) is returned in the case string==\"me\" and is also used to obtain a list of private $mail_recipients to match against.";
{string, ?meobj = player} = args;
if (!string)
return $nothing;
elseif (string == "*")
return $ambiguous_match;
elseif (string[1] == "*")
return this:match(@args);
elseif (string[1] == "~")
return $string_utils:match_player(string[2..$], meobj);
elseif (valid(o = $string_utils:match_player(@args)) || (o == $ambiguous_match))
return o;
else
return this:match(@args);
endif
.
#42:10
{match_result, string, ?cmd_id = ""} = args;
if (match_result == $nothing)
player:tell(cmd_id, "You must specify a valid mail recipient.");
elseif (match_result == $failed_match)
player:tell(cmd_id, "There is no mail recipient called \"", string, "\".");
elseif (match_result == $ambiguous_match)
if ((nostar = index(string, "*") != 1) && (lst = $player_db:find_all(string)))
player:tell(cmd_id, "\"", string, "\" could refer to ", (length(lst) > 20) ? tostr("any of ", length(lst), " players") | $string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%n (%#)", lst), "no one", " or "), ".");
else
player:tell(cmd_id, "I don't know which \"", nostar ? "*" | "", string, "\" you mean.");
endif
elseif (!valid(match_result))
player:tell(cmd_id, match_result, " does not exist.");
else
return 0;
endif
return 1;
.
#42:11
":make_message(sender,recipients,subject/replyto/additional-headers,body)";
" => message in the form as it will get sent.";
from = args[1];
recips = args[2];
hdrs = args[3];
body = args[4];
fromline = this:nn_list(from);
if (typeof(recips) != LIST)
recips = {recips};
endif
recips = this:nn_list(@recips);
others = {};
replyto = 0;
if (typeof(hdrs) != LIST)
hdrs = {hdrs};
endif
subj = hdrs[1];
if (sender = valid(from) && ((!is_player(from)) && this:nn(from.owner)))
others = {"Sender:   " + sender};
endif
replyto = {@hdrs, 0}[2] && this:nn_list(@hdrs[2]);
if (length(hdrs) > 2)
hdrs[1..2] = {};
for h in (hdrs)
if (match(h[1], "[a-z1-9-]+"))
others = {@others, $string_utils:left(h[1] + ": ", 15) + h[2]};
endif
endfor
endif
if (typeof(body) != LIST)
body = body ? {body} | {};
endif
return {time(), fromline, recips, subj || " ", @replyto ? {"Reply-to: " + replyto} | {}, @others, "", @body};
.
#42:12
if (args && (typeof(args[1]) == OBJ))
what = args[1];
"if ($object_utils:isa(what, $mail_recipient))";
"return \"*\" + what:name();";
"else";
"return what:name();";
"endif";
return tostr($object_utils:isa(what, $mail_recipient) ? "*" | "", what:(verb)(@listdelete(args, 1)));
else
return pass(@args);
endif
.
#42:13
return $string_utils:english_list($list_utils:map_arg(this, verb[1..index(verb, "_") - 1], args), "no one");
.
#42:14
":parse_address_field(string) => list of objects";
"This is the standard routine for parsing address lists that appear in From:, To: and Reply-To: lines";
objects = {};
string = args[1];
while (e = index(string, ")"))
if ((s = rindex(string[1..e], "(#")) && (#0 != (o = toobj(string[s + 2..e - 1]))))
objects = {@objects, o};
endif
string = string[e + 1..length(string)];
endwhile
return objects;
.
#42:15
":display_seq_full(msg_seq[,preamble]) => {cur, last-read-date}";
"This is the default message display routine.";
"Prints entire messages on folder (caller) to player.  msg_seq is the handle returned by :parse_message_seq(...) and indicates which messages should be printed.  preamble, if given will precede the output of the message itself, in which case the message number will be substituted for \"%d\".  Returns the number of the final message in the sequence (which can be then used as the new current message number).";
set_task_perms(caller_perms());
preamble = {@args, ""}[2];
cur = date = 0;
for x in (msgs = caller:messages_in_seq(args[1]))
cur = x[1];
date = x[2][1];
player:display_message(preamble ? strsub(preamble, "%d", tostr(cur)) | {}, player:msg_text(@x[2]));
if ((ticks_left() < 500) || (seconds_left() < 2))
suspend(0);
endif
endfor
return {cur, date};
.
#42:16
":display_seq_headers(msg_seq[,cur[,last_read_date]])";
"This is the default header display routine.";
"Prints a list of headers of messages on caller to player.  msg_seq is the handle returned by caller:parse_message_seq(...).  cur is the player's current message.  last_read_date is the date of the last of the already-read messages.";
set_task_perms(caller_perms());
cur = {@args, 0}[2];
last_old = {@args, $maxint, $maxint}[3];
width = abs(player.linelen) || 79;
for x in (msgs = caller:messages_in_seq(args[1]))
line = tostr($string_utils:right(x[1], 4, (cur == x[1]) ? ">" | " "), (x[2][1] > last_old) ? ":+ " | ":  ", caller:msg_summary_line(@x[2]));
player:tell(line[1..min(width, length(line))]);
if ((ticks_left() < 500) || (seconds_left() < 2))
suspend(0);
endif
endfor
player:tell("----+");
.
#42:17
":rm_message_seq(msg_seq)  removes the given sequence of from folder (caller)";
"...removed messages are saved in .messages_going for possible restoration.";
set_task_perms(caller_perms());
old = caller.messages;
new = save = nums = {};
next = 1;
for i in [1..length(seq = args[1]) / 2]
if ((ticks_left() < 500) || (seconds_left() < 2))
player:tell("... rmm ", old[next][1] - 1);
suspend(0);
endif
start = seq[(2 * i) - 1];
new = {@new, @old[next..start - 1]};
save = {@save, {start - next, old[start..(next = seq[2 * i]) - 1]}};
nums = {@nums, old[start][1], old[next - 1][1] + 1};
endfor
new = {@new, @old[next..length(old)]};
caller.messages_going = save;
caller.messages = new;
if ($object_utils:isa(caller, $mail_recipient))
caller:_fix_last_msg_date();
endif
return $seq_utils:tostr(nums);
.
#42:18
":undo_rmm()  restores previously deleted messages in .messages_going to .messages.";
set_task_perms(caller_perms());
old = caller.messages;
new = seq = {};
last = 0;
next = 1;
for s in (caller.messages_going)
new = {@new, @old[last + 1..last + s[1]], @s[2]};
last = last + s[1];
seq = {@seq, next + s[1], next = length(new) + 1};
endfor
caller.messages = {@new, @old[last + 1..length(old)]};
caller.messages_going = {};
if ($object_utils:isa(caller, $mail_recipient))
caller:_fix_last_msg_date();
endif
return seq;
.
#42:19
":list_rmm()    displays contents of .messages_going.";
":expunge_rmm() destroys contents of .messages_going once and for all.";
"... both return the number of messages in .messages_going.";
set_task_perms(caller_perms());
msgs = seq = {};
for s in (caller.messages_going)
msgs = {@msgs, @s[2]};
endfor
if (verb == "expunge_rmm")
caller.messages_going = {};
else
for x in (msgs)
player:tell($string_utils:right(x[1], 4), ":  ", caller:msg_summary_line(@x[2]));
if ((ticks_left() < 500) || (seconds_left() < 2))
suspend(0);
endif
endfor
if (msgs)
player:tell("----+");
endif
endif
return length(msgs);
.
#42:20
":renumber([cur]) -- assumes caller is a $mail_recipient or a $player.";
"...renumbers caller.messages, doing a suspend() if necessary.";
"...returns {number of messages,new cur}.";
set_task_perms(caller_perms());
cur = {@args, 0}[1];
caller.messages_going = {};
"... blow away @rmm'ed messages since there's no way to tell what their new numbers should be...";
msgs = caller.messages;
if (cur)
cur = $list_utils:iassoc_sorted(cur, msgs);
endif
while (1)
"...find first out-of-sequence message...";
l = 0;
r = (len = length(msgs)) + 1;
while ((r - 1) > l)
if (msgs[i = (r + l) / 2][1] > i)
r = i;
else
l = i;
endif
endwhile
"... r == first out-of-sequence, l == last in-sequence, l+1 == r ...";
if (l >= len)
return {l, cur};
endif
"...renumber as many messages as we have time for...";
chunk = {};
while (((r <= len) && (ticks_left() > 3000)) && (seconds_left() > 2))
for x in (msgs[r..min(r + 9, len)])
chunk = {@chunk, {r, x[2]}};
r = r + 1;
endfor
endwhile
caller.messages = {@msgs[1..l], @chunk, @msgs[r..len]};
if (chunk)
player:tell("...(renumbering ", l + 1, " -- ", r - 1, ")");
suspend(0);
else
player:tell("You lose.  This message collection is just too big.");
return;
endif
"... have to be careful since new mail may be received at this point...";
msgs = caller.messages;
endwhile
.
#42:21
":msg_summary_line(@msg) => date/from/subject as a single string.";
body = ("" in {@args, ""}) + 1;
if ((body > length(args)) || (!(subject = args[body])))
subject = "(None.)";
endif
if (args[1] < (time() - 31536000))
"...show year...";
c = ctime(args[1]);
date = c[5..11] + c[21..25];
else
date = ctime(args[1])[5..16];
endif
from = args[2];
while (subs = match(from, " ([^)]*)"))
from[subs[1]..subs[2]] = "";
endwhile
if (args[4] != " ")
subject = args[4];
endif
return tostr(date, "   ", $string_utils:left(from, 15), "   ", subject);
.
#42:22
":msg_summary_line(@msg) => date/from/subject as a single string.";
blank = "" in {@args, ""};
if ((blank < length(args)) && args[blank + 1])
if (length(args[blank + 1]) <= 28)
subject = args[blank + 1];
else
subject = args[blank + 1][1..25] + "...";
endif
else
subject = "(None.)";
endif
if (typeof(args[1]) == NUM)
date = ctime(args[1])[5..16];
start = 2;
else
date = "?Date?";
start = 1;
endif
if ((start == 2) && (index(args[2], "From:") != 1))
from = args[2];
if (args[4] != " ")
subject = args[4];
endif
else
from = "?From?";
for line in (args[start..blank - 1])
if (index(line, "Date:") == 1)
date = $string_utils:triml(line[6..length(line)])[5..16];
elseif (index(line, "From:") == 1)
from = $string_utils:triml(line[6..length(line)]);
elseif (index(line, "Subject:") == 1)
s = $string_utils:triml(line[9..length(line)]);
if (s != "(None.)")
subject = s;
endif
endif
endfor
endif
(subject && (length(subject) > 32)) && (subject = subject[1..32]);
return tostr(date, "   ", $string_utils:left(from, 20), "   ", subject);
.
#42:23
"parse_message_seq(strings,cur[,last_old])";
"This is the default <message-sequence> parsing routine for those mail commands that refer to sequences of messages (@mail, @read,...) on a folder.";
"  caller (the folder) is assumed to be a $mail_recipient or a player.";
"  strings is the <message-sequence> portion of the arg list.";
"  cur is the number of the player's current message for this folder.";
"Returns a string error message if the parse fails, otherwise";
"returns a list {msg_seq, @unused_strings}, where";
"   msg_seq is a handle understood by caller:display_seq_full/headers(), and ";
"   unused_strings is the list of remaining uninterpreted strings";
set_task_perms(caller_perms());
if (!(nummsgs = caller:length_all_msgs()))
return "%f %<has> no messages.";
elseif (typeof(strings = args[1]) != LIST)
strings = {strings};
endif
cur = {@args, 0}[2];
last_old = {@args, 0, 0}[3];
seq = result = {};
mode = #0;
"... changes to 0 if we start seeing message numbers, to 1 if we see masks...";
keywords = ":from:%from:to:%to:subject:body:before:after:since:until:first:last:kept:unkept";
keyalist = {{1, "from"}, {6, "%from"}, {12, "to"}, {15, "%to"}, {19, "subject"}, {27, "body"}, {32, "before"}, {39, "after"}, {45, "since"}, {51, "until"}, {57, "first"}, {63, "last"}, {68, "kept"}, {73, "unkept"}};
strnum = 0;
for string in (strings)
strnum = strnum + 1;
$command_utils:suspend_if_needed(0);
if (string && ((c = index(string, ":")) && ((k = index(keywords, ":" + string[1..c - 1])) && (k == rindex(keywords, ":" + string[1..c - 1])))))
"...we have a mask to apply...";
keywd = $list_utils:assoc(k, keyalist)[2];
if (mode == #0)
seq = {1, nummsgs + 1};
endif
mode = 1;
if (k <= 27)
"...from, subject, to, body...";
pattern = string[c + 1..length(string)];
if (keywd in {"subject", "body"})
elseif (keywd[1] == "%")
pattern = $string_utils:explode(pattern, "|");
else
pattern = this:((keywd == "to") ? "_parse_to" | "_parse_from")(pattern);
if (typeof(pattern) == STR)
return pattern;
endif
endif
seq = caller:(keywd + "_msg_seq")(pattern, seq);
if (typeof(seq) == STR)
if (strnum == 1)
return seq;
else
seq = {};
endif
endif
elseif (k <= 51)
"...before, since, after, until...";
if (typeof(date = this:_parse_date(string[c + 1..length(string)])) != NUM)
return tostr("Bad date `", string, "':  ", date);
endif
s = caller:length_date_le((keywd in {"before", "since"}) ? date - 1 | (date + 86399));
if (keywd in {"before", "until"})
seq = $seq_utils:remove(seq, s + 1, nummsgs);
else
seq = $seq_utils:remove(seq, 1, s);
endif
elseif (k <= 63)
"...first, last...";
if (n = tonum(string[c + 1..length(string)]))
seq = $seq_utils:(keywd + "n")(seq, n);
else
return tostr("Bad number in `", string, "'");
endif
else
"...kept, unkept...";
if (c < length(string))
return tostr("Unexpected junk after `", keywd, ":'");
elseif ((!(seq = caller:(keywd + "_msg_seq")(seq))) && (strnum == 1))
return tostr("%f %<has> no ", keywd, " messages.");
endif
endif
else
"...continue building the present sequence...";
if (mode)
seq && (result = $seq_utils:union(result, seq));
seq = {};
endif
mode = 0;
if (!string)
"...default case for @read: get the current message but skip to the next one if it's not there...";
if (cur)
i = min(caller:length_num_le(cur - 1) + 1, nummsgs);
seq = $seq_utils:add(seq, i, i);
else
return "%f %<has> no current message.";
endif
elseif ((index(string, "next") == 1) && (!index(string, "-")))
string[1..4] = "";
if ((n = string ? tonum(string) | 1) <= 0)
return tostr("Bad number `", string, "'");
elseif ((i = caller:length_num_le(cur) + 1) <= nummsgs)
seq = $seq_utils:add(seq, i, min((i + n) - 1, nummsgs));
else
return "%f %<has> no next message.";
endif
elseif ((index(string, "prev") == 1) && (!index(string, "-")))
string[1..4] = "";
if ((n = string ? tonum(string) | 1) <= 0)
return tostr("Bad number `", string, "'");
elseif (i = caller:length_num_le(cur - 1))
seq = $seq_utils:add(seq, max(1, (i - n) + 1), i);
else
return "%f %<has> no previous message.";
endif
elseif (string == "new")
s = last_old ? caller:length_date_le(last_old) | caller:length_num_le(cur);
if (s < nummsgs)
seq = $seq_utils:add(seq, s + 1, nummsgs);
else
return "%f %<has> no new messages.";
endif
elseif (string == "first")
seq = $seq_utils:add(seq, 1, 1);
elseif (n = tonum(string) || (((string in {"last", "$"}) && -1) || ((string == "cur") && cur)))
if (n <= 0)
seq = $seq_utils:add(seq, max(0, nummsgs + n) + 1, nummsgs);
elseif (i = caller:exists_num_eq(n))
seq = $seq_utils:add(seq, i, i);
else
return (string == "cur") ? "%f's current message has already been removed." | tostr("%f %<has> no message numbered `", string, "'.");
endif
elseif (((i = index(string, "..")) > 1) || ((i = index(string, "-")) > 1))
if ((start = tonum(sst = string[1..i - 1])) > 0)
s = caller:length_num_le(start - 1);
elseif (sst in {"next", "prev", "cur"})
s = max(0, caller:length_num_le(cur - (sst != "next")) - (sst == "prev"));
elseif (sst in {"last", "$"})
s = nummsgs - 1;
elseif (sst == "first")
s = 0;
else
return {$seq_utils:union(result, seq), @strings[strnum..length(strings)]};
endif
j = (string[i] == ".") ? i + 2 | (i + 1);
if ((end = tonum(est = string[j..length(string)])) > 0)
e = caller:length_num_le(end);
elseif (est in {"next", "prev", "cur"})
e = min(nummsgs, caller:length_num_le(cur - (est == "prev")) + (est == "next"));
elseif (est in {"last", "$"})
e = nummsgs;
elseif (est == "first")
e = 1;
else
return {$seq_utils:union(result, seq), @strings[strnum..length(strings)]};
endif
if (s < e)
seq = $seq_utils:add(seq, s + 1, e);
else
return tostr("%f %<has> no messages in range ", string, ".");
endif
elseif (string == "cur")
return "%f %<has> no current message.";
else
return {$seq_utils:union(result, seq), @strings[strnum..length(strings)]};
endif
endif
endfor
return {$seq_utils:union(result, seq)};
"Metadata 202106";
"Last-Modify: {864874102, \"Ken\", #75, \"JHM\"}";
.
#42:24
":_parse_from(string with |'s in it) => object list";
":_parse_to(string with |'s in it) => object list";
"  for from:string and to:string items in :parse_message_seq";
if (verb == "_parse_to")
match_obj = fail_obj = this;
match_verb = "match_recipient";
fail_verb = "match_failed";
else
match_obj = $string_utils;
match_verb = "match_player";
fail_obj = $command_utils;
fail_verb = "player_match_failed";
endif
plist = {};
for w in ($string_utils:explode(args[1], "|"))
if (fail_obj:(fail_verb)(p = match_obj:(match_verb)(w), w))
p = $string_utils:literal_object(w);
if ((p == $failed_match) || (!$command_utils:yes_or_no("Continue? ")))
return "Bad address list:  " + args[1];
endif
endif
plist = setadd(plist, p);
endfor
return plist;
.
#42:25
words = $string_utils:explode(args[1], "-");
if (length(words) == 1)
if (index("yesterday", words[1]) == 1)
time = $time_utils:dst_midnight((time() - (time() % 86400)) - 86400);
elseif (index("today", words[1]) == 1)
time = $time_utils:dst_midnight(time() - (time() % 86400));
elseif (typeof(time = $time_utils:from_day(words[1], -1)) == ERR)
time = "weekday, `Today', `Yesterday', or date expected.";
endif
elseif ((!words) || ((length(words) > 3) || ((!tonum(words[1])) || (E_TYPE == (year = $code_utils:tonum({@words, "-1"}[3]))))))
time = "Date should be of the form `5-Jan', `5-Jan-92', `Wed',`Wednesday'";
else
day = tonum(words[1]);
time = $time_utils:dst_midnight($time_utils:from_month(words[2], -1, day));
if (length(words) == 3)
thisyear = tonum(ctime(time)[21..24]);
if (100 > year)
year = (thisyear + 50) - (((thisyear - year) + 50) % 100);
endif
time = $time_utils:dst_midnight($time_utils:from_month(words[2], (year - thisyear) - (year <= thisyear), day));
endif
endif
return time;
.
#42:26
":new_message_num() => number that the next incoming message will receive.";
set_task_perms(caller_perms());
new = (msgs = caller.messages) ? msgs[length(msgs)][1] + 1 | 1;
if (rmsgs = caller.messages_going)
lbrm = rmsgs[length(rmsgs)][2];
return max(new, lbrm[length(lbrm)][1] + 1);
else
return new;
endif
.
#42:27
set_task_perms(caller_perms());
return length(caller.messages);
.
#42:28
set_task_perms(caller_perms());
date = args[1];
msgs = caller.messages;
if ((r = length(caller.messages)) < 25)
for l in [1..r]
if (msgs[l][2][1] > date)
return l - 1;
endif
endfor
return r;
else
l = 1;
while (l <= r)
if (date < msgs[i = (r + l) / 2][2][1])
r = i - 1;
else
l = i + 1;
endif
endwhile
return r;
endif
.
#42:29
set_task_perms(caller_perms());
date = args[1];
msgs = caller.messages;
if ((len = length(caller.messages)) < 25)
for r in [0..len - 1]
if (msgs[len - r][2][1] <= date)
return r;
endif
endfor
return len;
else
l = 1;
r = len;
while (l <= r)
if (date < msgs[i = (r + l) / 2][2][1])
r = i - 1;
else
l = i + 1;
endif
endwhile
return len - r;
endif
.
#42:30
":length_num_le(num) => number of messages in folder numbered <= num";
set_task_perms(caller_perms());
return $list_utils:iassoc_sorted(args[1], caller.messages);
.
#42:31
":exists_num_eq(num) => index of message in folder numbered == num";
set_task_perms(caller_perms());
return (i = $list_utils:iassoc_sorted(args[1], caller.messages)) && ((caller.messages[i][1] == args[1]) && i);
.
#42:32
":from_msg_seq(object or list[,mask])";
" => msg_seq of messages from any of these senders";
set_task_perms(caller_perms());
if (typeof(plist = args[1]) != LIST)
plist = {plist};
endif
mask = {@args, {1}}[2];
i = 1;
fseq = {};
for msg in (caller.messages)
if ((!mask) || (i < mask[1]))
elseif ((length(mask) < 2) || (i < mask[2]))
fromline = msg[2][2];
if (toobj(fromline[rindex(fromline, "(") + 1..rindex(fromline, ")") - 1]) in plist)
fseq = $seq_utils:add(fseq, i, i);
endif
else
mask = mask[3..length(mask)];
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return fseq || ("%f %<has> no messages from " + $string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%n (%#)", plist), "no one", " or "));
.
#42:33
":%from_msg_seq(string or list of strings[,mask])";
" => msg_seq of messages with one of these strings in the from line";
set_task_perms(caller_perms());
if (typeof(nlist = args[1]) != LIST)
nlist = {nlist};
endif
i = 1;
fseq = {};
mask = {@args, {1}}[2];
for msg in (caller.messages)
if ((!mask) || (i < mask[1]))
elseif ((length(mask) < 2) || (i < mask[2]))
fromline = " " + msg[2][2];
for n in (nlist)
if (index(fromline, n))
fseq = $seq_utils:add(fseq, i, i);
endif
endfor
else
mask = mask[3..length(mask)];
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return fseq || ("%f %<has> no messages from " + $string_utils:english_list($list_utils:map_arg($string_utils, "print", nlist), "no one", " or "));
.
#42:34
":to_msg_seq(object or list[,mask]) => msg_seq of messages to those people";
set_task_perms(caller_perms());
if (typeof(plist = args[1]) != LIST)
plist = {plist};
endif
mask = {@args, {1}}[2];
i = 1;
seq = {};
for msg in (caller.messages)
if ((!mask) || (i < mask[1]))
elseif ((length(mask) < 2) || (i < mask[2]))
toline = msg[2][3];
for r in ($mail_agent:parse_address_field(toline))
if (r in plist)
seq = $seq_utils:add(seq, i, i);
endif
endfor
else
mask = mask[3..length(mask)];
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return seq || ("%f %<has> no messages to " + $string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%n (%#)", plist), "no one", " or "));
.
#42:35
":%to_msg_seq(string or list of strings[,mask])";
" => msg_seq of messages containing one of strings in the to line";
set_task_perms(caller_perms());
if (typeof(nlist = args[1]) != LIST)
nlist = {nlist};
endif
i = 1;
seq = {};
mask = {@args, {1}}[2];
for msg in (caller.messages)
if ((!mask) || (i < mask[1]))
elseif ((length(mask) < 2) || (i < mask[2]))
toline = " " + msg[2][3];
for n in (nlist)
if (index(toline, n))
seq = $seq_utils:add(seq, i, i);
endif
endfor
else
mask = mask[3..length(mask)];
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return seq || ("%f %<has> no messages to " + $string_utils:english_list($list_utils:map_arg($string_utils, "print", nlist), "no one", " or "));
.
#42:36
":subject_msg_seq(target) => msg_seq of messages with target in the Subject:";
set_task_perms(caller_perms());
target = args[1];
i = 1;
seq = {};
mask = {@args, {1}}[2];
for msg in (caller.messages)
if ((!mask) || (i < mask[1]))
elseif ((length(mask) < 2) || (i < mask[2]))
subject = msg[2][4];
if (index(subject, target))
seq = $seq_utils:add(seq, i, i);
endif
else
mask = mask[3..length(mask)];
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return seq || (("%f %<has> no messages with subjects containing `" + target) + "'");
.
#42:37
":body_msg_seq(target[,mask]) => msg_seq of messages with target in the body";
set_task_perms(caller_perms());
target = args[1];
i = 1;
seq = {};
mask = {@args, {1}}[2];
for msg in (caller.messages)
if ((!mask) || (i < mask[1]))
elseif ({@mask, $maxint}[2] <= i)
mask = mask[3..length(mask)];
elseif ((bstart = "" in (msg = msg[2])) && ((l = length(msg)) > bstart))
while ((!index(msg[l], target)) && ((l = l - 1) > bstart))
$command_utils:suspend_if_needed(0);
endwhile
if (l > bstart)
seq = $seq_utils:add(seq, i, i);
endif
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return seq || tostr("%f %<has> no messages containing `", target, "' in the body.");
.
#42:38
":messages_in_seq(msg_seq) => list of messages in msg_seq on folder (caller)";
set_task_perms(caller_perms());
if (typeof(msgs = args[1]) != LIST)
return caller.messages[msgs];
elseif (length(msgs) == 2)
return caller.messages[msgs[1]..msgs[2] - 1];
else
return $seq_utils:extract(msgs, caller.messages);
endif
.
#42:39
":__convert_new(@msg) => msg in new format (if it isn't already)";
"               ^ don't forget the @ here.";
"If the msg is already in the new format it passes through unchanged.";
"If the msg format is unrecognizable, warnings are printed.";
if (typeof(date = args[1]) != NUM)
date = 0;
start = 1;
else
start = 2;
if (!((colon = index(args[2], ":")) && (args[2][1..colon] in {"From:", "To:", "Subject:"})))
return args;
endif
endif
from = to = 0;
subject = " ";
blank = "" in {@args, ""};
newhdr = {};
for line in (args[start..blank - 1])
if (index(line, "Date:") == 1)
if (date)
player:notify("Warning: two dates?");
endif
date = $time_utils:from_ctime(line[6..length(line)]);
elseif (index(line, "From:") == 1)
if (from)
player:notify("Warning: two from-lines?");
endif
from = $string_utils:triml(line[6..length(line)]);
elseif (index(line, "To:") == 1)
if (to)
player:notify("Warning: two to-lines?");
endif
to = $string_utils:triml(line[6..length(line)]);
elseif (index(line, "Subject:") == 1)
subject = $string_utils:triml(line[9..length(line)]);
else
newhdr = {@newhdr, line};
endif
endfor
if (!from)
player:notify("Warning: no from-line.");
endif
if (!to)
player:notify("Warning: no to-line.");
endif
return {date, from, to, subject, @newhdr, @args[blank..length(args)]};
.
#42:40
":to_text(@msg) => message in text form (suitable for printing)";
start = {"Date:     " + ctime(args[1]), "From:     " + this:english_address_field(args[2]), "To:       " + this:english_address_field(args[3]), @(args[4] == " ") ? {} | {"Subject:  " + args[4]}};
heads = {};
bodystart = "" in {@args, ""};
for h in (args[5..bodystart - 1])
heads = {@heads, this:english_address_header(h)};
endfor
return {@start, @heads, @args[bodystart..length(args)]};
.
#42:41
what = args[1];
who = args[2];
rest = args[3..length(args)];
if ($object_utils:isa(what, $mail_recipient))
return what:(verb)(who, @rest);
else
"...it's a player:";
"...  anyone can send mail to it.";
"...  only the player itself or a wizard can read it.";
return (verb == "is_usable_by") || $perm_utils:controls(who, what);
endif
.
#42:42
"WIZARDLY";
"raw_send(text,rcpts,sender) -- does the actual sending of a message.  Assumes that text has already been formatted correctly.  Decides who to send it to and who wants to be notified about it and does so.";
"Return {E_PERM} if the caller is not entitled to use this verb.";
"Return {0, @invalid_rcpts} if rcpts contains any invalid addresses.  No mail is sent in this case.";
"Return {1, @actual_rcpts} if successful.";
text = args[1];
rcpts = args[2];
from = args[3];
if (typeof(rcpts) != LIST)
rcpts = {rcpts};
endif
if (!(caller in {$mail_agent, $mail_editor}))
return {E_PERM};
elseif (bogus = (resolve = this:resolve_addr(rcpts, from))[1])
return {0, bogus};
else
this:touch(rcpts);
actual_rcpts = resolve[2];
biffs = resolve[3];
results = {};
for recip in (actual_rcpts)
if ((ticks_left() < 1000) || (seconds_left() < 5))
player:notify(tostr("...", recip));
suspend(0);
endif
if (typeof(e = recip:receive_message(text, from)) in {ERR, STR})
"...receive_message bombed...";
player:notify(tostr(recip, ":receive_message:  ", e));
e = 0;
elseif ((!is_player(recip)) || (!e))
"...not a player or receive_message isn't giving out the message number";
"...no need to force a notification...";
elseif (i = $list_utils:iassoc(recip, biffs))
"...player-recipient was already getting a notification...";
"...make sure notification includes a mention of him/her/itself.";
if (!(recip in listdelete(biffs[i], 1)))
biffs[i][2..1] = {recip};
endif
else
"...player-recipient wasn't originally being notified at all...";
biffs = {{recip, recip}, @biffs};
endif
results = {@results, e};
endfor
fork (0)
for b in (biffs)
if ((ticks_left() < 1000) || (seconds_left() < 5))
suspend(0);
endif
if ($object_utils:has_callable_verb(b[1], "notify_mail"))
mnums = {};
for r in (listdelete(b, 1))
mnums = {@mnums, (rn = r in actual_rcpts) && results[rn]};
endfor
b[1]:notify_mail(from, listdelete(b, 1), mnums);
endif
endfor
endfork
return {1, @actual_rcpts};
endif
.
#42:43
":english_address_field(string) => string";
"This routine removes object numbers from address lists as appearing in standard From:, To:, and Reply-To: fields.  It also indicates the current name of the object, if none of :name(), .name, and all .aliases are equal to the former name.";
field = args[1];
names = {};
while (subs = rmatch(field, "%(),? %(and %)?%|^%)%(.+%) (%(#[0-9]+%))"))
name = substitute("%3", subs);
o = toobj(substitute("%4", subs));
if (valid(o))
isrec = $object_utils:isa(o, $mail_recipient);
if (isrec)
matchname = name[2..length(name)];
else
matchname = name;
endif
curname = isrec ? o:name() | o:iname();
newname = (isrec ? "*" | "") + curname;
simplename = (isrec ? "*" | "") + o.name;
if (!(matchname in {curname, o.name, @o.aliases}))
name = tostr(name, " (now ", newname, ")");
endif
names = {name, @names};
else
names = {name + " (now recycled)", @names};
endif
field = field[1..subs[1]];
endwhile
return $string_utils:english_list(names);
.
#42:44
"resend_message(new_sender,new_rcpts,from,to,hdrs,body)";
" -- reformats and resends a previously sent message to new recipients.";
"msg is the previous message";
"Return E_PERM if new_sender isn't owned by the caller.";
"Return {0, @invalid_rcpts} if new_rcpts contains any invalid addresses.  No mail is sent in this case.";
"Return {1, @actual_rcpts} if successful.";
new_sender = args[1];
new_rcpts = args[2];
from = args[3];
to = args[4];
hdrs = args[5];
body = args[6];
if (typeof(hdrs) != LIST)
hdrs = {hdrs, 0};
elseif (length(hdrs) < 2)
hdrs = {@hdrs || {""}, 0};
endif
hdrs[3..2] = {{"Resent-By", this:nn_list(new_sender)}, {"Resent-To", this:nn_list(@new_rcpts)}};
if ($perm_utils:controls(caller_perms(), new_sender))
text = $mail_agent:make_message(from, to, hdrs, body);
return this:raw_send(text, new_rcpts, new_sender);
else
return E_PERM;
endif
.
#42:45
":parse_misc_headers(msg,@extract_names)";
"Extracts the miscellaneous (i.e., not including Date: From: To: Subject:)";
"from msg (a mail message in the usual transmission format).";
"Extract_names is a list of header names";
"=> {other_headers,bogus_headers,extract_texts,body}";
"where each element of extract_texts is a string or 0";
"  according as the corresponding header in extract_names is present.";
"bogus_headers is a list of those headers that are undecipherable";
"other_headers is a list of {header_name,header_text} for the remaining";
"  miscellaneous headers.";
"headers in msg";
msgtxt = args[1];
extract_names = args[2..length(args)];
extract_texts = $list_utils:make(length(extract_names));
heads = bogus = {};
for h in (msgtxt[5..(bstart = "" in {@msgtxt, ""}) - 1])
if (m = match(h, "%([a-z1-9-]+%): +%(.*%)"))
hname = h[m[3][1][1]..m[3][1][2]];
htext = h[m[3][2][1]..m[3][2][2]];
if (i = hname in extract_names)
extract_texts[i] = htext;
else
heads = {@heads, {hname, htext}};
endif
else
bogus = {@bogus, h};
endif
endfor
return {heads, bogus, extract_texts, msgtxt[bstart + 1..length(msgtxt)]};
.
#42:46
header = args[1];
if (m = match(header, "^%(Reply-To%|Resent-By%|Resent-To%|Original-From%): *"))
return header[m[1]..m[2]] + this:english_address_field(header[m[2] + 1..length(header)]);
else
return header;
endif
.
#42:47
if (args)
what = args[1];
if ($object_utils:isa(what, $mail_recipient))
name = "*" + what:name();
else
name = what:iname();
endif
return tostr(name, " (", what, ")");
return tostr($object_utils:isa(what, $mail_recipient) ? "*" | "", strsub(strsub(what:iname(), "(", ""), ")", ""), " (", what, ")");
else
return pass(@args);
endif
.
#42:48
":addrname(obj) name to use for obj in an address field";
what = args[1];
if ($object_utils:isa(what, $mail_recipient))
return "*" + what:name();
else
return what:iname();
endif
.
#42:49
"A nicer way than a direct call to player:tell to report helpful error information.";
"In most cases, :handle_error simply *does* call player:tell, but it can do something else with the information depending on the circumstance.";
player:tell($string_utils:from_list(args));
.
#42:50
"Only allow mailing lists/folders in here and only if their names aren't already taken.";
what = args[1];
return ($object_utils:isa(what, $mail_recipient) && this:check_names(what)) && (what:description() != parent(what):description());
.
#42:51
if ($__core_init_phase)
delete_verb(this, "xmoo_send_message");
delete_property(this, "trusts");
return pass(@args);
endif
.
#42:52
"send_log_message(perms,from,rcpt-list,hdrs,msg) -- formats and sends a mail message. hders is either the text of the subject line, or a {subject,{reply-to,...}} list.";
"Send a message while supplying a different permission for use by :mail_forward to determine moderation action.";
"Return E_PERM unless called by a wizard.";
"Return {0, @invalid_rcpts} if rcpt-list contains any invalid addresses.  No mail is sent in this case.";
"Return {1, @actual_rcpts} if successful.";
{perms, from, to, hdrs, msg} = args;
if (!caller_perms().wizard)
raise(E_PERM);
else
text = $mail_agent:make_message(from, to, hdrs, msg);
return this:raw_send(text, to, perms);
endif
.
#43:0
return this:ok(who = args[1]) && (("a letter to " + this:recipient_names(who)) + ((subject = this.subjects[who]) && ((" entitled \"" + subject) + "\"")));
.
#43:1
"invoke(rcptstrings,verb[,subject]) for a @send";
"invoke(1,verb,rcpts,subject,replyto,body) if no parsing is needed";
"invoke(2,verb,msg,flags,replytos) for an @answer";
if (!(which = args[1]))
player:tell_lines({tostr("Usage:  ", args[2], " <list-of-recipients>"), tostr("        ", args[2], "                      to continue with a previous draft")});
elseif (typeof(which) == LIST)
"...@send...";
if (rcpts = this:parse_recipients({}, which))
if (replyto = player:mail_option("replyto"))
replyto = this:parse_recipients({}, replyto, ".mail_options: ");
endif
if (0 == (subject = {@args, 0}[3]))
if (player:mail_option("nosubject"))
subject = "";
else
player:tell("Subject:");
subject = $command_utils:read();
endif
endif
return {rcpts, subject, replyto, {}};
endif
elseif (which == 1)
return args[3..6];
elseif (!(to_subj = this:parse_msg_headers(msg = args[3], flags = args[4])))
else
include = {};
if ("include" in flags)
prefix = ">            ";
for line in ($mail_agent:to_text(@msg))
if (!line)
prefix = ">  ";
endif
include = {@include, @this:fill_string(">  " + line, 70, prefix)};
endfor
endif
return {@to_subj, args[5], include};
endif
return 0;
.
#43:2
if (this:ok(who = args[1]))
this.recipients[who] = args[2];
this.subjects[who] = args[3];
this.replytos[who] = args[4] || {};
this:load(who, args[5]);
this.active[who]:tell("Composing ", this:working_on(who));
p = this.active[who];
if (p:mail_option("enter") && (!args[5]))
if (typeof(lines = $command_utils:read_lines()) == ERR)
p:tell(lines);
else
this:insert_line(p in this.active, lines, 0);
endif
endif
endif
.
#43:3
if (!dobjstr)
plyr = player;
elseif ($command_utils:player_match_result(plyr = player:my_match_player(dobjstr), dobjstr)[1])
return;
endif
if ((plyr != player) && (!this:readable(plyr in this.active)))
player:tell(plyr.name, "(", plyr, ") has not published anything here.");
elseif (typeof(msg = this:message_with_headers(plyr in this.active)) != LIST)
player:tell(msg);
else
player:display_message({((plyr == player) ? "Your" | tostr(plyr.name, "(", plyr, ")'s")) + " message so far:", ""}, player:msg_text(@msg));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669039, \"Erik\", #74, \"JHM\"}";
.
#43:4
return (this:readable(who = args[1]) || this:ok(who)) && $mail_agent:make_message(this.active[who], this.recipients[who], {this.subjects[who], this.replytos[who]}, this:text(who));
.
#43:5
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
elseif (!argstr)
player:tell(this.subjects[who]);
elseif (ERR == typeof(subj = this:set_subject(who, argstr)))
player:tell(subj);
else
player:tell(subj ? ("Setting the subject line for your message to \"" + subj) + "\"." | "Deleting the subject line for your message.");
endif
.
#43:6
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
else
this.subjects[who] = subj = args[2];
this:set_changed(who, 1);
return subj;
endif
.
#43:7
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
elseif (!args)
player:tell("Your message is currently to ", this:recipient_names(who), ".");
else
this.recipients[who] = this:parse_recipients({}, args);
this:set_changed(who, 1);
player:tell("Your message is now to ", this:recipient_names(who), ".");
endif
.
#43:8
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
else
this.recipients[who] = this:parse_recipients(this.recipients[who], args);
this:set_changed(who, 1);
player:tell("Your message is now to ", this:recipient_names(who), ".");
endif
.
#43:9
"parse_recipients(prev_list,list_of_strings) -- parses list of strings and adds any resulting player objects to prev_list.  Optional 3rd arg is prefixed to any mismatch error messages";
{recips, l, ?cmd_id = ""} = args;
pstrings = mdrops = {};
for s in ((typeof(l) == LIST) ? l | {l})
if (typeof(s) != STR)
if (valid(s) && (is_player(s) || ($mail_recipient in $object_utils:ancestors(s))))
mdrops = {@mdrops, s};
else
player:tell(cmd_id, s, " is not a valid mail recipient.");
endif
elseif ((!s) || (s[1] != "*"))
pstrings = {@pstrings, s};
elseif (!$mail_agent:match_failed(md = $mail_agent:match(s), s, cmd_id))
mdrops = {@mdrops, md};
endif
endfor
newreps = $string_utils:match_player(pstrings);
pmr = $command_utils:player_match_result({@recips, @newreps, @mdrops}, {@recips, @pstrings, @mdrops}, cmd_id);
return listdelete(pmr, 1);
"Metadata 202106";
"Last-Modify: {835902864, \"Erik\", #74, \"JHM\"}";
.
#43:10
return this:ok(who = args[1]) && $mail_agent:name_list(@this.recipients[who]);
.
#43:11
return $mail_agent:make_message(@args);
.
#43:12
"(obsolete verb... see $mail_agent:name_list)";
return $mail_agent:(verb)(@args[1]);
.
#43:13
"parse_msg_headers(msg,flags)";
"  parses msg to extract reply recipients and construct a subject line";
"  if the \"all\" flag is present, reply goes to all of the original recipients";
"  returns a list {recipients, subjectline} or 0 in case of error.";
msg = args[1];
flags = args[2];
replyall = "all" in flags;
objects = $mail_agent:parse_address_field(msg[2] + (replyall ? msg[3] | ""));
for line in (msg[5..("" in {@msg, ""}) - 1])
if (rt = index(line, "Reply-to:") == 1)
objects = $mail_agent:parse_address_field(line);
endif
endfor
recips = {};
for o in (objects)
if (o == #0)
player:tell("Sorry, but I can't parse the header of that message.");
return 0;
elseif ((!valid(o)) || (!(is_player(o) || ($mail_recipient in $object_utils:ancestors(o)))))
player:tell(o, " is no longer a valid player or maildrop; ignoring that recipient.");
elseif (o != player)
recips = setadd(recips, o);
endif
endfor
subject = msg[4];
if (subject == " ")
subject = "";
elseif (subject && (index(subject, "Re: ") != 1))
subject = "Re: " + subject;
endif
return {recips, subject};
.
#43:14
flags = {};
for o in ({"all", "include"})
if (player:mail_option(o))
flags = {@flags, o};
endif
endfor
reply_to = player:mail_option("replyto") || {};
flaglist = "+1#include -1#noinclude +2#all -2#sender 0#replyto ";
for a in (args)
if (i = index(a, "="))
value = a[i + 1..length(a)];
a = a[1..i - 1];
else
value = "";
endif
if ((typeof(a) != STR) || ((i = index(flaglist, "#" + a)) < 3))
player:tell("Unrecognized answer/reply option:  ", a);
return 0;
elseif (i != rindex(flaglist, "#" + a))
player:tell("Ambiguous answer/reply option:  ", a);
return 0;
elseif (j = index("0123456789", flaglist[i - 1]) - 1)
if (value)
player:tell("Flag does not take a value:  ", a);
return 0;
endif
f = {"include", "all"}[j];
flags = (flaglist[i - 2] == "+") ? setadd(flags, f) | setremove(flags, f);
elseif ((!value) || (value = this:parse_recipients({}, $string_utils:explode(value), "replyto flag:  ")))
reply_to = value || {};
endif
endfor
return {flags, reply_to};
.
#43:15
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
else
if (args)
this.replytos[who] = rt = this:parse_recipients({}, args);
this:set_changed(who, 1);
else
rt = this.replytos[who];
endif
player:tell(rt ? ("Replies will go to " + $mail_agent:name_list(@this.replytos[who])) + "." | "Reply-to field is empty.");
endif
.
#43:16
"WIZARDLY";
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
elseif (!(recips = this.recipients[who]))
player:tell("Umm... your message isn't addressed to anyone.");
else
msg = this:message_with_headers(who);
result = $mail_agent:raw_send(msg, recips, player);
if (!(e = result[1]))
player:tell((typeof(e) == ERR) ? e | ("Bogus recipients:  " + $string_utils:from_list(result[2])));
player:tell("Mail not sent.");
elseif (length(result) == 1)
player:tell("Mail not actually sent to anyone.");
else
player:tell("Mail actually sent to ", $mail_agent:name_list(@listdelete(result, 1)));
this:set_changed(who, 0);
this:done();
endif
endif
.
#43:17
if (dobjstr)
if (!(recips = this:parse_recipients({}, args)))
"parse_recipients has already complained about anything it doesn't like";
return;
endif
elseif (caller != player)
return E_PERM;
elseif (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
else
recips = this.recipients[who];
endif
resolve = $mail_agent:resolve_addr(recips, player);
if (resolve[1])
player:tell("Bogus addresses:  ", $string_utils:english_list(resolve[1]));
else
player:tell(dobjstr ? ("Mail to " + $mail_agent:name_list(@recips)) + " actually goes to " | "Your mail will actually go to ", $mail_agent:name_list(@resolve[2]));
endif
.
#43:18
player:tell_lines({"Available aliases:", ""});
for c in ((dobjstr == "all") ? $object_utils:descendants($mail_recipient) | $mail_agent.contents)
if (c:is_usable_by(player) || c:is_readable_by(player))
c:look_self();
endif
endfor
.
#43:19
if (!iobjstr)
player:tell("Usage:  ", verb, " [<list-of-people/lists>] to <list>");
return;
elseif ($mail_agent:match_failed(iobj = $mail_agent:match(iobjstr), iobjstr))
return;
endif
rstrs = dobjstr ? $string_utils:explode(dobjstr) | {"me"};
recips = this:parse_recipients({}, rstrs);
outcomes = iobj:add_forward(@recips);
if (typeof(outcomes) != LIST)
player:tell(outcomes);
return;
endif
added = {};
for r in [1..length(recips)]
if ((t = typeof(e = outcomes[r])) == OBJ)
added = setadd(added, recips[r]);
else
player:tell(verb, " ", recips[r]:dname(), " to ", $mail_agent:addrname(iobj), ":  ", e);
endif
endfor
if (added)
if ((i = player in added) && (!player:misc_option("third_person")))
added[i] = $you;
endif
player:tell("Subscribed ", $string_utils:dname_list(added), " to ", $mail_agent:addrname(iobj), ".");
endif
.
#43:20
if (!iobjstr)
player:tell("Usage:  ", verb, " [<list-of-people/lists>] from <list>");
return;
elseif ($mail_agent:match_failed(iobj = $mail_agent:match(iobjstr), iobjstr))
return;
endif
rstrs = dobjstr ? $string_utils:explode(dobjstr) | {"me"};
recips = this:parse_recipients({}, rstrs);
outcomes = iobj:delete_forward(@recips);
if (typeof(outcomes) != LIST)
player:tell(outcomes);
return;
endif
removed = {};
for r in [1..length(recips)]
if (typeof(e = outcomes[r]) == ERR)
player:tell(verb, " ", recips[r]:name(), " from ", iobj:name(), ":  ", (e == E_INVARG) ? "Not on list." | e);
else
removed = setadd(removed, recips[r]);
endif
endfor
if (removed)
if ((i = player in removed) && (!player:misc_option("third_person")))
removed[i] = $you;
endif
player:tell("Unsubscribed ", $string_utils:dname_list(removed), " from ", $mail_agent:name(iobj), ".");
endif
.
#43:21
"parse_msg_headers(msg,flags)";
"  parses msg to extract reply recipients and construct a subject line";
"  if the \"all\" flag is present, reply goes to all of the original recipients";
"  returns a list {recipients, subjectline} or 0 in case of error.";
msg = args[1];
flags = args[2];
replyall = "all" in flags;
recipients = $mail_agent:parse_address_field(msg[2] + (replyall ? msg[3] | ""));
subject = msg[4];
if (subject == " ")
subject = "";
endif
noreplyto = 1;
for line in (msg[1..("" in msg) || length(msg)])
if (typeof(line) != STR)
"Skip it";
elseif (index(line, "Subject:") == 1)
subject = $string_utils:triml(line[9..length(line)]);
elseif ((rt = index(line, "Reply-to:") == 1) || (noreplyto && ((index(line, "From:") == 1) || (replyall && (index(line, "To:") == 1)))))
if (rt)
noreplyto = 0;
recipients = {};
endif
recipients = {@recipients, @$mail_agent:parse_address_field(line)};
endif
endfor
if (subject && (index(subject, "Re: ") != 1))
subject = "Re: " + subject;
endif
recips = {};
for o in (recipients)
if (o == #0)
player:tell("Sorry, but I can't parse the header of that message.");
return 0;
elseif ((!valid(o)) || (!(is_player(o) || ($mail_recipient in $object_utils:ancestors(o)))))
player:tell(o, " is no longer a valid player or maildrop; ignoring that recipient.");
elseif (o != player)
recips = setadd(recips, o);
endif
endfor
return {recips, subject};
.
#43:22
lines = {"To:       " + (toline = $mail_agent:nn_list(@args[1])), "Subject:  " + $string_utils:trim(subject = args[2])};
if (args[3])
lines = {@lines, "Reply-to: " + $mail_agent:nn_list(@args[3])};
endif
lines = {@lines, "", @args[4]};
info = {"mail", tostr("MOOMail", subject ? ("(" + $string_utils:print(subject)) + ")" | (("-to(" + $string_utils:print(toline)) + ")")), lines, "@@sendmail"};
"reference, name, type, content";
mcp_info = {"sendmail", info[2], "string-list", info[3]};
return {@info, mcp_info};
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895556278, \"Ken\", #75, \"JHM\"}";
.
#44:0
if (this:changed(who = player in this.active))
player:tell("You are still editing ", this:working_on(who), ".  Please type ABORT or SAVE first.");
elseif (spec = this:parse_invoke(dobjstr, verb))
this:init_session(who, @spec);
endif
.
#44:1
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
endif
if (!dobjstr)
note = this.objects[who];
elseif (1 == (note = this:note_match_failed(dobjstr)))
return;
else
this.objects[who] = note;
endif
text = this:text(who);
mode = this.mode[who];
if ((mode == "string") && (length(text) <= 1))
text = text ? text[1] | "";
elseif (mode == "value")
value_list = this:to_value(@this:text(who));
if (value_list[1])
player:tell("Error on line ", value_list[1], ":  ", value_list[2]);
player:tell("Text not saved to ", this:working_on(who), ".");
return;
endif
text = value_list[2];
endif
if (ERR == typeof(result = this:set_note_text(note, text)))
player:tell("Text not saved to ", this:working_on(who), ":  ", result);
if ((result == E_TYPE) && (typeof(note) == OBJ))
player:tell("Do `text list' and try saving again.");
elseif (!dobjstr)
player:tell("Use `save' with an argument to save the text elsewhere.");
endif
else
player:tell("Text written to ", this:working_on(who), " in ", mode, " mode.");
this:set_changed(who, 0);
endif
.
#44:2
if (this:ok(who = args[1]))
if (typeof(text = args[3]) == STR)
mode = "string";
elseif ($list_utils:check_type(text, STR))
mode = "text";
else
mode = "value";
endif
this.mode[who] = mode;
this:load(who, (mode == "string") ? text ? {text} | {} | ((mode == "text") ? text | this:explode_list(0, text)));
this.objects[who] = args[2];
player:tell("Now editing ", this:working_on(who), ".", @(mode == "text") ? {} | {"  [", mode, " mode]"});
endif
.
#44:3
if (!(who = args[1]))
return "????";
endif
spec = this.objects[who];
if (typeof(spec) == LIST)
object = spec[1];
prop = spec[2];
else
object = spec;
prop = 0;
endif
return valid(object) ? tostr("\"", object:name(), "\"(", object, ")", prop ? "." + prop | "") | tostr(prop ? ("." + prop) + " on " | "", "invalid object (", object, ")");
.
#44:4
":parse_invoke(string,verb)";
" string is the actual commandline string indicating what we are to edit";
" verb is the command verb that is attempting to invoke the editor";
if (!(string = args[1]))
player:tell_lines({("Usage:  " + args[2]) + " <note>              (where <note> is some note object)", ("        " + args[2]) + " <object>.<property> (edits an arbitrary property)", ("        " + args[2]) + "                     (continues editing an unsaved note)"});
elseif (1 == (note = this:note_match_failed(string)))
elseif (ERR == typeof(text = this:note_text(note)))
player:tell("Couldn't retrieve text:  ", text);
else
return {note, text};
endif
return 0;
.
#44:5
"WIZARDLY";
if ((caller != $note_editor) || (caller_perms() != $note_editor.owner))
return E_PERM;
endif
set_task_perms(player);
if (typeof(spec = args[1]) == OBJ)
text = spec:text();
elseif ($object_utils:has_callable_verb(spec[1], getter = "get_" + spec[2]))
text = spec[1]:(getter)();
else
text = spec[1].(spec[2]);
endif
"if ((tt = typeof(text)) in {ERR, STR} || (tt == LIST && (!text || typeof(text[1]) == STR)))";
if (typeof(text) in {ERR, STR, LIST})
return text;
else
return E_TYPE;
endif
.
#44:6
"WIZARDLY";
if ((caller != $note_editor) || (caller_perms() != $note_editor.owner))
return E_PERM;
endif
set_task_perms(player);
if (typeof(spec = args[1]) == OBJ)
return spec:set_text(args[2]);
elseif ($object_utils:has_callable_verb(spec[1], setter = "set_" + spec[2]))
return spec[1]:(setter)(args[2]);
else
return spec[1].(spec[2]) = args[2];
endif
.
#44:7
if (pp = $code_utils:parse_propref(string = args[1]))
object = pp[1];
prop = pp[2];
else
object = string;
prop = 0;
endif
if ($command_utils:object_match_failed(note = player:my_match_object(object, this:get_room(player)), object))
elseif (prop)
if (!($object_utils:has_property(note, prop) || $object_utils:has_callable_verb(note, "get_" + prop)))
player:tell(object, " has no \".", prop, "\" property.");
else
return {note, prop};
endif
elseif (!$object_utils:isa(note, $note))
player:tell(object, "(", note, ") is not a note.");
else
return note;
endif
return 1;
.
#44:8
pass(@args);
if (who = this:loaded(player))
if (this.mode[who] == "string")
player:tell("Text will be stored as a single string instead of a list when possible.");
elseif (this.mode[who] == "value")
player:tell("Text will be stored as an evaluated value.");
endif
endif
.
#44:9
"mode [string|text|value]";
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
endif
if (!dobjstr)
player:tell("Available modes are string, text, and value.");
endif
modeswitch = 0;
if (index("string", dobjstr) == 1)
if (this.mode[who] == "value")
modeswitch = 1;
endif
this.mode[who] = mode = "string";
player:tell("Now in string mode:");
elseif (index("text", dobjstr) == 1)
if (this.mode[who] == "value")
modeswitch = 1;
endif
this.mode[who] = mode = "text";
player:tell("Now in text mode:");
elseif (index("value", dobjstr) == 1)
if (this.mode[who] != "value")
modeswitch = 1;
endif
this.mode[who] = mode = "value";
player:tell("Now in value mode:");
elseif (dobjstr)
player:tell("Unrecognized mode:  ", dobjstr);
player:tell("Should be one of `string', `text', or `value'");
return;
else
player:tell("Currently in ", mode = this.mode[who], " mode:");
endif
if (mode == "string")
player:tell("  store text as a single string instead of a list when possible.");
elseif (mode == "value")
player:tell("  store text as an evaluated value");
else
player:tell("  always store text as a list of strings.");
endif
if (modeswitch)
player:tell("Warning:  Switching ", (mode == "value") ? "into value" | "away from value", " mode probably doesn't do what you want; you may have to use a new `edit' command to get a listing in the appropriate form.");
endif
.
#44:10
what = args[1];
text = args[2];
if (typeof(text) == STR)
cmd = "@set-note-string";
type = "string";
vtyp = "str";
elseif ($list_utils:check_type(text, STR))
cmd = "@set-note-text";
type = "string-list";
vtyp = "str";
else
cmd = "@set-note-value";
text = this:explode_list(0, text);
type = "string-list";
vtyp = "val";
endif
name = (typeof(what) == OBJ) ? what:name() | tostr(what[1]:name(), ".", what[2]);
note = (typeof(what) == OBJ) ? what | tostr(what[1], ".", what[2]);
text = (typeof(text) == STR) ? {text} | text;
info = {"text", name, text, tostr(cmd, " ", note)};
mcp_info = {(vtyp + ":") + note, name, type, text};
return {@info, mcp_info};
"Metadata 202106";
"Last-Modify: {912983379, \"Ken\", #75, \"JHM\"}";
.
#44:11
"Copied from List Editor (#797):to_value by Erik (#37) Wed Apr 20 19:02:29 1994 EDT";
":to_value(@list_of_strings) => {line#, error_message} or {0,value}";
"converts the given list of strings back into a value if possible";
stack = {};
curlist = {};
curstr = 0;
i = 0;
for line in (args)
i = i + 1;
if (!(line = $string_utils:triml(line)))
"skip blank lines";
elseif ((char = line[1]) == "+")
if (curstr == 0)
return {i, "previous line is not a string"};
endif
curstr = curstr + line[2..length(line)];
else
if (curstr != 0)
curlist = {@curlist, curstr};
curstr = 0;
endif
if ((char == "}") || ((char == "{") && (!rindex(line, "}"))))
comma = 0;
for c in [1..length(line)]
char = line[c];
if (char == "}")
if (comma)
return {i, "unexpected `}'"};
elseif (!stack)
return {i, "too many }'s"};
endif
curlist = {@stack[1], curlist};
stack = listdelete(stack, 1);
elseif (char == "{")
comma = 1;
stack = {curlist, @stack};
curlist = {};
elseif (char == " ")
elseif ((!comma) && (char == ","))
comma = 1;
else
return {i, tostr("unexpected `", char, "'")};
endif
endfor
elseif (char == "\"")
curstr = line[2..length(line)];
elseif (char == "@")
if (!(v = $no_one:eval(("{" + line[2..length(line)]) + "}"))[1])
return {i, "Can't eval what's after the @"};
endif
curlist = {@curlist, @v[2]};
else
if (!(v = $no_one:eval(line))[1])
return {i, "Can't eval this line"};
endif
curlist = {@curlist, v[2]};
endif
endif
endfor
if (stack)
return {i, "missing }"};
endif
if (curstr != 0)
return {0, {@curlist, curstr}};
else
return {0, curlist};
endif
.
#44:12
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
elseif (this.mode[who] != "value")
player:tell("Sorry, `", verb, "' works only in value mode.");
elseif (typeof(range = this:parse_range(who, {"_", "1"}, @args)) != LIST)
player:tell(range);
elseif (range[3])
player:tell("Junk at end of cmd:  ", range[3]);
else
text = this.texts[who];
newins = ins = this.inserting[who];
start = range[1];
if (typeof(debris = this:explode_line("", text[start])) == STR)
player:tell("Line ", start, ":  ", debris);
return;
endif
if (!debris[1])
debris = listdelete(debris, 1);
endif
newlines = {};
for line in (text[i = start + 1..end = range[2]])
dlen = length(debris);
newlines = {@newlines, @debris[1..dlen - 1]};
if (ins == i)
newins = (start + length(newlines)) + 1;
endif
if (typeof(debris = this:explode_line(debris[dlen], line)) == STR)
player:tell("Line ", i, ":  ", debris);
return;
endif
i = i + 1;
endfor
explen = length(newlines) + length(debris);
if (ins > end)
newins = (ins - ((end - start) + 1)) + explen;
endif
this.texts[who] = {@text[1..start - 1], @newlines, @debris, @text[end + 1..length(text)]};
this.inserting[who] = newins;
"player:tell(\"--> \", start, \"..\", start + explen - 1);";
for line in [start..(start + explen) - 1]
this:list_line(player in this.active, line);
if ($command_utils:running_out_of_time())
suspend(0);
if (!(who = this:loaded(player)))
player:tell("ack!  something bad happened during a suspend...");
return;
endif
endif
endfor
if ((newins > length(text)) && (length(text) == ((start + explen) - 1)))
player:tell("^^^^");
endif
endif
.
#44:13
"Copied from List Editor (#797):explode_line by Erik (#37) Wed Apr 20 19:02:08 1994 EDT";
su = $string_utils;
prev = args[1];
line = su:triml(args[2]);
indent = length(args[2]) - length(line);
if (line[1] == "@")
if (!(splicee = $no_one:eval(("{" + line[2..length(line)]) + "}"))[1])
return "Can't eval what's after the @.";
endif
newlines = this:explode_list(indent + 1, splicee[2]);
return {prev, @newlines};
elseif (line[1] == "}")
if (this:is_delimiter(prev) && (!index(prev, "{")))
return {tostr(args[2][1..indent], su:trim(prev), " ", line)};
else
return args;
endif
elseif (line[1] != "{")
return args;
elseif (!rindex(line, "}"))
if (this:is_delimiter(prev))
return {(su:trimr(prev) + (rindex(prev, "{") ? " " | ", ")) + line};
else
return args;
endif
elseif (!(v = $no_one:eval(line))[1])
return "Can't eval this line.";
else
newlines = {@this:explode_list(indent + 2, v[2]), su:space(indent) + "}"};
if (this:is_delimiter(prev))
return {su:trimr(prev) + (rindex(prev, "{") ? " {" | ", {"), @newlines};
else
return {prev, su:space(indent) + "{", @newlines};
endif
endif
.
#44:14
"Copied from List Editor (#797):explode_list by Erik (#37) Wed Apr 20 19:02:10 1994 EDT";
":explode_list(indent,list) => corresponding list of strings to use.";
lines = {};
indent = $string_utils:space(args[1]);
for element in (args[2])
if (typeof(element) == STR)
lines = {@lines, (indent + "\"") + element};
else
lines = {@lines, indent + $string_utils:print(element)};
endif
endfor
return lines;
.
#44:15
"Copied from List Editor (#797):is_delimiter by Erik (#37) Wed Apr 20 19:02:21 1994 EDT";
line = $string_utils:triml(args[1]);
return line && ((line[1] == "}") || ((line[1] == "{") && (!rindex(line, "}"))));
.
#44:16
"Usage:  implode <start>-<end>";
"        implode <line>";
"        implode";
"Combine the given lines into a single list.  If both ends are given, they must be { and } lines, respectively.  If one line is given, the closest braces are used.  If no line is given, the closest braces to the current insertion point is used.";
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
elseif (this.mode[who] != "value")
player:tell("Sorry, `", verb, "' works only in value mode.");
elseif (typeof(range = this:parse_range(who, tostr(this.inserting[who = player in this.active]), @args)) != LIST)
player:tell(range);
elseif (range[3])
player:tell("Junk at end of cmd:  ", range[3]);
else
start = range[1];
end = range[2];
text = this.texts[who];
if (start == end)
if (match("^ *{$", text[start]))
end = this:end_of_list(who, start);
if (!end)
player:tell("Couldn't find end of list beginning at ", start, ".");
return;
endif
elseif (match("^ *}$", text[start]))
start = this:beginning_of_list(who, end);
if (!start)
player:tell("Couldn't find beginning of list ending at ", end, ".");
return;
endif
elseif (match("^ *}, {$", text[start]))
player:tell("Don't know how to handle }, { lines yet.");
return;
else
"Look for outer edges of list.";
while ((!found) && (start = start - 1))
found = match(text[start], "^ *%(}, %)?{$");
endwhile
if ((!found) || (!(end = this:end_of_list(who, start))))
player:tell("Couldn't identify enclosing list.");
return;
endif
endif
endif
if (typeof(start_extra = substitute("%1", match(text[start], "^ *%(}, %)?{$"))) != STR)
player:tell("Not a start line: ", text[start]);
return;
elseif (typeof(end_extra = substitute("%1", match(text[end], "^ *}%(, {%)?$"))) != STR)
player:tell("Not an end line: ", text[end]);
return;
endif
value_list = this:to_value("{", @text[start + 1..end - 1], "}");
if (value_list[1])
player:tell("Error on line ", (start - 1) + value_list[1], ":  ", value_list[2]);
else
if (start_extra)
indent = $string_utils:space(index(text[start], "}") - 1);
vallist = {text[start][1..length(indent) + 1]};
else
indent = $string_utils:space(index(text[start], "{") - 1);
vallist = {};
endif
for val in (value_list[2])
vallist = listappend(vallist, indent + $string_utils:print(val));
endfor
if (end_extra)
vallist = listappend(vallist, text[end][1..index(text[start], "}") - 1] + "{");
endif
this.texts[who][start..end] = vallist;
this.inserting[who] = start;
for line in [start..(start + length(vallist)) - 1]
this:list_line(who, line);
endfor
endif
endif
.
#44:17
"Copied from List Editor (#797):end_of_list by Erik (#37) Wed Apr 20 19:03:40 1994 EDT";
who = args[1];
start = args[2];
text = this.texts[who];
textlen = length(text);
line = text[start];
indent = index(line, "}") ? line[1..index(line, "}")] | strsub(line, "{", "}");
while ((start = start + 1) <= textlen)
if (index(text[start], indent) == 1)
return start;
endif
endwhile
.
#44:18
"Copied from List Editor (#797):beginning_of_list by Erik (#37) Wed Apr 20 19:03:44 1994 EDT";
who = args[1];
end = args[2];
text = this.texts[who];
line = text[end];
indent = line[1..index(line, "}") - 1] + "{";
while (end = end - 1)
if (index(text[end], indent) == 1)
return end;
endif
endwhile
.
#44:19
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
elseif (this.mode[who] != "text")
player:tell("Sorry, there's no `fill' in ", this.mode[who], " mode.");
elseif (typeof(range = this:parse_range(who, {"_-^", "_", "^"}, @args)) != LIST)
player:tell(range);
elseif (range[3])
player:tell("Junk at end of cmd:  ", range[3]);
elseif (!(result = this:join_lines(who, @range[1..2], length(verb) <= 4)))
player:tell((result == 0) ? "Need at least two lines to join." | result);
else
this:list_line(who, range[1]);
endif
"Copied from Generic Editor (#50):j by Erik (#74) Sat Aug 27 18:15:27 1994 EDT";
.
#44:20
if ($perm_utils:controls(caller_perms(), this))
return pass(@args);
else
return E_PERM;
endif
.
#45:0
if (!args)
player:tell("edit what?");
else
this:invoke(argstr, verb);
endif
.
#45:1
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
elseif (!(prepstr && iobjstr))
object = this.objects[who];
vname = this.verbnames[who];
changeverb = 0;
elseif ((prepstr != "as") || (!(spec = $code_utils:parse_verbref(iobjstr))))
player:tell("Usage: ", verb, " [as <object>:<verb>]");
return;
elseif ($command_utils:object_match_failed(object = player:my_match_object(spec[1], this:get_room(player)), spec[1]))
return;
else
vname = spec[2];
changeverb = 1;
endif
iobjstr = (tostr(object) + ":") + this:verb_name(object, vname);
if ((subs = player:eval_option("subs")) && player:edit_option("eval_subs"))
"if ((subs = player.eval_subs) && player:edit_option(\"eval_subs\"))";
text = {};
for x in (this:text(who))
text = {@text, $code_utils:substitute(x, subs)};
endfor
else
text = this:text(who);
endif
if (result = `this:set_verb_code(object, vname, text) ! ANY')
player:tell(iobjstr, " not compiled because:");
for x in (result)
player:tell("  ", x);
endfor
elseif (typeof(result) == ERR)
player:tell({result, ("You do not have write permission on " + iobjstr) + ".", ("The verb " + iobjstr) + " does not exist (!?!)", ("The object " + tostr(object)) + " does not exist (!?!)"}[1 + (result in {E_PERM, E_VERBNF, E_INVARG})]);
if (!prepstr)
player:tell("Do 'compile as <object>:<verb>' to write your code to another verb.");
endif
changeverb = 0;
else
player:tell(iobjstr, " successfully compiled.");
this:set_changed(who, 0);
endif
if (changeverb)
this.objects[who] = object;
this.verbnames[who] = vname;
endif
.
#45:2
{who} = args;
return this:ok(who) && tostr(this.objects[who], ":", toliteral(`verb_info(this.objects[who], this.verbnames[who])[3] ! ANY'), " ", $string_utils:from_list(`verb_args(this.objects[who], this.verbnames[who]) ! ANY', " "));
.
#45:3
{who, object, vname, lines, definer} = args;
if (this:ok(who))
this:load(who, this.active[who]:verbcode_internal_to_external(lines));
this.verbnames[who] = vname;
this.objects[who] = object;
this.active[who]:tell("Now editing ", this:working_on(who), ".");
"this.active[who]:tell(\"Now editing \", object, \":\\\"\", verb_info(object, vname)[3], \"\\\" \", $string_utils:from_list(verb_args(object, vname), \" \"), \".\");";
endif
.
#45:4
":parse_invoke(string,v)";
"  string is the commandline string to parse to obtain the obj:verb to edit";
"  v is the actual command verb used to invoke the editor";
" => {object, verbname, verb_code} or error";
vref = $string_utils:words(args[1]);
v = args[2];
bynumber = v[$] == "#";
if ((!vref) || (!(spec = $code_utils:parse_verbref(vref[1]))))
player:tell("Usage: ", v, " object:verb");
return;
endif
if (argspec = listdelete(vref, 1))
if (bynumber)
player:tell("Don't use args with ", v, ".");
return;
elseif (typeof(pas = $code_utils:parse_argspec(@argspec)) == LIST)
if (pas[2])
player:tell("Don't know what to do with \"", $string_utils:from_list(pas[2], " "), "\"");
return;
endif
argspec = pas[1][1..3];
argspec[2] = $code_utils:full_prep(argspec[2]) || argspec[2];
else
player:tell(pas);
return;
endif
endif
if ($command_utils:object_match_failed(object = player:my_match_object(spec[1], this:get_room(player)), spec[1]))
return 0;
elseif (bynumber)
if (!$string_utils:is_numeric(spec[2]))
player:tell("Must use verb number with ", v, ".");
return;
endif
vnum = toint(spec[2]);
if (vnum < 1)
player:tell("Verb number must be positive.");
return;
endif
code = `this:fetch_verb_code(object, vnum) ! ANY';
if (typeof(code) == ERR)
if (code == E_VERBNF)
player:tell("That object does not define that many verbs.");
else
player:tell(code);
endif
return code;
endif
return {object, vnum, code, object};
else
code = E_VERBNF;
definer = object;
while (valid(definer))
vnum = 0;
while ((vnum = $code_utils:find_verb_named_1_based(definer, spec[2], vnum + 1)) && (argspec && `verb_args(definer, vnum) != argspec ! E_PERM => 1'))
endwhile
if (vnum)
code = `this:fetch_verb_code(definer, vnum) ! ANY';
"FOR NOW...";
"vname = tostr(vnum - 1);";
break;
endif
definer = parent(definer);
endwhile
if (typeof(code) == ERR)
player:tell((code != E_VERBNF) ? code | "That object does not define that verb", argspec ? " with those arguments." | ".");
return code;
elseif ((definer != object) && (!player:edit_option("local")))
player:tell("That object does not define that verb", argspec ? " with those arguments" | "", ".  You may want to edit the verb on its ancestor, ", definer:iname(), " (", definer, ").");
return E_INVARG;
else
if (definer != object)
code = {tostr(object:dnamec(), " (", object, ") does not define a :", spec[2], " verb", argspec ? " with those arguments" | "", ".  The following code is from its ancestor, ", definer:iname(), " (", definer, ")."), @code};
endif
"return {object, vname, code, definer};";
return {object, vnum, code, definer};
endif
endif
.
#45:5
"WIZARDLY";
{object, vb} = args;
if ((caller != $verb_editor) || (caller_perms() != $verb_editor.owner))
return E_PERM;
else
set_task_perms(player);
return verb_code(object, vb, 1);
endif
.
#45:6
"WIZARDLY";
{object, vb, lines} = args;
if ((caller != $verb_editor) || (caller_perms() != $verb_editor.owner))
return E_PERM;
else
who = verb_info(object, vb)[1];
if (typeof(who) == OBJ)
set_task_perms(player);
endif
return set_verb_code(object, vb, player:verbcode_external_to_internal(lines));
endif
.
#45:7
{object, vname, code, definer} = args;
if (caller != $verb_editor)
raise(E_PERM);
endif
set_task_perms(player);
if (typeof(vname) == LIST)
vargs = tostr(" ", vname[2], " ", $code_utils:short_prep(vname[3]), " ", vname[4]);
vname = vname[1];
else
vargs = "";
endif
"... so the next 2 lines are actually wrong, since verb_info won't";
"... necessarily retrieve the correct verb if we have more than one";
"... matching the given same name; anyway, if parse_invoke understood vname,";
"... so will @program.  I suspect these were put here because in the";
"... old scheme of things, vname was always a number.";
if ((typeof(vname) == NUM) || $string_utils:is_numeric(vname))
vargs = verb_args(definer, vname);
if (!(vargs[2] in {"none", "any"}))
vargs[2] = $code_utils:short_prep(vargs[2]);
endif
vargs = tostr(" ", vargs[1], " ", vargs[2], " ", vargs[3]);
vname = strsub((vnames = $string_utils:explode(verb_info(definer, vname)[3]))[1], "*", "");
"(but i LIKE having a name instead of a number.)";
endif
name = tostr(object.name, ":", vname);
"";
if ((vargs == " this none this") && ((!player:list_option("args_assignment")) && (named = $code_utils:named_args_from_code(code))))
split = $code_utils:split_verb_code(code);
split[2][1..length(named[1]) + (named[2] ? 1 | 0)] = {};
code = $list_utils:flatten(split);
code = player:verbcode_internal_to_external(code);
reference = tostr(object, ":", vname, " (", $code_utils:named_args_list(named), ")");
else
code = player:verbcode_internal_to_external(code);
reference = tostr(object, ":", vname, vargs);
endif
info = {"program", name, code, tostr("@program ", reference)};
"reference, name, type, content";
mcp_info = {reference, "MOO verb: " + reference, "moo-code", code};
return {@info, mcp_info};
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895556159, \"Ken\", #75, \"JHM\"}";
.
#45:8
"verb_name(object, vname)";
"Find vname on object and return its full name (quoted).";
"This is useful for when we're working with verb numbers.";
{object, vname} = args;
if ((caller != $verb_editor) || (caller_perms() != $verb_editor.owner))
return E_PERM;
else
set_task_perms(player);
return `toliteral(verb_info(object, vname)[3]) ! ANY => tostr("???", vname)';
endif
.
#46:0
if ((caller != player) && (caller_perms() != player))
return E_PERM;
endif
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
else
this:insert_line(who, argstr);
endif
.
#46:1
if ((caller != player) && (caller_perms() != player))
return E_PERM;
endif
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
else
this:append_line(who, argstr);
endif
.
#46:2
if (!this:loaded(player))
player:tell(this:nothing_loaded_msg());
else
lines = $command_utils:read_lines();
this:insert_line(this:loaded(player), lines, 0);
endif
.
#46:3
nonum = 0;
if (verb == "view")
if (!args)
l = {};
for i in [1..length(this.active)]
if (this.readable[i])
l = {@l, this.active[i]};
endif
endfor
if (l)
player:tell("Players having readable texts in this editor:  ", $string_utils:names_of(l));
else
player:tell("No one has published anything in this editor.");
endif
return;
elseif ($command_utils:player_match_result(plyr = player:my_match_player(args[1]), args[1])[1])
"...no such player";
return;
elseif ((!(who = this:loaded(plyr))) || (!this:readable(who)))
player:tell(plyr:name(), "(", plyr, ") has not published anything in this editor.");
return;
endif
args = listdelete(args, 1);
elseif (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
endif
len = length(this.texts[who]);
ins = this.inserting[who];
window = 8;
if (len < (2 * window))
default = {"1-$"};
elseif (ins <= window)
default = {tostr("1-", 2 * window)};
else
default = {tostr(window, "_-", window, "^"), tostr(2 * window, "$-$")};
endif
if (typeof(range = this:parse_range(who, default, @args)) != LIST)
player:tell(tostr(range));
elseif (range[3] && (!(nonum = "nonum" == $string_utils:trim(range[3]))))
player:tell("Don't understand this:  ", range[3]);
elseif (nonum)
player:tell_lines(this.texts[who][range[1]..range[2]]);
else
for line in [range[1]..range[2]]
this:list_line(who, line);
if ($command_utils:running_out_of_time())
suspend(0);
if (!(who = this:loaded(player)))
player:tell("ack!  something bad happened during a suspend...");
return;
endif
endif
endfor
if ((ins > len) && (len == range[2]))
player:tell("^^^^");
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669067, \"Erik\", #74, \"JHM\"}";
.
#46:4
if (i = index(argstr, "\""))
text = argstr[i + 1..length(argstr)];
argstr = argstr[1..i - 1];
else
text = 0;
endif
spec = $string_utils:trim(argstr);
if (index("next", verb) == 1)
verb = "next";
spec = "+" + (spec || "1");
elseif (index("prev", verb) == 1)
verb = "prev";
spec = "-" + (spec || "1");
else
spec = spec || ".";
endif
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
elseif (ERR == typeof(number = this:parse_insert(who, spec)))
if (verb in {"next", "prev"})
player:tell("Argument must be a number.");
else
player:tell("You must specify an integer or `$' for the last line.");
endif
elseif ((number > (max = length(this.texts[who]) + 1)) || (number < 1))
player:tell("That would take you out of range (to line ", number, "?).");
else
this.inserting[who] = number;
if (typeof(text) == STR)
this:insert_line(who, text, 1);
else
if (verb != "next")
(number > 1) ? this:list_line(who, number - 1) | player:tell("____");
endif
if (verb != "prev")
(number < max) ? this:list_line(who, number) | player:tell("^^^^");
endif
endif
endif
.
#46:5
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
elseif (typeof(range = this:parse_range(who, {"_", "1"}, @args)) != LIST)
player:tell(range);
elseif (range[3])
player:tell("Junk at end of cmd:  ", range[3]);
else
player:tell_lines((text = this.texts[who])[from = range[1]..to = range[2]]);
player:tell("---Line", (to > from) ? "s" | "", " deleted.  Insertion point is before line ", from, ".");
this.texts[who] = {@text[1..from - 1], @text[to + 1..length(text)]};
if (!this.changes[who])
this.changes[who] = 1;
this.times[who] = time();
endif
this.inserting[who] = from;
endif
.
#46:6
if (callers() && (caller != this))
return E_PERM;
endif
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
elseif (typeof(subst = this:parse_subst(argstr && (argstr[1] + argstr), "c", "Empty search string?")) != LIST)
player:tell(tostr(subst));
elseif (typeof(start = subst[4] ? this:parse_insert(who, subst[4]) | this.inserting[who]) == ERR)
player:tell("Starting from where?", subst[4] ? ("  (can't parse " + subst[4]) + ")" | "");
else
search = subst[2];
case = !index(subst[3], "c", 1);
text = this.texts[who];
tlen = length(text);
while ((start <= tlen) && (!index(text[start], search, case)))
start = start + 1;
endwhile
if (start > tlen)
player:tell("`", search, "' not found.");
else
this.inserting[who] = start + 1;
this:list_line(who, start);
endif
endif
.
#46:7
if (callers() && (caller != this))
return E_PERM;
endif
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
elseif (typeof(subst = this:parse_subst(argstr)) != LIST)
player:tell(tostr(subst));
elseif (typeof(range = this:parse_range(who, {"_", "1"}, @$string_utils:explode(subst[4]))) != LIST)
player:tell(range);
elseif (range[3])
player:tell("Junk at end of cmd:  ", range[3]);
else
fromstr = subst[1];
tostr = subst[2];
global = index(subst[3], "g", 1);
case = !index(subst[3], "c", 1);
munged = {};
text = this.texts[who];
changed = {};
for line in [from = range[1]..to = range[2]]
t = t0 = text[line];
if (!fromstr)
t = tostr + t;
elseif (global)
t = strsub(t, fromstr, tostr, case);
elseif (i = index(t, fromstr, case))
t = (t[1..i - 1] + tostr) + t[i + length(fromstr)..length(t)];
endif
if (strcmp(t0, t))
changed = {@changed, line};
endif
munged = {@munged, t};
endfor
if (!changed)
player:tell("No changes in line", (from == to) ? tostr(" ", from) | tostr("s ", from, "-", to), ".");
else
this.texts[who] = {@text[1..from - 1], @munged, @text[to + 1..length(text)]};
if (!this.changes[who])
this.changes[who] = 1;
this.times[who] = time();
endif
for line in (changed)
this:list_line(who, line);
endfor
endif
endif
.
#46:8
verb = (is_move = verb[1] == "m") ? "move" | "copy";
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
endif
wargs = args;
t = to_pos = 0;
while (t = "to" in (wargs = wargs[t + 1..length(wargs)]))
to_pos = to_pos + t;
endwhile
range_args = args[1..to_pos - 1];
if ((!to_pos) || (ERR == typeof(dest = this:parse_insert(who, $string_utils:from_list(wargs, " ")))))
player:tell(verb, " to where? ");
elseif ((dest < 1) || (dest > ((last = length(this.texts[who])) + 1)))
player:tell("Destination (", dest, ") out of range.");
elseif (("from" in range_args) || ("to" in range_args))
player:tell("Don't use that kind of range specification with this command.");
elseif (typeof(range = this:parse_range(who, {"_", "^"}, @args[1..to_pos - 1])) != LIST)
player:tell(range);
elseif (range[3])
player:tell("Junk before `to':  ", range[3]);
elseif ((is_move && (dest >= range[1])) && (dest <= (range[2] + 1)))
player:tell("Destination lies inside range of lines to be moved.");
else
from = range[1];
to = range[2];
ins = this.inserting[who];
text = this.texts[who];
if (!is_move)
this.texts[who] = {@text[1..dest - 1], @text[from..to], @text[dest..last]};
if (ins >= dest)
this.inserting[who] = ((ins + to) - from) + 1;
endif
else
"oh shit... it's a move";
if (dest < from)
newtext = {@text[1..dest - 1], @text[from..to], @text[dest..from - 1], @text[to + 1..last]};
if ((ins >= dest) && (ins <= to))
ins = (ins > from) ? (ins - from) + dest | (((ins + to) - from) + 1);
endif
else
newtext = {@text[1..from - 1], @text[to + 1..dest - 1], @text[from..to], @text[dest..last]};
if ((ins > from) && (ins < dest))
ins = (ins <= to) ? ((ins + dest) - to) - 1 | (((ins - to) + from) - 1);
endif
endif
this.texts[who] = newtext;
this.inserting[who] = ins;
endif
if (!this.changes[who])
this.changes[who] = 1;
this.times[who] = time();
endif
player:tell("Lines ", is_move ? "moved." | "copied.");
endif
.
#46:9
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
elseif (typeof(range = this:parse_range(who, {"_-^", "_", "^"}, @args)) != LIST)
player:tell(range);
elseif (range[3])
player:tell("Junk at end of cmd:  ", range[3]);
elseif (!(result = this:join_lines(who, @range[1..2], length(verb) <= 4)))
player:tell((result == 0) ? "Need at least two lines to join." | result);
else
this:list_line(who, range[1]);
endif
.
#46:10
fill_column = 70;
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
elseif (this.mode[who] == "value")
player:tell("Sorry, there's no `fill' in value mode.");
elseif (typeof(range = this:parse_range(who, {"_", "1"}, @args)) != LIST)
player:tell(range);
elseif (range[3] && ((range[3][1] != "@") || ((fill_column = tonum(range[3][2..length(range[3])])) < 10)))
player:tell("Usage:  fill [<range>] [@ column]   (where column >= 10).");
else
join = this:join_lines(who, @range[1..2], 1);
newlines = this:fill_string((text = this.texts[who])[from = range[1]], fill_column);
if (fill = ((nlen = length(newlines)) > 1) || (newlines[1] != text[from]))
this.texts[who] = {@text[1..from - 1], @newlines, @text[from + 1..length(text)]};
if (((insert = this.inserting[who]) > from) && (nlen > 1))
this.inserting[who] = (insert + nlen) - 1;
endif
endif
if (fill || join)
for line in [from..(from + nlen) - 1]
this:list_line(who, line);
endfor
else
player:tell("No changes.");
endif
endif
.
#46:11
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
endif
if (typeof(e = this:set_readable(who, index("publish", verb) == 1)) == ERR)
player:tell(e);
elseif (e)
player:tell("Your text is now globally readable.");
else
player:tell("Your text is read protected.");
endif
.
#46:12
if (!(this:ok(who = player in this.active) && (typeof(this.texts[who]) == LIST)))
player:tell(this:nothing_loaded_msg());
else
player:tell("You are editing ", this:working_on(who), ".");
player:tell("Your insertion point is ", (this.inserting[who] > length(this.texts[who])) ? "after the last line: next line will be #" | "before line ", this.inserting[who], ".");
player:tell(this.changes[who] ? this:change_msg() | this:no_change_msg());
if (this.readable[who])
player:tell("Your text is globally readable.");
endif
endif
.
#46:13
if (!this.changes[who = player in this.active])
player:tell("No changes to throw away.  Editor cleared.");
else
player:tell("Throwing away session for ", this:working_on(who), ".");
endif
this:reset_session(who);
if (this.exit_on_abort)
this:done();
endif
.
#46:14
if (!(caller in {this, player}))
return E_PERM;
elseif (!valid(origin = this.original[who = player in this.active]))
player:tell("The place you came from (", origin, ") is not valid.");
else
try
origin:bless_for_entry(player);
player:moveto(origin);
except (E_VERBNF)
endtry
if (player.location == this)
player:tell("Hmmm... the place you came from doesn't want you back.");
else
if (msg = this:return_msg())
player.location:announce($string_utils:pronoun_sub(msg));
endif
return;
endif
endif
player:tell("You'll have to use `home' or `@move me'.");
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835739250, \"Erik\", #74, \"JHM\"}";
.
#46:15
"This catches subst and find commands that don't fit into the usual model, e.g., s/.../.../ without the space after the s, and find commands without the verb `find'.  Still behaves in annoying ways (e.g., loses if the search string contains multiple whitespace), but better than before.";
set_task_perms(caller_perms());
if ((c = callers()) && ((c[1][1] != this) || (length(c) > 1)))
return pass(@args);
endif
verb = args[1];
v = 1;
vmax = min(length(verb), 5);
while ((v <= vmax) && (verb[v] == "subst"[v]))
v = v + 1;
endwhile
argstr = $code_utils:argstr(verb, args[2]);
if (((v > 1) && (v <= length(verb))) && (((vl = verb[v]) < "A") || (vl > "Z")))
argstr = (verb[v..length(verb)] + (argstr && " ")) + argstr;
return this:subst();
elseif ("/" == verb[1])
argstr = (verb + (argstr && " ")) + argstr;
return this:find();
else
pass(@args);
endif
.
#46:16
return this:ok(who = args[1]) && this.inserting[who];
.
#46:17
return this:ok(who = args[1]) && ((((ins = tonum(args[2])) < 1) ? E_INVARG | ((ins <= (max = length(this.texts[who]) + 1)) || (ins = max))) && (this.inserting[who] = ins));
.
#46:18
return this:ok(who = args[1]) && this.changes[who];
.
#46:19
return this:ok(who = args[1]) && (((unchanged = !args[2]) || (this.times[who] = time())) && (this.changes[who] = !unchanged));
.
#46:20
return this:ok(who = args[1]) && this.original[who];
.
#46:21
return this:ok(who = args[1]) && (((valid(origin = args[2]) && (origin != this)) || ((origin == $nothing) || E_INVARG)) && (this.original[who] = origin));
.
#46:22
return (((who = args[1]) < 1) || (who > length(this.active))) ? E_RANGE | this.readable[who];
.
#46:23
return this:ok(who = args[1]) && (this.readable[who] = !(!args[2]));
.
#46:24
return (this:readable(who = args[1] || (player in this.active)) || this:ok(who)) && this.texts[who];
.
#46:25
texts = args[2];
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
elseif (typeof(texts) == STR)
texts = {texts};
elseif ((typeof(texts) != LIST) || (length(texts) && (typeof(texts[1]) != STR)))
return E_TYPE;
endif
this.texts[who] = texts;
this.inserting[who] = length(texts) + 1;
this.changes[who] = 0;
this.readable[who] = 0;
this.times[who] = time();
.
#46:26
"Dummy routine.  The child editor should provide something informative";
return this:ok(who = args[1]) && (("something [in " + this:name()) + "]");
.
#46:27
who = args[1];
if ((who < 1) || (who > length(this.active)))
return E_RANGE;
elseif ((length(c = callers()) < 2) ? player == this.active[who] | ((c[2][1] == this) || ($perm_utils:controls(c[2][3], this.active[who]) || (c[2][3] == $generic_editor.owner))))
return 1;
else
return E_PERM;
endif
.
#46:28
return ((who = args[1] in this.active) && (typeof(this.texts[who]) == LIST)) && who;
.
#46:29
if (this:ok(who = args[1]))
f = 1 + ((line = args[2]) in {(ins = this.inserting[who]) - 1, ins});
player:tell($string_utils:right(line, 3, " _^"[f]), ":_^"[f], " ", this.texts[who][line]);
endif
.
#46:30
":insert_line([who,] line or list of lines [,quiet])";
"  inserts the given text at the insertion point.";
"  returns E_NONE if the session has no text loaded yet.";
if (typeof(who = args[1]) != NUM)
args = {player in this.active, @args};
endif
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
elseif (typeof(text = this.texts[who]) != LIST)
return E_NONE;
else
if (typeof(lines = args[2]) != LIST)
lines = {lines};
endif
p = this.active[who];
quiet = (length(args) >= 3) ? args[3] | p:edit_option("quiet_insert");
insert = this.inserting[who];
this.texts[who] = {@text[1..insert - 1], @lines, @text[insert..length(text)]};
this.inserting[who] = insert + length(lines);
if (lines)
if (!this.changes[who])
this.changes[who] = 1;
this.times[who] = time();
endif
if (!quiet)
if (length(lines) != 1)
p:tell("Lines ", insert, "-", (insert + length(lines)) - 1, " added.");
else
p:tell("Line ", insert, " added.");
endif
endif
else
p:tell("No lines added.");
endif
endif
.
#46:31
":append_line([who,] string)";
"  appends the given string to the line before the insertion point.";
"  returns E_NONE if the session has no text loaded yet.";
if (typeof(who = args[1]) != NUM)
args = {player in this.active, @args};
endif
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
elseif ((append = this.inserting[who] - 1) < 1)
return this:insert_line(who, {args[2]});
elseif (typeof(text = this.texts[who]) != LIST)
return E_NONE;
else
this.texts[who][append] = text[append] + args[2];
if (!this.changes[who])
this.changes[who] = 1;
this.times[who] = time();
endif
p = this.active[who];
if (!p:edit_option("quiet_insert"))
p:tell("Appended to line ", append, ".");
endif
endif
.
#46:32
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
elseif ((from = args[2]) >= (to = args[3]))
return 0;
else
nline = "";
for line in ((text = this.texts[who])[from..to])
if (!(english = args[4]))
nline = nline + line;
else
len = length(line) + 1;
while ((len = len - 1) && (line[len] == " "))
endwhile
if (len > 0)
nline = (nline + line) + (index(".:", line[len]) ? "  " | " ");
endif
endif
endfor
this.texts[who] = {@text[1..from - 1], nline, @text[to + 1..length(text)]};
if ((insert = this.inserting[who]) > from)
this.inserting[who] = (insert <= to) ? from + 1 | ((insert - to) + from);
endif
if (!this.changes[who])
this.changes[who] = 1;
this.times[who] = time();
endif
return to - from;
endif
.
#46:33
"parse_number(who,string,before)   interprets string as a line number.  In the event that string is `.', `before' tells us which line to use.  Return 0 if string is bogus.";
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
endif
last = length(this.texts[who]);
ins = this.inserting[who] - 1;
string = args[2];
after = !args[3];
if (!string)
return 0;
elseif ("." == string)
return ins + after;
elseif (!(i = index("_^$", string[slen = length(string)])))
return tonum(string);
else
start = {ins + 1, ins, last + 1}[i];
n = 1;
if ((slen > 1) && (!(n = tonum(string[1..slen - 1]))))
return 0;
elseif (i % 2)
return start - n;
else
return start + n;
endif
endif
.
#46:34
"parse_range(who,default,@args) => {from to rest}";
numargs = length(args);
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
elseif (!(last = length(this.texts[who])))
return this:no_text_msg();
endif
default = args[2];
r = 0;
while (default && (LIST != typeof(r = this:parse_range(who, {}, default[1]))))
default = listdelete(default, 1);
endwhile
if (typeof(r) == LIST)
from = r[1];
to = r[2];
else
from = to = 0;
endif
saw_from_to = 0;
not_done = 1;
a = 2;
while (((a = a + 1) <= numargs) && not_done)
if (args[a] == "from")
if ((a == numargs) || (!(from = this:parse_number(who, args[a = a + 1], 0))))
return "from ?";
endif
saw_from_to = 1;
elseif (args[a] == "to")
if ((a == numargs) || (!(to = this:parse_number(who, args[a = a + 1], 1))))
return "to ?";
endif
saw_from_to = 1;
elseif (saw_from_to)
a = a - 1;
not_done = 0;
elseif (i = index(args[a], "-"))
from = this:parse_number(who, args[a][1..i - 1], 0);
to = this:parse_number(who, args[a][i + 1..length(args[a])], 1);
not_done = 0;
elseif (f = this:parse_number(who, args[a], 0))
from = f;
if ((a == numargs) || (!(to = this:parse_number(who, args[a + 1], 1))))
to = from;
else
a = a + 1;
endif
not_done = 0;
else
a = a - 1;
not_done = 0;
endif
endwhile
if (from < 1)
return tostr("from ", from, "?  (out of range)");
elseif (to > last)
return tostr("to ", to, "?  (out of range)");
elseif (from > to)
return tostr("from ", from, " to ", to, "?  (backwards range)");
else
return {from, to, $string_utils:from_list(args[a..numargs], " ")};
endif
.
#46:35
"parse_ins(who,string)  interprets string as an insertion point, i.e., a position between lines and returns the number of the following line or 0 if bogus.";
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
endif
who = args[1];
last = length(this.texts[who]) + 1;
ins = this.inserting[who];
string = args[2];
if (i = index("-+", string[1]))
rest = string[2..length(string)];
return ((n = tonum(rest)) || (rest == "0")) ? {ins - n, ins + n}[i] | E_INVARG;
else
if (!(j = index(string, "^") || index(string, "_")))
offset = 0;
else
offset = (j == 1) || tonum(string[1..j - 1]);
if (!offset)
return E_INVARG;
elseif (string[j] == "^")
offset = -offset;
endif
endif
rest = string[j + 1..length(string)];
if (i = rest in {".", "$"})
return offset + {ins, last}[i];
elseif (!(n = tonum(rest)))
return E_INVARG;
else
return (offset + (j && (string[j] == "^"))) + n;
endif
endif
.
#46:36
recognized_flags = (length(args) >= 2) ? args[2] | "gc";
null_subst_msg = (length(args) >= 3) ? args[3] | "Null substitution?";
cmd = args[1];
if (!cmd)
return "/xxx/yyy[/[g][c]] [<range>] expected..";
endif
bchar = cmd[1];
cmd = cmd[2..length(cmd)];
fromstr = cmd[1..(b2 = index(cmd + bchar, bchar, 1)) - 1];
cmd = cmd[b2 + 1..length(cmd)];
tostr = cmd[1..(b2 = index(cmd + bchar, bchar, 1)) - 1];
cmd = cmd[b2 + 1..length(cmd)];
cmdlen = length(cmd);
b2 = 0;
while (((b2 = b2 + 1) <= cmdlen) && index(recognized_flags, cmd[b2]))
endwhile
return ((fromstr == "") && (tostr == "")) ? null_subst_msg | {fromstr, tostr, cmd[1..b2 - 1], cmd[b2..cmdlen]};
.
#46:37
":invoke(...)";
"to find out what arguments this verb expects,";
"see this editor's parse_invoke verb.";
new = args[1];
rest = args[2..length(args)];
if ((!(caller in {this, player})) && (!$perm_utils:controls(caller_perms(), player)))
"...non-editor/non-player verb trying to send someone to the editor...";
return E_PERM;
endif
if ((who = this:loaded(player)) && this:changed(who))
if (!new)
if (this:suck_in(player))
player:tell("You are working on ", this:working_on(who));
endif
return;
elseif (player.location == this)
player:tell("You are still working on ", this:working_on(who));
if (msg = this:previous_session_msg())
player:tell(msg);
endif
return;
endif
"... we're not in the editor and we're about to start something new,";
"... but there's still this pending session...";
working = this:working_on(who);
if (typeof(working) == STR)
player:tell("You were working on ", this:working_on(who));
if (!$command_utils:yes_or_no("Do you wish to delete that session?"))
if (this:suck_in(player))
player:tell("Continuing with ", this:working_on(player in this.active));
if (msg = this:previous_session_msg())
player:tell(msg);
endif
endif
return;
endif
"... note session number may have changed => don't trust `who'";
this:kill_session(player in this.active);
endif
endif
spec = this:parse_invoke(new, @rest);
if (typeof(spec) == LIST)
if ((player:edit_option("local") && $object_utils:has_verb(this, "local_editing_info")) && (info = this:local_editing_info(@spec)))
this:invoke_local_editor(@info);
elseif (this:suck_in(player))
this:init_session(player in this.active, @spec);
endif
endif
.
#46:38
"The correct way to move someone into the editor.";
if (((loc = (who_obj = args[1]).location) != this) && (caller == this))
this.invoke_task = task_id();
"make sure accept gets called... argh!";
who_obj.wizard && this:accept(who_obj);
who_obj:moveto(this);
if (who_obj.location == this)
fork (0)
"...forked, just in case loc:announce is broken...";
if (valid(loc) && (msg = this:depart_msg()))
loc:announce($string_utils:pronoun_sub(msg));
endif
endfork
else
who_obj:tell("For some reason, I can't move you.   (?)");
this:exitfunc(who_obj);
endif
this.invoke_task = 0;
endif
return who_obj.location == this;
.
#46:39
"WIZARDLY";
who_obj = args[1];
from = args[2];
if ($object_utils:isa(from, $generic_editor))
"... never put an editor in .original, ...";
if (w = who_obj in from.active)
from = from.original[w];
else
from = #-1;
endif
endif
if (caller != this)
return E_PERM;
elseif (who = who_obj in this.active)
"... edit in progress here...";
if (valid(from))
this.original[who] = from;
endif
return -1;
else
for p in ({{"active", who_obj}, {"original", valid(from) ? from | $nothing}, {"times", time()}, @this.stateprops})
this.(p[1]) = {@this.(p[1]), p[2]};
endfor
return length(this.active);
endif
.
#46:40
"WIZARDLY";
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
else
for p in ({@this.stateprops, {"original"}, {"active"}, {"times"}})
this.(p[1]) = listdelete(this.(p[1]), who);
endfor
return who;
endif
.
#46:41
"WIZARDLY";
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
else
for p in (this.stateprops)
this.(p[1])[who] = p[2];
endfor
this.times[who] = time();
return who;
endif
.
#46:42
"WIZARDLY";
if ((caller != this) && (!caller_perms().wizard))
return E_PERM;
else
for victim in (this.contents)
victim:tell("Sorry, ", this:name(), " is going down.  Your editing session is hosed.");
victim:moveto(((who = victim in this.active) && valid(origin = this.original[who])) ? origin | (valid(victim.home) ? victim.home | $player_start));
endfor
for p in ({@this.stateprops, {"original"}, {"active"}, {"times"}})
this.(p[1]) = {};
endfor
return 1;
endif
.
#46:43
who_obj = args[1];
if (who_obj.wizard && (!(who_obj in this.active)))
this:accept(who_obj);
endif
pass(who_obj);
if (this.invoke_task == task_id())
"Means we're about to load something, so be quiet.";
this.invoke_task = 0;
elseif (who = this:loaded(who_obj))
who_obj:tell("You are working on ", this:working_on(who), ".");
elseif (msg = this:nothing_loaded_msg())
who_obj:tell(msg);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835903226, \"Erik\", #74, \"JHM\"}";
.
#46:44
if (!(who = (who_obj = args[1]) in this.active))
elseif (this.changes[who])
if (msg = this:no_littering_msg())
who_obj:tell_lines(msg);
endif
else
this:kill_session(who);
endif
pass(@args);
.
#46:45
"@flush <editor>";
"@flush <editor> at <month> <day>";
"@flush <editor> at <weekday>";
"The first form removes all sessions from the editor; the other two forms remove everything older than the given date.";
if (!$perm_utils:controls(player, this))
player:tell("Only the owner of the editor can do a ", verb, ".");
return;
endif
if (!prepstr)
player:tell("Trashing all sessions.");
this:kill_all_sessions();
elseif (prepstr != "at")
player:tell("Usage:  ", verb, " ", dobjstr, " [at [mon day|weekday]]");
else
p = prepstr in args;
if (t = $time_utils:from_day(iobjstr, -1))
elseif (t = $time_utils:from_month(args[p + 1], -1))
if (length(args) > (p + 1))
if (!(n = tonum(args[p + 2])))
player:tell(args[p + 1], " WHAT?");
return;
endif
t = t + ((n - 1) * 86400);
endif
else
player:tell("couldn't parse date");
return;
endif
for i in [-length(this.active)..-1]
if (this.times[-i] < t)
player:tell(this.active[-i]:name(), "(", this.active[-i], ") ", ctime(this.times[-i]));
this:kill_session(-i);
endif
endfor
endif
.
#46:46
if (!$perm_utils:controls(player, this))
player:tell(E_PERM);
return;
endif
if (i = index(dobjstr, "="))
default = dobjstr[i + 1..length(dobjstr)];
prop = dobjstr[1..i - 1];
if (argstr[1 + index(argstr, "=")] == "\"")
elseif (default[1] == "#")
default = toobj(default);
elseif (index("0123456789", default[1]))
default = tonum(default);
elseif (default == "{}")
default = {};
endif
else
default = 0;
prop = dobjstr;
endif
if (typeof(result = this:set_stateprops(prop, default)) == ERR)
player:tell((result != E_NACC) ? result | (prop + " is already a property on an ancestral editor."));
else
player:tell("Property added.");
endif
.
#46:47
if (!$perm_utils:controls(player, this))
player:tell(E_PERM);
elseif (typeof(result = this:set_stateprops(dobjstr)) == ERR)
player:tell((result != E_NACC) ? result | (dobjstr + " is already a property on an ancestral editor."));
else
player:tell("Property removed.");
endif
.
#46:48
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
pass(@args);
this:kill_all_sessions();
endif
.
#46:49
if ($__core_init_phase)
pass(@args);
this:kill_all_sessions();
endif
.
#46:50
remove = length(args) < 2;
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
elseif (!(length(args) in {1, 2}))
return E_ARGS;
elseif (typeof(prop = args[1]) != STR)
return E_TYPE;
elseif (i = $list_utils:iassoc(prop, this.stateprops))
if (!remove)
this.stateprops[i] = {prop, args[2]};
elseif ($object_utils:has_property(parent(this), prop))
return E_NACC;
else
this.stateprops = listdelete(this.stateprops, i);
endif
elseif (remove)
elseif (prop in properties(this))
this.stateprops = {{prop, args[2]}, @this.stateprops};
else
return $object_utils:has_property(this, prop) ? E_NACC | E_PROPNF;
endif
this:kill_all_sessions();
return 0;
.
#46:51
is_look_self = 1;
for c in (callers())
if (is_look_self && (c[2] in {"enterfunc", "confunc"}))
return {"", "Do a 'look' to get the list of commands, or 'help' for assistance.", "", @this.description};
elseif (c[2] != "look_self")
is_look_self = 0;
endif
endfor
d = {"Commands:", ""};
col = {{}, {}};
for c in [1..2]
for cmd in (this.commands2[c])
cmd = this:commands_info(cmd);
col[c] = {cmdargs = $string_utils:left(cmd[1] + " ", 12) + cmd[2], @col[c]};
endfor
endfor
i1 = length(col[1]);
i2 = length(col[2]);
right = 0;
while (i1 || i2)
if (!((i1 && (length(col[1][i1]) > 35)) || (i2 && (length(col[2][i2]) > 35))))
d = {@d, $string_utils:left(i1 ? col[1][i1] | "", 40) + (i2 ? col[2][i2] | "")};
i1 && (i1 = i1 - 1);
i2 && (i2 = i2 - 1);
right = 0;
elseif (right && i2)
d = {@d, (length(col[2][i2]) > 35) ? $string_utils:right(col[2][i2], 75) | ($string_utils:space(40) + col[2][i2])};
i2 = i2 - 1;
right = 0;
elseif (i1)
d = {@d, col[1][i1]};
i1 = i1 - 1;
right = 1;
else
right = 1;
endif
endwhile
return {@d, "", "----  Do `help <cmdname>' for help with a given command.  ----", "", "  <ins> ::= $ (the end) | [^]n (above line n) | _n (below line n) | . (current)", "<range> ::= <lin> | <lin>-<lin> | from <lin> | to <lin> | from <lin> to <lin>", "  <lin> ::= n | [n]$ (n from the end) | [n]_ (n before .) | [n]^ (n after .)", "`help insert' and `help ranges' describe these in detail.", @this.description};
.
#46:52
cmd = args[1];
if (pc = $list_utils:assoc(cmd, this.commands))
return pc;
elseif (this == $generic_editor)
return {cmd, "<<<<<======= Need to add this to .commands"};
else
return parent(this):commands_info(cmd);
endif
.
#46:53
who = {@args, player}[2];
objstr = args[1];
origin = this;
while ((where = player in origin.active) && ($recycler:valid(origin = origin.original[where]) && (origin != this)))
if (!$object_utils:isa(origin, $generic_editor))
return origin:match_object(args[1], who);
endif
endwhile
return who:my_match_object(objstr, #-1);
.
#46:54
who = args[1];
where = {0, @this.original}[1 + (who in this.active)];
msg = this.who_location_msg;
if ($recycler:valid(where))
msg = listappend(msg, where:who_location_msg(who), 3);
return $string_utils:pronoun_sub(msg, who, this, where);
else
msg = listappend(msg, (where == 0) ? "inactive" | "nowhere", 3);
return $string_utils:pronoun_sub(msg, who, this, this);
endif
.
#46:55
return this.(verb);
.
#46:56
return;
.
#46:57
"fill(string,width[,prefix])";
"tries to cut <string> into substrings of length < <width> along word boundaries.  Prefix, if supplied, will be prefixed to the 2nd..last substrings.";
if (length(args) < 2)
width = 2 + abs(player.linelen);
prefix = "";
else
width = args[2] + 1;
prefix = {@args, ""}[3];
endif
if (width < (3 + length(prefix)))
return E_INVARG;
endif
string = ("$" + args[1]) + " $";
len = length(string);
if (len <= width)
last = len - 1;
next = len;
else
last = rindex(string[1..width], " ");
if (last < ((width + 1) / 2))
last = width + index(string[width + 1..len], " ");
endif
next = last;
while (string[next = next + 1] == " ")
endwhile
endif
while (string[last = last - 1] == " ")
endwhile
ret = {string[2..last]};
width = width - length(prefix);
minlast = (width + 1) / 2;
while (next < len)
string = "$" + string[next..len];
len = (len - next) + 2;
if (len <= width)
last = len - 1;
next = len;
else
last = rindex(string[1..width], " ");
if (last < minlast)
last = width + index(string[width + 1..len], " ");
endif
next = last;
while (string[next = next + 1] == " ")
endwhile
endif
while (string[last = last - 1] == " ")
endwhile
if (last > 1)
ret = {@ret, prefix + string[2..last]};
endif
endwhile
return ret;
.
#46:58
"This catches subst and find commands that don't fit into the usual model, e.g., s/.../.../ without the space after the s, and find commands without the verb `find'.  Still behaves in annoying ways (e.g., loses if the search string contains multiple whitespace), but better than before.";
if ((caller != this) && (caller_perms() != player))
return E_PERM;
endif
verb = args[1];
args = args[2];
v = 1;
vmax = min(length(verb), 5);
while ((v <= vmax) && (verb[v] == "subst"[v]))
v = v + 1;
endwhile
argstr = $code_utils:argstr(verb, args);
if ((v > 1) && ((v <= length(verb)) && (((vl = verb[v]) < "A") || (vl > "Z"))))
argstr = (verb[v..length(verb)] + (argstr && " ")) + argstr;
this:subst();
return 1;
elseif ("/" == verb[1])
argstr = (verb + (argstr && " ")) + argstr;
this:find();
return 1;
else
return 0;
endif
.
#46:59
return $failed_match;
.
#46:60
":get_room([player])  => correct room to match in on invocation.";
who = {@args, player}[1];
if (who.location != this)
return who.location;
else
origin = this;
while ((where = player in origin.active) && (valid(origin = origin.original[where]) && (origin != this)))
if (!$object_utils:isa(origin, $generic_editor))
return origin;
endif
endwhile
return this;
endif
.
#46:61
":invoke_local_editor(type, name, text, upload)";
"Spits out the magic text that invokes the local editor in the player's client.";
"TYPE is the type of data--program, text, mail, value, etc.";
"NAME is a good human-readable name for the local editor to use for this particular piece of text.";
"TEXT is a string or list of strings, the initial body of the text being edited.";
"UPLOAD, a string, is a MOO command that the local editor can use to save the text when the user is done editing.  The local editor is going to send that command on a line by itself, followed by the new text lines, followed by a line containing only `.'.  The UPLOAD command should therefore call $command_utils:read_lines() to get the new text as a list of strings.";
if (caller != this)
return;
endif
type = args[1];
name = args[2];
text = args[3];
upload = args[4];
if (typeof(text) == STR)
text = {text};
endif
package = $mcp:match_package("dns-org-mud-moo-simpleedit");
session = $mcp:session_for(player);
if (session:handles_package(package))
mcp_info = args[5];
package:send_content(session, @mcp_info);
elseif (use_mcp = player:client_option("mcp_edit"))
"mcp version 1.0 editing";
res = player:client_notify("edit", {{"type", type}, {"name", name}, {"upload", upload}}, text);
return res;
else
notify(player, tostr("#$# edit name: ", name, " upload: ", upload));
":dump_lines() takes care of the final `.' ...";
for line in ($command_utils:dump_lines(text))
notify(player, line);
endfor
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895556373, \"Ken\", #75, \"JHM\"}";
.
#46:62
":invoke_parsed(...)";
"to find out what arguments this verb expects,";
"see this editor's parse_invoke verb.";
if ((!(caller in {this, player})) && (!$perm_utils:controls(caller_perms(), player)))
"...non-editor/non-player verb trying to send someone to the editor...";
return E_PERM;
endif
if ((who = this:loaded(player)) && this:changed(who))
if (player.location == this)
player:tell("You are still working on ", this:working_on(who));
if (msg = this:previous_session_msg())
player:tell(msg);
endif
return;
endif
"... we're not in the editor and we're about to start something new,";
"... but there's still this pending session...";
working = this:working_on(who);
if (typeof(working) == STR)
player:tell("You were working on ", this:working_on(who));
if (!$command_utils:yes_or_no("Do you wish to delete that session?"))
if (this:suck_in(player))
player:tell("Continuing with ", this:working_on(player in this.active));
if (msg = this:previous_session_msg())
player:tell(msg);
endif
endif
return;
endif
"... note session number may have changed => don't trust `who'";
this:kill_session(player in this.active);
endif
endif
spec = args;
if (typeof(spec) == LIST)
"mcp version";
"if (!(player:client_option(\"edit\") && $object_utils:has_verb(this, \"local_editing_info\") && (info = this:local_editing_info(@spec)) && (local = this:invoke_local_editor(@info))))";
"if (this:suck_in(player))";
"this:init_session(player in this.active, @spec);";
"endif";
"endif";
if ((player:edit_option("local") && $object_utils:has_verb(this, "local_editing_info")) && (info = this:local_editing_info(@spec)))
this:invoke_local_editor(@info);
elseif (this:suck_in(player))
this:init_session(player in this.active, @spec);
endif
endif
"spec = this:parse_invoke(@args);";
"if (typeof(spec) == LIST && this:suck_in(player))";
"this:init_session(player in this.active, @spec);";
"endif";
.
#46:63
"room used by who for party sorting.  sigh.";
who = args[1];
return {#-1, @this.original}[1 + (who in this.active)];
"Copied from generic room (#3):who_location by Erik (#74) Fri Sep 23 02:08:27 1994 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835727839, \"Erik\", #74, \"JHM\"}";
.
#46:64
return {@pass(@args), $command_utils, $string_utils, $code_utils, $perm_utils, $generic_editor, $generic_editor.owner, $object_utils, $player_start, $time_utils, this.help, $list_utils, $recycler};
.
#46:65
return (is_player(who_obj = args[1]) && (who_obj.wizard || pass(@args))) && this:new_session(who_obj, who_obj.location);
.
#46:66
player:tell("Topic?  Edit what you have to edit and get out.");
.
#47:0
object = args[1];
prop = args[2];
if (prop in $code_utils.builtin_props)
return valid(object);
else
return !(!property_info(object, prop));
endif
.
#47:1
what = args[1];
if (what.owner != caller_perms())
set_task_perms(caller_perms());
endif
props = properties(what) || {};
while (valid(what = parent(what)))
props = {@properties(what) || {}, @props};
endwhile
return props;
.
#47:2
":has_verb(OBJ object, STR verbname)";
"Find out if an object has a verb matching the given verbname.";
"Returns {location} if so, 0 if not, where location is the object or the ancestor on which the verb is actually defined.";
{object, verbname} = args;
while (E_VERBNF == (vi = `verb_info(object, verbname) ! ANY'))
object = parent(object);
endwhile
return vi ? {object} | 0;
.
#47:3
"Usage:  has_callable_verb(object, verb)";
"See if an object has a verb that can be called by another verb (i.e., that has its x permission bit set).";
"Return {location}, where location is the object that defines the verb, or 0 if the object doesn't have the verb.";
{object, verbname} = args;
while (valid(object))
if (`index(verb_info(object, verbname)[2], "x") ! E_VERBNF => 0' && verb_code(object, verbname))
return {object};
endif
object = parent(object);
endwhile
return 0;
.
#47:4
what = args[1];
if (what.owner != caller_perms())
set_task_perms(caller_perms());
endif
verbs = {};
while (valid(what))
verbs = {@verbs(what) || {}, @verbs};
what = parent(what);
endwhile
return verbs;
.
#47:5
":match_verb(OBJ object, STR verb)";
"Find out if an object has a given verb, and some information about it.";
"Returns {OBJ location, STR verb} if matched, 0 if not.";
"Location is the object on which it is actually defined, verb is a name";
"for the verb which can subsequently be used in verb_info (i.e., no";
"asterisks).";
{object, verb} = args;
verbname = strsub(verb, "*", "");
while (E_VERBNF == (info = `verb_info(object, verbname) ! ANY'))
object = parent(object);
endwhile
return info ? {object, verbname} | 0;
.
#47:6
":isa(x,y) == valid(x) && (y==x || y in :ancestors(x))";
what = args[1];
targ = args[2];
while (valid(what))
if (what == targ)
return 1;
endif
what = parent(what);
endwhile
return 0;
.
#47:7
"Usage:  ancestors(object[, object...])";
"Return a list of all ancestors of the object(s) in args, with no duplicates.";
"If called with a single object, the result will be in order ascending up the inheritance hierarchy.  If called with multiple objects, it probably won't.";
ret = {};
for o in (args)
what = o;
while (valid(what = parent(what)))
ret = setadd(ret, what);
endwhile
endfor
return ret;
.
#47:8
what = args[1];
kids = children(what);
result = {};
for x in (kids)
result = {@result, @this:descendants(x)};
endfor
return {@kids, @result};
.
#47:9
set_task_perms(caller_perms());
what = args[1];
kids = children(what);
result = {};
for x in (kids)
result = {@result, @this:descendants_suspended(x)};
endfor
$command_utils:suspend_if_needed(0);
return {@kids, @result};
.
#47:10
r = {what = args[1]};
for k in (children(what))
r = {@r, @this:(verb)(k)};
endfor
return r;
.
#47:11
":branches(object) => list of all descendants of object which have children.";
if (kids = children(object = args[1]))
s = {object};
for k in (kids)
s = {@s, @this:branches(k)};
endfor
return s;
else
return {};
endif
.
#47:12
":branches_suspended(object) => descendants of object having children.";
"this version calls suspend(0) as needed";
set_task_perms(caller_perms());
if (kids = children(object = args[1]))
s = {object};
for k in (kids)
$command_utils:suspend_if_needed(0);
s = {@s, @this:branches_suspended(k)};
endfor
return s;
else
return {};
endif
.
#47:13
":leaves(object) => list of all childless descendents of object";
s = {};
for k in (children(args[1]))
s = {@s, @this:leaves(k)};
endfor
return s || {args[1]};
.
#47:14
":leaves_suspended(object) => list of all childless descendents of object";
"this versions calls suspend(0) as needed";
set_task_perms(caller_perms());
s = {};
for k in (children(args[1]))
$command_utils:suspend_if_needed(0);
s = {@s, @this:leaves_suspended(k)};
endfor
return s || {args[1]};
.
#47:15
"$object_utils:contains(obj1, obj2) -- does obj1 contain obj2?";
"";
"Return true iff obj2 is under obj1 in the containment hierarchy; that is, if obj1 is obj2's location, or its location's location, or ...";
loc = args[1];
what = args[2];
while (valid(what))
what = what.location;
if (what == loc)
return 1;
endif
endwhile
return 0;
.
#47:16
"all_contents(object)";
"Return a list of all objects contained (at some level) by object.";
res = {};
for y in (args[1].contents)
res = {@res, y, @$object_utils:all_contents(y)};
endfor
return res;
.
#47:17
"findable_properties(object)";
"Return a list of properties on those members of object's ancestor list that are readable or are owned by the caller (or all properties if the caller is a wizard).";
what = args[1];
props = {};
who = caller_perms();
while (what != $nothing)
if ((what.r || (who == what.owner)) || who.wizard)
props = {@properties(what), @props};
endif
what = parent(what);
endwhile
return props;
.
#47:18
"owned_properties(what[, who])";
"Return a list of all properties on WHAT owned by WHO.";
"Only wizardly verbs can specify WHO; mortal verbs can only search for properties owned by their own owners.  For more information, talk to Gary_Severn.";
what = anc = args[1];
who = ((c = caller_perms()).wizard && (length(args) > 1)) ? args[2] | c;
props = {};
while (anc != $nothing)
for k in (properties(anc))
if (property_info(what, k)[1] == who)
props = listappend(props, k);
endif
endfor
anc = parent(anc);
endwhile
return props;
.
#47:19
":property_conflicts(object,newparent)";
"Looks for propertyname conflicts that would keep chparent(object,newparent)";
"  from working.";
"Returns a list of elements of the form {<propname>, @<objectlist>}";
"where <objectlist> is list of descendents of object defining <propname>.";
if (!valid(object = args[1]))
return E_INVARG;
elseif (!valid(newparent = args[2]))
return (newparent == #-1) ? {} | E_INVARG;
elseif (!($perm_utils:controls(caller_perms(), object) && (newparent.f || $perm_utils:controls(caller_perms(), newparent))))
"... if you couldn't chparent anyway, you don't need to know...";
return E_PERM;
endif
"... properties existing on newparent";
"... cannot be present on object or any descendent...";
props = conflicts = {};
for o in ({object, @$object_utils:descendents_suspended(object)})
for p in (properties(o))
if (property_info(newparent, p))
if (i = p in props)
conflicts[i] = {@conflicts[i], o};
else
props = {@props, p};
conflicts = {@conflicts, {p, o}};
endif
endif
$command_utils:suspend_if_needed(0);
endfor
$command_utils:suspend_if_needed(0);
endfor
return conflicts;
.
#47:20
":descendants_with_property_suspended(object,property)";
" => list of descendants of object on which property is defined.";
"calls suspend(0) as needed";
object = args[1];
prop = args[2];
if ((caller == this) || (object.w || $perm_utils:controls(caller_perms(), object)))
$command_utils:suspend_if_needed(0);
if (property_info(object, prop))
return {object};
endif
r = {};
for c in (children(object))
r = {@r, @this:descendants_with_property_suspended(c, prop)};
endfor
return r;
else
return E_PERM;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835903290, \"Erik\", #74, \"JHM\"}";
.
#47:21
"Usage:  locations(object)";
"Return a listing of the location hierarchy above object.";
ret = {};
what = args[1];
while (valid(what = what.location))
ret = {@ret, what};
endwhile
return ret;
.
#47:22
what = args[1];
kids = children(what);
result = {};
fkids = {};
for x in (kids)
result = {@result, @this:fertile_descendants(x)};
if (x.f)
fkids = {@fkids, x};
endif
endfor
return {@fkids, @result};
.
#47:23
"Copied from object utilities (#3669):all_properties by Haakon (#2) Mon Dec 28 14:24:36 1992 PST";
what = args[1];
if (what.owner != caller_perms())
set_task_perms(caller_perms());
endif
props = properties(what) || {};
while (valid(what = parent(what)))
props = {@properties(what) || {}, @props};
$command_utils:suspend_if_needed(0);
endwhile
return props;
.
#47:24
":match_verbs(OBJ object, STR regexp)";
"Find out if an object has any verbs matching the given regexp.";
"Returns {{location, verbs}, {location, verbs}, ...} if so, 0 if not, where each location is the object or the ancestor on which the verb is actually defined and each verbs is a list of verbs on that location that match regexp.";
object = args[1];
regexp = args[2];
if (regexp[1] != "^")
"anchor it...";
regexp = "^" + regexp;
endif
if (regexp[length(regexp)] != "$")
regexp = regexp + "$";
endif
ret = {};
while (valid(object))
vbs = {};
for vname in (verbs(object))
matched = 0;
for v in ($string_utils:explode(vname))
if ((!matched) && match(v, regexp))
vbs = {@vbs, vname};
matched = 1;
endif
endfor
endfor
if (vbs)
ret = {@ret, {object, vbs}};
endif
object = parent(object);
endwhile
return ret || 0;
"Copied from object utilities (#52):has_verb by Doug (#107) Wed Dec 28 18:15:25 1994 EST";
.
#47:25
":connected(object) => true if object is a connected player.";
"equivalent to (object in connected_players()) for valid players, perhaps with less server overhead.";
"use object:is_listening() if you want to allow for puppets and other non-player objects that still 'care' about what's said.";
return 1 + connected_seconds(@args);
.
#48:0
this.input_string = args[1];
this.input_length = length(args[1]);
this.input_index = 1;
.
#48:1
string = this.input_string;
len = this.input_length;
i = this.input_index;
while ((i <= len) && (string[i] == " "))
i = i + 1;
endwhile
if (i > len)
return "";
elseif ((ch = string[i]) in {"(", ")", "!", "?"})
this.input_index = i + 1;
return ch;
elseif (ch in {"&", "|"})
this.input_index = i = i + 1;
if ((i <= len) && (string[i] == ch))
this.input_index = i + 1;
endif
return ch + ch;
else
start = i;
while ((i <= len) && (!((ch = string[i]) in {"(", ")", "!", "?", "&", "|"})))
i = i + 1;
endwhile
this.input_index = i;
i = i - 1;
while (string[i] == " ")
i = i - 1;
endwhile
return this:canonicalize_spaces(string[start..i]);
endif
.
#48:2
name = args[1];
while (index(name, "  "))
name = strsub(name, "  ", " ");
endwhile
return name;
.
#48:3
this:init_scanner(args[1]);
this.player = args[2];
return this:parse_E();
"";
"Grammar for key expressions:";
"";
"    E ::= A       ";
"       |  E || A  ";
"       |  E && A  ";
"    A ::= ( E )   ";
"       |  ! A     ";
"       |  object  ";
"       |  ? object  ";
.
#48:4
exp = this:parse_A();
if (typeof(exp) != STR)
while ((token = this:scan_token()) in {"&&", "||"})
rhs = this:parse_A();
if (typeof(rhs) == STR)
return STR;
endif
exp = {token, exp, rhs};
endwhile
endif
return exp;
.
#48:5
token = this:scan_token();
if (token == "(")
exp = this:parse_E();
if ((typeof(exp) != STR) && (this:scan_token() != ")"))
return "Missing ')'";
else
return exp;
endif
elseif (token == "!")
exp = this:parse_A();
if (typeof(exp) == STR)
return exp;
else
return {"!", exp};
endif
elseif (token == "?")
next = this:scan_token();
if (next in {"(", ")", "!", "&&", "||", "?"})
return ("Missing object-name before '" + token) + "'";
elseif (next == "")
return "Missing object-name at end of key expression";
else
what = this:match_object(next);
if (typeof(what) == OBJ)
return {"?", this:match_object(next)};
else
return what;
endif
endif
elseif (token in {"&&", "||"})
return ("Missing expression before '" + token) + "'";
elseif (token == "")
return "Missing expression at end of key expression";
else
return this:match_object(token);
endif
.
#48:6
key = args[1];
who = args[2];
type = typeof(key);
if (!(type in {LIST, OBJ}))
return 1;
elseif (typeof(key) == OBJ)
return (who == key) || $object_utils:contains(who, key);
endif
op = key[1];
if (op == "!")
return !this:eval_key(key[2], who);
elseif (op == "?")
return key[2]:is_unlocked_for(who);
elseif (op == "&&")
return this:eval_key(key[2], who) && this:eval_key(key[3], who);
elseif (op == "||")
return this:eval_key(key[2], who) || this:eval_key(key[3], who);
else
return 1 / 0;
endif
.
#48:7
token = args[1];
if (token == "me")
return this.player;
elseif (token == "here")
if (valid(this.player.location))
return this.player.location;
else
return ("'here' has no meaning where " + this.player:name()) + " is";
endif
elseif (subs = match(token, "^%(#[0-9]+%)%[%(.*%)%]$"))
if (valid(o = toobj(substitute("%1", subs))))
if (o.name == (name = substitute("%2", subs)))
return o;
else
return tostr(o, "'s name is not '", name, "'");
endif
else
return tostr(o, " is not a valid object");
endif
else
what = this.player.location:match_object(token);
if (what == $failed_match)
return ("Can't find an object named '" + token) + "'";
elseif (what == $ambiguous_match)
return ("Multiple objects named '" + token) + "'";
else
return what;
endif
endif
.
#48:8
key = args[1];
type = typeof(key);
if (!(type in {LIST, OBJ}))
return "(None.)";
elseif (type == OBJ)
if (valid(key))
if ($string_utils:match_player(key.name) == key)
return tostr("~", key.name);
else
return tostr(key, "[", key.name, "]");
endif
else
return tostr(key);
endif
else
op = key[1];
arg1 = this:unparse_key(key[2]);
if (op == "?")
return "?" + arg1;
elseif (op == "!")
if (typeof(key[2]) == LIST)
return ("!(" + arg1) + ")";
else
return "!" + arg1;
endif
elseif (op in {"&&", "||"})
other = (op == "&&") ? "||" | "&&";
lhs = arg1;
rhs = this:unparse_key(key[3]);
if ((typeof(key[2]) == OBJ) || (key[2][1] != other))
exp = lhs;
else
exp = ("(" + lhs) + ")";
endif
exp = ((exp + " ") + op) + " ";
if ((typeof(key[3]) == OBJ) || (key[3][1] != other))
exp = exp + rhs;
else
exp = ((exp + "(") + rhs) + ")";
endif
return exp;
else
return 1 / 0;
endif
endif
.
#48:9
key = args[1];
who = args[2];
type = typeof(key);
if (!(type in {LIST, OBJ}))
return 1;
elseif (typeof(key) == OBJ)
return (who == key) || $object_utils:contains(who, key);
endif
op = key[1];
if (op == "!")
return !this:eval_key(key[2], who);
elseif (op == "?")
return key[2]:is_unlocked_for(who);
elseif (op == "&&")
return this:eval_key(key[2], who) && this:eval_key(key[3], who);
elseif (op == "||")
return this:eval_key(key[2], who) || this:eval_key(key[3], who);
elseif (op == ".")
if ($object_utils:has_property(who, key[2]) && who.(key[2]))
return 1;
else
for thing in ($object_utils:all_contents(who))
if ($object_utils:has_property(thing, key[2]) && thing.(key[2]))
return 1;
endif
endfor
endif
return 0;
elseif (op == ":")
if ($object_utils:has_verb(who, key[2]) && who:(key[2])())
return 1;
else
for thing in ($object_utils:all_contents(who))
if ($object_utils:has_verb(thing, key[2]) && thing:(key[2])())
return 1;
endif
endfor
endif
return 0;
else
return 1 / 0;
endif
.
#48:10
token = this:scan_token();
if (token == "(")
exp = this:parse_E();
if ((typeof(exp) != STR) && (this:scan_token() != ")"))
return "Missing ')'";
else
return exp;
endif
elseif (token == "!")
exp = this:parse_A();
if (typeof(exp) == STR)
return exp;
else
return {"!", exp};
endif
elseif (token == "?")
next = this:scan_token();
if (next in {":", ".", "(", ")", "!", "&&", "||", "?"})
return ("Missing object-name before '" + token) + "'";
elseif (next == "")
return "Missing object-name at end of key expression";
else
what = this:match_object(next);
if (typeof(what) == OBJ)
return {"?", this:match_object(next)};
else
return what;
endif
endif
elseif (token in {":", "."})
next = this:scan_token();
if (next in {":", ".", "(", ")", "!", "&&", "||", "?"})
return ("Missing verb-or-property-name before '" + token) + "'";
elseif (next == "")
return "Missing verb-or-property-name at end of key expression";
elseif (typeof(next) != STR)
return "Non-string verb-or-property-name at end of key expression";
else
return {token, next};
endif
elseif (token in {"&&", "||"})
return ("Missing expression before '" + token) + "'";
elseif (token == "")
return "Missing expression at end of key expression";
else
return this:match_object(token);
endif
.
#48:11
if ($__core_init_phase)
pass(@args);
this.input_string = "";
this.input_index = 0;
this.input_length = 0;
this.player = $nothing;
endif
.
#49:0
":make(n[,elt]) => a list of n elements, each of which == elt. elt defaults to 0.";
if ((n = args[1]) < 0)
return E_INVARG;
endif
ret = {};
build = {elt = {@args, 0}[2]};
while (1)
if (n % 2)
ret = {@ret, @build};
endif
if (n = n / 2)
build = {@build, @build};
else
return ret;
endif
endwhile
.
#49:1
":range([m,]n) => {m,m+1,...,n}";
if (listdelete(args, 1))
else
args = {1, @args};
endif
ret = {};
for k in [args[1]..args[2]]
ret = {@ret, k};
endfor
return ret;
.
#49:2
"map_prop({OBJ...}, PROP)";
"Return a list of OBJ.(PROP) for each OBJ in args[1].";
set_task_perms(caller_perms());
prop = args[2];
if ((len = length(objs = args[1])) > 50)
return {@this:map_prop(objs[1..len / 2], prop), @this:map_prop(objs[(len / 2) + 1..len], prop)};
endif
strs = {};
for foo in (objs)
strs = {@strs, foo.(prop)};
endfor
return strs;
.
#49:3
"map_verb({OBJ, ...}, VERB, [ARGS])";
"Return a list of OBJ:VERB(@ARGS) for each OBJ in args[1].";
set_task_perms(caller_perms());
if ((len = length(objs = args[1])) > 50)
return {@this:map_verb(@listset(args, objs[1..len / 2], 1)), @this:map_verb(@listset(args, objs[(len / 2) + 1..len], 1))};
endif
vrb = args[2];
rest = args[3..length(args)];
strs = {};
for o in (objs)
strs = {@strs, o:(vrb)(@rest)};
endfor
return strs;
.
#49:4
"map_arg([n,]object,verb,@args) -- assumes the nth element of args is a list, calls object:verb(@args) with each element of the list substituted in turn, returns the list of results.  n defaults to 1.";
"";
"map_arg(o,v,{a...},a2,a3,a4,a5)={o:v(a,a2,a3,a4,a5),...}";
"map_arg(4,o,v,a1,a2,a3,{a...},a5)={o:v(a1,a2,a3,a,a5),...}";
set_task_perms(caller_perms());
if (n = (args || raise(E_ARGS))[1])
i = 2;
else
n = i = 1;
endif
{object, verb, @rest} = args[i..$];
if (length(rest[n]) > 50)
return {@this:map_arg(n, object, verb, @listset(rest, rest[n][1..$ / 2], n)), @this:map_arg(n, object, verb, @listset(rest, rest[n][($ / 2) + 1..$], n))};
endif
results = {};
for a in (rest[n])
results = listappend(results, object:(verb)(@listset(rest, a, n)));
endfor
return results;
"Copied from list utilities (#55):map_arg by Ben (#3373) Sat Apr  6 22:26:24 1996 EST";
"Copied from NNTP News Boy (#911):map_arg by splat (#3024) Wed Apr 10 08:16:01 1996 EDT";
"Copied from splat (#3024):map_arg by splat (#3024) Wed Apr 10 08:21:14 1996 EDT";
.
#49:5
":map_builtin(objectlist,func) applies func to each of the objects in turn and returns the corresponding list of results.  This function is mainly here for completeness -- in the vast majority of situations, a simple for loop is better.";
set_task_perms(caller_perms());
"3-13-96 changed to use the new call_function builtin";
{objs, func} = args;
strs = {};
if (length(objs) > 100)
return {@this:map_builtin(objs[1..l = $ / 2], func), @this:map_builtin(objs[l + 1..$], func)};
endif
for o in (objs)
strs = {@strs, call_function(func, o)};
endfor
return strs;
"--";
if (!((builtin = args[2]) in {"tostr", "tonum", "toobj", "typeof", "length", "random", "ctime", "valid", "parent", "children", "properties", "verbs", "is_player", "idle_seconds", "connected_seconds"}))
return E_INVARG;
endif
objs = args[1];
if (length(objs) > 100)
return {@this:map_builtin(objs[1..l = length(objs) / 2], builtin), @this:map_builtin(objs[l + 1..length(objs)], builtin)};
endif
strs = {};
for foo in (objs)
strs = {@strs, eval(tostr("return ", builtin, "(", $string_utils:from_value(foo, 1, -1), ");"))[2]};
endfor
return strs;
.
#49:6
"find_insert(sortedlist,key[,compare]) => index of first element in sortedlist > key";
"  sortedlist is assumed to be sorted in increasing order and the number returned is anywhere from 1 to length(sortedlist)+1, inclusive.";
"  compare, if given, is an object/verb pair to call when comparing each function; it should return a positive number if a > b, negative if a < b, 0 if a == b.  default to >/</=.";
lst = args[1];
key = args[2];
cmp = args[3];
o = cmp[1];
v = cmp[2];
if ((r = length(lst)) < 25)
for l in [1..r]
if (cmp ? o:(v)(lst[l], key) > 0 | (lst[l] > key))
return l;
endif
endfor
return r + 1;
else
l = 1;
while (r >= l)
if (cmp ? o:(v)(lst[i = (r + l) / 2], key) > 0 | (lst[i = (r + l) / 2] > key))
r = i - 1;
else
l = i + 1;
endif
endwhile
return l;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {896158978, \"Ken\", #75, \"JHM\"}";
.
#49:7
"remove_duplicates(list) => list as a set, i.e., all repeated elements removed.";
out = {};
for x in (args[1])
out = setadd(out, x);
endfor
return out;
.
#49:8
"arrayset(list,value,pos1,...,posn) -- returns list modified such that";
"  list[pos1][pos2][...][posn] == value";
if (length(args) > 3)
return listset(@listset(args[1..3], this:arrayset(@listset(listdelete(args, 3), args[1][args[3]], 1)), 2));
"... Rog's entry in the Obfuscated MOO-Code Contest...";
else
return listset(@args);
endif
.
#49:9
":setremove_all(set,elt) => set with *all* occurences of elt removed";
set = args[1];
what = args[2];
while (w = what in set)
set = listdelete(set, w);
endwhile
return set;
.
#49:10
"append({a,b,c},{d,e},{},{f,g,h},...) =>  {a,b,c,d,e,f,g,h}";
if ((n = length(args)) > 50)
return {@this:append(@args[1..n / 2]), @this:append(@args[(n / 2) + 1..n])};
endif
l = {};
for a in (args)
l = {@l, @a};
endfor
return l;
.
#49:11
"reverse(list) => reversed list";
return this:_reverse(@args[1]);
.
#49:12
":_reverse(@list) => reversed list";
if ((n = length(args)) > 50)
return {@this:_reverse(@args[(n / 2) + 1..n]), @this:_reverse(@args[1..n / 2])};
endif
l = {};
for a in (args)
l = listinsert(l, a);
endfor
return l;
.
#49:13
"compress(list) => list with consecutive repeated elements removed, e.g.,";
"compress({a,b,b,c,b,b,b,d,d,e}) => {a,b,c,b,d,e}";
if (l = args[1])
out = {last = l[1]};
for x in (listdelete(l, 1))
if (x != last)
out = listappend(out, x);
last = x;
endif
endfor
return out;
else
return l;
endif
.
#49:14
"sort(list[,keys]) => sorts keys (assumed to be all numbers or strings) and returns list with the corresponding permutation applied to it.  keys defaults to the list itself.";
"sort({x1,x3,x2},{1,3,2}) => {x1,x2,x3}";
{lst, ?unsorted_keys = lst} = args;
use_sorted_lst = unsorted_keys != lst;
sorted_lst = sorted_keys = {};
for e in (unsorted_keys)
l = this:find_insert(sorted_keys, e);
sorted_keys = listinsert(sorted_keys, e, l);
if (use_sorted_lst)
sorted_lst = listinsert(sorted_lst, lst[length(sorted_keys)], l);
endif
endfor
return sorted_lst || sorted_keys;
.
#49:15
"slice(alist[,index]) returns a list of the index-th elements of the elements of alist, e.g., ";
"    slice({{\"z\",1},{\"y\",2},{\"x\",5}},2) => {1,2,5}.";
"index defaults to 1 and may also be a nonempty list, e.g., ";
"    slice({{\"z\",1,3},{\"y\",2,4}},{2,1}) => {{1,\"z\"},{2,\"y\"}}";
slice = {};
ind = {@args, 1}[2];
if (typeof(ind) == LIST)
for elt in (args[1])
s = {elt[ind[1]]};
for i in (listdelete(ind, 1))
s = {@s, elt[i]};
endfor
slice = {@slice, s};
endfor
else
for elt in (args[1])
slice = {@slice, elt[ind]};
endfor
endif
return slice;
.
#49:16
"assoc(target,list[,index]) returns the first element of `list' whose own index-th element is target.  Index defaults to 1.";
"returns {} if no such element is found";
{target, lst, ?indx = 1} = args;
for t in (lst)
if (t[indx] == target)
"... do this test first since it's the most likely to fail; this needs -d";
if ((typeof(t) == LIST) && (length(t) >= indx))
return t;
endif
endif
endfor
return {};
"Metadata 202106";
"Last-Modify: {879307020, \"Jay\", #2, \"JHM\"}";
.
#49:17
"iassoc(target,list[,index]) returns the index of the first element of `list' whose own index-th element is target.  Index defaults to 1.";
"returns 0 if no such element is found.";
{target, lst, ?indx = 1} = args;
i = 1;
for lsti in (lst)
if (lsti[indx] == target)
"... do this test first since it's the most likely to fail; this needs -d";
if ((typeof(lsti) == LIST) && (length(lsti) >= indx))
return i;
endif
endif
i = i + 1;
endfor
return 0;
"Metadata 202106";
"Last-Modify: {879307041, \"Jay\", #2, \"JHM\"}";
.
#49:18
"iassoc(target,list[,index]) returns the index of the first element of `list' whose own index-th element is target.  Index defaults to 1.";
"returns 0 if no such element is found.";
"suspends as needed.";
set_task_perms(caller_perms());
target = args[1];
indx = {@args, 1}[3];
i = 1;
for lsti in (args[2])
if (lsti[indx] == target)
"... do this test first since it's the most likely to fail; this needs -d";
if ((typeof(lsti) == LIST) && (length(lsti) >= indx))
return i;
endif
endif
i = i + 1;
if ((ticks_left() < 1000) || (seconds_left() < 2))
suspend(0);
endif
endfor
return 0;
.
#49:19
"assoc_prefix(target,list[,index]) returns the first element of `list' whose own index-th element has target as a prefix.  Index defaults to 1.";
{target, lst, ?indx = 1} = args;
for t in (lst)
if ((typeof(t) == LIST) && ((length(t) >= indx) && (index(t[indx], target) == 1)))
return t;
endif
endfor
return {};
.
#49:20
"iassoc_prefix(target,list[,index]) returns the index of the first element of `list' whose own index-th element has target as a prefix.  Index defaults to 1.";
{target, lst, ?indx = 1} = args;
for i in [1..length(lst)]
if ((typeof(lsti = lst[i]) == LIST) && ((length(lsti) >= indx) && (index(lsti[indx], target) == 1)))
return i;
endif
endfor
return 0;
.
#49:21
"iassoc_sorted(target,sortedlist[,i]) => index of last element in sortedlist whose own i-th element is <= target.  i defaults to 1.";
"  sortedlist is assumed to be sorted in increasing order and the number returned is anywhere from 0 to length(sortedlist), inclusive.";
{target, lst, ?indx = 1} = args;
if ((r = length(lst)) < 25)
for l in [1..r]
if (target < lst[l][indx])
return l - 1;
endif
endfor
return r;
else
l = 0;
r = r + 1;
while ((r - 1) > l)
if (target < lst[i = (r + l) / 2][indx])
r = i;
else
l = i;
endif
endwhile
return l;
endif
return;
"Metadata 202106";
"Last-Modify: {832143281, \"Erik\", #74, \"JHM\"}";
.
#49:22
":sort_alist(alist[,n]) sorts a list of tuples by n-th (1st) element.";
if ((alist_length = length(alist = args[1])) < 25)
"use insertion sort on short lists";
return this:sort(alist, this:slice(@args));
endif
sort_on = {@args, 1}[2];
left_index = alist_length / 2;
right_index = (alist_length + 1) / 2;
left_sublist = this:sort_alist(alist[1..left_index], sort_on);
right_sublist = this:sort_alist(alist[left_index + 1..alist_length], sort_on);
"...";
"... merge ...";
"...";
left_key = left_sublist[left_index][sort_on];
right_key = right_sublist[right_index][sort_on];
if (left_key > right_key)
merged_list = {};
else
"... alist_length >= 25 implies right_index >= 2...";
"... move right_index downward until left_key > right_key...";
r = right_index - 1;
while (left_key <= (right_key = right_sublist[r][sort_on]))
if (r = r - 1)
else
return {@left_sublist, @right_sublist};
endif
endwhile
merged_list = right_sublist[r + 1..right_index];
right_index = r;
endif
while (l = left_index - 1)
"... left_key > right_key ...";
"... move left_index downward until left_key <= right_key...";
while ((left_key = left_sublist[l][sort_on]) > right_key)
if (l = l - 1)
else
return {@right_sublist[1..right_index], @left_sublist[1..left_index], @merged_list};
endif
endwhile
merged_list[1..0] = left_sublist[l + 1..left_index];
left_index = l;
"... left_key <= right_key ...";
if (r = right_index - 1)
"... move right_index downward until left_key > right_key...";
while (left_key <= (right_key = right_sublist[r][sort_on]))
if (r = r - 1)
else
return {@left_sublist[1..left_index], @right_sublist[1..right_index], @merged_list};
endif
endwhile
merged_list[1..0] = right_sublist[r + 1..right_index];
right_index = r;
else
return {@left_sublist[1..left_index], right_sublist[1], @merged_list};
endif
endwhile
return {@right_sublist[1..right_index], left_sublist[1], @merged_list};
.
#49:23
"sort_alist_suspended(interval,alist[,n]) sorts a list of tuples by n-th element.  n defaults to 1.  Calls suspend(interval) as necessary.";
set_task_perms(caller_perms());
"... so it can be killed...";
interval = args[1];
if ((alist_length = length(alist = args[2])) < 10)
"insertion sort on short lists";
$command_utils:suspend_if_needed(interval);
return this:sort(alist, this:slice(@listdelete(args, 1)));
endif
"variables specially expanded for the anal-retentive";
sort_on = {@args, 1}[3];
left_index = alist_length / 2;
right_index = (alist_length + 1) / 2;
left_sublist = this:sort_alist_suspended(interval, alist[1..left_index], sort_on);
right_sublist = this:sort_alist_suspended(interval, alist[left_index + 1..alist_length], sort_on);
left_element = left_sublist[left_index];
right_element = right_sublist[right_index];
merged_list = {};
while (1)
$command_utils:suspend_if_needed(interval);
if (left_element[sort_on] > right_element[sort_on])
merged_list = {left_element, @merged_list};
if (left_index = left_index - 1)
left_element = left_sublist[left_index];
else
return {@right_sublist[1..right_index], @merged_list};
endif
else
merged_list = {right_element, @merged_list};
if (right_index = right_index - 1)
right_element = right_sublist[right_index];
else
return {@left_sublist[1..left_index], @merged_list};
endif
endif
endwhile
.
#49:24
":randomly_permute(list) => list with its elements randomly permuted";
"  each of the length(list)! possible permutations is equally likely";
plist = {};
for i in [1..length(ulist = args[1])]
plist = listinsert(plist, ulist[i], random(i));
endfor
return plist;
.
#49:25
"passoc(key,list1,list2)";
"passoc() behaves rather similarly to assoc, with the exception that it's intended for";
"parallel lists.  given a key from list1, it returns a list containing the key and the";
"corresponding item from list2 (\"corresponding\", in the case of parallel lists, means";
"having the same index.)";
indx = args[1] in args[2];
if (indx)
return {args[1], args[3][indx]};
else
return {};
endif
.
#49:26
"check_type(list, type)";
"Make sure all elements of <list> are of a given <type>.";
"<type> can be either one of LIST, STR, OBJ, NUM, ERR, or a list of same.";
"return true if all elements check, otherwise 0.";
typelist = (typeof(args[2]) == LIST) ? args[2] | {args[2]};
for element in (args[1])
if (!(typeof(element) in typelist))
return 0;
endif
endfor
return 1;
.
#49:27
"Usage:  random_permutation(list)";
"Return a list with the same elements as list, in a random order.";
old = args[1];
new = {};
while (old)
which = random(length(old));
new = listappend(new, old[which]);
old = listdelete(old, which);
endwhile
return new;
.
#49:28
":max_length(strings-or-lists[, default])";
"Return the maximum length of a set of strings or lists.";
"default is the minimum length that can be returned; 0 is a safe bet.";
max = args[2] || 0;
for item in (args[1])
max = max(max, length(item));
endfor
return max;
.
#49:29
"any_of(<list> l)";
"return a random element of l.";
return args[1][random($)];
.
#49:30
"Copied from $quinn_utils (#34283):unroll by Quinn (#19845) Mon Mar  8 09:29:03 1993 PST";
":flatten(LIST list_of_lists) => LIST of all lists in given list `flattened'";
newlist = {};
for elm in (args[1])
if (typeof(elm) == LIST)
newlist = {@newlist, @this:flatten(elm)};
else
newlist = {@newlist, elm};
endif
endfor
return newlist;
.
#49:31
"grep_l -- returns a list of indices into a list of strings representing the locations of the search string in the target list";
"Usage: $list_utils:grep_l(<strings>,<pattern>[,<delimited>])";
"       $list_utils:grep_l({\"foo\", \"bar\", \"baa\"},\"a\")";
"          => {{2, {2}}, {3, {2, 3}}}";
if ((length(args) < 2) || (length(args) > 3))
return E_ARGS;
elseif ((typeof(data = args[1]) != LIST) || (typeof(grepstr = args[2]) != STR))
return E_TYPE;
else
delimited = (length(args) == 3) && args[3];
where = {};
l = 0;
for line in (data)
$command_utils:suspend_if_needed(0);
l = l + 1;
if (typeof(line) != STR)
return E_TYPE;
else
if (found = $string_utils:index_all(line, grepstr, delimited))
where = {@where, {l, found}};
endif
endif
endfor
return where;
endif
.
#49:32
"grep -- returns a list of indices into a list of list of strings representing the locations of the search string";
if (length(args) != 2)
return E_ARGS;
elseif ((typeof(data = args[1]) != LIST) || (typeof(grepstr = args[2]) != STR))
return E_TYPE;
else
where = {};
l = 0;
for par in (data)
$command_utils:suspend_if_needed(0);
l = l + 1;
if (typeof(par) != LIST)
return E_TYPE;
else
if (found = $list_utils:grep_l(par, grepstr))
where = {@where, {l, found}};
endif
endif
endfor
return where;
endif
.
#49:33
"Copied from list utilities (#55):slice by Jay (#37) Mon Oct 18 15:37:49 1993 EDT";
"slice(alist[,index]) returns a list of the index-th elements of the elements of alist, e.g., ";
"    slice({{\"z\",1},{\"y\",2},{\"x\",5}},2) => {1,2,5}.";
"index defaults to 1 and may also be a nonempty list, e.g., ";
"    slice({{\"z\",1,3},{\"y\",2,4}},{2,1}) => {{1,\"z\"},{2,\"y\"}}";
slice = {};
ind = {@args, 1}[2];
if (typeof(ind) == LIST)
for elt in (args[1])
$command_utils:suspend_if_needed(0);
s = {elt[ind[1]]};
for i in (listdelete(ind, 1))
s = {@s, elt[i]};
endfor
slice = {@slice, s};
endfor
else
for elt in (args[1])
$command_utils:suspend_if_needed(0);
slice = {@slice, elt[ind]};
endfor
endif
return slice;
.
#49:34
":sort_suspended(interval,list[,keys]) => sorts keys (assumed to be all numbers or strings) and returns list with the corresponding permutation applied to it.  keys defaults to the list itself.";
"does suspend(interval) as needed.";
set_task_perms(caller_perms());
interval = args[1];
if (typeof(interval) != NUM)
return E_ARGS;
endif
lst = args[2];
unsorted_keys = (use_sorted_lst = length(args) >= 3) ? args[3] | lst;
sorted_lst = sorted_keys = {};
for e in (unsorted_keys)
l = this:find_insert(sorted_keys, e);
sorted_keys[l..l - 1] = {e};
if (use_sorted_lst)
sorted_lst[l..l - 1] = {lst[length(sorted_keys)]};
endif
$command_utils:suspend_if_needed(interval);
endfor
return sorted_lst || sorted_keys;
.
#49:35
":make_alist(lists[, pad])";
"Make an alist out of n parallel lists (basically a matrix transpose).";
"If the lists are of uneven length, fill the remaining tuples with pad (defaults to 0).";
alist = {};
pad = (length(args) > 1) ? args[2] | 0;
for i in [1..$list_utils:max_length(args[1])]
tuple = {};
for l in (args[1])
tuple = {@tuple, (i > length(l)) ? pad | l[i]};
endfor
alist = {@alist, tuple};
endfor
return alist;
.
#49:36
"Copied from APHiD (#33119):longest Sun May  9 21:00:18 1993 PDT";
"$list_utils:longest(<list>)";
"$list_utils:shortest(<list>)";
"             - Returns the shortest or longest element in the list.  Elements may be either strings or lists.  Returns E_TYPE if passed a non-list or a list containing non-string/list elements.  Returns E_RANGE if passed an empty list.";
if (typeof(all = args[1]) != LIST)
return E_TYPE;
elseif (all == {})
return E_RANGE;
else
result = all[1];
for things in (all)
if ((typeof(things) != LIST) && (typeof(things) != STR))
return E_TYPE;
else
result = (((verb == "longest") && (length(result) < length(things))) || ((verb == "shortest") && (length(result) > length(things)))) ? things | result;
endif
endfor
endif
return result;
.
#49:37
"$list_utils:count(item, list)";
"Returns the number of occurrences of item in list.";
if (length(args) != 2)
return E_ARGS;
elseif (typeof(xlist = args[2]) != LIST)
return E_INVARG;
endif
x = args[1];
counter = 0;
while (loc = x in xlist)
counter = counter + 1;
xlist = xlist[loc + 1..length(xlist)];
endwhile
return counter;
.
#49:38
"map_verb_safe(DEFAULT, {OBJ, ...}, VERB, [ARGS])";
"Return a list of `OBJ:VERB(@ARGS) ! ANY' for each OBJ in args[1].";
set_task_perms(caller_perms());
if ((len = length(objs = args[1])) > 50)
return {@this:map_verb(@listset(args, objs[1..len / 2], 1)), @this:map_verb(@listset(args, objs[(len / 2) + 1..len], 1))};
endif
vrb = args[2];
rest = args[3..length(args)];
strs = {};
for o in (objs)
strs = {@strs, `o:(vrb)(@rest) ! ANY'};
endfor
return strs;
"Copied from list utilities (#55):map_verb by splat (#3024) Wed Apr 10 07:51:48 1996 EDT";
.
#50:0
"Usage: object_match_failed(object, string)";
"Prints a message if string does not match object.  Generally used after object is derived from a :match_object(string).";
match_result = args[1];
string = args[2];
tell = $perm_utils:controls(caller_perms(), player) ? "notify" | "tell";
if ((index(string, "#") == 1) && ($code_utils:toobj(string) != E_TYPE))
"...avoid the `I don't know which `#-2' you mean' message...";
if (!valid(match_result))
player:(tell)(tostr(string, " does not exist."));
endif
return !valid(match_result);
elseif (match_result == $nothing)
player:(tell)("You must give the name of some object.");
elseif (match_result == $failed_match)
player:(tell)(tostr("You see no \"", string, "\" here."));
elseif (match_result == $ambiguous_match)
player:(tell)(tostr("You haven't specified which \"", string, "\" you mean."));
elseif (!valid(match_result))
player:(tell)(tostr(match_result, " does not exist."));
else
return 0;
endif
return 1;
.
#50:1
":player_match_failed(result,string)";
"  is exactly like :object_match_failed(result,string)";
"  except that its messages are more suitable for player searches.";
":player_match_result(results,strings)";
"  handles a list of results, also presumably from $string_utils:match_player(strings), printing messages to player for *each* of the nonmatching strings.  It returns a list, an overall result (true if some string didn't match --- just like player_match_failed), followed by the list players that matched.";
"";
"An optional 3rd arg gives an identifying string to prefix to each of the nasty messages.";
if (valid(player))
tell = $perm_utils:controls(caller_perms(), player) ? "notify" | "tell";
plyr = player;
else
tell = "notify";
plyr = $login;
endif
"...";
match_results = args[1];
strings = args[2];
cmdid = {@args, ""}[3] || "";
pmf = verb == "player_match_failed";
if (typeof(match_results) == OBJ)
match_results = {match_results};
strings = {strings};
endif
pset = {};
bombed = 0;
for i in [1..length(match_results)]
if (valid(result = match_results[i]))
pset = setadd(pset, match_results[i]);
elseif (result == $nothing)
"... player_match_result quietly skips over blank strings";
if (pmf)
plyr:(tell)("You must give the name of some player.");
bombed = 1;
endif
elseif (result == $failed_match)
plyr:(tell)(tostr(cmdid, "\"", strings[i], "\" is not the name of any player."));
bombed = 1;
elseif (result == $ambiguous_match)
lst = $player_db:find_all(strings[i]);
plyr:(tell)(tostr(cmdid, "\"", strings[i], "\" could refer to ", (length(lst) > 20) ? tostr("any of ", length(lst), " players") | $string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%n (%#)", lst), "no one", " or "), "."));
bombed = 1;
else
plyr:(tell)(tostr(result, " does not exist."));
bombed = 1;
endif
endfor
return pmf ? bombed | {bombed, @pset};
.
#50:2
"$command_utils:read() -- read a line of input from the player and return it";
"Optional first argument is a prompt portion to replace `a line of input' in the prompt.";
"Optional second argument is a prompt line to replace the entire bogus prefab prompt line.  If it doesn't contain `@abort', the phrase `Typing `@abort' will cancel the command.' will be added.";
"";
"Returns E_PERM if the current task is not a command task that has never called suspend().";
prompt = "";
if (args && (typeof(args[1]) == STR))
subprompt = args[1];
if ((length(args) > 1) && (typeof(args[2]) == STR))
prompt = args[2];
endif
else
subprompt = "a line of input";
endif
c = callers();
p = c[length(c)][5];
if (prompt == "")
prompt = ("[Type " + subprompt) + " or `@abort' to abort the command.]";
elseif (!index(prompt, "@abort"))
prompt = prompt + "  Typing `@abort' will cancel the command.";
endif
p:notify(prompt);
ans = read(@((caller == p) || $perm_utils:controls(caller_perms(), p)) ? {p} | {});
if (typeof(ans) == ERR)
return ans;
elseif ($string_utils:trim(ans) == "@abort")
p:notify(">> Command Aborted <<");
kill_task(task_id());
else
return ans;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878328452, \"Erik\", #74, \"JHM\"}";
.
#50:3
"$command_utils:read_lines() -- read zero or more lines of input";
"";
"Returns a list of strings, the lines typed by the player.  Returns E_PERM if the current task is not a command task that has never called suspend().";
"In order that one may enter arbitrary lines, including \"@abort\" or \".\", if the first character in an input line is `.' and there is some nonwhitespace afterwords, the `.' is dropped and the rest of the line is taken verbatim, so that, e.g., \".@abort\" enters as \"@abort\" and \"..\" enters as \".\".";
c = callers();
p = c[length(c)][5];
p:notify("[Type lines of input; use `.' to end or `@abort' to abort the command.]");
ans = {};
while (1)
if (typeof(line = read()) == ERR)
return line;
elseif ((line[1..min(6, length(line))] == "@abort") && ((tail = line[7..length(line)]) == $string_utils:space(tail)))
p:notify(">> Command Aborted <<");
kill_task(task_id());
elseif ((!line) || (line[1] != "."))
ans = {@ans, line};
elseif ((tail = line[2..length(line)]) == $string_utils:space(tail))
return ans;
else
ans = {@ans, tail};
endif
endwhile
.
#50:4
":yes-or-no([prompt]) -- prompts the player for a yes or no answer and returns a true value iff the player enters a line of input that is some prefix of \"yes\"";
"";
"Returns E_NONE if the player enters a blank line, E_INVARG, if the player enters something that isn't a prefix of \"yes\" or \"no\", and E_PERM if the current task is not a command task that has never called suspend().";
c = callers();
p = c[length(c)][5];
p:notify(tostr(args ? args[1] + " " | "", "[Enter `yes' or `no']"));
ans = read(@((caller == p) || $perm_utils:controls(caller_perms(), p)) ? {p} | {});
if (typeof(ans) == ERR)
return ans;
elseif (ans = $string_utils:trim(ans))
if (ans == "@abort")
p:notify(">> Command Aborted <<");
kill_task(task_id());
endif
return (index("yes", ans) == 1) || ((index("no", ans) != 1) && E_INVARG);
else
return E_NONE;
endif
.
#50:5
"$command_utils:read_lines_escape(escapes[,help]) -- read zero or more lines of input";
"";
"Similar to :read_lines() except that help is available and one may specify other escape sequences to terminate the read.";
"  escapes should be either a string or list of strings; this specifies which inputs other from `.' or `@abort' should terminate the read (... don't use anything beginning with a `.').";
"  help should be a string or list of strings to be printed in response to the player typing `?'; the first line of the help text should be a general comment about what the input text should be used for.  Successive lines should describe the effects of the alternative escapes.";
"Returns {end,list-of-strings-input} where end is the particular line that terminated this input or 0 if input terminated normally with `.'.  Returns E_PERM if the current task is not a command task that has never called suspend().  ";
"@abort and lines beginning with `.' are treated exactly as with :read_lines()";
c = callers();
p = c[length(c)][5];
escapes = {".", "@abort", @(typeof(args[1]) == LIST) ? args[1] | {args[1]}};
p:notify(tostr("[Type lines of input; `?' for help; end with `", $string_utils:english_list(escapes, "", "' or `", "', `", ""), "'.]"));
ans = {};
escapes[1..0] = {"?"};
"... set up the help text...";
help = {@args, "You are currently in a read loop."}[2];
if (typeof(help) != LIST)
help = {help};
endif
help[2..1] = {"Type `.' on a line by itself to finish.", "Anything else with a leading period is entered with the period removed.", "Type `@abort' to abort the command completely."};
while (typeof(line = read()) != ERR)
if ((trimline = $string_utils:trimr(line)) in escapes)
if (trimline == ".")
return {0, ans};
elseif (trimline == "@abort")
p:notify(">> Command Aborted <<");
kill_task(task_id());
elseif (trimline == "?")
p:notify_lines(help);
else
return {trimline, ans};
endif
else
if (line && (line[1] == "."))
line[1..1] = "";
endif
ans = {@ans, line};
endif
endwhile
return line;
.
#50:6
"Suspend, using output_delimiters() in case a client needs to keep track";
"of the output of the current command.";
"Args are TIME, amount of time to suspend, and optional (misnamed) OUTPUT.";
"If given no OUTPUT, just do a suspend.";
"If OUTPUT is neither list nor string, suspend and return output_delimiters";
"If OUTPUT is a list, it should be in the output_delimiters() format:";
"  {PREFIX, SUFFIX}.  Use these to handle that client stuff.";
"If OUTPUT is a string, it should be SUFFIX (output_delimiters[2])";
"";
"Proper usage:";
"The first time you want to suspend, use";
"  output_delimiters = $command_utils:suspend(time, x);";
"where x is some non-zero number.";
"Following, use";
"  $command_utils:suspend(time, output_delimiters);";
"To wrap things up, use";
"  $command_utils:suspend(time, output_delimiters[2]);";
"You'll probably want time == 0 most of the time.";
"Note: Using this from verbs called by other verbs could get pretty weird.";
set_task_perms(caller_perms());
time = args[1];
output = (length(args) == 2) && args[2];
if (!output)
suspend(time);
else
if (typeof(output) == LIST)
PREFIX = output[1];
SUFFIX = output[2];
if (PREFIX)
player:tell(output[2]);
endif
suspend(time);
if (SUFFIX)
player:tell(output[1]);
endif
elseif (typeof(output) == STR)
if (output)
player:tell(output);
endif
else
output = output_delimiters(player);
suspend(time);
if (output != {"", ""})
player:tell(output[1]);
endif
return output;
endif
endif
.
#50:7
"Return true if we're running out of ticks or seconds.";
return (seconds_left() < 2) || (ticks_left() < 4000);
.
#50:8
"Usage:  $command_utils:suspend_if_needed(<time>[, @<announcement>])";
"See if we're running out of ticks or seconds, and if so suspend(<time>) and return true.  If more than one arg is given, print the remainder with player:tell.";
if (this:running_out_of_time() && valid(player))
if (ann = listdelete(args, 1))
player:tell(@ann);
endif
set_task_perms(caller_perms());
suspend(args[1]);
return 1;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {879181085, \"Ken\", #75, \"JHM\"}";
.
#50:9
":dump_lines(text) => text `.'-quoted for :read_lines()";
"  text is assumed to be a list of strings";
"Returns a corresponding list of strings which, when read via :read_lines, ";
"produces the original list of strings (essentially, any strings beginning ";
"with a period \".\" have the period doubled).";
"The list returned includes a final \".\"";
text = args[1];
newtext = {};
i = lasti = 0;
for line in (text)
if (line && (line[1] == "."))
newtext = {@newtext, @(i > lasti) ? text[lasti + 1..i] | {}, "." + line};
lasti = i = i + 1;
else
i = i + 1;
endif
endfor
return {@newtext, @(i > lasti) ? text[lasti + 1..i] | {}, "."};
.
#50:10
":explain_syntax(here,verb,args)";
{here, verb, @_} = args;
for x in ({player, here, @valid(dobj) ? {dobj} | {}, @valid(iobj) ? {iobj} | {}})
what = x;
while (hv = $object_utils:has_verb(what, verb))
what = hv[1];
i = 0;
while (i = $code_utils:find_verb_named_1_based(what, verb, i + 1))
if (usage = $code_utils:verb_usage(what, i))
player:tell_lines(usage);
return 1;
elseif (evs = $code_utils:explain_verb_syntax(x, verb, @verb_args(what, i)))
if (index(verb_info(what, i)[2], "x") || (x in {player, here, @player.contents, @here.contents}))
player:tell("Try this instead:  ", evs);
return 1;
endif
endif
endwhile
what = parent(what);
endwhile
endfor
return 0;
.
#50:11
":do_huh(verb,args)  what :huh should do by default.";
set_task_perms(cp = caller_perms());
verb = args[1];
args = args[2];
bad = "That is not a valid command";
notify = $perm_utils:controls(cp, player) ? "notify" | "tell";
if (valid(player.location))
dobj = player.location:match_object(dobjstr);
iobj = player.location:match_object(iobjstr);
endif
if (player:my_huh(verb, args))
"... the player found something funky to do ...";
elseif (caller:here_huh(verb, args))
"... the room found something funky to do ...";
elseif (this:extra_huh(verb, args))
"... we found something on dobj or iobj";
elseif (player:last_huh(verb, args))
"... player's second round found something to do ...";
"elseif ($mistake_tracker:handle_failed_command(player, verb, argstr, dobjstr, prepstr, iobjstr, dobj, iobj))";
"... experimental mistake learner found something to do ...";
elseif (dobj == $ambiguous_match)
"... from here on, it's all error-reporting.";
if (iobj == $ambiguous_match)
player:(notify)(tostr(bad, " (\"", dobjstr, "\" and \"", iobjstr, "\" are both ambiguous names)."));
else
player:(notify)(tostr(bad, " (\"", dobjstr, "\" is an ambiguous name)."));
endif
elseif (iobj == $ambiguous_match)
player:(notify)(tostr(bad, " (\"", iobjstr, "\" is an ambiguous name)."));
else
(player:my_explain_syntax(caller, verb, args) || (caller:here_explain_syntax(caller, verb, args) || this:explain_syntax(caller, verb, args))) || player:(notify)(bad + ".");
endif
.
#50:12
"task_info(task id)";
"Return info (the same info supplied by queued_tasks()) about a given task id, or E_INVARG if there's no such task queued.";
"WIZARDLY";
set_task_perms(caller_perms());
tasks = queued_tasks();
task_id = args[1];
for task in (tasks)
if (task[1] == task_id)
return task;
endif
endfor
return E_INVARG;
.
#50:13
"Usage: room_match_failed(room, string)";
"";
"Prints a message if string does not match room.  Generally used after room is derived from a :match_room(string).";
match_result = args[1];
string = args[2];
tell = $perm_utils:controls(caller_perms(), player) ? "notify" | "tell";
if ((match_result == $nowhere) && (string != ""))
"special case $nowhere";
return 0;
elseif ((index(string, "#") == 1) && ((o = $code_utils:toobj(string)) != E_TYPE))
"...avoid the `I don't know which `#-2' you mean' message...";
if (!$recycler:valid(match_result))
if ($recycler:valid(o))
player:(tell)(tostr(o:dnamec_and_number(), " is not a room."));
else
player:(tell)(tostr(string, " does not exist."));
endif
endif
return !$recycler:valid(match_result);
elseif (match_result == $nothing)
player:(tell)("You must give the name of some place.");
elseif (match_result == $failed_match)
player:(tell)(tostr("There is no place named \"", string, "\"."));
elseif (match_result == $ambiguous_match)
player:(tell)(tostr("There is more than one place named \"", string, "\"."));
elseif (((typeof(match_result) == LIST) && (length(match_result) == 2)) && (match_result[1] == $ambiguous_match))
"handle new experimental match results";
"player:(tell)(tostr(\"Which \\\"\", string, \"\\\" do you mean, \", $language:name_list(match_result[2], {\"!!! HEY, THE MATCHER IS BROKEN!  COMPLAIN TO A WIZARD! !!!\", \" or \"}, \"d\"), \"?\"));";
player:report_ambiguous_match(string, match_result[2], "place");
elseif (!$recycler:valid(match_result))
player:(tell)(tostr(match_result, " does not exist."));
else
return 0;
endif
return 1;
.
#50:14
"_suspend -- suspends a task until resumed via $command_utils:_resume or time exceeds allotment (E_RANGE returned)";
"Usage: $command_utils:_suspend();";
if (length(args) > 1)
return E_ARGS;
elseif ((length(args) == 1) && (typeof(args[1]) != NUM))
return E_INVARG;
else
me = task_id();
this.suspenders = {@this.suspenders, me};
timeout = args ? args[1] + time() | $maxint;
while (1)
for pair in ({{0, 5}, {1, 5}, {2, 999999999}})
seconds = pair[1];
times = pair[2];
for i in [1..times]
suspend(seconds);
if (time() > timeout)
$error:raise(E_RANGE);
return;
endif
j = me in this.resumers;
if (j)
result = this.resume_values[j];
this.resumers[j..j] = {};
this.resume_values[j..j] = {};
return result;
endif
endfor
endfor
endwhile
endif
.
#50:15
"_resume -- resumes a suspended task, returns a value to the resumed task.";
"Usage: $command_utils:_resume(<task-id>[,<return-value>]);";
"       $command_utils:_resume(530609762,\"task resumed\");";
if (((l = length(args)) != 1) && (l != 2))
return E_ARGS;
elseif (!(typeof(args[1]) == NUM))
return E_TYPE;
else
task = args[1];
value = {@args, 0}[2];
if (i = task in this.suspenders)
this.suspenders[i..i] = {};
this.resumers = {@this.resumers, task};
this.resume_values = {@this.resume_values, value};
else
$error:raise(E_INVARG);
endif
endif
.
#50:16
if ($__core_init_phase)
pass(@args);
" this:suspend_database_cleanup();";
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {893174634, \"Ken\", #75, \"JHM\"}";
.
#51:0
if ((!player.wizard) || (player != this))
player:notify("Sorry.");
return;
endif
set_task_perms(player);
args = setremove(args, "to");
if ((length(args) != 2) || (!args[2]))
player:notify(tostr("Usage:  ", verb, " <object-or-property-or-verb> <owner>"));
return;
endif
what = args[1];
owner = player:my_match_player(args[2]);
bynumber = verb[$] == "#";
if ($command_utils:player_match_result(owner, args[2])[1])
elseif (spec = $code_utils:parse_verbref(what))
object = this:my_match_object(spec[1]);
if (!$command_utils:object_match_failed(object, spec[1]))
vname = spec[2];
if (bynumber && ((E_TYPE == (vname = $code_utils:tonum(vname))) || (vname < 1)))
player:notify("Verb number expected.");
return;
endif
try
info = verb_info(object, vname);
result = set_verb_info(object, vname, listset(info, owner, 1));
except (E_VERBNF)
player:notify(bynumber ? "Verb number out of range" | "That object does not define that verb.");
return;
except e (ANY)
player:notify(e[2]);
return;
endtry
player:notify("Verb owner set.");
endif
elseif (bynumber)
player:notify(tostr(verb, " can only be used for verbs."));
elseif (index(what, ".") && (spec = $code_utils:parse_propref(what)))
object = this:my_match_object(spec[1]);
if (!$command_utils:object_match_failed(object, spec[1]))
pname = spec[2];
e = $wiz_utils:set_property_owner(object, pname, owner);
if (e == E_NONE)
player:notify("+c Property owner set.  Did you really want to do that?");
else
player:notify(tostr(e && "Property owner set."));
endif
endif
else
object = this:my_match_object(what);
if (!$command_utils:object_match_failed(object, what))
player:notify(tostr($wiz_utils:set_owner(object, owner) && "Object ownership changed."));
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669191, \"Erik\", #74, \"JHM\"}";
.
#51:1
"@grant <object> to <player>";
"@grants <object> to <player>   --- same as @grant but may suspend.";
"A correct (?) version of @grant.  Ownership of the object changes as in @chown and :set_owner (i.e., .owner and all c properties change).  In addition all verbs and !c properties owned by the original owner change ownership as well.  Finally, for !c properties, instances on descendant objects change ownership (as in :set_property_owner).";
if ((!player.wizard) || (player != this))
player:notify("Sorry.");
return;
endif
set_task_perms(player);
if ((!iobjstr) || (!dobjstr))
player:notify(tostr("Usage:  ", verb, " <object> to <player>"));
elseif ($command_utils:object_match_failed(object = this:my_match_object(dobjstr), dobjstr))
"...object is bogus...";
elseif ($command_utils:player_match_failed(newowner = player:my_match_player(iobjstr), iobjstr))
"...newowner is bogus...";
else
player:notify(tostr(`$wiz_utils:do_grant(object, newowner, verb != "@grant") ! ANY'));
return;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669198, \"Erik\", #74, \"JHM\"}";
.
#51:2
set_task_perms(player);
dobj = player:my_match_player(dobjstr);
if (dobj == $nothing)
player:notify(tostr("Usage:  ", verb, " <playername>"));
elseif ($command_utils:player_match_result(dobj, dobjstr)[1])
elseif ((dobj.description == $player.description) && (!$command_utils:yes_or_no(tostr("@Programmer ", dobj:dname(), " despite ", dobj:pp(), " lack of description?"))))
player:notify(tostr("Okay, leaving ", dobj:name(), " !programmer."));
return;
elseif (result = $wiz_utils:set_programmer(dobj))
player:notify(tostr(dobj:dnamec(), " ", dobj:verb_sub("is"), " now ", dobj:noun_sub("a programmer"), ".  ", dobj:ppc(), " quota is currently ", $quota_utils:get_quota_total(dobj), "."));
player:notify(tostr(dobj:dnamec(), " and the other admins have been notified."));
if (msg = this:programmer_victim_msg())
dobj:notify(msg);
endif
if ($object_utils:isa(dobj.location, $room) && (msg = this:programmer_msg()))
dobj.location:announce_all_but({dobj}, msg);
endif
elseif (result == E_NONE)
player:notify(tostr(dobj:dnamec_and_number(), " ", dobj:verb_sub("is"), " already ", dobj:noun_sub("a programmer"), "..."));
else
player:notify(tostr(result));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669204, \"Erik\", #74, \"JHM\"}";
.
#51:3
if (!player.wizard)
player:notify("Sorry.");
return;
elseif ($code_utils:task_valid($shutdown_task))
player:notify("Shutdown already in progress.");
return;
endif
if (s = match(argstr, "^in +%([0-9]+%) +"))
bounds = s[3][1];
delay = tonum(argstr[bounds[1]..bounds[2]]);
argstr = argstr[s[2] + 1..length(argstr)];
else
delay = 2;
endif
if (!$command_utils:yes_or_no(tostr("Do you really want to shut down the server in ", delay, " minutes?")))
player:notify("Aborted.");
return;
endif
announce_times = {};
if (delay > 0)
while (delay > 0)
announce_times = {@announce_times, delay * 60};
delay = delay / 2;
endwhile
announce_times = {@announce_times, 30, 10};
$shutdown_time = time() + announce_times[1];
endif
$shutdown_message = tostr(player.name, " (", player, "): ", argstr);
$shutdown_task = task_id();
for i in [1..length(announce_times)]
msg = $generic_editor:fill_string(tostr("*** The server will be shut down by ", player.name, " (", player, ") in ", $time_utils:english_time(announce_times[i]), ": ", argstr, " ***"));
"...use raw notify() since :notify() verb could be broken...";
for p in (connected_players())
for line in (msg)
notify(p, line);
endfor
$command_utils:suspend_if_needed(0);
endfor
suspend(announce_times[i] - {@announce_times, 0}[i + 1]);
endfor
for p in (connected_players())
notify(p, tostr("*** Server shutdown by ", player.name, " (", player, "): ", argstr, " ***"));
boot_player(p);
endfor
suspend(0);
$shutdown_task = E_NONE;
set_task_perms(player);
shutdown(argstr);
.
#51:4
set_task_perms(player);
dump_database();
player:notify("Dumping...");
.
#51:5
set_task_perms(player);
if (argstr[1] != ":")
argstr = ":" + argstr;
endif
player:notify(tostr("Searching for verbs that appear to call ", argstr, " ..."));
player:notify("");
$code_utils:find_verbs_containing(argstr + "(");
.
#51:6
if (!caller_perms().wizard)
return;
elseif (!("mcd_pos" in properties(this)))
return;
endif
start = this.mcd_pos;
saved = args[1];
player:tell("*** Recycling from #", start, " ...");
suspend(0);
fork (0)
this:mcd_2(saved);
endfork
for i in [start..tonum(max_object())]
this.mcd_pos = i;
o = toobj(i);
if ($command_utils:running_out_of_time())
return;
endif
if (valid(o) && (!(o in saved)))
for x in (o.contents)
move(x, #-1);
endfor
recycle(o);
endif
endfor
delete_property(this, "mcd_pos");
"----------------------------------------";
suspend(0);
player:tell("Killing queued tasks ...");
for t in (queued_tasks())
kill_task(t[1]);
endfor
"----------------------------------------";
player:notify("Initializing core objects for inclusion...");
add_property(#1, "__init_for_core_flag", 0, {#2, "r"});
add_property($garbage, "__init_for_core_flag", 0, {#2, "r"});
delayed = saved;
work = {};
$__core_init_phase = 1;
while (delayed && (delayed != work))
work = delayed;
delayed = {};
for i in (work)
if ((seconds_left() < 2) || (ticks_left() < 4000))
suspend(0);
endif
player:notify(tostr("... ", i.name, " (", i, ")"));
if ($object_utils:has_callable_verb(i, "init_for_core"))
if (i:init_for_core() == "wait")
delayed = {@delayed, i};
player:notify("    (deferred)");
else
i.__init_for_core_flag = i;
endif
else
i.__init_for_core_flag = i;
endif
endfor
endwhile
if (delayed)
player:notify("");
player:notify("*** ALERT ALERT ALERT");
player:notify("*** A deadlock situation has occurred during initialization.");
player:notify("*** All objects being initialized from now on may be considered suspects.");
player:notify("*** Since initialization order is being faked, this core may be inconsistent.");
for i in (delayed)
i.__init_for_core_flag = i;
endfor
for i in (delayed)
if ((seconds_left() < 2) || (ticks_left() < 4000))
suspend(0);
endif
if ($object_utils:has_callable_verb(i, "init_for_core"))
i:init_for_core();
endif
endfor
endif
$__core_init_phase = 0;
delete_property(#1, "__init_for_core_flag");
delete_property($garbage, "__init_for_core_flag");
"----------------------------------------";
suspend(0);
while (queued_tasks())
player:tell("Waiting for queued tasks:  ", $string_utils:print(queued_tasks()));
suspend(1);
endwhile
"----------------------------------------";
player:tell("Compacting object numbers ...");
this:renumber_for_core();
"----------------------------------------";
fork (0)
for o in [#1..max_object()]
$quota_utils:object_bytes(o);
$command_utils:suspend_if_needed(0);
endfor
endfork
player:tell("Remeasuring all objects ...");
player:tell("Core database extraction is complete.  Type @shutdown to save it.");
$core_extraction_time = time();
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895265901, \"Ken\", #75, \"JHM\"}";
.
#51:7
"@toad[!][!] <player> [blacklist|redlist|graylist] [commentary]";
if (!args)
player:notify(tostr("Usage: ", verb, " <player> [blacklist|redlist|graylist] [comment]"));
return;
endif
whostr = args[1];
comment = $string_utils:first_word(argstr)[2];
if (verb == "@toad!!")
listname = "redlist";
elseif (verb == "@toad!")
listname = "blacklist";
elseif ((ln = {@args, ""}[2]) && (index(listname = $login:listname(ln), ln) == 1))
"...first word of coment is one of the magic words...";
comment = $string_utils:first_word(comment)[2];
else
listname = "";
endif
if ((!player.wizard) || (player != this))
player:notify("Yeah, right... you wish.");
return;
elseif ($command_utils:player_match_failed(who = player:my_match_player(whostr), whostr))
return;
elseif (((whostr != who:name()) && (!(whostr in who.aliases))) && (whostr != tostr(who)))
player:notify(tostr("Must be a full name or an object number:  ", who:name(), "(", who, ")"));
return;
elseif (who == player)
player:notify("If you want to toad yourself, you have to do it by hand.");
return;
elseif (!$command_utils:yes_or_no(("@toad " + who.name) + "?"))
player:notify("Okay, no new @toad... this time.");
return;
endif
dobj = who;
if (msg = player:toad_victim_msg())
notify(who, msg);
endif
if ($wiz_utils:rename_all_instances(who, "disfunc", "toad_disfunc"))
player:notify(tostr(who, ":disfunc renamed."));
endif
if ($wiz_utils:rename_all_instances(who, "recycle", "toad_recycle"))
player:notify(tostr(who, ":recycle renamed."));
endif
e = $wiz_utils:unset_player(who, $hacker);
player:notify(e ? tostr(who:name(), "(", who, ") is now a toad.") | tostr(e));
if (e && ($object_utils:isa(who.location, $room) && (msg = player:toad_msg())))
who.location:announce_all_but({who}, msg);
endif
if (listname && (!$login:(listname + "ed")(cname = $string_utils:connection_hostname(who.last_connect_place))))
$login:(listname + "_add")(cname);
player:notify(tostr("Site ", cname, " ", listname, "ed."));
else
cname = "";
endif
$mail_agent:send_message(player, $toad_log, tostr("@toad ", who:name(), " (", who, ")"), {$string_utils:from_list(who.all_connect_places, " "), @cname ? {$string_utils:capitalize(listname + "ed:  ") + cname} | {}, @comment ? {comment} | {}});
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669214, \"Erik\", #74, \"JHM\"}";
.
#51:8
"@untoad <object> [as namespec]";
"Turns object into a player.  Anything that isn't a guest is chowned to itself.";
if (!player.wizard)
player:notify("Yeah, right... you wish.");
elseif (prepstr && (prepstr != "as"))
player:notify(tostr("Usage:  ", verb, " <object> [as name,alias,alias...]"));
elseif ($command_utils:object_match_failed(dobj, dobjstr))
elseif (prepstr && (!(e = $building_utils:set_names(dobj, iobjstr))))
player:notify(tostr("Initial rename failed:  ", e));
elseif (e = $wiz_utils:set_player(dobj, g = $object_utils:isa(dobj, $guest)))
player:notify(tostr(dobj:name(), "(", dobj, ") is now a ", g ? "usable guest." | "player."));
elseif (e == E_INVARG)
player:notify(tostr(dobj:name(), "(", dobj, ") is not of an appropriate player class."));
player:notify("@chparent it to $player or some descendant.");
elseif (e == E_NONE)
player:notify(tostr(dobj:name(), "(", dobj, ") is already a player."));
elseif (e == E_NACC)
player:notify("Wait until $player_db is finished updating...");
elseif (e == E_RECMOVE)
player:notify(tostr("The name `", dobj:name(), "' is currently unavailable."));
player:notify(tostr("Try again with   ", verb, " ", dobj, " as <newname>"));
else
player:notify(tostr(e));
endif
.
#51:9
"@quota <player> is <number> [<reason>]";
"  changes a player's quota.  sends mail to the wizards.";
set_task_perms(player);
dobj = player:my_match_player(dobjstr);
if ($command_utils:player_match_result(dobj, dobjstr)[1])
return;
elseif (!valid(dobj))
player:notify("Set whose quota?");
return;
endif
qstr = iobjstr[1..(n = index(iobjstr + " ", " ")) - 1];
new = $code_utils:tonum((qstr[1] == "+") ? qstr[2..length(qstr)] | qstr);
old = $quota_utils:get_quota_total(dobj);
if (new < old)
player:notify(tostr("NOTE:  This is less than ", dobj:name(), "'s previous quota of ", old, "."));
endif
reason = (iobjstr[n + 1..length(iobjstr)] || $command_utils:read("a reason for the quota increase")) || "(none)";
if (typeof(new) != NUM)
player:notify(tostr("Set ", dobj.name, "'s quota to what?"));
return;
elseif (qstr[1] == "+")
new = old + new;
endif
result = $quota_utils:set_quota_total(dobj, new);
if (typeof(result) == ERR)
player:notify(tostr(result));
else
player:notify(tostr(dobj:name(), "'s quota set to ", new, "."));
endif
$mail_agent:send_message(player, $quota_log, tostr("@quota ", dobj:name(), " (", dobj, ") ", new, " (from ", old, ")"), tostr("Reason for quota ", ((new - old) < 0) ? "decrease: " | "increase: ", reason, "."));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669223, \"Erik\", #74, \"JHM\"}";
.
#51:10
set_task_perms(player);
now = time();
day = (24 * 60) * 60;
week = 7 * day;
month = 30 * day;
days_objects = days_players = {0, 0, 0, 0, 0, 0, 0};
weeks_objects = weeks_players = {0, 0, 0, 0};
months_objects = months_players = {};
nonplayer_objects = invalid_objects = 0;
always_objects = always_players = 0;
numo = 0;
if (argstr)
if (((!dobjstr) && (prepstr == "with")) && (index("objects", iobjstr) == 1))
with_objects = 1;
else
player:notify(tostr("Usage:  ", verb, " [with objects]"));
return;
endif
else
with_objects = 0;
players = players();
endif
for i in [1..with_objects ? tonum(max_object()) + 1 | length(players)]
if (with_objects)
o = toobj(i - 1);
else
o = players[i];
endif
if ($command_utils:running_out_of_time())
player:notify(tostr("... ", o));
suspend(0);
endif
if (valid(o))
numo = numo + 1;
p = is_player(o) ? o | o.owner;
if (!valid(p))
invalid_objects = invalid_objects + 1;
elseif (!$object_utils:isa(p, $player))
nonplayer_objects = nonplayer_objects + 1;
else
seconds = now - p.last_connect_time;
days = seconds / day;
weeks = seconds / week;
months = seconds / month;
if (seconds < 0)
if (is_player(o))
always_players = always_players + 1;
else
always_objects = always_objects + 1;
endif
elseif (months > 0)
while (months > length(months_players))
months_players = {@months_players, 0};
months_objects = {@months_objects, 0};
endwhile
if (is_player(o))
months_players[months] = months_players[months] + 1;
endif
months_objects[months] = months_objects[months] + 1;
elseif (weeks > 0)
if (is_player(o))
weeks_players[weeks] = weeks_players[weeks] + 1;
endif
weeks_objects[weeks] = weeks_objects[weeks] + 1;
else
if (is_player(o))
days_players[days + 1] = days_players[days + 1] + 1;
endif
days_objects[days + 1] = days_objects[days + 1] + 1;
endif
endif
endif
endfor
player:notify("");
player:notify(tostr("Last connected"));
player:notify(tostr("at least this     Num.     Cumul.   Cumul. %", with_objects ? "     Num.     Cumul.   Cumul. %" | ""));
player:notify(tostr("long ago        players   players   players ", with_objects ? "   objects   objects   objects" | ""));
player:notify(tostr("---------------------------------------------", with_objects ? "--------------------------------" | ""));
su = $string_utils;
col1 = 14;
col2 = 7;
col3 = 10;
col4 = 9;
col5 = 11;
col6 = 11;
col7 = 10;
nump = length(players());
totalp = totalo = 0;
for x in ({{days_players, days_objects, "day", 1}, {weeks_players, weeks_objects, "week", 0}, {months_players, months_objects, "month", 0}})
pcounts = x[1];
ocounts = x[2];
unit = x[3];
offset = x[4];
for i in [1..length(pcounts)]
$command_utils:suspend_if_needed(0);
j = i - offset;
player:notify(tostr(su:left(tostr(j, " ", unit, (j == 1) ? ":" | "s:"), col1), su:right(pcounts[i], col2), su:right(totalp = totalp + pcounts[i], col3), su:right((totalp * 100) / nump, col4), "%", with_objects ? tostr(su:right(ocounts[i], col5), su:right(totalo = totalo + ocounts[i], col6), su:right((totalo * 100) / numo, col7), "%") | ""));
endfor
player:notify("");
endfor
player:notify(tostr(su:left("Always:", col1), su:right(always_players, col2), su:right(totalp = totalp + always_players, col3), su:right((totalp * 100) / nump, col4), "%", with_objects ? tostr(su:right(always_objects, col5), su:right(totalo = totalo + always_objects, col6), su:right((totalo * 100) / numo, col7), "%") | ""));
with_objects && player:notify(tostr(su:left("Non-player owner:", (((col1 + col2) + col3) + col4) + 1), su:right(nonplayer_objects, col5), su:right(totalo = totalo + nonplayer_objects, col6), su:right((totalo * 100) / numo, col7), "%"));
with_objects && player:notify(tostr(su:left("Invalid owner:", (((col1 + col2) + col3) + col4) + 1), su:right(invalid_objects, col5), su:right(totalo = totalo + invalid_objects, col6), su:right((totalo * 100) / numo, col7), "%"));
player:notify("");
.
#51:11
"Auxiliary verb for parsing @kill soon [#-of-seconds] [player | everyone]";
"Args[1] is either # of seconds or player/everyone.";
"Args[2], if it exists, is player/everyone, and forces args[1] to have been # of seconds.";
"Return value: {# of seconds [default 60] , 1 for all, object for player.}";
set_task_perms(caller_perms());
nargs = length(args);
soon = tonum(args[1]);
if (nargs > 1)
everyone = args[2];
elseif (soon <= 0)
everyone = args[1];
else
everyone = 0;
endif
if (everyone == "everyone")
everyone = 1;
elseif (typeof(everyone) == STR)
result = player:my_match_player(everyone);
if ($command_utils:player_match_failed(result, everyone))
player:notify(tostr("Usage:  ", callers()[1][2], " soon [number of seconds] [\"everyone\" | player name]"));
return {-1, -1};
else
return {soon ? soon | 60, result};
endif
endif
return {soon ? soon | 60, everyone ? everyone | player};
"Metadata 202106";
"Last-Modify: {863669276, \"Erik\", #74, \"JHM\"}";
.
#51:12
"@net-who prints all connected users and hosts.";
"@net-who player player player prints specified users and current or most recent connected host.";
"@net-who from hoststring prints all players who have connected from that host or host substring.  Substring can include *'s, e.g. @net-who from *.foo.edu.";
if (player != this)
if ((feature = $community_group.feature) in player:features())
feature:(verb)(@args);
else
player:tell("That is not a valid command.");
endif
return;
endif
set_task_perms(player);
su = $string_utils;
if ((prepstr == "from") && dobjstr)
player:notify(tostr("Usage:  ", verb, " from <host string>"));
elseif (((prepstr != "from") || dobjstr) || (!iobjstr))
"Not parsing 'from' here...  Instead printing connected/recent users.";
footnotes = {};
if (!(pstrs = args))
unsorted = connected_players();
else
unsorted = listdelete($command_utils:player_match_result(su:match_player(pstrs), pstrs), 1);
endif
if (!unsorted)
return;
endif
alist = {};
nwidth = length("Player name");
for u in (unsorted)
pref = u.programmer ? "% " | "  ";
u.programmer && (footnotes = setadd(footnotes, "prog"));
$command_utils:suspend_if_needed(0);
if (u in connected_players())
lctime = ctime(time() - connected_seconds(u));
where = connection_name(u);
else
lctime = ctime(u.last_connect_time);
where = u.last_connect_place;
endif
u3 = {tostr(pref, u:name(), " (", u, ")"), lctime[5..10] + lctime[20..24]};
nwidth = max(length(u3[1]), nwidth);
where = $string_utils:connection_hostname(where);
if ($login:blacklisted(where))
where = "(*) " + where;
footnotes = setadd(footnotes, "black");
elseif ($login:graylisted(where))
where = "(+) " + where;
footnotes = setadd(footnotes, "gray");
endif
alist = {@alist, {@u3, where}};
endfor
alist = $list_utils:sort_alist_suspended(0, alist, 3);
$command_utils:suspend_if_needed(0);
headers = {"Player name", "Last Login", "From Where"};
before = {0, nwidth + 3, (nwidth + length(ctime(0))) - 11};
tell1 = "  " + headers[1];
tell2 = "  " + su:space(headers[1], "-");
for j in [2..3]
tell1 = su:left(tell1, before[j]) + headers[j];
tell2 = su:left(tell2, before[j]) + su:space(headers[j], "-");
endfor
player:notify(tell1);
player:notify(tell2);
active = 0;
for a in (alist)
$command_utils:suspend_if_needed(0);
tell1 = a[1];
for j in [2..3]
tell1 = su:left(tell1, before[j]) + a[j];
endfor
player:notify(tell1[1..min(length(tell1), 79)]);
endfor
if (footnotes)
player:notify("");
if ("prog" in footnotes)
player:notify(" %  == programmer.");
endif
if ("black" in footnotes)
player:notify("(*) == blacklisted site.");
endif
if ("gray" in footnotes)
player:notify("(+) == graylisted site.");
endif
endif
elseif (!index(where = iobjstr, "*"))
"Oh good... search for users from a site... the fast way.  No wild cards.";
nl = 0;
bozos = {};
sites = $site_db:find_all_keys(where);
while (sites)
s = sites;
sites = {};
for domain in (s)
for b in ($site_db:find_exact(domain))
if (typeof(b) == STR)
sites = setadd(sites, (b + ".") + domain);
else
bozos = setadd(bozos, b);
nl = max(length(tostr(b, valid(b) ? b:name() | "")), nl);
endif
endfor
endfor
endwhile
if (bozos)
player:notify(tostr(su:left("  Player", nl + 7), "From"));
player:notify(tostr(su:left("  ------", nl + 7), "----"));
for who in (bozos)
st = su:left(tostr(valid(who) ? (who.programmer ? "% " | "  ") + who:name() | "", " (", who, ")"), nl + 7);
comma = 0;
if ($object_utils:isa(who, $player))
for p in (who.all_connect_places)
if (comma && (length(p) >= (78 - length(st))))
player:notify(tostr(st, ","));
st = su:space(nl + 7) + p;
else
st = tostr(st, comma ? ", " | "", p);
endif
comma = 1;
$command_utils:suspend_if_needed(0);
endfor
else
st = st + (valid(who) ? "*** recreated ***" | "*** recycled ***");
endif
player:notify(st);
endfor
player:notify("");
player:notify(tostr(length(bozos), " player", (length(bozos) == 1) ? "" | "s", " found."));
else
player:notify(tostr("No sites matching `", where, "'"));
endif
else
"User typed 'from'.  Go search for users from this site.  (SLOW!)";
howmany = 0;
for who in (players())
$command_utils:suspend_if_needed(0);
matches = {};
for name in (who.all_connect_places)
if ((index(where, "*") && su:match_string(name, where)) || ((!index(where, "*")) && index(name, where)))
matches = {@matches, name};
endif
endfor
if (matches)
howmany = howmany + 1;
player:notify(tostr(who:name(), " (", who, "): ", su:english_list(matches)));
endif
endfor
player:notify(tostr(howmany || "No", " matches found."));
endif
.
#51:13
"Creates a player.";
"Syntax:  @make-player name[,aliases] [email-address [password]]";
"If no password is given, generates a random password for the player.";
"Email-address is stored in $registration_db and on the player object.";
"Comments can be added by specifying the email address \"email@address comment\"";
if (!player.wizard)
return;
else
return $wiz_utils:do_make_player(@args);
endif
.
#51:14
if (!player.wizard)
player:notify("Sorry.");
elseif (!$code_utils:task_valid($shutdown_task))
player:notify("No server shutdown in progress.");
$shutdown_task = E_NONE;
else
"... Reset time so that $login:check_for_shutdown shuts up...";
kill_task($shutdown_task);
$shutdown_task = E_NONE;
$shutdown_time = time() - 1;
for p in (connected_players())
notify(p, tostr("*** Server shutdown ABORTED by ", player:name(), " (", player, ")", argstr && (":  " + argstr), " ***"));
endfor
endif
.
#51:15
"This is the canonical doing-something-to-somebody message.";
"The corresponding property can either be";
"   string             msg for all occasions";
"   list of 2 strings  {we-are-there-msg,we-are-elsewhere-msg}";
return $string_utils:pronoun_sub(this.(verb));
m = this.(verb);
if (typeof(m) != LIST)
return $string_utils:pronoun_sub(m);
elseif ((this.location == dobj.location) || (length(m) < 2))
return $string_utils:pronoun_sub(m[1]);
else
return $string_utils:pronoun_sub(m[2]);
endif
.
#51:16
set_task_perms((caller in {this, $generic_editor, $verb_editor, $mail_editor, $note_editor, $help_editor}) ? this.owner | caller_perms());
if (!args[1]:accept(this))
notify(this, "Please note that your destination would not have accepted you if you were a non-wizard.");
endif
return move(this, args[1]);
"Metadata 202106";
"Last-Modify: {845673113, \"Ken\", #75, \"JHM\"}";
.
#51:17
"@newt <player> [commentary]";
"";
"Turns a player into a newt.  It can get better...";
"";
"Does a permissions check, then calls $wiz_utils:do_newt to do the legwork and tell the user what happened.";
if (!player.wizard)
return E_PERM;
endif
$wiz_utils:do_newt(@args);
.
#51:18
"@denewt <player> [commentary]";
"";
"Undoes the effects of @newt, sending commentary if given.  See $wiz_utils:do_denewt for details.";
if (!player.wizard)
return E_PERM;
endif
$wiz_utils:do_denewt(@args);
.
#51:19
"@log [<string>]    enters a comment in the server log.";
"If no string is given, you are prompted to enter one or more lines for an extended comment.";
set_task_perms(player);
whostr = tostr("from ", player:name(), " (", player, ")");
if ((!player.wizard) || (player != caller))
player:notify("Yeah, right.");
elseif (argstr)
server_log(tostr("COMMENT: [", whostr, "]  ", argstr));
player:notify("One-line comment logged.");
elseif (lines = $command_utils:read_lines())
server_log(tostr("COMMENT: [", whostr, "]"));
for l in (lines)
server_log(l);
endfor
server_log(tostr("END_COMMENT."));
player:notify(tostr(length(lines), " lines logged as extended comment."));
endif
.
#51:20
set_task_perms(player);
n = (dobjstr == "all") ? 0 | $code_utils:tonum(dobjstr || "20");
if (caller != this)
player:notify("You lose.");
elseif ((n == E_TYPE) && (index("now", dobjstr) != 1))
player:notify(tostr("Usage:  ", verb, " <number>  (where <number> indicates how many entries to look at in the guest log)"));
player:notify(tostr("Usage:  ", verb, " now (to see information about currently connected guests only)"));
elseif ((!dobjstr) || (index("now", dobjstr) != 1))
$guest_log:last(n);
else
"*way* too much copied code in here from @who...  Sorry.  --yduJ";
su = $string_utils;
conn = connected_players();
unsorted = {};
for g in (children($guest))
if (g in conn)
unsorted = {@unsorted, g};
endif
endfor
if (!unsorted)
player:tell("No guests found.");
return;
endif
footnotes = {};
alist = {};
nwidth = length("Player name");
for u in (unsorted)
pref = u.programmer ? "% " | "  ";
u.programmer && (footnotes = setadd(footnotes, "prog"));
u3 = {tostr(pref, u:name(), " (", u, ")"), su:from_seconds(connected_seconds(u)), su:from_seconds(idle_seconds(u)), where = $string_utils:connection_hostname(connection_name(u))};
nwidth = max(length(u3[1]), nwidth);
if ($login:blacklisted(where))
where = "(*) " + where;
footnotes = setadd(footnotes, "black");
elseif ($login:graylisted(where))
where = "(+) " + where;
footnotes = setadd(footnotes, "gray");
endif
alist = {@alist, u3};
endfor
alist = $list_utils:sort_alist_suspended(0, alist, 3);
$command_utils:suspend_if_needed(0);
headers = {"Player name", "Connected", "Idle Time", "From Where"};
time_width = length("59 minutes") + 2;
before = {0, w1 = nwidth + 3, w2 = w1 + time_width, w3 = w2 + time_width};
tell1 = "  " + headers[1];
tell2 = "  " + su:space(headers[1], "-");
for j in [2..4]
tell1 = su:left(tell1, before[j]) + headers[j];
tell2 = su:left(tell2, before[j]) + su:space(headers[j], "-");
endfor
player:notify(tell1);
player:notify(tell2);
active = 0;
for a in (alist)
$command_utils:suspend_if_needed(0);
tell1 = a[1];
for j in [2..4]
tell1 = su:left(tell1, before[j]) + tostr(a[j]);
endfor
player:notify(tell1[1..min(length(tell1), 79)]);
endfor
if (footnotes)
player:notify("");
if ("prog" in footnotes)
player:notify(" %  == programmer.");
endif
if ("black" in footnotes)
player:notify("(*) == blacklisted site.");
endif
if ("gray" in footnotes)
player:notify("(+) == graylisted site.");
endif
endif
endif
.
#51:21
set_task_perms(valid(caller_perms()) ? caller_perms() | player);
use = this.mail_identity;
if (valid(use) && (use != this))
return use:(verb)(@args);
else
return pass(@args);
endif
.
#51:22
"@[un]blacklist [<site or subnet>  [commentary]]";
"@[un]graylist  [<site or subnet>  [commentary]]";
"@[un]redlist   [<site or subnet>  [commentary]]";
"@[un]spooflist [<site of subnet>  [commentary]]";
set_task_perms(player);
if ((player != this) || (!player.wizard))
player:notify("Ummm.  no.");
return;
endif
undo = verb[2..3] == "un";
which = $login:listname(verb[undo ? 4 | 2]);
downgrade = {"", "graylist", "blacklist"}[1 + index("br", which[1])];
if (!(fw = $string_utils:first_word(argstr)))
"... Just print the list...";
slist = {};
if (s = $login.(which)[1])
slist = {@slist, "--- Subnets ---", @s};
endif
if (s = $login.(which)[2])
slist = {@slist, "--- Domains ---", @s};
endif
if (slist)
player:notify_lines($string_utils:columnize(slist, 3));
else
player:notify(tostr("The ", which, " is empty."));
endif
return;
endif
target = fw[1];
comment = fw[2] ? {fw[2]} | {};
if (is_literal = $site_db:domain_literal(target))
if (target[l = length(target)] == ".")
target = target[1..l - 1];
endif
fullname = "subnet " + target;
else
if (target[1] == ".")
target[1..1] = "";
endif
fullname = ("domain `" + target) + "'";
endif
rm = {};
entrylist = $login.(which)[1 + (!is_literal)];
if ((!undo) && (target in entrylist))
player:notify(tostr(fullname, " is already ", which, "ed."));
return;
endif
entrylist = setremove(entrylist, target);
confirm = 0;
if (is_literal)
for s in (entrylist)
if ((i = index(s, target + ".")) == 1)
"... target is a prefix of s, s should probably go...";
rm = {@rm, s};
elseif (index(target + ".", s + ".") != 1)
"... s is not a prefix of target...";
elseif (undo)
player:notify(tostr("You will need to un", which, " subnet ", s, " as well."));
elseif (confirm)
player:notify(tostr("...Subnet ", s, " already ", which, "ed..."));
else
player:notify(tostr("Subnet ", s, " already ", which, "ed."));
if (!(confirm = $command_utils:yes_or_no(tostr(which, " ", target, " anyway?"))))
return;
endif
endif
endfor
else
for s in (entrylist)
if ((i = rindex(s, "." + target)) && (i == (length(s) - length(target))))
"... target is a suffix of s, s should probably go...";
rm = {@rm, s};
elseif ((!(i = rindex("." + target, "." + s))) || (i < ((length(target) - length(s)) + 1)))
"... s is not a suffix of target...";
elseif (undo)
player:notify(tostr("You will need to un", which, " domain `", s, "' as well."));
elseif (confirm)
player:notify(tostr("...Domain `", s, "' already ", which, "ed..."));
else
player:notify(tostr("Domain `", s, "' already ", which, "ed."));
if (!(confirm = $command_utils:yes_or_no(tostr(which, " ", target, " anyway?"))))
return;
endif
endif
endfor
endif
namelist = $string_utils:english_list(rm);
downgraded = {};
if (rm)
ntries = (length(rm) == 1) ? "ntry" | "ntries";
if ($command_utils:yes_or_no(tostr("Remove e", ntries, " for ", namelist, "?")))
dg = undo && (downgrade && $command_utils:yes_or_no(downgrade + " them?"));
for s in (rm)
$login:(which + "_remove")(s);
dg && ($login:(downgrade + "_add")(s) && (downgraded = {@downgraded, s}));
endfor
player:notify(tostr("E", ntries, " removed", @dg ? {" and ", downgrade, "ed."} | {"."}));
else
player:notify(tostr(namelist, " will continue to be ", which, "ed."));
rm = {};
endif
endif
if (downgraded)
comment[1..0] = {tostr(downgrade, "ed ", $string_utils:english_list(downgraded), ".")};
endif
if (!undo)
$login:(which + "_add")(target);
player:notify(tostr(fullname, " ", which, "ed."));
if (rm)
comment[1..0] = {tostr("Subsumes ", which, "ing for ", namelist, ".")};
endif
elseif ($login:(which + "_remove")(target))
player:notify(tostr(fullname, " un", which, "ed."));
if (!downgrade)
elseif ($command_utils:yes_or_no(downgrade + " it?"))
$login:(downgrade + "_add")(target) && (downgraded = {target, @downgraded});
player:notify(tostr(fullname, " ", downgrade, "ed."));
else
player:notify(tostr(fullname, " not ", downgrade, "ed."));
endif
if (downgraded)
comment[1..0] = {tostr(downgrade, "ed ", $string_utils:english_list(downgraded), ".")};
endif
if (rm)
comment[1..0] = {tostr("Also removed ", namelist, ".")};
endif
elseif (rm)
player:notify(tostr(fullname, " itself was never actually ", which, "ed."));
comment[1..0] = {tostr("Removed ", namelist, ".")};
else
player:notify(tostr(fullname, " was not ", which, "ed before."));
return;
endif
subject = tostr(undo ? "@un" | "@", which, " ", fullname);
$mail_agent:send_message(player, $site_log, subject, comment);
"...";
"... make sure we haven't screwed ourselves...";
uhoh = {};
for site in (player.all_connect_places)
if (index(site, target) && $login:(which + "ed")(site))
uhoh = {@uhoh, site};
endif
endfor
if (uhoh)
player:notify(tostr("WARNING:  ", $string_utils:english_list(uhoh), " are now ", which, "ed!"));
endif
.
#51:23
"@copyo*bject fromobj [to toobj]";
"makes toobj an identical copy of fromobj (including property/verb ownerships)";
"if toobj isn't given, we create an object.";
"The only things that are not copied:  the player flag, .programmer, .wizard, .owner, .contents, .location, ownership of rc properties.";
set_task_perms(player);
if ($command_utils:object_match_failed(dobj = player:my_match_object(dobjstr), dobjstr))
return;
elseif (!prepstr)
iobj = $recycler:_create(dobj);
if (typeof(iobj) == ERR)
player:tell("Couldn't create copy: ", iobj);
return;
endif
elseif (prepstr != "to")
player:tell("Usage:  ", verb, " <object> [to <object2>]");
return;
elseif ($command_utils:object_match_failed(iobj = player:my_match_object(iobjstr), iobjstr))
return;
elseif (!(e = $building_utils:recreate(iobj, dobj)))
player:tell("Couldn't recreate ", iobj, ": ", e);
return;
endif
chparent(iobj, parent(dobj));
for p in ({"name", "r", "w", "f"})
iobj.(p) = dobj.(p);
endfor
for p in ($object_utils:all_properties(parent(dobj)))
if (!is_clear_property(dobj, p))
iobj.(p) = dobj.(p);
endif
endfor
for p in (properties(dobj))
add_property(iobj, p, dobj.(p), property_info(dobj, p));
endfor
for v in [1..length(verbs(dobj))]
add_verb(iobj, verb_info(dobj, v), verb_args(dobj, v));
set_verb_code(iobj, v, verb_code(dobj, v));
endfor
player:tell(iobj, " made into a clone of ", dobj:name(), "(", dobj, ")");
.
#51:24
"Usage:  @corify <object> as <propname>";
"Adds <object> to the core, as $<propname>";
"Reminds the wizard to write an :init_for_core verb, if there isn't one already.";
if (!player.wizard)
player:tell("Sorry, the core is wizardly territory.");
return E_PERM;
endif
if (dobj == $failed_match)
dobj = player:my_match_object(dobjstr);
endif
if ($command_utils:object_match_failed(dobj, dobjstr))
return;
endif
if (!iobjstr)
player:tell("Usage:  @corify <object> as <propname>");
return;
elseif (iobjstr[1] == "$")
iobjstr = iobjstr[2..length(iobjstr)];
endif
if (typeof(e = add_property(#0, iobjstr, dobj, {player, "r"})) == ERR)
player:tell(e);
return;
endif
player:Tell(dobj:dnamec(), " corified as $", iobjstr, ".");
if (!("init_for_core" in verbs(dobj)))
player:tell(dobj:dnamec(), " has no :init_for_core verb.  Strongly consider adding one before doing anything else.");
endif
.
#51:25
"Usage:  @make-guest <guestname>";
"";
"Creates a player called <guestname>_guest (the <guestname> guest) owned by $hacker and a child of $guest.";
if (!args)
player:Tell("Usage: ", verb, " <guest name>");
return;
endif
adj = args[1];
if (adj == "guest")
guestname = "guest";
guestaliases = {"guest"};
else
if (s = match(adj, "^%(.*%)[ _]guest$"))
adj = substitute("%1", s);
endif
guestname = adj + "_guest";
guestaliases = {guestname, adj, adj + " guest"};
endif
if (!player.wizard)
return player:tell("Sorry, you can't create guests.");
elseif (typeof(guest = $wiz_utils:do_make_guest(guestname, guestaliases)) == STR)
player:tell(guest);
else
player:tell("Created ", guest:iname(), " (", guest, ").", (guestname == "guest") ? "" | tostr("  Now don't forget to @describe ", guest:po(), " as something."));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835367395, \"Erik\", #74, \"JHM\"}";
.
#51:26
if (player != this)
player:tell("Sorry, not even a good try.");
return;
endif
blokestr = $string_utils:first_word(argstr)[1];
bloke = $nothing;
if ((!valid(bloke)) || (!is_player(bloke)))
bloke = player:my_match_player(blokestr);
endif
if (!valid(bloke))
player:tell("I can't find that player, or it's ambiguous.");
return;
endif
if (!(bloke in connected_players()))
player:tell(bloke:namec(), " isn't connected.");
return;
endif
if (player == bloke)
player:tell("Try \"@quit\".");
return;
endif
comment = $string_utils:first_word(argstr)[2];
if (!comment)
comment = "(No reason given.)";
endif
$mail_agent:send_message(player, $toad_log, tostr("@boot ", bloke:name(), " (", bloke, ")"), {$string_utils:from_list(bloke.all_connect_places, " "), @comment ? {comment} | {}});
boot_player(bloke);
dobj = bloke;
$you:say_action(this.boot_player_msg);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669350, \"Erik\", #74, \"JHM\"}";
.
#51:27
"Generates a report on the characters who have not been active lately.";
"This needs work:  Need to rename the verbs so we have the following categories:";
" Non-programmers";
" Programmers with no owned objects";
" Programmers who own stuff";
"Usage:";
" @idlers months -- all players over";
" @kill-idlers months";
" @useless months";
" @kill-useless months";
" @kill-unconnected months";
if (!(callers() ? caller_perms() | player).wizard)
return player:tell(E_PERM);
endif
if ((months = tonum(dobjstr)) < 1)
player:tell(dobjstr, " not a good amount.");
return;
endif
threshold = time() - ((((months * 31) * 24) * 60) * 60);
"one month of seconds ago";
flags = {0};
player:tell("Report of players ", months, " months idle");
act = (verb[1..5] == "@kill") ? "@zap" | "@report";
for x in (players())
if ($object_utils:isa(x, $player))
if (x in $wiz_utils.dont_reap)
doit = 0;
elseif (x.programmer)
doit = 0;
elseif (x.last_connect_time == $maxint)
"never connected -- created before threshold?";
doit = x.last_disconnect_time < threshold;
elseif (verb in {"@kill-unconnected", "@unconnected"})
doit = 0;
elseif (x.last_disconnect_time > threshold)
doit = 0;
elseif (verb in {"@kill-useless", "@useless"})
doit = (x.owned_objects == {x}) && (!x.programmer);
else
doit = 1;
endif
$command_utils:suspend_if_needed(2);
if (doit)
this:(act)(tostr(x));
endif
endif
endfor
player:tell("Done with report.");
"Copied from generic wizard (#57):@idlers by Erik (#74) Sun Nov 13 14:04:40 1994 EST";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {913134420, \"core initialization\", #3023, \"core\"}";
.
#51:28
if ((!(callers() ? caller_perms() | player).wizard) || (player != this))
return notify(player, tostr(E_PERM, caller_perms()));
endif
who = player:my_match_player(args[1]);
if (who == $ambiguous_match)
notify(player, tostr(who, " is ambiguous."));
return;
endif
if (who == $failed_match)
notify(player, tostr(who, " is not a player."));
return;
endif
if (tonum(who) < 0)
notify(player, tostr(who, " is unknown."));
return;
endif
notify(player, tostr($string_utils:center(((("-- " + who.name) + " (") + tostr(who)) + ")", 78, "-")));
if (is_clear_property(who, "description"))
notify(player, tostr("*****NO DESCRIPTION"));
endif
$command_utils:suspend_if_needed(5);
if (who.last_connect_time == $maxint)
$command_utils:suspend_if_needed(5);
notify(player, tostr(">> ****NEVER CONNECTED, CREATED :", $time_utils:english_time(time() - who.last_disconnect_time), " ago"));
else
notify(player, tostr(">> ", $time_utils:english_time(time() - who.last_connect_time)));
endif
notify(player, tostr(">> ", ctime(who.last_connect_time), " -- ", ctime(who.last_disconnect_time)));
notify(player, tostr(">> ", who.email_address));
$command_utils:suspend_if_needed(0);
this:pros(dobjstr = tostr(dobj = who));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669356, \"Erik\", #74, \"JHM\"}";
.
#51:29
if (!dobjstr)
player:tell("Immortal players:");
player:tell($string_utils:names_of($wiz_utils.dont_reap));
else
who = player:my_match_player(dobjstr);
if ($command_utils:player_match_failed(who, dobjstr))
elseif (who in $wiz_utils.dont_reap)
player:tell(who:dnamec(), " is already immortal.");
else
$wiz_utils.dont_reap = setadd($wiz_utils.dont_reap, who);
player:tell(who:dnamec(), " will not be reaped by any automatic mechanism.");
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669361, \"Erik\", #74, \"JHM\"}";
.
#51:30
"@zap player";
"This must be +x, because it's called by @kill-*.";
whostr = args[1];
if (!(callers() ? caller_perms() | player).wizard)
player:notify("Yeah, right... you wish.");
return;
elseif ($command_utils:player_match_failed(who = player:my_match_player(whostr), whostr))
return;
elseif (((whostr != who.name) && (!(whostr in who.aliases))) && (whostr != tostr(who)))
player:notify(tostr("Must be a full name or an object number:  ", who.name, "(", who, ")"));
return;
elseif (who == player)
player:notify("If you want to toad yourself, you have to do it by hand.");
return;
endif
this:("@report")(tostr(dobj = who));
if ((time() - who.last_connect_time) < ((((3 * 30) * 24) * 60) * 60))
player:tell("***** LESS THAN 3 MONTHS IDLE ****");
endif
if ($command_utils:yes_or_no(("@toad " + who.name) + "?"))
if (((email = who.email_address) && (current = $registration_db:find_exact(email))) && (ind = $list_utils:iassoc(who, current)))
current[ind] = {@current[ind], "zapped due to inactivity"};
$registration_db:insert(email, current);
endif
for x in (setremove(who.owned_objects, who))
player:tell("x ", $building_utils:object_audit_string(x));
$command_utils:suspend_if_needed(0);
$recycler:_recycle(x);
endfor
$wiz_utils:unset_player(who);
player:tell("x ", $building_utils:object_audit_string(who));
$recycler:_recycle(who);
player:tell(whostr, " zapped!");
else
player:tell(whostr, " saved!");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669366, \"Erik\", #74, \"JHM\"}";
.
#51:31
"does a raw set_property_info() on the named property.";
if ((!player.wizard) || (player != this))
player:notify("Sorry.");
return;
endif
set_task_perms(player);
if (length(args) != 2)
player:notify(tostr("Usage:  ", verb, " <object>.<property> <permissions>"));
return;
endif
{what, perms} = args;
if (spec = $code_utils:parse_verbref(what))
player:notify("Use @chmod.");
return;
elseif (index(what, ".") && (spec = $code_utils:parse_propref(what)))
if (valid(object = player:my_match_object(spec[1])))
pname = spec[2];
try
{owner, oldperms} = property_info(object, pname);
perms = $perm_utils:apply(oldperms, perms, "rwc");
set_property_info(object, pname, {owner, perms});
except (E_PROPNF)
player:notify("That object does not have that property.");
return;
except (E_INVARG)
"can only happen on set_property_info ";
"since object is known to be valid";
if (!valid(owner))
player:notify(tostr("Property is currently owned by ", owner, " <invalid>.  It needs to be @chowned to someone real."));
else
player:notify(tostr("\"", perms, "\" is not a valid permissions string for a property."));
endif
return;
except e (ANY)
player:notify(e[2]);
return;
endtry
player:notify(tostr("Property permissions set to \"", perms, "\"."));
endif
else
player:notify("Use @chmod.");
return;
endif
.
#51:32
set_task_perms(player);
if (!args)
player:notify(tostr("Usage:  ", verb, " <pattern>"));
return;
endif
pattern = argstr;
regexp = verb[2] == "e";
player:notify(tostr("Searching for core verbs ", regexp ? "matching the regular expression " | "containing the string ", $string_utils:print(pattern), " ..."));
player:notify("");
$code_utils:(regexp ? "find_verbs_matching" | "find_verbs_containing")(pattern, $list_utils:sort($nominate_for_core()));
"Copied from generic wizard (#57):@grepcore by Ken (#75) Fri Oct 28 18:47:11 1994 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863650981, \"Erik\", #74, \"JHM\"}";
.
#51:33
"A verb to automate the creation of the core database.";
if (callers() || (!player.wizard))
return E_PERM;
elseif (args == {})
player:tell("Continuing with this command will destroy all but the central core of the database.  If you're really sure that you want to do this, type '", verb, " ", tonum(max_object()), "' now.");
return;
elseif (toobj(tonum(args[1])) != max_object())
player:tell("Nice try, but you mistyped the self-destruct password.  Type '", verb, "' again to get a new password.");
return;
elseif (verb_info($wiz, verb)[1] != player)
player:tell("Sorry, but you must own this verb in order to use it.");
return;
endif
"----------------------------------------";
if (connected_players() != {this})
player:notify("Booting all other connected players in 30 seconds ...");
for i in (setremove(connected_players(), this))
i:notify(tostr("*** You will be booted in 30 seconds: ", this:name("i"), " (", this, ") ", this:verb_sub("is"), " making a core DB."));
endfor
suspend(25);
for i in (setremove(connected_players(), this))
i:notify(tostr("*** You will be booted in 5 seconds: ", this:name("i"), " (", this, ") ", this:verb_sub("is"), " making a core DB."));
endfor
suspend(5);
if (player in connected_players())
player:notify("Booting all other players NOW ...");
for i in (setremove(connected_players(), this))
boot_player(i);
endfor
endif
endif
"----------------------------------------";
player:notify("Collecting the list of core objects ...");
saved = $core_objects();
suspend(0);
"----------------------------------------";
player:notify("Killing all queued tasks ...");
for t in (queued_tasks())
kill_task(t[1]);
endfor
"----------------------------------------";
player:notify("Making some saved player the owner of every saved object, verb and property ...");
for i in [1..length(saved)]
if ($command_utils:running_out_of_time())
suspend(0);
player:notify(tostr("... finished ", i - 1, " out of ", length(saved), " saved objects ..."));
endif
o = saved[i];
if ((!(o.owner in saved)) || (o.owner == $first_wizard))
o.owner = (valid(o.owner) && o.owner.wizard) ? $core_wizard | $hacker;
endif
old_verbs = {};
for vname in [1..length(verbs(o))]
if ((seconds_left() < 2) || (ticks_left() < 2000))
suspend(0);
player:notify(tostr("... finished ", i - 1, " out of ", length(saved), " saved objects ..."));
endif
info = verb_info(o, vname);
if ((!(info[1] in saved)) || (info[1] == $first_wizard))
info[1] = (valid(info[1]) && info[1].wizard) ? $core_wizard | $hacker;
endif
set_verb_info(o, vname, info);
endfor
for p in ($object_utils:all_properties(o))
if ((seconds_left() < 2) || (ticks_left() < 2000))
suspend(0);
player:notify(tostr("... finished ", i - 1, " out of ", length(saved), " saved objects ..."));
endif
info = property_info(o, p);
if ((!(info[1] in saved)) || (info[1] == $first_wizard))
info = listset(info, (valid(info[1]) && info[1].wizard) ? $core_wizard | $hacker, 1);
endif
set_property_info(o, p, info);
endfor
"Save renumbering from itself, or so we hope.";
"All but the first of these lines should be unnecessary now, but it never hurts to be safe.";
$first_wizard.owner = $first_wizard;
set_verb_info($wiz, "@make-core-db", listset(verb_info($wiz, "@make-core-db"), #2, 1));
set_verb_info($wiz, "mcd_2", listset(verb_info($wiz, "mcd_2"), #2, 1));
set_verb_info($wiz, "renumber_for_core", listset(verb_info($wiz, "renumber_for_core"), #2, 1));
endfor
"----------------------------------------";
player:notify("Removing all unsaved :recycle and :exitfunc verbs ...");
for i in [0..tonum(max_object())]
o = toobj(i);
if (i && ((i % 100) == 0))
player:notify(tostr("... ", o));
endif
$command_utils:suspend_if_needed(0);
if (valid(o) && (!(o in saved)))
for v in ({"recycle", "exitfunc"})
while ((hv = $object_utils:has_verb(o, v)) && (hv[1] == o))
delete_verb(o, v);
endwhile
endfor
endif
endfor
player:tell("Recycling unsaved objects ...");
add_property(this, "mcd_pos", 0, {player, "r"});
suspend(0);
this:mcd_2(saved);
"Copied from James (#2142):@make-core-db Sun Nov  6 20:52:54 1994 EST";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878516395, \"Ken\", #75, \"JHM\"}";
.
#51:34
"Renumbering verb.  Hopefully handles all properties now.  Assumes #2.wizard==1.";
if (!caller_perms().wizard)
return E_PERM;
endif
"Since running task perms don't get renumbered...";
set_task_perms(#2);
old_numbers = {#0, #1, #2};
for i in [3..tonum(max_object())]
j = toobj(i);
if (valid(j))
k = renumber(j);
if (j == player)
player = k;
endif
if (tonum(k) != length(old_numbers))
notify(#2, "This isn't logically possible, you know.");
notify(#2, "Oh yeah, and the core is broken.  Ow.  BIG CRASH.");
notify(#2, "GAME OVER TRY AGAIN THANKS FOR PLAYING");
return E_INVARG;
endif
old_numbers = {@old_numbers, j};
if ((ticks_left() < 2000) || (seconds_left() < 2))
suspend(0);
endif
endif
endfor
reset_max_object();
"All renumbered, now it's time to HAVE FUN WITH PROPERTIES.";
you_are_invalid = toobj(tonum(max_object()) + 1);
for i in [0..tonum(max_object())]
it = j = toobj(i);
props = properties(it);
while (valid(it = parent(it)))
props = {@properties(it), @props};
endwhile
for p in (props)
if (!is_clear_property(j, p))
stack = {};
work = {j.(p)};
done = {};
while (work || stack)
while (work)
if ((seconds_left() < 2) || (ticks_left() < 4000))
suspend(0);
endif
item = work[1];
work = listdelete(work, 1);
if (typeof(item) == OBJ)
if (tonum(item) >= 0)
if (newnum = item in old_numbers)
done = {@done, toobj(newnum - 1)};
else
"BEEP!  YOU ARE INVALID.";
notify(#2, tostr("Invalid object (", item, ") found in ", old_numbers[i + 1], ".", p, ".  Please check ", old_numbers[i + 1], ":init_for_core() for errors before the next core build."));
done = {@done, you_are_invalid};
endif
else
done = {@done, item};
endif
elseif (typeof(item) == LIST)
stack = {{work, done}, @stack};
work = item;
done = {};
else
done = {@done, item};
endif
endwhile
if (stack)
work = stack[1][1];
done = {@stack[1][2], done};
stack = listdelete(stack, 1);
endif
endwhile
j.(p) = done[1];
endif
endfor
endfor
"Copied from James (#2142):renumber_for_core Sun Nov  6 20:55:06 1994 EST";
.
#51:35
"Usage:  @check-all-owned-objects";
"";
"Verify that everyone's .owned_objects list is correct.";
if (player != this)
return player:tell("That is not a valid command.");
else
player:tell("Checking ownership lists...");
$quota_utils:verify_owned_objects();
player:tell("Finished checking ownership lists.");
endif
.
#52:0
if (!player.programmer)
player:tell("Sorry, you have to be a programmer to use that command.");
return E_PERM;
elseif (!(e = $quota_utils:property_addition_permitted(player)))
if (e == E_MAXREC)
player:tell("Property addition not permitted:  Too many unmeasured objects.");
else
player:tell("Property addition not permitted:  Quota exceeded.");
endif
return;
endif
set_task_perms(player);
nargs = length(args);
usage = tostr("Usage:  ", verb, " <object>.<prop-name> [<init_value> [<perms> [<owner>]]]");
if ((nargs < 1) || (!(spec = $code_utils:parse_propref(args[1]))))
player:notify(usage);
return;
endif
object = player:my_match_object(spec[1]);
name = spec[2];
if ($command_utils:object_match_failed(object, spec[1]))
return;
endif
if (nargs < 2)
value = 0;
else
q = $string_utils:prefix_to_value(argstr[$string_utils:word_start(argstr)[2][1]..length(argstr)]);
if (q[1] == 0)
player:notify(tostr("Syntax error in initial value:  ", q[2]));
return;
endif
value = q[2];
args = {args[1], value, @$string_utils:words(q[1])};
nargs = length(args);
endif
perms = (nargs < 3) ? "rc" | args[3];
if (nargs < 4)
owner = player;
else
owner = player:my_match_player(args[4]);
if ($command_utils:player_match_result(owner, args[4])[1])
return;
endif
endif
if (nargs > 4)
player:notify(usage);
return;
endif
e = add_property(object, name, value, {owner, perms});
if (typeof(e) != ERR)
player:notify(tostr("Property added with value ", $string_utils:print(object.(name), 1), "."));
elseif (e != E_INVARG)
player:notify(tostr(e));
elseif ($object_utils:has_property(object, name))
player:notify(tostr("Property ", object, ".", name, " already exists."));
else
for i in [1..length(perms)]
if (!index("rcw", perms[i]))
player:notify(tostr("Unknown permission bit:  ", perms[i]));
return;
endif
endfor
"...the only other possibility...";
player:notify("Property is already defined on one or more descendents.");
player:notify(tostr("Try @check-prop ", args[1]));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669410, \"Erik\", #74, \"JHM\"}";
.
#52:1
set_task_perms(player);
if (length(args) != 2)
player:notify(tostr("Usage:  ", verb, " <object-or-property-or-verb> <permissions>"));
return;
endif
{what, perms} = args;
bynumber = verb[$] == "#";
if (spec = $code_utils:parse_verbref(what))
if (!player.programmer)
player:notify("You need to be a programmer to do this.");
player:notify("Read `help programmer-policy' to see how to become a programmer.");
return;
endif
if (valid(object = player:my_match_object(spec[1])))
vname = spec[2];
if (bynumber && ((E_TYPE == (vname = $code_utils:tonum(vname))) || (vname < 1)))
player:notify("Verb number expected.");
return;
endif
try
{owner, oldperms, names} = verb_info(object, vname);
perms = $perm_utils:apply(oldperms, perms, "rwxd");
except (E_VERBNF)
player:notify(bynumber ? "Verb number out of range" | "That object does not define that verb.");
return;
except e (ANY)
player:notify(e[2]);
return;
endtry
if (!valid(owner))
player:notify(tostr("That verb is owned by an invalid object (", owner, "); it needs to be @chowned."));
elseif (!is_player(owner))
player:notify(tostr("That verb is owned by a non-player object (", owner:name(), ", ", owner, "); it needs to be @chowned."));
elseif (index(perms, "w"))
player:notify("That would allow anyone to change your verb.");
else
try
set_verb_info(object, vname, {owner, perms, names});
except (E_INVARG)
player:notify(tostr("\"", perms, "\" is not a valid permissions string for a verb."));
return;
except e (ANY)
player:notify(e[2]);
return;
endtry
player:notify(tostr("Verb permissions set to \"", perms, "\"."));
endif
return;
endif
elseif (bynumber)
player:notify(tostr(verb, " can only be used for verbs."));
return;
elseif (index(what, ".") && (spec = $code_utils:parse_propref(what)))
if (valid(object = player:my_match_object(spec[1])))
pname = spec[2];
if (`property_info(parent(object), pname) ! ANY')
player:notify("Property permissions can be changed only on the object that defines the property.");
return;
endif
try
{owner, oldperms} = property_info(object, pname);
perms = $perm_utils:apply(oldperms, perms, "rwc");
except (E_PROPNF)
player:notify("That object does not have that property.");
return;
except e (ANY)
player:notify(e[2]);
return;
endtry
result = $wiz_utils:set_property_flags(object, pname, perms);
if (result == E_INVARG)
player:notify(tostr("\"", perms, "\" is not a valid permissions string for a property."));
elseif (typeof(result) == ERR)
player:notify(tostr(result));
else
player:notify(tostr("Property permissions set to \"", perms, "\"."));
endif
return;
endif
elseif (valid(object = player:my_match_object(what)))
try
perms = $perm_utils:apply(tostr(object.r ? "r" | "", object.w ? "w" | "", object.f ? "f" | ""), perms, "rwf");
object.r = index(perms, "r");
object.w = index(perms, "w");
object.f = index(perms, "f");
except (E_PERM)
player:notify("Permission denied.");
return;
except e (E_INVARG)
player:notify(e[2]);
return;
endtry
player:notify(tostr("Object permissions set to \"", perms, "\"."));
return;
endif
$command_utils:object_match_failed(object, what);
.
#52:2
if (player != caller)
return;
endif
set_task_perms(player);
bynumber = verb == "@args#";
if (!(args && (spec = $code_utils:parse_verbref(verbref = args[1]))))
player:notify(tostr(args ? ("\"" + args[1]) + "\"?  " | "", "<object>:<verb>  expected."));
elseif ($command_utils:object_match_failed(object = player:my_match_object(({oname, name} = spec)[1]), oname))
"...can't find object...";
elseif (bynumber && (E_TYPE == (name = $code_utils:tonum(name))))
player:notify("Verb number expected.");
elseif (bynumber && ((name < 1) || `name > length(verbs(object)) ! E_PERM => 0'))
player:notify("Verb number out of range.");
elseif ((info = verb_args(object, name)) == E_VERBNF)
player:notify("That object does not have a verb with that name.");
elseif (typeof(info) == ERR)
player:notify(tostr(info));
elseif (typeof(pas = $code_utils:parse_argspec(@listdelete(args, 1))) != LIST)
"...arg spec is bogus...";
player:notify(tostr(pas));
elseif (!(newargs = pas[1]))
if ((info == {"this", "none", "this"}) && (named = $code_utils:named_args_from_code(verb_code(object, name))))
arglist = tostr("(", $code_utils:named_args_list(named), ")");
else
arglist = $string_utils:from_list(info, " ");
endif
player:notify(tostr("@args ", verbref, " ", arglist));
elseif (pas[2])
player:notify(tostr("\"", pas[2][1], "\" unexpected."));
else
oldinfo = info;
info[2] = info[2][1..index(info[2] + "/", "/") - 1];
newargs = newargs[1..min(length(newargs), 3)];
info = {@newargs, @info[length(newargs) + 1..length(info)]};
result = set_verb_args(object, name, info);
if (result == E_INVARG)
player:notify(tostr("\"", info[2], "\" is not a valid preposition (?)"));
elseif (typeof(result) == ERR)
player:notify(tostr(result));
else
changedargs = verb_args(object, name) != oldinfo;
if (length(pas[1]) > 3)
newnamed = pas[1][4..length(pas[1])];
code = verb_code(object, name);
named = (info == {"this", "none", "this"}) ? $code_utils:named_args_from_code(code) | 0;
if (named != newnamed)
split = $code_utils:split_verb_code(code);
if (named)
split[2][1..length(named[1]) + (named[2] ? 1 | 0)] = {};
usage = $code_utils:verb_usage(object, name);
if (usage)
namedreg = tostr("^\"%(.*%)(", $code_utils:named_args_list(named), ")\";$");
newnamedsub = tostr("^\"%1(", $code_utils:named_args_list(newnamed), ")\";");
newusage = {};
for ln in [1..length(usage)]
if (s = match(split[1][ln], namedreg))
newusage = {@newusage, substitute(newnamedsub, s)};
else
newusage = {@newusage, E_INVARG};
endif
endfor
if (!(E_INVARG in newusage))
split[1][1..length(usage)] = newusage;
endif
endif
endif
set_verb_code(object, name, {@split[1], @$code_utils:named_args_to_code(newnamed), @split[2]});
player:notify(tostr(changedargs ? "Verb arguments changed and named args " | "Named args ", named ? "changed." | "added."));
return;
endif
endif
player:notify(changedargs ? "Verb arguments changed." | "Verb arguments unchanged.");
endif
endif
.
#52:3
set_task_perms(player);
if ($command_utils:object_match_failed(object = player:my_match_object(dobjstr), dobjstr))
"...bogus object...";
elseif ($command_utils:object_match_failed(parent = player:my_match_object(iobjstr), iobjstr))
"...bogus new parent...";
"elseif (object == player && !player.programmer)";
"  player:notify(\"You need to be a programmer to do this.\");";
"  player:notify(\"If you want to become a programmer, talk to a wizard.\");";
elseif ((this != player) && (!$object_utils:isa(player, $player)))
"...They chparented to #1 and want to chparent back to $prog.  Probably for some nefarious purpose...";
player:notify("You don't seem to already be a valid player class.  Perhaps chparenting away from the $player hierarchy was not such a good idea.  Permission denied.");
elseif (is_player(object) && (!$object_utils:isa(parent, $player)))
player:notify(tostr(object, " is a player and ", parent, " is not a player class."));
player:notify("You really *don't* want to do this.  Trust me.");
elseif (typeof(result = `chparent(object, parent) ! ANY') != ERR)
player:notify("Parent changed.");
elseif ((result == E_INVARG) && (valid(object) && valid(parent)))
player:notify(tostr("Some property existing on ", parent, " is defined on ", object, " or one of its descendants."));
player:notify(tostr("Try @check-chparent ", dobjstr, " to ", iobjstr));
else
player:notify(tostr(result));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863666059, \"Erik\", #74, \"JHM\"}";
.
#52:4
"A MOO-code evaluator.  Type `;CODE' or `eval CODE'.";
"Calls player:eval_cmd_string to first transform CODE in any way appropriate (e.g., prefixing .eval_env) and then do the actual evaluation.  See documentation for this:eval_cmd_string";
"If you set your .eval_time property to 1, you find out how many ticks and seconds you used.";
"If eval-d is used, the evaluation is performed as if the debug flag were unset.";
if (player != this)
player:tell("That is not a valid command.");
return;
endif
if (!player.programmer)
player:tell("You're trying to eval, but you don't have a programmer bit.  Sorry.");
return;
endif
set_task_perms(player);
debug = (length(verb) > 4) ? verb[5] == "+" | (!player:eval_option("no_debug"));
"result = player:eval_cmd_string(argstr, verb != \"eval-d\");";
result = player:eval_cmd_string(argstr, debug);
player:print_eval_result(result);
return;
if (result[1])
val = result[2];
if (typeof(val) == OBJ)
player:notify(tostr("=> ", val, "  ", valid(val) ? ("(" + val:name()) + ")" | ((a = $list_utils:assoc(val, {{#-1, "<$nothing>"}, {#-2, "<$ambiguous_match>"}, {#-3, "<$failed_match>"}})) ? a[2] | "<invalid>")));
elseif (typeof(val) == ERR)
player:notify(tostr("=> ", $code_utils:error_name(val), "  (", val, ")"));
else
player:notify(tostr("=> ", $string_utils:print(result[2])));
endif
"if (player.eval_time && !output_delimiters(player)[2])";
if (player:eval_option("time") && (!output_delimiters(player)[2]))
player:notify(tostr("[used ", result[3], " tick", (result[3] != 1) ? "s, " | ", ", result[4], " second", (result[4] != 1) ? "s" | "", ".]"));
endif
else
player:notify_lines(result[2]);
nerrors = length(result[2]);
player:notify(tostr(nerrors, " error", (nerrors == 1) ? "." | "s."));
endif
"Copied from generic programmer (#58):eval by Ken (#75) Mon Jan  2 18:12:07 1995 EST";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {913134420, \"core initialization\", #3023, \"core\"}";
.
#52:5
set_task_perms(player);
if ((length(args) != 1) || (!(spec = $code_utils:parse_propref(args[1]))))
player:notify(tostr("Usage:  ", verb, " <object>.<property>"));
return;
endif
object = player:my_match_object(spec[1]);
pname = spec[2];
if ($command_utils:object_match_failed(object, spec[1]))
return;
endif
result = delete_property(object, pname);
if (result == E_PROPNF)
player:notify("That object does not define that property.");
elseif (typeof(result) == ERR)
player:notify(tostr(result));
else
player:notify("Property removed.");
endif
.
#52:6
set_task_perms(player);
if (!(args && (spec = $code_utils:parse_verbref(args[1]))))
player:notify(tostr("Usage:  ", verb, " <object>:<verb-name(s)> [<dobj> [<prep> [<iobj> [<permissions> [<owner>]]]]]"));
return;
elseif ($command_utils:object_match_failed(object = player:my_match_object(spec[1]), spec[1]))
return;
elseif (!(e = $quota_utils:verb_addition_permitted(player)))
if (e == E_MAXREC)
player:tell("Verb addition not permitted:  Too many unmeasured objects.");
else
player:tell("Verb addition not permitted:  Quota exceeded.");
endif
return;
elseif (!player.programmer)
player:notify("Sorry, only programmers can add verbs.");
return;
endif
name = spec[2];
"...Adding another verb of the same name is often a mistake...";
namelist = $string_utils:explode(name);
for n in (namelist)
if (i = index(n, "*"))
n[i..i] = "";
endif
if ((hv = $object_utils:has_verb(object, n)) && (hv[1] == object))
player:notify(tostr("Warning:  Verb `", n, "' already defined on that object."));
endif
endfor
if (typeof(pas = $code_utils:parse_argspec(@listdelete(args, 1))) != LIST)
player:notify(tostr(pas));
return;
endif
verbargs = {@pas[1], "none", "none", "none"}[1..3];
rest = pas[2];
if (rest)
perms = rest[1];
elseif (verbargs == {"this", "none", "this"})
perms = "rxd";
else
perms = "rd";
endif
if (length(rest) < 2)
owner = player;
elseif (length(rest) > 2)
player:notify(tostr("\"", rest[3], "\" unexpected."));
return;
elseif ($command_utils:player_match_result(owner = player:my_match_player(rest[2]), rest[2])[1])
return;
elseif (owner == $nothing)
player:notify("Verb can't be owned by no one!");
return;
endif
try
add_verb(object, {owner, perms, name}, verbargs);
except e (ANY)
if (e[1] == E_INVARG)
player:notify(tostr(rest ? tostr("\"", perms, "\" is not a valid set of permissions.") | tostr("\"", verbargs[2], "\" is not a valid preposition (?)")));
else
player:notify(e[2]);
endif
return;
endtry
vnum = length(verbs(object));
if (named = pas[1][4..$])
try
set_verb_code(object, vnum, {@$code_utils:commentify($code_utils:named_args_to_usage(namelist, named)), "\"\";", @$code_utils:named_args_to_code(named)});
except e (ANY)
player:notify(tostr("Error setting named-args code:  ", e[2]));
endtry
endif
player:notify(tostr("Verb added [", vnum, "] (", vnum - 1, ")", named ? " with named args" | "", "."));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669416, \"Erik\", #74, \"JHM\"}";
.
#52:7
set_task_perms(player);
if (!(args && (spec = $code_utils:parse_verbref(args[1]))))
player:notify(tostr("Usage:  ", verb, " <object>:<verb>"));
elseif ($command_utils:object_match_failed(object = player:my_match_object(spec[1]), spec[1]))
"...bogus object...";
elseif (typeof(argspec = $code_utils:parse_argspec(@listdelete(args, 1))) != LIST)
player:notify(tostr(argspec));
elseif (argspec[2])
player:notify($string_utils:from_list(argspec[2], " ") + "??");
elseif (length(argspec = argspec[1]) in {1, 2})
player:notify({"Missing preposition", "Missing iobj specification"}[length(argspec)]);
else
verbname = spec[2];
if (verb == "@rmverb#")
loc = $code_utils:tonum(verbname);
if ((E_TYPE == loc) || (loc < 1))
player:notify(tostr(verbname, " is not a (1-based) verb number."));
return;
elseif (loc > length(verbs(object)))
player:notify(tostr(object, " does not have that many verbs."));
return;
endif
else
if (index(verbname, "*") > 1)
verbname = strsub(verbname, "*", "");
endif
loc = $code_utils:find_last_verb_named_1_based(object, verbname);
if (argspec)
argspec = argspec[1..3];
argspec[2] = $code_utils:full_prep(argspec[2]) || argspec[2];
while (loc && `verb_args(object, loc) != argspec ! E_PERM => 1')
loc = $code_utils:find_last_verb_named_1_based(object, verbname, loc);
endwhile
endif
if (!loc)
player:notify(tostr("That object does not define that verb", argspec ? " with those args." | "."));
return;
endif
endif
info = `verb_info(object, loc) ! E_PERM';
vargs = `verb_args(object, loc) ! E_PERM';
try
delete_verb(object, loc);
except e (ANY)
player:notify(e[2]);
return;
endtry
if (info)
player:notify(tostr("Verb ", object, ":", info[3], " [", loc, "] {", $string_utils:from_list(vargs, " "), "} removed."));
else
player:notify(tostr("Unreadable verb ", loc, " on ", object, " removed."));
endif
endif
.
#52:8
"@list <obj>:<verb> [<dobj> <prep> <iobj>] [with(out) parens|numbers] [ranges] [all]";
set_task_perms(player);
bynumber = verb == "@list#";
pflag = this:list_option("parentheses");
nflag = !this:list_option("nonumbers");
do_args_flag = !this:list_option("args_assignment");
aflag = 0;
argspec = {};
range = {};
spec = args ? $code_utils:parse_verbref(args[1]) | E_INVARG;
args = spec ? listdelete(args, 1) | E_INVARG;
while (args)
if (args[1] && ((index("without", args[1]) == 1) || (args[1] == "wo")))
"...w,wi,wit,with => 1; wo,witho,withou,without => 0...";
fval = !index(args[1], "o");
if (index("parentheses", args[2]) == 1)
pflag = fval;
args[1..2] = {};
elseif (index("numbers", args[2]) == 1)
nflag = fval;
args[1..2] = {};
else
player:notify(tostr(args[1], " WHAT?"));
args = E_INVARG;
endif
elseif (index("all", args[1]) == 1)
if (bynumber)
player:notify("Don't use `all' with @list#.");
args = E_INVARG;
else
aflag = 1;
args[1..1] = {};
endif
elseif (index("0123456789", args[1][1]) || (index(args[1], "..") == 1))
if (E_INVARG == (s = $seq_utils:from_string(args[1])))
player:notify(tostr("Garbled range:  ", args[1]));
args = E_INVARG;
else
range = $seq_utils:union(range, s);
args = listdelete(args, 1);
endif
elseif (bynumber)
player:notify("Don't give args with @list#.");
args = E_INVARG;
elseif (argspec)
"... second argspec?  Not likely ...";
player:notify(tostr(args[1], " unexpected."));
args = E_INVARG;
elseif (typeof(pas = $code_utils:parse_argspec(@args)) == LIST)
argspec = pas[1][1..3];
argspec[2] = $code_utils:full_prep(argspec[2]) || argspec[2];
args = pas[2];
else
"... argspec is bogus ...";
player:notify(tostr(pas));
args = E_INVARG;
endif
endwhile
if (args == E_INVARG)
if (bynumber)
player:notify(tostr("Usage:  ", verb, " <object>:<verb> [with|without parentheses|numbers] [ranges]"));
else
player:notify(tostr("Usage:  ", verb, " <object>:<verb> [<dobj> <prep> <iobj>] [with|without parentheses|numbers] [all] [ranges]"));
endif
return;
elseif ($command_utils:object_match_failed(object = player:my_match_object(spec[1]), spec[1]))
return;
endif
shown_one = 0;
what = object;
vloc = 0;
while (1)
"... find the next verb...";
if (bynumber)
vloc = $code_utils:tonum(spec[2]);
if (vloc == E_TYPE)
player:notify("Verb number expected.");
return;
elseif ((vloc < 1) || `vloc > length(verbs(what)) ! E_PERM => 0')
player:notify("Verb number out of range.");
return;
endif
else
while ((vloc = $code_utils:find_verb_named_1_based(what, spec[2], vloc + 1)) ? argspec && `verb_args(what, vloc) != argspec ! E_PERM => 1' | valid(what = parent(what)))
endwhile
endif
if (!vloc)
if (!shown_one)
player:notify(tostr("That object does not define that verb", argspec ? " with those args." | "."));
endif
return;
endif
if (shown_one)
player:notify("");
elseif ((!aflag) && (what != object))
player:notify(tostr("Object ", object, " does not define that verb", argspec ? " with those args" | "", ", but its ancestor ", what, " does."));
endif
shown_one = 1;
try
code = verb_code(what, vloc, pflag);
except e (ANY)
player:notify(tostr("Verb ", vloc, " on ", what, " -- ", e[2]));
if (aflag)
continue;
else
return;
endif
endtry
info = verb_info(what, vloc);
vargs = verb_args(what, vloc);
if (lastmodify = $metadata_item($verb_metadata(what, vloc), "Last-Modify"))
{m_when, ?m_name = m_who:dname(), m_who, ?m_moo = $network.moo_name} = lastmodify[2];
modify_string = tostr("Last modified ", ctime(m_when), " by ", m_name, " (", m_who, ")", (m_moo == $network.moo_name) ? "" | ((" (on " + m_moo) + ")"), ".");
else
modify_string = "";
endif
if (index(vargs[2], "/"))
vargs[2] = tostr("(", vargs[2], ")");
endif
if ((vargs == {"this", "none", "this"}) && (do_args_flag && (named = $code_utils:named_args_from_code(code))))
vargstr = tostr("(", $code_utils:named_args_list(named), ")");
firstarg = length($code_utils:split_verb_code(code)[1]) + 1;
lastarg = ((firstarg + length(named[1])) - 1) + (named[2] ? 1 | 0);
else
vargstr = $string_utils:from_list(vargs, " ");
firstarg = lastarg = 0;
endif
if (modify_string)
player:notify(modify_string);
endif
player:notify(tostr(what, ":", $string_utils:print(info[3]), " ", vargstr));
code = this:verbcode_internal_to_external(code);
lineseq = $seq_utils:range(1, length(code));
range && (lineseq = $seq_utils:intersection(range, lineseq));
if (code == {})
player:notify("That verb has not been programmed.");
elseif (!lineseq)
player:notify("No lines in that range.");
else
for k in [1..length(lineseq) / 2]
for i in [lineseq[(2 * k) - 1]..lineseq[2 * k] - 1]
if ((range || (i < firstarg)) || (i > lastarg))
if (nflag)
player:notify(tostr(" "[1..i < 10], i, ":  ", code[i]));
else
player:notify(code[i]);
endif
endif
$command_utils:suspend_if_needed(0);
endfor
endfor
endif
if (!aflag)
return;
endif
endwhile
return;
"Metadata 202106";
"Last-Modify: {833903074, \"Ken\", #75, \"JHM\"}";
.
#52:9
"Kills one or more tasks.";
"Arguments:";
"   object:verb -- kills all tasks which were started from that object and verb.";
"   all -- kills all tasks owned by invoker";
"   all player-name -- wizard variant:  kills all tasks owned by player.";
"   all everyone -- wizard variant:  really kills all tasks.";
"   Integer taskid -- kills the specifically named task.";
"   soon [integer] -- kills all tasks scheduled to run in the next [integer] seconds, which defaults to 60.";
"   %integer -- kills all tasks which end in the digits contained in integer.";
set_task_perms(player);
if (length(args) == 0)
player:notify_lines({tostr("Usage:  ", verb, " [object]:[verb]"), tostr("        ", verb, " task_id"), tostr("        ", verb, " soon [number-of-seconds]", player.wizard ? " [everyone|<player name>]" | ""), tostr("        ", verb, " all", player.wizard ? " [everyone|<player name>]" | "")});
return;
elseif (taskid = tonum(args[1]))
elseif (all = args[1] == "all")
everyone = 0;
realplayer = player;
if (player.wizard && (length(args) > 1))
realplayer = player:my_match_player(args[2]);
everyone = args[2] == "everyone";
if ((!valid(realplayer)) && (!everyone))
$command_utils:player_match_result(realplayer, args[2]);
return;
elseif (!everyone)
set_task_perms(realplayer);
endif
endif
elseif (soon = args[1] == "soon")
realplayer = player;
if (length(args) > 1)
soon = tonum(args[2]);
if ((soon <= 0) && (!player.wizard))
player:notify(tostr("Usage:  ", verb, " soon [positive-number-of-seconds]"));
return;
elseif (player.wizard)
result = this:kill_aux_wizard_parse(@args[2..length(args)]);
soon = result[1];
if (result[1] < 0)
"already gave them an error message";
return;
elseif (result[2] == 1)
everyone = 1;
else
everyone = 0;
set_task_perms(result[2]);
realplayer = result[2];
endif
endif
else
soon = 60;
everyone = 0;
endif
elseif (percent = args[1][1] == "%")
l = length(args[1]);
digits = tonum(args[1][2..l]);
percent = tonum("1" + "0000000000"[1..l - 1]);
elseif (colon = index(argstr, ":"))
whatstr = argstr[1..colon - 1];
vrb = argstr[colon + 1..length(argstr)];
if (whatstr)
what = player:my_match_object(whatstr);
endif
else
player:notify_lines({tostr("Usage:  ", verb, " [object]:[verb]"), tostr("        ", verb, " task_id"), tostr("        ", verb, " soon [number-of-seconds]", player.wizard ? " [everyone|<player name>]" | ""), tostr("        ", verb, " all", player.wizard ? " [\"everyone\"|<player name>]" | "")});
return;
endif
"OK, parsed the line, and punted them if it was bogus.  This verb could have been a bit shorter at the expense of readability.  I think it's getting towards unreadable as is.  At this point we've set_task_perms'd, and set up an enormous number of local variables.  Evaluate them in the order we set them, and we should never get var not found.";
queued_tasks = queued_tasks();
killed = 0;
if (taskid)
returnval = kill_task(taskid);
if (typeof(returnval) == ERR)
player:notify(tostr("Invalid task ID ", taskid, "."));
else
player:notify(tostr("Killed task ", taskid, "."));
killed = 1;
endif
elseif (all)
for task in (queued_tasks)
if (everyone || (realplayer == task[5]))
kill_task(task[1]);
killed = 1;
this:_kill_task_message(task);
endif
endfor
elseif (soon)
now = time();
for task in (queued_tasks)
if (((task[2] - now) < soon) && ((!player.wizard) || (everyone || (realplayer == task[5]))))
kill_task(task[1]);
killed = 1;
this:_kill_task_message(task);
endif
endfor
elseif (percent)
for task in (queued_tasks)
if (digits == (task[1] % percent))
kill_task(task[1]);
killed = 1;
this:_kill_task_message(task);
endif
endfor
elseif ((colon || vrb) || whatstr)
for task in (queued_tasks)
if ((((((whatstr == "") || (valid(task[6]) && (index(task[6]:name(), whatstr) == 1))) || (valid(task[9]) && (index(task[9]:name(), whatstr) == 1))) || (task[9] == what)) || (task[6] == what)) && ((vrb == "") || (index(" " + strsub(task[7], "*", ""), " " + vrb) == 1)))
this:_kill_task_message(task);
kill_task(task[1]);
killed = 1;
endif
endfor
else
player:notify("Something is funny; I didn't understand your @kill command.  You shouldn't have gotten here.  Please send yduJ mail saying you got this message from @kill, and what you had typed to @kill.");
endif
if (!killed)
player:notify("No tasks killed.");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669433, \"Erik\", #74, \"JHM\"}";
.
#52:10
"Usage:  @copy[-x] source:verbname to target[:verbname]";
"  the target verbname, if not given, defaults to that of the source.  If the target verb doesn't already exist, a new verb is installed with the same args, names, code, and permission flags as the source.  Otherwise, the existing target's verb code is overwritten and no other changes are made.";
"  @copy-x is like @copy except that any new verb is created with the x flag reset.";
"This the poor man's version of multiple inheritance... the main problem is that someone may update the verb you're copying and you'd never know.";
set_task_perms(player);
if (!player.programmer)
player:notify("You need to be a programmer to do this.");
player:notify("If you want to become a programmer, read help programmer-policy.");
return;
endif
if ((!(from = $code_utils:parse_verbref(dobjstr))) || (!iobjstr))
player:notify(tostr("Usage:  ", verb, " obj:verb to obj:verb"));
player:notify(tostr("        ", verb, " obj:verb to obj"));
player:notify(tostr("        ", verb, " obj:verb to :verb"));
return E_INVARG;
elseif ($command_utils:object_match_failed(fobj = player:my_match_object(from[1]), from[1]))
return;
elseif (iobjstr[1] == ":")
to = {fobj, iobjstr[2..length(iobjstr)]};
elseif (!(to = $code_utils:parse_verbref(iobjstr)))
iobj = player:my_match_object(iobjstr);
if ($command_utils:object_match_failed(iobj, iobjstr))
return;
endif
to = {iobj, from[2]};
elseif ($command_utils:object_match_failed(tobj = player:my_match_object(to[1]), to[1]))
return;
else
to[1] = tobj;
endif
from[1] = fobj;
to_firstname = strsub(to[2][1..index(to[2] + " ", " ") - 1], "*", "") || "*";
if ((!(hv = $object_utils:has_verb(to[1], to_firstname))) || (hv[1] != to[1]))
try
info = verb_info(@from);
vargs = verb_args(@from);
except v (E_PERM, E_INVARG, E_VERBNF)
player:notify(tostr("Retrieving ", from[1], ":", from[2], " --> ", v[1]));
return;
endtry
if (!player.wizard)
info[1] = player;
endif
if (verb == "@copy-x")
"... make sure this is an unusable copy...";
info[2] = strsub(info[2], "x", "");
vargs = {"this", "none", "this"};
endif
if (from[2] != to[2])
info[3] = to[2];
endif
try
add_verb(to[1], info, vargs);
except v (ANY)
player:notify(tostr("Adding ", to[1], ":", to[2], " --> ", v[2]));
return;
endtry
endif
code = verb_code(@from);
owner = verb_info(@from)[1];
if (owner != player)
code = {@code, tostr("\"Copied from ", from[1]:name_and_number(), ":", from[2], (from[1] == owner) ? " " | tostr(" by ", player:name_and_number(), " "), ctime(), "\";")};
endif
try
set_verb_code(to[1], to_firstname, code);
except v (ANY)
player:notify(tostr("Copying ", from[1], ":", from[2], " to ", to[1], ":", to[2], " --> ", v[1]));
return;
endtry
player:notify(tostr(to[1], ":", to[2], " code set."));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {836093247, \"Ken\", #75, \"JHM\"}";
.
#52:11
set_task_perms(caller_perms());
task = args[1];
player:notify(tostr("Killed: ", $string_utils:right(tostr("task ", task[1]), 17), ", verb ", task[6], ":", task[7], ", line ", task[8], (task[9] != task[6]) ? ", this==" + tostr(task[9]) | ""));
.
#52:12
"This version of @program deals with multiple verbs having the same name.";
"... @program <object>:<verbname> <dobj> <prep> <iobj>  picks the right one.";
if (player != caller)
return;
endif
set_task_perms(player);
"...";
"...catch usage errors first...";
"...";
punt = "...set punt to 0 only if everything works out...";
if (!(args && (spec = $code_utils:parse_verbref(args[1]))))
player:notify(tostr("Usage: ", verb, " <object>:<verb> [<dobj> <prep> <iobj>]"));
elseif ($command_utils:object_match_failed(object = player:my_match_object(spec[1]), spec[1]))
"...bogus object...";
elseif (0 && index(spec[2], "("))
"OLD--handle this in :parse_argspec now.";
named = $code_utils:parse_named_args(spec[2], @listdelete(args, 1));
if (typeof(named) == LIST)
if (named[2])
player:notify("Can't parse after last arg: ", $string_utils:from_list(named[2]));
else
punt = 0;
named = named[1];
verbname = named[1];
if (index(verbname, "*") > 1)
verbname = strsub(verbname, "*", "");
endif
named_args = named[2];
rest_arg = named[3];
argspec = {"this", "none", "this"};
endif
else
player:notify(tostr(named));
endif
elseif (verb == "@program#")
verbname = $code_utils:tonum(spec[2]);
if (verbname == E_TYPE)
player:notify("Verb number expected.");
elseif (length(args) > 1)
player:notify("Don't give args for @program#.");
elseif ((verbname < 1) || `verbname > length(verbs(object)) ! E_PERM => 0')
player:notify("Verb number out of range.");
else
argspec = 0;
punt = 0;
endif
elseif (typeof(argspec = $code_utils:parse_argspec(@listdelete(args, 1))) != LIST)
player:notify(tostr(argspec));
elseif (argspec[2])
player:notify($string_utils:from_list(argspec[2], " ") + "??");
elseif (length(argspec = argspec[1]) in {1, 2})
player:notify({"Missing preposition", "Missing iobj specification"}[length(argspec)]);
else
punt = 0;
verbname = spec[2];
if (index(verbname, "*") > 1)
verbname = strsub(verbname, "*", "");
endif
endif
"...";
"...if we have an argspec, we'll need to reset verbname...";
"...";
if (punt)
elseif (argspec)
named = argspec[4..min(5, $)];
argspec = argspec[1..3];
if (!(argspec[2] in {"none", "any"}))
argspec[2] = $code_utils:full_prep(argspec[2]);
endif
loc = $code_utils:find_verb_named_1_based(object, verbname);
while (loc && (`verb_args(object, loc) ! E_PERM' != argspec))
loc = $code_utils:find_verb_named_1_based(object, verbname, loc + 1);
endwhile
if (loc)
verbname = loc;
else
punt = "...can't find it....";
player:notify("That object has no verb matching that name + args.");
endif
else
named = {};
loc = (typeof(verbname) == NUM) ? verbname | 0;
endif
"...";
"...get verb info...";
"...";
if (!punt)
try
info = verb_info(object, verbname);
except e (ANY)
if (e[1] == E_VERBNF)
player:notify("That object does not have that verb definition.");
else
player:notify(e[2]);
endif
punt = 1;
endtry
if (!punt)
aliases = info[3];
if (!loc)
loc = aliases in (verbs(object) || {});
endif
endif
endif
"...";
"...read the code...";
"...";
if (punt)
player:notify(tostr("Now ignoring code for ", args ? args[1] | "nothing in particular", "."));
$command_utils:read_lines();
player:notify("Verb code ignored.");
else
player:notify(tostr("Now programming ", object:name(), ":", aliases, @loc ? {"[", loc, "](", loc - 1, ")."} | "[??]"));
lines = $command_utils:read_lines();
lines = this:verbcode_external_to_internal(lines);
if (named)
code = $code_utils:split_verb_code(lines);
lines = {@code[1], @$code_utils:named_args_to_code(named), @code[2]};
"if (lines[1] == \"\\\"Usage:\\\";\")";
"lines[1..1] = $code_utils:commentify($code_utils:named_args_to_usage($string_utils:explode(aliases), named));";
"endif";
"named_args = named[1];";
"rest_arg = named[2];";
"for i in [1..length(named_args)]";
"lines = {@lines, tostr(named_args[i], \" = args[\", i, \"];\")};";
"endfor";
"if (rest_arg)";
"if (named_args)";
"lines = {@lines, tostr(rest_arg, \" = args[\", length(named_args) + 1, \"..length(args)];\")};";
"elseif (rest_arg != \"args\")";
"lines = {@lines, tostr(rest_arg, \" = args;\")};";
"endif";
"endif";
"lines = {@lines, @code[2]};";
endif
try
result = set_verb_code(object, verbname, lines);
except e (ANY)
result = e[2] + " ";
"just in case some idiot throws an error with an empty string";
endtry
if (result)
if (typeof(result) == STR)
player:notify(result);
else
player:notify_lines(result);
player:notify(tostr(length(result), " error(s)."));
endif
player:notify("Verb not programmed.");
else
player:notify("0 errors.");
player:notify("Verb programmed.");
endif
endif
"Copied from generic programmer (#58):@prog by Erik (#74) Mon May  9 13:57:52 1994 EDT";
"Copied from Erik (#74):@program by Erik (#74) Mon May  9 15:56:03 1994 EDT";
.
#52:13
"Usage: @setenv <environment string>";
"Set your .eval_env property.  Most useful when eval won't work to set it";
"because your .eval_env contains an error.";
if (player != this)
return player:tell("That is not a valid command.");
elseif (!player.programmer)
return player:tell("Sorry, non-programmers can't eval.");
endif
set_task_perms(player);
if (!argstr)
player:notify(tostr("Current eval environment:  ", player:eval_option("env")));
player:notify(tostr("(Use `", verb, " <environment string>' to change it.)"));
"player:notify(tostr(\"Usage:  \", verb, \" <environment string>\"));";
return;
endif
player:notify(tostr("Previous eval environment:  ", player:eval_option("env")));
result = player:set_eval_env(argstr);
if (typeof(result) in {ERR, STR})
player:notify(tostr(result));
return;
endif
player:notify(tostr("New eval environment (", player:eval_option("ticks"), " ticks):  ", player:eval_option("env")));
.
#52:14
set_task_perms((caller_perms() == $nothing) ? player | caller_perms());
dobj = player:my_match_player(dobjstr);
if (!dobjstr)
player:notify(tostr("Usage: ", verb, " <player> [from <start>] [to <end>]"));
return;
elseif ($command_utils:player_match_result(dobj, dobjstr)[1])
return;
endif
dobjwords = $string_utils:words(dobjstr);
if (args[1..length(dobjwords)] == dobjwords)
args = args[length(dobjwords) + 1..length(args)];
endif
if (!(parse_result = $code_utils:_parse_audit_args(@args)))
player:notify(tostr("Usage:  ", verb, " player [from <start>] [to <end>]"));
return;
endif
return $building_utils:do_prospectus(dobj, @parse_result);
"Metadata 202106";
"Last-Modify: {863669437, \"Erik\", #74, \"JHM\"}";
.
#52:15
"@display <object>[.[property]]*[,[inherited_property]]*[:[verb]]*[;[inherited_verb]]*";
"null names for properties and verbs are interpreted as meaning all of them.";
opivu = {{}, {}, {}, {}, {}};
string = "";
punc = 1;
literal = 0;
for jj in [1..length(argstr)]
j = argstr[jj];
if (literal)
string = string + j;
literal = 0;
elseif (j == "\\")
literal = 1;
elseif (y = index(".,:;", j))
opivu[punc] = {@opivu[punc], string};
punc = 1 + y;
string = "";
else
string = string + j;
endif
endfor
opivu[punc] = {@opivu[punc], string};
objname = opivu[1][1];
it = this:my_match_object(objname);
if ($command_utils:object_match_failed(it, objname))
return;
endif
readable = (it.owner == this) || (it.r || this.wizard);
cant = {};
if ("" in opivu[2])
if (readable)
prop = properties(it);
else
prop = {};
cant = setadd(cant, it);
endif
if (this:display_option("firstancestor"))
what = it;
while ((!prop) && valid(what = parent(what)))
if ((what.owner == this) || (what.r || this.wizard))
prop = properties(what);
else
cant = setadd(cant, what);
endif
endwhile
endif
else
prop = opivu[2];
endif
if ("" in opivu[3])
inh = {};
for what in ({it, @$object_utils:ancestors(it)})
if (((what.owner == this) || what.r) || this.wizard)
inh = {@inh, @properties(what)};
else
cant = setadd(cant, what);
endif
endfor
else
inh = opivu[3];
endif
for q in (inh)
if (q in properties(it))
prop = setadd(prop, q);
inh = setremove(inh, q);
endif
endfor
vrb = {};
if ("" in opivu[4])
if (readable)
vrbs = verbs(it);
else
vrbs = {};
cant = setadd(cant, it);
endif
what = it;
if (this:display_option("firstancestor"))
while ((!vrbs) && valid(what = parent(what)))
if ((what.owner == this) || (what.r || this.wizard))
vrbs = verbs(what);
else
cant = setadd(cant, what);
endif
endwhile
endif
for n in [1..length(vrbs)]
vrb = setadd(vrb, {what, n});
endfor
else
for w in (opivu[4])
if (y = $object_utils:has_verb(it, w))
vrb = setadd(vrb, {y[1], w});
else
this:notify(tostr("No such verb, \"", w, "\""));
endif
endfor
endif
if ("" in opivu[5])
for z in ({it, @$object_utils:ancestors(it)})
if (((this == z.owner) || z.r) || this.wizard)
for n in [1..length(verbs(z))]
vrb = setadd(vrb, {z, n});
endfor
else
cant = setadd(cant, z);
endif
endfor
else
for w in (opivu[5])
if (typeof(y = $object_utils:has_verb(it, w)) == LIST)
vrb = setadd(vrb, {y[1], w});
else
this:notify(tostr("No such verb, \"", w, "\""));
endif
endfor
endif
if (({""} in opivu) || (opivu[2..5] == {{}, {}, {}, {}}))
this:notify(tostr(it:name(), " (", it, ") [ ", it.r ? "readable " | "", it.w ? "writeable " | "", it.f ? "fertile " | "", is_player(it) ? "(player) " | "", it.programmer ? "programmer " | "", it.wizard ? "wizard " | "", "]"));
if (it.owner != (is_player(it) ? it | this))
this:notify(tostr("  Owned by ", valid(p = it.owner) ? p:name() | "** extinct **", " (", p, ")."));
endif
this:notify(tostr("  Child of ", valid(p = parent(it)) ? p:name() | "** none **", " (", p, ")."));
if (it.location != $nothing)
this:notify(tostr("  Location ", valid(p = it.location) ? p:name() | "** unplace (tell a wizard, fast!) **", " (", p, ")."));
endif
endif
set_task_perms(this.owner);
blankargs = this:display_option("blank_tnt") ? {"this", "none", "this"} | #-1;
for b in (vrb)
$command_utils:suspend_if_needed(0);
{where, q} = b;
short = (typeof(q) == NUM) ? q | strsub((y = index(q, " ")) ? q[1..y - 1] | q, "*", "");
inf = verb_info(where, short);
if ((typeof(inf) == LIST) || (inf == E_PERM))
name = (typeof(inf) == LIST) ? inf[3] | q;
name = index(name, " ") ? ("\"" + name) + "\"" | name;
line = $string_utils:left(tostr($string_utils:right(tostr(where), 6), ":", name, " "), 32);
if (inf == E_PERM)
line = line + "   ** unreadable **";
else
line = $string_utils:left(tostr(line, inf[1]:name(), " (", inf[1], ") "), 53) + ((i = inf[2] in {"x", "xd", "d", "rd"}) ? {" x", " xd", "  d", "r d"}[i] | inf[2]);
vargs = verb_args(where, short);
if (vargs != blankargs)
if (this:display_option("shortprep") && (!(vargs[2] in {"any", "none"})))
vargs[2] = $code_utils:short_prep(vargs[2]);
endif
line = $string_utils:left(line + " ", 60) + $string_utils:from_list(vargs, " ");
endif
endif
this:notify(line);
elseif (inf == E_VERBNF)
this:notify(tostr(inf));
this:notify(tostr("  ** no such verb, \"", short, "\" **"));
else
this:notify("This shouldn't ever happen. @display is buggy.");
endif
endfor
all = {@prop, @inh};
max = (length(all) < 4) ? 999 | (abs(this.linelen) - 54);
depth = (length(all) < 4) ? -1 | 1;
truncate_owner_names = length(all) > 1;
for q in (all)
$command_utils:suspend_if_needed(0);
inf = property_info(it, q);
if (inf == E_PROPNF)
if (q in $code_utils.builtin_props)
this:notify(tostr($string_utils:left("," + q, 25), "Built in property ", (length(y = $string_utils:from_value(it.(q), 1, depth)) > max) ? y[1..max] + ".." | y));
else
this:notify(tostr("  ** property not found, \"", q, "\" **"));
endif
else
pname = $string_utils:left(tostr((q in properties(it)) ? "." | (is_clear_property(it, q) ? " " | ","), q, " "), 25);
if (inf == E_PERM)
this:notify(pname + "   ** unreadable **");
else
oname = inf[1]:name();
truncate_owner_names && ((length(oname) > 12) && (oname = oname[1..12]));
(inf[2][1] != "r") && (inf[2][1..0] = " ");
(inf[2][2] != "w") && (inf[2][2..1] = " ");
this:notify($string_utils:left(tostr($string_utils:left(tostr(pname, oname, " (", inf[1], ") "), 47), inf[2], " "), 54) + $string_utils:print_truncated(it.(q), max, ".."));
endif
endif
endfor
if (cant)
failed = {};
for k in (cant)
failed = listappend(failed, tostr(k:name(), " (", k, ")"));
endfor
this:notify($string_utils:centre(tostr(" no permission to read ", $string_utils:english_list(failed, ", ", " or ", " or "), ". "), 75, "-"));
else
this:notify($string_utils:centre(" finished ", 75, "-"));
endif
.
#52:16
set_task_perms(player);
"Let 'em @kill it.";
count = 0;
for i in [0..tonum(max_object())]
if ($command_utils:running_out_of_time())
player:notify(tostr("Counting... [", count, "/", i - 1, "]"));
suspend(0);
endif
if (valid(toobj(i)))
count = count + 1;
endif
endfor
player:notify(tostr("There are ", count, " valid objects out of ", tonum(max_object()) + 1, " allocated object numbers."));
.
#52:17
set_task_perms((caller_perms() == $nothing) ? player | caller_perms());
dobj = player:my_match_player(dobjstr);
if (!dobjstr)
player:notify(tostr("Usage: ", verb, " <player> [from <start>] [to <end>]"));
return;
elseif ($command_utils:player_match_result(dobj, dobjstr)[1])
return;
endif
dobjwords = $string_utils:words(dobjstr);
if (args[1..length(dobjwords)] == dobjwords)
args = args[length(dobjwords) + 1..length(args)];
endif
if (!(parse_result = $code_utils:_parse_audit_args(@args)))
player:notify(tostr("Usage:  ", verb, " player [from <start>] [to <end>]"));
return;
endif
start = parse_result[1];
end = parse_result[2];
player:notify(tostr("Objects owned by ", valid(dobj) ? dobj:name() | dobj, ((" (from #" + tostr(start)) + " to #") + tostr(end), ")", ":"));
count = 0;
printed_anything = 0;
for i in [start..end]
o = toobj(i);
if ($command_utils:running_out_of_time())
if (!printed_anything)
player:notify(tostr(o, " ..."));
endif
suspend(3);
endif
if (valid(o) && (o.owner == dobj))
kids = 0;
for k in (children(o))
if (k.owner != o.owner)
kids = 2;
elseif (kids == 0)
kids = 1;
endif
endfor
if (is_player(o))
c = "P";
elseif ($object_utils:isa(o, $player))
c = "p";
elseif ($object_utils:isa(o, $room))
c = "R";
elseif ($object_utils:isa(o, $exit))
c = "E";
elseif ($object_utils:isa(o, $note))
c = "N";
elseif ($object_utils:isa(o, $container))
c = "C";
elseif ($object_utils:isa(o, $thing))
c = "T";
else
c = " ";
endif
"The verbs() call below might fail, but that's OK";
v = verbs(o);
if (v)
vstr = tostr("[", $string_utils:right(length(v), 3), "] ");
else
vstr = "      ";
endif
if (o.r && o.f)
r = "f";
elseif (o.r)
r = "r";
elseif (o.f)
r = "F";
else
r = " ";
endif
player:notify(tostr(" kK"[kids + 1], r, c, vstr, o:name(), " (", o, ")"));
count = count + 1;
printed_anything = 1;
endif
endfor
if (count)
player:notify("");
endif
player:notify(tostr("Total: ", count, " object", (count == 1) ? "." | "s."));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669443, \"Erik\", #74, \"JHM\"}";
.
#52:18
set_task_perms(player);
if (prepstr == "in")
pattern = dobjstr;
object = player.location:match_object(iobjstr);
if (object == $failed_match)
objlist = player:eval_cmd_string(iobjstr, 0);
if (!objlist[1])
player:notify(tostr("Had trouble reading `", iobjstr, "':  "));
player:notify_lines(@objlist[2]);
return;
elseif (typeof(objlist[2]) == OBJ)
objlist = {objlist[2..2]};
elseif (typeof(objlist[2]) != LIST)
player:notify(tostr("Value of `", iobjstr, "' is not an object or list:  ", $string_utils:print(objlist[2])));
return;
else
objlist = objlist[2..2];
endif
elseif ($command_utils:object_match_failed(object, iobjstr))
return;
else
objlist = {object};
endif
elseif ((prepstr == "from") && (player.wizard && (n = tonum(toobj(iobjstr)))))
pattern = dobjstr;
objlist = {n};
elseif (args && player.wizard)
pattern = argstr;
objlist = {};
else
player:notify(tostr("Usage:  ", verb, " <pattern> ", player.wizard ? "[in {<objectlist>} | from <number>]" | "in {<objectlist>}"));
return;
endif
player:notify(tostr("Searching for verbs ", prepstr, " ", iobjstr, (verb == "@egrep") ? " matching the pattern " | " containing the string ", $string_utils:print(pattern), " ..."));
player:notify("");
$code_utils:((verb == "@egrep") ? "find_verbs_matching" | "find_verbs_containing")(pattern, @objlist);
.
#52:19
set_task_perms(player);
if (dobjstr == "")
player:notify(tostr("Usage:  ", verb, " <object-or-property-or-verb>"));
return;
endif
if (index(dobjstr, ".") && (spec = $code_utils:parse_propref(dobjstr)))
if (valid(object = player:my_match_object(spec[1])))
return $code_utils:show_property(object, spec[2]);
endif
elseif (spec = $code_utils:parse_verbref(dobjstr))
if (valid(object = player:my_match_object(spec[1])))
return $code_utils:show_verbdef(object, spec[2]);
endif
elseif (((dobjstr[1] == "$") && ((pname = dobjstr[2..length(dobjstr)]) in properties(#0))) && (typeof(#0.(pname)) == OBJ))
if (valid(object = #0.(pname)))
return $code_utils:show_object(object);
endif
elseif ((dobjstr[1] == "$") && (spec = $code_utils:parse_propref(dobjstr)))
return $code_utils:show_property(#0, spec[2]);
else
if (valid(object = player:my_match_object(dobjstr)))
return $code_utils:show_object(object);
endif
endif
$command_utils:object_match_failed(object, dobjstr);
.
#52:20
"@check-chparent object to newparent";
"checks for property name conflicts that would make @chparent bomb.";
set_task_perms(player);
if (!(dobjstr && iobjstr))
player:notify(tostr("Usage:  ", verb, " <object> to <newparent>"));
elseif ($command_utils:object_match_failed(object = player:my_match_object(dobjstr), dobjstr))
"...bogus object...";
elseif ($command_utils:object_match_failed(parent = player:my_match_object(iobjstr), iobjstr))
"...bogus new parent...";
elseif (player != this)
player:notify(tostr(E_PERM));
elseif (typeof(result = $object_utils:property_conflicts(object, parent)) == ERR)
player:notify(tostr(result));
elseif (result)
su = $string_utils;
player:notify("");
player:notify(su:left("Property", 30) + "Also Defined on");
player:notify(su:left("--------", 30) + "---------------");
for r in (result)
player:notify(su:left(tostr(parent, ".", r[1]), 30) + su:from_list(listdelete(r, 1), " "));
$command_utils:suspend_if_needed(0);
endfor
else
player:notify("No property conflicts found.");
endif
.
#52:21
"@check-prop object.property";
"  checks for descendents defining the given property.";
set_task_perms(player);
if (!(spec = $code_utils:parse_propref(dobjstr)))
player:notify(tostr("Usage:  ", verb, " <object>.<prop-name>"));
elseif ($command_utils:object_match_failed(object = player:my_match_object(spec[1]), spec[1]))
"...bogus object...";
elseif (!($perm_utils:controls(player, object) || object.w))
player:notify("You can't create a property on that object anyway.");
elseif ($object_utils:has_property(object, prop = spec[2]))
player:notify("That object already has that property.");
elseif (olist = $object_utils:descendants_with_property_suspended(object, prop))
player:notify("The following descendents have this property defined:");
player:notify("  " + $string_utils:from_list(olist, " "));
else
player:notify("No property name conflicts found.");
endif
.
#52:22
"set_eval_env(string);";
"Run <string> through eval.  If it doesn't compile, return E_INVARG.  If it crashes, well, it crashes.  If it works okay, set .eval_env to it and set .eval_ticks to the amount of time it took.";
if (!caller_perms().programmer)
return E_PERM;
endif
set_task_perms(caller_perms());
program = args[1];
while (index(program, ticks = $code_utils:gensym()))
endwhile
value = $code_utils:eval_d(timedprog = tostr(ticks, " = ticks_left();", program, ";return ", ticks, " - ticks_left() - 2;"));
if (!value[1])
return "Syntax error in new env:  " + $string_utils:from_list(value[2], "; ");
elseif (typeof(eval(timedprog)[2]) == ERR)
"...bomb out if there's an actual error in program...";
"...we shouldn't actually get here";
return tostr(value[2]);
endif
ok = this:set_eval_option("env", program);
if (typeof(ok) == STR)
return ok;
else
this:set_eval_option("ticks", value[2]);
return 1;
endif
.
#52:23
"@clearproperty <obj>.<prop>";
"Set the value of <obj>.<prop> to `clear', making it appear to be the same as the property on its parent.";
set_task_perms(player);
if (!(l = $code_utils:parse_propref(dobjstr)))
player:notify(tostr("Usage:  ", verb, " <object>.<property>"));
elseif ($command_utils:object_match_failed(dobj = player:my_match_object(l[1]), l[1]))
"... bogus object...";
elseif (is_clear_property(dobj, prop = l[2]))
player:notify(tostr("Property ", dobj, ".", prop, " is already clear!"));
elseif ((result = clear_property(dobj, prop)) == E_INVARG)
player:notify(tostr("You can't clear ", dobj, ".", prop, "; none of the ancestors define that property."));
elseif (typeof(result) == ERR)
player:notify(tostr(result));
else
player:notify(tostr("Property ", dobj, ".", prop, " cleared; value is now ", $string_utils:print(dobj.(prop)), "."));
endif
.
#52:24
"Syntax: @disown <object> [from <object>]";
"This command is used to remove unwanted children of objects you control. If you control an object, and there is a child of that object you do not want, this command will chparent() the object to its grandparent.";
if (prepstr)
if (prepstr != "from")
player:notify("Usage:  ", verb, " <object> [from <object>]");
return;
elseif ($command_utils:object_match_failed(iobj = player:my_match_object(iobjstr), iobjstr))
"... from WHAT?..";
return;
elseif (valid(dobj = $string_utils:literal_object(dobjstr)))
"... literal object number...";
if (parent(dobj) != iobj)
player:notify(tostr(dobj, " is not a child of ", iobj:name(), " (", iobj, ")"));
return;
endif
elseif ($command_utils:object_match_failed(dobj = $string_utils:match(dobjstr, children(iobj), "name", children(iobj), "aliases"), dobjstr))
"... can't match dobjstr against any children of iobj";
return;
endif
elseif ($command_utils:object_match_failed(dobj = player:my_match_object(dobjstr), dobjstr))
"... can't match dobjstr...";
return;
endif
victim = dobj;
parent = parent(victim);
if ($perm_utils:controls(player, victim))
"... why is he using @disown?... probably by mistake...";
player:notify(tostr(victim:name(), " (", victim, ") is yours.  Use @chparent."));
elseif (!valid(parent))
player:notify(tostr(victim:name(), " (", victim, ") is already an orphan."));
elseif (!$perm_utils:controls(player, parent))
player:notify(tostr(parent:name(), " (", parent, "), the parent of ", victim:name(), " (", victim, "), is not yours."));
elseif (!valid(grandparent = parent(parent)))
"... still not sure about this... do we care?  --Rog...";
player:notify(tostr(victim:name(), " (", victim, ") has no grandparent to take custody."));
else
chparent(victim, grandparent);
player:notify(tostr(victim:name(), " (", victim, ")'s parent is now ", grandparent:name(), " (", grandparent, ")."));
endif
.
#52:25
":eval_cmd_string(string[,debug])";
"Evaluates the string the way this player would normally expect to see it evaluated if it were typed on the command line.  debug (defaults to 1) indicates how the debug flag should be set during the evaluation.";
" => {@eval_result, ticks, seconds}";
"where eval_result is the result of the actual eval() call.";
"";
"For the case where string is an expression, we need to prefix `return ' and append `;' to string before passing it to eval().  However this is not appropriate for statements, where it is assumed an explicit return will be provided somewhere or that the return value is irrelevant.  The code below assumes that string is an expression unless it either begins with a semicolon `;' or one of the MOO language statement keywords.";
"Next, the substitutions described by this.eval_subs, which should be a list of pairs {string, sub}, are performed on string";
"Finally, this.eval_env is prefixed to the beginning while this.eval_ticks is subtracted from the eventual tick count.  This allows string to refer to predefined variables like `here' and `me'.";
set_task_perms(caller_perms());
program = args[1] + ";";
debug = {@args, 1}[2] ? 33 | 0;
if (!match(program, "^ *%(;%|%(if%|fork?%|return%|while%|try%)[^a-z0-9A-Z_]%)"))
program = "return " + program;
endif
"program = tostr(this.eval_env, \";\", $code_utils:substitute(program, this.eval_subs));";
"ticks = ticks_left() - 48 - this.eval_ticks + debug;";
program = tostr(this:eval_option("env"), ";", $code_utils:substitute(program, this:eval_option("subs")));
eval_ticks = this:eval_option("ticks");
ticks = ((ticks_left() - 46) - eval_ticks) + debug;
seconds = seconds_left();
value = debug ? eval(program) | $code_utils:eval_d(program);
seconds = seconds - seconds_left();
ticks = ticks - ticks_left();
return {@value, ticks, seconds};
"Metadata 202106";
"Last-Modify: {878790236, \"Erik\", #74, \"JHM\"}";
.
#52:26
"@dump something [with [id=...] [noprops] [noverbs] [create]]";
"This spills out all properties and verbs on an object, calling suspend at appropriate intervals.";
"   id=#nnn -- specifies an idnumber to use in place of the object's actual id (for porting to another MOO)";
"   noprops -- don't show properties.";
"   noverbs -- don't show verbs.";
"   create  -- indicates that a @create command should be generated and all of the verbs be introduced with @verb rather than @args; the default assumption is that the object already exists and you're just doing this to have a look at it.";
set_task_perms(player);
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
return;
endif
if (prepstr && (prepstr != "with"))
player:notify(tostr("Usage:  ", verb, " something [with [id=...] [noprops] [noverbs] [create]]"));
return;
endif
targname = tostr(dobj);
options = {"props", "verbs"};
create = 0;
if (iobjstr)
for o in ($string_utils:explode(iobjstr))
if (index(o, "id=") == 1)
targname = o[4..length(o)];
elseif (o in {"noprops", "noverbs"})
options = setremove(options, o[3..length(o)]);
elseif (o in {"create"})
create = 1;
endif
endfor
endif
if (create)
parent = parent(dobj);
pstring = tostr(parent);
for p in (properties(#0))
if (#0.(p) == parent)
pstring = "$" + p;
endif
endfor
if (targname == "_")
player:notify("@building-option +create-set-co");
endif
player:notify(tostr("@create ", pstring, " named ", dobj:name(), ":", $string_utils:from_list(dobj.aliases, ",")));
endif
for p in (("props" in options) ? properties(dobj) | {})
pquoted = $string_utils:print(p);
info = property_info(dobj, p);
value = dobj.(p);
if (create)
uvalue = (typeof(value) == LIST) ? "{}" | 0;
player:notify(tostr("@prop ", targname, ".", pquoted, " ", uvalue || $string_utils:print_suspended(value), " ", info[2] || "\"\"", (info[1] == dobj.owner) ? "" | tostr(" ", info[1])));
if (uvalue && value)
if (subs = match(p, "%(.+%)_msg$"))
player:notify(tostr("@", substitute("%1", subs), " ", targname, " is ", $string_utils:print(player:unparse_message(value))));
else
player:notify(tostr(";", targname, ".(", pquoted, ") = ", $string_utils:print_suspended(value)));
endif
endif
else
if (info[2] != "rc")
player:notify(tostr("@chmod ", targname, ".", pquoted, " ", info[2]));
endif
if (info[1] != dobj.owner)
player:notify(tostr("@chown ", targname, ".", pquoted, " ", info[1]));
endif
if (subs = match(p, "%(.+%)_msg$"))
player:notify(tostr("@", substitute("%1", subs), " ", targname, " is ", $string_utils:print(player:unparse_message(value))));
else
player:notify(tostr(";", targname, ".(", pquoted, ") = ", $string_utils:print_suspended(value)));
endif
endif
$command_utils:suspend_if_needed(0);
endfor
for a in (("props" in options) ? $object_utils:ancestors(dobj) | {})
for p in (properties(a))
$command_utils:suspend_if_needed(1);
pquoted = $string_utils:print(p);
value = dobj.(p);
"avalue = a.(p);";
if (typeof(value) == ERR)
player:notify(tostr("\"", targname, ".(", pquoted, ") => ", $code_utils:error_name(value), " (", value, ")"));
"elseif ((typeof(avalue) == ERR) || (value != avalue))";
elseif (!is_clear_property(dobj, p))
if (subs = match(p, "%(.+%)_msg$"))
player:notify(tostr("@", substitute("%1", subs), " ", targname, " is ", $string_utils:print(player:unparse_message(value))));
else
player:notify(tostr(";", targname, ".(", pquoted, ") = ", $string_utils:print_suspended(value)));
endif
endif
endfor
$command_utils:suspend_if_needed(1);
endfor
if (!("verbs" in options))
return;
endif
player:notify("");
vnum = 1;
while ((info = verb_info(dobj, vnum)) || (info == E_PERM))
if (index(info[3], "(old)") && create)
player:tell("\"Skipping ", dobj, ":\"", info[3], "\"...");
else
suspend(1);
if (typeof(info) == ERR)
player:notify(tostr("\"", dobj, ": [", vnum, "] (", vnum - 1, ") --- ", info, "\";"));
else
if (i = index(vname = info[3], " "))
vname = vname[1..i - 1];
endif
if (vname[1] != "*")
vname = strsub(vname, "*", "");
endif
args = verb_args(dobj, vnum);
prep = (args[2] in {"any", "none"}) ? args[2] | $code_utils:short_prep(args[2]);
perms = (info[2] != ((args == {"this", "none", "this"}) ? "rxd" | "rd")) ? info[2] || "\"\"" | "";
if (create)
if (info[1] == dobj.owner)
tail = perms ? tostr(" ", perms) | "";
else
tail = tostr(" ", perms || info[2], " ", info[1]);
endif
player:notify(tostr("@verb ", targname, ":\"", info[3], "\" ", args[1], " ", prep, " ", args[3], tail));
else
player:notify(tostr("@args ", targname, ":\"", info[3], "\" ", args[1], " ", prep, " ", args[3]));
if (info[1] != dobj.owner)
player:notify(tostr("@chown ", targname, ":", vname, " ", info[1]));
endif
if (perms)
player:notify(tostr("@chmod ", targname, ":", vname, " ", perms));
endif
endif
if (code = verb_code(dobj, vnum, 1, 1))
player:notify(tostr("@program ", targname, ":", vname));
for c in (code)
player:notify(c);
$command_utils:suspend_if_needed(0);
endfor
player:notify_lines({".", ""});
endif
endif
endif
"HEY WOW, we don't have to do this any more!";
"if (index(tostr(\" \", info[3], \" \"), \" * \"))";
"... oh shit, we have a * verb.  may as well forget trying to list...";
"... the rest; they're invisible.";
"break;";
"endif";
vnum = vnum + 1;
$command_utils:suspend_if_needed(0);
endwhile
player:notify("\"***finished***");
.
#52:27
"Definition from Lambda:#33337";
set_task_perms(player);
if (!dobjstr)
if ((verb[7] != "(") && (verb[length(verb)] != ")"))
player:tell("Usage:  @verbs <object>");
return;
else
dobjstr = verb[8..length(verb) - 1];
endif
endif
thing = player:my_match_object(dobjstr);
if (!$command_utils:object_match_failed(thing, dobjstr))
verbs = $code_utils:verbs(thing);
player:tell(";verbs(", thing, ") => ", $string_utils:from_value(verbs, 1, -1));
endif
.
#52:28
"Usage:  @make-setter <object>.<property>";
"Write the standard :set_foo verb for a property.";
"Works by copying $code_utils:standard_set_property";
if (!player.programmer)
player:tell("I don't understand that.");
elseif ((!dobjstr) || (!(spec = $code_utils:parse_propref(dobjstr))))
player:tell_lines($code_utils:verb_usage());
return;
elseif ($command_utils:object_match_failed(what = player:my_match_object(whatname = spec[1]), whatname))
elseif (!$perm_utils:controls(player, what))
player:tell("You don't own ", what:title(), ".");
elseif (!(info = property_info(what, propname = spec[2])))
player:tell(what:titlec(), " has no \"", propname, "\" property.");
elseif ((index(propname, " ") || index(propname, "\"")) || index(propname, "*"))
player:tell("The standard setter verb won't work; you can't have a space, a quotation mark, or an asterisk in a verb name.");
elseif (!$perm_utils:controls_prop(player, what, propname))
player:tell("You don't own ", what:title(), ".", propname, ".");
elseif (index(info[2], "c") && (!player.wizard))
player:tell(what:titlec(), ".", propname, " is +c, so the standard setter verb won't work.  @chmod it, or write your own verb.");
elseif ($code_utils:find_verb_named_1_based(what, verbname = "set_" + propname))
player:tell(what:titlec(), " already has a ", verbname, " verb.");
else
code = listdelete(verb_code($code_utils, "standard_set_property"), 2);
for v in (verbs(what))
if (match(v, "^%(set_[^ ]+%|.* set_[^ ]+%)"))
vname = strsub($string_utils:explode(v)[1], "*", "");
if (((verb_code(what, vname) == code) && ((oldinfo = verb_info(what, vname))[1] == player)) && (oldinfo[2] == "rx"))
set_task_perms(player);
set_verb_info(what, vname, {player, "rxd", (v + " ") + verbname});
player:tell(what:titlec(), " already had a standard setter verb; it's now named \"", (v + " ") + verbname, "\".");
return;
endif
endif
endfor
set_task_perms(player);
add_verb(what, {player, "rx", verbname}, {"this", "none", "this"});
set_verb_code(what, verbname, code);
if ((player.wizard && (!index(info[2], "c"))) && (!info[1].wizard))
player:tell(what:titlec(), ".", propname, " is !c and owned by ", info[1]:name(), ", so this verb doesn't need wizard permissions.  \"@chown ", what, ":", verbname, " to ", info[1], "\" to give ", info[1]:name(), " the verb.");
endif
player:tell("Wrote ", what:title(), ":", verbname, ".");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869627072, \"Erik\", #74, \"JHM\"}";
.
#52:29
":display_option(name) => returns the value of the specified @display option";
":list_option(name) => returns the value of the specified @list option";
":eval_option(name) => returns the value of the specified eval option";
{name} = args;
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
foo_options = verb + "s";
return #0.(foo_options):get(this.(foo_options), name);
.
#52:30
":set_list_option(oname,value)";
":set_display_option(oname,value)";
":set_eval_option(oname,value)";
"Changes the value of the named option.";
"Returns a string error if something goes wrong.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
raise(E_PERM);
endif
"...this is kludgy, but it saves me from writing the same verb 3 times.";
"...there's got to be a better way to do this...";
foo_options = verb[5..$] + "s";
"...";
if (typeof(s = #0.(foo_options):set(this.(foo_options), @args)) == STR)
return s;
elseif (equal(s, this.(foo_options)))
return 0;
else
this.(foo_options) = s;
return 1;
endif
return;
"Metadata 202106";
"Last-Modify: {832018316, \"Ken\", #75, \"JHM\"}";
.
#52:31
"@<what>-option <option> [is] <value>   sets <option> to <value>";
"@<what>-option <option>=<value>        sets <option> to <value>";
"@<what>-option +<option>     sets <option>   (usually equiv. to <option>=1";
"@<what>-option -<option>     resets <option> (equiv. to <option>=0)";
"@<what>-option !<option>     resets <option> (equiv. to <option>=0)";
"@<what>-option <option>      displays value of <option>";
set_task_perms(player);
what = {"display", "list", "eval"}[index("dle", verb[2])];
options = what + "_options";
option_pkg = #0.(options);
set_option = ("set_" + what) + "_option";
if (!args)
player:notify_lines({("Current " + what) + " options:", "", @option_pkg:show(this.(options), option_pkg.names)});
return;
elseif (typeof(presult = option_pkg:parse(args)) == STR)
player:notify(presult);
return;
else
if (length(presult) > 1)
if (typeof(sresult = this:(set_option)(@presult)) == STR)
player:notify(sresult);
return;
elseif (!sresult)
player:notify("No change.");
return;
endif
endif
player:notify_lines(option_pkg:show(this.(options), presult[1]));
endif
.
#52:32
":options_packages()";
"Returns a list of options packages usable by this player.";
return {@pass(args), $list_options, $display_options, $eval_options};
.
#52:33
"debug(value[, message])";
"Print <value> to this, and return <value>";
"If <message> is supplied, it is prepended to <value>; otherwise, the caller's object number is used as a prefix.";
if (length(args) > 1)
msg = tostr(@listdelete(args, 1), ": ");
else
msg = tostr(caller, ": ");
endif
this:($perm_utils:controls(caller_perms(), this) ? "notify" | "tell")(msg + $string_utils:print(args[1]));
return args[1];
.
#52:34
"#<string>[.<property>|.parent] [exit|player|inventory|room] [for <code>] returns information about the object (we'll call it <thing>) named by string.  String is matched in the current room unless one of exit|player|inventory is given.";
"If neither .<property>|.parent nor <code> is specified, just return <thing>.";
"If .<property> is named, return <thing>.<property>.  .parent returns parent(<thing>).";
"If <code> is given, it is evaluated, with the value returned by the first part being substituted for %# in <code>.";
"For example, the command";
"  #Joe.parent player for tonum(%#)";
"will return 58 (unless Joe has chparented since writing this).";
set_task_perms(player);
if (!(whatstr = verb[2..dot = min(index(verb + ".", "."), index(verb + ":", ":")) - 1]))
player:tell("Usage:  #string [exit|player|inventory]");
return;
elseif (!args)
what = player:my_match_object(whatstr);
elseif (index("exits", args[1]) == 1)
what = player.location:match_exit(whatstr);
elseif (index("inventory", args[1]) == 1)
what = player:match(whatstr);
elseif (index("players", args[1]) == 1)
what = player:my_match_player(whatstr);
elseif (index("rooms", args[1]) == 1)
what = this:my_match_room(whatstr);
else
what = player.location:match_object(whatstr);
endif
if ((!valid(what)) && match(whatstr, "^[0-9]+$"))
what = toobj(whatstr);
endif
if ($command_utils:object_match_failed(what, whatstr))
return;
endif
while (verb[dot + 1..dot + 7] == ".parent")
what = parent(what);
dot = dot + 7;
endwhile
if (dot >= length(verb))
value = {1, what};
elseif (!$object_utils:has_property(what, prop = verb[dot + 2..length(verb)]))
totell = {"Property not found"};
s = match(prop, "%([_a-z][_a-z0-9]*%)%(.*%)");
if ((verb[dot + 1] != ".") || (!s))
totell = {"Property not found.  You may be trying to evaluate code other than a property reference.  Try this instead:", tostr("  #", index(whatstr, " ") ? $string_utils:print(whatstr) | whatstr, dobjstr ? " " + dobjstr | "", " for %#", verb[dot + 1], prop)};
endif
return player:tell_lines(totell);
else
value = (this.programmer ? $code_utils | $no_one):eval_d(tostr(";return ", what, ".(", $string_utils:print(prop = verb[dot + 2..length(verb)]), ");"));
endif
if ((prepstr && this.programmer) && value[1])
pval = $string_utils:print(value[2]);
value = this:eval_cmd_string(strsub(iobjstr, "%#", pval), 0);
endif
player:print_eval_result(value);
if (prepstr && (!this.programmer))
player:tell("(Sorry, you can't evaluate arbitrary code.)");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669464, \"Erik\", #74, \"JHM\"}";
.
#52:35
"@mailverb object:verb [script]";
"Mail (using sendmail) a copy of the verb code, arguments, owner, and permissions for the verb specified to the address specified in player.email_address.";
"If the second argument is \"script\", the mail message takes the form of a script with @verb and @program commands.  If it is not specified, the message contains information as displayed by @show object:verb.";
"The @rubber-chicken verb name is a blatant and reprehensible attempt to get the author's name on a bit of moo history.";
largs = length(args);
if (((largs < 1) || ((largs == 2) && (args[2] != "script"))) || (largs > 2))
player:notify(("Usage: " + verb) + " object:verb [script]");
return;
endif
vr = $code_utils:parse_verbref(args[1]);
if (!vr)
player:notify("That's not a valid verb.");
else
object = player:my_match_object(vr[1]);
if ($command_utils:object_match_failed(object, vr[1]))
return;
else
if (!$object_utils:has_verb(object, vr[2]))
player:notify("That object does not define that verb.");
else
subj = "JaysHouseMOO verb: " + args[1];
if (!(body = verb_code(object, vr[2])))
player:notify(tostr(body));
return;
endif
"this bit stolen from $object_utils:show_verbdef.";
info = verb_info(object, vr[2]);
arg_specs = verb_args(object, vr[2]);
owner = info[1];
perms = info[2];
names = info[3];
cmnt = ((((("\"Copied from " + tostr(object)) + "@") + tostr($network.MOO_name)) + " ") + ctime()) + "\";";
body = {cmnt, @body};
if (largs == 2)
verbline = (((((((("@verb " + tostr(object)) + ":\"") + names) + "\" ") + $string_utils:from_list(arg_specs, " ")) + " ") + perms) + " ") + tostr(owner);
progline = ((("@program " + tostr(object)) + ":\"") + vr[2]) + "\"";
body = {verbline, progline, @body, "."};
else
namestr = tostr(object, ":", names);
ownstr = tostr("Owner:            ", valid(owner) ? tostr(owner:name(), " (", owner, ")") | "*** NONE ***");
permstr = tostr("Permissions:      ", perms);
dstr = tostr("Direct Object:    ", arg_specs[1]);
pstr = tostr("Preposition:      ", arg_specs[2]);
istr = tostr("Indirect Object:  ", arg_specs[3]);
body = {namestr, ownstr, permstr, dstr, pstr, istr, "", @body};
endif
$network:sendmail(player.email_address, subj, @body);
player:notify((("Mailed " + args[1]) + " to ") + player.email_address);
endif
endif
endif
.
#52:36
"'@find #<object>', '@find <player>', '@find :<verb>' '@find .<property>' - Attempt to locate things. Verbs and properties are found on any object in the player's vicinity, and some other places.";
if (!dobjstr)
player:tell("Usage: '@find #<object>' or '@find <player>' or '@find :<verb>' or '@find .<property>'.");
return;
endif
if (dobjstr[1] == ":")
name = dobjstr[2..length(dobjstr)];
this:find_verb(name);
return;
elseif (dobjstr[1] == ".")
name = dobjstr[2..length(dobjstr)];
this:find_property(name);
return;
elseif (dobjstr[1] == "#")
target = toobj(dobjstr);
if (!valid(target))
player:tell(target, " does not exist.");
endif
else
target = player:my_match_player(dobjstr);
$command_utils:player_match_result(target, dobjstr);
endif
if (valid(target))
player:tell(target:name("dc#"), " ", target:verb_sub("is"), " ", valid(target.location) ? target.location:name("#i", {"in"}) | "nowhere (#-1)", ".");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669481, \"Erik\", #74, \"JHM\"}";
.
#52:37
"'find_verb (<name>)' - Search for a verb with the given name. The objects searched are those returned by this:find_verbs_on(). The printing order relies on $list_utils:remove_duplicates to leave the *first* copy of each duplicated element in a list; for example, {1, 2, 1} -> {1, 2}, not to {2, 1}.";
name = args[1];
results = "";
objects = $list_utils:remove_duplicates(this:find_verbs_on());
for thing in (objects)
if (valid(thing) && (mom = $object_utils:has_verb(thing, name)))
results = ((((results + "   ") + thing:name()) + "(") + tostr(thing)) + ")";
mom = mom[1];
if (thing != mom)
results = ((((results + "--") + mom:name()) + "(") + tostr(mom)) + ")";
endif
endif
endfor
if (results)
this:tell("The verb :", name, " is on", results);
else
this:tell("The verb :", name, " is nowhere to be found.");
endif
.
#52:38
"'find_verbs_on ()' -> list of objects - Return the objects that @find searches when looking for a verb. The objects are searched (and the results printed) in the order returned. Feature objects are included in the search. Duplicate entries are removed by the caller.";
return {this, this.location, @valid(this.location) ? this.location:contents() | {}, @this:contents(), @this:features()};
.
#52:39
"'property_inherited_from (<object>, <property name>)' -> object - Return the ancestor of <object> on which <object>.<property> is originally defined. If <object>.<property> is not actually defined, return 0. The property is taken as originally defined on the earliest ancestor of <object> which has it. If the property is built-in, return $nothing.";
what = args[1];
prop = args[2];
if (!$object_utils:has_property(what, prop))
return 0;
elseif (prop in $code_utils.builtin_props)
return $nothing;
endif
ancestor = what;
while ($object_utils:has_property(parent(ancestor), prop))
ancestor = parent(ancestor);
endwhile
return ancestor;
.
#52:40
"'find_property (<name>)' - Search for a property with the given name. The objects searched are those returned by this:find_properties_on(). The printing order relies on $list_utils:remove_duplicates to leave the *first* copy of each duplicated element in a list; for example, {1, 2, 1} -> {1, 2}, not to {2, 1}.";
name = args[1];
results = "";
objects = $list_utils:remove_duplicates(this:find_properties_on());
for thing in (objects)
if (valid(thing) && (mom = $object_utils:has_property(thing, name)))
results = ((((results + "   ") + thing:name()) + "(") + tostr(thing)) + ")";
mom = this:property_inherited_from(thing, name);
if (thing != mom)
if (valid(mom))
results = ((((results + "--") + mom:name()) + "(") + tostr(mom)) + ")";
else
results = results + "--built-in";
endif
endif
endif
endfor
if (results)
this:tell("The property .", name, " is on", results);
else
this:tell("The property .", name, " is nowhere to be found.");
endif
.
#52:41
"'find_properties_on ()' -> list of objects - Return the objects that @find searches when looking for a property. The objects are searched (and the results printed) in the order returned. Feature objects are *not* included in the search. Duplicate entries are removed by the caller.";
return {this, this.location, @valid(this.location) ? this.location:contents() | {}, @this:contents()};
.
#52:42
"@set*property [<object>.]<property-name> to <value>";
"";
"<object> defaults to the player.";
set_task_perms(player);
if (!(spec = $code_utils:parse_propref(dobjstr)))
if ($object_utils:has_property(this, dobjstr))
object = this;
name = dobjstr;
else
player:notify(("You have no \"" + dobjstr) + "\" property to set.");
"player:notify(\"Usage:  \" + verb + \" [<object>.]<prop-name> to <value>\");";
return;
endif
else
object = player:my_match_object(spec[1]);
name = spec[2];
if ($command_utils:object_match_failed(object, spec[1]))
return;
endif
if (!$object_utils:has_property(object, name))
player:tell(object, " has no \"", name, "\" property.");
return;
endif
endif
"Let :set_name handle the .name property.    --DKO, 8/15/96";
if ((name in $code_utils.builtin_props) && (name != "name"))
if (name == "programmer")
if (player.wizard)
player:notify("Please use the @programmer command.");
else
if (object != player)
player:notify(tostr(E_PERM));
elseif (iobjstr == "0")
if (player.programmer)
player:notify("Please notify an admin if you want to get rid of your programming bit.");
else
player:notify("Your programmer bit is already unset!");
endif
elseif ($object_utils:isa(player, $prog))
if (player.programmer)
player:notify("You already have a programmer bit!");
else
player:notify("Please read `help programmer-policy' to see how to get a programmer bit.");
endif
else
player:notify("Type `@programmer me' to become a read-only programmer.");
endif
endif
elseif (name == "wizard")
if (player.wizard)
player:notify("Please use :set_wizard().");
else
player:notify("Nice try.");
endif
elseif (name in {"r", "w", "f"})
player:notify("Please use the @chmod command.");
elseif (name == "owner")
player:notify("Please use the @grant command.");
elseif (name in {"location", "contents"})
player:notify("Please use the @move command.");
elseif (name == "name")
player:notify("Please use the @rename command.");
else
player:notify(("You can't set the " + name) + " property directly.");
endif
return;
elseif ((!(has_verb = $object_utils:has_verb(object, "set_" + name))) && (!$perm_utils:controls_property(player, object, name)))
player:notify(tostr(E_PERM));
return;
endif
arg = argstr[$string_utils:word_start(argstr)[3][1]..length(argstr)];
q = $string_utils:to_value(arg);
if (q[1] == 0)
if ($command_utils:object_match_failed(value = $string_utils:match_object(arg, player.location), arg))
return;
endif
else
value = q[2];
endif
if ((object == player) && (name == "name"))
old_dnamec = player:dnamec();
endif
if (has_verb)
ret = `object:("set_" + name)(value) ! ANY';
elseif (matchspec = rmatch(name, ".*_msg$"))
message = name[1..matchspec[1] - 1];
ret = `object:set_message(message, value, player.pronoun_sub_style) ! ANY';
else
ret = E_VERBNF;
endif
if ((ret == E_VERBNF) && (!(has_verb && (name in $code_utils.builtin_props))))
"handle :set_* wrappers as elegantly as possible (not very) -- splat";
try
object.(name) = value;
ret = 1;
except (E_PERM)
player:notify(tostr(E_PERM));
return;
endtry
endif
if (typeof(ret) == ERR)
player:notify(tostr("You can't set ", object, ".", name, " to ", $string_utils:print(value), (ret == E_NONE) ? "." | ((": " + tostr(ret)) + ".")));
else
player:notify(tostr(object, ".", name, " set to ", $string_utils:print(value), "."));
"Prevent silent player name changes.";
if ((object == player) && (name == "name"))
player.location:announce(old_dnamec, " ", player:verb_sub("changes"), " ", player:pp(), " name to ", player:dname(), ".");
endif
endif
"Copied from Doug (#107):@set by Doug (#107) Thu Aug 15 17:57:14 1996 EDT";
"Changed 4/19/97 by splat to handle exceptions and :set_* wrappers.";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863649823, \"Erik\", #74, \"JHM\"}";
.
#52:43
"@dump something [with [id=...] [noprops] [noverbs] [create]]";
"This spills out all properties and verbs on an object, calling suspend at appropriate intervals.";
"   id=#nnn -- specifies an idnumber to use in place of the object's actual id (for porting to another MOO)";
"   noprops -- don't show properties.";
"   noverbs -- don't show verbs.";
"   create  -- indicates that a @create command should be generated and all of the verbs be introduced with @verb rather than @args; the default assumption is that the object already exists and you're just doing this to have a look at it.";
set_task_perms(player);
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
return;
endif
if (prepstr && (prepstr != "with"))
player:notify(tostr("Usage:  ", verb, " something [with [id=...] [noprops] [noverbs] [create]]"));
return;
endif
0 && player:receive_document({$jaddress.browser_object, {{"objnum", tostr(dobj)}}});
targname = tostr(dobj);
options = {"props", "verbs"};
create = 0;
if (iobjstr)
for o in ($string_utils:explode(iobjstr))
if (index(o, "id=") == 1)
targname = o[4..length(o)];
elseif (o in {"noprops", "noverbs"})
options = setremove(options, o[3..length(o)]);
elseif (o in {"create"})
create = 1;
endif
endfor
endif
if (create)
parent = parent(dobj);
pstring = tostr(parent);
for p in (properties(#0))
if (#0.(p) == parent)
pstring = "$" + p;
endif
endfor
player:notify(tostr("@create ", pstring, " named ", dobj:name(), ":", $string_utils:from_list(dobj.aliases, ",")));
endif
for p in (("props" in options) ? properties(dobj) | {})
pquoted = $string_utils:print(p);
info = property_info(dobj, p);
value = dobj.(p);
if (create)
uvalue = (typeof(value) == LIST) ? "{}" | 0;
player:notify(tostr("@prop ", targname, ".", pquoted, " ", uvalue || $string_utils:print_suspended(value), " ", info[2] || "\"\"", (info[1] == dobj.owner) ? "" | tostr(" ", info[1])));
if (uvalue && value)
player:notify(tostr(";", targname, ".(", pquoted, ") = ", $string_utils:print_suspended(value)));
endif
else
if (info[2] != "rc")
player:notify(tostr("@chmod ", targname, ".", pquoted, " ", info[2]));
endif
if (info[1] != dobj.owner)
player:notify(tostr("@chown ", targname, ".", pquoted, " ", info[1]));
endif
player:notify(tostr(";", targname, ".(", pquoted, ") = ", $string_utils:print_suspended(value)));
endif
$command_utils:suspend_if_needed(0);
endfor
for a in (("props" in options) ? $object_utils:ancestors(dobj) | {})
for p in (properties(a))
$command_utils:suspend_if_needed(1);
pquoted = $string_utils:print(p);
value = dobj.(p);
avalue = a.(p);
if (typeof(value) == ERR)
player:notify(tostr("\"", targname, ".(", pquoted, ") => ", $code_utils:error_name(value), " (", value, ")"));
elseif ((typeof(avalue) == ERR) || (value != avalue))
player:notify(tostr(";", targname, ".(", pquoted, ") = ", $string_utils:print_suspended(value)));
endif
endfor
$command_utils:suspend_if_needed(1);
endfor
if (!("verbs" in options))
return;
endif
$command_utils:suspend_if_needed(0);
v = tostr(0);
while ((info = verb_info(dobj, v)) || (info == E_PERM))
argspec = verb_args(dobj, v);
player:receive_document({$jaddress.browser_verb, {{"objnum", tostr(dobj)}, {"verbnum", v}, {"verbname", info[3]}, {"dobjspec", argspec[1]}, {"prepspec", argspec[2]}, {"iobjspec", argspec[3]}}});
if (index(tostr(" ", info[3], " "), " * "))
"... oh shit, we have a * verb.  may as well forget trying to list...";
"... the rest; they're invisible.  set v to something nonstring.";
v = E_TYPE;
else
v = tostr(tonum(v) + 1);
endif
$command_utils:suspend_if_needed(0);
endwhile
.
#52:44
"Usage:  :print_eval_result(result)";
"";
"Print the given result to this, with appropriate permissions.";
"";
"Result is of the form {TRUE, value, ticks, seconds}, {TRUE, value}, or {FALSE, reason for failure}.";
"";
"Will print seconds and ticks left, if they're provided and this programmer has @eval-option +time.";
result = args[1];
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
tell = "notify";
else
tell = "tell";
endif
if (result[1])
val = result[2];
if (typeof(val) == OBJ)
this:(tell)(tostr("=> ", $string_utils:print_with_names(val)));
0 && this:(tell)(tostr("=> ", val, "  ", valid(val) ? ("(" + val:name()) + ")" | ((a = $list_utils:assoc(val, {{#-1, "<$nothing>"}, {#-2, "<$ambiguous_match>"}, {#-3, "<$failed_match>"}})) ? a[2] | "<invalid>")));
elseif (typeof(val) == ERR)
this:(tell)(tostr("=> ", $code_utils:error_name(val), "  (", val, ")"));
elseif ((typeof(val) == LIST) && this:eval_option("print_names"))
this:(tell)(tostr("=> ", $string_utils:print_with_names(result[2])));
else
this:(tell)(tostr("=> ", $string_utils:print(result[2])));
endif
if ((this:eval_option("time") && (!output_delimiters(this)[2])) && (length(result) > 2))
this:(tell)(tostr("[used ", result[3], " tick", (result[3] != 1) ? "s, " | ", ", result[4], " second", (result[4] != 1) ? "s" | "", ".]"));
endif
else
this:(tell + "_lines")(result[2]);
nerrors = length(result[2]);
this:(tell)(tostr(nerrors, " error", (nerrors == 1) ? "." | "s."));
endif
.
#52:45
if (children(this))
help = this.help;
if (typeof(help) == OBJ)
help = {help};
elseif (typeof(help) != LIST)
help = {};
endif
help = setremove(help, #3212);
else
help = {};
endif
return {@pass(@args), @help};
"Metadata 202106";
"Last-Modify: {833294291, \"Ken\", #75, \"JHM\"}";
.
#52:46
"Charter: given a block of verb code lines from the user, transform it into code ready to be passed to set_verb_code().  In particular, reverse any transformation made by :verbcode_internal_to_external.";
"This version transforms `// foo' comments to `\"foo\";' comments.";
lines = args[1];
new_comments = this:list_option("//_comments");
if (!new_comments)
return lines;
endif
newlines = {};
pat = ("^[ " + $string_utils.tab) + "]*// ?%(.*%)$";
for line in (lines)
mat = match(line, pat);
if (mat)
comment = substitute("%1", mat);
out = $code_utils:commentify({comment});
newlines = {@newlines, out[1]};
else
newlines = {@newlines, line};
endif
endfor
return newlines;
"Metadata 202106";
"Last-Modify: {912983903, \"Ken\", #75, \"JHM\"}";
.
#52:47
"Charter: given a block of verb code lines from the user, transform it into code ready to be passed to set_verb_code().  In particular, reverse any transformation made by :verbcode_internal_to_external.";
"This version transforms `// foo' comments to `\"foo\";' comments.";
lines = args[1];
new_comments = this:list_option("//_comments");
if (!new_comments)
return lines;
endif
newlines = {};
for line in (lines)
mat = match(line, "^ *// ?%(.*%)$");
if (mat)
comment = substitute("%1", mat);
out = $code_utils:commentify({comment});
newlines = {@newlines, out[1]};
else
newlines = {@newlines, line};
endif
endfor
return newlines;
.
#52:48
"@rdisplay <object>[.[property]]*[,[inherited_property]]*[:[verb-regexp]]*[;[inherited_verb]]*";
"null names for properties and verbs are interpreted as meaning all of them.";
opivu = {{}, {}, {}, {}, {}};
string = "";
punc = 1;
literal = 0;
for jj in [1..length(argstr)]
j = argstr[jj];
if (literal)
string = string + j;
literal = 0;
elseif (j == "\\")
literal = 1;
elseif (y = index(".,:;", j))
opivu[punc] = {@opivu[punc], string};
punc = 1 + y;
string = "";
else
string = string + j;
endif
endfor
opivu[punc] = {@opivu[punc], string};
objname = opivu[1][1];
it = this:my_match_object(objname);
if ($command_utils:object_match_failed(it, objname))
return;
endif
readable = (it.owner == this) || (it.r || this.wizard);
cant = {};
if ("" in opivu[2])
if (readable)
prop = properties(it);
else
prop = {};
cant = setadd(cant, it);
endif
if (this:display_option("firstancestor"))
what = it;
while ((!prop) && valid(what = parent(what)))
if ((what.owner == this) || (what.r || this.wizard))
prop = properties(what);
else
cant = setadd(cant, what);
endif
endwhile
endif
else
prop = opivu[2];
endif
if ("" in opivu[3])
inh = {};
for what in ({it, @$object_utils:ancestors(it)})
if (((what.owner == this) || what.r) || this.wizard)
inh = {@inh, @properties(what)};
else
cant = setadd(cant, what);
endif
endfor
else
inh = opivu[3];
endif
for q in (inh)
if (q in properties(it))
prop = setadd(prop, q);
inh = setremove(inh, q);
endif
endfor
vrb = {};
if ("" in opivu[4])
if (readable)
vrbs = verbs(it);
else
vrbs = {};
cant = setadd(cant, it);
endif
what = it;
if (this:display_option("firstancestor"))
while ((!vrbs) && valid(what = parent(what)))
if ((what.owner == this) || (what.r || this.wizard))
vrbs = verbs(what);
else
cant = setadd(cant, what);
endif
endwhile
endif
for n in [1..length(vrbs)]
vrb = setadd(vrb, {what, n});
endfor
else
for w in (opivu[4])
"HACKED dec. 28 1994 by Doug";
"if (y = $object_utils:has_verb(it, w))";
"vrb = setadd(vrb, {y[1], w});";
if (z = $object_utils:("match_verbs(new)")(it, w))
for y in (z)
for v in (y[2])
vrb = setadd(vrb, {y[1], v});
endfor
endfor
"ENDHACK";
else
this:notify(tostr("No such verb, \"", w, "\""));
endif
endfor
endif
if ("" in opivu[5])
for z in ({it, @$object_utils:ancestors(it)})
if (((this == z.owner) || z.r) || this.wizard)
for n in [1..length(verbs(z))]
vrb = setadd(vrb, {z, n});
endfor
else
cant = setadd(cant, z);
endif
endfor
else
for w in (opivu[5])
if (typeof(y = $object_utils:has_verb(it, w)) == LIST)
vrb = setadd(vrb, {y[1], w});
else
this:notify(tostr("No such verb, \"", w, "\""));
endif
endfor
endif
if (({""} in opivu) || (opivu[2..5] == {{}, {}, {}, {}}))
this:notify(tostr(it:name(), " (", it, ") [ ", it.r ? "readable " | "", it.w ? "writeable " | "", it.f ? "fertile " | "", is_player(it) ? "(player) " | "", it.programmer ? "programmer " | "", it.wizard ? "wizard " | "", "]"));
if (it.owner != (is_player(it) ? it | this))
this:notify(tostr("  Owned by ", valid(p = it.owner) ? p:name() | "** extinct **", " (", p, ")."));
endif
this:notify(tostr("  Child of ", valid(p = parent(it)) ? p:name() | "** none **", " (", p, ")."));
if (it.location != $nothing)
this:notify(tostr("  Location ", valid(p = it.location) ? p:name() | "** unplace (tell a wizard, fast!) **", " (", p, ")."));
endif
endif
set_task_perms(this.owner);
blankargs = this:display_option("blank_tnt") ? {"this", "none", "this"} | #-1;
for b in (vrb)
$command_utils:suspend_if_needed(0);
{where, q} = b;
short = (typeof(q) == NUM) ? q | strsub((y = index(q, " ")) ? q[1..y - 1] | q, "*", "");
inf = verb_info(where, short);
if ((typeof(inf) == LIST) || (inf == E_PERM))
name = (typeof(inf) == LIST) ? inf[3] | q;
name = index(name, " ") ? ("\"" + name) + "\"" | name;
line = $string_utils:left(tostr($string_utils:right(tostr(where), 6), ":", name, " "), 32);
if (inf == E_PERM)
line = line + "   ** unreadable **";
else
line = $string_utils:left(tostr(line, inf[1]:name(), " (", inf[1], ") "), 53) + ((i = inf[2] in {"x", "xd", "d", "rd"}) ? {" x", " xd", "  d", "r d"}[i] | inf[2]);
vargs = verb_args(where, short);
if (vargs != blankargs)
if (this:display_option("shortprep") && (!(vargs[2] in {"any", "none"})))
vargs[2] = $code_utils:short_prep(vargs[2]);
endif
line = $string_utils:left(line + " ", 60) + $string_utils:from_list(vargs, " ");
endif
endif
this:notify(line);
elseif (inf == E_VERBNF)
this:notify(tostr(inf));
this:notify(tostr("  ** no such verb, \"", short, "\" **"));
else
this:notify("This shouldn't ever happen. @display is buggy.");
endif
endfor
all = {@prop, @inh};
max = (length(all) < 4) ? 999 | (abs(this.linelen) - 54);
depth = (length(all) < 4) ? -1 | 1;
truncate_owner_names = length(all) > 1;
for q in (all)
$command_utils:suspend_if_needed(0);
inf = property_info(it, q);
if (inf == E_PROPNF)
if (q in $code_utils.builtin_props)
this:notify(tostr($string_utils:left("," + q, 25), "Built in property ", (length(y = $string_utils:from_value(it.(q), 1, depth)) > max) ? y[1..max] + ".." | y));
else
this:notify(tostr("  ** property not found, \"", q, "\" **"));
endif
else
pname = $string_utils:left(tostr((q in properties(it)) ? "." | (is_clear_property(it, q) ? " " | ","), q, " "), 25);
if (inf == E_PERM)
this:notify(pname + "   ** unreadable **");
else
oname = inf[1]:name();
truncate_owner_names && ((length(oname) > 12) && (oname = oname[1..12]));
(inf[2][1] != "r") && (inf[2][1..0] = " ");
(inf[2][2] != "w") && (inf[2][2..1] = " ");
this:notify($string_utils:left(tostr($string_utils:left(tostr(pname, oname, " (", inf[1], ") "), 47), inf[2], " "), 54) + $string_utils:print_truncated(it.(q), max, ".."));
endif
endif
endfor
if (cant)
failed = {};
for k in (cant)
failed = listappend(failed, tostr(k:name(), " (", k, ")"));
endfor
this:notify($string_utils:centre(tostr(" no permission to read ", $string_utils:english_list(failed, ", ", " or ", " or "), ". "), 75, "-"));
else
this:notify($string_utils:centre(" finished ", 75, "-"));
endif
"Copied from generic programmer (#58):@d by Doug (#107) Tue Jan  3 19:02:19 1995 EST";
"Copied from Doug (#107):@rd by Doug (#107) Tue Jan  3 19:42:18 1995 EST";
.
#52:49
set_task_perms(player);
dotraces = 0;
if (prepstr)
if ((prepstr == "with") && (iobjstr == "traces"))
dotraces = 1;
else
return player:tell("Usage: @forked [player] [with traces]");
endif
endif
if (!dobjstr)
tasks = queued_tasks();
elseif ($command_utils:player_match_result(dobj = player:my_match_player(dobjstr), dobjstr)[1])
return;
elseif (typeof(tasks = $wiz_utils:queued_tasks(dobj)) != LIST)
player:notify(tostr(verb, " ", dobj:name(), "(", dobj, "):  ", tasks));
return;
endif
if (tasks)
su = $string_utils;
player:notify("Queue ID    Start Time            Owner         Verb (Line) [This]");
player:notify("--------    ----------            -----         ------------------");
now = time();
for task in (tasks)
q_id = task[1];
start = task[2];
time = (start >= now) ? ctime(start)[5..24] | su:left((start == -1) ? "Reading input ..." | tostr(now - start, " seconds ago..."), 20);
owner = task[5];
owner_name = valid(owner) ? owner:name() | tostr("Dead ", owner);
vloc = task[6];
vname = task[7];
lineno = task[8];
this = task[9];
trace = {};
if (dotraces && (!`trace = task_stack(q_id, 1) ! E_INVARG'))
forkedmsg = "   (forked)";
else
forkedmsg = "";
endif
player:notify(tostr(su:left(tostr(q_id), 10), "  ", time, "  ", su:left(owner_name, 12), "  ", vloc, ":", vname, " (", lineno, ")", (this != vloc) ? tostr(" [", this, "]") | "", forkedmsg));
if (dotraces && trace)
for line in ($code_utils:format_traceback(trace)[1..$ - 1])
player:notify("  " + line);
endfor
endif
endfor
if (dotraces)
player:tell("(End of list.)");
endif
else
player:notify("No tasks.");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {893174792, \"Ken\", #75, \"JHM\"}";
.
#52:50
if ($__core_init_phase)
"remove httpd help";
if (this == $prog)
this.help = setremove(this.help, #3212);
endif
pass(@args);
endif
return;
"Metadata 202106";
"Last-Modify: {833072675, \"Ken\", #75, \"JHM\"}";
.
#52:51
{reference, lines} = args;
set_task_perms(caller_perms());
args = $string_utils:words(reference);
punt = 1;
if (!(spec = $code_utils:parse_verbref(args[1])))
raise(E_INVARG, "Invalid reference: " + reference);
elseif ($command_utils:object_match_failed(object = player:my_match_object(spec[1]), spec[1]))
return;
elseif ($string_utils:is_numeric(spec[2]))
"numeric verbref";
if ((verbname = $code_utils:tonum(spec[2])) == E_TYPE)
raise(E_INVARG, "Invalid verb number");
elseif (length(args) > 1)
raise(E_INVARG, "Invalid reference: " + referenec);
elseif ((verbname < 1) || `verbname > length(verbs(object)) ! E_PERM => 0')
raise(E_INVARG, "Verb number out of range.");
else
argspec = 0;
punt = 0;
endif
elseif (typeof(argspec = $code_utils:parse_argspec(@listdelete(args, 1))) != LIST)
raise(E_INVARG, tostr(argspec));
elseif (argspec[2])
raise(E_INVARG, $string_utils:from_list(argspec[2], " ") + "??");
elseif (length(argspec = argspec[1]) in {1, 2})
raise(E_INVARG, {"Missing preposition", "Missing iobj specification"}[length(argspec)]);
else
punt = 0;
verbname = spec[2];
if (index(verbname, "*") > 1)
verbname = strsub(verbname, "*", "");
endif
endif
"...";
"...if we have an argspec, we'll need to reset verbname...";
"...";
if (punt)
elseif (argspec)
named = argspec[4..min(5, $)];
argspec = argspec[1..3];
if (!(argspec[2] in {"none", "any"}))
argspec[2] = $code_utils:full_prep(argspec[2]);
endif
loc = $code_utils:find_verb_named_1_based(object, verbname);
while (loc && (`verb_args(object, loc) ! E_PERM' != argspec))
loc = $code_utils:find_verb_named_1_based(object, verbname, loc + 1);
endwhile
if (loc)
verbname = loc;
else
punt = "...can't find it....";
raise(E_INVARG, "That object has no verb matching that name + args.");
endif
else
named = {};
loc = (typeof(verbname) == NUM) ? verbname | 0;
endif
if (!punt)
try
info = verb_info(object, verbname);
except e (ANY)
if (e[1] == E_VERBNF)
raise(E_INVARG, "That object does not have that verb definition.");
else
raise(E_INVARG, tostr(e[2]));
endif
punt = 1;
endtry
if (!punt)
aliases = info[3];
if (!loc)
loc = aliases in (verbs(object) || {});
endif
endif
endif
if (punt)
return;
else
lines = this:verbcode_external_to_internal(lines);
if (named)
code = $code_utils:split_verb_code(lines);
lines = {@code[1], @$code_utils:named_args_to_code(named), @code[2]};
endif
try
result = set_verb_code(object, verbname, lines);
except e (ANY)
result = e[2] + " ";
"just in case some idiot throws an error with an empty string";
endtry
what = tostr(object:name(), ":", aliases, @loc ? {"[", loc, "](", loc - 1, ")."} | "[??]");
if (result)
if (typeof(result) == STR)
return {"Error programming " + what, result, "Verb not programmed."};
else
return {"Error programming " + what, @result, tostr(length(result), " error(s)."), "Verb not programmed."};
endif
else
return {"0 errors.", "Verb programmed."};
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895598937, \"Ken\", #75, \"JHM\"}";
.
#53:0
":eval_d(code...) => {compiled?,result}";
"This works exactly like the builtin eval() except that the code is evaluated ";
"as if the d flag were unset.";
code = {"set_verb_code(this,2,{\"\\\"Do not remove this verb!  This is an auxiliary verb for :eval_d().\\\";\"});", "dobj=iobj=this=#-1;", "dobjstr=iobjstr=prepstr=argstr=verb=\"\";", tostr("caller=", caller, ";"), "set_task_perms(caller_perms());", @args};
if (!caller_perms().programmer)
return E_PERM;
elseif (svc = set_verb_code(this, 2, code))
lines = {};
for line in (svc)
if ((index(line, "Line ") == 1) && (n = tonum(line[6..(colon = index(line + ":", ":")) - 1])))
lines = {@lines, tostr("Line ", n - 5, line[colon..length(line)])};
else
lines = {@lines, line};
endif
endfor
return {0, lines};
else
set_task_perms(caller_perms());
return {1, this:("1")()};
endif
.
#53:1
"Do not remove this verb!  This is an auxiliary verb for :eval_d().";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {913092933, \"Jay\", #2, \"JHM\"}";
.
#53:2
":tonum(number as string) => number";
return match(s = args[1], "^ *[-+]?[0-9]+ *$") ? tonum(s) | E_TYPE;
.
#53:3
":toobj(objectid as string) => objectid";
return match(s = args[1], "^ *#[-+]?[0-9]+ *$") ? toobj(s) | E_TYPE;
.
#53:4
"toerr(n), toerr(\"E_FOO\"), toerr(\"FOO\") => E_FOO.";
if (typeof(s = args[1]) != STR)
n = tonum(s) + 1;
if (n > length(this.error_list))
return 1;
endif
elseif (!(n = (s in this.error_names) || (("E_" + s) in this.error_names)))
return 1;
endif
return this.error_list[n];
.
#53:5
"error_name(E_FOO) => \"E_FOO\"";
return this.error_names[tonum(args[1]) + 1];
.
#53:6
set_task_perms(caller_perms());
object = args[1];
what = {@args, {"props", "verbs"}}[2];
player:notify(tostr("Object ID:  ", object));
player:notify(tostr("Name:       ", object:name()));
names = {"Parent", "Location", "Owner"};
vals = {parent(object), object.location, object.owner};
for i in [1..length(vals)]
if (!valid(vals[i]))
val = "*** NONE ***";
else
val = ((vals[i]:name() + " (") + tostr(vals[i])) + ")";
endif
player:notify(tostr(names[i], ":      "[1..12 - length(names[i])], val));
endfor
line = "Flags:     ";
if (is_player(object))
line = line + " player";
endif
for flag in ({"programmer", "wizard", "r", "w", "f"})
if (object.(flag))
line = (line + " ") + flag;
endif
endfor
player:notify(line);
if (player.programmer && ((player.wizard || (player == object.owner)) || object.r))
if (("verbs" in what) && verbs(object))
player:notify("Verb definitions:");
for v in (verbs(object))
$command_utils:suspend_if_needed(0);
player:notify(tostr("    ", v));
endfor
endif
if ("props" in what)
if (properties(object))
player:notify("Property definitions:");
for p in (properties(object))
$command_utils:suspend_if_needed(0);
player:notify(tostr("    ", p));
endfor
endif
all_props = $object_utils:all_properties(object);
if (all_props != {})
player:notify("Properties:");
for p in (all_props)
$command_utils:suspend_if_needed(0);
val = object.(p);
if (val == E_PERM)
STR = "(Permission denied.)";
else
STR = $string_utils:from_value(val, 1, -1);
endif
player:notify(tostr("    ", p, ": ", STR));
endfor
endif
endif
elseif (player.programmer)
player:notify("** Can't list properties or verbs: permission denied.");
endif
if (object.contents)
player:notify("Contents:");
for o in (object.contents)
$command_utils:suspend_if_needed(0);
player:notify(tostr("    ", o:name(), " (", o, ")"));
endfor
endif
.
#53:7
set_task_perms(caller_perms());
object = args[1];
pname = args[2];
if (pname in this.builtin_props)
player:notify(tostr(object, ".", pname));
player:notify("Built-in property.");
else
info = property_info(object, pname);
if (typeof(info) == ERR)
player:notify(tostr(info));
return;
endif
owner = info[1];
perms = info[2];
player:notify(tostr(object, ".", pname));
player:notify(tostr("Owner:        ", valid(owner) ? tostr(owner:name(), " (", owner, ")") | "*** NONE ***"));
player:notify(tostr("Permissions:  ", perms));
endif
player:notify(tostr("Value:        ", $string_utils:print(object.(pname))));
.
#53:8
{object, vname} = args;
set_task_perms(caller_perms());
if (!(hv = $object_utils:has_verb(object, vname)))
player:notify("That object does not define that verb.");
return;
elseif (hv[1] != object)
player:notify(tostr("Object ", object, " does not define that verb, but its ancestor ", hv[1], " does."));
object = hv[1];
endif
try
{owner, perms, names} = verb_info(object, vname);
except e (ANY)
player:notify(e[2]);
return;
endtry
{direct, prep, indirect} = verb_args(object, vname);
player:notify(tostr(object, ":", names));
player:notify(tostr("Owner:            ", valid(owner) ? tostr(owner:name(), " (", owner, ")") | "*** NONE ***"));
player:notify(tostr("Permissions:      ", perms));
player:notify(tostr("Direct Object:    ", direct));
player:notify(tostr("Preposition:      ", prep));
player:notify(tostr("Indirect Object:  ", indirect));
.
#53:9
if (args[4..5] == {"none", "this"})
return 0;
endif
thisobj = args[1];
verb = args[2];
adobj = args[3];
aprep = args[4];
aiobj = args[5];
prep_part = (aprep == "any") ? "to" | this:short_prep(aprep);
".........`any' => `to' (arbitrary),... `none' => empty string...";
if ((adobj == "this") && (dobj == thisobj))
dobj_part = dobjstr;
iobj_part = ((!prep_part) || (aiobj == "none")) ? "" | ((aiobj == "this") ? dobjstr | iobjstr);
elseif ((aiobj == "this") && (iobj == thisobj))
dobj_part = (adobj == "any") ? dobjstr | ((adobj == "this") ? iobjstr | "");
iobj_part = iobjstr;
elseif (!("this" in args[3..5]))
dobj_part = (adobj == "any") ? dobjstr | "";
iobj_part = (prep_part && (aiobj == "any")) ? iobjstr | "";
else
return 0;
endif
return tostr(verb, dobj_part ? " " + dobj_part | "", prep_part ? " " + prep_part | "", iobj_part ? " " + iobj_part | "");
.
#53:10
"returns the permissions of the current verb (either the owner or the result of the most recent set_task_perms()).";
return caller_perms();
.
#53:11
"returns the object where the current verb is defined.";
return callers()[1][4];
.
#53:12
":verb_documentation([object,verbname]) => documentation at beginning of verb code, if any";
"default is the calling verb";
set_task_perms(caller_perms());
if (args)
{object, vname} = args;
else
{_, vname, _, object, @_} = callers()[1];
endif
try
code = verb_code(object, vname);
except e (ANY)
return e[1];
endtry
return this:uncommentify(this:split_verb_code(code)[1]);
if (OLD_VERSION)
doc = {};
for line in (code)
if (match(line, "^\"%([^\\\"]%|\\.%)*\";$"))
"... now that we're sure `line' is just a string, eval() is safe...";
doc = {@doc, $no_one:eval(line)[2]};
else
return doc;
endif
endfor
return doc;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863662892, \"Erik\", #74, \"JHM\"}";
.
#53:13
":set_verb_documentation(object,verbname,text)";
"  changes documentation at beginning of verb code";
"  text is either a string or a list of strings";
"  returns a non-1 value if anything bad happens...";
{object, vname, text} = args;
set_task_perms(caller_perms());
if (typeof(code = verb_code(object, vname)) == ERR)
return code;
elseif (typeof(vd = $code_utils:verb_documentation(object, vname)) == ERR)
return vd;
elseif (!(typeof(text) in {LIST, STR}))
raise(E_INVARG, "Verb documentation must be a string or a list of strings.");
else
newdoc = {};
for l in ((typeof(text) == LIST) ? text | {text})
if (typeof(l) != STR)
raise(E_INVARG, "Verb documentation must be a string or a list of strings.");
endif
newdoc = {@newdoc, toliteral(l) + ";"};
endfor
if ((ERR == typeof(svc = set_verb_code(object, vname, {@newdoc, @code[length(vd) + 1..length(code)]}))) || svc)
"... this shouldn't happen.  I'm not setting this code -d just yet...";
return svc;
else
return 1;
endif
endif
.
#53:14
"$code_utils:parse_propref(string)";
"Parses string as a MOO-code property reference, returning {object-string, prop-name-string} for a successful parse and false otherwise.  It always returns the right object-string to pass to, for example, this-room:match_object.";
s = args[1];
if (dot = rindex(s, "."))
object = s[1..dot - 1];
prop = s[dot + 1..length(s)];
if ((object == "") || (prop == ""))
return 0;
elseif (0 && (object[1] == "$"))
"might be something like $feature->foo";
object = $string_utils:literal_object(object);
if (typeof(object) != OBJ)
return 0;
endif
object = tostr(object);
endif
elseif (index(s, "$") == 1)
object = "#0";
prop = s[2..length(s)];
else
return 0;
endif
return {object, prop};
.
#53:15
"$code_utils:parse_verbref(string)";
"Parses string as a MOO-code verb reference, returning {object-string, verb-name-string} for a successful parse and false otherwise.  It always returns the right object-string to pass to, for example, this-room:match_object().";
s = args[1];
if (colon = index(s, ":"))
object = s[1..colon - 1];
verbname = s[colon + 1..length(s)];
if (!(object && verbname))
return 0;
elseif ((object[1] == "$") && 0)
"Why was this check taking place here?";
pname = object[2..length(object)];
if ((!(pname in properties(#0))) || (typeof(object = #0.(pname)) != OBJ))
return 0;
endif
object = tostr(object);
endif
return {object, verbname};
else
return 0;
endif
.
#53:16
"Usage:";
"";
"Attempts to parse the given sequence of args into a verb_arg specification.";
"";
"Returns {verb_args, remaining_args} if successful.  Verb args will usually be {dobjspec, prepspec, iobjspec}, but can also be {\"this\", \"none\", \"this\", named_args, rest_arg}, when args are given in `named' form.  Returns a string error message if parsing fails.";
"";
"  e.g., :parse_argspec(\"this\",\"in\",\"front\",\"of\",\"any\",\"foo\"..)";
"           => {{\"this\",\"in front of\",\"any\"},{\"foo\"..}}";
"";
"        :parse_argspec(\"(foo,\",\"bar,\",\"@baz)\",\"rxd\")";
"           => {{\"this\", \"none\", \"this\", {\"foo\", \"bar\"}, \"baz\"}, {\"rxd\"}}";
if (!args)
return {{}, {}};
elseif ((ds = args[1]) == "tnt")
return {{"this", "none", "this"}, listdelete(args, 1)};
elseif (!(ds in {"this", "any", "none"}))
if (ds == "()")
return {{"this", "none", "this", {}, ""}, listdelete(args, 1)};
elseif (ds[1] == "(")
args[1][1..1] = "";
regular = {};
rest = "";
remaining = 0;
while (args)
arg = args[1];
args = listdelete(args, 1);
if (match(arg, "^[a-z_][a-z_0-9]*,$"))
regular = {@regular, arg[1..$ - 1]};
elseif (match(arg, "^@?[a-z_][a-z_0-9]*)$"))
if (arg[1] == "@")
rest = arg[2..$ - 1];
else
regular = {@regular, arg[1..$ - 1]};
endif
remaining = args;
args = {};
else
return tostr("Can't parse named arg `", arg, "'");
endif
endwhile
if (typeof(remaining) == LIST)
return {{"this", "none", "this", regular, rest}, remaining};
else
return "Couldn't find close parenthesis for named args.";
endif
else
return tostr("\"", ds, "\" is not a valid direct object specifier.");
endif
elseif (`args[2] in {"none", "any"} ! E_RANGE => 1')
verbargs = args[1..min(3, $)];
rest = args[4..$];
elseif (!({prep, @unused} = $code_utils:get_prep(@args[2..$]))[1])
return tostr("\"", args[2], "\" is not a valid preposition.");
else
verbargs = {ds, prep, @unused[1..min(1, $)]};
rest = unused[2..$];
endif
if ((length(verbargs) >= 3) && (!(verbargs[3] in {"this", "any", "none"})))
return tostr("\"", verbargs[3], "\" is not a valid indirect object specifier.");
endif
return {verbargs, rest};
.
#53:17
if (server_version() != this._version)
this:_fix_preps();
endif
return this.prepositions;
.
#53:18
":short_prep(p) => shortest preposition equivalent to p";
"p may be a single word or one of the strings returned by verb_args().";
if (server_version() != this._version)
this:_fix_preps();
endif
word = args[1];
word = word[1..index(word + "/", "/") - 1];
if (p = word in this._other_preps)
return this._short_preps[this._other_preps_n[p]];
elseif (word in this._short_preps)
return word;
else
return "";
endif
.
#53:19
if (server_version() != this._version)
this:_fix_preps();
endif
prep = args[1];
if (p = prep in this._short_preps)
return this.prepositions[p];
elseif (p = prep in this._other_preps)
return this.prepositions[this._other_preps_n[p]];
else
return "";
endif
.
#53:20
":get_prep(@args) extracts the prepositional phrase from the front of args, returning a list consisting of the preposition (or \"\", if none) followed by the unused args.";
":get_prep(\"in\",\"front\",\"of\",...) => {\"in front of\",...}";
":get_prep(\"inside\",...)          => {\"inside\",...}";
":get_prep(\"frabulous\",...}       => {\"\", \"frabulous\",...}";
prep = "";
allpreps = {@this._short_preps, @this._other_preps};
rest = 1;
for i in [1..length(args)]
attempt = (i == 1) ? args[1] | tostr(attempt, " ", args[i]);
if (attempt in allpreps)
prep = attempt;
rest = i + 1;
endif
if (!(attempt in this._multi_preps))
return {prep, @args[rest..$]};
endif
endfor
return {prep, @args[rest..$]};
.
#53:21
":_fix_preps() updates the properties on this having to do with prepositions.";
"_fix_preps should be called whenever we detect that a new server version has been installed.";
orig_args = verb_args(this, verb);
multis = nothers = others = shorts = longs = {};
i = 0;
while (typeof(set_verb_args(this, verb, {"this", tostr(i), "this"})) != ERR)
l = verb_args(this, verb)[2];
all = $string_utils:explode(l, "/");
s = all[1];
for p in (listdelete(all, 1))
if (length(p) <= length(s))
s = p;
endif
endfor
for p in (all)
while (j = rindex(p, " "))
multis = {p = p[1..j - 1], @multis};
endwhile
endfor
longs = {@longs, l};
shorts = {@shorts, s};
others = {@others, @setremove(all, s)};
nothers = {@nothers, @$list_utils:make(length(all) - 1, length(shorts))};
i = i + 1;
endwhile
set_verb_args(this, verb, orig_args);
this.prepositions = longs;
this._short_preps = shorts;
this._other_preps = others;
this._other_preps_n = nothers;
this._multi_preps = multis;
this._version = server_version();
return;
.
#53:22
":find_verb_named(object,name[,n])";
"  returns the *number* of the first verb on object matching the given name.";
"  optional argument n, if given, starts the search with verb n,";
"  causing the first n verbs (0..n-1) to be ignored.";
"  -1 is returned if no verb is found.";
"  This routine does not find inherited verbs.";
{object, name, ?n = 0} = args;
for i in [n + 1..length(verbs(object))]
verbinfo = verb_info(object, i);
if (this:verbname_match(verbinfo[3], name))
return i - 1;
endif
endfor
return -1;
.
#53:23
":find_verb_named(object,name[,n])";
"  returns the (0-based) *number* of the last verb on object matching the given name.";
"  optional argument n, if given, starts the search with verb n-1,";
"  causing verbs (n..length(verbs(object))-1) to be ignored.";
"  -1 is returned if no verb is found.";
"  This routine does not find inherited verbs.";
{object, name, ?last = length(verbs(object))} = args;
if (last < 0)
"last == -1 equivalent to omitting last; ";
"maybe we can deprecate this...";
last = length(verbs(object));
endif
for i in [1..last]
verbinfo = verb_info(object, tostr(last - i));
if (this:verbname_match(verbinfo[3], name))
return last - i;
endif
endfor
return -1;
.
#53:24
":find_callable_verb_named(object,name[,n])";
"  returns the *number* of the first verb on object that matches the given name and has the x flag set.";
"  optional argument n, if given, starts the search with verb n, causing the first n verbs (0..n-1) to be ignored.";
"  -1 is returned if no verb is found.";
"  This routine does not find inherited verbs.";
{object, name, ?n = 0} = args;
for i in [n + 1..length(verbs(object))]
verbinfo = verb_info(object, i);
if (index(verbinfo[2], "x") && this:verbname_match(verbinfo[3], name))
return i - 1;
endif
endfor
return -1;
.
#53:25
":verbname_match(fullverbname,name) => TRUE iff `name' is a valid name for a verb with the given `fullname'";
verblist = (" " + args[1]) + " ";
if (index(verblist, (" " + (name = args[2])) + " ") && (!match(name, "[ *]")))
"Note that if name has a * or a space in it, then it can only match one of the * verbnames";
return 1;
else
namelen = length(name);
while (m = match(verblist, "[^ *]*%(%*%)[^ ]*"))
vlast = m[2];
if ((namelen >= (m[3][1][1] - m[1])) && ((!(v = strsub(verblist[m[1]..vlast], "*", ""))) || (index(v, (verblist[vlast] == "*") ? name[1..min(namelen, length(v))] | name) == 1)))
return 1;
endif
verblist = verblist[vlast + 1..length(verblist)];
endwhile
endif
return 0;
.
#53:26
"$code_utils:find_verbs_containing(pattern[,object|object-list])";
"";
"Print (to player) the name and owner of every verb in the database whose code contains PATTERN as a substring.  Optional second argument limits the search to the specified object or objects.";
"";
"Because it searches the entire database, this function may suspend the task several times before returning.";
"";
set_task_perms(caller_perms());
"... puts the task in a player's own job queue and prevents someone from learning about verbs that are otherwise unreadable to him/her.";
pattern = args[1];
where = {@args, 0}[2];
count = 0;
if (typeof(where) == NUM)
for i in [where..tonum(max_object())]
if (valid(o = toobj(i)))
count = count + this:_find_verbs_containing(pattern, o);
endif
if ($command_utils:running_out_of_time())
player:notify(tostr("...", o));
suspend(0);
endif
endfor
elseif (typeof(where) == LIST)
for o in (where)
count = count + this:_find_verbs_containing(pattern, o);
endfor
else
"...typeof(where) == OBJ...";
count = this:_find_verbs_containing(pattern, where);
endif
player:notify("");
player:notify(tostr("Total: ", count, " verb", (count == 1) ? "." | "s."));
.
#53:27
":_find_verbs_containing(pattern,object)";
"number of verbs in object with code having a line containing pattern";
"prints verbname and offending line to player";
{pattern, o} = args;
set_task_perms(caller_perms());
count = 0;
verbs = $code_utils:verbs(o);
if (typeof(verbs) == ERR)
player:notify(tostr("verbs(", o, ") => ", verbs));
return 0;
endif
for vnum in [1..length(verbs)]
if (l = this:_grep_verb_code(pattern, o, vnum))
owner = verb_info(o, vnum)[1];
player:notify(tostr(o, ":", verbs[vnum], " [", index(verb_info(o, vnum)[2], "d") ? "+d " | "-d ", valid(owner) ? owner:name() | "Recycled Player", " (", owner, ")]:  ", l));
count = count + 1;
endif
if ($command_utils:running_out_of_time())
player:notify(tostr("...", o));
suspend(0);
endif
endfor
return count;
"Metadata 202106";
"Last-Modify: {863660839, \"Erik\", #74, \"JHM\"}";
.
#53:28
"$code_utils:find_verbs_matching(pattern[,object|object-list])";
"";
"Print (to player) the name and owner of every verb in the database whose code has a substring matches the regular expression PATTERN.  Optional second argument limits the search to the specified object or objects.";
"";
"Because it searches the entire database, this function may suspend the task several times before returning.";
"";
set_task_perms(caller_perms());
"... puts the task in a player's own job queue and prevents someone from learning about verbs that are otherwise unreadable to him/her.";
{pattern, ?where = 0} = args;
count = 0;
if (typeof(where) == NUM)
for o in [toobj(where)..max_object()]
if (valid(o))
count = count + this:_find_verbs_matching(pattern, o);
endif
if ($command_utils:running_out_of_time())
player:notify(tostr("...", o));
suspend(0);
endif
endfor
elseif (typeof(where) == LIST)
for o in (where)
count = count + this:_find_verbs_matching(pattern, o);
endfor
else
count = this:_find_verbs_matching(pattern, where);
endif
player:notify("");
player:notify(tostr("Total: ", count, " verb", (count == 1) ? "." | "s."));
.
#53:29
":_find_verbs_matching(regexp,object)";
"number of verbs in object with code having a line matching regexp";
"prints verbname and offending line to player";
set_task_perms(caller_perms());
{pattern, o} = args;
count = 0;
verbs = $code_utils:verbs(o);
if (typeof(verbs) == ERR)
player:notify(tostr("verbs(", o, ")  => ", verbs));
return 0;
endif
for vnum in [1..length(verbs)]
if (l = this:_egrep_verb_code(pattern, o, vnum))
owner = verb_info(o, vnum)[1];
player:notify(tostr(o, ":", verbs[vnum], " [", index(verb_info(o, vnum)[2], "d") ? "+d " | "-d ", valid(owner) ? owner:name() | "Recycled Player", " (", owner, ")]:  ", l));
count = count + 1;
endif
if ($command_utils:running_out_of_time())
player:notify(tostr("...", o));
suspend(0);
endif
endfor
return count;
"Metadata 202106";
"Last-Modify: {863662120, \"Erik\", #74, \"JHM\"}";
.
#53:30
":_grep_verb_code(pattern,object,verbname) => line or 0";
"  returns line on which pattern occurs in code for object:verbname";
{pattern, object, vb} = args;
set_task_perms(caller_perms());
try
for line in (vc = verb_code(object, vb))
if (index(line, pattern))
return line;
endif
endfor
except (E_PERM)
return E_PERM;
endtry
return 0;
"Copied from code utilities (#59):_grep_verb_code by splat (#3024) Sun May 12 01:51:44 1996 EDT";
return;
"Copied from splat (#3024):_grep_verb_code by splat (#3024) Sun May 12 01:57:45 1996 EDT";
return;
"Metadata 202106";
"Last-Modify: {831880665, \"splat\", #3024, \"JHM\"}";
.
#53:31
":_egrep_verb_code(regexp,object,verbname) => 0 or line";
"  returns  first line matching regexp in object:verbname code";
set_task_perms(caller_perms());
{pattern, @verbspec} = args;
try
for line in (vc = verb_code(@verbspec))
if (match(line, pattern))
return line;
endif
endfor
except (E_PERM)
return E_PERM;
endtry
return 0;
"Metadata 202106";
"Last-Modify: {863663034, \"Erik\", #74, \"JHM\"}";
.
#53:32
"Parse [from <start>] [to <end>] [for <name>].";
"Takes a series of strings, most likely @args with dobjstr removed.";
"Returns a list {NUM start, NUM end, STR name}, or {} if there is an error.";
fail = length(args) % 2;
start = 0;
end = tonum(max_object());
match = "";
while (args && (!fail))
prep = args[1];
if (prep == "from")
if ((start = player.location:match_object(args[2])) >= #0)
start = tonum(start);
else
start = tonum(args[2]);
endif
elseif (prep == "to")
if ((end = player.location:match_object(args[2])) >= #0)
end = tonum(end);
else
end = tonum(args[2]);
endif
elseif (prep == "for")
match = args[2];
else
fail = 1;
endif
args = args[3..length(args)];
endwhile
return fail ? {} | {start, end, match};
.
#53:33
":help_db_list([player,[location]]) -- help dbs to be searched by player at given location";
target = {@args, player}[1];
location = (length(args) >= 2) ? args[2] | target.location;
olist = {target, @$object_utils:ancestors(target)};
if (valid(location))
olist = {@olist, location, @$object_utils:ancestors(location)};
endif
dbs = {};
for o in (olist)
h = o.help;
if (typeof(h) == OBJ)
h = {h};
endif
for db in (h)
if ((typeof(db) == OBJ) && (valid(db) && (!(db in dbs))))
dbs = {@dbs, db};
endif
endfor
endfor
return {@dbs, $help};
.
#53:34
":help_db_search(string,dblist)";
"  searches each of the help db's in dblist for a topic matching string.";
"  Returns  {db,topic}  or  {$ambiguous_match,{topic...}}  or {}";
what = args[1];
dblist = args[2];
topics = {};
help = 1;
for db in (dblist)
if ({what} == (ts = db:find_topics(what)))
return {db, ts[1]};
elseif (ts && (typeof(ts) == LIST))
if (help)
help = db;
endif
for t in (ts)
topics = setadd(topics, t);
endfor
endif
endfor
if (length(topics) > 1)
string = "ambiguous ";
for word in ({what, @topics})
if (match(word, "[ \\\"]"))
string = tostr(string, $string_utils:print(word), " ");
else
string = tostr(string, word, " ");
endif
endfor
return {$failed_help, string};
return {$failed_help, $string_utils:from_list({"ambiguous", what, @topics}, " ")};
return {$ambiguous_match, topics};
elseif (topics)
return {help, topics[1]};
else
return {$failed_help, tostr("failed ", what)};
return {};
endif
.
#53:35
":corify_object(object)  => string representing object";
"  usually just returns tostr(object), but in the case of objects that have";
"  corresponding #0 properties, return the appropriate $-string.";
object = args[1];
for p in (properties(#0))
if (#0.(p) == object)
return "$" + p;
endif
endfor
return tostr(object);
.
#53:36
"$code_utils:substitute(string,subs) => new line";
"Subs are a list of lists, {{\"target\",\"sub\"},{...}...}";
"Substitutes targets for subs in a delimited string fashion, avoiding substituting anything inside quotes, e.g. player:tell(\"don't sub here!\")";
s = args[1];
subs = args[2];
lets = "abcdefghijklmnopqrstuvwxyz0123456789";
for x in (subs)
len = length(sub = x[1]);
delimited = index(lets, sub[1]) && index(lets, sub[len]);
prefix = "";
while (i = index(s, sub))
prefix = prefix + s[1..i - 1];
if ((((prefix == "") || ((!delimited) || (!index(lets, prefix[length(prefix)])))) && ((!delimited) || (((i + len) > length(s)) || (!index(lets, s[i + len]))))) && (!this:inside_quotes(prefix)))
prefix = prefix + x[2];
else
prefix = prefix + s[i..(i + len) - 1];
endif
s = s[i + len..length(s)];
endwhile
s = prefix + s;
endfor
return s;
.
#53:37
"See if the end of the string passed as args[1] ends 'inside' a doublequote.  Used by $code_utils:substitute.";
string = args[1];
quoted = 0;
for i in [1..length(string)]
if ((string[i] == "\"") && ((!quoted) || (string[i - 1] != "\\")))
quoted = !quoted;
endif
endfor
return quoted;
.
#53:38
"verb_or_property(<obj>, <name> [, @<args>])";
"Looks for a callable verb or property named <name> on <obj>.";
"If <obj> has a callable verb named <name> then return <obj>:(<name>)(@<args>).";
"If <obj> has a property named <name> then return <obj>.(<name>).";
"Otherwise return E_PROPNF.";
"N.B.: a verb raising E_VERBNF will act like an undefined verb.";
set_task_perms(caller_perms());
{o, v, @rest} = args;
if ((r = o:(v)(@rest)) == E_VERBNF)
return o.(v);
else
return r;
endif
"set_task_perms(caller_perms());";
"{o, v, @rest} = args;";
"return ``o:(v)(@rest) ! E_VERBNF => o.(v)' ! E_PROPNF';";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {912965164, \"Ken\", #75, \"JHM\"}";
.
#53:39
"task_valid(NUM id)";
"Return true iff there is currently a valid task with the given id.";
id = args[1];
set_task_perms($no_one);
return (id == task_id()) || (E_PERM == kill_task(id));
.
#53:40
if (a = $list_utils:assoc(args[1], queued_tasks()))
return a[5];
else
return E_INVARG;
endif
.
#53:41
":argstr(verb,args[,argstr]) => what argstr should have been.  ";
"Recall that the command line is parsed into a sequence of words; `verb' is";
"assigned the first word, `args' is assigned the remaining words, and argstr";
"is assigned a substring of the command line, which *should* be the one";
"starting first nonblank character after the verb, but is instead (because";
"the parser is BROKEN!) the one starting with the first nonblank character";
"after the first space in the line, which is not necessarily after the verb.";
"Clearly, if the verb contains spaces --- which can happen if you use";
"backslashes and quotes --- this loses, and argstr will then erroneously";
"have extra junk at the beginning.  This verb, given verb, args, and the";
"actual argstr, returns what argstr should have been.";
{verb, args, ?argstr} = args;
if (!index(verb, " "))
return argstr;
elseif (!args)
return "";
endif
"space in verb => two possible cases:";
"(1) first space was not in a quoted string.";
"    first word of argstr == rest of verb unless verb ended on this space.";
if ((nqargs = $string_utils:words(argstr)) == args)
return argstr;
elseif (((nqn = length(nqargs)) == (length(args) + 1)) && (nqargs[2..nqn] == args))
return argstr[$string_utils:word_start(argstr)[2][1]..length(argstr)];
else
"(2) first space was in a quoted string.";
"    argstr starts with rest of string";
qs = $string_utils:word_start("\"" + argstr);
return argstr[qs[(length(qs) - length(args)) + 1][1] - 1..length(argstr)];
endif
.
#53:42
":verbname_match(fullverbname,name) => TRUE iff `name' is a valid name for a verb with the given `fullname'";
verblist = (" " + args[1]) + " ";
if (index(verblist, (" " + (name = args[2])) + " ") && (!(index(name, "*") || index(name, " "))))
"Note that if name has a * or a space in it, then it can only match one of the * verbnames";
return 1;
else
namelen = length(name);
while (star = index(verblist, "*"))
vstart = rindex(verblist[1..star], " ") + 1;
vlast = (vstart + index(verblist[vstart..length(verblist)], " ")) - 2;
if ((namelen >= (star - vstart)) && ((!(v = strsub(verblist[vstart..vlast], "*", ""))) || (index(v, (verblist[vlast] == "*") ? name[1..min(namelen, length(v))] | name) == 1)))
return 1;
endif
verblist = verblist[vlast + 1..length(verblist)];
endwhile
endif
return 0;
.
#53:43
":show_who_listing_idle(players[,more_players[,player to tell]])";
" prints a listing of the indicated players.";
" For players in the first list, idle/connected times are shown if the player is logged in, otherwise the last_disconnect_time is shown.  For players in the second list, last_disconnect_time is shown, no matter whether the player is logged in.";
" If third arg is passed, then it is used instead of caller to send spam to. In this case :tell is used instead of :notify.";
if (length(args) == 1)
args = {@args, {}};
endif
if (length(args) == 2)
args = {@args, caller};
endif
return $who_utils:(verb)(@args);
.
#53:44
":_egrep_verb_code(regexp,object,verbname) => list of lines number";
"  returns list of all lines matching regexp in object:verbname code";
{pattern, object, vb} = args;
set_task_perms(caller_perms());
lines = {};
for line in (vc = verb_code(object, vb, 1, 0))
if (match(line, pattern))
lines = {@lines, line};
endif
endfor
return lines;
.
#53:45
":_grep_verb_code_all(pattern,object,verbname) => list of lines";
"  returns list of lines on which pattern occurs in code for object:verbname";
{pattern, object, verbname} = args;
set_task_perms(caller_perms());
lines = {};
for l in [1..length(vc = verb_code(object, verbname))]
line = vc[l];
if (index(line, pattern))
lines = {@lines, tostr(l, ": ", line)};
endif
endfor
return lines;
.
#53:46
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
"On $code_utils, it's kept tnt !x, so no one can call it.  @Make-setter <object>.<property> will copy it to an object.";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
.
#53:47
":verb_usage([object,verbname]) => usage string at beginning of verb code, if any";
"default is the calling verb";
set_task_perms(caller_perms());
if (args)
{object, vname} = args;
else
{_, vname, _, object, @_} = callers()[1];
endif
try
code = verb_code(object, vname);
except e (ANY)
return e[1];
endtry
doc = {};
for line in (code)
if (match(line, "^\"%([^\\\"]%|\\.%)*\";$"))
"... now that we're sure `line' is just a string, eval() is safe...";
e = $no_one:eval(line)[2];
if (subs = match(e, "^%(Usage: +%)%([^ (]+%)%(.*$%)"))
"Server is broken, hence the next three lines:";
if (subs[3][3][1] > subs[3][3][2])
subs[3][3] = {0, -1};
endif
indent = ("^%(" + $string_utils:space(length(substitute("%1", subs)))) + " *%)%([^ ]+%)%(.*$%)";
docverb = substitute("%2", subs);
if ((typeof(vname) == NUM) || match(vname, "^[0-9]+$"))
vname = docverb;
endif
doc = {@doc, (substitute("%1", subs) + vname) + substitute("%3", subs)};
elseif (subs = match(e, indent))
if (substitute("%2", subs) == docverb)
doc = {@doc, (substitute("%1", subs) + vname) + substitute("%3", subs)};
else
doc = {@doc, e};
endif
elseif (indent)
return doc;
endif
else
return doc;
endif
endfor
return doc;
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863663089, \"Erik\", #74, \"JHM\"}";
.
#53:48
":connected_players()";
"Returns 'real' $players, ignores all the non$player (presumably network) connections.";
r = {};
for i in (connected_players())
if ($object_utils:isa(i, $player))
r = {@r, i};
endif
endfor
return r;
.
#53:49
":players()";
"Returns 'real' $players, ignores all the non$player (presumably network) connections.";
"This may be too spammy for words.";
r = {};
for i in ($object_utils:descendants($player))
if (is_player(i))
r = {@r, i};
endif
endfor
return r;
.
#53:50
"safe_eval(string)";
"evaluate the given string with $no_one's perms, if it doesn't have any verb or function calls in it";
if (subs = match(args[1], "^%([^\"()=]%|\"%([^\\\"]*%|\\.%)*\"%)*$"))
"The above was suggested by Lambda to avoid an exponential performance loss in the new regexp routines.";
"if (subs = match(args[1], \"^%([^\\\"()=]+%|\\\"%([^\\\\\\\"]%|\\\\.%)*\\\"%)*$\"))";
"More permissive version commented out because I don't trust it and I'm too lazy to figure it out.  Looks like its primary advantage is allowing variable assignment, but it also happens to allow property assignment.";
"if (match(args[1], \"^%([^:\\\"]*%|\\\"%([^\\\\\\\"]%|\\\\.%)*\\\"%)*$\") && !match(args[1], \"%(^%|[^.a-z]%)[a-z_][a-z0-9_]* *(\"))";
return $no_one:eval(args[1]);
else
return E_PERM;
endif
.
#53:51
"returns the callers() frame for the current verb.";
return callers()[1];
.
#53:52
"returns {this:verb_frame(), @callers()}.";
return callers();
.
#53:53
"Usage:  :parse_named_args(@args)";
"";
"Given a set of args, see if they form something like \"foo(bar, baz)\" or \"foo(bar, baz, @quux)\".  If so, return a list {name, regular args, `rest' arg}--in these examples, {\"foo\", {\"bar\", \"baz\"}, \"\"} and {\"foo\", {\"bar\", \"baz\"}, \"quux\"}--followed by a list of remaining args after the closing parenthesis.  If not, return an error or a string to print at the user.";
"";
"The caller should make sure the first arg has a parenthesis in it; if not, we'll return E_INVARG.  This verb is generally pretty strict; it doesn't allow for whitespace before or after the parentheses, or before commas.";
left = index(args[1], "(");
if (!left)
return E_INVARG;
endif
vname = args[1][1..left - 1];
args[1][1..left] = "";
regular = {};
rest = "";
if (args != {")"})
while (args)
arg = args[1];
args = listdelete(args, 1);
if (match(arg, "^[a-z_][a-z_0-9]*,$"))
regular = {@regular, arg[1..length(arg) - 1]};
elseif (match(arg, "^@?[a-z_][a-z_0-9]*)$"))
if (arg[1] == "@")
rest = arg[2..length(arg) - 1];
else
regular = {@regular, arg[1..length(arg) - 1]};
endif
if (args)
return {{vname, regular, rest}, args};
endif
else
return tostr("Can't parse: ", arg);
endif
endwhile
endif
return {{vname, regular, rest}, {}};
.
#53:54
"Usage:  split_verb_code(code)";
"";
"Split verb code into documentation and code sections.  Return {documentation, code}.  May at some point recognize other sections (like a metainformation section at the end).";
code = args[1];
for i in [1..length(code)]
if (!match(code[i], "^\"%([^\\\"]%|\\.%)*\";$"))
return {code[1..i - 1], code[i..length(code)]};
endif
endfor
return {code, {}};
.
#53:55
"Usage:  :named_args_to_usage(vnames, named)";
"";
"From the given list of verb names and `named args' set ({named args, rest arg}), construct a list of strings that form a `Usage' message, like {\"Usage:  foo(bar)\"}.";
vnames = args[1];
named = args[2];
arglist = tostr("(", this:named_args_list(named), ")");
width = $list_utils:max_length(vnames);
out = {};
for vname in (vnames)
out = {@out, tostr("        :", $string_utils:left(vname, width), arglist)};
endfor
out[1][1..6] = "Usage:";
return out;
.
#53:56
"Usage:  commentify(lines)";
"";
"Given lines of text, translate them into comment strings and return the list.";
lines = args[1];
out = {};
for line in (lines)
out = {@out, $string_utils:print(line) + ";"};
endfor
return out;
.
#53:57
"Usage:  uncommentify(lines)";
"";
"Given lines of comment code, translate them into text and return the list.";
lines = args[1];
set_task_perms($no_one);
out = {};
for line in (lines)
if (match(line, "^\"%([^\\\"]%|\\.%)*\";$"))
out = {@out, eval("return " + line)[2]};
else
return E_INVARG;
endif
endfor
return out;
.
#53:58
"Usage:  :named_args_list(named)";
"";
"Given a named args list in the form generated by $code_utils:parse_named_args, return a string like \"foo, bar, @baz\".";
named = args[1];
return $string_utils:from_list({@named[1], @named[2] ? {"@" + named[2]} | {}}, ", ");
.
#53:59
"Usage:  :named_args_from_code(code)";
"";
"Given some code, figure out if the first few non-documentation lines match a template for named args.";
code = args[1];
code = this:split_verb_code(code)[2];
if (code && (s = match(code[1], "^%([a-z_][a-z_0-9]*%) = args;")))
return {{}, substitute("%1", s)};
endif
regular = {};
rest = "";
for i in [1..length(code)]
if (s = match(code[i], tostr("^%([a-z_][a-z_0-9]*%) = args%[", i, "%];$")))
if ((next = substitute("%1", s)) in regular)
return 0;
endif
regular = {@regular, next};
elseif (s = match(code[i], tostr("^%([a-z_][a-z_0-9]*%) = args%[", i, "..%(length(args)%|%$%)%];$")))
if ((rest = substitute("%1", s)) in regular)
return 0;
else
return {regular, rest};
endif
elseif (!regular)
return 0;
else
return {regular, rest};
endif
endfor
return {regular, rest};
.
#53:60
"Usage:  :named_args_to_code(named)";
"";
"Convert the given `named args' set into assignment statements.";
named = args[1];
named_args = named[1];
rest_arg = named[2];
lines = {};
for i in [1..length(named_args)]
lines = {@lines, tostr(named_args[i], " = args[", i, "];")};
endfor
if (rest_arg)
if (named_args)
lines = {@lines, tostr(rest_arg, " = args[", length(named_args) + 1, "..length(args)];")};
elseif (rest_arg != "args")
lines = {@lines, tostr(rest_arg, " = args;")};
endif
endif
return lines;
.
#53:61
"Generate a random variable name.  Excludes reserved moocode words.";
alpha = $string_utils.alphabet + "_";
var = alpha[random(27)];
alphanum = alpha + $string_utils.digits;
for i in [2..5]
var = var + alphanum[random(37)];
endfor
if ((var in $code_utils.reserved) || (var in $code_utils.predefined))
var = var + "1";
endif
return var;
.
#53:62
"Usage:  :verbs(object)";
"";
"Return a list of verbs on the object, using either verbs() or a horrible kluge depending on permissions.  If the latter, unreadable verbs will not be listed by name but will be listed as E_PERM.";
object = args[1];
set_task_perms(caller_perms());
verbs = verbs(object);
if ((typeof(verbs) == LIST) || (verbs != E_PERM))
return verbs;
endif
verbs = {};
i = 1;
while ((info = verb_info(object, i)) != E_VERBNF)
verbs = {@verbs, info[3] || E_PERM};
i = i + 1;
endwhile
return verbs;
.
#53:63
set_task_perms(caller_perms());
{object, oldname, newname} = args;
"emulating old behavior here -- server returns E_INVARG in this case";
if (`property_info(parent(object), oldname) ! ANY')
return E_PROPNF;
endif
return `set_property_info(object, oldname, {@property_info(object, oldname), newname}) ! ANY';
.
#53:64
"Usage:  :format_traceback(tb, [error, [errormsg]])";
"";
"format a traceback like the server does, given output from ";
"the exception handling traceback value or callers(line numbers)";
{tb, @errorinfo} = args;
{top, @tb} = tb;
if (errorinfo)
{error, ?errormsg = ""} = errorinfo;
if (!errormsg)
errormsg = error;
endif
msg = tostr(": ", errormsg);
else
msg = "";
endif
oldplayer = player;
text = {this:_format_traceback_frame(top, oldplayer) + msg};
oldplayer = top[5];
for fr in (tb)
text = {@text, "... called from " + this:_format_traceback_frame(fr, oldplayer)};
oldplayer = fr[5];
endfor
text = {@text, "(End of traceback)"};
return text;
"Metadata 202106";
"Last-Modify: {893779838, \"Erik\", #74, \"JHM\"}";
.
#53:65
"Usage:  :_format_traceback_frame(frame)";
"";
{frame, ?oldplayer = player} = args;
{ths, vname, prog, vloc, plyr, ?line = 0} = frame;
if (((vloc == #-1) && (prog == #-1)) && (vname != ""))
vname = ("built-in function " + vname) + "()";
elseif ((vloc == #-1) && (vname == ""))
vname = "Input to eval()";
elseif ((vloc < #0) || (prog < #0))
"how does this HAPPEN?";
vname = "vloc and prog are both < #0 -- what?";
else
vname = tostr(vloc, ":", vname);
endif
if (vloc != ths)
thstr = tostr(" (this == ", ths, ")");
else
thstr = "";
endif
if (oldplayer != plyr)
plyrstr = tostr("   (player == ", plyr, ")");
else
plyrstr = "";
endif
return tostr(vname, thstr, ", line ", line || "?", plyrstr);
.
#53:66
":find_verb_named_1_based(object,name[,n])";
"  (== find_verb_named_0_based(object,name[,n-1]) + 1)";
"  returns the (1-based) *number* of the first verb on object matching the given name.";
"  optional argument n, if given, starts the search with verb n,";
"  causing the first n-1 verbs (1..n-1) to be ignored.";
"  0 is returned if no verb is found.";
"  This routine does not find inherited verbs.";
{object, name, ?n = 1} = args;
for i in [n..length(verbs(object))]
verbinfo = verb_info(object, i);
if (this:verbname_match(verbinfo[3], name))
return i;
endif
endfor
return 0;
.
#53:67
":find_last_verb_named_1_based(object,name[,n])";
"  ( == :find_last_verb_named_0_based(object,name[,n-1]) + 1)";
"  returns the (1-based) *number* of the last verb on object matching the given name.";
"  optional argument n, if given, starts the search with verb n-1,";
"  causing verbs (n..length(verbs(object))) to be ignored.";
"  0 is returned if no verb is found.";
"  This routine does not find inherited verbs.";
{object, name, ?start = length(verbs(object)) + 1} = args;
while (start = start - 1)
verbinfo = verb_info(object, start);
if (this:verbname_match(verbinfo[3], name))
return start;
endif
endwhile
return 0;
.
#53:68
":find_callable_verb_named(object,name[,n])";
"  returns the *number* of the first verb on object that matches the given name and has the x flag set.";
"  optional argument n, if given, starts the search with verb n, causing the first n verbs (0..n-1) to be ignored.";
"  -1 is returned if no verb is found.";
"  This routine does not find inherited verbs.";
{object, name, ?n = 0} = args;
for i in [n + 1..length(verbs(object))]
verbinfo = verb_info(object, i);
if (index(verbinfo[2], "x") && this:verbname_match(verbinfo[3], name))
return i - 1;
endif
endfor
return -1;
"Copied from code utilities (#59):find_callable_verb_named by Rog (#145) Sat Apr  6 20:03:56 1996 EST";
.
#53:69
"a MOSTLY accurate attempt at figuring out callers() as it would be if the calling verb hadn't been overridden.  Or pass a callers() stack and figure it out from there (good for utility verbs).";
{?stack = callers()} = args;
try
{top, @stack} = stack;
except (E_ARGS)
raise(E_INVARG);
endtry
it = top[1..2];
them = $object_utils:ancestors(top[1]);
index = top[4] in them;
while (stack)
{cur, @rest} = stack;
if ((cur[1..2] != it) || ((cur[4] in them) >= (top[4] in them)))
break;
else
stack = rest;
endif
endwhile
return stack;
"Copied from James (#2142):callers_before_pass Tue May  7 02:09:10 1996 EDT";
.
#53:70
"returns the frame of the verb that called the current verb, or called whatever verb overrode the current verb.";
return `this:callers_before_pass(callers())[1] ! E_RANGE => 0';
"the error check needs work.";
.
#53:71
"returns the object from which the message that this verb is acting as a method for was sent.";
return `this:callers_before_pass(callers())[1][1] ! E_RANGE => #-1';
.
#53:72
"returns the perms with which the message this verb is acting as a method for was sent.";
return `this:callers_before_pass(callers())[1][5] ! E_RANGE => #-1';
.
#54:0
dblist = args[2];
"...dblist = $code_utils:help_db_list()";
text = {{$jtext.title, "Full Help System Index"}, {$jtext.header, 1, "Full Index"}};
for db in (dblist)
if ($object_utils:has_callable_verb(db, "index"))
ind = db:index({tostr(db:name(), " (", db, ")")});
if (ind[1][1] == $jtext.title)
ind = listdelete(ind, 1);
endif
if ((ind[1][1] == $jtext.header) && (ind[1][2] == 1))
ind[1][2] = 2;
endif
text = {@text, @ind};
endif
endfor
return text;
.
#54:1
dblist = args[2];
if (!dblist)
dblist = $code_utils:help_db_list($wiz);
endif
"...dblist = $code_utils:help_db_list()";
text = {$jtext.dlist};
for db in (dblist)
if ($object_utils:isa(db, $generic_help))
for p in (properties(db))
if ((h = db.(p)) && ("*index*" in h))
text = {@text, {{$jtext.link.root, {$jaddress.help, {{"subject", p}}}, p}, tostr(h[2] || db:name())}};
endif
endfor
endif
endfor
fullindex = {};
for p in (properties(this))
if (this.(p) && (this.(p)[1] == "*full_index*"))
fullindex = {$jtext.dlist, {{$jtext.link.root, {$jaddress.help, {{"subject", p}}}, p}, "EVERYTHING"}};
endif
endfor
return {{$jtext.title, "Available Help Indices"}, {$jtext.header, 3, "Available Help Indices"}, text, fullindex};
hdr = "Available Help Indices";
text = {"", hdr, $string_utils:space(hdr, "-")};
for db in ($code_utils:help_db_list())
if ($object_utils:isa(db, $generic_help))
for p in (properties(db))
if ((h = db.(p)) && ("*index*" in h))
text = {@text, tostr($string_utils:left(p, 14), " -- ", h[2] || db:name(), " (", db, ")")};
endif
endfor
endif
endfor
for p in (properties(this))
if (this.(p) && (this.(p)[1] == "*full_index*"))
return {@text, "", tostr($string_utils:left(p, 14), " -- ", "EVERYTHING")};
endif
endfor
return text;
.
#54:2
"Usage:  :match_help_db(string, db list)";
"";
"Tries to match the given string as an object or against a list of named databases.  Doesn't try to make sure that its return value is, in fact, a help database; just a valid object (or $nothing, or $ambiguous_match, or $failed_match).  However, this:help_db_match_failed will determine if the object at least makes an attempt to act like a help database.";
name = args[1];
dblist = args[2];
if (!name)
return $nothing;
elseif (valid(object = $string_utils:literal_object(name)) || (object == $ambiguous_match))
return object;
else
return $string_utils:match(name, dblist, "name", dblist, "aliases");
endif
.
#54:3
"Usage:  help_db_match_failed(object, string)";
"Prints a message if string does not match the given help db.  Generally used after object is derived from a :match_help_db(string).";
set_task_perms(caller_perms());
match_result = args[1];
string = args[2];
tell = $perm_utils:controls(caller_perms(), player) ? "notify" | "tell";
if ((index(string, "#") == 1) && ($code_utils:toobj(string) != E_TYPE))
"...avoid the `I don't know which `#-2' you mean' message...";
if (!valid(match_result))
player:(tell)(tostr(string, " does not exist."));
endif
return !valid(match_result);
elseif (match_result == $nothing)
player:(tell)("You must give the name of a help database.");
elseif (match_result == $failed_match)
player:(tell)(tostr("There is no help database named \"", string, "\"."));
elseif (match_result == $ambiguous_match)
player:(tell)(tostr("More than one help database could be called \"", string, "\"."));
elseif (!$recycler:valid(match_result))
player:(tell)(tostr(match_result, " does not exist."));
elseif (!this:verify_db(match_result))
player:(tell)(tostr(match_result:dnamec(), " is not a help database."));
else
return 0;
endif
return 1;
.
#54:4
if (((text = pass(@args)) != E_PROPNF) || ((!valid(object = $string_utils:match_object(what = args[1], player.location))) || (!$object_utils:has_property(object, "help_text"))))
return text;
else
return object.help_text;
endif
.
#54:5
set_task_perms(caller_perms());
if (((text = pass(@args)) != E_PROPNF) || ((!valid(object = $string_utils:match_object(what = args[1], player.location))) || (!$object_utils:has_property(object, "help_text"))))
return text;
elseif (typeof(e = object.help_text = args[2]) == ERR)
return e;
else
return 1;
endif
.
#54:6
"Usage:  :verify_db(object)";
"";
"Makes a futile attempt to verify that the given object can in fact serve as a help database, by checking if it either (a) is a descendant of $generic_help or (b) implements the verbs required and named in `help $generic_help'.";
if ($object_utils:isa(object = args[1], $generic_help))
return 1;
endif
for v in ({"find_topics", "get_topic", "topic_text", "set_topic_text", "dump_topic"})
if (!$object_utils:has_verb(object, v))
return 0;
endif
endfor
return 1;
.
#54:7
return {@pass(@args), $player, $prog, $object_utils, $code_utils, $jtext, $jtext.title, $jtext.header, $jtext.dlist, $generic_help, $jtext.link, $jtext.link.root, $jaddress, $jaddress.help, $string_utils, $list_utils, $wiz, $command_utils, $perm_utils, $recycler, this.author_feature};
"Metadata 202106";
"Last-Modify: {868907291, \"Erik\", #74, \"JHM\"}";
.
#54:8
if (!$__core_init_phase)
$error:raise(E_PERM);
endif
pass(@args);
for p in ({"@projects", "projects", "project-policy", "purpose", "programmer-policy", "user-policy", "gopher", "http-gateway", "neu", "site", "theme", "@tutorial", "steering-committee", "sc"})
this:delete_topic(p);
endfor
add_property(this, "copyright", {"*forward*", "core-copyright"}, property_info(this, "core-copyright"));
known = {};
for i in (this.known_dbs)
if (valid(i))
known = {@known, i};
endif
endfor
this.known_dbs = known;
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {868907155, \"Erik\", #74, \"JHM\"}";
.
#55:0
"Return a toad (child of #1, owned by $hacker) from this.contents.  Move it to #-1.  Recreate as a child of args[1], or of #1 if no args are given.  Chown to caller_perms() or args[2] if present.";
what = args ? args[1] | $root_class;
who = (length(args) == 2) ? args[2] | caller_perms();
if (!(caller_perms().wizard || (who == caller_perms())))
return E_PERM;
elseif (!(((what == #-1) || valid(what)) && is_player(who)))
return E_INVARG;
elseif ((what != #-1) && (((who != what.owner) && (!what.f)) && (!who.wizard)))
return E_PERM;
endif
for potential in (this.contents)
if (((potential.owner == $hacker) && (parent(potential) == $garbage)) && (!children(potential)))
return this:setup_toad(potential, who, what);
endif
endfor
return E_NONE;
"Metadata 202106";
"Last-Modify: {863647173, \"Erik\", #74, \"JHM\"}";
.
#55:1
"Take the object in args[1], and turn it into a child of #1 owned by $hacker.";
item = args[1];
if (!$perm_utils:controls(caller_perms(), item))
return E_PERM;
endif
this:addhist(caller_perms(), item);
if (why = $builtins:_protect_recycle(item))
return raise(E_INVARG, why);
endif
"this is dangerous and stupid, so it's commented out.";
"if (is_player(item))";
"  $wiz_utils:unset_player(item);";
"endif";
"...recreate can fail (:recycle can crash)...";
this:add_orphan(item);
$quota_utils:preliminary_reimburse_quota(item.owner, item);
$building_utils:recreate(item, $garbage);
this:remove_orphan(item);
"...";
$wiz_utils:set_owner(item, $hacker);
if (is_player(item))
$wiz_utils:unset_player(item);
endif
item.name = tostr("Recyclable ", item);
move(item, this);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {865030670, \"Ken\", #75, \"JHM\"}";
.
#55:2
":_create(parent[, owner]) => OBJ or ERR";
e = set_task_perms(caller_perms());
if (typeof(e) == ERR)
return e;
else
val = this:_recreate(@args);
return (val == E_NONE) ? $quota_utils:bi_create(@args) | val;
endif
.
#55:3
if (caller == this)
h = this.history;
if ((len = length(h)) > this.nhist)
h = h[len - this.nhist..len];
endif
this.history = {@h, args};
endif
.
#55:4
if ($perm_utils:controls(valid(caller_perms()) ? caller_perms() | player, this))
for x in (this.history)
pname = valid(x[1]) ? x[1]:name() | "A recycled player";
oname = valid(x[2]) ? x[2]:name() | "recycled";
player:notify(tostr(pname, " (", x[1], ") recycled ", x[2], " (now ", oname, ")"));
endfor
else
player:notify("Sorry.");
endif
.
#55:5
dobj = valid(dobj) ? dobj | $string_utils:match_object(dobjstr, player.location);
if (!valid(dobj))
dobj = (n = tonum(dobjstr)) ? toobj(n) | #-1;
endif
if (!valid(dobj))
player:tell("Couldn't parse ", dobjstr, " as a valid object number.");
elseif (!$object_utils:isa(dobj, $garbage))
player:tell(dobj:dnamec(), " isn't a garbage object.");
elseif (!(dobj in this.contents))
player:tell("Couldn't find ", dobj, " in ", this:name(), ".");
else
if (typeof(emsg = this:setup_toad(dobj, player, $root_class)) != ERR)
dobj:moveto(player);
dobj.aliases = {dobj.name = "Object " + tostr(dobj)};
dobj.proper = 1;
player:tell("You now have ", dobj, " ready for @recreation.");
if (this.announce_removal_msg)
player.location:announce($string_utils:pronoun_sub(this.announce_removal_msg));
endif
else
player:tell(emsg);
endif
endif
.
#55:6
"this:setup_toad(objnum,new_owner,parent)";
"Called by :_create and :request.";
if (caller != this)
return E_PERM;
endif
potential = args[1];
who = args[2];
what = args[3];
if (!$quota_utils:creation_permitted(who))
return E_QUOTA;
elseif (parent(potential) != $garbage)
stack = $string_utils:print({args, parent(potential), callers()});
message = tostr("SETUP_TOAD FAILURE: ", stack);
server_log(message);
for dude in ($db_group:connected_members())
dude:receive_page(message);
endfor
$error:raise(E_PERM);
else
$wiz_utils:set_owner(potential, who);
move(potential, #-1);
set_task_perms({@callers(), {#-1, "", player}}[2][3]);
"... if :initialize crashes...";
this:add_orphan(potential);
$building_utils:recreate(potential, what);
this:remove_orphan(potential);
"... if we don't get this far, the object stays on the orphan list...";
"... orphan list should be checked periodically...";
return potential;
endif
.
#55:7
if (caller == this)
this.orphans = {args[1], @this.orphans};
endif
.
#55:8
if (caller == this)
this.orphans = setremove(this.orphans, args[1]);
endif
.
#55:9
"Usage:  valid(object)";
"True if object is valid and not $garbage.";
return ((typeof(args[1]) == OBJ) && valid(args[1])) && (!$object_utils:isa(args[1], $garbage));
.
#55:10
if ($__core_init_phase)
pass(@args);
this.orphans = this.history = {};
endif
.
#55:11
"Usage:  :accept(what)";
"";
what = args[1];
return parent(what) == $garbage;
.
#56:0
return {("Garbage object " + tostr(this)) + "."};
.
#56:1
player:tell_lines(this:description());
.
#56:2
return this.name;
.
#56:3
"set_verb_info(str <verb-name>, list <info>)";
"as the server primitive.";
"sets verb info for a verb on this object.";
"allows objects to customize behavior, e.g. to cache information.";
set_task_perms(caller_perms());
return set_verb_info(this, @args);
.
#56:4
"Usage:  add_verb(list <info>, list <args>)";
"as the server primitive.";
"add a verb to this object.";
set_task_perms(caller_perms());
return add_verb(this, @args);
.
#56:5
"name_and_number() => \"name (#number)\"";
nameverb = verb[1..index(verb, "_") - 1];
return tostr(this:(nameverb)(), " (", this, ")");
.
#57:0
if (i = args[1] in {"noinclude", "sender"})
return {{{"include", "all"}[i], !args[2]}};
else
return {args};
endif
.
#57:1
"... we'll take anything...";
raw = args[2];
if (raw == 1)
"...+@mail => @mailo=new";
return {args[1], "new"};
else
return args[1..2];
endif
.
#57:2
oname = args[1];
raw = args[2];
if (typeof(raw) == LIST)
if (length(raw) > 1)
return "Too many arguments.";
endif
raw = raw[1];
elseif (typeof(raw) == NUM)
return {oname, raw && ((oname == "manymsgs") ? 20 | 1)};
endif
if ((value = $code_utils:tonum(raw)) == E_TYPE)
return tostr("`", raw, "'?  Number expected.");
endif
return {oname, value};
.
#57:3
oname = args[1];
raw = args[2];
if (typeof(raw) == STR)
raw = $string_utils:explode(raw, ",");
elseif (typeof(raw) == NUM)
return raw ? "You need to give one or more recipients." | {oname, 0};
endif
value = $mail_editor:parse_recipients({}, raw);
if (value)
return {oname, value};
else
return "No valid recipients in list.";
endif
.
#57:4
value = this:get(@args);
if (value)
return {tostr(value), {tostr("Query when asking for ", value, " or more messages.")}};
else
return {0, {"Willing to be spammed with arbitrarily many messages/headers"}};
endif
.
#57:5
value = this:get(@args);
if (value)
return {value, {"Sticky folders:  mail commands default to whatever", "mail collection the previous successful command looked at."}};
else
return {0, {"Teflon folders:  mail commands always default to `on me'."}};
endif
.
#57:6
if (value = this:get(@args))
return {"", {tostr("Default message sequence for @mail:  ", (typeof(value) == STR) ? value | $string_utils:from_list(value, " "))}};
else
return {0, {"Default message sequence for @mail:  last:15"}};
endif
.
#57:7
if (value = this:get(@args))
return {"", {tostr("Default Reply-to:  ", $mail_agent:name_list(@value))}};
else
return {0, {"No default Reply-to: field"}};
endif
.
#57:8
if (o = (name = args[2]) in {"sender", "noinclude"})
args[2] = {"all", "include"}[o];
return {@pass(@args), tostr("(", name, " is a synonym for -", args[2], ")")};
else
return pass(@args);
endif
.
#57:9
"... must be object, list of objects, or false...";
value = args[1];
if (typeof(value) == OBJ)
return {{value}};
elseif (!this:istype(value, {{OBJ}}))
return $string_utils:capitalize("Object or list of objects expected.");
else
return {value};
endif
.
#57:10
":check_netmail(value) => Makes sure the email-address is one that can actually be used by $network:sendmail().";
"The actual value sent is not checked since it can only be a boolean flag.  The player's .email_address property is what is checked.";
"Possible situations where the address would be unusable are when the address is invalid or we can't connect to the site to send mail.";
"Returns a string error message if unusable or {value} otherwise.";
if (caller != this)
return E_PERM;
endif
if (args[1])
if (!$network.active)
return "Can't set +netmail:  Outgoing mail not enabled.";
elseif (reason = $network:email_will_fail(player.email_address))
return tostr("Invalid registered .email_address: ", reason);
endif
endif
return args;
.
#57:11
if (value = this:get(@args))
return {value, {"Have MOO-mail automatically forwarded to your", "registered email-address."}};
else
return {value, {"Receive MOO-mail here on the MOO."}};
endif
"Last modified Tue Jun  1 02:10:08 1993 EDT by Edison@OpalMOO (#200).";
.
#57:12
return {@pass(@args), $code_utils, $string_utils, $mail_editor, $mail_agent, $network};
.
#59:0
if (i = args[1] in {"thisonly"})
return {{{"firstancestor"}[i], !args[2]}};
else
return {args};
endif
"Copied from Mail Options (#63):actual by Rog (#145) Tue Apr 16 01:40:49 1996 EDT";
.
#59:1
if (o = (name = args[2]) in {"thisonly"})
args[2] = {"firstancestor"}[o];
return {@pass(@args), tostr("(", name, " is a synonym for -", args[2], ")")};
else
return pass(@args);
endif
"Copied from Mail Options (#63):show by Rog (#145) Tue Apr 16 01:48:20 1996 EDT";
.
#60:0
":get(options,name) => returns the value of the option specified by name";
"i.e., if {name,value} is present in options, return value";
"      if name is present, return 1";
"      otherwise return 0";
{options, name} = args;
if (name in options)
return 1;
elseif (a = $list_utils:assoc(name, options))
return a[2];
else
return 0;
endif
.
#60:1
":set(optionlist,oname,value) => revised optionlist or string error message.";
"oname must be the full name of an option in .names or .extras.";
"Note that values must not be of type ERR.  ";
"FALSE (0, blank string, or empty list) is always a legal value.";
"If a verb :check_foo is defined on this, it will be used to typecheck any";
"non-false or object-type value supplied as a new value for option `foo'.";
"";
"   :check_foo(value) => string error message or {value to use}";
"";
"If instead there is a property .check_foo, that will give either the expected ";
"type or a list of allowed types.";
"Otherwise, the option is taken to be a boolean flag and all non-false, ";
"non-object values map to 1.";
"";
options = args[1];
oname = args[2];
value = args[3];
if (!((oname in this.names) || (oname in this.extras)))
return "Unknown option:  " + oname;
elseif (typeof(value) == ERR)
"... no option should have an error value...";
return "Error value";
elseif ((!value) && (typeof(value) != OBJ))
"... always accept FALSE (0, blankstring, emptylist)...";
elseif ($object_utils:has_callable_verb(this, check = "check_" + oname))
"... a :check_foo verb exists; use it to typecheck the value...";
if (typeof(c = this:(check)(value)) == STR)
return c;
endif
value = c[1];
elseif ($object_utils:has_property(this, tprop = "type_" + oname))
"... a .type_foo property exists...";
"... property value should be a type or list of types...";
if (!this:istype(value, t = this.(tprop)))
return $string_utils:capitalize(this:desc_type(t) + " value expected.");
endif
elseif ($object_utils:has_property(this, cprop = "choices_" + oname))
"... a .choices_foo property exists...";
"... property value should be a list of {value,docstring} pairs...";
if (!$list_utils:assoc(value, c = this.(cprop)))
return tostr("Allowed values: ", $string_utils:english_list($list_utils:slice(c, 1), "(??)", " or "));
endif
else
"... value is considered to be boolean...";
if (!value)
"... must be an object.  oops.";
return tostr("Non-object value expected.");
endif
value = 1;
endif
"... We now have oname and a value.  However, if oname is one of the extras,";
"... then we need to call :actual to see what it really means.";
if (oname in this.names)
nvlist = {{oname, value}};
elseif ((typeof(nvlist = this:actual(oname, value)) != LIST) || (!nvlist))
return nvlist || "Not implemented.";
endif
"... :actual returns a list of pairs...";
for nv in (nvlist)
oname = nv[1];
value = nv[2];
if (i = (oname in options) || $list_utils:iassoc(oname, options))
if ((!value) && (typeof(value) != OBJ))
"value == 0, blank string, empty list";
options[i..i] = {};
elseif (value == 1)
options[i] = oname;
else
options[i] = {oname, value};
endif
elseif (value || (typeof(value) == OBJ))
options[1..0] = {(value == 1) ? oname | {oname, value}};
endif
endfor
return options;
.
#60:2
":parse(args[,...]) => {oname [,value]} or string error message";
"additional arguments are fed straight through to :parse_* routines.";
" <option> <value>     => {option, value}";
" <option>=<value>     => {option, value}";
" <option> is <value>  => {option, value}";
" +<option>            => {option, 1}";
" -<option>            => {option, 0}";
" !<option>            => {option, 0}";
" <option>             => {option}";
if (!(words = args[1]))
return "";
endif
option = words[1];
words[1..1] = {};
if (flag = option && index("-+!", option[1]))
option[1..1] = "";
endif
if (i = index(option, "="))
rawval = option[i + 1..length(option)];
option = option[1..i - 1];
if (i == 1)
"... =bar ...";
return "Blank option name?";
elseif (flag)
"... +foo=bar";
return "Don't give a value if you use +, -, or !";
elseif (words)
"... foo=bar baz quux";
rawval = {rawval, @words};
"OLD VERSION";
"... foo=bar junk";
"    return $string_utils:from_list(words, \" \") + \"??\";";
endif
elseif (!option)
return "Blank option name?";
elseif (flag)
if (words)
"... +foo junk";
return "Don't give a value if you use +, -, or !";
endif
rawval = (flag - 1) % 2;
else
words && ((words[1] == "is") && (words[1..1] = {}));
rawval = words;
endif
"... do we know about this option?...";
if (!(oname = this:_name(strsub(option, "-", "_"))))
return tostr((oname == $failed_match) ? "Unknown" | "Ambiguous", " option:  ", option);
endif
"... determine new value...";
if (!rawval)
"... `@option foo is' or `@option foo=' ...";
return (rawval == {}) ? {oname} | {oname, 0};
elseif ($object_utils:has_callable_verb(this, pverb = "parse_" + oname))
return this:(pverb)(oname, rawval, args[2..length(args)]);
elseif ($object_utils:has_property(this, cprop = "choices_" + oname))
return this:parsechoice(oname, rawval, this.(cprop));
elseif (rawval in {0, "0", {"0"}})
return {oname, 0};
elseif (rawval in {1, "1", {"1"}})
return {oname, 1};
else
return tostr("Option is a flag, use `+", option, "' or `-", option, "' (or `!", option, "')");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835974764, \"Erik\", #74, \"JHM\"}";
.
#60:3
":_name(string) => full option name corresponding to string ";
"               => $failed_match or $ambiguous_match as appropriate.";
if (((string = args[1]) in this.names) || (string in this.extras))
return string;
endif
char = (namestr = this._namelist)[1];
if (!(i = index(namestr, char + string)))
return $failed_match;
elseif (i != rindex(namestr, char + string))
return $ambiguous_match;
else
j = index(namestr[i + 1..length(namestr)], char);
return namestr[i + 1..(i + j) - 1];
endif
.
#60:4
":add_name(name[,isextra]) adds name to the list of options recognized.";
"name must be a nonempty string and must not contain spaces, -, +, !, or =.";
"isextra true means that name isn't an actual option (recognized by :get) but merely a name that the option setting command should recognize to set a particular combination of options.  Actual options go in .names; others go in .extras";
{name, ?isextra = 0} = args;
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
elseif ((!name) || match(name, "[-!+= ]"))
"...name is blank or contains a forbidden character";
return E_INVARG;
elseif (name in this.names)
"...name is already in option list";
if (isextra)
this.names = setremove(this.names, name);
this.extras = setadd(this.extras, name);
return 1;
else
return 0;
endif
elseif (name in this.extras)
if (isextra)
return 0;
else
this.names = setadd(this.names, name);
this.extras = setremove(this.extras, name);
return 1;
endif
else
char = this._namelist[1];
if (isextra)
this.extras = setadd(this.extras, name);
else
this.names = setadd(this.names, name);
endif
if (!index(this._namelist, (char + name) + char))
this._namelist = tostr(this._namelist, name, char);
endif
return 1;
endif
.
#60:5
":remove_name(name) removes name from the list of options recognized.";
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
elseif (!(((name = args[1]) in this.names) || (name in this.extras)))
"...hmm... already gone...";
return 0;
else
char = this._namelist[1];
this._namelist = strsub(this._namelist, (char + name) + char, char);
this.names = setremove(this.names, name);
this.extras = setremove(this.extras, name);
return 1;
endif
.
#60:6
":show(options,name or list of names)";
" => text describing current value of option and what it means";
name = args[2];
if (typeof(name) == LIST)
text = {};
for n in (name)
text = {@text, @this:show(@listset(args, n, 2))};
endfor
return text;
elseif (!((name in this.names) || (name in this.extras)))
return {"Unknown option:  " + name};
elseif ($object_utils:has_callable_verb(this, sverb = "show_" + name))
r = this:(sverb)(@args);
value = r[1];
desc = r[2];
elseif ($object_utils:has_property(this, sverb) && ((value = this:get(args[1], name)) in {0, 1}))
desc = this.(sverb)[value + 1];
if (typeof(desc) == STR)
desc = {desc};
endif
elseif ($object_utils:has_property(this, cprop = "choices_" + name))
if (!(value = this:get(args[1], name)))
desc = this.(cprop)[1][2];
elseif (!(a = $list_utils:assoc(value, this.(cprop))))
return {(name + " has unexpected value ") + $string_utils:print(value)};
else
desc = a[2];
endif
elseif (name in this.extras)
return {name + " not documented (complain)"};
else
value = this:get(args[1], name);
desc = {"not documented (complain)"};
if (typeof(value) in {LIST, STR})
desc[1..0] = $string_utils:print(value);
value = "";
endif
endif
canonized_name = strsub(name, "_", "-");
if (value in {0, 1})
which = "-+"[value + 1] + canonized_name;
elseif ((typeof(value) in {OBJ, STR, NUM}) && (value != ""))
which = tostr(" ", canonized_name, "=", value);
else
which = " " + canonized_name;
endif
show = {$string_utils:left(which, this.namewidth) + desc[1]};
for i in [2..length(desc)]
show = {@show, $string_utils:space(this.namewidth) + desc[i]};
endfor
return show;
.
#60:7
":actual(<name>,<value>) => list of {<name>,<value>} pairs or string errormsg";
" corresponding to what setting option <name> to <value> actually means";
" e.g., :actual(\"unfoo\",1) => {{\"foo\",0}}";
" e.g., :actual(\"g7mode\",1) => {{\"splat\",37},{\"baz\",#3}}";
return "Not implemented.";
.
#60:8
":istype(value,types) => whether value is one of the given types";
if ((vtype = typeof(value = args[1])) in (types = args[2]))
return 1;
elseif (vtype != LIST)
return 0;
else
for t in (types)
if ((typeof(t) == LIST) && this:islistof(value, t))
return 1;
endif
endfor
endif
return 0;
.
#60:9
":islistof(value,types) => whether value (a list) has each element being one of the given types";
types = args[2];
for v in (value = args[1])
if (!this:istype(v, types))
return 0;
endif
endfor
return 1;
.
#60:10
":desc_type(types) => string description of types";
nlist = {};
for t in (types = args[1])
if (typeof(t) == LIST)
if (length(t) > 1)
nlist = {@nlist, tostr("(", this:desc_type(t), ")-list")};
else
nlist = {@nlist, tostr(this:desc_type(t), "-list")};
endif
elseif (t in {NUM, OBJ, STR, LIST})
nlist = {@nlist, {"number", "object", "string", "?", "list"}[t + 1]};
else
return "Bad type list";
endif
endfor
return $string_utils:english_list(nlist, "nothing", " or ");
.
#60:11
":parsechoice(oname,rawval,assoclist)";
which = {};
oname = args[1];
rawval = args[2];
choices = $list_utils:slice(args[3], 1);
errmsg = tostr("Allowed values for this flag: ", $string_utils:english_list(choices, "(??)", " or "));
if (typeof(rawval) == LIST)
if (length(rawval) > 1)
return errmsg;
endif
rawval = rawval[1];
elseif (typeof(rawval) != STR)
return errmsg;
endif
for c in (choices)
if (index(c, rawval) == 1)
which = {@which, c};
endif
endfor
if (!which)
return errmsg;
elseif (length(which) > 1)
return tostr(rawval, " is ambiguous.");
else
return {oname, which[1]};
endif
.
#61:0
if (i = args[1] in {"numbers"})
return {{{"nonumbers"}[i], !args[2]}};
else
return {args};
endif
.
#61:1
if (o = (name = args[2]) in {"numbers"})
args[2] = {"nonumbers"}[o];
return {@pass(@args), tostr("(", name, " is a synonym for -", args[2], ")")};
else
return pass(@args);
endif
.
#62:0
return pass(@args) || (args[1] in $list_utils:map_prop($object_utils:descendants($wiz), "mail_identity"));
.
#63:0
player:tell_lines(this:description());
player:tell(this:psc(), " ", this:verb_sub("is"), " moving around from room to room, cleaning up.");
.
#63:1
"$housekeeper:cleanup([insist]) => clean up player's objects. Argument is 'up' or 'up!' for manually requested cleanups (notify player differently)";
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
endif
for object in (this.clean)
x = object in this.clean;
if (this.requestors[x] == player)
if (result = this:replace(object, @args))
player:tell(result, ".");
endif
endif
$command_utils:suspend_if_needed(0);
endfor
player:tell("The housekeeper has finished cleaning up your objects.");
.
#63:2
"replace the object given to its proper spot (if there is one).";
{object, ?insist = 0} = args;
notify(this, tostr("cleaning ", object, " task ", task_id(), " insist ", insist));
i = object in this.clean;
if (!i)
return tostr(object, " is not on the ", this:name(), "'s cleanup list");
endif
place = this.destination[i];
if (!(($recycler:valid(object) && ($recycler:valid(place) || (place == #-1))) && (!(object.location in this.recycle_bins))))
"object no longer valid (recycled or something), remove it.";
this.clean = listdelete(this.clean, i);
this.requestors = listdelete(this.requestors, i);
this.destination = listdelete(this.destination, i);
return tostr(object) + " is no longer valid, removed from cleaning list";
endif
oldloc = loc = object.location;
if (object.location == place)
"already in its place";
return "";
endif
requestor = this.requestors[i];
if (insist != "up!")
for thing in (object.contents)
if (thing:is_listening())
return ((("Not returning " + object:name()) + " because ") + thing:name()) + " is inside";
endif
$command_utils:suspend_if_needed(0);
endfor
if (valid(loc) && (loc != $limbo))
if (loc:is_listening())
return ((("Not returning " + object:name()) + " because ") + loc:name()) + " is holding it";
endif
for y in (loc:contents())
if ((y != object) && y:is_listening())
return (((("Not returning " + object:name()) + " because ") + y:name()) + " is in ") + loc:name();
endif
$command_utils:suspend_if_needed(0);
endfor
endif
endif
if (valid(place) && (!place:accept(object)))
return (place:name() + " won't accept ") + object:name();
endif
requestor:tell("As you requested, the housekeeper tidies ", object:name(), " from ", valid(loc) ? loc:name() | "nowhere", " to ", valid(place) ? place:name() | "nowhere", " (", place, ").");
if ($object_utils:has_verb(loc, "announce_all_but"))
loc:announce_all_but({requestor, object}, "At ", requestor:name(), "'s request, the housekeeper sneaks in, picks up ", object:name(), " and hurries off to put ", object:po(), " away.");
endif
this:moveit(object, place, requestor);
if ((loc = object.location) == oldloc)
return object:name() + " wouldn't go; perhaps the destination is locked";
else
object:tell("The housekeeper puts you away.");
endif
if ($object_utils:isa(loc, $room))
loc:announce_all_but({object}, "At ", requestor:name(), "'s request, the housekeeper sneaks in, deposits ", object:title(), " and leaves.");
else
loc:tell("You notice the housekeeper sneak in, give you ", object:title(), " and leave.");
endif
return "";
.
#63:3
if (args)
if (!valid(who = args[1]))
return;
endif
player:tell(who:name(), "'s personal cleanup list:");
else
who = 0;
player:tell("Housekeeper's complete cleanup list:");
endif
player:tell("------------------------------------------------------------------");
printed_anything = 0;
objs = this.clean;
reqs = this.requestors;
dest = this.destination;
for i in [1..length(objs)]
$command_utils:suspend_if_needed(2);
req = reqs[i];
ob = objs[i];
place = dest[i];
if (((who == 0) || (req == who)) || (ob.owner == who))
if (!valid(ob))
player:tell($string_utils:left(tostr(ob), 7), $string_utils:left("** recycled **", 50), "(", req:name(), ")");
else
player:tell($string_utils:left(tostr(ob), 7), $string_utils:left(ob:name(), 26), "=>", $string_utils:left(tostr(place), 7), place:name() || "nowhere", " (", req:name(), ")");
endif
printed_anything = 1;
endif
endfor
if (!printed_anything)
player:tell("** The housekeeper has nothing in the cleanup list.");
endif
player:tell("------------------------------------------------------------------");
.
#63:4
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
endif
what = args[1];
if (!valid(what))
return "invalid object";
endif
who = (length(args) > 1) ? args[2] | player;
where = (length(args) > 2) ? args[3] | what.location;
if ($object_utils:isa(who, $guest))
return tostr("Guests can't use the ", this:name(), ".");
endif
if (!is_player(who))
return tostr("Non-players can't use the ", this:name(), ".");
endif
if (!valid(where))
return tostr("The ", this:name(), "doesn't know how to find ", where, " in order to put away ", what:name(), ".");
endif
for x in (this.eschews)
if ($object_utils:isa(what, x[1]))
ok = 0;
for y in [3..length(x)]
if ($object_utils:isa(what, x[y]))
ok = 1;
endif
endfor
if (!ok)
return tostr("The ", this:name(), " doesn't do ", x[2], "!");
endif
endif
endfor
if ($object_utils:has_callable_verb(where, "litterp") ? where:litterp(what) | ((where in this.public_places) && (!(what in where.residents))))
return tostr("The ", this:name(), " won't litter ", where:name(), "!");
endif
if (i = what in this.clean)
if ((!this:controls(i, who)) && valid(this.destination[i]))
return tostr(this.requestors[i]:name(), " already asked that ", what:name(), " be kept at ", this.destination[i]:name(), "!");
endif
this.requestors[i] = who;
this.destination[i] = where;
else
this.clean = {what, @this.clean};
this.requestors = {who, @this.requestors};
this.destination = {where, @this.destination};
endif
return tostr(this:dnamec(), " will keep ", what:dname(), " ", valid(where) ? where:in_name() | where, ".");
.
#63:5
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
endif
what = args[1];
who = (length(args) > 1) ? args[2] | player;
if (i = what in this.clean)
if (!this:controls(i, who))
return tostr("You may remove an object from ", this:name(), " list only if you own the object, the place it is kept, or if you placed the original cleaning order.");
endif
this.clean = listdelete(this.clean, i);
this.destination = listdelete(this.destination, i);
this.requestors = listdelete(this.requestors, i);
return tostr(what:name(), " (", what, ") removed from cleanup list.");
else
return tostr(what:name(), " not in cleanup list.");
endif
.
#63:6
"does player control entry I?";
i = args[1];
who = args[2];
if ((who in {this.owner, @this.owners}) || who.wizard)
return "Yessir.";
endif
cleanable = this.clean[i];
if (this.requestors[i] == who)
return "you asked for the previous result, you can change this one.";
elseif (((who == cleanable.owner) || (!valid(dest = this.destination[i]))) || (who == dest.owner))
return "you own the object or the place where it is being cleaned too, or the destination is no longer valid.";
else
return "";
endif
.
#63:7
"start the housekeeper cleaning continuously. Kill any previous continuous";
"task. Not meant to be called interactively.";
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
endif
if (this.task)
taskn = this.task;
this.task = 0;
kill_task(taskn);
endif
fork taskn (0)
while (1)
for x in (this.clean)
fork (0)
this:replace(x);
endfork
suspend(this.testing ? 2 | this:time());
endfor
this:litterbug();
endwhile
endfork
this.task = taskn;
.
#63:8
for room in (this.public_places)
for thingy in (room.contents)
suspend(10);
litter = 0;
if (((thingy.location == room) && this:is_litter(thingy)) && (!this:is_watching(thingy, #-1)))
"if it is litter and no-one is watching";
fork (0)
this:send_home(thingy);
endfork
suspend(0);
endif
endfor
endfor
.
#63:9
return valid(thing = args[1]) && thing:is_listening();
.
#63:10
if (caller != this)
return E_PERM;
endif
litter = args[1];
littering = litter.location;
this:ejectit(litter, littering);
home = litter.location;
if ($object_utils:isa(home, $room))
home:announce_all("The ", this:name(), " sneaks in, deposits ", litter:title(), " and leaves.");
else
loc:tell("You notice the ", this:name(), " sneak in, give you ", litter:title(), " and leave.");
endif
if ($object_utils:has_callable_verb(littering, "announce_all_but"))
littering:announce_all_but({litter}, "The ", this:name(), " sneaks in, picks up ", litter:title(), " and rushes off to put it away.");
endif
.
#63:11
"Wizardly verb to move object with requestor's permission";
if (caller != this)
return E_PERM;
else
set_task_perms(player = args[3]);
args[1]:moveto(args[2]);
endif
.
#63:12
"this:ejectit(object,room): Eject args[1] from args[2].  Callable only by housekeeper's quarters verbs.";
if (caller == this)
args[2]:eject(args[1]);
endif
.
#63:13
what = args[1];
if (!(where = what in this.clean))
return 0;
else
return {this.destination[where], this.requestors[where]};
endif
.
#63:14
thingy = args[1];
for x in (this.litter)
if ($object_utils:isa(thingy, x[1]) && (!$object_utils:isa(thingy, x[2])))
return 1;
endif
endfor
return 0;
.
#63:15
if ($__core_init_phase)
this.litter = {};
this.public_places = {};
this.requestors = {};
this.destination = {};
this.clean = {};
this.eschews = {};
this.recycle_bins = {};
this.cleaning = #-1;
this.task = 0;
pass(@args);
endif
.
#63:16
count = 0;
for i in (this.requestors)
if (i == player)
count = count + 1;
endif
$command_utils:suspend_if_needed(1);
endfor
player:tell("Number of items in cleanup list: ", tostr(length(this.clean)));
player:tell("Number of items you requested to be tidied: ", tostr(count));
player:tell("Number of requestors: ", tostr(length($list_utils:remove_duplicates(this.requestors))));
player:tell("Time to complete one cleaning circuit: ", $time_utils:english_time(length(this.clean) * this:time()));
.
#63:17
"return a string status if the hosuekeeper is cleaning this object";
cleanable = args[1];
info = this:is_object_cleaned(cleanable);
if (info == 0)
return tostr(cleanable:name(), " is not cleaned by the ", this:name(), ".");
else
return tostr(cleanable:name(), " is kept tidy at ", info[1]:name(), " (", info[1], ") at ", info[2]:name(), "'s request.");
endif
.
#63:18
"Returns the amount of time to suspend between objects while continuous cleaning.";
"Currently set to try to complete cleaning circuit in one hour, but not exceed one object every 20 seconds.";
return max(20, 3600 / length(this.clean));
.
#64:0
return $english:((verb == "verb_sub") ? "get_conj" | "get_noun")(args[1], this:is_plural());
.
#64:1
return this:pp();
.
#64:2
"English only.";
return args[1];
.
#65:0
"Definition from #1";
desc = this:description();
if (desc)
player:tell_lines(desc);
else
player:tell("You see nothing special.");
endif
player:tell("See `help ", this:name(), "' for more information.");
.
#65:1
"Proper usage for the Generic Feature Object:";
"";
"Make a short description.  The :look_msg automatically includes a pointer to `help <this object>', so you don't have to.";
"";
"Put a list of the commands you want people to use in <this object>.feature_verbs.";
"";
"When someone types `help <this object>', they will be told the comment strings from each of the verbs named in .feature_verbs.";
"";
"When you're ready, `@reg <this object>' to enter it in the feature registry.";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835730042, \"Joe\", #73, \"JHM\"}";
.
#65:2
return 1;
return this in args[1]:features();
.
#65:3
caller_perms = callers() ? caller_perms() | player;
if ((caller == this) || $perm_utils:controls(caller_perms, this))
commands = {};
for i in [1..length(verbs(this))]
if ((index((info = verb_info(this, i))[2], "x") && (!("this" in verb_args(this, i)))) && (!index(info[3], "(old)")))
commands = setadd(commands, info[3]);
endif
endfor
this:set_commands(commands);
player:tell("Done.");
else
player:tell("You can't do that, it's not yours.");
endif
.
#65:4
"set_commands(commands_list)";
"set `commands' list.";
"E_TYPE if arg is not a list.";
"E_INVARG if any element is not a string.";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
if (typeof(commands = args[1]) != LIST)
return E_TYPE;
endif
for command in (commands)
if (typeof(command) != STR)
return E_INVARG;
endif
endfor
return this.commands = commands;
endif
.
#65:5
"match_command(vrb, dlist, plist, ilist)";
"return true if this object can handle the command, false otherwise";
"vrb - name of the verb the player typed";
"dlist - list of objspecs that this command matches";
"plist and ilist - likewise for prepspecs, iobjspecs";
{vrb, dlist, plist, ilist} = args;
what = this;
while (what != $feature)
if ((info = verb_info(what, vrb)) && (info[3] in what.commands))
vargs = verb_args(what, vrb);
if (((vargs[1] in dlist) && (vargs[2] in plist)) && (vargs[3] in ilist))
return 1;
else
return 0;
endif
endif
what = parent(what);
endwhile
.
#65:6
"Copied from generic thing (#5):moveto by Jay (#2) Tue Oct 27 18:12:53 1992 CST";
where = args[1];
if ((!valid(where)) || this:is_unlocked_for(where))
pass(where);
endif
.
#65:7
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
pass(@args);
this:set_feature_ok(1);
this:set_commands({});
else
"should this raise an error, or just die?";
endif
.
#65:8
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
.
#65:9
return this.feature_verbs;
.
#65:10
"Copied from Root Class (#1):examine_verb_ok by Joe Feedback (#2) Sun Sep 26 17:31:45 1993 EDT";
"examine_verb_ok(loc, index, info, syntax, commands_ok, hidden_verbs)";
"loc is the object that defines the verb; index (1-based) is which verb on the object; info is verb_info; syntax is verb_args; commands_ok is determined by this:commands_ok, probably, but passed in so we don't have to calculate it for each verb.";
"hidden_verbs is passed in for the same reasons.  It should be a list, each of whose entries is either a string with the full verb name to be hidden (e.g., \"d*rop th*row\") or a list of the form {verb location, full verb name, args}.";
{loc, vindex, info, syntax, commands_ok, hidden_verbs} = args;
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
raise(E_PERM);
endif
vname = info[3];
return (((((syntax[2..3] != {"none", "this"}) && (!index(vname, "("))) && ((commands_ok && index(info[2], "x")) || ("this" in syntax))) && verb_code(loc, vindex)) && (!(vname in hidden_verbs))) && (!({loc, vname, syntax} in hidden_verbs));
.
#65:11
"Usage:  @[un]reg*ister <feature>";
"";
"Add [remove] this feature object from the public registry.";
set_task_perms(player);
unregister = verb[2] == "u";
if (typeof(error = this.registry:(unregister ? "remove_feature" | "add_feature")(this)) == STR)
player:tell(error);
elseif (unregister)
player:tell("You remove ", this:dname(), " from ", this.registry:dname(), ".");
else
player:tell("You add ", this:dname(), " to ", this.registry:dname(), ".");
endif
.
#65:12
return this.registry:match_feature(args[1]);
.
#65:13
return {@pass(@args), $code_utils, $object_utils, $perm_utils, $string_utils, this.registry};
.
#65:14
"Definition from #79";
all_help = $string_utils:pronoun_sub(this.help_text) || {};
if (typeof(all_help) == STR)
all_help = {all_help};
endif
helpless = {};
for vrb in (this:obvious_verbs(player))
if (typeof(vrb) == LIST)
vrb = vrb[2];
endif
if (loc = $object_utils:has_verb(this, vrb))
help = $code_utils:verb_documentation(this, vrb);
if (help)
if (all_help)
msg = verb_info(this, vrb)[3];
underline = $string_utils:space(length(msg), "=");
all_help = {@all_help, "", msg, underline, "", @help};
else
msg = verb_info(this, vrb)[3];
underline = $string_utils:space(length(msg), "=");
all_help = {msg, underline, "", @help};
endif
else
helpless = {@helpless, vrb};
endif
endif
endfor
if (helpless)
if (all_help)
all_help = {@all_help, "", ("No help found on " + $string_utils:english_list(helpless, "nothing", " or ")) + "."};
else
return {(((((("Sorry, but no help is available on " + this:name()) + "(") + tostr(this)) + ").") + "Try `examine ") + this:name()) + "'."};
endif
endif
"return {@all_help, \"----\"};";
return all_help;
.
#65:15
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
pass(@args);
$feature.registry:remove_feature(this);
endif
.
#66:0
return raise(@args);
this:(this.names[tonum(args[1]) + 1])();
.
#66:1
"... hmmm... don't know how to raise E_NONE...";
return E_NONE;
.
#66:2
"...raise E_TYPE ...";
1[2];
.
#66:3
"...raise E_DIV ...";
1 / 0;
.
#66:4
"...raise E_PERM ...";
this.owner.password;
.
#66:5
"...raise E_PROPNF ...";
this.a;
.
#66:6
"...raise E_VERBNF ...";
this:a();
.
#66:7
"...raise E_VARNF ...";
a;
.
#66:8
"...raise E_INVIND ...";
#-1.a;
.
#66:9
move(this, this);
.
#66:10
"...raise E_MAXREC ...";
this:(verb)();
.
#66:11
"...raise E_RANGE ...";
{}[1];
.
#66:12
"...raise E_ARGS ...";
tonum();
.
#66:13
"...raise E_NACC ...";
move($hacker, this);
.
#66:14
"...raise E_INVARG ...";
parent(#-1);
.
#66:15
set_task_perms($no_one);
"...raise E_QUOTA ...";
create($thing);
.
#66:16
return {@pass(@args), $hacker, $no_one, $thing};
.
#66:17
return 0;
.
#67:0
"Say something out loud, directed at someone or something.";
"Usage:";
"  `target message";
"Example:";
"  Munchkin is talking to Kenneth, who's in the same room with him.  He types:";
"      `kenneth What is the frequency?";
"  The room sees:";
"       Munchkin [to Kenneth]: What is the frequency?";
name = verb[2..length(verb)];
if (!name)
player:tell("Usage:  `<target> <message>");
return;
endif
who = player.location:match_object(name);
if (!valid(who))
who = $parse_utils:parse_object_args($string_utils:words(name), {});
if (!who)
return;
endif
dobj = {who};
else
dobj = {{who}};
endif
nothere = {};
here = player:where_are_you();
for who in (dobj[1])
if ((who:where_are_you() != here) && who:is_listening())
nothere = setadd(nothere, who);
endif
endfor
if (nothere)
dobj = {nothere};
player:tell($string_utils:pronoun_sub(this.nothere_msg));
else
$you:say_action(this.((dobj == {{player}}) ? "toself_msg" | "to_msg"));
"event = {{\"speaker\", player}, {\"targets\", dobj[1]}, {\"speech\", argstr}, {\"default_handler\", this.default_handler_event_directed_speech}};";
"player.location:broadcast_event_directed_speech(event);";
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {881822348, \"Ken\", #75, \"JHM\"}";
.
#67:1
"Say something out loud, in some specific way.";
"Usage:";
"  [how]: message";
"Example:";
"  Munchkin decideds to sing some lyrics.  He types:";
"      [sings]: I am the eggman";
"  The room sees:";
"      Munchkin [sings]: I am the eggman";
player.location:announce_all((((player:dnamec() + " ") + verb) + " ") + argstr);
.
#67:2
"Perform some physical, non-verbal, action.";
"Usage:";
"  ]third person action";
"Example:";
"  Munchkin has annoyed some would-be tough guy.  He types:";
"      ]hides behind the reactor.";
"  The room sees:";
"      [Munchkin hides behind the reactor.]";
"To avoid having the extra space added after your name, use ]] instead of ].  For example, Munchkin could type:";
"      ]]'s hiding from Frebble!";
"  At which point, the people in the room would see:";
"      [Munchkin's hiding from Frebble!]";
if (((verb[1] == "]") && (length(verb) > 1)) && (verb[2] == "]"))
player.location:announce_all("[", ((player:dnamec() + verb[3..length(verb)]) + (argstr ? " " + argstr | "")) + "]");
else
player.location:announce_all("[", (((player:dnamec() + " ") + verb[2..length(verb)]) + (argstr ? " " + argstr | "")) + "]");
endif
.
#67:3
"Say something out loud, in some specific way.";
"Usage:";
"  ~how message";
"Example:";
"  Munchkin decides to sing some lyrics.  He types:";
"      ~sings Why are you so far away?";
"  The room sees:";
"       Munchkin [sings]: Why are you so far away?";
what = verb[2..length(verb)];
player.location:announce_all(player:dnamec(), " [", what, "]: ", $code_utils:argstr(verb, args));
.
#67:4
"Usage:  to <target> <message>";
"This is just an alias for the ` command, for people with stupid telecommunication software.  It loses if you try to put any quotation marks in the target string.";
if (args)
"argstr = argstr[length(args[1]) + 2..length(argstr)];";
{first, argstr} = $string_utils:first_word(argstr);
this:("`" + first)();
else
player:tell("Usage:  to <target> <message>");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {858907883, \"splat\", #3024, \"JHM\"}";
.
#67:5
pass(@args);
delete_property(this, "default_handler_event_directed_speech");
.
#68:0
"Usage:  think-big <message>";
"Just like `think', only BIGGER AND MORE ANNOYING, SO DON'T EVER USE IT.";
pname = player:name();
lines = player:linesplit(argstr, 75 - length((pname + " . o O ( ") + " )"));
length = 0;
for line in (lines)
length = max(length, length(line));
endfor
space = $string_utils:space(length(pname) + 1);
bound = $string_utils:space(length, "_");
player.location:announce_all(space, "        ", bound);
player.location:announce_all(space, "       /", $string_utils:space(length), "\\");
for line in (lines)
player.location:announce_all(pname, " . o O ( ", $string_utils:center(line, length), " )");
endfor
player.location:announce_all(space, "       \\", bound, "/");
.
#68:1
"Usage:  think <message>";
"Create a thought bubble with the given text.  DON'T WORRY.  NO ONE ELSE CAN READ YOUR MIND.  IT'S COMPLETELY PRIVATE.";
if (verb[1] == "%")
argstr = $code_utils:argstr(verb, args);
verb[1..1] = "";
if (index(verb, " "))
verb = tostr("\"", verb, "\"");
endif
if (argstr)
argstr = (verb + " ") + argstr;
else
argstr = verb;
endif
endif
if (this:good($string_utils:explode(argstr)) && (argstr = $string_utils:from_list($string_utils:explode(argstr), " ")))
player.location:announce_all(player:dnamec(), " . o O [ ", argstr, " ]");
else
player.location:announce_all(player:dnamec(), " . o O ( ", argstr, " )");
endif
.
#68:2
pname = player:name();
player.location:announce_all((($string_utils:space(length(pname) + 8) + ".") + $string_utils:space(length(argstr), "-")) + ".");
player.location:announce_all(pname, " . o O ( ", argstr, " )");
player.location:announce_all((($string_utils:space(length(pname) + 8) + "`") + $string_utils:space(length(argstr), "-")) + "'");
.
#68:3
"args[1] is a list of strings.  A false value is returned iff the list doesn't make up a syntactically correct word square.";
"stolen from #22285:good on lambdamoo. -Matt";
words = length(args[1]);
for word in (args[1])
if (length(word) != words)
return 0;
endif
endfor
return 1;
.
#69:0
"do(text, who, thing, location, dobj, iobj[, <numbered args>])";
"Apply pronoun substitution to a piece of text.";
"text is a list in \"text\" format, to be documented elsewhere.";
"who, thing, location, dobj, and iobj are lists of the form {<list of objects>, @<args to $string_utils:english_list>}.";
"<numbered args> defaults to {}.";
set_task_perms($no_one);
if (typeof(text = args[1]) != LIST)
return E_TYPE;
elseif (length(args) < 6)
return E_ARGS;
endif
return $string_utils:from_list($list_utils:map_arg(this, "do_segment", text, @listdelete(args, 1)));
.
#69:1
"do_segment(segment,who,thing,location,numbered args)";
"if segment is a string, we just return it.";
"otherwise, it must be a list.";
"if segment[1] is a string, then call";
"  this:(\"sub_\" + segment[1])(@listdelete(segment, 1), @listdelete(args, 1))";
"other args to be documented elsewhere.";
if (typeof(segment = args[1]) == STR)
return segment;
elseif (typeof(type = segment[1]) == STR)
if (type == "lines")
return $list_utils:map_arg($string_utils, "pronoun_sub", listdelete(segment, 1), @args[2..length(args)]);
endif
args[1..1] = listdelete(segment, 1);
return this:("sub_" + type)(@args);
else
return E_TYPE;
endif
.
#69:2
"sub_name(objspec, capitalize, who, thing, location, numbered args)";
"return the title of an object";
"objspec is as specified in this:do_object";
"use :titlec if capitalize is 1; uppercase if capitalize is 2; titleize if capitalize is 3";
"if capitalize is a list pass it to the new name code";
"sub_dname and sub_iname get definite- and indefinite-article forms.";
if (typeof(args[2]) == LIST)
if (valid(what = this:do_object(@listdelete(args, 2))))
return what:name(@args[2]);
elseif (typeof(what) == LIST)
if (typeof(what[1]) == LIST)
return $language:name_list(what[1], listdelete(what, 1), @args[2]);
else
return what[1]:("do_" + verb)(listdelete(what, 1), args[2]);
endif
else
return "nothing";
endif
endif
if (valid(what = this:do_object(@listdelete(args, 2))))
ret = what:(verb[5..length(verb)] + (args[2] ? "c" | ""))();
elseif (typeof(what) == LIST)
if (typeof(what[1]) == LIST)
ret = $string_utils:(verb[5..length(verb)] + (args[2] ? "c_list" | "_list"))(@what);
else
ret = what[1]:("do_" + verb)(listdelete(what, 1), args[2]);
endif
else
ret = args[2] ? "Nothing" | "nothing";
endif
if (args[2] == 2)
return $string_utils:uppercase(ret);
elseif (args[2] == 3)
return $language:titleize(ret);
else
return ret;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866524421, \"James\", #2142, \"JHM\"}";
.
#69:3
"do_object(objspec, who, thing, location, dobj, iobj, @numbered args)";
"if objspec is a number, it's taken as an index into numbered args.";
"if objspec is an object, well, it's an object.";
"if objspec is a string, it must be one of \"who\", \"thing\", \"location\", \"dobj\", or \"iobj\", in which case the objects specified in the args are used.";
"if objspec is a list, call";
"  this:(\"obj_\" + objspec[1])(@listdelete(objspec, 1), @listdelete(args, 1))";
if (typeof(objspec = args[1]) == STR)
if (length(args) < 6)
ret = {@args, dobj, iobj}[objspec in {0, "player", "thing", "location", "0", "dobj", "iobj"}];
else
ret = {@args}[objspec in {0, "player", "thing", "location", "dobj", "iobj"}];
endif
elseif (typeof(objspec) == LIST)
args[1..1] = listdelete(objspec, 1);
ret = this:("obj_" + objspec[1])(@args);
elseif (typeof(objspec) == NUM)
ret = args[6 + objspec];
elseif (typeof(objspec) == OBJ)
ret = objspec;
else
return E_TYPE;
endif
if (length(ret[1]) == 1)
return ret[1][1];
else
return ret;
endif
.
#69:4
"sub_verb(actor, normal-form verb, who, thing, location, numbered args)";
"actor is an objspec, as detailed in this:do_object.";
"normal-form verb is a string, the form of the verb used by ordinary third-person-singular players: for example, \"eats\" or \"teleports\".";
actor = this:do_object(@listdelete(args, 2));
ov = (typeof(args[2]) == LIST) ? this:do(@listdelete(args, 1)) | args[2];
if (typeof(actor) == OBJ)
v = actor:verb_sub(ov);
return (v == E_VERBNF) ? ov | v;
elseif (typeof(actor) == LIST)
return $they:verb_sub(ov);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866899362, \"splat\", #3024, \"JHM\"}";
.
#69:5
"sub_pronoun(objspec, pronoun, who, thing, location, numbered args)";
"return a pronoun (or possessive adjective)";
"objspec as detailed in this:do_object";
"pronoun is a string of 2 or 3 characters; the first char should be \"p\"; the second char is the type of pronoun (o, p, q, r, or s) and may be followed by \"c\" to get the capitalized form.";
set_task_perms($no_one);
"Do away with fancy args checking because we have set_task_perms()--the args checking was just to keep people from looking at !r properties.";
"This also makes it more general--maybe it should be renamed.";
pronoun = args[2];
what = this:do_object(@listdelete(args, 2));
if (typeof(what) == OBJ)
return what:(pronoun)() || $it:(pronoun)();
elseif (typeof(what[1]) == OBJ)
return what:do_sub_pronoun(listdelete(what, 1), args[2]);
elseif (what[1])
return (($you in what[1]) ? $plural_you | $they):(pronoun)();
else
return $string_utils:(index(pronoun, "c") ? "namec_list" | "name_list")(@what);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {865897593, \"splat\", #3024, \"JHM\"}";
.
#69:6
"sub_objnum(objspec, who, thing, location, numbered args)";
"return the objnum of an object";
"objspec is as specified in this:do_object";
what = this:do_object(@args);
if (typeof(what) == LIST)
return $string_utils:english_list($list_utils:map_builtin(what[1], "tostr"), listdelete(what, 1));
else
return what;
endif
.
#69:7
"sub_property(objspec, propname, capitalize, who, thing, location, numbered args)";
"return a property value";
"objspec as detailed in this:do_object";
"propname must be a string";
"if capitalize is true, capitalize the value before returning it.";
set_task_perms($no_one);
what = this:do_object(args[1], @args[4..$]);
if (typeof(what) == OBJ)
ret = what.(args[2]);
else
ret = $string_utils:english_list($list_utils:map_prop(what[1], args[2]), listdelete(what, 1));
endif
if (args[3])
return $string_utils:capitalize(ret);
else
return ret;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {912984110, \"Ken\", #75, \"JHM\"}";
.
#69:8
"Usage:  obj_location(objspec, who, thing, location, numbered args)";
"objspec as in do_object";
"return objspec's location, or $nothing";
return this:(strsub(verb, "obj", "") + "s")(this:do_object(@args));
.
#69:9
"sub_noun(actor, singular noun, who, thing, location, numbered args)";
"actor is an objspec, as detailed in this:do_object.";
"singular noun is a string, the ordinary singular form of the noun in question";
"In case you were wondering, this is mostly useful for things like";
"  Munchkin shakes his head.";
"  Munchkin and Froo-Froo shake their heads.";
actor = this:do_object(@listdelete(args, 2));
if (typeof(actor) == OBJ)
noun = actor:noun_sub((typeof(args[2]) == LIST) ? this:do(@listdelete(args, 1)) | args[2]);
return (noun == E_VERBNF) ? args[2] | noun;
elseif (typeof(actor) == LIST)
return $they:noun_sub((typeof(args[2]) == LIST) ? this:do(@listdelete(args, 1)) | args[2]);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866899247, \"splat\", #3024, \"JHM\"}";
.
#69:10
"lines(lines, who, thing, location, numbered args)";
"return a list of strings derived from the items in <lines>.";
set_task_perms($no_one);
return $list_utils:map_arg($string_utils, "pronoun_sub", @args);
.
#69:11
"sub_special(objspec, verb, args, who, thing, location, numbered args)";
"call back to the object with the message";
set_task_perms($no_one);
what = this:do_object(args[1], @args[4..$]);
if (typeof(what) == OBJ)
return what:("sub_" + args[2])(@args[3] || {});
elseif (typeof(what) == LIST)
return $string_utils:english_list($list_utils:map_verb(what, "sub_" + args[2], @args[3] || {}));
else
return tostr(what);
endif
.
#69:12
"$pronoun_sub:parse_parties(args, caller)";
"Canonicalize the list of parties for a pronoun_sub.";
"args is a list of arguments: who, thing, where, dobj, iobj.  These default to player, caller (the value of \"this\" when called), who.location, dobj, and iobj, respectively.  Any of these may be lists, of the form {<list of objs>, @<args to $string_utils:english_list>}.";
"Returns a list of these lists with appropriate defaults substituted.";
thing = args[2];
{?who = player, ?what = thing, ?where = this:_locations(who), ?dobj, ?iobj, @other} = args[1];
return $list_utils:map_arg(1, this, "_listify", {who, what, where, dobj, iobj, @other});
.
#69:13
"Canonicalize argument into {<list of objects>, @<args to $string_utils:english_list>} form.";
if (typeof(l = args[1]) == LIST)
return l || {l};
elseif ((typeof(l) == OBJ) && (!valid(l)))
return {{}};
endif
return {{l}};
.
#69:14
"args[1] = {<list of objects>, @<args to $string_utils:english_list>}";
"Return {<set of objects' locations>, @<args to $string_utils:english_list>}";
l = args[1];
if (typeof(l) == OBJ)
l = {{l}};
endif
locs = {};
for o in (l[1])
try
locs = setadd(locs, (verb == "_outer_locations") ? o:where_are_you() | o.location);
except (E_INVIND, E_VERBNF)
"in case we're invalid or garbage or something";
endtry
endfor
l[1] = locs;
return l;
.
#69:15
"sub_string(whichstr, capitalize, who, thing, location, numbered args)";
"return a string (dobjstr, iobjstr, argstr, prepstr)";
"whichstr is string naming the variable";
"capitalize if capitalize is 1; uppercase if capitalize is 2; titleize if capitalize is 3";
ret = {dobjstr, iobjstr, argstr, prepstr}[args[1] in {"dobjstr", "iobjstr", "argstr", "prepstr"}];
if (args[2] == 1)
return $string_utils:capitalize(ret);
elseif (args[2] == 2)
return $string_utils:uppercase(ret);
elseif (args[2] == 3)
return $language:titleize(ret);
else
return ret;
endif
"Copied from pronoun substitution (#98):sub_name by Erik (#74) Fri Oct 21 20:07:50 1994 EDT";
.
#69:16
"Usage:  :message_empty(message)";
"Return true if the message is empty, i.e., a false value, or a message that unparses to a false value.";
message = args[1];
return (!message) || ((message[1] == this) && (length(message) == 2));
.
#69:17
return {@pass(@args), $string_utils, $no_one, $list_utils, $language, $object_utils, $you, $it, $plural_you, $they, $prog, this.generic, this.("two-letter"), this.lambdacore};
.
#69:18
return $string_utils:uppercase(this:do(@args));
.
#69:19
return this:do($list_utils:any_of(args[1]), @listdelete(args, 1));
.
#69:20
"Usage: mentioned_parties(message)";
"returns a list of the parties mentioned in <message>.";
{item} = args;
if (typeof(item) == STR)
item = this.lambdacore:parse(item);
endif
{should_be_this, v, @info} = item;
if (should_be_this != this)
return `should_be_this:(verb)(item) ! E_VERBNF, E_INVIND, E_TYPE => raise(E_INVARG)';
endif
if (v == "lines")
return $list_utils:remove_duplicates($list_utils:flatten($list_utils:map_arg(this, verb, info)));
elseif (v != "do")
raise(E_INVARG);
endif
them = {};
partiful = {"name", "dname", "iname", "verb", "noun", "special", "property", "pronoun", "objnum"};
for it in (info)
if (((typeof(it) == LIST) && it) && (it[1] in partiful))
them = {@them, it[2]};
endif
endfor
return $list_utils:remove_duplicates(them);
.
#69:21
"obj_special(objspec, verb, args, who, thing, location, numbered args)";
"call back to the object with the message";
set_task_perms($no_one);
what = this:do_object(args[1], @args[4..$]);
if (typeof(what) == OBJ)
return $code_utils:verb_or_property(what, "obj_" + args[2], @args[3] || {});
elseif (typeof(what) == LIST)
"this is completely wrong, but I haven't got a clue what else to do here";
return {$set_utils:union(@$list_utils:slice($list_utils:map_arg(this, "_listify", $list_utils:map_arg($code_utils, "verb_or_property", what[1], "obj_" + args[2], @args[3] || {})))), @what[2..$]};
else
return tostr(what);
endif
"Copied from pronoun substitution (#98):sub_special by James (#2142) Sun Apr 21 17:25:15 1996 EDT";
.
#69:22
"obj_contents(objspec, who, thing, location, numbered args)";
"the set of all contents of objects specified by <objspec>, with proper :english_list args";
what = this:do_object(args[1], @args[2..$]);
if (typeof(what) == OBJ)
return {what:contents()};
elseif (typeof(what) == LIST)
return {$set_utils:union(@$list_utils:map_verb(what[1], "contents")), @what[2..$]};
else
return tostr(what);
endif
"Copied from pronoun substitution (#98):sub_special by James (#2142) Sun Apr 21 17:25:15 1996 EDT";
"Copied from pronoun substitution (#98):obj_special by James (#2142) Sun Apr 21 18:05:40 1996 EDT";
.
#69:24
"Usage: flatten_message(message, parties) => {newmessage, newparties}";
"returns a predigested version of <message> in which none of the parties are complex or depend on each other, as well as a set of parties to match.";
result = this:_flatten_message(@args, {}, {});
return `{result[1], {{{}}, {{}}, {{}}, {{}}, {{}}, @$list_utils:map_arg(this, "_listify", result[2])}} ! E_TYPE, E_RANGE => raise(E_INVARG)';
.
#69:25
"Usage: flatten_message(message, oparties, parties, partyids)";
"=> {newmessage, newparties, newpartyids}";
"returns a new message, with no parties depending on each other or requiring special parsing.  Also a list of the new parties and a list of which original parties they stand for.  Don't call this, call the one without the underline, but this is what you should implement if you want to make your own message types (other than $pronoun_sub messages)";
{item, oparties, parties, partyids} = args;
if (typeof(item) == STR)
item = this.lambdacore:parse(item);
endif
{should_be_this, v, @info} = item;
if (should_be_this != this)
return `should_be_this:(verb)(@args) ! E_VERBNF, E_INVIND, E_TYPE => raise(E_INVARG)';
endif
if (v == "lines")
done = {this, v};
for item in (info)
result = this:(verb)(item, oparties, parties, partyids);
try
{newitem, parties, partyids} = result;
except (E_ARGS, E_TYPE)
raise(E_INVARG);
endtry
done = {@done, newitem};
endfor
return {done, parties, partyids};
elseif (v != "do")
raise(E_INVARG);
endif
them = {this, v};
partiful = {"name", "dname", "iname", "verb", "noun", "special", "property", "pronoun", "objnum"};
subordful = {"verb", "noun"};
try
for pit in (info)
it = pit;
if ((typeof(it) == LIST) && it)
if (it[1] in partiful)
party = it[2];
if (ix = party in partyids)
it[2] = ix;
else
partyids = {@partyids, party};
parties = {@parties, this:do_object(party, @oparties)};
it[2] = length(parties);
endif
endif
if ((it[1] in subordful) && (typeof(it[3]) == LIST))
try
{result, parties, partyids} = this:(verb)({$pronoun_sub, "do", @it[3]}, oparties, parties, partyids);
it[3] = result[3..$];
except (E_ARGS, E_TYPE, E_RANGE)
raise(E_INVARG);
endtry
elseif (it[1] == "pick")
try
submessage = it[2][random($)];
{result, parties, partyids} = this:(verb)({$pronoun_sub, "do", @submessage}, oparties, parties, partyids);
them = {@them, @result[3..$]};
continue pit;
except (E_ARGS, E_TYPE, E_RANGE)
raise(E_INVARG);
endtry
endif
endif
them = {@them, it};
endfor
except (E_RANGE)
raise(E_INVARG);
endtry
return {them, parties, partyids};
"Metadata 202106";
"Last-Modify: {866899530, \"splat\", #3024, \"JHM\"}";
.
#69:26
"obj_special(objspec, verb, args, who, thing, location, numbered args)";
"call back to the object with the message";
what = this:do_object(args[1], @args[3..$]);
return {this:_listify(what)[1], @args[2]};
"Copied from pronoun substitution (#98):sub_special by James (#2142) Sun Apr 21 17:25:15 1996 EDT";
"Copied from pronoun substitution (#98):obj_special by James (#2142) Sun Apr 28 23:27:39 1996 EDT";
.
#70:0
who = args[1];
if (who.home == this)
move(who, $limbo);
this:announce("You hear a quiet popping sound; ", who:dname(), " ", who:verb_sub("has"), " disconnected.");
else
pass(who);
endif
.
#70:1
who = args[1];
if ($limbo:accept(who))
move(who, $limbo);
else
pass(who);
endif
.
#70:2
if (!$__core_init_phase)
$error:raise(E_PERM);
endif
pass(@args);
"set description, name, etc";
clear_property(this, "proper");
clear_property(this, "say_msg");
this.name = "first room";
this.aliases = {"first room"};
this.description = {"This is all there is right now."};
this.exits = this.entrances = {};
this.dark = 0;
for v in ({"emote", "in_name"})
delete_verb(this, v);
endfor
.
#70:3
return {@pass(@args), $limbo, $error};
.
#72:0
len = length(st = args[1]);
if (k = st in $list_utils:slice(this.noun_exceptions, 2))
return this.noun_exceptions[k][1];
endif
return this:_remove_s(st, len);
.
#72:1
len = length(st = args[1]);
if ((len > 2) && (st[1..2] == "a "))
st = st[3..len];
len = length(st);
elseif ((length(st) > 3) && (st[1..3] == "an "))
st = st[4..len];
len = length(st);
endif
if (k = st in $list_utils:slice(this.noun_exceptions))
return this.noun_exceptions[k][2];
endif
return this:_add_s(st, len);
.
#72:2
"@add-vowel-exc*eption <word> to $english";
"Add to $english.vowel_exceptions a word prefix that starts with a vowel but does not take 'an' as an indefinite article (eg. \"usu\").";
"";
"@add-nonvowel-exc*eption <word> to $english";
"Add to $english.nonvowel_exceptions a word prefix that starts with a non-vowel but takes 'an' as an indefinite article (eg. \"honor\").";
"";
"If the player is not a wizard, mail is sent to this.suggestions_recipient.";
who = callers() ? caller_perms() | player;
non = index(verb, "non") ? "non" | "";
propname = non + "vowel_exceptions";
vowel = dobjstr[1] in this.vowels;
if ((vowel && non) || ((!vowel) && (!non)))
who:notify(((("Sorry, a " + non) + "vowel exception must start with a ") + (non ? "consonant" | "vowel")) + ".");
return;
endif
for prefix in (this.(propname))
if ((length(dobjstr) >= length(prefix)) && (dobjstr[1..length(prefix)] == prefix))
who:notify(((("The prefix \"" + prefix) + "\" is already recognized as a ") + non) + "vowel exception.");
return;
endif
endfor
who:notify(((("All words starting with \"" + dobjstr) + "\" take \"") + (non ? "an" | "a")) + "\" as an indefinite article.");
if (!$command_utils:yes_or_no("Is this correct?"))
who:notify("Aborted.");
return;
endif
if (!who.wizard)
subject = ("suggestion for " + non) + "vowel exception";
recipient = this.suggestions_recipient;
result = $mail_agent:send_message(who, {recipient}, subject, dobjstr);
if (result && result[1])
who:notify(("Suggestion sent to " + $mail_agent:addrname(recipient)) + ".");
else
who:notify(("Error sending mail to " + $mail_agent:addrname(recipient)) + "!");
endif
else
this.(propname) = {@this.(propname), dobjstr};
who:notify(((("Added \"" + dobjstr) + "\" to the list of ") + non) + "vowel exceptions.");
endif
.
#72:3
"Capitalizes its argument as if it were a word in a title.";
if (string = args[1])
i = 1;
while ((i < length(string)) && (!index(this.alphabet + "0123456789", string[i])))
i = i + 1;
endwhile
j = length(string);
while ((j > i) && (!index(this.alphabet + "0123456789", string[j])))
j = j - 1;
endwhile
if (!(string[i..j] in this.capitalize_exceptions))
string[i] = $string_utils:uppercase(string[i]);
endif
endif
return string;
.
#72:4
"[non]vowel_exception(<word>)";
"Does <word> start with a [non]vowel and take `a[n]' as an indefinite article?";
word = args[1];
exc = this.(verb + "s");
for prefix in (exc)
if (index(word, prefix) == 1)
return 1;
endif
endfor
return 0;
.
#72:5
"article(word) ==> \"a\" or \"an\"";
{word, ?is_plural = 0} = args;
if (is_plural)
return "some";
elseif (!word)
"this case just sucks all around";
return "a";
else
if (!index("0123456789" + this.alphabet, word[1]))
"here's how we handle `decorations' like scare-quotes and *emphasis*";
try
word[1..match(word, "%w")[1] - 1] = "";
except (E_RANGE)
"DWI";
endtry
endif
if (index("0123456789", word[1]))
"hand-optimized for your pleasure by wizard #3024";
"get rid of everything but the number.  scientific notation can BITE ME";
word = word[1..`match(word, "^[0-9,]+%(%.[0-9]*%)?")[2] ! E_RANGE => $'];
"then get rid of decimals unless that's all we've got";
if (`word[1..2] != "0." ! E_RANGE => 1')
word = word[1..(index(word, ".") || (1 + $)) - 1];
word = strsub(word, ",", "");
"and lower thousand-groups too";
word = word[1..1 + (($ - 1) % 3)];
else
word[1..match(word, "0%.%(000%)*")[2]] = "";
word = word || "0";
endif
"now we just have the digits representing the first word group for some fairly sensible way of pronouncing numbers.  Teensy decimal fractions are kinda iffy, but ...";
"note that only 'eight', 'eighty', 'eight hundred', 'eighteen', and 'eleven' begin with vowels.";
"note also that we don't pronounce 1800 as 'eighteen hundred'.";
if (((word[1] == "8") || (word == "18")) || (word == "11"))
return "an";
else
return "a";
endif
elseif (((word[1] in $english.vowels) && (!$english:vowel_exception(word))) || $english:nonvowel_exception(word))
return "an";
else
return "a";
endif
endif
"turned from a nice simple routine to a bloated monster by James, who places the blame squarely on Doug.";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {860031705, \"splat\", #3024, \"JHM\"}";
.
#72:6
"get_conj/noun(spec[,plural])";
"Return the singular [plural] form of the verb or noun.";
"spec can be one of \"singular/plural\", \"singular\", \"singular/\", or \"/plural\", e.g., \"is/are\", \"is\", \"is/\", or \"/are\".";
{spec, ?plural = 0} = args;
i = index(spec + "/", "/");
sing = spec[1..i - 1];
if (i < length(spec))
plur = spec[i + 1..length(spec)];
else
plur = "";
endif
cap = strcmp("a", (i == 1) ? spec[2] | spec) > 0;
part = (verb == "get_noun") ? "noun" | "verb";
if (plural)
vb = plur || this:(("_" + part) + "_plural")(sing);
else
vb = sing || this:(("_" + part) + "_singular")(plur);
endif
if (cap)
return $string_utils:capitalize(vb);
else
return vb;
endif
"Copied from gender utilities (#41):get_conj by Doug (#107) Sun May 15 20:18:43 1994 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835903447, \"Erik\", #74, \"JHM\"}";
.
#72:7
len = length(st = args[1]);
if ((len > 2) && (rindex(st, "n't") == (len - 2)))
return this:(verb)(st[1..len - 3]) + "n't";
elseif (a = $list_utils:assoc(st, this.verb_exceptions, 2 - (pluralize = (verb == "_verb_plural") ? 1 | 0)))
return a[pluralize + 1];
endif
return this:(pluralize ? "_remove_s" | "_add_s")(st, len);
.
#72:8
"_add_s(word, length)";
"Add -s to the end of the word.";
st = args[1];
len = args[2];
if (len < 2)
return st + "s";
endif
if ((st[len] == "y") && (!index("aeiou", st[len - 1])))
return st[1..len - 1] + "ies";
elseif ((((st[len] == "o") && (!index("aeiouy", st[len - 1]))) || index("sx", st[len])) || ((len > 1) && (index("chsh", st[len - 1..len]) % 2)))
return st + "es";
else
return st + "s";
endif
.
#72:9
"_remove_s(word, length)";
"Remove -s from the end of the word.";
st = args[1];
len = args[2];
if ((len <= 3) || (st[len] != "s"))
return st;
elseif (st[len - 1] != "e")
return st[1..len - 1];
elseif ((((st[len - 2] == "h") && index("cs", st[len - 3])) || index("ox", st[len - 2])) || (index("sz", st[len - 2]) && (!index("aeiouy", st[len - 3]))))
return st[1..len - 2];
elseif (st[len - 2] == "i")
return st[1..len - 3] + "y";
else
return st[1..len - 1];
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {893773364, \"Ragnar\", #102, \"JHM\"}";
.
#72:10
"name_list(<object list>, <:english_list() args>, @<:name() args>)";
"";
"Creates an english list out of the names of the objects in <object list>.  <:english_list args> are passed on to :english_list(), and <:name() args> to :name().";
"";
"Note that the string to use for \"nothing\" in :english_list gets the name flags applied to it in a fairly standardized way.  I suppose this may break some really weird applications but $pronoun_sub sure seems to like this sort of thing.";
nargs = args[3..length(args)];
largs = args[2] || {"nothing"};
first = args[1] ? {args[1][1]:name(@nargs)} | {$name_utils:fake_proper_name(largs[1], @nargs)};
nargs = $name_utils:remove_name_flag("c", nargs);
return $string_utils:english_list({@first, @$list_utils:map_verb_safe(args[1] ? listdelete(args[1], 1) | {}, "name", @nargs)}, @largs);
.
#72:11
"name_and_number_list(<object list>, <:english_list() args>, @<:name() args>)";
"Creates an english list out of the names of the objects in <object list>, with objnums postfixed in parentheses.  <:english_list args> are passed on to :english_list(), and <:name() args> to :name().";
nargs = args[3..length(args)];
first = args[1] ? {args[1][1]:name(@nargs)} | {};
nargs = $name_utils:remove_name_flag("c", nargs);
temp = {@first, @$list_utils:map_verb(args[1] ? listdelete(args[1], 1) | {}, "name", @nargs)};
it = {};
for i in [1..length(temp)]
it = {@it, tostr(temp[i] || "nothing", " (", args[1][i], ")")};
endfor
return $string_utils:english_list(it, @args[2]);
.
#72:12
"Capitalizes its argument in the manner of a title.";
words = $string_utils:to_list(args[1]);
if (length(words) < 2)
if (words)
return $string_utils:capitalize(args[1]);
else
return args[1];
endif
else
return $string_utils:from_list({$string_utils:capitalize(words[1]), @$list_utils:map_arg($english, "capitalize", words[2..length(words) - 1]), $string_utils:capitalize(words[length(words)])}, " ");
endif
.
#72:13
return {@pass(@args), $list_utils, $mail_agent, $command_utils, $string_utils, $name_utils, this.all, this.everything, this.here};
.
#72:14
if ($__core_init_phase)
pass(@args);
this.suggestions_recipient = $first_wiz;
else
$error:raise(E_PERM);
endif
.
#72:15
return this.selector_words || {};
.
#72:16
if (args[1] in {"everyone", "everybody"})
tmp = {};
for item in (args[2].contents)
if (is_player(item))
tmp = {@tmp, item};
endif
endfor
return tmp;
else
return ((args[1] in this.selector_words) && {this.(args[1])}) || {};
endif
.
#72:17
"For completeness' sake.";
return "the";
.
#72:18
return this:(strsub(verb, "_else", "_but_me"))(@args);
.
#72:19
sel = verb[11..length(verb)];
i = index(sel, "_");
selword = sel[1..i - 1];
sel[1..i] = "";
i = index(sel, "_");
sel[1..i] = "";
exceptions = $parse_utils:parse_object_args($string_utils:words(strsub(sel, "_", " ")), {}, @args);
return (exceptions == 0) ? exceptions | $set_utils:difference($language:selector_word_default(selword, @args), exceptions);
.
#72:20
name = args[1];
return name in {@this.cardinal_directions, "up", "down"};
.
#72:21
dir = args[1];
return this.cardinal_direction_opposites[dir in this.cardinal_directions];
.
#72:22
"name_list(<object list>, <:english_list() args>, @<:name() args>)";
"";
"Creates an english list out of the names of the objects in <object list>.  <:english_list args> are passed on to :english_list(), and <:name() args> to :name().";
"";
"Note that the string to use for \"nothing\" in :english_list gets the name flags applied to it in a fairly standardized way.  I suppose this may break some really weird applications but $pronoun_sub sure seems to like this sort of thing.";
nargs = args[3..length(args)];
largs = args[2] || {"nothing"};
first = args[1] ? {args[1][1]:name(@nargs)} | {$name_utils:fake_proper_name(largs[1], @nargs)};
nargs = $name_utils:remove_name_flag("c", nargs);
return $string_utils:english_list({@first, @$list_utils:map_verb_safe(args[1] ? listdelete(args[1], 1) | {}, "name", @nargs)}, @largs);
.
#73:0
if (typeof(old = args[1]) == LIST)
return {$pronoun_sub, "lines", @$list_utils:map_arg(this, "parse", @args)};
elseif (typeof(old) != STR)
return E_TYPE;
endif
all = {};
new = {$pronoun_sub, "do"};
current = "";
while (old)
segment = this:parse_string(old);
current = current + segment[1];
old = segment[3];
segment = segment[2];
if (segment)
if (current)
new = {@new, current};
current = "";
endif
if (segment == 1)
all = {@all, new};
new = {$pronoun_sub, "do"};
else
new = {@new, segment};
endif
endif
endwhile
if (current)
new = {@new, current};
endif
return all ? {$pronoun_sub, "lines", @all, new} | new;
"Metadata 202106";
"Last-Modify: {866906860, \"splat\", #3024, \"JHM\"}";
.
#73:1
text = args[1];
if (!text)
return {"%", ""};
elseif (text[1] == "%")
return {"%", text[2..length(text)]};
elseif (text[1] == "{")
if (text[2] == "}")
return {"%{}", text[3..length(text)]};
endif
return this:parse_verbose(text);
else
return {"%" + text[1], text[2..length(text)]};
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {864675095, \"James\", #2142, \"JHM\"}";
.
#73:2
"parse verbose-form sub";
"standard format is terms, separated by spaces.";
{subject} = args;
subject = $string_utils:triml(subject[2..$]);
parts = {};
while (subject)
if ("\"" == subject[1])
q = $string_utils:prefix_to_value(subject);
if (q[1] == 0)
"we now can't rely on the remainder";
return {args[1], ""};
endif
parts = {@parts, q[2]};
subject = q[1];
elseif ("{" == subject[1])
{q, subject} = this:(verb)(subject);
parts = {@parts, q};
elseif ("}" == subject[1])
subject[1..1] = "";
return {parts, subject};
else
q = match(subject, "^%([^%} ]%|%%.%)*");
word_last = `q[2] ! E_RANGE => length(subject)';
word = $string_utils:substitute(subject[1..word_last], {{"%%", "%"}, {"%", ""}});
if (word in $code_utils.error_names)
word = $code_utils:toerr(word);
elseif (match(word, "^#-?[0-9]+$"))
word = toobj(word);
elseif (match(word, "^-?[0-9]+$"))
word = tonum(word);
elseif (match(word, "^-?[0-9]+%(%.[0-9]+%)?%([Ee][+-]?[0-9]+%)?$"))
word = tofloat(word);
endif
parts = {@parts, word};
subject[1..word_last] = "";
endif
subject = $string_utils:triml(subject);
endwhile
"things didn't nest right, so we don't rely on anything we did ...";
return {args[1], ""};
"Metadata 202106";
"Last-Modify: {864686735, \"James\", #2142, \"JHM\"}";
.
#73:3
"unparse list-based pronoun_sub into generic (verbose) form";
if (((typeof(text = args[1]) == LIST) && (length(text) >= 2)) && (text[1..2] == {$pronoun_sub, "do"}))
return $string_utils:from_list($list_utils:map_arg(this, "unparse_segment", text[3..length(text)]));
elseif (((typeof(text) == LIST) && (length(text) >= 2)) && (text[1..2] == {$pronoun_sub, "lines"}))
return $list_utils:map_arg(this, "unparse", text[3..length(text)]);
elseif (!text)
return "";
else
return this:unparse($pronoun_sub.lambdacore:parse(text));
endif
.
#73:4
"Usage:  unparse_segment(segment)";
"If a string, escape % and return";
"else do something else";
if (typeof(segment = args[1]) == STR)
return strsub(segment, "%", "%%");
else
"must be list";
if ($object_utils:has_callable_verb(this, "unparse_" + (type = segment[1])))
return this:("unparse_" + type)(@listdelete(segment, 1));
else
return this:unparse_verbose(@segment);
endif
endif
.
#73:5
ret = "%{";
for element in (args)
if (typeof(element) == LIST)
element = this:(verb)(@element)[2..$];
elseif ((((((typeof(element) != STR) || (!element)) || index(element, " ")) || index("\"{", element[1])) || (element in $code_utils.error_names)) || match(element, "^#?-?[0-9]+%(%.[0-9]+%)?%([Ee][+-]?[0-9]+%)?$"))
element = $string_utils:print(element);
else
element = $string_utils:substitute(element, {{"%", "%%"}, {"}", "%}"}});
endif
ret = tostr(ret, element, " ");
endfor
return ret[1..length(ret) - 1] + "}";
"Metadata 202106";
"Last-Modify: {864674727, \"James\", #2142, \"JHM\"}";
.
#73:6
"parse_string(string) => {segments, remainder}";
"segments is a list of segments, and remainder is the remaining unparsed string.";
if (typeof(string = args[1]) != STR)
return E_TYPE;
endif
k = index(string, "%");
if (k == 0)
return {string, {}, ""};
endif
return {string[1..k - 1], @this:parse_segment(string[k + 1..length(string)])};
.
#73:7
return {@pass(@args), $pronoun_sub, $list_utils, $string_utils, $pronoun_sub.lambdacore, $object_utils};
.
#73:8
"parse_string(string) => {segments, remainder}";
"segments is a list of segments, and remainder is the remaining unparsed string.";
if (typeof(string = args[1]) != STR)
return E_TYPE;
endif
j = index(string, ")") || $maxint;
k = index(string, "%");
if ((k == 0) || (j < k))
return (j == $maxint) ? {string, {}, ""} | {string[1..j - 1], {}, string[j + 1..length(string)]};
endif
return {string[1..k - 1], @this:parse_segment(string[k + 1..length(string)])};
"Copied from Generic Pronoun_sub Translator (#131):parse_string by James (#2142) Thu Nov 17 15:41:57 1994 EST";
.
#73:9
if (typeof(old = args[1]) != STR)
return E_TYPE;
endif
all = {};
new = {};
current = "";
segment = 1;
while (segment)
segment = this:parse_delimited_string(old);
current = current + segment[1];
old = segment[3];
segment = segment[2];
if (segment)
if (current)
new = {@new, current};
current = "";
endif
if (segment == 1)
all = {@all, new};
new = {};
else
new = {@new, segment};
endif
endif
endwhile
if (current)
new = {@new, current};
endif
return all ? {{{"pick", {@all, new}}}, old} | {new, old};
"Metadata 202106";
"Last-Modify: {866907197, \"splat\", #3024, \"JHM\"}";
.
#73:10
"unparse list-based pronoun_sub into generic (verbose) form";
if (typeof(text = args[1]) == LIST)
return $string_utils:from_list($list_utils:map_arg(this, "unparse_delimited_segment", text));
else
return "%?";
endif
.
#73:11
"Usage:  unparse_segment(segment)";
"If a string, escape % and ) and return";
"else do something else";
if (typeof(segment = args[1]) == STR)
return strsub(strsub(segment, "%", "%%"), ")", "%)");
else
"must be list";
if ($object_utils:has_callable_verb(this, "unparse_" + (type = segment[1])))
return this:("unparse_" + type)(@listdelete(segment, 1));
else
return this:unparse_verbose(@segment);
endif
endif
.
#74:0
"integrate(list, verb_or_property)";
set_task_perms(caller_perms());
what = args[1];
voo = args[2];
result = this:parse(what, voo);
ret = {};
for i in (result)
par = "";
for j in (listdelete(i, length(i)))
pn = length(j);
c = j[length(j)];
sep = E_NONE;
while (typeof(sep) == ERR)
if (index(" -", c))
sep = "";
elseif (index(".?!:", c))
sep = "  ";
elseif (index("\"'`", c))
pn = pn - 1;
if (!pn)
sep = "  ";
endif
else
sep = " ";
endif
endwhile
par = tostr(par, j, sep);
endfor
par = tostr(par, i[length(i)]);
if (match(par, "[^ ]"))
ret = {@ret, par};
endif
endfor
return ret;
.
#74:1
"parse(list lists of objects and/or strings, verb_or_property)";
set_task_perms(caller_perms());
what = args[1];
voo = args[2];
result = {};
for l in (what)
result = {@result, {}};
for os in (l)
if (msg = this:_parse(os, voo))
if (msg)
result[index = length(result)] = {@result[index], msg};
endif
endif
endfor
if (!result[index = length(result)])
result = listdelete(result, index);
endif
endfor
return result;
.
#74:2
set_task_perms(caller_perms());
return $code_utils:verb_or_property(@args);
.
#74:3
"Usage: visible_of(LIST objects)";
"Returns the visible objects of the list, based on object:visible.";
set_task_perms(caller_perms());
what = args[1];
r = {};
for i in (what)
i:visible() && (r = {@r, i});
endfor
return r;
.
#74:4
set_task_perms(caller_perms());
what = verb[4..length(verb)];
return this:ok(args[1], ("integrate_" + what) + "_msg");
.
#74:5
set_task_perms($no_one);
os = args[1];
voo = args[2];
if (typeof(os) == STR)
return os;
elseif ((typeof(os) == OBJ) && (typeof(msg = $code_utils:verb_or_property(os, voo)) == STR))
return msg;
elseif ((((typeof(os) == LIST) && (typeof(os[1]) == OBJ)) && (typeof(os[2]) == STR)) && (typeof(msg = $code_utils:verb_or_property(@os)) == STR))
return msg;
else
return 0;
endif
.
#74:6
"integrate(list of lists of OBJ and STRs, integrate_verb_name)";
" e.g. {{STR, OBJ, OBJ}, {STR, OBJ}} =>";
"      {STR, STR}";
"      with integration done.";
set_task_perms(caller_perms());
{to_integrate_maybe, verb_name, ?sep = this.default_separator, ?who = player, ?thing = caller} = args;
result = {};
integrated = {};
not_integrated = {};
invalid_message = {};
pronoun_sub = $pronoun_sub;
empty = "^ *$";
for paragraph in (to_integrate_maybe)
para = "";
ssep = "";
try
for chunk in (paragraph)
type = typeof(chunk);
ok = 1;
if (type == STR)
message = chunk;
elseif (type == OBJ)
if (chunk in integrated)
ok = 0;
elseif (!thing:ok_to_integrate(chunk))
ok = 0;
not_integrated = setadd(not_integrated, chunk);
elseif (is_empty = match(message = chunk:(verb_name)(), empty))
ok = 0;
not_integrated = setadd(not_integrated, chunk);
else
integrated = {@integrated, chunk};
endif
elseif (((type == LIST) && chunk) && (chunk[1] == pronoun_sub))
message = $string_utils:pronoun_sub(chunk, who, thing);
else
"this shouldn't happen, but don't barf too badly if it does";
message = tostr(chunk);
endif
"we only care about the empty case if it's an object";
"otherwise assume the user wanted empty space";
if (ok)
para = (para + ssep) + message;
ssep = sep;
endif
endfor
result = {@result, para};
except v (ANY)
invalid_message = setadd(invalid_message, {paragraph, v});
endtry
endfor
return {result, not_integrated, invalid_message};
"Metadata 202106";
"Last-Modify: {865606351, \"Ken\", #75, \"JHM\"}";
.
#75:0
":_make(@value) => new node with value";
if (!(caller in {this._mgr, this}))
raise(E_PERM);
endif
prop = " " + this:_genname();
add_property(this, prop, args, {$code_utils:verb_location().owner, ""});
return prop;
.
#75:1
":_kill(node) destroys the given node.";
if (!(caller in {this, this._mgr}))
raise(E_PERM);
endif
delete_property(this, args[1]);
.
#75:2
return (caller == this._mgr) ? this.(args[1]) | raise(E_PERM);
.
#75:3
{prop, @value} = args;
return (caller == this._mgr) ? this.(prop) = value | raise(E_PERM);
.
#75:4
gp = this._genname;
ngp = "";
{i, l, @_} = match(gp, "[^z]%|$");
aaa = this._list_of_a_s;
if (i <= l)
alpha = $string_utils.alphabet;
return this._genname = tostr(aaa[1..i - 1], alpha[1 + index(alpha, gp[i])], gp[i + 1..$]);
else
if (length(aaa) < i)
aaa = this._list_of_a_s = aaa = aaa + "aaaaaaaa";
endif
return this._genname = aaa[1..i];
endif
.
#75:5
":_ord(element) => ordinal (key) value for element";
"This verb is a dummy. You have to decide what your leaves are going to look like and then write this verb accordingly.  It should, given a leaf/list-element, return the corresponding key value.";
raise(E_NONE, "Verb not yet implemented.");
"";
"For a sorted list where the keys are the elements of the list:";
return args[1];
"";
"For an association list, where all of the leaves are of the form {key,datum}:";
return args[1][1];
.
#75:6
if (!caller_perms().wizard)
raise(E_PERM);
endif
this.mowner = $hacker;
this._mgr = $biglist;
.
#76:0
value = this:get(@args);
if (!value)
value = this.default_message;
endif
r = {"", {value, "looks like: " + this:sub_message(value, player, "connected", player)}};
return r;
.
#76:1
raw = args[2];
if (typeof(raw) != STR)
if (raw == 0)
args[2] = "";
else
return "Please supply a message.";
endif
endif
if (!(string = args[2]))
result = this.default_message;
else
result = string;
endif
args[2] = result;
return args[1..2];
.
#76:2
{message, who, what, ?forwho = $nobody} = args;
" standard_msg = $string_utils:substitute(standard, subset = {{\"%h\", has}, {\"%t\", ctime()[12..16]}, {\"%d\", ctime()[5..10]}, {\"%w\", ctime()[1..3]}, {\"%d\", who:dname()}, {\"%i\", who:iname()}, {\"%n\", who:name()}, {\"%D\", who:dnamec()}, {\"%I\", who:inamec()}, {\"%N\", who:namec()}, {\"%m\", message}, {\"%#\", tostr(who)}, {\"%c\", tostr(length($code_utils:connected_players()))}}, 1);";
message = $time_utils:time_sub(message);
message = strsub(message, "%m", what, 1);
message = strsub(message, "%M", $string_utils:uppercase(what), 1);
message = strsub(message, "%c", tostr(length($code_utils:connected_players())));
message = $string_utils:pronoun_sub($pronoun_sub.("two-letter"):parse(message), who, $login_watcher, (who.location == $limbo) ? $player_start | who.location, dobj, iobj, forwho);
return message;
"Metadata 202106";
"Last-Modify: {834681627, \"Erik\", #74, \"JHM\"}";
.
#76:3
{value} = args;
return {value};
.
#76:4
value = this:get(@args);
l = {};
if (!value)
return {0, {"Always show messages."}};
else
return {"", {("Stop showing messages after " + $string_utils:from_seconds(value)) + " of idleness."}};
endif
.
#76:5
oname = args[1];
raw = args[2];
if (typeof(raw) == LIST)
raw = raw[1];
endif
if (typeof(raw) == NUM)
if (raw == 1)
return "Please supply a positive number of minutes.";
endif
value = raw;
elseif ((value = $code_utils:tonum(raw)) == E_TYPE)
return tostr("`", raw, "'?  Number expected.");
endif
if (value < 0)
return "Please supply a positive number of minutes.";
endif
return {oname, value * 60};
.
#77:0
"parse_segment(string) => {segment, remainder}";
"%s,%o,%p,%q,%r    => <who>'s pronouns.  <who> defaults to player.";
"%n,%d,%i,%t,%l,%% => <who>, dobj, iobj, <thing>, location and %";
"<thing> defaults to caller; <location> defaults to who.location";
"%S,%O,%P,%Q,%R, %N,%D,%I,%T,%L have corresponding capitalized substitutions.";
" %[#n], %[#d], ...  =>  <who>, dobj, etc.'s object number";
"%(foo) => <who>.foo and %(Foo) => <who>.foo capitalized. %[dfoo] => dobj.foo, etc..";
"%<foo> -> whatever <who> does when normal people foo. This is determined by calling :verb_sub() on the <who>.";
"%<d:foo> -> whatever <dobj> does when normal people foo.";
"%% and %{verbose form} are handled by the generic pronoun_sub translator.";
text = args[1];
s = text[1];
objspec = "nditl";
objects = {"player", "dobj", "iobj", "thing", "location"};
prnspec = "sopqrSOPQR";
prprops = {"ps", "po", "pp", "pq", "pr", "Psc", "Poc", "Ppc", "Pqc", "Prc"};
textlen = length(text);
if ((s == "<") && (gt = index(text[1..textlen], ">")))
"handling %<verb> ";
vb = text[2..gt - 1];
if ((length(vb) > 2) && (vb[2] == ":"))
" %<d:verb>";
vbs = objects[index(objspec, vb[1]) || 1];
vb = vb[3..length(vb)];
else
vbs = "player";
endif
return {{"verb", vbs, vb}, text[gt + 1..textlen]};
else
if (brace = index("([", s))
if (!(w = index(text, ")]"[brace])))
return {"%" + text, ""};
else
p = text[2..w - 1];
if (brace == 1)
"%(property)";
segment = {"property", p, index("ABCDEFGHIJKLMNNOPQRSTUVWXYZ", p[1])};
elseif (p[1] == "#")
"%[#n] => object number";
segment = {"objnum", objects[index(objspec, p[2])]};
elseif (!(o = index(objspec, p[1])))
segment = ("[" + p) + "]";
elseif (match(p = p[2..length(p)], "p[opqrs]c?"))
" %[dp.c?]";
segment = {"pronoun", objects[o], p};
else
" %[dproperty] ";
segment = {"property", objects[o], p[1..w - 3], strcmp(p[1], "a") < 0};
endif
return {segment, text[w + 1..textlen]};
endif
else
if (o = index(objspec, s))
segment = {"name", objects[o], strcmp(s, "a") < 0};
elseif (w = index(prnspec, s, 1))
segment = {"pronoun", "player", prprops[w]};
elseif (s == "#")
segment = {"objnum", "player"};
else
return pass(text);
endif
return {segment, text[2..textlen]};
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835903501, \"Erik\", #74, \"JHM\"}";
.
#77:1
"unparse_name(objspec, capitalize)";
if (typeof(objspec = args[1]) == STR)
return "%" + (args[2] ? "NTLDI" | "ntldi")[objspec in {"player", "thing", "location", "dobj", "iobj"}];
else
return "%?";
endif
.
#77:2
"unparse_verb(actor, normal-form verb)";
"actor is an objspec.";
"normal-form verb is like \"eats\" or \"teleports\".";
if (typeof(actor = args[1]) == STR)
if (actor == "player")
return tostr("%<", args[2], ">");
else
return tostr("%<", "tldi"[actor in {"thing", "location", "dobj", "iobj"}], ":", args[2], ">");
endif
else
return "%?";
endif
.
#77:3
"unparse_pronoun(objspec, pronoun)";
if (typeof(objspec = args[1]) == STR)
pronoun = args[2];
if (objspec == "player")
return "%" + $string_utils:(index(pronoun, "c") ? "capitalize" | "lowercase")(pronoun[1]);
elseif (letter = "tldi"[objspec in {"thing", "location", "dobj", "iobj"}])
return tostr("%[", letter, pronoun, "]");
else
return "%?";
endif
else
return "%?";
endif
.
#77:4
"unparse_property(objspec, propname, capitalize)";
if (typeof(objspec = args[1]) == STR)
propname = args[2];
capitalize = args[3];
if (objspec == "player")
return tostr("%(", $string_utils:(capitalize ? "capitalize" | "downcase")(propname), ")");
elseif (letter = "tldi"[objspec in {"thing", "location", "dobj", "iobj"}])
return tostr("%[", letter, $string_utils:(capitalize ? "capitalize" | "lowercase")(propname), "]");
endif
endif
return "%?";
.
#77:5
"unparse_objnum(objspec)";
if (typeof(objspec = args[1]) == STR)
if (letter = "ntldi"[objspec in {"player", "thing", "location", "dobj", "iobj"}])
return tostr("%[#", letter, "]");
endif
endif
return "%?";
.
#77:6
"unparse_object(objspec)";
"only handles who, thing, location, dobj, iobj";
return "ntldi"[args[1] in {"player", "thing", "location", "dobj", "iobj"}] || E_INVARG;
.
#77:7
return (args[2] ? "A " | "a ") + this:unparse_name(args[1], 0);
.
#77:8
return (args[2] ? "The " | "the ") + this:unparse_name(args[1], 0);
.
#77:9
"parse_string(string);";
"Return {<segments>, <rest>}, where <segments> is a list of segments, and <rest> is the remaining unparsed string.";
"Convert 'the %n' into :dname appropriately.";
string = args[1];
k = index(string, "%");
if (k == 0)
return {string, {}, ""};
elseif (k > 1)
prefix = string[1..k - 1];
string = string[k + 1..length(string)];
segment = this:parse_segment(string);
if ((typeof(segment[1]) == LIST) && (segment[1][1] == "name"))
if (rmatch(prefix, "%<a $", 1))
prefix = prefix[1..k - 3];
segment[1][1] = "iname";
segment[1][3] = 0;
elseif (rmatch(prefix, "%<A $", 1))
prefix = prefix[1..k - 3];
segment[1][1] = "iname";
segment[1][3] = 1;
elseif (rmatch(prefix, "%<the $", 1))
prefix = prefix[1..k - 5];
segment[1][1] = "dname";
segment[1][3] = 0;
elseif (rmatch(prefix, "%<The $", 1))
prefix = prefix[1..k - 5];
segment[1][1] = "dname";
segment[1][3] = 1;
endif
endif
return {prefix, @segment};
else
string[1..1] = "";
return {"", @this:parse_segment(string)};
endif
.
#77:10
return {@pass(@args), $string_utils};
.
#78:0
"parse_segment(string) => {segment, remainder}";
"%% - becomes %";
"%{information} - some kind of verbose representation";
"- everything else here is just an abbreviation for this form";
"";
"%$ indicates a variable to be inserted, as specified by the next letter:";
"a - argstr";
"d - dobjstr";
"i - iobjstr";
"p - prepstr";
"These can be capitalized, indicating that the inserted string should be.";
"";
"\"object specifiers\" indicate an object to find information about:";
"%n, %d, %i, %t -- <who>, dobj, iobj, <thing>";
"<who> defaults to player";
"<thing> defaults to caller";
"%<number> -- available for class specialization";
"%l -- <location>, defaults to player.location, exists for backwards";
"compatibility";
"%x<objspec>{<something>} -- calls back to <objspec> for some special object";
"";
"An object specifier must be followed by at least one other character,";
"an \"information specifier\":";
"l - the object's location, which in turn is considered an object specifier";
"and must be followed by another information specifier";
"L - the object's outermost location";
"c - the object's contents";
"# - the object's number";
"n - the object's name (actually title)";
"o - objective pronoun    (\"them\")";
"p - possessive adjective (\"their\")";
"q - possessive pronoun   (\"theirs\")";
"r - reflexive pronoun    (\"themselves\")";
"s - subjective pronoun   (\"they\")";
"d - definite-article     (\"the refrigerator\")";
"i - definite-article     (\"a refrigerator\")";
"' - possessive (w/[idn]) (\"a refrigerator's\")";
".(foo) - the object's \"foo\" property";
":(foos) - whatever the object does when a normal person foos.";
"This is determined by calling object:verb_sub.";
"'(foo) - noun, to be pluralized if the object is plural";
"This is determined by calling object:noun_sub,";
"or default pluralization if the object is multiple";
"x{foo arg1 arg2 ...} - special message obtained through <object>:sub_<foo>";
"";
"n, o, p, q, r, and s can be capitalized to get capitalized forms of the";
"information specified.  (N calls :titlec.)";
"Likewise, (Foo) gets capitalized form of a property or verb_sub.";
text = args[1];
s = text[1];
prnspec = "sopqrSOPQR";
prnoffs = 5;
prprops = {"ps", "po", "pp", "pq", "pr", "Psc", "Poc", "Ppc", "Pqc", "Prc", "PSU", "POU", "PPU", "PQU", "PRU"};
namspec = "ndiNDI";
namoffs = 3;
namargs = {"", "d", "i", "c", "dc", "ic", "u", "du", "iu"};
extspec = ".:'";
extends = {"property", "verb", "noun"};
strspec = "adipADIP";
strings = {"argstr", "dobjstr", "iobjstr", "prepstr", "argstr", "dobjstr", "iobjstr", "prepstr"};
textlen = length(text);
try
"%;{} is DEAD.";
"if (s == \";\")";
"if ((!(brace = index(\"{[(<`\", text[2]))) || (!(end = index(text[3..textlen], \"}])>'\"[brace]))))";
"return {\"%\" + s, text[2..textlen]};";
"endif";
"return {{\"eval\", text[3..end + 1]}, text[end + 3..textlen]};";
if ((s == "$") && (n = index(strspec, text[2], 1)))
return {{"string", strings[n], n > 4}, text[3..textlen]};
elseif (s == "*")
if (text[2] == "(")
result = this:parse_delimited_message(text[3..textlen]);
return {{"allcaps", result[1]}, result[2]};
elseif (text[2] == "[")
ext = text[3..textlen];
k = {};
while (ext[1] == "(")
result = this:parse_delimited_message(ext[2..length(ext)]);
k = {@k, result[1]};
ext = result[2];
endwhile
return {{"pick", k}, ext[2..length(ext)]};
endif
elseif (index(")|", s))
return {s, text[2..textlen]};
elseif (s == "(")
result = this:parse_delimited_message(text[2..textlen]);
if (!result[1])
return {"", text[2..textlen]};
else
return {result[1][1], this:unparse({$pronoun_sub, "do", @result[1][2..$]}) + result[2]};
endif
endif
except (E_RANGE)
return pass(text);
endtry
try
if (!(object = this:parse_obj(text)))
return pass(text);
endif
{object, cap, rest} = object;
k = 1;
info = rest[1];
if (ext = index(extspec, info))
if ((info == "'") && (n = index(namspec, rest[2], 1)))
k = 2;
segment = {"name", object, {"p" + namargs[(namoffs * cap) + n]}};
else
extension = this:("parse_" + info)(rest[2..$]);
if (extension)
return {{extends[ext], object, @extension[1]}, extension[2]};
else
segment = 0;
endif
endif
elseif (pr = index(prnspec, info, 1))
segment = {"pronoun", object, prprops[(prnoffs * cap) + pr]};
elseif (info == "#")
segment = {"objnum", object};
elseif (n = index(namspec, info, 1))
segment = {"name", object, {namargs[(namoffs * cap) + n]}};
elseif (info == "{")
result = this:parse_verbose(rest);
if (result)
return {{"name", object, result[1]}, result[2]};
else
segment = 0;
endif
elseif ((info == "x") && (rest[2] == "{"))
result = this:parse_verbose(rest[2..$]);
if (result)
return {{"special", object, result[1][1], result[1][2..$]}, result[2]};
else
segment = 0;
endif
endif
except (E_RANGE)
segment = 0;
endtry
if (segment)
return {segment, rest[k + 1..$]};
else
return pass(text);
endif
"Copied from Two-Letter Pronoun_Sub Translator (#191):parse_segment by James (#2142) Sun Oct  2 21:14:25 1994 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866908539, \"splat\", #3024, \"JHM\"}";
.
#78:1
if ((brace = index("{[(<`", (text = args[1])[1])) && (end = index(text, "}])>'"[brace])))
return {{text[2..end - 1], index("ABCDEFGHIJKLMNOPQRSTUVWXYZ", text[2], 1)}, text[end + 1..length(text)]};
endif
.
#78:2
if ((text = args[1])[1] == "(")
text = this:parse_delimited_message(text[2..length(text)]);
if (!((text && (length(text[1]) == 1)) && (typeof(text[1][1]) == STR)))
text[1] = {text[1]};
endif
return text;
elseif ((brace = index("{[(<`", text[1])) && (end = index(text, "}])>'"[brace])))
return {{text[2..end - 1]}, text[end + 1..length(text)]};
endif
.
#78:3
if ((o = this:obj_unparse(args[1])) && (e = this:extension_unparse(args[2])))
return tostr(o, ":", e);
else
return this:unparse_verbose("verb", @args);
endif
.
#78:4
if (os = (objspec = args[1]) in {"player", "thing", "location", "dobj", "iobj"})
return tostr("%", "ntldi"[os]);
elseif (typeof(objspec) in {NUM, OBJ})
return tostr("%", objspec);
elseif (typeof(objspec) == LIST)
if (objspec[1] == "location")
return tostr(this:(verb)(objspec[2]), "l");
elseif (objspec[1] == "outer_location")
return tostr(this:(verb)(objspec[2]), "L");
elseif (objspec[1] == "contents")
return tostr(this:(verb)(objspec[2]), "c");
elseif (objspec[1] == "special")
return tostr("%x", this:(verb)(objspec[2])[2..$], this:unparse_verbose(objspec[3], @objspec[4])[2..$]);
elseif (objspec[1] == "listargs")
return tostr("%a", this:(verb)(objspec[2])[2..$], this:unparse_verbose(@objspec[3])[2..$]);
endif
endif
.
#78:5
extension = args[1];
if (typeof(extension) == LIST)
return ("(" + this:unparse_delimited_message(extension)) + ")";
endif
ends = ")}]>'";
for end in [1..5]
if (!index(extension, ends[end]))
return ("({[<`"[end] + extension) + ends[end];
endif
endfor
.
#78:6
if (o = this:obj_unparse(args[1]))
if (typeof(args[2]) == LIST)
result = $name_utils:collapse_to_string(args[2]);
if (pos = result in {"", "p", "d", "pd", "i", "pi", "c", "pc", "dc", "pdc", "ic", "pic", "u", "pu", "du", "pdu", "iu", "piu"})
if (index("0123456789", o[2]) && (pos > 12))
result = this:unparse_verbose(@args[2]);
result[1..1] = o;
return result;
elseif (pos > 6)
o[2] = $string_utils:capitalize(o[2]);
endif
return tostr(o, {"n", "'n", "d", "'d", "i", "'i", @index("0123456789", o[2]) ? {"N", "'N", "D", "'D", "I", "'I"} | {"n", "'n", "d", "'d", "i", "'i"}, "N", "'N", "D", "'D", "I", "'I"}[pos]);
else
result = this:unparse_verbose(@args[2]);
result[1..1] = o;
return result;
endif
else
info = verb[9];
if (args[2])
o[2] = $string_utils:capitalize(o[2]);
if ((args[2] == 2) || index("0123456789", o[2]))
info = $string_utils:capitalize(info);
endif
endif
return tostr(o, info);
endif
else
return this:unparse_verbose(verb[9..length(verb)], @args);
endif
"Copied from Two-Letter Pronoun_Sub Translator (#191):unparse_name by James (#2142) Sun Oct  2 21:12:40 1994 EDT";
.
#78:7
if (o = this:obj_unparse(args[1]))
return tostr(o, "#");
else
return this:unparse_verbose(verb[9..length(verb)], @args);
endif
.
#78:8
if ((o = this:obj_unparse(args[1])) && match(prn = args[2], "^p[opqrs][cu]?$"))
if (length(prn) == 2)
return tostr(o, $string_utils:lowercase(prn[2]));
elseif (index("0123456789", o[2]))
return tostr(o, $string_utils:capitalize(prn[2]));
else
o[2] = $string_utils:uppercase(o[2]);
return tostr(o, (prn[3] == "u") ? $string_utils:capitalize(prn[2]) | prn[2]);
endif
endif
return this:unparse_verbose("pronoun", @args);
.
#78:9
if ((o = this:obj_unparse(args[1])) && (e = this:extension_unparse(args[2])))
return tostr(o, "'", e);
else
return this:unparse_verbose("noun", @args);
endif
.
#78:10
if ((o = this:obj_unparse(args[1])) && (e = this:extension_unparse(args[2])))
pname = $string_utils:(args[3] ? "capitalize" | "lowercase")(e[2..length(e)]);
return tostr(o, ".(", pname);
else
return this:unparse_verbose("verb", @args);
endif
.
#78:11
if ((n = args[1] in {"argstr", "dobjstr", "iobjstr", "prepstr"}) && (args[2] in {0, 1}))
return "%$" + "adipADIP"[n + (4 * args[2])];
else
return this:unparse_verbose("string", @args);
endif
.
#78:12
if (o = this:obj_unparse(args[1]))
if (typeof(args[2]) == LIST)
result = $name_utils:collapse_to_string(args[2]);
if (match(result, "^p?[di]?[uc]?$"))
if (match(result, "[uc]"))
o[2] = $string_utils:capitalize(o[2]);
endif
if (index(result, "u"))
uppercase = 2;
else
uppercase = 1;
endif
if (index(result, "p"))
o = o + "'";
endif
if (s = index(result, "i"))
o = o + "iI"[uppercase];
elseif (s = index(result, "d"))
o = o + "dD"[uppercase];
else
o = o + "nN"[uppercase];
endif
return o;
return tostr(o, {"n", "'n", "d", "'d", "i", "'i", "n", "'n", "d", "'d", "i", "'i", "N", "'N", "D", "'D", "I", "'I"}[pos]);
else
result = this:unparse_verbose(@args[2]);
result[1..1] = o;
return result;
endif
endif
info = verb[9];
if (args[2])
info = $string_utils:capitalize(info);
endif
return tostr(o, info);
else
return this:unparse_verbose(verb[9..length(verb)], @args);
endif
"Copied from Two-Letter Pronoun_Sub Translator (#191):unparse_name by James (#2142) Sun Oct  2 21:12:40 1994 EDT";
"Copied from Two-Letter Pronoun_Sub Translator (#191):unparse_name by Erik (#74) Wed Oct 26 20:24:01 1994 EDT";
.
#78:13
if (o = this:obj_unparse(args[1]))
if (typeof(args[2]) == LIST)
result = $name_utils:collapse_to_string(args[2]);
if (subs = match(result, "^p?%([di]?%)%([uc]?%)$"))
if (cap = substitute("%2", subs))
o[2] = $string_utils:capitalize(o[2]);
endif
if (index(result, "p"))
o = o + "'";
endif
spec = substitute("%1", subs) || "n";
return o + ((cap == "u") ? "NDI"[index("ndi", spec)] | spec);
return tostr(o, {"n", "'n", "d", "'d", "i", "'i", "n", "'n", "d", "'d", "i", "'i", "N", "'N", "D", "'D", "I", "'I"}[pos]);
else
result = this:unparse_verbose(@args[2]);
result[1..1] = o;
return result;
endif
endif
info = verb[9];
if (args[2])
info = $string_utils:capitalize(info);
endif
return tostr(o, info);
else
return this:unparse_verbose(verb[9..length(verb)], @args);
endif
"Copied from Two-Letter Pronoun_Sub Translator (#191):unparse_name by James (#2142) Sun Oct  2 21:12:40 1994 EDT";
"Copied from Two-Letter Pronoun_Sub Translator (#191):unparse_name by Erik (#74) Wed Oct 26 20:24:01 1994 EDT";
.
#78:14
return ("%*(" + this:unparse_delimited_message(args[1])) + ")";
.
#78:15
return ("%(" + $string_utils:from_list($list_utils:map_arg(this, "unparse_delimited_message", args[1]), "|")) + ")";
"don't need this ugly stuff below anymore.";
return ("%*[(" + $string_utils:from_list($list_utils:map_arg(this, "unparse_delimited_message", args[1]), ")(")) + ")]";
"Metadata 202106";
"Last-Modify: {866910201, \"splat\", #3024, \"JHM\"}";
.
#78:16
if ((o = this:obj_unparse(args[1])) && (e = this:unparse_verbose(args[2], @args[3])))
return tostr(o, "x", e[2..$]);
else
return this:unparse_verbose("special", @args);
endif
.
#78:17
"Usage: parse_obj(text)";
"=> {objspec, cap, rest}";
text = args[1];
objspec = "NDITL";
objects = {"player", "dobj", "iobj", "thing", "location"};
try
s = text[1];
if (o = index(objspec, s))
object = objects[o];
k = 2;
cap = !(!index(objspec, s, 1));
elseif (o = index("ax", s))
try
{object, cap, rest} = this:(verb)(text[2..$]);
{vargs, text} = this:parse_verbose(rest);
if (s == "x")
{sspec, @vargs} = vargs;
object = {"special", object, sspec, vargs};
cap = cap || strcmp("x", s);
else
object = {"listargs", object, vargs};
cap = cap || strcmp("a", s);
endif
k = 1;
except (E_ARGS, E_TYPE)
return 0;
endtry
else
k = 1;
while (`tonum(text[1..k]) ! E_RANGE')
k = k + 1;
endwhile
object = tonum(text[1..k - 1]);
cap = 0;
endif
if (!object)
return 0;
endif
textlen = length(text);
while ((k <= textlen) && (j = index("lLc", text[k], 1)))
object = {{"location", "outer_location", "contents"}[j], object};
k = k + 1;
endwhile
return {object, cap, text[k..$]};
except (E_RANGE)
endtry
return 0;
"Metadata 202106";
"Last-Modify: {864674903, \"James\", #2142, \"JHM\"}";
.
#78:18
"parse_string(string) => {segments, remainder}";
"segments is a list of segments, and remainder is the remaining unparsed string.";
if (typeof(string = args[1]) != STR)
return E_TYPE;
endif
l = index(string, "|");
k = index(string, "%");
if (k == 0)
return l ? {string[1..l - 1], 1, string[l + 1..$]} | {string, {}, ""};
elseif (l && (l < k))
return {string[1..l - 1], 1, string[l + 1..$]};
endif
return {string[1..k - 1], @this:parse_segment(string[k + 1..length(string)])};
"Copied from Generic Pronoun_sub Translator (#131):parse_string by splat (#3024) Sat Jun 21 11:16:51 1997 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866906369, \"splat\", #3024, \"JHM\"}";
.
#78:19
"parse_string(string) => {segments, remainder}";
"segments is a list of segments, and remainder is the remaining unparsed string.";
if (typeof(string = args[1]) != STR)
return E_TYPE;
endif
j = index(string, ")") || $maxint;
k = index(string, "%");
l = index(string, "|");
if ((k == 0) || (j < k))
if (l && (l < j))
return {string[1..l - 1], 1, string[l + 1..$]};
else
return (j == $maxint) ? {string, {}, ""} | {string[1..j - 1], {}, string[j + 1..length(string)]};
endif
elseif (l && (l < k))
return {string[1..l - 1], 1, string[l + 1..$]};
endif
return {string[1..k - 1], @this:parse_segment(string[k + 1..length(string)])};
"Copied from Generic Pronoun_sub Translator (#131):parse_string by James (#2142) Thu Nov 17 15:41:57 1994 EST";
"Copied from Generic Pronoun_sub Translator (#131):parse_delimited_string by splat (#3024) Sat Jun 21 11:16:59 1997 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866906595, \"splat\", #3024, \"JHM\"}";
.
#78:20
"unparse list-based pronoun_sub into generic (verbose) form";
if (typeof(text = args[1]) == LIST)
if ((((length(text) == 1) && (typeof(text[1]) == LIST)) && text[1]) && (text[1][1] == "pick"))
return $string_utils:from_list($list_utils:map_arg(this, "unparse_delimited_message", text[1][2]), "|");
endif
return $string_utils:from_list($list_utils:map_arg(this, "unparse_delimited_segment", text));
else
return "%?";
endif
"Copied from Generic Pronoun_sub Translator (#131):unparse_delimited_message by splat (#3024) Sat Jun 21 12:00:46 1997 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866909454, \"splat\", #3024, \"JHM\"}";
.
#78:21
"unparse list-based pronoun_sub into two-letter form";
result = pass(@args);
if (typeof(result) == LIST)
return $string_utils:from_list(result, "|");
else
return result;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866909026, \"splat\", #3024, \"JHM\"}";
.
#78:22
"Usage:  unparse_segment(segment)";
"If a string, escape % and ) and return";
"else do something else";
if (typeof(segment = args[1]) == STR)
return strsub(strsub(strsub(segment, "%", "%%"), ")", "%)"), "|", "%|");
else
return pass(@args);
endif
"Copied from Generic Pronoun_sub Translator (#131):unparse_delimited_segment by splat (#3024) Sat Jun 21 12:01:00 1997 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866909749, \"splat\", #3024, \"JHM\"}";
.
#78:23
"Usage:  unparse_segment(segment)";
"If a string, escape % and return";
"else do something else";
if (typeof(segment = args[1]) == STR)
return strsub(strsub(segment, "%", "%%"), "|", "%|");
else
return pass(@args);
endif
"Copied from Generic Pronoun_sub Translator (#131):unparse_segment by splat (#3024) Sat Jun 21 12:13:49 1997 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {866909815, \"splat\", #3024, \"JHM\"}";
.
#79:0
if (!$__core_init_phase)
raise(E_PERM);
endif
this.node_info = {this.owner, ""};
.
#79:1
{prefix, @value} = args;
if (caller != this)
raise(E_PERM);
endif
this.last_modify = {time(), task_id()};
add_property(this, " " + prefix, value, this.node_info);
.
#79:2
{prefix} = args;
if (caller != this)
raise(E_PERM);
endif
this.last_modify = {time(), task_id()};
return `delete_property(this, " " + prefix) ! E_PROPNF => 0';
.
#79:3
{prefix} = args;
if (caller != this)
raise(E_PERM);
endif
return this.(" " + prefix);
.
#79:4
{prefix, @value} = args;
if (caller != this)
raise(E_PERM);
endif
this.last_modify = {time(), task_id()};
return this.(" " + prefix) = value;
.
#79:5
{d1, d2} = args;
return d1 == d2;
"for case-sensitive data do:";
"return equal(d1, d2);";
.
#79:6
{set, _, d} = args;
return setadd(set, d);
"for case-sensitive data do:";
"return is_member(d, set) ? set | {@set, d};";
.
#79:7
{set, k, _} = args;
return {@set, k};
.
#79:8
{set, k, d} = args;
return {@set, {k, d}};
.
#79:9
return 0;
.
#79:10
{_, _, perms, @_} = callers()[2];
return (perms != this.owner) && (!perms.wizard);
.
#79:11
"find(string[,n]) => datum corresponding to string with the search starting at node for string[1..n], n defaults to 0 (root node), this.ambiguous or this.failed";
"find_key(string[,n]) is like :find but returns the full string key rather than the associated datum.  Note that if several string keys present in the db share a common prefix, :find_key(prefix) will return this.ambiguous, but if there is a unique datum associated with all of these strings :find(prefix) will return it rather than this.ambiguous.";
"Assumes n<=length(string)";
{search, ?sofar = 0} = args;
if ((caller != this) && this:prohibit_lookup())
raise(E_PERM);
endif
key_is_data = (verb == "find_key") || this.no_data;
case = this.key_case;
while (1)
prefix = search[1..sofar];
rest = search[sofar + 1..$];
{common_string, branches, exacts, ?data} = this:get_node(prefix);
if (key_is_data)
data = exacts;
endif
if (i = case ? is_member(search, exacts) | (search in exacts))
"...exact match for one of the strings in this node...";
return data[i];
elseif (index(common_string, rest, case) == 1)
"...ambiguous iff there's more than one object represented in this node..";
return this:(key_is_data ? "_only_key" | "_only")(prefix);
elseif (index(rest, common_string, case) != 1)
"...search string doesn't agree with common portion...";
return this.failed;
elseif (index(branches, search[sofar = (sofar + length(common_string)) + 1], case))
"...search string follows one of continuations leading to other nodes...";
"continue;";
else
"...search string may partially match one of the strings in this node...";
for i in [1..length(exacts)]
if (index(exacts[i], search, case) == 1)
return data[i];
endif
endfor
return this.failed;
endif
endwhile
.
#79:12
{search, ?sofar = 0} = args;
if ((caller != this) && this:prohibit_lookup())
raise(E_PERM);
endif
case = this.key_case;
while (1)
prefix = search[1..sofar];
{common, branches, exacts, ?data} = this:get_node(prefix);
if (i = case ? is_member(search, exacts) | (search in exacts))
return this.no_data ? exacts[i] | data[i];
elseif ((index(rest = search[sofar + 1..$], common, case) != 1) || (case ? equal(common, rest) | (common == rest)))
return this.failed;
elseif (index(branches, search[sofar = (sofar + length(common)) + 1], case))
continue;
else
return this.failed;
endif
endwhile
.
#79:13
":find_all(string[, asuffix])";
{search, ?asuffix = 0} = args;
if ((caller != this) && this:prohibit_lookup())
raise(E_PERM);
endif
sofar = 0;
no_data = this.no_data;
case = this.key_case;
accum_verb = "accum_" + (asuffix || (no_data ? "keys" | (verb[10..$] || "data")));
while (1)
prefix = search[1..sofar];
rest = search[sofar + 1..$];
{common, branches, exacts, ?data} = this:get_node(prefix);
if (index(common, rest, case) == 1)
"...return entire subtree.";
return this:_every(prefix, accum_verb, {});
elseif (index(rest, common, case) != 1)
"...common portion doesn't agree.";
return {};
elseif (!index(branches, search[sofar = (sofar + length(common)) + 1], case))
"...matching string is in exacts.  length(search) >= sofar,";
"...so there will be at most one matching string.";
for i in [1..length(exacts)]
if (index(exacts[i], search, case) == 1)
return this:(accum_verb)({}, exacts[i], @no_data ? {} | data[i..i]);
endif
endfor
return {};
endif
"...matching strings are in a subnode.";
"continue;";
endwhile
.
#79:14
":_only(prefix) => if all strings in this node have the same datum, return it, otherwise, return this.ambiguous.";
{prefix} = args;
if (caller != this)
raise(E_PERM);
endif
{common, continuations, exacts, ?data} = this:get_node(prefix);
if (continuations)
what = this:_only(tostr(prefix, common, continuations[1]));
if (what == this.ambiguous)
return what;
endif
elseif (data)
what = data[1];
data = listdelete(data, 1);
else
"..this can only happen with the root node of an empty db.";
return this.failed;
endif
for x in (data)
if (!this:data_equal(what, x))
return this.ambiguous;
endif
endfor
for i in [2..length(continuations)]
if (!this:data_equal(what, this:_only(tostr(prefix, common, continuations[i]))))
return this.ambiguous;
endif
endfor
return what;
.
#79:15
":_only_key(prefix) => if all strings in this node have the same key, return it, otherwise, return this.ambiguous.";
{prefix} = args;
if (caller != this)
raise(E_PERM);
endif
{common, branches, exacts, ?_} = this:get_node(prefix);
"... life is much simpler if we only care about keys.";
"... if there's more than one string here, we barf.";
if (branches || (length(exacts) > 1))
return this.ambiguous;
elseif (exacts)
return exacts[1];
else
"... this can only happen with the root node of an empty db.";
return this.failed;
endif
.
#79:16
{string, accum_verb, results} = args;
if (caller != this)
raise(E_PERM);
endif
{common, branches, exacts, @data} = this:get_node(string);
for i in [1..length(exacts)]
results = this:(accum_verb)(results, exacts[i], (data ? data[1] | exacts)[i]);
endfor
prefix = string + common;
for i in [1..length(branches)]
results = this:_every(prefix + branches[i], accum_verb, results);
endfor
return results;
.
#79:17
":insert(string,datum) -- inserts <string,datum> correspondence into db.";
"Returns {old_datum} (or 1) if there was a <string,old_datum> correspondence there before, otherwise returns 0";
{search, ?datum = 0} = args;
if ((caller != this) && this:prohibit_modify())
raise(E_PERM);
endif
sofar = 0;
no_data = this.no_data;
case = this.key_case;
while (1)
"length(search) >= sofar";
prefix = search[1..sofar];
{common, branches, exacts, ?data} = this:get_node(prefix);
if (i = case ? is_member(search, exacts) | (search in exacts))
"... exact match ...";
if (no_data)
this:set_node(prefix, common, branches, listset(exacts, search, i));
return {exacts[i]};
else
this:set_node(prefix, common, branches, listset(exacts, search, i), listset(data, datum, i));
return {data[i]};
endif
endif
rest = search[sofar + 1..$];
if (index(rest, common, case) != 1)
"... find where new string disagrees with common portion...";
c = this:_common(rest, common) + 1;
"... make a new node with a shorter common portion....";
this:make_node(prefix + common[1..c], common[c + 1..$], branches, exacts, @no_data ? {} | {data});
this:set_node(prefix, common[1..c - 1], common[c], {search}, @no_data ? {} | {{datum}});
return 0;
elseif (case ? equal(rest, common) | (rest == common))
".. new string == common portion, insert...";
this:set_node(prefix, common, branches, {search, @exacts}, @no_data ? {} | {{datum, @data}});
return 0;
elseif (!index(branches, search[sofar = (sofar + length(common)) + 1], case))
"... new string may blow away one of the exact matches (i.e., matches one of them up to the first character beyond the common portion) in which case we need to create a new subnode....";
nprefix = search[1..sofar];
for m in (exacts)
if (index(m, nprefix, case) == 1)
i = is_member(m, exacts);
"... we already know m != search ...";
"... string m has been blown away.  create new node ...";
ncommon = m[sofar + 1..this:_common(search, m)];
this:make_node(nprefix, ncommon, "", {search, m}, @no_data ? {} | {{datum, data[i]}});
this:set_node(prefix, common, branches + search[sofar], listdelete(exacts, i), @no_data ? {} | {listdelete(data, i)});
return 0;
endif
endfor
"... new string hasn't blown away any of the exact matches, insert it as a new exact match...";
this:set_node(prefix, common, branches, {search, @exacts}, @no_data ? {} | {{datum, @data}});
return 0;
endif
"... new string matches pre-existing continuation. insert in subnode....";
"continue;";
endwhile
.
#79:18
":delete(string[,n]) deletes any <string,something> pair from the tree starting at node for string[1..n], n defaulting to 0 (root node)";
"Returns {something} if such a pair existed, otherwise returns 0";
"If that node is not the root node and ends up containing only one string and no subnodes, we kill it and return {something,string2,something2} where <string2,something2> is the remaining pair.";
if ((caller != this) && this:prohibit_modify())
raise(E_PERM);
endif
{search, ?sofar = 0} = args;
use_data = !this.no_data;
case = this.key_case;
prefix = search[1..sofar];
rest = search[sofar + 1..$];
{common, branches, exacts, ?data} = this:get_node(prefix);
if (i = case ? is_member(search, exacts) | (search in exacts))
previous = {(use_data ? data | exacts)[i]};
exacts = listdelete(exacts, i);
if (use_data)
data = listdelete(data, i);
endif
elseif ((case ? equal(rest, common) | (rest == common)) || ((index(rest, common, case) != 1) || (!index(branches, search[d = (sofar + length(common)) + 1], case))))
"... hmm string isn't here...";
return 0;
elseif ((previous = this:delete(search, d)) && (length(previous) > 1))
"... a subtree just died, incorporate the sole survivor...";
{@previous, rexact, rdata} = previous;
i = index(branches, search[d], case);
branches[i..i] = "";
exacts = {rexact, @exacts};
if (use_data)
data = {rdata, @data};
endif
else
return previous;
endif
if ((!prefix) || ((length(exacts) + length(branches)) != 1))
"... this node survives...";
this:set_node(prefix, common, branches, exacts, @use_data ? {data} | {});
return previous;
elseif (exacts)
"... one exact match survives, pass it to parent and kill ourselves...";
this:kill_node(prefix);
return {@previous, exacts[1], use_data && data[1]};
else
"... one branch survives, incorporate it...";
{scommon, @srest} = this:get_node(p = tostr(prefix, common, branches));
this:kill_node(p);
this:set_node(prefix, tostr(common, branches, scommon), @srest);
return previous;
endif
.
#79:19
":delete2(string,datum) deletes the pair <string,datum> from the database.  This is similar to :delete except that if the entry for that string has a different associated datum, it will not be removed.  :delete2(string,datum) is equivalent to ";
"  actual=this:find_exact(string);";
"  if (this:data_equal(actual, datum))";
"    return this:delete(string); ";
"  elseif (actual != this.failed)";
"    return {actual};";
"  else";
"    return 0;";
"  endif";
"except that delete2 will be significantly faster on a large db.";
{search, datum} = args;
if ((caller != this) && this:prohibit_modify())
raise(E_PERM);
endif
if (this.no_data)
"... no real difference between delete and delete2 in this case";
if (this.key_case ? equal(datum, search) | (datum == search))
return this:delete(search);
elseif (this:find_exact(search) != this.failed)
return {search};
else
return 0;
endif
else
return this:_delete2(search, datum, 0);
endif
.
#79:20
":_delete2(string,datum,n) deletes the pair <string,datum> from the tree starting at node for string[1..n].";
"Similar to :delete except that if the entry for that string has a different associated datum, it will not be removed.  ";
"Assumes !this.no_data";
{search, datum, sofar} = args;
if (caller != this)
raise(E_PERM);
endif
case = this.key_case;
prefix = search[1..sofar];
rest = search[sofar + 1..$];
{common, branches, exacts, data} = this:get_node(prefix);
if (i = case ? is_member(search, exacts) | (search in exacts))
previous = {data[i]};
if (!this:data_equal(data[i], datum))
return previous;
endif
exacts = listdelete(exacts, i);
data = listdelete(data, i);
elseif ((case ? equal(rest, common) | (rest == common)) || ((index(rest, common, case) != 1) || (!index(branches, search[d = (sofar + length(common)) + 1], case))))
"... hmm string isn't here...";
return 0;
elseif ((previous = this:_delete2(search, datum, d)) && (length(previous) > 1))
"... a subtree just died, incorporate the sole survivor...";
{@previous, rexact, rdata} = previous;
i = index(branches, search[d], case);
branches[i..i] = "";
exacts = {rexact, @exacts};
data = {rdata, @data};
else
return previous;
endif
if ((!prefix) || ((length(exacts) + length(branches)) != 1))
"... this node survives...";
this:set_node(prefix, common, branches, exacts, data);
return previous;
elseif (exacts)
"... one exact match survives, pass it to parent and kill ourselves...";
this:kill_node(prefix);
return {@previous, exacts[1], data[1]};
else
"... one branch survives, incorporate it...";
{scommon, @srest} = this:get_node(p = tostr(prefix, common, branches));
this:kill_node(p);
this:set_node(prefix, tostr(common, branches, scommon), @srest);
return previous;
endif
.
#79:21
":clearall([NODATA_FLAG[, CASE_FLAG]])";
"removes all associations.";
"The NODATA_FLAG argument, if given, changes the .no_data flag for the database.";
"The CASE_FLAG argument, if given, changes the .key_case flag for the database.";
{?type = this.no_data ? 1 | 0, ?case_sensitive = 0} = args;
if ((caller != this) && this:prohibit_modify())
raise(E_PERM);
elseif (!(type in {0, 1, 3, 4}))
raise(E_INVARG, "TYPE argument must be 0, 1, (3 or 4).");
endif
"for backward compatibility";
this.no_data = type % 2;
this.key_case = case_sensitive;
for p in (properties(this))
if ((p[1] == " ") && (p != " "))
delete_property(this, p);
endif
"... there should be a better way....";
"...This is bad as it leaves the db in an inconsistent state...";
if ((ticks_left() < 4000) || (seconds_left() < 2))
suspend(0);
endif
endfor
this:set_node("", "", "", {}, @this.no_data ? {} | {{}});
.
#79:22
if ((caller != this) && this:prohibit_modify())
raise(E_PERM);
endif
this:_kill_subtrees("", 0);
this:clearall(@args);
.
#79:23
":_kill_subtree(node,count)...wipes out all subtrees";
"...returns count + number of nodes removed...";
{prefix, count} = args;
if (caller != this)
raise(E_PERM);
endif
{common, branches, @_} = this:get_node(prefix);
if ((ticks_left() < 500) || (seconds_left() < 2))
player:tell("...", count);
suspend(0);
endif
for i in [1..length(branches)]
count = this:_kill_subtrees(n = tostr(prefix, common, branches[i]), count) + 1;
this:kill_node(n);
endfor
return count;
.
#79:24
":_common(first,second) => length of longest common prefix";
{first, second} = args;
r = min(length(first), length(second));
l = 1;
while (r >= l)
h = (r + l) / 2;
if (this.key_case ? equal(first[l..h], second[l..h]) | (first[l..h] == second[l..h]))
l = h + 1;
else
r = h - 1;
endif
endwhile
return r;
.
#79:25
{?prefix = ""} = args;
{common, branches, @_} = this:get_node(prefix);
depth = 0;
string = prefix;
if ((ticks_left() < 500) || (seconds_left() < 2))
player:tell("...", prefix);
suspend(0);
endif
for i in [1..length(branches)]
r = this:depth(tostr(prefix, common, branches[i]));
if (r[1] > depth)
{depth, string} = r;
endif
endfor
return {depth + 1, string};
.
#79:26
{prefix, count} = args;
{common, branches, exacts, ?_} = this:get_node(prefix);
count = length(exacts) + count;
if ((ticks_left() < 500) || (seconds_left() < 2))
player:tell("...", count);
suspend(0);
endif
for i in [1..length(branches)]
count = this:count_entries(tostr(prefix, common, branches[i]), count);
endfor
return count;
.
#79:27
{prefix, count} = args;
{common, branches, exacts, ?_} = this:get_node(prefix);
for s in (exacts)
count = count + length(s);
endfor
if ((ticks_left() < 500) || (seconds_left() < 2))
player:tell("...", count);
suspend(0);
endif
for i in [1..length(branches)]
count = this:count_chars(tostr(prefix, common, branches[i]), count);
endfor
return count;
.
#79:28
"count [entries|chars] in <db>";
"  reports on the number of distinct string keys or the number of characters";
"  in all string keys in the db";
if (index("entries", dobjstr) == 1)
player:tell(this:count_entries("", 0), " strings in ", this:name(), "(", this, ")");
elseif (index("chars", dobjstr) == 1)
player:tell(this:count_chars("", 0), " chars in ", this:name(), "(", this, ")");
else
player:tell("Usage: ", verb, " entries|chars in <db>");
endif
.
#80:0
if (args[1] in this:sitting())
this:delete_sitting(args[1]);
endif
return pass(@args);
"Metadata 202106";
"Last-Modify: {833314906, \"Ben\", #3373, \"JHM\"}";
.
#80:1
verbing = this:sitting_msg();
on_msg = this:on_msg();
pronoun = on_msg ? (" " + on_msg) + " " | " ";
along_with = (" " + this:desc_along_with_msg()) + " ";
sitting = this:sitting();
others = setremove(sitting, player);
what = args ? args[1] | this;
name = (what == this) ? what:dname() | what:po();
you = player in sitting;
if (sitting)
if (you)
themstr = $string_utils:iname_list(others);
return tostr("You are ", verbing, pronoun, name, others ? along_with + themstr | "", ".");
else
themstr = $string_utils:inamec_list(others);
return tostr(themstr, " ", (length(others) > 1) ? "are" | others[1]:verb_sub("is"), " ", verbing, pronoun, name, ".");
endif
else
return $string_utils:pronoun_sub(this:empty_msg(what));
endif
return;
"Metadata 202106";
"Last-Modify: {833585297, \"Ben\", #3373, \"JHM\"}";
.
#80:2
return $string_utils:pronoun_sub(this.(verb), player, @args);
.
#80:3
pass(@args);
this:set_sitting({});
.
#80:4
if (verb[1..4] == "sub_")
verb = verb[5..$] + "_msg";
endif
return $string_utils:pronoun_sub(this.(verb), @args);
.
#80:5
oldloc = this.location;
pass(@args);
if (this.location != oldloc)
fork (0)
this:delete_sitting(this:sitting(), args[2], oldloc);
endfork
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {857919963, \"Ben\", #3373, \"JHM\"}";
.
#80:6
":insert_sitting(what[, where])";
"Put what at the end of this.sitting, or make it the whereth element if where <= length(this.sitting) + 1.";
"Move what if what is already in this.sitting.";
if (!valid(what = args[1]))
return E_INVARG;
endif
this.location:broadcast_event_sit(what, this);
sitting = setremove(this.sitting, what);
n = length(sitting);
if (length(args) > 1)
where = args[2];
else
where = n + 1;
endif
return this.sitting = listinsert(sitting, what, where);
.
#80:7
":delete_sitting(what)";
"Remove what from this.sitting.";
if (typeof(what = args[1]) != LIST)
what = {what};
endif
this.location:broadcast_event_stand(what, this);
return this.sitting = $set_utils:difference(this.sitting, what);
.
#80:8
":event_sit(what, where)";
"Receive the event of what sitting where.";
what = args[1];
where = args[2];
if (where != this)
return this:delete_sitting(what);
endif
.
#80:9
if (player in this:sitting())
this:delete_sitting(player);
endif
pass(@args);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {857920004, \"Ben\", #3373, \"JHM\"}";
.
#80:10
return this.sitting;
.
#80:11
if (!($perm_utils:controls(caller_perms(), this) || (this == caller)))
return E_PERM;
else
return this.sitting = args[1];
endif
.
#80:12
return {this:sitting()};
"Metadata 202106";
"Last-Modify: {857007900, \"Ben\", #3373, \"JHM\"}";
.
#80:13
":seat_for(player) => the $sittable the player is sitting on";
{player} = args;
for seat in (player.location:contents())
if (`player in seat:sitting() ! ANY' && $object_utils:isa(seat, this))
return seat;
endif
endfor
return $failed_match;
"Metadata 202106";
"Last-Modify: {865231053, \"Ben\", #3373, \"JHM\"}";
.
#81:0
if (((!valid(caller_perms())) && dobjstr) && (dobjstr != "down"))
player:tell("You can't do that.");
return;
endif
if (player in this.sitting)
player:tell($string_utils:pronoun_sub(this:already_sitting_msg()));
elseif (player == this.location)
player:tell("Perhaps you should put it down first.");
elseif (player.location != this.location)
player:tell("From that distance? Don't stuff about with my ", this:name(), ".");
elseif ((this.squeeze < 0) && (length(this.sitting) >= this.seats))
player:tell($string_utils:pronoun_sub(this.nosqueeze_msg));
else
p = random(1 + length(this.sitting));
this:insert_sitting(player, p);
if (length(this.sitting) >= (this.seats + random(1 + max(this.squeeze, 0))))
if (random(2) == 1)
who = this.sitting[1];
else
who = this.sitting[length(this.sitting)];
endif
this:delete_sitting(who, this:squeeze_msg(who));
endif
endif
.
#81:1
if ((verb == "get") && (dobjstr != "up"))
player:tell("You can't get that from ", this:dname(), ".");
elseif (player in this.sitting)
this:delete_sitting(player);
else
player:tell("You aren't sitting on ", this:dname(), ".");
endif
.
#81:2
"Usage: shove/push <sitter> from <furniture>";
"You must be sitting on the piece of furniture to shove someone else from it.";
if (!valid(dobj))
player:tell("Push whom off ", this:dname(), "?");
elseif (msg = this:shoving_policy(player, dobj))
player:tell(msg);
else
dobj_position = dobj in this.sitting;
player_position = player in this.sitting;
if (!player_position)
sitters_beyond = {};
else
if (player_position > dobj_position)
sitters_beyond = this.sitting[1..dobj_position - 1];
else
sitters_beyond = this.sitting[dobj_position + 1..length(this.sitting)];
endif
this:move_next_to(player, dobj);
endif
this:delete_sitting(dobj, this:shove_msg());
if (sitters_beyond)
who = dobj;
dobj = {sitters_beyond};
if (msg = this:also_shove_msg())
this:delete_sitting(sitters_beyond, msg, this.location, who);
endif
endif
return 1;
endif
return;
"Metadata 202106";
"Last-Modify: {833589207, \"Ben\", #3373, \"JHM\"}";
.
#81:3
"augh I can't stand it anymore.";
desc = pass(@args);
try
what = this;
if ($list_utils:grep_l(desc, this:name()))
what = this:gender_obj();
endif
msg = this:sitting_string(what);
if (desc)
if (length(desc) > 1)
if (msg)
return {@desc, msg};
else
return desc;
endif
else
return {(desc[1] + $string_utils:pronoun_sub(this.location.integrate_sep_msg)) + msg};
endif
elseif (msg)
return {msg};
else
return {};
endif
except v (ANY)
return desc;
endtry
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {834536054, \"Ken\", #75, \"JHM\"}";
.
#81:4
people = this.sitting;
if (people)
if (you = player in people)
people[you] = $you;
endif
arglist = {{people}, @args ? listdelete(args, 1) | {}};
normal = pass(@arglist);
if (normal)
return normal;
else
return $string_utils:pronoun_sub(this.integrate_empty_msg, @arglist) + $string_utils:pronoun_sub(this.integrate_sitters_msg, @arglist);
endif
else
return $string_utils:pronoun_sub(this.integrate_empty_msg, {{}}, @args ? listdelete(args, 1) | {});
endif
.
#81:5
return this.(verb);
.
#81:6
"next_to(whom)";
"returns {before, after}";
"if player is on an end, the appropriate half of the return value will be E_RANGE.";
sitting = this.sitting;
if (whom = args[1] in sitting)
return {sitting[whom - 1], sitting[whom + 1]};
else
return E_INVARG;
endif
.
#81:7
":delete_sitting(<what>[, <msg>[, <location>[, <actor>]]])";
"Remove <what> (an object or a list of objects) from this.sitting, announcing <msg> (defaults to this:stand_msg(<what>)) to <location> (defaults to this.location) with <actor> (defaults to player) as the enactor of the removal.";
"Does nothing if none of <what> are in this.sitting.";
if (typeof(what = args[1]) != LIST)
what = {what};
endif
loc = (length(args) > 2) ? args[3] | this.location;
who = (length(args) > 3) ? args[4] | player;
if (what = $set_utils:intersection(what, this.sitting))
pass(what);
what = $set_utils:intersection(what, loc.contents);
if (what)
dobj = {what};
$you:say_action(args[2] ? args[2] | this:stand_msg({what}), who, this, loc);
endif
endif
.
#81:8
":insert_sitting(what[, where[, msg]])";
"Insert what at the end of [at the whereth position of] this.sitting, announcing this:sit_msg(position, what) [msg] to the room.";
{what, ?where = length(this.sitting) + 1, ?msg = 0} = args;
if (msg == 0)
if (!this.sitting)
mess = {};
else
if (where == 1)
neighbors = {this.sitting[1]};
elseif (where == (length(this.sitting) + 1))
neighbors = {this.sitting[length(this.sitting)]};
else
neighbors = {this.sitting[where - 1], this.sitting[where]};
endif
dobj = {neighbors};
mess = this:next_to_msg(what);
"This kluge...  we have to excise the #98, \"do\" off the front of the list or $you:say_action hates us.";
mess = mess[3..length(mess)];
endif
msg = {@this:sit_msg(what), @mess, "."};
endif
ret = pass(@args);
if (msg)
$you:say_action(msg, what);
endif
return ret;
"Metadata 202106";
"Last-Modify: {835903950, \"Erik\", #74, \"JHM\"}";
.
#81:9
pass(what = args[1], this:fall_msg(what));
.
#81:10
return this.visible && (!($pronoun_sub:message_empty(this.integrate_empty_msg) && (this.sitting && ($pronoun_sub:message_empty(this.integrate_room_msg) && $pronoun_sub:message_empty(this.integrate_sitters_msg)))));
.
#81:11
return this.sitting ? (pass(@args) && $pronoun_sub:message_empty(this.integrate_empty_msg)) && $pronoun_sub:message_empty(this.integrate_sitters_msg) | $pronoun_sub:message_empty(this.integrate_empty_msg);
.
#81:12
return $set_utils:difference(pass(@args), $furniture:obvious_verbs(@args));
"Metadata 202106";
"Last-Modify: {878248297, \"Erik\", #74, \"JHM\"}";
.
#81:13
who = args[1];
return this.obvious_seat;
.
#81:14
"move_next_to(player, target)";
{who, target} = args;
sitting = this:sitting();
if (!((whopos = who in sitting) && (targetpos = target in sitting)))
raise(E_INVARG);
endif
bet = sitting[min(whopos, targetpos) + 1..max(whopos, targetpos) - 1];
if (whopos > targetpos)
bet = $list_utils:reverse(bet);
endif
if (bet)
$you:say_action($furniture.move_next_to_msg, who, this, this.location, target, {bet});
this:insert_sitting(who, targetpos, "");
endif
return length(bet);
"Copied from generic piece of furniture (#296):move_next_to by Ben (#3373) Tue May  7 02:19:47 1996 EDT";
.
#81:15
":shoving_policy(who, whom)";
"Must you be sitting on the piece of furniture to shove someone else from it?";
{who, whom} = args;
sitting_on_this = tostr(this:sitting_msg(), " ", this:on_msg(), " ", this:dname());
if (!(whom in this.sitting))
return tostr("You aren't ", sitting_on_this, ".");
elseif (!(who in this.sitting))
return tostr("You need to be ", sitting_on_this, " to push others off.");
elseif (who == whom)
return tostr("You contort yourself brilliantly, but manage to resist pushing yourself off ", this:dname(), ".");
endif
return 0;
"Metadata 202106";
"Last-Modify: {858984118, \"Ben\", #3373, \"JHM\"}";
.
#82:0
"Usage:  :set_components  (components)";
"        :set_objects     (objects)";
"        :set_object_names(names)";
"";
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
return;
"Metadata 202106";
"Last-Modify: {834607904, \"Ken\", #75, \"JHM\"}";
.
#82:1
"Usage:  :add_object(object, name)";
"";
"This verb is used to add an object to the module.  The name here is the one used by the module system.  The name must conform to the rules for MOO identifiers.";
{object, name} = args;
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
raise(E_PERM, ((caller_perms():dnamec() + " is not allowed to add objects to ") + this:dname()) + ".");
elseif (object in this.objects)
raise(E_INVARG, (object:dnamec() + " is already in ") + this:dname());
elseif (object in this.locals)
raise(E_INVARG, (object:dnamec() + " is already a local dependency in ") + this:dname());
elseif (name in this.object_names)
raise(E_INVARG, (name + " is already in ") + this:dname());
elseif (index(name, "local_") == 1)
raise(E_INVARG, "module names can't begin with \"local_\"");
else
add_property(this, "obj_" + name, object, {this.owner, "r"});
this:set_objects({@this.objects, object});
this:set_object_names({@this.object_names, name});
endif
return;
"Metadata 202106";
"Last-Modify: {878326752, \"Erik\", #74, \"JHM\"}";
.
#82:2
"Usage:  :duplicate_for_pack()";
"";
"Replicate the objects in this module to prepare for packing.  Preserves interobject relationships.  Returns a list of objects that corresponds to the list this.objects.";
"";
if (caller != this)
raise(E_PERM);
endif
objects = {};
where = {};
for i in [1..length(this.objects)]
object = this.objects[i];
name = this.object_names[i];
where = {@where, object.location};
if ($object_utils:has_callable_verb(object, "init_for_module"))
"don't bother duplicating objects that aren't going to be affected by an :init_for_module.";
this:report_status("Duplicating ", object:name(), " (", name, ")...");
o = this:duplicate_object(object);
this:report_status("... done duplicating ", object:name(), " ", name);
objects = {@objects, o};
this.("obj_" + name) = o;
else
this:set_duplicated_objects({@this.duplicated_objects, 0});
this:report_status(" - skipping duplication of ", object:name(), " ", name);
objects = {@objects, {object}};
endif
endfor
"finalize property values";
for i in [1..length(objects)]
o = objects[i];
if (typeof(o) == OBJ)
this:report_status("Finalizing property values for ", o:name(), " (", this:obj_name(o), ")");
this:duplicate_object_finalize(o);
move(o, where[i]);
else
o = o[1];
objects[i] = o;
endif
endfor
":init_for_module is called by :pack";
"for o in (objects)";
"  `o:init_for_module() ! E_VERBNF';";
"endfor";
return objects;
"Metadata 202106";
"Last-Modify: {878320564, \"Erik\", #74, \"JHM\"}";
.
#82:3
"Usage:  @pack module at sink [sinkarg1, etc]";
"";
"This initiates a module pack at the specified sink.  Standard sinks include `player' which outputs the flat format at the player.";
set_task_perms(player);
{sinkstr, @sinkargs} = $string_utils:explode(iobjstr);
sink = this:match_sink(sinkstr);
if (this:match_sink_failed(sink, sinkstr))
return;
endif
sink = sink[1];
pt = sink:parse_commandline(@sinkargs);
player:tell("Packing ", this:dname(), " to ", pt:dname(), "...");
try
this:pack(pt);
except v (ANY)
if (v[1] != this.pack_error)
player:tell_lines($code_utils:format_traceback(v[4], v[1], v[2]));
else
player:Tell("Fatal pack error: ", v[2]);
endif
player:tell("(Cleaning up module pack...)");
this:pack_abort(v[1], ": ", v[2]);
$recycler:_recycle(pt);
return;
endtry
$recycler:_recycle(pt);
player:tell("Done.");
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {893779761, \"Erik\", #74, \"JHM\"}";
.
#82:4
"Usage:  :pack(sink)";
"";
"This actually does the packing of a module.  It does the following:";
"";
"  * ensure this is the only pack running";
"  * duplicate objects";
"  * initiate them for module (call :init_for_module on each one)";
"  * pack them using the packing helper verbs";
"  * recycle the duplicated objects";
"";
{target} = args;
if (caller != this)
raise(E_PERM);
elseif ((t = this:pack_task()) && (t != task_id()))
raise(this.pack_error, "Not packing task");
endif
set_task_perms(caller_perms());
this:pack_start(target);
this:pack_line("--- begin module ---");
objects = this:duplicate_for_pack();
this:report_status("Init for module...");
for o in (objects)
if (!(o in this.objects))
"don't call if this isn't a copy";
if ($object_utils:has_callable_verb(o, "init_for_module"))
o:init_for_module(this);
endif
endif
endfor
depends = {};
for o in (objects)
depends = $set_utils:union(depends, this:dependencies_for_object(o));
$command_utils:suspend_if_needed(0);
endfor
if (depends)
player:tell("The following objects must be @added or @defined:");
this:display_dependencies(depends);
raise(this.pack_error, "Unresolved dependencies");
endif
this:pack_header({"Name", this:name()}, {"Date", ctime()}, {"Type", this.format_description}, {"Boundary", boundary = "---module/body-item-boundary"}, {"Objects", $string_utils:from_list($list_utils:map_arg(this, "obj_name", objects), " ")}, {"Locals", $string_utils:from_list($list_utils:map_arg(this, "local_name", this.locals), " ")});
"object definitions including info, verbs, and defined properties";
this:report_status("Packing objects...");
for o in (objects)
"pack object info";
this:pack_object_info(boundary, o);
$command_utils:suspend_if_needed(0);
endfor
this:report_status("Packing verbs and properties...");
for o in (objects)
"verbs and properties";
this:report_status("   - ", o:name(), "(", this:obj_name(o), ")");
this:pack_object_verbs(boundary, o);
this:pack_object_properties(boundary, o);
$command_utils:suspend_if_needed(0);
endfor
"save our verbs!";
this:report_status("Saving module verbs and properties...");
this:pack_object_verbs(boundary, this);
for o in (objects)
"pack object inherited properties";
this:pack_object_inherited_properties(boundary, o);
$command_utils:suspend_if_needed(0);
endfor
this:report_status("Saving components...");
for thing in (this.components)
$command_utils:suspend_if_needed(0);
this:("pack_" + thing[1])(boundary, @thing[2..$]);
$command_utils:suspend_if_needed(0);
endfor
this:pack_line("--- end module ---");
this:pack_finish();
"WIZARDLY";
return;
"Metadata 202106";
"Last-Modify: {878322957, \"Erik\", #74, \"JHM\"}";
.
#82:5
"Usage:  :is_packing    ()";
"        :pack_task     ()";
"        :is_unpacking  ()";
"        :unpacking_task()";
"";
"These verbs are various tests to see if there is an operation in progress on this module.";
if ($code_utils:task_valid(this.operation_in_progress))
return this.operation_in_progress;
else
return 0;
endif
return;
"Metadata 202106";
"Last-Modify: {834607905, \"Ken\", #75, \"JHM\"}";
.
#82:6
"Usage:  :pack_start()";
{target} = args;
if (caller == this)
this.pack_target = target;
this.core_ref_cache = {{}, {}};
this.duplicated_objects = {};
this.operation_in_progress = task_id();
endif
return;
"Metadata 202106";
"Last-Modify: {834607905, \"Ken\", #75, \"JHM\"}";
.
#82:7
"Usage:  :pack_warning()";
this.pack_target:receive_warning(@args);
return;
"Metadata 202106";
"Last-Modify: {834607905, \"Ken\", #75, \"JHM\"}";
.
#82:8
"Usage:  :pack_fatal_error()";
`this.pack_target:abnormal_termination(@args) ! ANY';
return;
"Metadata 202106";
"Last-Modify: {834607905, \"Ken\", #75, \"JHM\"}";
.
#82:9
"Usage:  :pack_finish()";
if (caller == this)
this:nuke_duplicated_objects();
this.pack_target:finish();
this.operation_in_progress = 0;
this.pack_target = $nothing;
endif
return;
"Metadata 202106";
"Last-Modify: {834607905, \"Ken\", #75, \"JHM\"}";
.
#82:10
"Usage:  :pack_abort()";
if (caller == this)
this:nuke_duplicated_objects();
this:pack_fatal_error(@args);
endif
return;
"Metadata 202106";
"Last-Modify: {834607905, \"Ken\", #75, \"JHM\"}";
.
#82:11
"Usage:  :nuke_duplicated_objects()";
if (caller == this)
for i in (this.duplicated_objects)
if (typeof(i) == OBJ)
`chparent(i, $root_class) ! ANY';
`delete_verb(i, "recycle") ! ANY';
if (`is_player(i) ! ANY')
$wiz_utils:unset_player(i);
endif
`$recycler:_recycle(i) ! ANY';
$command_utils:suspend_if_needed(0);
endif
endfor
this.duplicated_objects = {};
endif
return;
"Metadata 202106";
"Last-Modify: {868453146, \"Ken\", #75, \"JHM\"}";
.
#82:12
"Usage:  :@unpack()";
set_task_perms(callers() ? caller_perms() | player);
{sourcestr, @sourceargs} = $string_utils:explode(iobjstr);
src = this:match_source(sourcestr);
if (this:match_source_failed(src, sourcestr))
return;
endif
src = src[1];
pt = src:parse_commandline(@sourceargs);
if (typeof(pt) != OBJ)
return player:tell(pt);
elseif (typeof(module = $recycler:_create($module)) != OBJ)
return player:tell("Unable to create module.");
endif
module:set_name("new module");
module:set_made_for_unpack({});
try
pt:fetch();
module:unpack_from(pt);
except v (ANY)
player:tell_lines($code_utils:format_traceback(v[4], v[1], v[2]));
player:Tell("(Cleaning up...)");
$recycler:_recycle(pt);
for zog in (module.made_for_unpack)
$command_utils:suspend_if_needed(0);
if (is_player(zog))
$wiz_utils:unset_player(zog);
endif
$recycler:_recycle(zog);
endfor
$recycler:_recycle(module);
return;
endtry
$recycler:_recycle(pt);
player:tell("Done.");
return;
"Metadata 202106";
"Last-Modify: {893779927, \"Erik\", #74, \"JHM\"}";
.
#82:13
"Usage:  :unpack_from()";
{pt} = args;
set_task_perms(caller_perms());
modheaders = pt:read_headers();
{name, date, format, boundry, boundary, objects, locals} = pt:parse_headers({"Name", "Date", "Type", "Boundry", "Boundary", "Objects", "Locals"}, modheaders);
if (typeof(boundary) != STR)
"historical accident.";
boundary = boundry;
endif
this:set_name(name);
"first we need to create all of the objects using Objects list";
"then unpack them";
"  during the unpack phase we can: ";
"    set object ownership correctly";
"    define `defined properties'";
"    add verbs";
"    setup parentage";
"then finalize extraction by setting all the inherited properties";
"";
"Phase pre-I: check local dependencies";
if ((typeof(locals) == STR) && (names = $string_utils:explode(locals)))
player:tell("This module depends on some objects that it can't find.  Please type expressions that evaluate to the appropriate object for each name:");
for name in (names)
local = $command_utils:read(("an expression that evaluates to the `" + name) + "' object");
result = $no_one:eval_d(local);
if (result[1])
o = result[2];
if ((typeof(o) == OBJ) && $recycler:valid(o))
this:add_local(o, name);
else
raise(E_INVARG, "Dependency must be a valid object.");
endif
elseif (result[2])
raise(E_INVARG, result[2][1]);
else
raise(result[2]);
endif
endfor
endif
"Phase I: create objects";
made = {};
for i in ($string_utils:explode(objects))
$command_utils:suspend_if_needed(0);
if (typeof(o = $recycler:_create($root_class)) != OBJ)
for wot in (made)
$recycler:_recycle(wot);
endfor
raise("Creation error", "Unable to create object");
else
made = {@made, o};
this:add_object(o, i);
endif
endfor
"we gots objects!";
"now read and unpack all of the elements";
this:set_made_for_unpack(made);
while (headers = pt:read_headers())
{t} = pt:parse_headers({"Type"}, headers);
this:("unpack_" + t)(headers, pt:read_body(boundary), pt);
$command_utils:suspend_if_needed(0);
endwhile
this:finish_unpack();
"WIZARDLY";
return;
"Metadata 202106";
"Last-Modify: {878328678, \"Erik\", #74, \"JHM\"}";
.
#82:14
"Usage:  :resolve_name()";
{name} = args;
if (name[1] in {"$", "#"})
return $string_utils:literal_object(name);
elseif (name == "module")
return this;
elseif (i = name in this.object_names)
return this.objects[i];
elseif ((index(name, "local_") == 1) && (i = name[7..$] in this.local_names))
return this.locals[i];
else
return $failed_match;
endif
return;
"Metadata 202106";
"Last-Modify: {878328791, \"Erik\", #74, \"JHM\"}";
.
#82:15
"Usage:  :resolve_owner()";
{o} = args;
if (o == "wizard")
if (!caller_perms().wizard)
raise("permissions", "Require wizardly permissions and not a wizard.");
endif
return caller_perms();
elseif (o == "nonwizard")
if (!caller_perms().wizard)
return caller_perms();
elseif ((typeof(own = `caller_perms().public_identity ! ANY') == OBJ) && valid(own))
return own;
else
return $hacker;
endif
else
if (valid(ref = this:resolve_name(o)))
return ref;
else
raise("permissions", "Unable to resolve owner.");
endif
endif
return;
"Metadata 202106";
"Last-Modify: {878328589, \"Erik\", #74, \"JHM\"}";
.
#82:16
"Usage:  :object_unpack_from()";
"setup this.extract_object_{property-defined, property-inherited, and verb}";
"return objid of munged object";
{pt} = args;
if (caller != this)
raise(E_PERM);
endif
set_task_perms(caller_perms());
if (!(oheaders = pt:read_headers()))
return $failed_match;
else
{typ, reference, owner, p, name, flgs, boundryo, boundaryo} = pt:parse_headers({"Type", "Reference", "Owner", "Parent", "Name", "flags", "Boundry", "Boundary"}, oheaders);
if (typeof(boundaryo) != STR)
"historical accident.";
boundaryo = boundryo;
endif
prnt = this:resolve_name(p);
what = this:resolve_name(reference);
ownr = this:resolve_owner(owner);
"fix parentage, ownership, and flags";
chparent(what, prnt);
$wiz_utils:set_owner(what, ownr, 1);
flags = $string_utils:explode(flgs);
for i in ({"r", "f"})
if (i in flags)
what.(i) = 1;
endif
endfor
if ("programmer" in flags)
i.programmer = 1;
endif
if ("wizard" in flags)
"yeah, right";
endif
if ("player" in flags)
"we should probably distinguish these two cases better";
if ($object_utils:isa(what, $Player))
$wiz_utils:set_player(what, 1);
else
set_player_flag(what, 1);
endif
endif
inherited = {};
while (partheaders = pt:read_headers())
{ptype, pname} = pt:parse_headers({"Type", "Name"}, partheaders);
if (ptype == "property-defined")
{powner, pperms} = pt:parse_headers({"Owner", "Perms"}, partheaders);
value = pt:read_body(boundaryo);
{worked, newvalue} = this:unsub_references(value);
if (!worked)
"sigh, what to do here?";
newvalue = E_INVARG;
endif
add_property(what, newvalue, {powner, pperms});
else
value = pt:read_body(boundaryo);
{worked, newvalue} = this:unsub_references(value);
if (!worked)
"sigh, what to do here?";
newvalue = E_INVARG;
endif
inherited = {@inherited, {pname, newvalue}};
endif
endwhile
endif
"WIZARDLY";
return;
"Metadata 202106";
"Last-Modify: {868900660, \"Erik\", #74, \"JHM\"}";
.
#82:17
"Usage:  :unsub_references()";
{value} = args;
return $no_one:eval((((";module = " + tostr(this)) + "; return ") + value) + ";");
"Metadata 202106";
"Last-Modify: {834607908, \"Ken\", #75, \"JHM\"}";
.
#82:18
"Usage:  :duplicate_object()";
{o} = args;
if (caller != this)
raise(E_PERM);
endif
new = $recycler:_create(parent(o));
$wiz_utils:set_owner(new, o.owner);
this:set_duplicated_objects(setadd(this.duplicated_objects, new));
for p in ({"name", "r", "w", "f"})
new.(p) = o.(p);
endfor
" new.wizard = o.wizard;";
new.programmer = o.programmer;
set_player_flag(new, is_player(o));
for p in ($object_utils:all_properties(parent(o)))
$command_utils:suspend_if_needed(0);
if (p in this.ignored_properties)
continue;
endif
if (!is_clear_property(o, p))
new.(p) = this:substitute_references(o.(p), 0);
endif
endfor
for p in (properties(o))
$command_utils:suspend_if_needed(0);
if (p in this.ignored_properties)
continue;
endif
info = property_info(o, p);
info[1] = this:resolve_owner(this:owner_name(info[1]));
add_property(new, p, this:substitute_references(o.(p), 0), info);
endfor
for v in [1..length(verbs(o))]
$command_utils:suspend_if_needed(0);
info = verb_info(o, v);
info[1] = this:resolve_owner(this:owner_name(info[1]));
add_verb(new, info, verb_args(o, v));
set_verb_code(new, v, verb_code(o, v));
endfor
return new;
"Metadata 202106";
"Last-Modify: {845668490, \"Ken\", #75, \"JHM\"}";
.
#82:19
"Usage:  :substitute_references()";
{value, ?pack = 1} = args;
return this:_substitute_references({}, value, pack);
"Metadata 202106";
"Last-Modify: {834607908, \"Ken\", #75, \"JHM\"}";
.
#82:20
"Usage:  :_substitute_references()";
{unknown, value, pack} = args;
if (typeof(value) == OBJ)
if (known = `this:obj_ref(value) ! E_INVIND')
return {unknown, known};
else
if (known == E_INVIND)
if (pack)
return raise(E_INVIND, tostr("Invalid object ", value));
else
return {unknown, tostr(value)};
endif
else
return {setadd(unknown, value), tostr(value)};
endif
endif
elseif (typeof(value) == LIST)
nl = {};
if (!value)
result = "{}";
else
result = "";
for v in (value)
{nknown, v} = this:_substitute_references(unknown, v, pack);
result = (result + ", ") + v;
endfor
result = ("{" + result[3..$]) + "}";
endif
return {unknown, result};
else
return {unknown, toliteral(value)};
endif
return;
"Metadata 202106";
"Last-Modify: {834607909, \"Ken\", #75, \"JHM\"}";
.
#82:21
"Usage:  :duplicate_object_finalize()";
set_task_perms(caller_perms());
{object} = args;
if (caller != this)
raise(E_PERM);
endif
evalprfx = (";module = " + tostr(this)) + "; return ";
unknown = {};
for prop in ($object_utils:all_properties(object))
if (prop in this.ignored_properties)
continue;
endif
if (`!is_clear_property(object, prop) ! E_PROPNF => 1')
if (`{unk, value} = object.(prop) ! E_TYPE, E_ARGS')
unknown = $set_utils:union(unknown, unk);
this:report_status(" - ", this:obj_name(object), ".", prop, " = ", $string_utils:print(value));
{worked, result} = $no_one:eval_d((evalprfx + value) + ";");
if (!worked)
raise(tostr(this:obj_name(object), ".", prop, " = ", toliteral(result)));
else
object.(prop) = result;
endif
endif
endif
$command_utils:suspend_if_needed(0);
endfor
"Copied from generic object component (#428):finalize_properties by Xythian (#77) Mon Apr 29 13:30:08 1996 EDT";
"WIZARDLY";
return;
"Metadata 202106";
"Last-Modify: {878333625, \"Erik\", #74, \"JHM\"}";
.
#82:22
"Usage:  :pack_line()";
set_task_perms(caller_perms());
this.pack_target:receive_line(@args);
return;
"Metadata 202106";
"Last-Modify: {834607909, \"Ken\", #75, \"JHM\"}";
.
#82:23
"Usage:  :pack_header()";
elements = args;
set_task_perms(caller_perms());
for element in (elements)
{name, value} = element;
this:pack_line((name + ": ") + value);
endfor
this:pack_line("");
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {834607909, \"Ken\", #75, \"JHM\"}";
.
#82:24
"Usage:  :pack_object_info()";
{boundary, o} = args;
set_task_perms(caller_perms());
if ((valid(o.location) && (name = this:obj_name(o.location))) && (name[1] != "#"))
where = {{"Location", name}};
else
where = {};
endif
this:pack_header({"Type", "object"}, {"Reference", this:obj_name(o)}, {"Owner", this:owner_name(o.owner)}, {"Parent", this:obj_name(parent(o))}, {"Name", o.name}, @where, {"Flags", this:object_flags(o)});
this:pack_line(boundary);
"WIZARDLY";
return;
"Metadata 202106";
"Last-Modify: {878335856, \"Erik\", #74, \"JHM\"}";
.
#82:25
"Usage:  :pack_object_verbs()";
{boundary, o} = args;
set_task_perms(caller_perms());
go = 1;
v = 1;
while (go)
$command_utils:suspend_if_needed(0);
try
this:pack_verb(boundary, o, v);
except zot (E_VERBNF)
go = 0;
except zot (E_PERM)
this:pack_warning("Error reading verb ", this:obj_name(o), ":", v);
endtry
v = v + 1;
endwhile
"WIZARDLY";
return;
"Metadata 202106";
"Last-Modify: {868900520, \"Erik\", #74, \"JHM\"}";
.
#82:26
"Usage:  :pack_object_properties()";
{boundary, o} = args;
set_task_perms(caller_perms());
for p in (properties(o))
$command_utils:suspend_if_needed(0);
if (p in this.ignored_properties)
continue;
endif
try
this:pack_property(boundary, o, p);
except v (E_PERM)
this:pack_warning("Unable to read defined property: ", o, ".", p);
endtry
endfor
"WIZARDLY";
return;
"Metadata 202106";
"Last-Modify: {877467665, \"Erik\", #74, \"JHM\"}";
.
#82:27
"Usage:  :pack_object_inherited_properties()";
{boundary, o} = args;
set_task_perms(caller_perms());
for p in ($object_utils:all_properties(parent(o)))
$command_utils:suspend_if_needed(0);
if (p in this.ignored_properties)
continue;
endif
lost = 0;
try
if (!is_clear_property(o, p))
{unknown, value} = this:substitute_references(o.(p));
{owner, perms} = property_info(o, p);
this:pack_header({"Type", "property-inherited"}, {"Object", this:obj_name(o)}, {"Name", p});
this:pack_line(value);
this:pack_line(boundary);
endif
except v (E_PERM)
this:pack_warning("Unable to read inherited property: ", this:obj_name(o), ".", p);
except v (E_INVIND)
lost = tostr(v[2], " in ", this.objects[(o in this.duplicated_objects) || (o in this.objects)], ".", p);
endtry
if (lost)
return raise(this.pack_error, lost);
endif
endfor
"WIZARDLY";
return;
"Metadata 202106";
"Last-Modify: {868900520, \"Erik\", #74, \"JHM\"}";
.
#82:28
"Usage:  :owner_name()";
{what} = args;
if (what == $first_wizard)
return "wizard";
"sigh";
elseif (ref = this:obj_name(what))
return ref;
elseif (what.wizard)
return "wizard";
else
return "nonwizard";
endif
return;
"Metadata 202106";
"Last-Modify: {834607910, \"Ken\", #75, \"JHM\"}";
.
#82:29
"Usage:  :object_flags()";
{o} = args;
flags = tostr({"", "r "}[o.r + 1], {"", "w "}[o.w + 1], {"", "f "}[o.f + 1], {"", "player "}[is_player(o) + 1], {"", "programmer "}[o.programmer + 1], {"", "wizard "}[o.wizard + 1]);
return flags;
"Metadata 202106";
"Last-Modify: {834607910, \"Ken\", #75, \"JHM\"}";
.
#82:30
"Usage:  :pack_lines()";
{lines} = args;
this.pack_target:receive_lines(lines);
return;
"Metadata 202106";
"Last-Modify: {834607910, \"Ken\", #75, \"JHM\"}";
.
#82:31
"Usage:  :obj_ref()";
{o} = args;
if (a = $list_utils:assoc(o, this.defines))
return a[2];
endif
ref = this:obj_name(@args);
if ((ref && (ref[1] != "$")) && (ref[1] != "#"))
ref = "module.obj_" + ref;
endif
return ref;
"Metadata 202106";
"Last-Modify: {878322375, \"Erik\", #74, \"JHM\"}";
.
#82:32
"Usage:  :unpack_object()";
{headers, body, pt} = args;
if (caller != this)
return;
endif
set_task_perms(caller_perms());
{typ, reference, owner, p, name, where, flgs} = pt:parse_headers({"Type", "Reference", "Owner", "Parent", "Name", "Location", "Flags"}, headers);
wot = this:resolve_name(reference);
prnt = this:resolve_name(p);
ownr = this:resolve_owner(owner);
chparent(wot, prnt);
$wiz_utils:set_owner(wot, ownr);
wot:set_name(name);
if (flgs)
flags = $string_utils:explode(flgs);
else
flags = {};
endif
for z in ({"programmer", "r", "f"})
if (z in flags)
wot.(z) = 1;
endif
endfor
if ("player" in flags)
if ($object_utils:isa(wot, $Player))
$wiz_utils:set_player(wot, 1);
else
set_player_flag(wot, 1);
endif
endif
if (where)
loc = this:resolve_name(where);
move(wot, loc);
endif
return;
"Metadata 202106";
"Last-Modify: {878335797, \"Erik\", #74, \"JHM\"}";
.
#82:33
"Usage:  :unpack_property-defined()";
{headers, body, pt} = args;
if (caller != this)
return;
endif
set_task_perms(caller_perms());
{typ, reference, name, owner, perms} = pt:parse_headers({"Type", "Object", "Name", "Owner", "Perms"}, headers);
if (!perms)
perms = "";
endif
wot = this:resolve_name(reference);
ownr = this:resolve_owner(owner);
value = this:unsub_references(body[1])[2];
add_property(wot, name, value, {ownr, perms});
return;
"Metadata 202106";
"Last-Modify: {834607911, \"Ken\", #75, \"JHM\"}";
.
#82:34
"Usage:  :unpack_property-inherited()";
{headers, body, pt} = args;
if (caller != this)
return;
endif
set_task_perms(caller_perms());
{typ, reference, name} = pt:parse_headers({"Type", "Object", "Name"}, headers);
wot = this:resolve_name(reference);
value = this:unsub_references(body[1])[2];
wot.(name) = value;
return;
"Metadata 202106";
"Last-Modify: {834607911, \"Ken\", #75, \"JHM\"}";
.
#82:35
"Usage:  :unpack_verb()";
{headers, body, pt} = args;
if (caller != this)
return;
endif
set_task_perms(caller_perms());
{typ, reference, name, owner, perms, vargs} = pt:parse_headers({"Type", "Object", "Name", "Owner", "Perms", "Args"}, headers);
wot = this:resolve_name(reference);
ownr = this:resolve_owner(owner);
if (reference[1] == "$")
player:tell("warning: unpacking verb onto ", $string_utils:nn(wot));
for modname in ($string_utils:explode(name))
if ((modname in verbs(wot)) || ($object_utils:has_verb(wot, name) == {wot}))
newnames = listappend(newnames, modname + "(module)");
player:tell("warning: packed verb has same name: renaming \"", modname, "\" to \"", newnames[$], "\".");
else
newnames = listappend(newnames, modname);
endif
endfor
name = $string_utils:from_list(newnames, " ");
elseif (!valid(wot))
player:Tell("warning: no object for verb \"", name, "\"");
endif
add_verb(wot, {ownr, perms, name}, $string_utils:explode(vargs));
set_verb_code(wot, length(verbs(wot)), body);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {877373633, \"Erik\", #74, \"JHM\"}";
.
#82:36
"Usage:  :match_sink()";
{s, @a} = args;
whats = verb[7..$] + "s";
return $string_utils:match_all(s, this.(whats), "name", this.(whats), "aliases");
"Metadata 202106";
"Last-Modify: {845221260, \"Ken\", #75, \"JHM\"}";
.
#82:37
"Usage:  :match_sink_failed()";
{v, s} = args;
if (length(v) > 1)
player:tell("\"", s, "\" could match any of ", $string_utils:dname_list(v), ".  Please be more specific.");
elseif (!valid(v[1]))
player:tell("\"", s, "\" does not match any available sinks.  Available sinks are ", $string_utils:dname_list(this.sinks), ".");
else
return 0;
endif
return 1;
"Metadata 202106";
"Last-Modify: {834607911, \"Ken\", #75, \"JHM\"}";
.
#82:38
"Usage:  :set_duplicated_objects()";
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
return;
"Metadata 202106";
"Last-Modify: {834607912, \"Ken\", #75, \"JHM\"}";
.
#82:39
"Usage:  :@add [object, verb, or property] to this";
"";
if ($perm_utils:controls(player, this))
if (index(dobjstr, ".") || index(dobjstr, ":"))
what = "";
if (pref = match(dobjstr, "%(#%|%$[0-9A-Za-z_]+%)%.%([A-Za-z_0-9]+%)"))
what = "property";
elseif (pref = match(dobjstr, "%(#%|%$[0-9A-Za-z_]+%)%:%([A-Za-z_0-9]+%)"))
what = "verb";
endif
if (what)
{o, p} = {$string_utils:literal_object(ostr = substitute("%1", pref)), substitute("%2", pref)};
if (!valid(o))
return player:tell(ostr, " does not specify a valid object.");
endif
try
this:("add_" + what)(o, p);
player:tell("Added.");
except v (E_PERM, E_INVARG)
player:tell(v[2]);
endtry
return;
endif
else
if (phot = match(dobjstr, "%([^(]+%) (%([^(]+%))"))
{whatstr, name} = {substitute("%1", phot), substitute("%2", phot)};
else
whatstr = dobjstr;
name = "";
endif
what = player:my_match_object(whatstr);
if ($command_utils:object_match_failed(what, whatstr))
return;
elseif (!name)
player:tell("Adding ", $string_utils:nn(what), " to ", this:dname(), ".");
if (!(name = $command_utils:read("a module name for the object")))
return player:tell("Aborted.");
endif
name = strsub(name, " ", "_");
endif
player:tell("Added ", $string_utils:nn(what), " as ", name, " to ", this:dname(), ".");
this:add_object(what, name);
endif
else
player:tell("You are not allowed to do that.");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878321651, \"Erik\", #74, \"JHM\"}";
.
#82:40
"Usage:  :look_self()";
player:tell(this:dname());
pass(@args);
what = {};
for i in [1..length(this.objects)]
o = this.objects[i];
n = this.object_names[i];
c = $object_utils:has_verb(o, "init_for_module") ? "" | "NC";
what = {@what, {c, o:name(), tostr(o), n}};
endfor
if (this.components)
what = {@what, {"", "--- non-object components ---", "", ""}};
for i in (this.components)
what = {@what, {"", this:obj_name(i[2]), i[1], i[3]}};
endfor
endif
if (this.locals)
what = {@what, {"", "--- local dependencies ---", "", ""}};
for i in [1..length(this.locals)]
o = this.locals[i];
n = this.local_names[i];
"local dependencies are never copied, so why bother pointing it out";
what = {@what, {"", o:name(), tostr(o), n}};
endfor
endif
if (this.defines)
what = {@what, {"", "--- definitions ---", "", ""}};
for i in (this.defines)
{o, string} = i;
what = {@what, {"", o:name(), tostr(o), string}};
endfor
endif
if (what)
player:tell_lines($string_utils:columnize_with_headers({"C", "Object", "Id", "Name"}, what));
else
player:tell("It contains nothing.");
endif
return;
"Metadata 202106";
"Last-Modify: {878327389, \"Erik\", #74, \"JHM\"}";
.
#82:41
"Usage:  :nominate_for_core()";
return {$modules.module, @$module.sinks, @$module.sources, $wiz_utils, $string_utils, $perm_utils, $code_utils, @this.objects, @pass(@args)};
"Metadata 202106";
"Last-Modify: {895555379, \"Ken\", #75, \"JHM\"}";
.
#82:42
"Usage:  :remove_object()";
"Usage:  :remove_object(object)";
"";
{object} = args;
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
raise(E_PERM, ((caller_perms():dnamec() + " is not allowed to remove objects from ") + this:dname()) + ".");
elseif (!(i = object in this.objects))
raise(E_INVARG, (object:dnamec() + " is not in ") + this:dname());
else
delete_property(this, "obj_" + this.object_names[i]);
this:set_objects(listdelete(this.objects, i));
this:set_object_names(listdelete(this.object_names, i));
endif
return;
"Metadata 202106";
"Last-Modify: {878320625, \"Erik\", #74, \"JHM\"}";
.
#82:43
"Usage:  :@remove()";
"Usage: @remove object from this";
"";
if ($perm_utils:controls(player, this))
if (index(dobjstr, ".") || index(dobjstr, ":"))
what = "";
if (pref = match(dobjstr, "%(#%|%$[0-9A-Za-z_]+%)%.%([A-Za-z_0-9]+%)"))
what = "property";
elseif (pref = match(dobjstr, "%(#%|%$[0-9A-Za-z_]+%)%:%([A-Za-z_0-9]+%)"))
what = "verb";
endif
if (what)
{o, p} = {$string_utils:literal_object(ostr = substitute("%1", pref)), substitute("%2", pref)};
if (!valid(o))
return player:tell(ostr, " does not specify a valid object.");
endif
try
this:("remove_" + what)(o, p);
player:tell("Removed.");
except v (E_PERM, E_INVARG)
player:tell(v[2]);
endtry
return;
endif
else
whatstr = dobjstr;
if (!valid(what = this:resolve_name(whatstr)))
what = player:my_match_object(whatstr);
endif
if ($command_utils:object_match_failed(what, whatstr))
return;
elseif (!(what in this.objects))
player:tell(what:dnamec(), " is not in ", this:dname(), ".");
else
this:remove_object(what);
player:tell("Removed ", $string_utils:nn(what), " from ", this:dname(), ".");
endif
endif
else
player:tell("You're not allowed to do that.");
endif
return;
"Metadata 202106";
"Last-Modify: {834607913, \"Ken\", #75, \"JHM\"}";
.
#82:44
"Usage:  :pack_description(boundary)";
{boundary} = args;
set_task_perms(caller_perms());
this:pack_header({"Type", "description"});
this:pack_lines(this:description());
this:pack_line(boundary);
this:pack_line(boundary);
"WIZARDLY";
return;
return;
"Metadata 202106";
"Last-Modify: {868900520, \"Erik\", #74, \"JHM\"}";
.
#82:45
{headers, body, pt} = args;
if (caller != this)
return;
endif
this:set_description(body);
return;
"Metadata 202106";
"Last-Modify: {834607913, \"Ken\", #75, \"JHM\"}";
.
#82:46
"Usage:  :add_property(object, name)";
"        :add_verb(object, name)";
"";
"This verb is used to add a property or verb to the module.  This is for adding properties or verbs to objects that are not in the module, for example, #0 (for #0.foobar).";
{object, name} = args;
typ = verb[5..$];
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
raise(E_PERM, ((caller_perms():dnamec() + " is not allowed to add things to ") + this:dname()) + ".");
elseif (object in this.objects)
raise(E_INVARG, (object:dnamec() + " is already in ") + this:dname());
elseif ({typ, object, name} in this.components)
raise(E_INVARG, "That verb or property is already in " + this:dname());
else
this:set_components({@this.components, {typ, object, name}});
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878320638, \"Erik\", #74, \"JHM\"}";
.
#82:47
"Usage:  :remove_property(object, name)";
"        :remove_verb(object, name)";
"";
"This verb is used to remove a property or verb to the module.  This is for adding properties or verbs to objects that are not in the module, for example, #0 (for #0.foobar).";
{object, name} = args;
typ = verb[8..$];
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
raise(E_PERM, ((caller_perms():dnamec() + " is not allowed to remove things from ") + this:dname()) + ".");
elseif (i = {typ, object, name} in this.components)
this:set_components(listdelete(this.components, i));
else
raise(E_INVARG, "That verb or property is not in " + this:dname());
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {834607913, \"Ken\", #75, \"JHM\"}";
.
#82:48
"pack_verb(o, v)";
"Pack a verb.";
set_task_perms(caller_perms());
{boundary, o, v} = args;
{owner, perms, name} = verb_info(o, v);
if (index(name, "(old)"))
return;
endif
{dspec, pspec, ispec} = verb_args(o, v);
if (!(pspec in {"any", "none"}))
pspec = $code_utils:short_prep(pspec);
endif
vargs = {dspec, pspec, ispec};
vcode = verb_code(o, v);
this:pack_header({"Type", "verb"}, {"Object", this:obj_name(o)}, {"Name", name}, {"Owner", this:owner_name(owner)}, {"Perms", perms}, {"Args", $string_utils:from_list(vargs, " ")});
this:pack_lines(vcode);
this:pack_line(boundary);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869151727, \"Erik\", #74, \"JHM\"}";
.
#82:49
"pack_property(boundary, o, p)";
"pack a property";
{boundary, o, p} = args;
set_task_perms(caller_perms());
lost = 0;
try
{unknown, value} = this:substitute_references(o.(p), 1);
except v (E_INVIND)
lost = tostr(v[2], " in ", o, ".", p);
endtry
if (lost)
return raise(this.pack_error, lost);
endif
if (unknown)
raise(this.pack_error, tostr("unknown objects: ", this:obj_name(o), ".", p, " ", $string_utils:print(unknown)));
endif
{owner, perms} = property_info(o, p);
this:pack_header({"Type", "property-defined"}, {"Object", this:obj_name(o)}, {"Name", p}, {"Owner", this:owner_name(owner)}, {"Perms", perms});
this:pack_line(value);
this:pack_line(boundary);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {868900520, \"Erik\", #74, \"JHM\"}";
.
#82:50
if (caller == this)
for i in (this.objects)
$command_utils:suspend_if_needed(0);
`i:module_finish_unpack() ! ANY';
endfor
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878320653, \"Erik\", #74, \"JHM\"}";
.
#82:51
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {845255565, \"Ken\", #75, \"JHM\"}";
.
#82:52
if ($perm_utils:controls(player, this))
if (idx = index(dobjstr, "="))
objstr = dobjstr[1..idx - 1];
evalstr = $string_utils:trim(dobjstr[idx + 1..$]);
else
objstr = dobjstr;
evalstr = dobjstr;
endif
dobj = player:my_match_object(objstr);
if ($command_utils:object_match_failed(dobj, objstr))
return;
endif
if (verb == "@undefine")
if (idx = $List_utils:iassoc(dobj, this.defines))
this:set_defines(listdelete(this.defines, idx));
return player:tell("Undefined ", $string_utils:nn(dobj), ".");
else
return player:tell($string_utils:nn(dobj), " not defined.");
endif
endif
if (`$no_one:eval(evalstr)[2] ! ANY' != dobj)
if (verb == "@force-define")
player:tell("Warning: ", evalstr, " does not evaluate to ", $string_utils:nn(dobj), ".");
else
return player:tell("Error: ", evalstr, " does not evaluate to ", $string_utils:nn(dobj), ".");
endif
endif
if (i = $list_utils:iassoc(dobj, this.defines))
this:set_defines(listset(this.defines, evalstr, i));
else
this:set_defines(listappend(this.defines, {dobj, evalstr}));
endif
player:Tell("Defined ", $string_utils:nn(dobj), " as ", evalstr);
else
player:tell(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {877467322, \"Erik\", #74, \"JHM\"}";
.
#82:53
"for now, just player:tell";
player:tell(tostr(@args));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {845664939, \"Ken\", #75, \"JHM\"}";
.
#82:55
{o} = args;
set_task_perms(caller_perms());
refs = {};
for p in ($object_utils:all_properties(o))
if (!is_clear_property(o, p))
if (r = this:dependencies_for_value(o.(p)))
$command_utils:suspend_if_needed(0);
refs = {@refs, @r};
endif
endif
endfor
if (!this:obj_name(parent(o)))
refs = setadd(refs, parent(o));
endif
return refs;
"Metadata 202106";
"Last-Modify: {878337173, \"Erik\", #74, \"JHM\"}";
.
#82:56
{value} = args;
if (typeof(value) == OBJ)
try
if (this:obj_name(value))
return {};
else
return {value};
endif
except (E_INVIND)
return {value};
endtry
elseif (typeof(value) == LIST)
return $list_utils:remove_duplicates($list_utils:flatten($list_utils:map_arg(this, "dependencies_for_value", value)));
else
"well, if it isn't an object or a list, it doesn't contain objects";
return {};
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {846654460, \"Ken\", #75, \"JHM\"}";
.
#82:57
"Usage:  :obj_name()";
{o} = args;
if (o == #0)
return "#0";
elseif (o < #0)
return tostr(o);
elseif (!$recycler:valid(o))
raise(E_INVIND, "Invalid object " + tostr(o));
elseif (i = o in this.objects)
return this.object_names[i];
elseif (i = o in this.duplicated_objects)
return this.object_names[i];
elseif (i = o in this.locals)
return "local_" + this.local_names[i];
elseif (o == this)
return "module";
elseif ((a = $list_utils:assoc(o, this.defines)) && match(a[2], "%$[a-zA-Z0-9]+"))
return a[2];
elseif (is_player(o))
return "";
else
return "";
endif
return "";
"Metadata 202106";
"Last-Modify: {878322453, \"Erik\", #74, \"JHM\"}";
.
#82:58
set_task_perms(player);
depends = {};
for i in (this.objects)
depends = $set_utils:union(depends, this:dependencies_for_object(i));
$command_utils:suspend_if_needed(0);
endfor
this:display_dependencies(depends);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253954, \"Erik\", #74, \"JHM\"}";
.
#82:59
"Usage:  :display_dependencies(depends)";
"";
"Show the table of dependencies remaining in the module.";
depends = args[1];
if (caller_perms() != player)
raise(E_PERM);
endif
{depends} = args;
lines = {};
curos = $list_utils:slice(this.defines);
curns = $list_utils:slice(this.defines, 2);
for item in (depends)
if (i = item in curos)
defined_as = curns[i];
else
defined_as = "(undefined)";
endif
lines = {@lines, {valid(item) ? item:name() | "", tostr(item), defined_as}};
endfor
player:notify_lines($string_utils:columnize_with_headers({"Object", "Id", "Defined As"}, lines));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253931, \"Erik\", #74, \"JHM\"}";
.
#82:60
set_task_perms(callers() ? caller_perms() | player);
{sourcestr, @sourceargs} = $string_utils:explode(iobjstr);
src = this:match_source(sourcestr);
if (this:match_source_failed(src, sourcestr))
return;
endif
src = src[1];
pt = src:parse_commandline(@sourceargs);
if (typeof(pt) != OBJ)
return player:tell(pt);
endif
try
this:set_made_for_update({});
this:set_todo_for_update({});
pt:fetch();
this:update_from(pt);
except v (ANY)
player:tell_lines($code_utils:format_traceback(v[4], v[1], v[2]));
player:Tell("(Cleaning up...)");
$recycler:_recycle(pt);
made = this.made_for_update;
for i in [0..length(made) - 1]
$command_utils:suspend_if_needed(0);
zog = made[length(made) - i];
if (typeof(zog) == OBJ)
if (is_player(zog))
$wiz_utils:unset_player(zog);
endif
$recycler:_recycle(zog);
if (zog in this.objects)
this:remove_object(zog);
endif
else
if (zog[1] == "set_player")
wot = zog[2];
isplayer = zog[3];
if ($object_utils:isa(wot, $Player))
$wiz_utils:set_player(wot, isplayer);
else
set_player_flag(wot, isplayer);
endif
elseif (zog[1] == "set")
"reverse property set";
zog[2].(zog[3]) = zog[4];
elseif (typeof(zog[1]) == STR)
"zog[1] is the name of a reversal function, [2..$] are its args";
call_function(@zog);
elseif (typeof(zog[1]) == OBJ)
"likewise, object, verb, @args";
zog[1]:(zog[2])(zog[3..$]);
endif
endif
endfor
return;
endtry
$recycler:_recycle(pt);
player:tell("Done.");
return;
"Metadata 202106";
"Last-Modify: {893779953, \"Erik\", #74, \"JHM\"}";
.
#82:61
"Usage:  :update_from()";
{pt} = args;
set_task_perms(caller_perms());
modheaders = pt:read_headers();
{name, date, format, boundry, boundary, objects, locals} = pt:parse_headers({"Name", "Date", "Type", "Boundry", "Boundary", "Objects", "Locals"}, modheaders);
if (typeof(boundary) != STR)
"historical accident.";
boundary = boundry;
endif
"i have NO IDEA WHY, but this seems to be necessary to make the subsequent :yes_or_no calls work.";
suspend(0);
if (this.name != name)
player:tell("Are you sure you want to update ", this.name, " from ", name, "?");
if (!$command_utils:yes_or_no())
return;
endif
endif
"check dependency list against current dependencies";
"check object list against current objects";
"create new objects if needed";
"note the number of verbs on each of the existing objects";
"note the list of existing properties on each object";
"note a list of objects to be recycled";
"for each module item,";
"  if it is an object,";
"    check and adjust owner, name, flags; record undo info";
"    note to chparent and/or move if needed";
"  if it is a verb,";
"    add it";
"  if it is a defined property,";
"    if it is already on the object,";
"      remove it from the list of existing properties on the object";
"    otherwise,";
"      note to add it";
"  if it is an inherited property,";
"    note to change it";
"delete each property that existed previously and is not in the new module; record undo info";
"chparent objects; record undo info";
"add defined properties from above; record undo info";
"set inherited properties only if they are not clear now; record undo info";
"for each object, delete the verbs that were on the object previously";
"move objects that need to be moved; record undo info";
"for each object to be removed,";
"  if the user agrees it should be recycled,";
"    recycle it; record undo info";
"remove obsolete objects from the module's object list";
"for each object in the module,";
"  if it has a :module_finish_update verb,";
"    call it.";
"";
"Phase pre-I: check local dependencies";
localnames = $string_utils:explode((typeof(locals) == STR) ? locals | "");
oldlocals = this.local_names;
for lname in (localnames)
if (lname in oldlocals)
oldlocals = setremove(oldlocals, lname);
else
result = $no_one:eval_d($command_utils:read(("an expression that evaluates to the `" + lname) + "' object"));
if (result[1])
o = result[2];
if ((typeof(o) == OBJ) && $recycler:valid(o))
undo = {this, "remove_local", o};
this:add_local(o, lname);
this:made_for_update(undo);
else
raise(E_INVARG, "Dependency must be a valid object.");
endif
elseif (result[2])
raise(E_INVARG, result[2][1]);
else
raise(result[2]);
endif
endif
endfor
for lname in (oldlocals)
o = this.locals[lname in this.local_names];
undo = {this, "add_local", o, lname};
this:remove_local(o, lname);
this:made_for_update(undo);
endfor
"Phase I: check object list";
oldobjects = this.object_names;
plists = {};
for oname in ($string_utils:explode(objects))
$command_utils:suspend_if_needed(0);
if (oname in oldobjects)
oldobjects = setremove(oldobjects, oname);
o = this.objects[oname in this.object_names];
this:todo_for_update({"verbs", o, length(verbs(o))});
for p in (properties(o))
plists = {@plists, {o, p}};
endfor
else
if (typeof(o = $recycler:_create($root_class)) != OBJ)
raise("Creation error", "Unable to create object");
else
this:made_for_update(o);
this:add_object(o, oname);
endif
endif
endfor
this:todo_for_update({"oldobjects", oldobjects});
this:set_props_for_update(plists);
"Phase II: read items";
while (headers = pt:read_headers())
{t} = pt:parse_headers({"Type"}, headers);
this:("update_" + t)(headers, pt:read_body(boundary), pt);
$command_utils:suspend_if_needed(0);
endwhile
"Phase III: perform deferred operations";
player:tell("WARNING:  Actions may be performed now that will be impossible to undo automatically.  Do you want to continue?");
if (!$command_utils:yes_or_no())
raise("user abort");
endif
"delete properties";
for prop in (this.props_for_update)
{o, p} = prop;
undo = {"add_property", o, p, o.(p), property_info(o, p)};
delete_property(o, p);
this:made_for_update(undo);
$command_utils:suspend_if_needed(0);
endfor
"chparent objects";
for action in (this.todo_for_update)
if (action[1] == "chparent")
o = action[2];
undo = {"chparent", o, parent(o)};
chparent(@action[2..$]);
this:made_for_update(undo);
endif
$command_utils:suspend_if_needed(0);
endfor
"add properties";
for action in (this.todo_for_update)
if (action[1] == "add_property")
add_property(@action[2..$]);
this:made_for_update({"delete_property", @action[2..3]});
endif
$command_utils:suspend_if_needed(0);
endfor
"set properties";
for action in (this.todo_for_update)
if (action[1] == "set")
{function, o, p, value} = action;
if (is_clear_property(o, p))
o.(p) = value;
this:made_for_update(clear_property(o, p));
else
player:tell("Warning: Not updating ", o, ".", p, " to ", toliteral(value), ".");
endif
endif
$command_utils:suspend_if_needed(0);
endfor
"delete verbs";
for action in (this.todo_for_update)
if (action[1] == "verbs")
{function, o, n} = action;
newverbs = length(verbs(o)) - n;
for i in [1..n]
"this can be expected to break.  argh.";
undo = {{"set_verb_code", o, ((newverbs + n) - i) + 1}, {"add_verb", o, verb_info(o, 1), verb_args(o, 1)}};
delete_verb(o, 1);
for item in (undo)
this:made_for_update(item);
endfor
endfor
endif
$command_utils:suspend_if_needed(0);
endfor
"move objects";
for action in (this.todo_for_update)
if (action[1] == "move")
{function, o, where} = action;
undo = {"move", o, o.location};
move(o, where);
this:made_for_update(undo);
endif
$command_utils:suspend_if_needed(0);
endfor
"recycle objects";
for oname in (oldobjects)
o = this.objects[oname in this.object_names];
this:remove_object(o);
this:made_for_update({this, "add_object", o, oname});
player:tell("Recycle ", $string_utils:nn(o), "?  (This can't be undone.)");
if ($command_utils:yes_or_no())
$recycler:_recycle(o);
endif
$command_utils:suspend_if_needed(0);
endfor
"Phase IV: per-object predefined update methods";
this:finish_update();
return;
"Copied from generic module (#82):unpack_from by Erik (#2) Fri Jul 18 16:02:47 1997 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878336378, \"Erik\", #74, \"JHM\"}";
.
#82:62
"Usage:  :made_for_update(item)";
"";
item = args[1];
if (caller != this)
raise(E_PERM);
endif
this.(verb) = {@this.(verb), item};
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869516145, \"Erik\", #74, \"JHM\"}";
.
#82:63
"Usage:  :unpack_object()";
{headers, body, pt} = args;
if (caller != this)
return;
endif
set_task_perms(caller_perms());
{typ, reference, owner, p, name, where, flgs} = pt:parse_headers({"Type", "Reference", "Owner", "Parent", "Name", "Location", "Flags"}, headers);
wot = this:resolve_name(reference);
prnt = this:resolve_name(p);
ownr = this:resolve_owner(owner);
this:todo_for_update({"chparent", wot, prnt});
if (ownr != wot.owner)
$wiz_utils:set_owner(wot, ownr);
this:made_for_update({$wiz_utils, "set_owner", wot, ownr});
endif
if (name != wot.name)
wot:set_name(name);
this:made_for_update({wot, "set_name", name});
endif
if (flgs)
flags = $string_utils:explode(flgs);
else
flags = {};
endif
for z in ({"programmer", "r", "f"})
set = z in flags;
"double negative ensures 1 or 0, i.e., we test only truth value";
if ((!set) != (!wot.(z)))
wot.(z) = set;
this:made_for_update({"set", wot, z, !set});
endif
endfor
isplayer = "player" in flags;
if ((!isplayer) != (!is_player(wot)))
if ($object_utils:isa(wot, $Player))
$wiz_utils:set_player(wot, isplayer);
else
set_player_flag(wot, isplayer);
endif
this:made_for_update("set_player", wot, !isplayer);
endif
if (where)
loc = this:resolve_name(where);
if (loc != where)
this:todo_for_update({"move", wot, loc});
endif
endif
return;
"Copied from generic module (#82):unpack_object by Erik (#2) Fri Jul 18 17:19:45 1997 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878335828, \"Erik\", #74, \"JHM\"}";
.
#82:64
"Usage:  :update_verb()";
{headers, body, pt} = args;
if (caller != this)
return;
endif
set_task_perms(caller_perms());
{typ, reference, name, owner, perms, vargs} = pt:parse_headers({"Type", "Object", "Name", "Owner", "Perms", "Args"}, headers);
wot = this:resolve_name(reference);
ownr = this:resolve_owner(owner);
if (!(wot in this.objects))
if (i = name in verbs(wot))
player:tell("Replace ", $string_utils:nn(wot), ":", name, "?");
if ($command_utils:yes_or_no())
undo = {"set_verb_code", wot, i, verb_code(wot, i)};
set_verb_code(wot, i, body);
this:made_for_update(undo);
return;
endif
"otherwise, go ahead and add it, modified.";
else
player:tell("warning: updating verb onto ", $string_utils:nn(wot));
endif
newnames = {};
for modname in ($string_utils:explode(name))
if ((modname in verbs(wot)) || ($object_utils:has_verb(wot, name) == {wot}))
newnames = listappend(newnames, modname + "(module)");
player:tell("warning: packed verb has same name: renaming \"", modname, "\" to \"", newnames[$], "\".");
else
newnames = listappend(newnames, modname);
endif
endfor
name = $string_utils:from_list(newnames, " ");
elseif (!valid(wot))
player:Tell("warning: no object for verb \"", name, "\"");
endif
add_verb(wot, {ownr, perms, name}, $string_utils:explode(vargs));
set_verb_code(wot, length(verbs(wot)), body);
this:made_for_update({"delete_verb", wot, length(verbs(wot))});
"Copied from generic module (#82):unpack_verb by Erik (#2) Fri Jul 18 17:51:32 1997 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869758190, \"Erik\", #74, \"JHM\"}";
.
#82:65
"Usage:  :update_property-defined()";
{headers, body, pt} = args;
if (caller != this)
return;
endif
set_task_perms(caller_perms());
{typ, reference, name, owner, perms} = pt:parse_headers({"Type", "Object", "Name", "Owner", "Perms"}, headers);
if (!perms)
perms = "";
endif
wot = this:resolve_name(reference);
ownr = this:resolve_owner(owner);
value = this:unsub_references(body[1])[2];
if ({wot, name} in this.props_for_update)
this:set_props_for_update(setremove(this.props_for_update, {wot, name}));
{oldowner, oldperms} = property_info(wot, name);
if (oldowner != ownr)
if (e = $wiz_utils:set_property_owner(wot, name, ownr))
this:made_for_update({$wiz_utils, "set_property_owner", wot, name, oldowner});
else
raise(e);
endif
endif
if (oldperms != perms)
if (e = $wiz_utils:set_property_flags(wot, name, perms))
this:made_for_update({$wiz_utils, "set_property_flags", wot, name, perms});
else
raise(e);
endif
endif
oldvalue = wot.(name);
if (value != oldvalue)
player:tell("WARNING not changing ", wot, ".", name, " from ", toliteral(oldvalue), " to ", toliteral(value));
endif
else
this:todo_for_update({"set", wot, name, value});
endif
return;
"Copied from generic module (#82):unpack_property-defined by Erik (#2) Fri Jul 18 17:55:07 1997 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {896898437, \"Erik\", #74, \"JHM\"}";
.
#82:66
"Usage:  :unpack_property-inherited()";
{headers, body, pt} = args;
if (caller != this)
return;
endif
set_task_perms(caller_perms());
{typ, reference, name} = pt:parse_headers({"Type", "Object", "Name"}, headers);
wot = this:resolve_name(reference);
value = this:unsub_references(body[1])[2];
this:todo_for_update({"set", wot, name, value});
return;
"Copied from generic module (#82):unpack_property-inherited by Erik (#2) Fri Jul 18 18:16:27 1997 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {896908902, \"Erik\", #74, \"JHM\"}";
.
#82:67
if (caller == this)
for i in (this.objects)
$command_utils:suspend_if_needed(0);
`i:module_finish_update() ! ANY';
endfor
endif
"Copied from generic module (#82):finish_unpack by Erik (#2) Mon Jul 21 14:01:34 1997 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869516146, \"Erik\", #74, \"JHM\"}";
.
#82:68
"Usage:  :init_for_module()";
"";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
this.sources[3..$] = {};
this.sinks[3..$] = {};
else
raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869519638, \"Erik\", #74, \"JHM\"}";
.
#82:69
"Usage:  :module_finish_unpack()";
"";
if (!$perm_utils:controls(caller_perms(), this))
raise(E_PERM);
endif
if ($code_utils:verb_frame()[4] == this)
try
add_property(#0, "module", this, {this.owner, "r"});
except (E_INVARG)
"there already is a $module property.";
$module = this;
endtry
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869698102, \"Erik\", #74, \"JHM\"}";
.
#82:70
"Usage:  :@add-local <object> to this";
"";
if ($perm_utils:controls(player, this))
if (phot = match(dobjstr, "%([^(]+%) (%([^(]+%))"))
{whatstr, name} = {substitute("%1", phot), substitute("%2", phot)};
else
whatstr = dobjstr;
name = "";
endif
what = player:my_match_object(whatstr);
if ($command_utils:object_match_failed(what, whatstr))
return;
elseif (!name)
player:tell("Adding local dependency ", $string_utils:nn(what), " to ", this:dname(), ".");
if (!(name = $command_utils:read("a module name for the object")))
return player:tell("Aborted.");
endif
name = strsub(name, " ", "_");
endif
player:tell("Added ", $string_utils:nn(what), " as ", name, " to ", this:dname(), ".");
this:add_local(what, name);
else
player:tell("You are not allowed to do that.");
endif
"Copied from generic module (#316):@add by Erik (#74) Fri Oct 31 11:33:55 1997 EST";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878321608, \"Erik\", #74, \"JHM\"}";
.
#82:71
"Usage:  :add_local(object, name)";
"";
"This verb is used to add a local dependency to the module.  The name here is the one used by the module system.  The name must conform to the rules for MOO identifiers.";
{object, name} = args;
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
raise(E_PERM, ((caller_perms():dnamec() + " is not allowed to add local dependencies to ") + this:dname()) + ".");
elseif (object in this.objects)
raise(E_INVARG, (object:dnamec() + " is already in ") + this:dname());
elseif (object in this.locals)
raise(E_INVARG, (object:dnamec() + " is already a local dependency in ") + this:dname());
elseif (name in this.local_names)
raise(E_INVARG, (name + " is already in ") + this:dname());
else
add_property(this, "obj_local_" + name, object, {this.owner, "r"});
this:set_locals({@this.locals, object});
this:set_local_names({@this.local_names, name});
endif
return;
"Copied from generic module (#316):add_object by Erik (#74) Fri Oct 31 13:04:26 1997 EST";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878328658, \"Erik\", #74, \"JHM\"}";
.
#82:72
"Usage:  :remove_local()";
"Usage:  :remove_local(object)";
"";
{object} = args;
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
raise(E_PERM, ((caller_perms():dnamec() + " is not allowed to remove local dependencies from ") + this:dname()) + ".");
elseif (!(i = object in this.locals))
raise(E_INVARG, (object:dnamec() + " is not in ") + this:dname());
else
delete_property(this, "obj_local_" + this.local_names[i]);
this:set_locals(listdelete(this.locals, i));
this:set_local_names(listdelete(this.local_names, i));
endif
return;
"Copied from generic module (#316):remove_object by Erik (#74) Fri Oct 31 13:07:03 1997 EST";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878322518, \"Erik\", #74, \"JHM\"}";
.
#82:73
"Usage:  :@remove()";
"Usage: @remove object from this";
"";
if ($perm_utils:controls(player, this))
whatstr = dobjstr;
if (!valid(what = this:resolve_name(whatstr)))
what = player:my_match_object(whatstr);
endif
if ($command_utils:object_match_failed(what, whatstr))
return;
elseif (!(what in this.locals))
player:tell(what:dnamec(), " is not a local dependency in ", this:dname(), ".");
else
this:remove_local(what);
player:tell("Removed ", $string_utils:nn(what), " from ", this:dname(), ".");
endif
else
player:tell("You're not allowed to do that.");
endif
return;
"Copied from generic module (#316):@remove by Erik (#74) Fri Oct 31 13:20:54 1997 EST";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878322105, \"Erik\", #74, \"JHM\"}";
.
#82:74
"Usage: :local_name(object)";
"";
"Return the name of the local dependency for the given object.";
{object} = args;
obj_name = this:obj_name(object);
if (index(obj_name, "local_") == 1)
return obj_name[7..$];
else
raise(E_INVARG, "Not a local dependency: " + object);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878322853, \"Erik\", #74, \"JHM\"}";
.
#83:0
":open(address, port, [connect-connection-to])";
"Open a network connection to address/port.  If the connect-connection-to is passed, then the connection will be connected to that object when $login gets ahold of it.  If not, then the connection is just ignored by $login, i.e. not bothered by it with $welcome_message etc.";
"The object specified by connect-connection-to has to be a player (though it need not be a $player).";
"Either way, the return is either 1) an error or 2) a pair {invalid, valid} where <invalid> is the object the initial connection is made to, and <valid> is the object that will be logged in.  (If no third arg, <valid> will be #-1.)";
"";
{address, port, ?connect_to = $nothing} = args;
if (!this:trusts(caller_perms()))
raise(E_PERM);
endif
if (valid(connect_to))
set_task_perms(caller_perms());
return this:open_connect_to(address, port, connect_to);
elseif (typeof(connection = open_network_connection(address, port)) != OBJ)
return connection;
endif
this.connection_owners = {{connection, caller_perms()}, @this.connection_owners};
return {connection, connect_to};
"Metadata 202106";
"Last-Modify: {865055227, \"Ken\", #75, \"JHM\"}";
.
#83:1
"is_connected(what) - Returns true if what is connected, false if not";
return !(typeof(idle_seconds(args[1])) == ERR);
.
#83:2
"if (!caller_perms().wizard)";
"  return E_PERM;";
"endif";
{c} = args;
if (!this:controls(caller_perms(), c))
return E_PERM;
endif
boot_player(c);
if (i = $list_utils:iassoc(c, $network.connect_connections_to))
$network.connect_connections_to = listdelete($network.connect_connections_to, i);
endif
return 1;
.
#83:3
"sendmail(to, subject, @lines)";
debugging = 0;
if (!this:sendmail_ok(caller_perms()))
return tostr(E_PERM);
endif
{to, subject, @body} = args;
fromstr = this.postmaster;
rstr = to;
headers = {{"Subject", subject}};
for i in [1..length(body)]
$command_utils:suspend_if_needed(0);
if (m = match(body[i], "^%([a-z0-9-]*%): %(.*%)$"))
headers = {@headers, $string_utils:explode_match(m)};
elseif (!body[i])
body = `body[i + 1..$] ! E_RANGE => {}';
break;
else
body = body[i..$];
break;
endif
endfor
try
result = $smtp:sendmail(this.postmaster, fromstr, rstr, {to}, {{"Subject", subject}}, body);
player:tell("*** Sent SMTP mail to ", to, ": ", result[2]);
except v (ANY)
player:tell("*** Error sending SMTP mail to ", to, ": ", v[2]);
endtry
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895723034, \"Ken\", #75, \"JHM\"}";
.
#83:4
"Given an email address, return {userid, site}.";
"Valid addresses are of the form `userid[@site]'.";
"At least for now, if [@site] is left out, site will be returned as blank.";
"Should be a default address site, or something, somewhere.";
address = args[1];
return (at = index(address, "@")) ? {address[1..at - 1], address[at + 1..length(address)]} | {address, ""};
.
#83:5
"given a site, try to figure out what the `local' domain is.";
"if site has a @ or a % in it, give up and return E_INVARG.";
"blank site is returned as is; try this:local_domain(this.localhost) for the answer you probably want.";
site = args[1];
if (index(site, "@") || index(site, "%"))
return E_INVARG;
elseif (!site)
return "";
elseif ($site_db:domain_literal(site))
return site;
else
pieces = $string_utils:to_list(site, ".");
l = length(pieces);
if (length(pieces[l]) == 2)
pieces[1..l - 3] = {};
else
pieces[1..l - 2] = {};
endif
return $string_utils:from_list(pieces, ".") || E_INVARG;
endif
.
#83:6
return args[1].wizard || (args[1] in this.sendmail_ok);
.
#83:7
"Peer at an incoming connection.  Decide if it should be connected to something, return that object. Called only by #0:do_login_command";
what = args[1];
this:clean_up();
if (index(connection_name(what), " to "))
if (ct = $list_utils:assoc(what, this.connect_connections_to))
this.open_connections = setremove(this.open_connections, what);
this.connect_connections_to = setremove(this.connect_connections_to, ct);
return ct[2];
elseif (what in this.ignore)
this:close(what);
return -1;
else
this.ignore = setadd(this.ignore, what);
return 1;
endif
else
return 0;
endif
return;
"Metadata 202106";
"Last-Modify: {832143314, \"Erik\", #74, \"JHM\"}";
.
#83:8
for i in (this.open_connections)
if (typeof(idle_seconds(i)) == ERR)
this.open_connections = setremove(this.open_connections, i);
endif
endfor
for i in (this.connect_connections_to)
if (typeof(idle_seconds(i[1])) == ERR)
this.connect_connections_to = setremove(this.connect_connections_to, i);
endif
endfor
for i in (this.ignore)
if (typeof(idle_seconds(i)) == ERR)
this.ignore = setremove(this.ignore, i);
endif
endfor
.
#83:9
"invalid_email_address(email) -- check to see if email looks like a valid email address. Return reason why not.";
address = args[1];
if (!address)
return "no email address supplied.";
endif
if (!(at = rindex(address, "@")))
return ("'" + address) + "' doesn't look like a valid internet email address.";
endif
name = address[1..at - 1];
host = address[at + 1..length(address)];
if (match(name, "^in%%"))
return tostr("'", name, "' doesn't look like a valid username (try removing the 'in%').");
endif
if (!match(host, $network.valid_host_regexp))
return tostr("'", host, "' doesn't look like a valid internet host.");
endif
if (!match(name, $network.valid_email_regexp))
return tostr("'", name, "' doesn't look like a valid user name for internet mail.");
endif
return "";
.
#83:10
":email_will_fail(email-address[, display?]) => Makes sure the email-address is one that can actually be used by $network:sendmail().";
reason = this:invalid_email_address(args[1]);
if (reason && {@args, 0}[2])
player:tell("Invalid email address: ", reason);
endif
return reason;
"following is code from OpalMOO, not used here";
"Possible situations where the address would be unusable are when the address is invalid or we can't connect to the site to send mail.";
"If <display> is true, error messages are displayed to the player and 1 is returned when address is unuable.  If <display> is false and address is unusable, the error message is returned.  If the address is usable, 0 is always returned.";
if (!this:approved_for_network(caller_perms()))
return E_PERM;
endif
"michele changed this line from:";
"if (!this:valid_email_address(email = args[1]))";
"since there is no such verb";
if (this:invalid_email_address(email = args[1]))
msg = tostr("Your email address (", email, ") is not a usable account.");
elseif ((result = this:verify_email_address(email)) == E_INVARG)
msg = tostr("Unable to connect to ", this:parse_address(email)[2], ".");
elseif (typeof(result) == STR)
msg = tostr("The site ", (parse = this:parse_address(email))[2], " does not recognize ", parse[1], " as a valid account.");
else
return 0;
endif
if ({@args, 0}[2])
player:tell(msg);
return 1;
else
return msg;
endif
"Last modified Tue Jun 15 00:19:01 1993 EDT by Ranma (#200).";
"not really, since michele changed it on may 15 1994";
.
#83:11
"Usage:  :listen(object, port)";
"";
"Start listening on the given port, attaching incoming connections to the given object.  Works basically like the listen() built-in.";
"This updates $network.listeners, when it does a successful listen.";
"";
"May raise or return an error, depending on the d status of the calling verb.";
0;
"";
"On JHM, this verb allows any object to listen for itself (i.e., args[1] == caller), without needing wizard permissions.";
"(only we took it off so ppl couldn't hack the server --*)";
"if (caller != args[1])";
set_task_perms(caller_perms());
"endif";
return listen(@args);
.
#83:12
"Usage:  :unlisten(port)";
"";
"Stop listening on the given port.  Should probably check that the calling object is the one listening on said port, but insecurity is cool.  We update $network.listeners when we've unlistened successfully.";
"";
"May raise or return an error, depending on the d status of the calling verb.";
return unlisten(@args);
.
#83:13
if ($__core_init_phase)
pass(@args);
this.active = 0;
this.sendmail_ok = {};
this.site = "yoursite";
this.postmaster = "postmastername@yourhost";
this.MOO_name = "YourMOO";
this.port = 7777;
this.httpd_prefix = "not-a-url:////";
this.ignore = this.connect_connections_to = {};
this.connection_owners = {};
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {854480471, \"Ken\", #75, \"JHM\"}";
.
#83:14
"Usage:  :reply_address(user)";
"";
"Return a string with the reply address for the person in question.";
user = args[1];
if (!($object_utils:isa(user, $player) && is_player(user)))
user = user.owner;
endif
return user:misc_option("public_email") ? user:email_address() | tostr(user.name, "@", this.mail_domain);
"Metadata 202106";
"Last-Modify: {863645180, \"Erik\", #74, \"JHM\"}";
.
#83:15
"This returns a list of listening objects.  It does not remove duplicates.";
caller_perms().wizard || raise(E_PERM);
return $list_utils:slice(listeners());
.
#83:16
"This returns a list of ports that are being listened on.";
caller_perms().wizard || raise(E_PERM);
return $list_utils:slice(listeners(), 2);
.
#83:17
{con, @rest} = args;
builtin = verb;
if (!this:controls(caller_perms(), con))
return E_PERM;
else
return call_function(builtin, con, @rest);
endif
.
#83:18
"Open a network connection and connect it someplace.";
{address, port, connect_to} = args;
if (caller != this)
"i'd rather use exceptions here, but I don't feel like going and fixing all the code that assumes it will return errors right now";
return E_PERM;
elseif (!valid(connect_to))
return E_INVARG;
elseif (!$perm_utils:controls(caller_perms(), connect_to))
return E_PERM;
elseif ($login:is_newted(connect_to))
return E_PERM;
elseif (typeof(connection = open_network_connection(address, port)) != OBJ)
return connection;
else
this.connect_connections_to = {@this.connect_connections_to, {connection, connect_to}};
return {connection, connect_to};
endif
return;
"Metadata 202106";
"Last-Modify: {833214140, \"Ken\", #75, \"JHM\"}";
.
#83:19
{who, what} = args;
if (!(a = $list_utils:assoc(what, this.connection_owners)))
return 0 && callers()[2][3].wizard;
endif
{con, perms} = a;
return $perm_utils:controls(who, perms);
.
#83:20
{c} = args;
if (caller != #0)
raise(E_PERM);
endif
while (i = $list_utils:iassoc(c, this.connection_owners))
this.connection_owners = listdelete(this.connection_owners, i);
endwhile
while (i = $list_utils:iassoc(c, this.connect_connections_to))
this.connect_connections_to = listdelete(this.connect_connections_to, i);
endwhile
return;
.
#83:21
{who} = args;
return who.wizard;
"Metadata 202106";
"Last-Modify: {913134421, \"core initialization\", #3023, \"core\"}";
.
#84:0
{who} = args;
return who.wizard || (who in this.trusts);
"Metadata 202106";
"Last-Modify: {878783884, \"Ken\", #75, \"JHM\"}";
.
#84:1
{?host = $network.maildrop, ?port = 25, ?helo = host} = args;
if (caller == this)
if (typeof(attempt = $network:open(host, port)) != LIST)
raise(this.error, tostr(("Error connecting to maildrop " + host) + ":", port, ": ", attempt));
else
connection = attempt[1];
ready = this:get_reply(connection, {"220"});
this:send_HELO(connection, helo);
return {connection, ready};
endif
else
raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878789861, \"Ken\", #75, \"JHM\"}";
.
#84:2
{connection, ?ok = {"250"}} = args;
if (caller != this)
raise(E_PERM);
endif
try
message = read(connection);
except (E_INVARG)
raise(this.error, "Connection prematurely terminated");
endtry
multiline = 0;
try
code = message[1..3];
if (length(message) > 3)
rest = message[5..$];
multiline = message[4] == "-";
else
rest = "";
endif
if (multiline)
rest = this:_get_multiline_reply(connection, code, rest);
endif
if (this.debugging)
player:tell("SMTP S->C: " + toliteral({code, rest}));
endif
if (code in ok)
"ok";
return {code, rest};
else
"not ok";
raise(this.error_code, rest, {code, rest});
endif
except (E_RANGE)
raise(this.error, "Unexpected return value: " + message);
endtry
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878789613, \"Ken\", #75, \"JHM\"}";
.
#84:3
{connection, forward_path} = args;
if (caller != this)
raise(E_PERM);
endif
this:send(connection, ("RCPT TO:<" + forward_path) + ">");
return this:get_reply(connection);
"Metadata 202106";
"Last-Modify: {878789432, \"Ken\", #75, \"JHM\"}";
.
#84:4
{connection, reverse_path} = args;
if (caller != this)
raise(E_PERM);
endif
this:send(connection, ("MAIL FROM:<" + reverse_path) + ">");
return this:get_reply(connection);
"Metadata 202106";
"Last-Modify: {878789444, \"Ken\", #75, \"JHM\"}";
.
#84:5
{connection, data} = args;
if (caller != this)
raise(E_PERM);
endif
this:send(connection, "DATA");
if (this:get_reply(connection, {"354"}))
for line in (data)
$command_utils:suspend_if_needed(0);
if (line == ".")
line = "..";
endif
while (!this:send(connection, line))
suspend(1);
endwhile
endfor
this:send(connection, ".");
endif
return this:get_reply(connection);
"Metadata 202106";
"Last-Modify: {878789490, \"Ken\", #75, \"JHM\"}";
.
#84:6
{connection, ?thishost = $network.maildrop} = args;
if (caller != this)
raise(E_PERM);
endif
this:send(connection, "HELO " + thishost);
return this:get_reply(connection);
"Metadata 202106";
"Last-Modify: {878789505, \"Ken\", #75, \"JHM\"}";
.
#84:7
{connection, address} = args;
if (caller != this)
raise(E_PERM);
endif
this:send(connection, "VRFY " + address);
return this:get_reply(connection);
"Metadata 202106";
"Last-Modify: {878789517, \"Ken\", #75, \"JHM\"}";
.
#84:8
{connection} = args;
if (caller != this)
raise(E_PERM);
endif
this:send(connection, "RSET");
return this:get_reply(connection);
"Metadata 202106";
"Last-Modify: {878789528, \"Ken\", #75, \"JHM\"}";
.
#84:9
if (caller != this)
raise(E_PERM);
endif
{connection, code, firstline} = args;
lines = {firstline};
reading = 1;
while (reading)
try
message = read(connection);
except (E_INVARG)
raise(this.error, "Connection prematurely terminated");
endtry
if (length(message) < 5)
raise(this.error, "Unexpected message format");
elseif (message[4] != "-")
reading = 0;
endif
line = message[5..$];
lines = {@lines, line};
endwhile
return lines;
"Metadata 202106";
"Last-Modify: {878788745, \"Ken\", #75, \"JHM\"}";
.
#84:10
{connection, address} = args;
if (caller != this)
raise(E_PERM);
endif
this:send(connection, "EXPN " + address);
return this:get_reply(connection);
"Metadata 202106";
"Last-Modify: {878789540, \"Ken\", #75, \"JHM\"}";
.
#84:11
{connection, address} = args;
if (caller != this)
raise(E_PERM);
endif
this:send(connection, "NOOP");
return this:get_reply(connection);
"Metadata 202106";
"Last-Modify: {878789553, \"Ken\", #75, \"JHM\"}";
.
#84:12
{connection} = args;
if (caller != this)
raise(E_PERM);
endif
this:send(connection, "QUIT");
try
return this:get_reply(connection, {"221"});
finally
boot_player(connection);
endtry
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878789961, \"Ken\", #75, \"JHM\"}";
.
#84:13
{?from = $network.postmaster, ?host = $network.maildrop, ?port = 25, recipients, message_body} = args;
if (!this:trusts(caller_perms()))
raise(E_PERM);
endif
{connection, greeting} = this:connect(host, port);
this:send_MAIL(connection, from);
for recipient in (recipients)
this:send_RCPT(connection, recipient);
endfor
result = this:send_DATA(connection, message_body);
this:send_QUIT(connection);
return result;
"Metadata 202106";
"Last-Modify: {913064791, \"Ken\", #75, \"JHM\"}";
.
#84:14
{connection, line} = args;
if (caller == this)
if (this.debugging)
player:tell("SMTP C->S: " + line);
endif
return notify(connection, line);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {878789407, \"Ken\", #75, \"JHM\"}";
.
#84:15
"$smtp:sendmail(?from = $network.postmaster, to = list of addresses, headers, body of message) => result";
"to = list of email addresses";
{?from = $network.postmaster, ?fromstr = from, ?recptstring = $string_utils:from_list(recipients, ", "), recipients, headers, body} = args;
if (!this:trusts(caller_perms()))
raise(E_PERM);
endif
fromhdr = "From: " + fromstr;
tohdr = "To: " + recptstring;
datehdr = "Date: " + $time_utils:time_sub(this.time_format, time());
if (subject = $list_utils:assoc("Subject", headers))
subject = subject[2];
subjecthdr = tostr("Subject: ", subject ? subject | "(no subject)");
else
subjecthdr = "Subject: (no subject)";
endif
hdrs = {subjecthdr};
for header in (headers)
{name, value} = header;
if (name != "Subject")
hdrs = {@hdrs, tostr(name, ": ", value)};
endif
endfor
agenthdr = tostr("X-Mail-Agent: ", $network.moo_name, " (", $network.site, " ", $network.port, ")");
hdrs = {@hdrs, agenthdr};
message = {datehdr, fromhdr, tohdr, @hdrs, "", @body};
"return this:raw_sendmail(from, recipients, message);";
return this:add_queued_message(from, recipients, message);
"Metadata 202106";
"Last-Modify: {913064832, \"Ken\", #75, \"JHM\"}";
.
#84:16
if (caller == this)
if ($code_utils:task_valid(this.queued_mail_task))
return;
endif
fork t (0)
while (this.queued_messages)
message = this.queued_messages[1];
if (length(message) == 2)
message = message[2];
endif
this.queued_messages = listdelete(this.queued_messages, 1);
try
result = this:raw_sendmail(@message);
except v (ANY)
this.queued_messages = {@this.queued_messages, {v, message}};
endtry
suspend(this.queue_interval);
endwhile
endfork
this.queued_mail_task = t;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {913048019, \"Ken\", #75, \"JHM\"}";
.
#84:17
if (caller == this)
message = args;
this.queued_messages = {@this.queued_messages, message};
this:send_queued_mail();
return {"250", "Message queued locally for delivery via " + $network.maildrop};
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {913048222, \"Ken\", #75, \"JHM\"}";
.
#85:0
"@login-option <option> [is] <value>   sets <option> to <value>";
"@login-option <option>=<value>        sets <option> to <value>";
"@login-option +<option>     sets <option>   (usually equiv. to <option>=1";
"@login-option -<option>     resets <option> (equiv. to <option>=0)";
"@login-option !<option>     resets <option> (equiv. to <option>=0)";
"@login-option <option>      displays value of <option>";
"";
"@login-options for a listing of available options.";
option_pkg = this.options;
if ((caller != player) || (caller_perms() != player))
raise(E_PERM, "Unexpected caller");
endif
if (!args)
player:tell_lines({"Current login options:", "", @option_pkg:show(this:get_login_options(player), option_pkg.names)});
return;
elseif (typeof(presult = option_pkg:parse(args)) == STR)
player:tell(presult);
return;
else
if (length(presult) > 1)
if (typeof(sresult = this:set_login_option(player, @presult)) == STR)
player:tell(sresult);
return;
elseif (!sresult)
player:tell("No change.");
return;
endif
endif
player:tell_lines(option_pkg:show(this:get_login_options(player), presult[1]));
endif
"Copied from generic player (#6):@misc-o by Xythian (#77) Thu May  2 20:56:02 1996 EDT";
.
#85:1
if (caller != this)
raise(E_PERM);
endif
{who, ?option = 0} = args;
propname = this:options_propname(who);
if (option)
return this.options:get(`this.(propname)[2] ! E_PROPNF => {}', option);
else
return this.(propname)[2];
endif
.
#85:2
{who, @option} = args;
if (caller != this)
raise(E_PERM);
endif
propname = this:options_propname(who);
if (((option[1] == "interest") && option[2]) && (!this:interesting(who)))
return "You must have an interesting list to restrict messages to the people you think are interesting.";
endif
result = this.options:set(this.(propname)[2], @option);
if (typeof(result) == STR)
return result;
elseif (equal(result, this.(propname)[2]))
return 0;
else
this.(propname)[2] = result;
return 1;
endif
return;
"Metadata 202106";
"Last-Modify: {832018387, \"Ken\", #75, \"JHM\"}";
.
#85:3
{who} = args;
if (caller != this)
raise(E_PERM);
endif
this.users = {@this.users, who};
add_property(this, propname = this:options_propname(who), {{}, {}}, {this.owner, ""});
"migrate old options for now";
`this:migrate_from_old_watcher(who) ! ANY';
return 1;
"Metadata 202106";
"Last-Modify: {833067543, \"Ken\", #75, \"JHM\"}";
.
#85:4
{who} = args;
if (caller != this)
raise(E_PERM);
endif
if (index = who in this.users)
this.users = listdelete(this.users, index);
`delete_property(this, this:options_propname(who)) ! E_PROPNF';
endif
return 1;
.
#85:5
{who, what} = args;
if (caller != this)
raise(E_PERM);
endif
propname = this:options_propname(who);
old = this.(propname)[1];
if (what && (!old))
this:set_login_option(who, "interest", 1);
elseif (!what)
this:set_login_option(who, "interest", 0);
endif
return this.(propname)[1] = what;
.
#85:6
{who} = args;
if (caller != who)
raise(E_PERM);
endif
return this:add_user(who);
.
#85:7
{who} = args;
if (caller != who)
raise(E_PERM);
endif
return this:remove_user(who);
.
#85:8
{who} = args;
if (caller != this)
raise(E_PERM);
endif
return this.(this:options_propname(who))[1];
.
#85:9
{who} = args;
return tostr("_options_", who);
.
#85:10
"Usage: @inter*esting [player] ...";
"Declares players interesting to you.";
"Displays current list of interesting players if called with no arguments.";
"An empty interesting list means you see all logins/logouts.";
if ((length(args) == 1) && (args[1] == "everyone"))
this:set_interesting(player, {});
player:tell("Flushed your list of especially interesting people. Login watcher will now report all logins/logouts.");
return;
endif
interesting = this:interesting(player);
for who in (interesting)
if ((!valid(who)) || (!is_player(who)))
interesting = setremove(interesting, who);
player:tell(tostr(who), " removed from your interesting list.");
endif
endfor
this:set_interesting(player, interesting);
if (!args)
if (length(interesting) == 0)
player:tell("Your list of interesting people is empty -- all players who login and logout will be announced.");
return;
else
player:tell("Currently, you think only ", $string_utils:dname_list(interesting, "nobody"), (length(interesting) > 1) ? " are" | " is", " interesting.");
endif
else
players = listdelete($command_utils:player_match_result(player:my_match_player(args), args), 1);
already = $set_utils:intersection(interesting, players);
if (already)
player:tell("You already think ", $string_utils:dname_list(already), (length(already) > 1) ? " are" | " is", " interesting.");
endif
players = $set_utils:diff(players, already);
this:set_interesting(player, {@this:interesting(player), @players});
player:tell("Added ", $string_utils:dname_list(players, "nobody"), " to your list of interesting players.");
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669847, \"Erik\", #74, \"JHM\"}";
.
#85:11
"Usage: @unint*eresting player [player ...]";
"Declares players to be uninteresting to you.  (I.e. removes them from your .interesting list.)";
if (!args)
player:tell("Usage: @unint*eresting player [player ...]");
player:tell("       @unint*eresting everyone -- erases your interesting persons list.");
else
interesting = this:interesting(player);
if ((length(args) == 1) && (args[1] == "everyone"))
this:set_interesting(player, {});
player:tell("Flushed your list of especially interesting people. Login watcher will now report all logins/logouts.");
return;
endif
for who in (interesting)
if ((!valid(who)) || (!is_player(who)))
interesting = setremove(interesting, who);
player:tell(tostr(who), " removed from your interesting list.");
endif
endfor
this:set_interesting(player, interesting);
players = listdelete($command_utils:player_match_result(player:my_match_player(args), args), 1);
remove = $set_utils:intersection(players, interesting);
already = $set_utils:diff(players, remove);
if (already)
player:tell("You already think that ", $string_utils:dname_list(already), (length(already) > 1) ? " are" | " is", " uninteresting.");
endif
this:set_interesting(player, $set_utils:diff(this:interesting(player), remove));
player:tell("Removed ", $string_utils:dname_list(remove, "nobody"), " from your list of interesting players.");
endif
"Copied from login watcher (#118):@uninteresting by Xythian (#77) Thu May  2 22:30:51 1996 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863669853, \"Erik\", #74, \"JHM\"}";
.
#85:12
if (caller != #0)
return E_PERM;
endif
user = args[1];
if ($object_utils:isa(user, $player))
this:announce2("connected", user);
endif
"Copied from login watcher (#118):hello by Xythian (#77) Thu May  2 22:34:20 1996 EDT";
.
#85:13
if (caller != #0)
return E_PERM;
endif
user = args[1];
if (!valid(user))
return E_ARGS;
endif
if ($object_utils:isa(user, $player))
this:announce2("disconnected", user);
endif
"Copied from login watcher (#118):goodbye by Xythian (#77) Thu May  2 22:34:24 1996 EDT";
.
#85:14
if ((caller != this) || ($code_utils:verb_location() != this))
raise(E_PERM);
endif
{message, who} = args;
`this.old_login_watcher:(verb)(@args) ! ANY';
fork (0)
has = who:verb_sub("has");
options = this.options;
standard_msg = options.default_message;
standard = options:sub_message(standard_msg, who, message);
for i in (this.users)
$command_utils:suspend_if_needed(0);
if (!this:login_option(i, "on"))
continue;
endif
idle = this:login_option(i, "idle") || $maxint;
if ((valid(i) && (typeof(`idle_time = idle_seconds(i) ! E_INVARG') != ERR)) && (i.location != who.location))
if (idle_time > idle)
continue;
endif
whoint = $object_utils:isa(who, $guest) ? $string_utils:match_player("guest") | who;
interested = (!this:login_option(i, "interest")) || (whoint in this:interesting(i));
(who in this.uninteresting) && (interested = 0);
if (interested)
msg = this:login_option(i, "message") || standard_msg;
if (msg != standard_msg)
msg = options:sub_message(msg, who, message, i);
else
msg = standard;
endif
i:tell(msg);
endif
endif
endfor
endfork
return;
"Metadata 202106";
"Last-Modify: {833067470, \"Ken\", #75, \"JHM\"}";
.
#85:15
"Usage: @login [on|off|all|interesting]";
"Without arguments, tells you your current login-watcher settings.";
"@login on and @login off do the obvious.  If `all' is specified, sets the login watcher to show all login announcements, even those for people not in your interesting list.  If `interesting' is specified, sets the login watcher to show only login announcements for people in your interesting list.";
if ($object_utils:isa(player, $guest))
player:tell("Sorry, guests cannot use the login watcher.");
return;
endif
if (!(argstr in {"", "on", "off", "all", "interesting"}))
return player:tell("Usage: @login [on|off|all|interesting]");
endif
oldinterest = this:login_option(player, "interest");
oldon = this:login_option(player, "on");
if (argstr == "on")
this:set_login_option(player, "on", 1);
elseif (argstr == "off")
this:set_login_option(player, "on", 0);
elseif (argstr == "interesting")
this:set_login_option(player, "on", 1);
if (this:interesting(player))
this:set_login_option(player, "interest", 1);
else
return player:Tell("You have no interesting list.  You must have an interesting list before you can set this option.");
endif
elseif (argstr == "all")
this:set_login_option(player, "on", 1);
this:set_login_option(player, "interest", 0);
endif
nowon = this:login_option(player, "on");
nowinterest = this:login_option(player, "interest");
changed = !((nowon == oldon) && (nowinterest == oldinterest));
now = changed ? "now " | "";
if (nowon && nowinterest)
player:tell("You are ", now, "currently listening to login watcher announcements for interesting players.");
elseif (nowon)
player:tell("You are ", now, "currently listening to login watcher announcements for everyone.");
else
player:tell("You are ", now, "not listening to login watcher announcements.");
endif
.
#85:16
return !$object_utils:isa(args[1], $guest);
.
#85:17
return {this.options, @pass(@args)};
.
#85:18
if ($__core_init_phase)
for user in (this.users)
this:remove_user(user);
$command_utils:suspend_if_needed(0);
endfor
delete_property(this, "old_login_watcher");
delete_verb(this, "migrate_from_old_watcher");
return pass(@args);
endif
return;
"Metadata 202106";
"Last-Modify: {833067563, \"Ken\", #75, \"JHM\"}";
.
#85:19
"Usage: @wwho";
"Displays the @who output for those players which you have marked as interesting to you (see $login_watcher:@interesting).";
"@wwho all toggles whether you see all the players including the disconnected ones or not.";
if (valid(caller) && (caller != player))
return E_PERM;
endif
if (!(player in this.users))
player:tell("You don't appear to be a user of ", this:dname(), ".");
player:tell("@add-feature login watcher to begin using it.");
elseif (!(interesting = this:interesting(player)))
player:tell(verb, " is used to list players who are in your login watcher interesting list. You haven't set anyone as interesting. Use @interesting <player> to start your list.");
return;
elseif (!((argstr == "all") || (interesting = $set_utils:intersection($code_utils:connected_players(), interesting))))
player:tell("No interesting people connected.");
else
$code_utils:show_who_listing(interesting, {}, player);
endif
.
#86:0
args[2]:contribute_partial(args[1]);
.
#86:1
args[2]:contribute_partial("{~ ", args[1], "}");
.
#86:2
args[2]:contribute_partial("(text ", $string_utils:print(args[1]), ")");
.
#86:3
"Given a string, output a string that has HTML entities in place of reserved characters.";
line = args[1];
target = args[2];
line = strsub(line, "&", "&amp;");
line = strsub(line, "<", "&lt;");
target:contribute_partial(strsub(line, ">", "&gt;"));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835904767, \"Erik\", #74, \"JHM\"}";
.
#86:4
return {@pass(@args), $string_utils};
.
#87:0
return pass(@args) && (!$object_utils:isa(args[1], $guest));
.
#88:0
list_of_strings = args[1];
target = args[2];
for string in (list_of_strings)
target:contribute_partial(string);
target:contribute_newline();
target:contribute_newline();
endfor
.
#88:1
list_of_strings = args[1];
target = args[2];
for string in (list_of_strings)
target:contribute_partial(("(text \"     o  " + string) + "\")");
target:contribute_newline();
target:contribute_newline();
endfor
"Copied from List Of Strings Tag (#362):to_linemode by Jubal (#1767) Mon Oct 10 06:50:33 1994 EDT";
.
#88:2
list_of_strings = args[1];
target = args[2];
for string in (list_of_strings)
target:contribute_partial(string);
target:contribute_newline();
target:contribute_newline();
endfor
"Copied from List Of Strings Tag (#362):to_linemode by Jubal (#1767) Mon Oct 10 06:51:32 1994 EDT";
.
#89:0
"Usage:  get_now(url)";
"Returns a list of strings, or an error if we couldn't connect.";
"for now, only do text/html and text/ascii...";
if (!this:trusted(caller_perms()))
return E_PERM;
endif
{url, ?binary = 0} = args;
{host, port, path} = this:parse_url(url);
request = tostr("GET ", path, " HTTP/1.0");
opentime = time();
con = $network:open(host, port);
opentime = time() - opentime;
if (typeof(con) == ERR)
raise(con, this:interpret_error(con));
else
con = con[1];
endif
for line in ({request, "User-Agent: JHCore/1.0 LambdaMOO/" + server_version(), "Accept: text/html", "Accept: text/ascii", "Accept: text/plain", ""})
notify(con, line);
endfor
status = read(con);
ok = "HTTP/%([0-9]%.[0-9]%) 200";
if (!match(status, ok))
$network:close(con);
raise(this.error, status);
endif
headers = {};
while ((typeof(string = `read(con) ! E_INVARG') == STR) && string)
headers = {@headers, string};
endwhile
if (typeof(string) == ERR)
$network:close(con);
raise(this.error, "Error reading HTTP headers");
endif
count = this.limit;
now = time();
timeout = 30;
if (binary)
set_connection_option(con, "binary", 1);
results = "";
while (((typeof(`string = read(con) ! E_INVARG') == STR) && ((count = count - 1) > 0)) && ((now + timeout) > (now = time())))
results = results + string;
endwhile
else
results = {};
while (((typeof(`string = read(con) ! E_INVARG') == STR) && ((count = count - 1) > 0)) && ((now + timeout) > (now = time())))
results = {@results, string};
endwhile
endif
$network:close(con);
if (opentime > 1)
"This is to keep repeated calls to $network:open to 'slow responding hosts' from totally spamming.";
suspend(1);
endif
return {headers, results};
"Metadata 202106";
"Last-Modify: {869801253, \"Erik\", #74, \"JHM\"}";
.
#89:1
"parse gopher result line:";
"return {host, port, tag, label}";
"host/port/tag are what you send to the gopher server to get that line";
"label is <type>/human readable entry";
string = args[1];
tab = index(string, "	");
label = string[1..tab - 1];
string = string[tab + 1..length(string)];
tab = index(string, "	");
tag = string[1..tab - 1];
string = string[tab + 1..length(string)];
tab = index(string, "	");
host = string[1..tab - 1];
if (host[length(host)] == ".")
host[length(host)..length(host)] = "";
endif
string = string[tab + 1..length(string)];
tab = index(string, "	");
port = tonum(tab ? string[1..tab - 1] | string);
return {host, port, tag, label};
"ignore extra material after port, if any";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869801253, \"Erik\", #74, \"JHM\"}";
.
#89:2
"$gopher:show_text(who, start, end, ..node..)";
"like who:notify_lines($gopher:get(..node..)[start..end]), but pipelined";
if (!this:trusted(caller_perms()))
return E_PERM;
endif
who = args[1];
start = args[2];
end = args[3];
args = args[4..length(args)];
con = $network:open(args[1], args[2]);
if (typeof(con) == ERR)
player:tell("Sorry, can't get this information now.");
return;
endif
con = con[1];
notify(con, args[3]);
read(con);
"initial blank line";
line = 0;
sent = 0;
end = end || this.limit;
while (((string = read(con)) != ".") && (typeof(string) == STR))
line = line + 1;
if ((line >= start) && ((!end) || (line <= end)))
sent = sent + 1;
if (valid(who))
if (string && (string[1] == "."))
string = string[2..length(string)];
endif
who:notify(string);
else
notify(who, string);
endif
endif
endwhile
$network:close(con);
return sent;
"Metadata 202106";
"Last-Modify: {869801253, \"Erik\", #74, \"JHM\"}";
.
#89:3
type = args[1];
if (type == "1")
return "menu";
elseif (type == "?")
return "menu?";
elseif (type == "0")
return "text";
elseif (type == "7")
return "search";
elseif (type == "9")
return "binary";
elseif (type == "2")
return "phone directory";
elseif (type == "4")
return "binhex";
elseif (type == "8")
return "telnet";
elseif (type == "I")
return "image";
elseif (type == " ")
"not actually gopher protocol: used by 'goto'";
return "";
else
return "unknown";
endif
"not done, need to fill out";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869801253, \"Erik\", #74, \"JHM\"}";
.
#89:4
"return a 'nice' string showing the information in a gopher node";
if (typeof(parse = args[1]) == STR)
parse = this:parse(parse);
endif
if (parse[1] == "!")
return {"[remembered set]", "", ""};
endif
if (length(parse) > 3)
label = parse[4];
if (label)
type = $gopher:type(label[1]);
label = label[2..length(label)];
if (type == "menu")
elseif (type == "search")
label = (("<" + parse[3][rindex(parse[3], "	") + 1..length(parse[3])]) + "> ") + label;
else
label = (type + ": ") + label;
endif
else
label = "(top)";
endif
else
label = parse[3] + " (top)";
endif
port = "";
if (parse[2] != 70)
port = tostr(" ", parse[2]);
endif
return {tostr("[", parse[1], port, "]"), label, parse[3]};
"Metadata 202106";
"Last-Modify: {869801253, \"Erik\", #74, \"JHM\"}";
.
#89:5
"Usage: get(url)";
"returns a list of strings, or an error if it couldn't connect. Results are cached.";
if (!this:trusted(caller_perms()))
return E_PERM;
endif
request = args;
"15 minute timeout";
now = time();
if (index = request in this.cache_requests)
if (this.cache_times[index] > now)
return this.cache_values[index];
endif
endif
value = this:get_now(@args);
"don't want to run out of ticks while adding new value";
$command_utils:suspend_if_needed(0);
while (this.cache_times && (this.cache_times[1] < now))
this.cache_times = listdelete(this.cache_times, 1);
this.cache_values = listdelete(this.cache_values, 1);
this.cache_requests = listdelete(this.cache_requests, 1);
"caution: don't want to suspend between test and removal";
$command_utils:suspend_if_needed(0);
endwhile
$command_utils:suspend_if_needed(0);
this.cache_times = {@this.cache_times, time() + ((typeof(value) == ERR) ? 120 | 1800)};
this.cache_values = {@this.cache_values, value};
this.cache_requests = {@this.cache_requests, request};
return value;
"Metadata 202106";
"Last-Modify: {869801253, \"Erik\", #74, \"JHM\"}";
.
#89:6
if (!this:trusted(caller_perms()))
return E_PERM;
endif
this.cache_values = this.cache_times = this.cache_requests = {};
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869801253, \"Erik\", #74, \"JHM\"}";
.
#89:7
"unparse(host, port, tag, label) => string";
host = args[1];
port = args[2];
tag = args[3];
label = args[4];
if (tab = index(tag, "	"))
"remove search terms from search nodes";
tag = tag[1..tab - 1];
endif
return tostr(label, "	", tag, "	", host, "	", port);
"Metadata 202106";
"Last-Modify: {869801253, \"Erik\", #74, \"JHM\"}";
.
#89:8
"return an explanation for a 'false' $HTTP:get result";
value = args[1];
if (value == E_INVARG)
return "That HTTP server is not reachable or is not responding.";
elseif (value == E_QUOTA)
return "HTTP connections cannot be made at this time because of system resource limitations!";
elseif (typeof(value) == ERR)
return tostr("The HTTP request results in an error: ", value);
else
return "The HTTP request has no results.";
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869801253, \"Erik\", #74, \"JHM\"}";
.
#89:9
"default -- gopher trusts everybody";
return 1;
"Metadata 202106";
"Last-Modify: {869801253, \"Erik\", #74, \"JHM\"}";
.
#89:10
if ($__core_init_phase)
pass(@args);
this:clear_cache();
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869801253, \"Erik\", #74, \"JHM\"}";
.
#89:11
"Usage:  :parse_url(url)";
"only absolute URLS from now on!";
{url} = args;
"a simplistic view of things";
"that trailing / is mandatory...cope.";
urlregexp = "^%([A-Za-z]*%)://%([^/]*%)%(/.*%)$";
if (!(m = match(url, urlregexp)))
raise(E_INVARG, "Invalid URL");
elseif ((scheme = substitute("%1", m)) != "http")
raise(E_INVARG, "Only the HTTP scheme is supported");
endif
host = substitute("%2", m);
if (i = index(host, ":"))
port = tonum(host[i + 1..$]);
host = host[1..i - 1];
else
port = 80;
endif
path = substitute("%3", m);
if ((!host) || (!port))
raise(E_INVARG, "Invalid URL");
else
return {host, port, path};
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869801253, \"Erik\", #74, \"JHM\"}";
.
#89:12
s = tostr(args[1]);
r = "";
for n in [1..length(s)]
c = s[n];
if (index("abcdefghijklmnopqrstuvwxyz0123456789$_!@^&*().", c))
r = r + c;
else
r = tostr(r, "%", $string_utils:character_to_hex_ascii(c));
endif
endfor
return r;
"Copied from Jaddress Dispatcher (#889):escape_for_url by Ken (#75) Sat Apr 13 01:47:30 1996 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869801253, \"Erik\", #74, \"JHM\"}";
.
#89:13
s = args[1];
r = "";
while (p = index(s, "%"))
r = (r + s[1..p - 1]) + $string_utils:hex_ascii_to_character(s[p + 1..p + 2]);
s[1..p + 2] = "";
endwhile
return r + s;
"Copied from Jaddress Dispatcher (#889):unescape_for_url by Ken (#75) Sat Apr 13 01:47:36 1996 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869801253, \"Erik\", #74, \"JHM\"}";
.
#89:14
return {@pass(@args), $string_utils};
"Metadata 202106";
"Last-Modify: {869801253, \"Erik\", #74, \"JHM\"}";
.
#89:15
"Usage:  :init_for_module()";
"";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
this:clear_cache();
else
raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869801253, \"Erik\", #74, \"JHM\"}";
.
#89:16
if (!$perm_utils:controls(caller_perms(), this))
raise(E_PERM);
endif
try
add_property(#0, "http", this, {this.owner, "r"});
except (E_INVARG)
"there already is a $http property.";
$http = this;
endtry
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869801253, \"Erik\", #74, \"JHM\"}";
.
#89:17
"Usage:  post_now(url, fields, [referer[, binary]])";
"Returns a list of strings, or an error if we couldn't connect.";
"for now, only do text/html and text/ascii...";
if (!this:trusted(caller_perms()))
return E_PERM;
endif
{url, fields, ?referer = "", ?binary = 0} = args;
{host, port, path} = $http:parse_url(url);
request = tostr("POST ", path, " HTTP/1.0");
opentime = time();
con = $network:open(host, port);
opentime = time() - opentime;
if (typeof(con) == ERR)
raise(con, $http:interpret_error(con));
else
con = con[1];
endif
encoded = this:encode_fields(fields);
if (referer)
referer = {"Referer: " + referer};
else
referer = {};
endif
for line in ({request, "User-Agent: JHCore/1.0 LambdaMOO/" + server_version(), "Accept: text/html", "Accept: text/ascii", "Accept: text/plain", @referer, tostr("Content-length: ", length(encoded)), "Content-type: application/x-www-form-urlencoded", "", encoded})
notify(con, line);
endfor
status = read(con);
ok = "HTTP/%([0-9]%.[0-9]%) 200";
if (!match(status, ok))
$network:close(con);
raise(this.error, status);
endif
headers = {};
while ((typeof(string = `read(con) ! E_INVARG') == STR) && string)
headers = {@headers, string};
endwhile
if (typeof(string) == ERR)
$network:close(con);
raise(this.error, "Error reading HTTP headers");
endif
count = this.limit;
now = time();
timeout = 30;
if (binary)
set_connection_option(con, "binary", 1);
results = "";
while (((typeof(`string = read(con) ! E_INVARG') == STR) && ((count = count - 1) > 0)) && ((now + timeout) > (now = time())))
results = results + string;
endwhile
else
results = {};
while (((typeof(`string = read(con) ! E_INVARG') == STR) && ((count = count - 1) > 0)) && ((now + timeout) > (now = time())))
results = {@results, string};
endwhile
endif
$network:close(con);
if (opentime > 1)
"This is to keep repeated calls to $network:open to 'slow responding hosts' from totally spamming.";
suspend(1);
endif
return {headers, results};
"Copied from http (#88):get_now by Ken (#200) Thu Jan 22 23:51:12 1998 EST";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895876545, \"Ken\", #75, \"JHM\"}";
.
#89:18
"Usage:  post_now(url, fields, [referer[, binary]])";
"Returns a list of strings, or an error if we couldn't connect.";
"for now, only do text/html and text/ascii...";
if (!this:trusted(caller_perms()))
return E_PERM;
endif
{url, fields, ?referer = "", ?binary = 0} = args;
{host, port, path} = $http:parse_url(url);
request = tostr("POST ", path, " HTTP/1.0");
opentime = time();
con = $network:open(host, port);
opentime = time() - opentime;
if (typeof(con) == ERR)
raise(con, $http:interpret_error(con));
else
con = con[1];
endif
encoded = this:encode_fields(fields);
if (referer)
referer = {"Referer: " + referer};
else
referer = {};
endif
for line in ({request, "User-Agent: JHCore/1.0 LambdaMOO/" + server_version(), "Accept: text/html", "Accept: text/ascii", "Accept: text/plain", @referer, tostr("Content-length: ", length(encoded)), "Content-type: application/x-www-form-urlencoded", "", encoded})
notify(con, line);
endfor
status = read(con);
ok = "HTTP/%([0-9]%.[0-9]%) 200";
if (!match(status, ok))
$network:close(con);
raise(this.error, status);
endif
headers = {};
while ((typeof(string = `read(con) ! E_INVARG') == STR) && string)
headers = {@headers, string};
endwhile
if (typeof(string) == ERR)
$network:close(con);
raise(this.error, "Error reading HTTP headers");
endif
count = this.limit;
now = time();
timeout = 30;
if (binary)
set_connection_option(con, "binary", 1);
results = "";
while (((typeof(`string = read(con) ! E_INVARG') == STR) && ((count = count - 1) > 0)) && ((now + timeout) > (now = time())))
results = results + string;
endwhile
else
results = {};
while (((typeof(`string = read(con) ! E_INVARG') == STR) && ((count = count - 1) > 0)) && ((now + timeout) > (now = time())))
results = {@results, string};
endwhile
endif
$network:close(con);
if (opentime > 1)
"This is to keep repeated calls to $network:open to 'slow responding hosts' from totally spamming.";
suspend(1);
endif
return {headers, results};
"Metadata 202106";
"Last-Modify: {895876585, \"Ken\", #75, \"JHM\"}";
.
#89:19
{alist} = args;
string = "";
for pair in (alist)
{key, value} = pair;
if (string)
string = string + "&";
endif
string = (string + key) + "=";
if (typeof(value) in {STR, NUM})
string = string + $http:escape_for_url(tostr(value));
elseif (typeof(value) == LIST)
string = string + $string_utils:from_list($list_utils:map_arg($http, "escape_for_url", value), "%0D%0A");
else
raise(E_INVARG);
endif
endfor
return string;
"Metadata 202106";
"Last-Modify: {895876622, \"Ken\", #75, \"JHM\"}";
.
#90:0
return {@pass(@args), this.owner};
.
#90:1
if (!caller_perms().wizard)
return E_PERM;
else
this:set_quota_total(args[1], this.default_player_quota);
"args[1].ownership_quota = this.large_negative_number;";
endif
.
#90:2
if (!caller_perms().wizard)
return E_PERM;
else
victim = args[1];
if ($registrar:is_second_char(victim))
"don't increment quota for 2nd chars when programmering";
else
this:set_quota_total(victim, max(this:get_quota_total(victim), this.default_programmer_quota));
endif
endif
.
#90:3
"Usage:";
"";
"Add <what> to <who>'s .owned_objects list, provided <who> owns it.";
who = args[1];
what = args[2];
if (caller in {this, what, who})
if (what.owner == who)
owned = who.owned_objects;
if (typeof(owned) == LIST)
who.owned_objects = setadd(owned, what);
elseif (typeof(owned) == NUM)
who.owned_objects = owned + 1;
endif
return 1;
else
return E_INVARG;
endif
else
return E_PERM;
endif
.
#90:4
"Usage:";
"";
"Remove <what> from <who>'s .owned_objects list, provided <who> doesn't own it.";
who = args[1];
what = args[2];
if (caller in {this, what, who})
if (what.owner != who)
owned = who.owned_objects;
if (typeof(owned) == LIST)
who.owned_objects = setremove(owned, what);
elseif (typeof(owned) == NUM)
who.owner.owned_objects = owned - 1;
endif
return 1;
else
return E_INVARG;
endif
else
return E_PERM;
endif
.
#90:5
if ((caller != this) && (!caller_perms().wizard))
return E_PERM;
else
changes = 0;
for p in (players())
if (typeof(p.owned_objects) == LIST)
for o in (p.owned_objects)
this:disown_object(p, o);
"will only happen if p doesn't actually own o";
$command_utils:suspend_if_needed(0);
endfor
elseif (typeof(p.owned_objects) == NUM)
p.owned_objects = 0;
endif
endfor
for o in ($object_utils:descendants_suspended(#1))
this:own_object(o.owner, o);
"will only happen if o.owner is valid, of course";
changes = changes + 1;
$command_utils:suspend_if_needed(0);
endfor
return changes;
endif
"Copied from wizard utilities (#26):verify_owned_objects by Erik (#74) Wed Nov 23 17:48:07 1994 EST";
.
#90:6
if ((caller != this) && (!this:can_peek(caller_perms(), args[1])))
return E_PERM;
else
"$registrar is in the core";
seconds = $registrar:all_second_chars(args[1]);
if (seconds == E_INVARG)
return {args[1]};
else
return seconds;
endif
endif
"Copied from Byte Quota Utilities (#2564):all_characters by Erik (#74) Sun Nov 27 20:19:53 1994 EST";
.
#90:7
return (args[1] == this.owner) || $perm_utils:controls(args[1], args[2]);
"Copied from Byte Quota Utilities (#2564):can_peek by Erik (#74) Sun Nov 27 20:20:08 1994 EST";
.
#91:0
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249566, \"Ken\", #75, \"JHM\"}";
.
#91:1
"Usage:  :set_parent_package(parent)";
"";
{parent} = args;
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
if ($recycler:valid(this.parent_package))
"don't bother raising an error; it's not important.";
`this.parent_package:remove_cord_type(this) ! E_VERBNF';
endif
this.parent_package = parent;
if ($recycler:valid(parent))
try
parent:add_cord_type(this);
except e (E_VERBNF)
this.parent_protocol = $nothing;
"in this case, we want to inform the caller/programmer/user that something has really gone wrong.";
raise(@e[1..3]);
endtry
endif
else
raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249566, \"Ken\", #75, \"JHM\"}";
.
#92:0
value = this:get(@args);
if (!value)
value = $player:who_option("columns");
endif
l = {};
for i in (value)
if (typeof(i) == LIST)
l = {@l, tostr(i[1], ":", i[2])};
else
l = {@l, i};
endif
endfor
return {"", {"Include these columns in @who:", $string_utils:from_list(l, " ")}};
"return {\"\", {\"Include these columns in @who:\", $string_utils:english_list(l)}};";
.
#92:1
raw = args[2];
if (typeof(raw) == LIST)
columnsd = raw;
elseif (typeof(raw) == STR)
columnsd = $string_utils:explode(raw);
else
return "Please supply a column list.";
endif
if (!raw)
return "You have to have SOME columns.";
endif
columns = {};
data = {};
for c in (columnsd)
if (index(c, ":"))
name = (x = $string_utils:explode(c, ":"))[1];
if (name in {"connected", "idle"})
if (!{"", "short", "long"}[$string_utils:find_prefix(x[2], {"short", "long"}) + 1])
return "Length parameter on idle and connected columns must be either long or short.";
endif
elseif (!$string_utils:is_numeric(x[2]))
return ("`" + x[2]) + "' can't be parsed as a number.";
else
x[2] = tonum(x[2]);
endif
width = x[2];
else
name = c;
width = 0;
endif
columns = {@columns, name};
data = {@data, width ? {name, width} | name};
endfor
if (!("name" in columns))
return "A @who listing without names doesn't make sense.";
elseif (unknown = $set_utils:diff(columns, this.valid_columns))
return tostr("Column types ", $string_utils:english_list(unknown), " ", (length(unknown) == 1) ? "is" | "are", " not recognized.  Valid column types are ", $string_utils:english_list(this.valid_columns), ".");
endif
args[2] = data;
return args[1..2];
"Copied from Mail Options (#63):parse_@mail by Doug (#107) Sun Jun  5 17:59:32 1994 EDT";
"Copied from Eval Options (#1459):parse_env by Ken (#75) Mon Sep 12 19:26:41 1994 EDT";
"Copied from Who Options (#524):show_columns by Ken (#75) Tue Sep 13 20:37:56 1994 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {893174209, \"Ken\", #75, \"JHM\"}";
.
#92:2
":show_who_listing(players, [[, moreplayers], spam])";
"name:20  location:20  doing:30  last_disconnect:28";
connected = {};
if (length(args) > 1)
disconnected = args[2];
else
disconnected = {};
endif
if (length(args) > 2)
spamv = "tell";
spam = toobj(args[3]);
else
spamv = "notify";
spam = caller;
endif
columns = this:_columns(player:who_option("columns") || $player:who_option("columns"));
sort = player:who_option("order") || $player:who_option("order");
asc = player:who_option("ascending");
for i in (args[1])
this:sin();
if (!$object_utils:isa(i, $Player))
spam:(spamv)(tostr($string_utils:nn(i), " is not a $player."));
elseif (typeof(idle_seconds(i)) != ERR)
connected = setadd(connected, i);
else
disconnected = setadd(disconnected, i);
endif
endfor
this:sin();
active = 0;
inactive = 0;
strings = {};
if ((!connected) && (!disconnected))
return;
endif
if (connected)
strings = (v = this:_get_columns(columns, connected, sort))[3];
sortd = v[2];
idles = v[1];
this:sin();
strings = this:("_sort_by_" + sort)(sortd, strings, asc);
disconnected && (strings = {@strings, "        " + $string_utils:space(15, "-")});
for i in (idles)
inactive = inactive + (i > 300);
endfor
active = active + (length(connected) - inactive);
endif
this:sin();
if (disconnected)
cd = this:_columns({"name", "location", "last_disconnect"});
stringsd = (v = this:_get_columns(cd, disconnected, "last_disconnect"))[3];
stringsd = this:_sort_by_last_disconnect(v[2], stringsd, 0);
inactive = inactive + length(disconnected);
strings = {@strings, @stringsd};
endif
if (connected)
title = this:_build_title(columns);
else
title = this:_build_title(cd, datad, widthsd);
endif
spam:(spamv + "_lines")(title);
spam:(spamv + "_lines")(strings);
total = inactive + active;
if (total == 1)
spam:(spamv + "_lines")({"", tostr("Total: ", total, " person, who has", active ? "" | " not", " been active recently.")});
else
spam:(spamv + "_lines")({"", tostr("Total: ", total, " people, ", active ? (active == total) ? (active == 2) ? "both" | "all" | active | ((total == 2) ? "neither" | "none"), " of ", (active == 1) ? "whom has" | "whom have", " been active recently.")});
endif
return total;
.
#92:3
raw = args[2];
if (typeof(raw) != STR)
if (!raw)
return "Please supply a sort order.";
endif
endif
if (!((order = raw) in this.valid_sorts))
return ("Valid orders are " + $string_utils:english_list(this.valid_sorts)) + ".";
endif
return args[1..2];
"Copied from Mail Options (#63):parse_@mail by Doug (#107) Sun Jun  5 17:59:32 1994 EDT";
"Copied from Eval Options (#1459):parse_env by Ken (#75) Mon Sep 12 19:54:39 1994 EDT";
.
#92:4
if (value = this:get(@args))
if (typeof(value) == STR)
return {"", {("Sort by " + value) + "."}};
else
return {"", {"Unrecognized sort order:", value}};
endif
else
return {0, {"Don't sort @who listing."}};
endif
"Copied from Mail Options (#63):show_@mail by Doug (#107) Sun Jun  5 17:30:32 1994 EDT";
"Copied from Eval Options (#1459):show_env by Ken (#75) Mon Sep 12 19:54:56 1994 EDT";
.
#92:5
order = this:get(args[1], "order");
value = this:get(@args);
if (order == "name")
return {value, {value ? "Names alphabetically A-Z" | "Names alphabetically Z-A"}};
elseif (order == "location")
return {value, {value ? "Locations alphabetically A-Z" | "Locations alphabetically Z-A"}};
elseif (order == "party")
return {value, {value ? "Most crowded place last." | "Most crowded place first."}};
elseif (order == "idle")
return {value, {value ? "Least idle person last." | "Least idle person first."}};
elseif (order == "connected")
return {value, {value ? "Shortest connect time first." | "Shortest connect time last."}};
else
return {value, {"Don't sort @who listing."}};
endif
"Copied from Mail Options (#63):show_@mail by Doug (#107) Sun Jun  5 17:30:32 1994 EDT";
"Copied from Eval Options (#1459):show_env by Ken (#75) Mon Sep 12 19:54:56 1994 EDT";
"Copied from Who Options (#524):show_order by Ken (#75) Mon Sep 12 20:12:47 1994 EDT";
.
#92:6
"_get_name(list of people)";
"return {column width, {list of names}}";
p = args[1];
return p:inamec();
.
#92:7
p = args[1];
return idle_seconds(p);
.
#92:8
p = args[1];
return connected_seconds(p);
.
#92:9
p = args[1];
return p:is_idle() ? p:idle_msg() | p:doing_msg();
.
#92:10
p = args[1];
if (typeof(m = p.location:who_location_msg(p)) != STR)
m = " ** Nowhere **";
endif
return m;
.
#92:11
p = args[1];
return (p.last_connect_time == $maxint) ? 0 | p.last_disconnect_time;
return ctime(p.last_disconnect_time);
.
#92:12
"_build_title(column names, columns)";
"construct title lines";
columns = args[1];
titles = {};
linelen = player:linelen() || 79;
for i in (columns)
titles = {@titles, this.column_titles[i[1] in this.valid_columns]};
endfor
titlel = "";
linel = "";
csep = this.csep;
lengths = $list_utils:slice(columns, 2);
for i in [1..length(lengths)]
if (lengths[i] == "short")
lengths[i] = 4;
elseif (lengths[i] == "long")
lengths[i] = 10;
endif
endfor
for i in [1..length(titles)]
titlel = (titlel + this:left_just(titles[i], lengths[i])) + csep;
linel = (linel + this:left_just($string_utils:space(length(titles[i]), "-"), lengths[i])) + csep;
endfor
return {this:left_just(titlel, linelen), this:left_just(linel, linelen)};
.
#92:13
"sort by idle(idles, listing, asc)";
sort = $list_utils:sort(args[2], args[1]);
return args[3] ? $list_utils:reverse(sort) | sort;
.
#92:14
"sort by idle(idles, listing, asc)";
sort = $list_utils:sort(args[2], args[1]);
return args[3] ? sort | $list_utils:reverse(sort);
.
#92:15
columns = args[1];
people = args[2];
sort = args[3];
data = {};
s = {};
id = {};
linelen = player:linelen() || 79;
for i in (people)
this:sin();
data = {@data, (v = this:_get_line(i, columns, sort))[2][1..min(linelen, length(v[2]))]};
s = {@s, v[1]};
id = {@id, v[3]};
endfor
$command_utils:suspend_if_needed(0);
return {id, s, data};
"Metadata 202106";
"Last-Modify: {835905430, \"Erik\", #74, \"JHM\"}";
.
#92:16
"Left Justify/truncate string at specified width";
"left_just(string, width, filler)";
{string, width, ?filler = " "} = args;
string = string[1..min(length(string), width)];
return (length(string) < width) ? string + $string_utils:space(width - length(string), filler) | string;
"Metadata 202106";
"Last-Modify: {835905466, \"Erik\", #74, \"JHM\"}";
.
#92:17
for i in (args[1])
if ((typeof(i) == LIST) || (typeof(i) == STR))
"ok";
else
return "String or list expected.";
endif
endfor
return args;
.
#92:18
{d, l, ?ul = 0} = args;
if (typeof(d) == NUM)
if ((typeof(l) == NUM) || (l == "long"))
d = $string_utils:from_seconds(d);
l = 10;
else
d = this:_format_time_short(d);
l = 4;
endif
endif
return ul ? d | this:left_just(d, l);
"Metadata 202106";
"Last-Modify: {835905483, \"Erik\", #74, \"JHM\"}";
.
#92:19
"format time as follows";
"..s  ..m ..h  ..d";
t = args[1];
if (t < 60)
s = tostr(t);
x = "s";
elseif (t < 3600)
s = tostr(t / 60);
x = "m";
elseif (t < 86400)
s = tostr(t / 3600);
x = "h";
else
s = tostr(t / 86400);
x = "d";
endif
return this:right_just(s, 3) + x;
.
#92:20
"Right Justify/truncate string at specified width";
"right_just(string, width, filler)";
{string, width, ?filler = " "} = args;
if (length(string) > width)
return string[(length(string) - width) + 1..length(string)];
elseif (length(string) == width)
return string;
else
return $string_utils:space(width - length(string), filler) + string;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835905498, \"Erik\", #74, \"JHM\"}";
.
#92:21
"Gather columns etc into one neat list";
columns = args[1];
f = {};
for i in (columns)
if (typeof(i) == LIST)
f = {@f, i};
else
f = {@f, {i, this.column_widths[i in this.valid_columns]}};
endif
endfor
return f;
.
#92:22
p = args[1];
columns = args[2];
sort = args[3];
sval = this:("_get_" + sort)(p);
l = "";
for f in (columns)
l = (l + "  ") + this:_get_column(p, @f);
endfor
return {sval, l[3..length(l)], idle_seconds(p)};
.
#92:23
s = args[1];
l = args[2];
return this:left_just(s, l);
"Metadata 202106";
"Last-Modify: {835905512, \"Erik\", #74, \"JHM\"}";
.
#92:24
s = args[1];
return this:left_just($string_utils:from_seconds(s), 10);
.
#92:25
p = args[1];
name = args[2];
width = args[3];
x = this:("_get_" + name)(p);
if (typeof(width) == STR)
return this:((("_format_" + name) + "_") + width)(x);
else
return this:("_format_" + name)(x, width);
endif
.
#92:26
return $command_utils:suspend_if_needed(0);
.
#92:27
s = args[1];
l = args[2];
return s ? this:left_just(ctime(s), l) | "Never";
.
#92:28
s = args[1];
l = args[2];
return this:left_just(s, l);
"Copied from Who Options (#404):_format_name by Erik (#74) Fri Sep 23 00:30:38 1994 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835905518, \"Erik\", #74, \"JHM\"}";
.
#92:29
p = args[1];
return p.location;
"Copied from Who Options (#404):_get_location by Erik (#74) Fri Sep 23 00:59:17 1994 EDT";
.
#92:30
"sort by idle(idles, listing, asc)";
rooms = $list_utils:remove_duplicates(args[1]);
sizes = $list_utils:map_arg(this, "_party_size", rooms);
rooms = $list_utils:sort(rooms, sizes);
ranks = $list_utils:map_arg(this, "_party_rank", args[1], rooms);
sort = $list_utils:sort(args[2], ranks);
return args[3] ? sort | $list_utils:reverse(sort);
"Copied from Who Options (#404):_sort_by_name by Erik (#74) Fri Sep 23 01:03:19 1994 EDT";
.
#92:31
return length($set_utils:intersection(args[1].contents, connected_players()));
.
#92:32
return args[1] in args[2];
.
#93:0
target = args[1];
address = args[2];
set_task_perms($no_one);
if (objnum = $jaddress:extract("objnum", address))
it = $string_utils:literal_object(objnum[2]);
if (!valid(it))
if (it == $nothing)
target:receive_vbox({$jtext.vgroup, {$jtext.title, "$nothing"}, {$jtext.paragraph, tostr("$nothing (", it, ") is not a valid object.  It is used to indicate the object that matches the empty string, and also as the `location' of objects that aren't really located anywhere and `parent' of root objects.")}});
elseif (it == $ambiguous_match)
target:receive_vbox({$jtext.vgroup, {$jtext.title, "$ambiguous_match"}, {$jtext.paragraph, tostr("$ambiguous_match (", it, ") is not a valid object.  It is used to indicate that a given string could refer to more than one of a given set of objects (for example, the objects `nearby').")}});
elseif (it == $failed_match)
target:receive_vbox({$jtext.vgroup, {$jtext.title, "$failed_match"}, {$jtext.paragraph, tostr("$failed_match (", it, ") is not a valid object.  It is used to indicate that a given string could not refer to any of a given set of objects (for example, the objects `nearby').")}});
else
target:receive_vbox({$jtext.vgroup, {$jtext.title, "$failed_match"}, {$jtext.paragraph, tostr(it, " is not a valid object.  Please hang up and try again.")}});
endif
return;
endif
elseif (user = $jaddress:extract("user", address))
it = $string_utils:match_player(user[2]);
if (!valid(it))
if (it == $nothing)
target:receive_vbox({$jtext.vgroup, {$jtext.title, "Malformed address"}, {$jtext.paragraph, "No value was provided for the `user' attribute."}});
elseif (it == $ambiguous_match)
target:receive_vbox({$jtext.vgroup, {$jtext.title, "Ambiguous match"}, {$jtext.paragraph, tostr("The name `", user, "' could match more than one user.")}});
else
"shouldn't be anything else but $failed_match";
target:receive_vbox({$jtext.vgroup, {$jtext.title, "No match"}, {$jtext.paragraph, tostr("There is no user named `", user, "'.")}});
endif
return;
endif
endif
readable = it.r;
cant = {};
if (readable)
prop = properties(it);
else
prop = {};
cant = setadd(cant, it);
endif
if (readable)
vrb = verbs(it);
else
vrb = {};
cant = setadd(cant, it);
endif
name = it:name();
target:receive_vbox({$jtext.title, name});
target:receive_vbox({$jtext.header, 1, tostr(name, " (", it, "): browser view")});
for line in (it:description())
if (line)
target:receive_vbox({$jtext.paragraph, line});
endif
endfor
dlist = {$jtext.dlist, {"Attributes", tostr(it.r ? "readable " | "", it.w ? "writeable " | "", it.f ? "fertile " | "", is_player(it) ? "(player) " | "", it.programmer ? "programmer " | "", it.wizard ? "wizard " | "", (!(((it.r || it.w) || it.f) || is_player(it))) ? "(none)" | "")}};
dlist = listappend(dlist, {"Owned by", this:make_link(it.owner)});
dlist = listappend(dlist, {"Child of", this:make_link(parent(it))});
if (children(it))
children = {$jtext.hgroup};
for child in (children(it))
children = {@children, this:make_link(child), ", "};
$command_utils:suspend_if_needed(0);
endfor
dlist = listappend(dlist, {"Children", listdelete(children, length(children))});
endif
dlist = listappend(dlist, {"Location", this:make_link(it.location)});
target:receive_vbox(dlist);
if (readable)
target:receive_vbox({$jtext.header, 2, "Verbs"});
dlist = {$jtext.dlist};
for n in [0..length(verbs(it)) - 1]
$command_utils:suspend_if_needed(0);
q = tostr(n);
short = strsub(y = index(q, " ") ? q[1..y - 1] | q, "*", "");
inf = verb_info(it, short);
vargs = verb_args(it, short);
if ((typeof(inf) == LIST) || (inf == E_PERM))
name = (typeof(inf) == LIST) ? inf[3] | q;
if (inf == E_PERM)
line = "** unreadable **";
dlist = listappend(dlist, {tostr(it, ":", n + 1), line});
else
line = {$jtext.hgroup, this:make_link(inf[1]), " " + ((i = inf[2] in {"x", "xd", "d", "rd"}) ? {" x", " xd", "  d", "r d"}[i] | inf[2])};
if (!(vargs[2] in {"any", "none"}))
vargs[2] = $code_utils:short_prep(vargs[2]);
endif
line = {@line, " " + $string_utils:from_list(vargs, " ")};
dlist = listappend(dlist, {$jaddress.browser_verb:make_link(it, q, name, @vargs), line});
endif
endif
endfor
target:receive_vbox(dlist);
endif
if (properties(it))
spaces = 0;
target:receive_vbox({$jtext.header, 2, "Properties"});
dlist = {$jtext.dlist};
for prop in (properties(it))
info = property_info(it, prop);
if (info == E_PROPNF)
"just ignore it, bogosity due to non-atomicity";
elseif (prop[1] == " ")
"just ignore it, this is probably a huge monster db";
"on second thought, keep a counter";
spaces = spaces + 1;
elseif (info == E_PERM)
dlist = listappend(dlist, {"." + prop, "** unreadable **"});
else
(info[2][1] != "r") && (info[2][1..0] = " ");
(info[2][2] != "w") && (info[2][2..1] = " ");
val = it.(prop);
dlist = listappend(dlist, {"." + prop, {$jtext.hgroup, this:make_link(info[1]), (" " + info[2]) + " ", (typeof(val) == OBJ) ? this:make_link(val) | $string_utils:print(it.(prop))}});
endif
endfor
target:receive_vbox(dlist);
if (spaces)
target:receive_vbox({$jtext.paragraph, "(Plus ", tostr(spaces), " properties whose names begin with spaces.)"});
endif
endif
if (valid(what = parent(it)))
target:receive_vbox({$jtext.header, 2, "Inherited Properties"});
nlist = {$jtext.itemize_unnumbered};
while (valid(what))
if (!what.r)
nlist = listappend(nlist, {$jtext.hgroup, this:make_link(what), " is unreadable."});
else
nlist = listappend(nlist, {$jtext.hgroup, $jaddress.browser_inherited_properties:make_link(it, what), " inherited from ", this:make_link(what), "."});
endif
what = parent(what);
endwhile
target:receive_vbox(nlist);
endif
return;
"Metadata 202106";
"Last-Modify: {833404892, \"Erik\", #74, \"JHM\"}";
.
#93:1
"make_link(object)";
object = args[1];
return this:make_named_link(tostr(valid(object) ? object.name | "<void>", " (", object, ")"), object);
return {$jtext.link, {this, {{"objnum", tostr(object)}}}, tostr(valid(object) ? object.name | "<void>", " (", object, ")")};
.
#93:2
"make_core_link(property)";
"this one prints core objects nicely.";
property = args[1];
if (typeof(o = #0.(property)) == OBJ)
return this:make_named_link(tostr("$", property), o);
"return {$jtext.link, {this, {{\"objnum\", tostr(o)}}}, tostr(\"$\", property)};";
else
return tostr("$", property);
endif
.
#93:3
"make_named_link(name, object)";
if (valid(args[2]) || (args[2] in {#-1, #-2, #-3}))
return {$jtext.link.root, {this, {{"objnum", tostr(args[2])}}}, args[1]};
else
return args[1];
endif
.
#93:4
return {@pass(@args), $no_one, $jaddress, $string_utils, $jtext, $jtext.vgroup, $jtext.title, $jtext.paragraph, $jtext.header, $jtext.dlist, $jtext.hgroup, $command_utils, $code_utils, $jaddress.browser_verb, $jtext.itemize_unnumbered, $jaddress.browser_inherited_properties, $jtext.link, $jtext.link.root};
.
#94:0
return;
.
#94:1
return;
.
#94:2
return;
.
#94:3
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
pass(@args);
type = this.tag_type;
if ($jtext.(type) == this)
delete_property($jtext, type);
endif
endif
.
#94:4
return {@pass(@args), $perm_utils, $jtext};
.
#94:5
return this:to_lemoo(@args);
"Copied from Address Root (#871):to_mcp by Erik (#74) Fri Feb 24 14:38:17 1995 EST";
.
#94:6
"Usage:  :help_text()";
"";
return this.description;
.
#95:0
vname = "to_" + args[1];
box = args[2];
target = args[3];
class = this:tag_class(box);
class:(vname)(box, target);
.
#95:1
box = args[1];
if (typeof(box) == STR)
return this.string;
elseif ((typeof(box) == LIST) && (length(box) != 0))
if (typeof(box[1]) == OBJ)
return box[1];
elseif (typeof(box[1]) == STR)
return this.list_of_strings;
else
$error:raise(E_TYPE);
endif
else
$error:raise(E_TYPE);
endif
.
#95:2
"make_vgroup(parts)";
result = {$jtext.vgroup};
for part in (args[1])
result = listappend(result, (typeof(part) == LIST) ? part | {$jtext.paragraph, part});
endfor
return result;
.
#95:3
"Prints the argument (must be a list) as an english list, e.g. {1, 2, 3} is printed as \"1, 2, and 3\", and {1, 2} is printed as \"1 and 2\".";
"This version returns its results as a list of hboxes, suitable for insertion into a $jtext.paragraph.";
"Optional arguments are treated as follows:";
"  Second argument is the string to use when the empty list is given.  The default is \"nothing\".";
"  Third argument is the string to use in place of \" and \".  A typical application might be to use \" or \" instead.";
"  Fourth argument is the string to use instead of a comma (and space).  Gary_Severn's deranged mind actually came up with an application for this.  You can ask him.";
"  Fifth argument is a string to use after the penultimate element before the \" and \".  The default is to have a comma without a space.";
{things, ?nothingstr = "nothing", ?andstr = " and ", ?commastr = ", ", ?finalcommastr = ","} = args;
nthings = length(things);
if (nthings == 0)
return {nothingstr};
elseif (nthings == 1)
return {things[1]};
elseif (nthings == 2)
return {things[1], andstr, things[2]};
else
ret = {};
for k in [1..nthings - 1]
if (k == (nthings - 1))
commastr = finalcommastr;
endif
ret = {@ret, things[k], commastr};
endfor
return {@ret, andstr, things[nthings]};
endif
return;
"Metadata 202106";
"Last-Modify: {832015030, \"Erik\", #74, \"JHM\"}";
.
#95:4
jtext_tag_types = {this.root, this.hgroup, this.link, this.vgroup, this.header, this.title, this.string, this.list_of_strings, this.paragraph, this.dlist, this.plaintext, this.list, this.index, this.itemize, this.itemize_unnumbered, this.itemize_numbered, this.isindex, this.hr};
return {@pass(@args), this.owner, $error, @jtext_tag_types};
.
#95:5
"Usage:  :help_text()";
"";
props = properties(this);
text = {{$jtext.title, "HELP ON JTEXT DISPATCHER"}, {$jtext.header, 1, "HELP ON JTEXT DISPATCHER"}, {$jtext.paragraph, "The jtext dispatcher is the registry for jtags as well as the entity responsible for dispatching jtags (see `", $help:make_link("help dispatchers"), ")."}};
topics = {};
for i in ($list_utils:sort(props))
if (typeof(this.(i)) == OBJ)
topics = {@topics, {$jtext.link.root, {$jaddress.help, {{"subject", tostr(this.(i))}}}, "$jtext." + i}};
endif
endfor
return {@text, {$jtext.index, @topics}};
.
#95:6
":make_obj_link(object, nargs)";
"makes a $jtext.object link to object with text object:name(@nargs).";
object = args[1];
nargs = args[2];
return {$jtext.link.root, {$jaddress.object, {{"objnum", tostr(object)}}}, object:name(@nargs)};
.
#96:0
hgroup = args[1];
target = args[2];
form = verb[4..$];
for hbox in (hgroup[2..length(hgroup)])
$jtext:convert_to(form, hbox, target);
endfor
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835905809, \"Erik\", #74, \"JHM\"}";
.
#96:1
hgroup = args[1];
target = args[2];
target:contribute_partial("{hgroup");
for hbox in (hgroup[2..length(hgroup)])
target:contribute_partial(" ");
$jtext:convert_to("tkmoo", hbox, target);
endfor
target:contribute_partial("}");
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835905818, \"Erik\", #74, \"JHM\"}";
.
#96:2
hgroup = args[1];
target = args[2];
target:contribute_partial("(hgroup");
for hbox in (hgroup[2..length(hgroup)])
target:contribute_partial(" ");
$jtext:convert_to("lemoo", hbox, target);
endfor
target:contribute_partial(")");
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835905824, \"Erik\", #74, \"JHM\"}";
.
#96:3
hgroup = args[1];
target = args[2];
if (length(hgroup) > 1)
$jtext:convert_to("html", hgroup[2], target);
endif
for hbox in (hgroup[3..length(hgroup)])
"target:contribute_partial(\" \");";
$command_utils:suspend_if_needed(0);
$jtext:convert_to("html", hbox, target);
endfor
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835905839, \"Erik\", #74, \"JHM\"}";
.
#97:0
"Usage:  get_info(str <domain>, str <info>)";
"return the requested information about the country marked by <domain>";
"returns E_PROPNF if domain doesn't exist, E_INVARG if domain has no info";
if ((data = this:find_exact(args[1])) == $failed_match)
return E_PROPNF;
elseif (!(info = $list_utils:assoc(args[2], data)))
return E_INVARG;
else
return info[2];
endif
.
#97:1
"Usage:  set_info(str <domain>, str <info>, <value>)";
"set the given type of information about the country marked by <domain>";
"add the domain if it doesn't exist already.";
if ((!caller_perms().wizard) && (!(caller_perms() in this.cartographers)))
return E_PERM;
endif
if ((data = this:find_exact(args[1])) == $failed_match)
this:insert(args[1], {{args[2], args[3]}});
elseif (which = $list_utils:iassoc(args[2], data))
data[which][2] = args[3];
this:insert(args[1], data);
else
this:insert(args[1], {@data, {args[2], args[3]}});
endif
.
#97:2
"Usage:  delete_info(str <domain>, str <info>)";
"Remove the given information from the db.";
"return E_PROPNF if no such domain";
"return E_INVARG if no such information";
"else return a true value";
if ((!caller_perms().wizard) && (!(caller_perms() in this.cartographers)))
return E_PERM;
endif
if ((data = this:find_exact(args[1])) == $failed_match)
return E_PROPNF;
elseif (which = $list_utils:iassoc(args[2], data))
new = listdelete(data, which);
if (new)
this:insert(args[1], new);
else
this:delete(args[1]);
endif
return 1;
else
return E_INVARG;
endif
.
#97:3
"Called by $guest:defer to see if there's a specific nationality of guest we want to use.  Returns the guest, or #-1.";
if (!caller_perms().wizard)
return E_PERM;
endif
who = #-1;
cn = $string_utils:connection_hostname(connection_name(player));
adjective = "";
domain = "";
while (cn)
if (adjective = this:get_info(cn, "adjective"))
country = cn;
cn = "";
elseif (dot = index(cn[2..length(cn)], "."))
cn = cn[dot + 1..length(cn)];
else
cn = "";
endif
endwhile
if (adjective)
if (valid(foreigner = $string_utils:match_player(name = adjective + "_guest")) && (!(foreigner in connected_players())))
who = foreigner;
elseif (typeof(who = $wiz_utils:do_make_guest(name, this:get_aliases(adjective))) == STR)
return $nothing;
else
description = this:get_info(country, "description-tag");
if (!description)
description = tostr("This one has a slight ", this:get_info(country, "accent") || adjective, " accent, however.");
endif
who.default_description = {"By definition, guests appear nondescript.", description};
who.description = who.default_description;
endif
endif
return valid(who) ? who:defer() | who;
.
#97:4
"Take a country name and return a list of appropriate aliases for a guest from it.";
if (site = args[1])
return {site, site + " guest"};
else
return E_ARGS;
endif
.
#97:5
return {@pass(@args), $list_utils, $guest, $string_utils, $player_db, $hacker, $wiz_utils, $player_start};
.
#97:6
if ($__core_init_phase)
pass(@args);
this.cartographers = {};
endif
.
#98:0
if (caller == this)
return tostr("I", this.next_id = this.next_id + 1);
else
raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#98:1
{message, alist} = args;
cord = caller;
session = cord.session;
if (cord in $cord.registry)
return this:send_(session, tostr(cord.id), message, @alist);
else
raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#98:2
cord = caller;
session = cord.session;
this:send_closed(session, tostr(cord.id));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#98:3
{session, id, message, @assocs} = args;
if (caller == this)
$cord:mcp_receive(id, message, assocs);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#98:4
{session, id, @rest} = args;
if (caller == this)
$cord:mcp_closed(id);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#98:5
{name} = args;
for i in ($object_utils:leaves($cord.type_root))
if (name == (($mcp.registry:package_name(i.parent_package) + "-") + i.cord_name))
return i;
endif
endfor
return $failed_Match;
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#98:6
if (caller == $cord)
return pass(@args);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#98:7
session = caller;
len = length($cord.registry_ids);
for i in [0..len - 1]
idx = len - i;
cord = $cord.registry[idx];
if (cord.session == session)
$recycler:_recycle(cord);
endif
endfor
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#98:8
{cord_type} = args;
parent = $mcp:package_name(cord_type.parent_package);
if (suffix = cord_type.cord_name)
return (parent + "-") + suffix;
else
return parent;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#99:0
target = args[2];
link = args[1];
"target:contribute_link(link);";
$jtext:convert_to("linemode", link[3], target);
target:contribute_link(link);
.
#99:1
target = args[2];
link = args[1];
"target:contribute_link(link);";
target:contribute_partial("{link {unavailable} ");
$jtext:convert_to("tkmoo", link[3], target);
target:contribute_partial("}");
"target:contribute_link(link);";
.
#99:2
target = args[2];
link = args[1];
"target:contribute_link(link);";
target:contribute_partial("(link ");
$jaddress:convert_to("lemoo", link[2], target);
target:contribute_partial(" ");
$jtext:convert_to("lemoo", link[3], target);
target:contribute_partial(")");
"target:contribute_link(link);";
.
#99:3
target = args[2];
link = args[1];
"target:contribute_link(link);";
address = link[2];
url = address[1]:to_url(address);
target:contribute_partial("<A HREF=\"", url, "\">");
$jtext:convert_to("html", link[3], target);
target:contribute_partial("</A>");
.
#100:0
args[2]:contribute_reset();
args[2]:contribute_title(args[1]);
.
#100:1
args[2]:contribute_reset();
args[2]:contribute_title(args[1]);
.
#100:2
args[2]:contribute_reset();
args[2]:contribute_title(args[1]);
.
#100:3
args[2]:contribute_reset();
args[2]:contribute_title(args[1]);
args[2]:contribute_partial("<HEAD>");
args[2]:contribute_partial("<TITLE>", args[1][2], "</TITLE>");
args[2]:contribute_partial("</HEAD>");
args[2]:contribute_newline();
"args[2]:contribute_partial(\"<BODY BGCOLOR=\\\"#000000\\\" TEXT=\\\"#FFFFFF\\\" link=\\\"0099ff\\\" vlink=\\\"#00ff99\\\" ALINK=\\\"#FF00FF\\\">\");";
args[2]:contribute_partial("<BODY>");
args[2]:contribute_newline();
"args[2]:contribute_partial(\"<A HREF=\\\"/browser_object/objnum!%232\\\">Jay</A> set JHM's web pages to black for 48 hours to protest second-class treatment from the US Government for free speech.  <A href=\\\"http://www.cdt.org/speech.html\\\">Read about it at this WWW page.</a><p>\");";
"args[2]:contribute_newline();";
.
#101:0
":redlisted(hostname)   => is hostname on the .redlist";
sitelist = this.redlist;
if (!caller_perms().wizard)
return E_PERM;
elseif (((hostname = args[1]) in sitelist[1]) || (hostname in sitelist[2]))
return 1;
elseif ($site_db:domain_literal(hostname))
for lit in (sitelist[1])
if ((index(hostname, lit) == 1) && ((hostname + ".")[length(lit) + 1] == "."))
return 1;
endif
endfor
else
for dom in (sitelist[2])
if (index(dom, "*"))
"...we have a wildcard; let :match_string deal with it...";
if ($string_utils:match_string(hostname, dom))
return 1;
endif
else
"...tail of hostname ...";
if ((r = rindex(hostname, dom)) && ((("." + hostname)[r] == ".") && (((r - 1) + length(dom)) == length(hostname))))
return 1;
endif
endif
endfor
endif
return 0;
"Copied from Login Commands (#12):redlisted by Ken (#75) Sun Jun  2 02:47:21 1996 EDT";
return;
"Metadata 202106";
"Last-Modify: {833698149, \"Ken\", #75, \"JHM\"}";
.
#101:1
{who} = args;
if (caller != this)
raise(E_PERM);
endif
hostname = $string_utils:connection_hostname(connection_name(player));
if (this:Redlisted(hostname))
return 0;
elseif (this:check_flood(hostname))
return 0;
endif
return 1;
"Metadata 202106";
"Last-Modify: {834374313, \"Ken\", #75, \"JHM\"}";
.
#101:2
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
where = args[1];
which = 1 + (!$site_db:domain_literal(where));
this.redlist[which] = setadd(this.redlist[which], where);
return 1;
"Metadata 202106";
"Last-Modify: {833698864, \"Ken\", #75, \"JHM\"}";
.
#101:3
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
where = args[1];
which = 1 + (!$site_db:domain_literal(where));
if (where in this.redlist[which])
this.redlist[which] = setremove(this.redlist[which], where);
return 1;
else
return E_INVARG;
endif
return;
"Metadata 202106";
"Last-Modify: {833698876, \"Ken\", #75, \"JHM\"}";
.
#101:4
return 0;
{host} = args;
if (caller != this)
raise(E_PERM);
endif
"make sure host isn't flooding us";
"flush old records";
now = time();
for n in [-length(this.floodcounters)..-1]
if ((now - this.floodcounters[-n][2]) > 60)
this.floodcounters = listdelete(this.floodcounters, -n);
endif
endfor
"make sure this host isn't spamming us";
if (idx = $list_utils:assoc(host, this.floodcounters))
{_, when, count} = this.floodcounters[idx];
else
this.floodcounters = {@this.floodcounters, {host, when = time(), count = 0}};
idx = length(this.floodcounters);
endif
if (count >= 30)
return 1;
else
count = count + 1;
this.floodcounters[idx] = {host, when, count};
return 0;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {834374852, \"Ken\", #75, \"JHM\"}";
.
#102:0
vgroup = args[1];
target = args[2];
for vbox in (vgroup[2..length(vgroup)])
$jtext:convert_to("linemode", vbox, target);
endfor
.
#102:1
vgroup = args[1];
target = args[2];
for vbox in (vgroup[2..length(vgroup)])
$jtext:convert_to("tkmoo", vbox, target);
endfor
.
#102:2
vgroup = args[1];
target = args[2];
for vbox in (vgroup[2..length(vgroup)])
$jtext:convert_to("lemoo", vbox, target);
endfor
.
#102:3
vgroup = args[1];
target = args[2];
for vbox in (vgroup[2..length(vgroup)])
$jtext:convert_to("html", vbox, target);
$command_utils:suspend_if_needed(0);
endfor
.
#102:4
return {@pass(@args), $jtext, $command_utils};
.
#102:5
vgroup = args[1];
target = args[2];
for vbox in (vgroup[2..length(vgroup)])
$jtext:convert_to("mcp", vbox, target);
endfor
"Copied from Vgroup Tag (#620):to_lemoo by Erik (#74) Sun Aug 13 15:42:41 1995 EDT";
.
#103:0
hgroup = args[1];
target = args[2];
for pair in (hgroup[2..length(hgroup)])
$jtext:convert_to("linemode", pair[1], target);
target:contribute_newline();
target:contribute_partial("      ");
"target:contribute_partial(\"  --  \");";
"if ($jtext:tag_class(pair[2]) == $jtext.paragraph)";
"pair[2][1] = $jtext.hgroup;";
"endif";
$jtext:convert_to("linemode", pair[2], target);
target:contribute_newline_maybe();
endfor
target:contribute_newline();
.
#103:1
hgroup = args[1];
target = args[2];
target:contribute_newline();
for pair in (hgroup[2..length(hgroup)])
target:contribute_partial("(paragraph ");
$jtext:convert_to("lemoo", pair[1], target);
target:contribute_partial("(text \"  --  \")");
$jtext:convert_to("lemoo", pair[2], target);
target:contribute_partial(")");
target:contribute_newline();
endfor
target:contribute_newline();
.
#103:2
hgroup = args[1];
target = args[2];
target:contribute_partial("<DL>");
target:contribute_newline();
for pair in (hgroup[2..length(hgroup)])
target:contribute_partial("<DT>");
$jtext:convert_to("html", pair[1], target);
target:contribute_partial("<DD>");
$jtext:convert_to("html", pair[2], target);
target:contribute_newline();
endfor
target:contribute_partial("</DL>");
target:contribute_newline();
.
#103:3
"Copied from Definition List Tag (#651):to_lemoo by Ron (#267) Fri May 21 21:47:29 1993 EDT";
hgroup = args[1];
target = args[2];
for pair in (hgroup[2..length(hgroup)])
target:contribute_partial("{hgroup ");
$jtext:convert_to("tkmoo", pair[1], target);
target:contribute_partial(" {~  --  } ");
$jtext:convert_to("tkmoo", pair[2], target);
target:contribute_partial("} ");
target:contribute_newline();
endfor
target:contribute_newline();
.
#104:0
"Self-destruct on logout.";
if (!(caller in {#0, this}))
return E_PERM;
endif
fork (60)
if (this in connected_players())
"You live .. this time.";
return;
endif
fork (0)
$wiz_utils:unset_player(this);
$recycler:_recycle(this);
endfork
for o in (setremove(this.owned_objects, this))
$recycler:_recycle(o);
endfor
endfork
pass(@args);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {881441393, \"Ken\", #75, \"JHM\"}";
.
#104:1
if (player != this)
player:tell(E_PERM);
return E_PERM;
endif
dobj = player:my_match_player(dobjstr);
if (dobj == $nothing)
player:notify(tostr("Usage:  ", verb, " me"));
elseif ($command_utils:player_match_result(dobj, dobjstr)[1])
elseif (dobj != this)
player:notify("Sorry, you can only @programmer! yourself.");
elseif (!$object_utils:isa(player, $player))
"...They chparented to #1 and want to chparent back to $prog.  Probably for some nefarious purpose...";
player:notify("You don't seem to already be a valid player class.  Perhaps chparenting away from the $player hierarchy was not such a good idea.  Permission denied.");
elseif (!this.public_identity.programmer)
player:notify("Sorry, only programmers can @programmer their test characters.");
elseif (result = $wiz_utils:set_programmer(this))
player:notify("You're now a programmer.  Congratulations.  DARK WIZARDS ARE LOGGING EVERYTHING.");
player.location:announce(this:namec(), " becomes a programmer.");
elseif (result == E_NONE)
player:notify("Too late!  Apparently somebody already @programmered you!");
else
player:notify(tostr(result));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863670400, \"Erik\", #74, \"JHM\"}";
.
#104:2
return {@pass(@args), @valid(this.public_identity) ? {tostr("(This is a testing character for ", this.public_identity:dname(), ".)")} | {}};
"Metadata 202106";
"Last-Modify: {857202488, \"splat\", #3024, \"JHM\"}";
.
#104:3
"Copied from generic player (#6):@quit by Erik (#2) Sat Apr 16 14:10:22 1994 EDT";
if ($command_utils:yes_or_no(tostr("Disconnecting will recycle this character", (this.owned_objects != {this}) ? " and all its owned objects" | "", ".  Quit anyway?")))
boot_player(player);
else
player:tell("OK.");
endif
"-- argh, let the player decide; #3:disfunc() takes care of this --Rog";
"player:moveto(player.home)";
.
#105:0
"Given a string, output a string that has HTML entities in place of reserved characters.";
line = args[1];
line = strsub(line, "&", "&amp;");
line = strsub(line, "<", "&lt;");
return strsub(line, ">", "&gt;");
.
#106:0
set_task_perms(caller_perms());
if (!args)
return {};
endif
if ($object_utils:has_callable_verb(player, "features") && (typeof(player:features()) == LIST))
help = {};
topic = args[1];
for search in (player:features())
if (((valid(search) && (where = $object_utils:has_callable_verb(search, topic))) && (vnames = verb_info(where[1], topic)[3])) && (topic in $string_utils:explode(strsub(vnames, "*", ""))))
help = {@help, tostr(where[1], ":", topic)};
endif
endfor
if (help)
return help;
elseif (index(topic, ":"))
return pass(@args);
else
return {};
endif
else
return {};
endif
.
#106:1
"{\"*index*\" [, title]}";
"This produces a columnated list of topics in this help db, headed by title.";
$command_utils:suspend_if_needed(0);
return {};
title = args[1] ? args[1][1] | tostr(this:name(), " (", this, ")");
su = $string_utils;
return {{$jtext.header, 3, title}, {$jtext.paragraph, "This database can't be indexed."}};
return {{$jtext.header, 3, title}, {$jtext.plaintext, @this:columnize(@this:sort_topics(this:find_topics())), ""}};
return {"", title, su:from_list($list_utils:map_arg(su, "space", su:explode(title), "-"), " "), @this:columnize(@this:sort_topics(this:find_topics()))};
.
#107:0
hgroup = args[1];
target = args[2];
for hbox in (hgroup[2..length(hgroup)])
$jtext:convert_to("linemode", hbox, target);
endfor
target:contribute_newline();
target:contribute_newline();
.
#107:1
hgroup = args[1];
target = args[2];
target:contribute_partial("{hgroup");
for hbox in (hgroup[2..length(hgroup)])
target:contribute_partial(" ");
$jtext:convert_to("tkmoo", hbox, target);
endfor
target:contribute_partial("}");
target:contribute_newline();
target:contribute_newline();
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835905886, \"Erik\", #74, \"JHM\"}";
.
#107:2
"Copied from Paragraph Tag (#696):to_tkmoo by Jonny (#88) Sat Feb 13 14:47:56 1993 CST";
hgroup = args[1];
target = args[2];
target:contribute_partial("(hgroup");
for hbox in (hgroup[2..length(hgroup)])
target:contribute_partial(" ");
$jtext:convert_to("lemoo", hbox, target);
endfor
target:contribute_partial(")");
target:contribute_newline();
target:contribute_newline();
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835905894, \"Erik\", #74, \"JHM\"}";
.
#107:3
hgroup = args[1];
target = args[2];
target:contribute_partial("<P>");
for hbox in (hgroup[2..length(hgroup)])
$jtext:convert_to("html", hbox, target);
endfor
target:contribute_newline();
"target:contribute_newline();";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835905907, \"Erik\", #74, \"JHM\"}";
.
#108:0
header = args[1];
target = args[2];
header_level = header[2];
$jtext:convert_to("linemode", header[3], target);
"By special arrangement, :contribute_newline will uppercase its line given a true argument.   Ewwwww.";
length = target:contribute_newline(header_level < 3);
if ((header_level == 1) || (header_level == 3))
target:contribute_partial($string_utils:space(length, "="));
target:contribute_newline();
elseif ((header_level == 2) || (header_level == 4))
target:contribute_partial($string_utils:space(length, "-"));
target:contribute_newline();
endif
target:contribute_newline();
.
#108:1
header = args[1];
target = args[2];
target:contribute_partial("{header ");
$jtext:convert_to("tkmoo", header[3], target);
target:contribute_partial("}");
target:contribute_newline();
target:contribute_newline();
.
#108:2
header = args[1];
target = args[2];
target:contribute_partial(("(header " + tostr(header[2])) + " ");
"Hack to make headers work for me....  ^^^^^^^^^^^^^^^^^^^^^^";
$jtext:convert_to("lemoo", header[3], target);
target:contribute_partial(")");
target:contribute_newline();
target:contribute_newline();
.
#108:3
header = args[1];
target = args[2];
target:contribute_partial("<H", header[2], ">");
$jtext:convert_to("html", header[3], target);
target:contribute_partial("</H", header[2], ">");
target:contribute_newline();
target:contribute_newline();
.
#110:0
target = args[2];
link = args[1];
"target:contribute_link(link);";
$jtext:convert_to("linemode", link[3], target);
target:contribute_link(link);
.
#110:1
target = args[2];
link = args[1];
"target:contribute_link(link);";
target:contribute_partial("{link ");
$jaddress:convert_to("tkmoo", link[2], target);
target:contribute_partial(" ");
$jtext:convert_to("tkmoo", link[3], target);
target:contribute_partial("}");
"target:contribute_link(link);";
.
#110:2
"Copied from Browser Link Tag (#597):to_lemoo by Ron (#267) Sun May 16 20:39:31 1993 EDT";
target = args[2];
link = args[1];
"target:contribute_link(link);";
target:contribute_partial("(link ");
$jaddress:convert_to("lemoo", link[2], target);
target:contribute_partial(" ");
$jtext:convert_to("lemoo", link[3], target);
target:contribute_partial(")");
"target:contribute_link(link);";
"Copied from Link Tag (#755):to_lemoo by Erik (#74) Fri Mar 17 16:19:57 1995 EST";
.
#110:3
target = args[2];
link = args[1];
"target:contribute_link(link);";
address = link[2];
url = address[1]:to_url(address);
target:contribute_partial("<A HREF=\"", url, "\">");
$jtext:convert_to("html", link[3], target);
target:contribute_partial("</A>");
.
#110:4
return {@pass(@args), $jtext, $jaddress};
.
#110:5
"Copied from Browser Link Tag (#597):to_lemoo by Ron (#267) Sun May 16 20:39:31 1993 EDT";
target = args[2];
link = args[1];
"target:contribute_link(link);";
target:contribute_partial("(link ");
$jaddress:convert_to("mcp", link[2], target);
target:contribute_partial(" ");
$jtext:convert_to("mcp", link[3], target);
target:contribute_partial(")");
"target:contribute_link(link);";
"Copied from Link Tag (#755):to_lemoo by Erik (#74) Fri Mar 17 16:19:57 1995 EST";
"Copied from Link Tag (#755):to_lemoo by Erik (#74) Fri Mar 17 16:20:21 1995 EST";
.
#113:0
if (pass(@args))
return 1;
else
who = args[1];
for group in ((verb[4] == "u") ? this.moderator_groups | this.reader_groups)
if (group:is_member(who))
return 1;
endif
endfor
return 0;
endif
.
#113:1
return (valid(grp = this.owner_group) && grp:is_member(args[1])) || pass(@args);
.
#114:0
return (args[1] in this.trusted) || args[1].wizard;
.
#114:1
if ($__core_init_phase)
this.primary = {};
this.secondary = {};
this.trusted = {$quota_utils.owner};
this.log = {callers()};
"set_verb_code(this, \"trust\", {\"return ((p=args[1]).wizard || p in this.trusted);\"});";
" set_verb_code(this, \"add_log\", {\"if (caller == this)\", \"return this.log={@this.log,{callers(),time(),@args}};\", \"else\", \"return E_PERM;\", \"endif\"});";
"delete_property(this, \"no_log_objects\");";
pass(@args);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {865047437, \"Ken\", #75, \"JHM\"}";
.
#114:2
":add_relation( master, slave );";
"=> E_PERM [if not allowed to use this verb]";
"=> E_ARGS/E_INVARGS [if passed bogus arguments]";
"=> E_RECMOVE [if passed recursive registrations]";
"=> E_NACC [if 'slave' is already registered to someone";
"=> E_QUOTA [if a Guest character is registered]";
"=> 1 [if all goes well]";
this:add_log(@args);
if (!this:trust(caller_perms()))
return E_PERM;
elseif (length(args) != 2)
return E_ARGS;
elseif (((typeof(master = args[1]) != OBJ) || (!valid(master))) || (!is_player(master)))
return E_INVARG;
elseif (((typeof(slave = args[2]) != OBJ) || (!valid(slave))) || (!is_player(slave)))
return E_INVARG;
elseif (this:is_second_char(master))
"no recursive registration";
return E_RECMOVE;
elseif (this:is_registered_primary_char(slave))
"no recursive registration";
return E_RECMOVE;
elseif (slave in this.secondary)
"already registered?";
return E_NACC;
elseif ($object_utils:isa(slave, $guest))
"guests don't get registered";
return E_QUOTA;
elseif ($object_utils:isa(master, $guest))
"guests don't get registered";
return E_QUOTA;
else
this.primary = {@this.primary, master};
this.secondary = {@this.secondary, slave};
return 1;
endif
.
#114:3
if (caller != this)
return E_PERM;
endif
cl = callers();
"vb = what verb is logging?";
vb = cl[1][2];
"     unwind to caller of Registrar";
while (cl && (cl[1][1] == this))
cl = listdelete(cl, 1);
endwhile
cl1 = cl[1];
cll = cl[length(cl)];
"who = player at top of stack";
who = cll[5];
last = this.log[1];
if ((who != last[1]) || (argstr != last[2]))
"different verb call";
if ((!(cl1[1] in this.no_log_objects)) && (!cl1[3].wizard))
"some objects access registration but do not reveal it; wiz-permed callers might as well be trusted since wizperms could just read .primary and .secondary";
this.log = {{who, argstr, vb, time(), cll[2], @args}, @this.log[1..min(200, length(this.log))]};
endif
endif
.
#114:4
":clean_relations()";
"=> E_PERM [if not allowed to use this verb]";
"=> 1 [if registration information is cleaned";
this:add_log(@args);
if (this:trust(caller_perms()))
len = length(this.primary);
for y in [1..len]
x = (len - y) + 1;
while ((((!valid(master = this.primary[x])) || (!valid(slave = this.secondary[x]))) || (!is_player(master))) || (!is_player(slave)))
this.primary = listdelete(this.primary, x);
this.secondary = listdelete(this.secondary, x);
endwhile
endfor
return 1;
else
return E_PERM;
endif
.
#114:5
":primary_char( player )";
"=> E_PERM [if not permitted to use this verb]";
"=> <player> [referring to the primary registered character";
this:add_log(@args);
if (!this:trust(caller_perms()))
return E_PERM;
elseif (!is_player(p = args[1]))
return E_INVARG;
elseif (loc = p in this.secondary)
return this.primary[loc];
else
return p;
endif
.
#114:6
"other_chars(player)";
" => {all OTHER players this might be, possibly an empty list}";
"all_chars(player)";
" => {all players this might be, never empty}";
"other_shared_chars(player)";
"other_second_chars(player)";
"all_shared_chars(player)";
"all_second_chars(player)";
" => see above, but special information only";
if (caller != this)
this:add_log(@args);
endif
if (!this:trust(caller_perms()))
return E_PERM;
elseif (!is_player(who = args[1]))
return E_INVARG;
elseif (who in this.shared)
return E_INVARG;
else
if (loc = who in this.secondary)
primary = this.primary[loc];
else
primary = who;
endif
all = {primary};
if (!index(verb, "shared"))
for x in [1..length(this.primary)]
if (this.primary[x] == primary)
all = setadd(all, this.secondary[x]);
endif
endfor
endif
if (!index(verb, "second"))
for x in [1..length(this.shared_owners)]
if (primary in this.shared_owners[x])
all = setadd(all, this.shared[x]);
endif
endfor
endif
if (index(verb, "other"))
all = setremove(all, who);
endif
result = {};
for x in (all)
if (is_player(x))
result = {@result, x};
endif
endfor
return result;
endif
.
#114:7
":all_relations()";
"=> E_PERM [if perms not trusted";
"=> list of all relations in the following form:";
"   { { primary, secondary [, others ] } [ , { ... } ... ] }";
this:add_log(@args);
if (!this:trust(caller_perms()))
return E_PERM;
else
primary = this.primary;
secondary = this.secondary;
all = {};
seen = {};
for x in [1..length(primary)]
master = primary[x];
if (master in seen)
for y in [1..length(all)]
if (master in all[y])
all[y] = setadd(all[y], secondary[x]);
endif
endfor
else
all = {@all, {master, secondary[x]}};
endif
endfor
return all;
endif
.
#114:8
":delete_relation( master, slave)";
"=> E_PERM [if perms don't allow etc]";
"=> E_INVARG [if the link doesn't exist]";
"=> 1 [if existed and now deleted]";
this:add_log(@args);
if (!this:trust(caller_perms()))
return E_PERM;
elseif (!((master = args[1]) in (p = this.primary)))
return E_INVARG;
elseif (!((slave = args[2]) in (s = this.secondary)))
return E_INVARG;
else
for x in [1..length(p)]
if ((p[x] == master) && (s[x] == slave))
this.primary = listdelete(p, x);
this.secondary = listdelete(s, x);
return 1;
endif
endfor
return E_INVARG;
endif
.
#114:9
":same_as ( Fred, Barney )";
"=> E_PERM [if perms don't permit]";
"=> true/false [is Fred linked to Barney?]";
this:add_log(@args);
if (!this:trust(caller_perms()))
return E_PERM;
else
return args[2] in this:all_chars(args[1]);
endif
.
#114:10
":is_primary_char( dude )";
":is_secondary_char( dude )";
"=> E_PERM [if perms don't permit]";
"=> true/false";
if (caller != this)
this:add_log(@args);
endif
if (!this:trust(caller_perms()))
return E_PERM;
elseif (verb == "is_primary_char")
return !((args[1] in this.secondary) || (args[1] in this.shared));
else
return (args[1] in this.secondary) || (args[1] in this.shared);
endif
.
#114:11
":add_shared( shared, owner )";
"  'shared' is a player object shared by more than one player";
"  'owner' is a player or a list of players who share the shared char";
"  if 'shared' is already entered as a list, and 'owner' is not a list, then 'owner' is -added- as an additional owner of 'shared'.";
this:add_log(@args);
if (!this:trust(caller_perms()))
return E_PERM;
elseif ((!valid(shared = args[1])) || (!is_player(shared)))
return E_INVARG;
elseif ($object_utils:isa(shared, $guest))
return E_QUOTA;
elseif (shared in this.secondary)
return E_NACC;
elseif (shared in this.primary)
return E_RECMOVE;
else
if (loc = shared in this.shared)
old_owners = this.shared_owners[loc];
else
old_owners = {};
endif
if (typeof(owners = args[2]) == OBJ)
new_owners = {@old_owners, owners};
elseif (typeof(owners) == LIST)
new_owners = owners;
else
return E_INVARG;
endif
for x in (new_owners)
if ((((((!valid(x)) || (!is_player(x))) || (x in this.secondary)) || (x in this.shared)) || (x == shared)) || $object_utils:isa(x, $guest))
new_owners = setremove(new_owners, x);
endif
endfor
if (loc)
this.shared_owners[loc] = new_owners;
else
this.shared = {@this.shared, shared};
this.shared_owners = {@this.shared_owners, new_owners};
endif
return {shared, new_owners};
endif
.
#114:12
":is_registered_primary_char( who )";
"  not just 'is who a primary character' but 'has who been -registered- as such?";
if (caller != this)
this:add_log(@args);
endif
if (!this:trust(caller_perms()))
return E_PERM;
elseif (length(args) != 1)
return E_ARGS;
elseif (typeof(who = args[1]) != OBJ)
return E_INVARG;
elseif (!valid(who))
return E_INVIND;
elseif (!is_player(who))
return E_INVARG;
elseif (who in this.primary)
return 1;
else
for i in (this.shared_owners)
if (who in i)
return 1;
endif
endfor
return 0;
endif
.
#114:13
":delete_shared ( shared [ , owner ] )";
"  deletes the 'shared' link.  if owner is specified (as an object or as a list), the link is only deleted for the specified owner(s).  (if all owners are deleted, the link is deleted.)";
this:add_log(@args);
if (!this:trust(caller_perms()))
return E_PERM;
elseif (!(loc = (shared = args[1]) in this.shared))
return E_INVARG;
else
if (length(args) == 1)
owners = {};
elseif (typeof(owners = args[2]) == OBJ)
owners = {owners};
endif
old_owners = this.shared_owners[loc];
for x in (owners)
old_owners = setremove(old_owners, x);
endfor
for x in (old_owners)
if ((!valid(x)) || (!is_player(x)))
old_owners = setremove(old_owners, x);
endif
endfor
if (old_owners)
this.shared_owners[loc] = old_owners;
else
this.shared = listdelete(this.shared, loc);
this.shared_owners = listdelete(this.shared_owners, loc);
endif
endif
.
#114:14
this:add_log(@args);
if (this:trust(caller_perms()))
while (x = args[1] in this.primary)
this.primary = listdelete(this.primary, x);
this.secondary = listdelete(this.secondary, x);
endwhile
while (x = args[1] in this.secondary)
this.secondary = listdelete(this.secondary, x);
this.primary = listdelete(this.primary, x);
endwhile
endif
.
#115:0
link = args[1];
target = args[2];
target:contribute_partial("(", link[1].address_type, " (");
for assoc in (link[2])
target:contribute_partial("(", $string_utils:print(assoc[1]), " ", $string_utils:print(assoc[2]), ")");
endfor
target:contribute_partial(")", ")");
.
#115:1
link = args[1];
target = args[2];
target:contribute_partial(" { address-type: ", link[1].address_type, " args: \" ");
for assoc in (link[2])
target:contribute_partial(assoc[1], ": ", assoc[2], " ");
endfor
target:contribute_partial("\"}");
.
#115:2
address = args[1];
if (search = $jaddress:extract("search", address))
searchstr = "?" + this:keyword_or_value_to_url(search[2]);
alist = setremove(address[2], search);
else
searchstr = "";
alist = address[2];
endif
return ("/" + $string_utils:from_list({this.address_type, @$list_utils:map_arg(this, "keyword_and_value_to_url", alist)}, "/")) + searchstr;
.
#115:3
s = tostr(this:keyword_or_value_to_url(args[1][1]), "!", this:keyword_or_value_to_url(args[1][2]));
return s;
.
#115:4
s = tostr(args[1]);
r = "";
for n in [1..length(s)]
c = s[n];
if (index("abcdefghijklmnopqrstuvwxyz0123456789$-@^&*().", c))
r = r + c;
else
r = tostr(r, "%", $string_utils:character_to_hex_ascii(c));
endif
endfor
"Added the following  lines because spaces only occur in search keywords. RT";
if (length(rs = $string_utils:explode(r)) > 1)
r = $string_utils:from_list(rs, "+");
endif
return r;
return strsub(r, "-", "_");
.
#115:5
s = tostr(args[1][1], ":", args[1][2]);
r = "";
for n in [1..length(s)]
c = s[n];
if (index("abcdefghijklmnopqrstuvwxyz0123456789$_@!^&*().", c))
r = r + c;
else
r = tostr(r, "%", $string_utils:character_to_hex_ascii(c));
endif
endfor
return r;
.
#115:6
"Definition from #871";
ja = $jaddress;
alist = {};
for pair in ($string_utils:explode(args[1], "/"))
bang = index(pair, "!");
if (!bang)
return E_INVARG;
else
alist = listappend(alist, {ja:unescape_for_url(pair[1..bang - 1]), ja:unescape_for_url(pair[bang + 1..length(pair)])});
endif
endfor
return {this, alist};
.
#115:7
return $object_utils:has_property(this, verb) ? this.(verb) | E_PROPNF;
.
#115:8
return this.optional_attributes;
.
#115:9
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
pass(@args);
type = this.address_type;
if ($jaddress.(type) == this)
delete_property($jaddress, type);
endif
endif
.
#115:10
return {@pass(@args), $string_utils, $jaddress, $list_utils, $object_utils, $perm_utils};
.
#115:11
"Usage: :from_user() => <jaddress>";
"";
"Prompt the user for values for address types attributes. A jaddress is returned (if possible).";
attributes = {};
for attribute in (this.attributes)
mine = attribute:from_user(this:("default_" + attribute:attribute_name())(), attribute in this:optional_attributes());
if (mine)
attributes = {@attributes, mine};
endif
endfor
return {this, attributes};
.
#115:12
if ($__core_init_phase)
pass(@args);
if (this == $jaddress.root)
delete_property(this, "attributes");
delete_property(this, "optional_attributes");
endif
endif
.
#115:13
"Usage:  :help_text()";
"";
return this.description;
.
#116:0
":add_dependent(object) -- object will now recieve any events broadcast from this dispatching object.";
object = args[1];
if ((!$perm_utils:controls(caller_perms(), object)) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
this.dependents = setadd(this.dependents, object);
.
#116:1
":remove_dependent(object) -- object will no longer recieve events broadcasted from this dispatcher.";
object = args[1];
if ((!$perm_utils:controls(caller_perms(), object)) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
this.dependents = setremove(this.dependents, object);
.
#116:2
"Usage: @rm-dependent <dependent> from <this>";
"Ex:    @rm-dependent hidden mike from here";
set_task_perms(player);
dep = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dep, dobjstr))
return 0;
endif
error = this:remove_dependent(dep);
if (typeof(error) == ERR)
player:tell(error);
else
player:tell(((dep:dnamec() + " removed as a dependent from ") + this:dname()) + ".");
endif
"Last modified Wed May 22 02:57:27 1996 PDT by Hobbes, #2305@Pueblo.";
return;
"Metadata 202106";
"Last-Modify: {832761365, \"Doug\", #107, \"JHM\"}";
.
#116:3
"Usage:   @add-dependent <dependent> to <this>";
"Example: @add-dependent hidden mike to here";
set_task_perms(player);
dep = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dep, dobjstr))
return 0;
endif
error = this:add_dependent(dep);
if (typeof(error) == ERR)
player:tell(error);
else
player:tell(((dep:dnamec() + " added as a dependent to ") + this:dname()) + ".");
endif
"Last modified Wed May 22 02:57:27 1996 PDT by Hobbes, #2305@Pueblo.";
return;
"Metadata 202106";
"Last-Modify: {832761177, \"Doug\", #107, \"JHM\"}";
.
#116:4
this.dependents = {};
return pass(@args);
.
#116:5
":broadcast_event_<type>(@event-args) -- broadcasts the event <type> with args `event-args' to all objects listening to this dispatcher.";
"if (!$perm_utils:controls(caller_perms(), this))";
"  return E_PERM;";
"endif";
name = verb[11..length(verb)];
retlist = {};
for o in (this:("dependents_" + name)(@args))
ret = o:(name)(@args);
if (ret)
retlist = {@retlist, ret};
endif
endfor
return retlist;
"Metadata 202106";
"Last-Modify: {875333201, \"Erik\", #74, \"JHM\"}";
.
#116:6
"Copied from event dispatcher (#1751):dependents by Doug (#444) Mon Apr 25 01:08:30 1994 EDT";
":dependents:  give the list of all dependents, to which all events could presumably be passed.";
"By default this simply returns a list.";
return this.dependents;
.
#116:7
if ($__core_init_phase)
pass(@args);
if (this != $event_dispatcher)
clear_property(this, "dependents");
endif
endif
.
#117:0
":send_me(jaddress, [source])  => send document specified by jaddress to caller";
"The address will be passed off to the address class object, where it will decide its favorite way to send that document to caller by interrogating the caller by currently unspecified means.";
{address, ?source} = args;
class = address[1];
return class:send_to(caller, @args);
"Metadata 202106";
"Last-Modify: {835906299, \"Erik\", #74, \"JHM\"}";
.
#117:1
"extract(field, address)";
return $list_utils:assoc(args[1], args[2][2]);
.
#117:2
vname = "to_" + args[1];
link = args[2];
target = args[3];
class = link[1];
return class:(vname)(link, target);
.
#117:3
s = tostr(args[1]);
r = "";
for n in [1..length(s)]
c = s[n];
if (index("abcdefghijklmnopqrstuvwxyz0123456789$-@^&*().", c))
r = r + c;
else
r = tostr(r, "%", $string_utils:character_to_hex_ascii(c));
endif
endfor
return strsub(r, "-", "_");
.
#117:4
s = tostr(this:keyword_or_value_to_url(args[1][1]), "!", this:keyword_or_value_to_url(args[1][2]));
return s;
.
#117:5
s = tostr(args[1]);
r = "";
for n in [1..length(s)]
c = s[n];
if (index("abcdefghijklmnopqrstuvwxyz0123456789$_!@^&*().", c))
r = r + c;
else
r = tostr(r, "%", $string_utils:character_to_hex_ascii(c));
endif
endfor
return r;
.
#117:6
"Since the PARC gateway seems to be gratuitously converting ! into %21, we have to move this up here, instead of the end where it belongs (and is commented out).";
URL = args[1];
URL = this:unescape_for_url(URL);
if (URL == "/")
"home page?  eeeee";
return this.home_page_jaddress;
return E_INVARG;
elseif (colon = index(URL, ":"))
"panic";
else
URL[1..1] = "";
"get rid of leading /";
slash = index(URL, "/");
question = index(URL, "?");
"note: according to spec, question must be after slash if slash exists.";
if (slash)
type = URL[1..slash - 1];
if (question)
rest = URL[slash..question - 1];
search = {URL[question + 1..length(URL)]};
else
rest = URL[slash..length(URL)];
search = 0;
endif
else
rest = "";
if (question)
type = URL[1..question - 1];
search = {URL[question + 1..length(URL)]};
else
type = URL;
search = 0;
endif
endif
"type = this:unescape_for_url(type);";
if (type in properties(this))
return this.(type):from_url(rest, @search ? {search[1]} | {});
else
return E_INVARG;
endif
endif
.
#117:7
s = args[1];
r = "";
while (p = index(s, "%"))
r = (r + s[1..p - 1]) + $string_utils:hex_ascii_to_character(s[p + 1..p + 2]);
s[1..p + 2] = "";
endwhile
return r + s;
.
#117:8
"Usage:  :from_pseudocommand(string)";
"";
"Given a command like `help mumbly' or `@proj frobnication', find a jaddress equivalent.";
string = args[1];
for type in (this.pseudocommand_candidates)
if (jaddress = type:from_pseudocommand(string))
return jaddress;
endif
endfor
.
#117:9
return {@pass(@args), $list_utils, $string_utils, $code_utils};
.
#117:10
if ($__core_init_phase)
pass(@args);
"We haven't resolved what happens to the home page (returned from :from_url(\"\")), so make sure that people are at least aware of it being broken.";
this.home_page_jaddress = E_INVARG;
this.pseudocommand_candidates = {this.help};
delete_property(this, "attribute");
endif
.
#117:11
string = args[1];
if (!string)
return $nothing;
endif
ps = properties(this);
if (string in ps)
return this.(string);
endif
match = "";
for p in (properties(this))
if (index(p, string))
if (match)
return $ambiguous_match;
else
match = p;
endif
endif
endfor
return match ? this.(match) | $failed_match;
.
#117:12
match = args[1];
string = args[2];
if (valid(match))
return;
elseif ($code_utils:toobj(string) != E_TYPE)
player:tell(string, " does not exist.");
elseif (match == $nothing)
player:tell("You must give the name of an address type.");
elseif (match == $failed_match)
player:tell("\"", string, "\" is not an address type.");
else
player:tell("There is more than one address type matching \"", string, "\".");
endif
return 1;
.
#117:13
"Usage:  :help_text()";
"";
props = properties(this);
text = {{$jtext.title, "HELP ON JADDRESS DISPATCHER"}, {$jtext.header, 1, "HELP ON JADDRESS DISPATCHER"}, {$jtext.paragraph, "The jaddress dispatcher is the registry for jaddresses as well as the entity responsible for dispatching things to jaddresses.  See help about ", $help:make_link("jaddresses"), "."}};
topics = {};
for i in ($list_utils:sort(props))
if (typeof(this.(i)) == OBJ)
topics = {@topics, {$jtext.link.root, {$jaddress.help, {{"subject", tostr(this.(i))}}}, "$jaddress." + i}};
endif
endfor
return {@text, {$jtext.index, @topics}};
.
#118:0
if (i = args[1] in {"verbose"})
return {{{"brief"}[i], !args[2]}};
elseif (args[1] == "timestamp")
return {{"tstamp", args[2]}};
else
return {args};
endif
.
#118:1
if (o = (name = args[2]) in {"verbose"})
args[2] = {"brief"}[o];
return {@pass(@args), tostr("(", name, " is a synonym for -", args[2], ")")};
else
return pass(@args);
endif
.
#118:2
value = this:get(@args);
l = {};
if (!value)
return {0, {"Don't time stamp."}};
elseif (value > 0)
return {"", {"Time stamp after event at: " + tostr(value, " minute", (value == 1) ? "" | "s")}};
else
return {"", {"Time stamp before event at: " + tostr(abs(value), " minute", (value == -1) ? "" | "s")}};
endif
.
#118:3
value = this:get(@args);
if (!value)
value = $player:misc_option("fstamp");
endif
if (typeof(value) != STR)
value = $player:misc_option("fstamp");
endif
return {"", {"Format string: " + value, "looks like: " + $player:timestamp_string(value, 0)}};
.
#118:4
"... we'll take anything...";
raw = args[2];
if (raw == 1)
"...+@mail => @mailo=new";
return {args[1], ""};
else
return args[1..2];
endif
"Copied from Mail Options (#63):parse_@mail by Ken (#75) Sun Oct 16 17:53:24 1994 EDT";
.
#118:5
oname = args[1];
raw = args[2];
if (typeof(raw) == LIST)
raw = raw[1];
endif
if (typeof(raw) == NUM)
if (raw == 1)
return "Please supply a number of minutes.";
endif
return {oname, raw};
endif
if ((value = $code_utils:tonum(raw)) == E_TYPE)
return tostr("`", raw, "'?  Number expected.");
endif
return {oname, value};
"Copied from Mail Options (#63):parse_manymsgs by Doug (#107) Sun Jun  5 17:53:25 1994 EDT";
"Copied from Eval Options (#1459):parse_ticks by Ken (#75) Sun Oct 16 21:26:36 1994 EDT";
.
#119:0
"Calls built-in create.";
set_task_perms(caller_perms());
return create(@args);
.
#119:1
return args[1].ownership_quota > 0;
.
#119:2
return 1;
.
#119:3
who = args[1];
if (caller_perms() == who)
q = who.ownership_quota;
total = (typeof(who.owned_objects) == LIST) ? length(setremove(who.owned_objects, who)) | 0;
if (q == 0)
player:tell(tostr("You can't create any more objects", (total < 1) ? "." | tostr(" until you recycle some of the ", total, " you already own.")));
else
player:tell(tostr("You can create ", q, " new object", (q == 1) ? "" | "s", (total == 0) ? "." | tostr(" without recycling any of the ", total, " that you already own.")));
endif
else
if ($perm_utils:controls(caller_perms(), who))
player:tell(tostr(who.name, "'s quota is currently ", who.ownership_quota, "."));
else
player:tell("Permission denied.");
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835906313, \"Erik\", #74, \"JHM\"}";
.
#119:4
if ($perm_utils:controls(caller_perms(), args[1]) || (caller == this))
return args[1].ownership_quota;
else
return E_PERM;
endif
.
#119:5
"Charge args[1] for the quota required to own args[2]";
if ((caller == this) || caller_perms().wizard)
args[1].ownership_quota = args[1].ownership_quota - 1;
else
return E_PERM;
endif
.
#119:6
"Reimburse args[1] for the quota required to own args[2]";
if ((caller == this) || caller_perms().wizard)
args[1].ownership_quota = args[1].ownership_quota + 1;
else
return E_PERM;
endif
.
#119:7
"Set args[1]'s quota to args[2]";
if (caller_perms().wizard || (caller == this))
return args[1].ownership_quota = args[2];
else
return E_PERM;
endif
.
#119:8
return 0;
.
#119:9
"Is args[1] permitted to examine args[2]'s quota information?";
return $perm_utils:controls(args[1], args[2]);
.
#119:10
"Is args[1] permitted to examine args[2]'s quota information?";
return args[1].wizard;
.
#119:11
"Usage:  :get_quota_total(who)";
"";
"Return total quota for the given user--that is, how much stuff e's allowed to own.";
who = args[1];
if ((caller == this) || $perm_utils:controls(caller_perms(), who))
if (typeof(who.owned_objects) == LIST)
return length(who.owned_objects) + who.ownership_quota;
else
return E_INVARG;
endif
else
return E_PERM;
endif
.
#119:12
"Set args[1]'s quota TOTAL to args[2]";
o = args[1];
new = args[2];
if (caller_perms().wizard || (caller == this))
if (typeof(o.owned_objects) == LIST)
return args[1].ownership_quota = new - length(o.owned_objects);
else
return E_INVARG;
endif
else
return E_PERM;
endif
"Copied from Object Quota Utilities (#914):set_quota by Erik (#74) Wed Nov 23 15:57:33 1994 EST";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835906350, \"Erik\", #74, \"JHM\"}";
.
#119:13
"Usage:  :get_quota_usage(who)";
"";
"Return amount of quota currently tied up in who's objects.";
who = args[1];
if ((caller == this) || $perm_utils:controls(caller_perms(), who))
if (typeof(who.owned_objects) == LIST)
return length(who.owned_objects);
else
return E_INVARG;
endif
else
return E_PERM;
endif
"Copied from Object Quota Utilities (#914):get_quota_total by Erik (#74) Wed Nov 23 17:24:07 1994 EST";
.
#120:0
hgroup = args[1];
target = args[2];
for item in (hgroup[2..length(hgroup)])
target:contribute_partial("  o  ");
$jtext:convert_to("linemode", item, target);
target:contribute_newline();
endfor
target:contribute_newline();
.
#120:1
hgroup = args[1];
target = args[2];
target:contribute_partial("<UL>");
target:contribute_newline();
for item in (hgroup[2..length(hgroup)])
target:contribute_partial("<LI>");
$jtext:convert_to("html", item, target);
target:contribute_newline();
endfor
target:contribute_partial("</UL>");
target:contribute_newline();
.
#120:2
"Copied from Definition List Tag (#651):to_lemoo by Ron (#267) Sun May 16 19:35:41 1993 EDT";
hgroup = args[1];
target = args[2];
target:contribute_newline();
for item in (hgroup[2..length(hgroup)])
target:contribute_partial("(paragraph ");
target:contribute_partial("(text \"  o   \")");
$jtext:convert_to("lemoo", item, target);
target:contribute_partial(")");
target:contribute_newline();
endfor
target:contribute_newline();
.
#120:3
"Copied from Unnumbered Itemization Tag (#1051):to_lemoo by Jay (#88) Mon Oct 11 02:06:17 1993 EDT";
"Copied from Definition List Tag (#651):to_lemoo by Ron (#267) Sun May 16 19:35:41 1993 EDT";
hgroup = args[1];
target = args[2];
for item in (hgroup[2..length(hgroup)])
target:contribute_partial("{hgroup ");
target:contribute_partial("{~  o   } ");
$jtext:convert_to("tkmoo", item, target);
target:contribute_partial("}");
target:contribute_newline();
endfor
target:contribute_newline();
.
#121:0
if (caller in {this, $building_utils})
this.possessor = this.owner;
this.unique = 1;
help = this.owner.help;
if (typeof(help) == OBJ)
help = {help};
endif
if (typeof(help) == LIST)
this.owner.help = setadd(help, this);
else
this.owner.help = this;
endif
set_task_perms(caller_perms());
pass(@args);
else
return E_PERM;
endif
.
#121:1
if (caller in {this, $building_utils})
help = this.owner.help;
if (typeof(help) == OBJ)
help = {help};
elseif (typeof(help) != LIST)
help = {};
endif
help = setremove(help, this);
if (length(help) == 1)
this.owner.help = help[1];
elseif (help)
this.owner.help = help;
else
this.owner.help = 0;
endif
set_task_perms(caller_perms());
pass(@args);
else
return E_PERM;
endif
.
#121:2
if ((args[1] == $nothing) || (this.possessor == $no_one))
return pass(@args);
endif
.
#121:3
if (args)
return pass(@args);
else
return {@pass(@args), "my-index"};
endif
.
#121:4
if (args[1] == "my-index")
return {"*index*", "Personal Help Topics"};
else
return pass(@args);
endif
.
#121:5
return {@pass(@args) || {}, $building_utils, $no_one};
.
#123:0
"Usage: parse_args(words, nonwords)";
"";
"Parse words into a list of objects, ignoring anything in nonwords.  Tell the player about args that don't match or are ambiguous.";
"";
"This code is highly language-dependent.";
"";
"An attempt has been made to reduce language dependence while simultaneously removing this verb from its siamese-twin relationship with the Who's Feature.  It doesn't seem any more like a horrible kludge than the Who's Feature, and should be similarly extensible in horrible ugly ways.";
words = args[1];
nonwords = args[2];
objects = {};
where = (length(args) > 2) ? args[3] | player.location;
who = (length(args) > 3) ? args[4] | player;
"Note that the following introduces a precedence order.  Is this important enough to be worth changing?  It might be worth doing a clean rewrite with strict left-to-right processing.";
for sel in ($language:selector_words())
while (i = sel in words)
found = 0;
if (i < length(words))
vname = "_matchsel_" + $string_utils:from_list(words[i..length(words)], "_");
for feature in ({player, $language, @player:features()})
if ((!found) && $object_utils:has_callable_verb(feature, vname))
these = feature:(vname)(where, who);
if (these == 0)
"Assume the parsing stuff printed its own error.";
return 0;
endif
objects = $set_utils:union(objects, these);
found = 1;
endif
endfor
endif
if (!found)
objects = $set_utils:union(objects, $language:selector_word_default(sel, where, who));
words = listdelete(words, i);
else
words = words[1..i - 1];
endif
endwhile
endfor
allwords = words;
for nonword in (nonwords)
while (position = nonword in words)
words = listdelete(words, position);
endwhile
endfor
if (!words)
"If they're all non-words, then use them all as words.";
words = allwords;
endif
matches = $list_utils:map_arg(player.location, "match_object", words);
not_here = ambiguous = {};
for i in [1..length(matches)]
match = matches[i];
if (valid(match))
objects = setadd(objects, match);
elseif (match == $ambiguous_match)
ambiguous = setadd(ambiguous, words[i]);
else
not_here = setadd(not_here, words[i]);
endif
endfor
if (ambiguous)
player:tell("Which ", $string_utils:english_list(ambiguous, "", " or "), " do you mean?");
endif
not_here_or_made = {};
if (not_here)
if (this.quota_hungry)
delete = {};
for o in (not_here)
if ((o[1] == "`") && (o[length(o)] == "'"))
name = o[2..length(o) - 1];
if (typeof(ob = $recycler:_create(#1)) == OBJ)
ob.name = name;
ob.proper = 1;
objects = {@objects, ob};
delete = {@delete, ob};
endif
else
not_here_or_made = {@not_here_or_made, o};
endif
endfor
if (delete)
fork (1)
for i in (delete)
$recycler:_recycle(i);
endfor
endfork
endif
else
not_here_or_made = not_here;
endif
if (not_here_or_made)
player:tell("You see no ", $string_utils:english_list(not_here_or_made, "", " or "), " here.");
endif
endif
if (ambiguous || not_here_or_made)
return 0;
else
return objects;
endif
"Copied from Antisocial Feature (#199):parse_args by James (#2142) Fri Oct 28 15:09:21 1994 EDT";
.
#123:1
if ($__core_init_phase)
pass(@args);
delete_verb(this, "parse_whos");
delete_verb(this, "first_parse_object_args");
delete_verb(this, "_who's_here");
endif
.
#123:2
return {@pass(@args), $language, $string_utils, $object_utils, $set_utils, $list_utils, $recycler, $parse_utils, $pronoun_sub};
.
#124:0
":send_to(target, jaddress, source)  => send document specified by jaddress to target, with source as place for all links";
"source = args[3];";
target = args[1];
jaddress = args[2];
alist = jaddress[2];
if (note_object = $list_utils:assoc("objnum", alist))
note_object = toobj(note_object[2]);
else
"yeah, player will be an http player sometimes, but that should be ok";
name = $list_utils:assoc("name", alist)[2];
note_object = $note:match_type_object(name);
endif
return note_object:deliver_to(target);
.
#124:1
note = args[1];
return {this, {{"objnum", tostr(note)}}};
.
#125:0
hgroup = args[1];
target = args[2];
target:contribute_partial("<ISINDEX>");
.
#126:0
if (this.line > length(this.data))
return 0;
elseif (delim = "" in this.data[this.line..$])
delim = (delim + this.line) - 1;
hdrs = this.data[this.line..delim - 1];
this.line = delim + 1;
else
return 0;
endif
headers = {};
for hdr in (hdrs)
if (i = index(hdr, ": "))
headers = {@headers, {hdr[1..i - 1], hdr[i + 2..$]}};
endif
endfor
return headers;
"Copied from http source (#173):read_headers by Erik (#2) Mon Feb 10 15:34:32 1997 EST";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#126:1
delim = args[1];
if (i = delim in this.data[this.line..$])
i = (i + this.line) - 1;
body = this.data[this.line..i - 1];
this.line = min(i + 1, length(this.data));
return body;
else
return {};
endif
"Copied from http source (#173):read_body by Erik (#2) Mon Feb 10 15:34:37 1997 EST";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#126:2
{hdrs, raw} = args;
ret = $list_utils:make(length(hdrs));
for hdr in (raw)
{h, v} = hdr;
if (i = h in hdrs)
ret[i] = v;
endif
endfor
return ret;
"Copied from http source (#173):parse_headers by Erik (#2) Mon Feb 10 15:34:40 1997 EST";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#126:3
"Usage:  :read_line()";
"";
line = this.line;
this.line = line + 1;
return this.data[line];
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#126:4
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#127:0
raw = args[1];
target = args[2];
len = target:linelen() || 79;
max = (len - (len % 24)) - 24;
newlined = 1;
for item in (listdelete(raw, 1))
$jtext:convert_to("linemode", item, target);
"ugly";
col = length(target.contributed_line) + 2;
"2 spaces";
if (col > max)
target:contribute_newline();
newlined = 1;
else
target:contribute_partial($string_utils:space(26 - ((col % 24) || 24)));
newlined = 0;
endif
endfor
if (!newlined)
target:contribute_newline();
endif
target:contribute_newline();
.
#127:1
raw = args[1];
target = args[2];
target:contribute_partial("<DIR>");
target:contribute_newline();
for item in (listdelete(raw, 1))
target:contribute_partial("<LI>");
$jtext:convert_to("html", item, target);
target:contribute_newline();
endfor
target:contribute_partial("</DIR>");
target:contribute_newline();
.
#127:2
raw = args[1];
target = args[2];
len = target:linelen() || 79;
max = (len - (len % 24)) - 24;
newlined = 1;
for item in (listdelete(raw, 1))
$jtext:convert_to("lemoo", item, target);
"ugly";
col = length(target.contributed_line) + 2;
"2 spaces";
if (col > max)
target:contribute_newline();
newlined = 1;
else
target:contribute_partial($string_utils:space(26 - ((col % 24) || 24)));
newlined = 0;
endif
endfor
if (!newlined)
target:contribute_newline();
endif
target:contribute_newline();
"Copied from Index Tag (#1191):to_linemode by Jubal (#1767) Mon Oct 10 07:14:56 1994 EDT";
.
#128:0
who = (caller == this) ? args[1] | player;
if (this.open)
who:tell(this:opened_msg());
return;
endif
who.location:broadcast_event_door_open_attempted(who, this);
if (this:is_locked())
if (this.automatic)
this:unlock(@args);
if (this:is_locked())
who.location:broadcast_event_door_open_failed(who, this);
return;
endif
else
who.location:broadcast_event_door_open_failed(who, this);
who:tell(this:locked_msg());
return;
endif
endif
$you:say_action(this:open_msg(), who);
this:set_open(1);
who.location:broadcast_event_door_open_completed(who, this);
if ((typeof(other_side = this:other_side()) == OBJ) && $object_utils:isa(other_side, $door))
other_side.source:announce_all_but({who}, other_side:opens_msg(who));
other_side:set_open(1);
other_side.source:broadcast_event_door_open_other_side(who, other_side);
endif
.
#128:1
if (!this.open)
player:tell(this:closed_msg());
else
"assume we might want to fail to close someday?";
player.location:broadcast_event_door_close_attempted(player, this);
$you:say_action(this:close_msg());
this:set_open(0);
player.location:broadcast_event_door_close_completed(player, this);
if ((typeof(other_side = this:other_side()) == OBJ) && $object_utils:isa(other_side, $door))
other_side.source:announce(other_side:closes_msg());
other_side:set_open(0);
other_side:broadcast_event_door_close_other_side(player, other_side);
endif
endif
.
#128:2
return $string_utils:pronoun_sub(@{this.(verb) || $door.(verb), @args});
.
#128:3
desc = pass(@args);
what = this;
if (desc)
line = desc[length(desc)];
if (index(line, this:name()))
what = this:gender_obj();
endif
endif
msg = this:((this.open ? "opened" | "closed") + "_msg")(player, what);
if (!msg)
msg = {};
endif
return {@desc, @(typeof(msg) == LIST) ? msg | {msg}};
.
#128:4
if (this.automatic)
if (!this.open)
this:open(args[1]);
endif
if (!this.open)
return;
endif
endif
if (this.open)
pass(@args);
else
args[1]:tell(this:closed_msg());
endif
.
#128:5
return $object_utils:isa(args[1], $door) || $perm_utils:controls(player, this);
.
#128:6
if (player.location != this.source)
player:tell("You can't see that here.");
return;
endif
if (this:is_locked())
player:tell(this:locked_msg());
return;
endif
if (this:has_key())
if (this.open)
if (this.automatic)
this:close(@args);
endif
if (this.open)
player:tell(this:opened_msg());
return;
endif
endif
$you:say_action(this:lock_msg());
this:set_locked(1);
if ((typeof(other_side = this:other_side()) == OBJ) && $object_utils:isa(other_side, $door))
other_side.source:announce(this:locks_msg());
other_side:set_locked(1);
endif
else
player:tell(this:no_key_msg());
endif
.
#128:7
if (player.location != this.source)
player:tell("You can't see that here.");
elseif (!this:is_locked())
player:tell(this:unlocked_msg());
elseif (this:has_key())
$you:say_action(this:unlock_msg());
this:set_locked(0);
if ((typeof(other_side = this:other_side()) == OBJ) && $object_utils:isa(other_side, $door))
other_side.source:announce(this:unlocks_msg());
other_side:set_locked(0);
endif
else
player:tell(this:no_key_msg());
endif
.
#128:8
return this.locked;
.
#128:9
return ((this.user_key in player.contents) || (this.user_key == this)) || $object_utils:contains(this, this.user_key);
.
#128:10
if ((((typeof(dest = this.dest) == OBJ) && valid(dest)) && (typeof(source = this.source) == OBJ)) && valid(source))
for find in (dest:obvious_exits())
if ((find.dest == source) && $object_utils:isa(find, $door))
return find;
endif
endfor
endif
return $nothing;
.
#128:11
if (valid(other_side = this:find_other_side()))
this:set_other_side(other_side);
other_side:set_other_side(this);
endif
.
#128:12
if ((!valid(this.other_side)) || (valid(this.other_side) && (this.other_side.other_side != this)))
this:setup_connection();
endif
return this.other_side;
.
#128:13
$you:say_action(this:knock_msg());
if ((typeof(other_side = this:other_side()) == OBJ) && $object_utils:isa(other_side, $door))
other_side.source:announce(other_side:knocks_msg());
endif
.
#128:14
if (this:trusted(caller))
vname = verb[5..length(verb)];
what = args[1];
this.(vname) = what;
if (valid(other_side = this:other_side()))
other_side.(vname) = what;
endif
return what;
else
return E_PERM;
endif
.
#128:15
return this.(verb) || $door.(verb);
.
#128:16
if (this:trusted(caller))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
.
#128:17
"The door closes by itself.";
if (this.open)
msg = this:self_closes_msg();
this.source:announce($string_utils:pronoun_sub(msg));
if (player.location == this.source)
player:tell($string_utils:pronoun_sub(msg, $you));
endif
this.source:broadcast_event_door_close_self(this);
this:set_open(0);
return 1;
"hey, why don't we close the other side, anyway?";
endif
.
#128:18
"Usage: set_open(open)";
"";
"Sets the open flag of the door to <open>.  True means the door is open, false means it isn't.  It will raise the usual errors for bad arguments (hope this doesn't break anything) and raises or returns E_PERM for permission errors (right now it returns E_PERM, but in the future it will raise instead).  If, however, everything goes well, it returns the new value of the open flag.";
"";
"Don't count on the open flag being the exact value you put in.  In the future all values will probably be converted to 0 or 1.";
"";
"Additionally, this is where we manage the self-closing task if this is a self-closing door.";
"";
{open} = args;
if (!this:trusted(caller))
"FIXME: this should be 'raise' when other code permits:";
return E_PERM;
endif
if (this.open = open)
if ((delay = this:close_delay()) >= 0)
fork t (delay)
if (this.open)
this:self_close();
endif
endfork
this:set_closing_task(t);
endif
else
if (this.closing_task && (this.closing_task != task_id()))
"yes, this does get called from .closing_task, which will suicide without the above check";
`kill_task(this.closing_task) ! E_INVARG, E_PERM';
"in the above line we take care of the case where the closing task has already run, been killed, or just died.  The E_PERM is kinda random -- we don't expect the taskid to get reused, but if someone hijacked it there's nothing any of our callers is likely to be able to do about it so we ignore that condition too.";
this:set_closing_task(0);
endif
endif
return this.open;
"Metadata 202106";
"Last-Modify: {855974699, \"James\", #2142, \"JHM\"}";
.
#128:19
if (typeof(delay = this.close_delay) == LIST)
return random(delay[1]) + delay[2];
endif
return delay;
.
#128:20
"This particular event handler transmits speech directed at a door through that door, so people can now be rude and sit and make you talk to them through closed doors.  :)";
"";
"This verb will blow up with E_INVARG if you pass it a malformed speech event, so don't do that.  :)";
event = args[1];
try
"FIXME: there's no security check for this verb, so it can be used by spoofers.  (Hey, someone really oughta let CERT know about this one!)";
targets = $list_utils:assoc("targets", event)[2];
if (this in targets)
speaker = $list_utils:assoc("speaker", event)[2];
argstr = $list_utils:assoc("speech", event)[2];
try
this:other_side().source:announce_all("A voice from the other side of ", this:other_side():dname(), " says, \"", argstr, "\"");
except (E_INVIND, E_TYPE)
"If the door doesn't have another side, or that other side doesn't have a source, we just ignore it.  After all, sound doesn't travel in a vacuum.";
endtry
endif
except (E_RANGE, E_TYPE)
"Notice that the only way E_RANGE or E_TYPE should be able to occur outside the other try block is if the event packet we were passed is not an alist or is missing required fields.  Callers passing in such junk may not be expecting errors to be raised, but then, they deserve to lose.";
raise(E_INVARG, "Event record missing required field");
endtry
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {855977029, \"James\", #2142, \"JHM\"}";
.
#128:21
return (this.automatic || (this.open && "(this.close_delay < 0)")) && pass(@args);
.
#128:22
return this.help;
"Copied from generic door (#1200):help_msg by Erik (#74) Sun Oct 23 18:13:50 1994 EDT";
.
#128:23
if ((!this.open) && this.opaque)
return "";
endif
return pass(@args);
.
#128:24
return args[1] ? this.open ? "Open" | "Closed" | (this.open ? "open" | "closed");
.
#128:25
if ($__core_init_phase)
ret = pass(@args);
this.integrate_room_msg = $pronoun_sub.("two-letter"):parse("%Td to %2d %t:(is) %{special thing open {0%}}.");
return ret;
else
$error:raise(E_PERM);
endif
.
#128:26
"Usage:  pull <someone> through <door>";
"";
"Brings someone inside without leaving the door open.";
if (player.location != this.source)
player:tell("You see no \"", iobjstr, "\" here.");
return;
elseif ($command_utils:object_match_failed(dobj = this.dest:match_object(dobjstr), dobjstr))
return;
elseif (dobj.location != this.dest)
player:tell("But ", dobj:dname(), " isn't ", this.dest:name("d", {"in"}), ".");
return;
elseif (!$object_utils:isa(dobj, $player))
"this is probably the wrong test, but i'm tired of the lambdamoo link walking in from the sunroom.";
player:tell("You can't pull ", dobj:dname(), " through ", this:dname(), ".");
return;
elseif (!valid(other = this:other_side()))
player:tell("Sorry, nothing can come in that way.");
return;
else
lock = this:is_locked();
close = !this.open;
if (close)
this:open(player);
if (!this.open)
return;
endif
endif
$you:say_action(this.pull_msg);
other:move(dobj);
if (lock)
this:lock();
elseif (close)
this:close();
endif
endif
.
#128:27
"Usage:  :walking_cost(who)";
"";
who = args[1];
cost = pass(@args);
if (((!this.open) && this:is_locked()) && (!this:has_key(who)))
cost = cost + 20;
endif
return cost;
.
#128:28
return this.user_key;
.
#128:29
"Use %t{{\"open\"%} \"ic\"} to get \"an open foo\", for example (don't forget to escape the quotes)";
return (this.open ? "open " | "closed ") + args[1];
"Metadata 202106";
"Last-Modify: {863743982, \"Ken\", #75, \"JHM\"}";
.
#129:0
what = verb[6..length(verb)];
if (typeof(value = this:get(@args)) == OBJ)
return {"", {tostr(value.name, "(", value, ")"), ("is the " + what) + " used by @dig."}};
else
return {0, {(("No default " + what) + " set, uses $") + what}};
endif
.
#129:1
oname = args[1];
raw = args[2];
if (typeof(raw) == STR)
what = player:my_match_object(raw);
elseif (typeof(raw) == NUM)
return raw ? "You need to specify an object." | {oname, 0};
elseif (typeof(raw) == LIST)
return "This option must be an OBJECT.";
else
what = raw;
endif
option = verb[7..length(verb)];
parent = #0.(option);
if ($command_utils:object_match_failed(what, tostr(raw)))
return "";
elseif (!$object_utils:isa(what, parent))
return ("This option must be set to a descendant of $" + option) + ".";
else
return {oname, what};
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863370171, \"Ken\", #75, \"JHM\"}";
.
#130:0
return {@pass(@args), this.root, this.preceding, this.browser};
.
#130:1
"Usage:  :help_text()";
"";
return this.description;
.
#131:0
"Make person a programmer as well as adding em to the group.";
set_task_perms(caller_perms());
if (ret = pass(@args))
$wiz_utils:set_programmer(args[1]);
endif
return ret;
.
#132:0
hgroup = args[1];
target = args[2];
target:contribute_partial("<OL>");
target:contribute_newline();
for item in (hgroup[2..length(hgroup)])
target:contribute_partial("<LI>");
$jtext:convert_to("html", item, target);
target:contribute_newline();
endfor
target:contribute_partial("</OL>");
target:contribute_newline();
.
#133:0
if (this:can_put_in(dobj, dobjstr))
dobj:moveto(this);
if (dobj.location == this)
$you:say_action(this:put_msg());
else
player:tell(this:put_fail_msg());
endif
endif
.
#133:1
if (ret = this:can_get_from(dobjstr))
dobj = ret[2];
dobj:moveto(player);
if (dobj.location == player)
$you:say_action(this:remove_msg());
else
dobj:moveto(this.location);
if (dobj.location == this.location)
$you:say_action(this:remove_msg());
player:tell("You can't pick up ", dobj:dname(), ", so it tumbles onto the floor.");
else
player:tell(this:remove_fail_msg());
endif
endif
endif
.
#133:2
return this.(verb);
.
#133:3
desc = pass(@args);
"defaultdesc = {\"You see nothing special.\"};";
"if (!desc)";
" desc = defaultdesc;";
"endif";
integrate = $integration_utils:integrate(args, "integrate_container_msg");
if (integrate)
if (!desc)
return integrate;
else
desc[$] = (desc[$] + $string_utils:pronoun_sub(this.integrate_sep_msg)) + integrate[1];
if (length(integrate) > 1)
desc = {@desc, @integrate[2..$]};
endif
endif
endif
return desc;
"Metadata 202106";
"Last-Modify: {857910316, \"Ben\", #3373, \"JHM\"}";
.
#133:4
return (msg = this.(verb)) ? $string_utils:pronoun_sub(msg) | "";
.
#133:5
":can_put_in(what, whatstr)";
"Can player put what (called whatstr) in this?";
"Print failure messages to player if not.";
what = args[1];
whatstr = args[2];
if (!this:can_reach(player))
elseif (what == $nothing)
player:tell("What do you want to put ", prepstr, " ", this:dname(), "?");
elseif ($command_utils:object_match_failed(what, whatstr))
elseif (!this:accept(what))
player:tell("You can't put ", what:dname(), " ", prepstr, " ", this:dname(), ".");
else
return 1;
endif
return 0;
.
#133:6
"can_reach(<who>)";
"Can <who> reach this to manipulate it?";
"Print failure messages to player if not.";
who = args[1];
if ((this.location != who) && (this.location != who.location))
"Should probably be more generalized.";
player:tell("You can't get at ", this:dname(), ".");
return 0;
endif
return 1;
.
#133:7
where = args[1];
if ((!valid(where)) || this:is_unlocked_for(where))
pass(where);
endif
"Copied from generic thing (#5):moveto by Erik (#74) Fri Sep 23 21:59:00 1994 EDT";
.
#133:8
return !is_player(args[1]);
.
#133:9
if (verb[1..4] == "sub_")
verb = verb[5..$] + "_msg";
endif
return $string_utils:pronoun_sub(this.(verb), @args);
.
#133:10
":can_get_from(whatstr)";
"Can player get something called whatstr from this?";
"Print failure messages to player and return 0 if not.";
"Otherwise return {1, obj} where obj is what whatstr matched.";
whatstr = args[1];
if (!this:can_reach(player))
elseif ((what = this:match(whatstr)) == $nothing)
player:tell("What do you want to take from ", this:dname(), "?");
elseif (what == $failed_match)
player:tell("There is no \"", whatstr, "\" ", this:in_msg(), " ", this:dname(), ".");
elseif ($command_utils:object_match_failed(what, whatstr))
elseif (!(what in this.contents))
player:tell(what:dnamec(), " ", what:verb_sub("isn't"), " ", this:in_msg(), " ", this:dname(), ".");
else
return {1, what};
endif
return 0;
"Metadata 202106";
"Last-Modify: {856540396, \"Ben\", #3373, \"JHM\"}";
.
#134:0
return (args && match(args[1], "^%(failed [^ ]+$%|ambiguous [^ ]%)")) ? args[1] | {};
.
#134:1
topic = args[1];
if (topic[1..7] == "failed ")
topic[1..7] = "";
$wiz_utils:missed_help(topic, 0);
header = tostr("`help ", topic, "' not found");
return {{$jtext.title, header}, {$jtext.header, 1, header}, {$jtext.paragraph, tostr("Sorry, but no help is available on `", topic, "'.  If you can't find what you're looking for, you may want to send mail (see "), @this:line_to_hboxes("`help mail'"), ") to ", $mail_agent:addrname(this.help_recipient), "."}};
elseif (topic[1..10] == "ambiguous ")
topics = listdelete($string_utils:words(topic), 1);
topic = topics[1];
topics = $help:sort_topics(listdelete(topics, 1));
$wiz_utils:missed_help(topic, 1);
header = tostr("`help ", topic, "' ambiguous");
return {{$jtext.title, header}, {$jtext.header, 1, header}, {$jtext.paragraph, "Sorry, the topic `", topic, "' is ambiguous.  I don't know which of the following topics you mean:"}, {$jtext.index, @$list_utils:map_arg($jaddress.help, "make_link", topics)}};
endif
.
#134:2
if ($__core_init_phase)
pass(@args);
this.help_recipient = $mail_agent:match("*Bug-reports");
endif
.
#135:0
hgroup = args[1];
target = args[2];
for hbox in (hgroup[2..length(hgroup)])
"target:contribute_partial(hbox);";
$jtext:convert_to("linemode", hbox, target);
target:contribute_newline();
endfor
target:contribute_newline();
.
#135:1
hgroup = args[1];
target = args[2];
for hbox in (hgroup[2..length(hgroup)])
target:tell(hbox);
"target:contribute_partial(hbox);";
"target:contribute_newline();";
endfor
.
#135:2
hgroup = args[1];
target = args[2];
for hbox in (hgroup[2..length(hgroup)])
$jtext:convert_to("lemoo", hbox, target);
target:contribute_newline();
endfor
target:contribute_newline();
.
#135:3
hgroup = args[1];
target = args[2];
target:contribute_partial("<PRE WIDTH=80>");
target:contribute_newline();
for hbox in (hgroup[2..length(hgroup)])
$jtext:convert_to("html", hbox, target);
"target:contribute_partial($html:from_literal_text(hbox));";
target:contribute_newline();
endfor
target:contribute_partial("</PRE>");
target:contribute_newline();
.
#135:4
hgroup = args[1];
target = args[2];
for hbox in (hgroup[2..length(hgroup)])
target:contribute_partial("(plaintext ");
$jtext:convert_to("mcp", hbox, target);
target:contribute_partial(")");
target:contribute_newline();
endfor
target:contribute_newline();
"Copied from Plaintext Tag (#1272):to_lemoo by Erik (#74) Sun Aug 13 15:35:07 1995 EDT";
.
#136:0
hgroup = args[1];
target = args[2];
for hbox in (hgroup[2..length(hgroup)])
$jtext:convert_to("linemode", hbox, target);
target:contribute_newline();
endfor
target:contribute_newline();
.
#136:1
hgroup = args[1];
target = args[2];
for hbox in (hgroup[2..length(hgroup)])
$jtext:convert_to("lemoo", hbox, target);
target:contribute_newline();
endfor
target:contribute_newline();
.
#136:2
hgroup = args[1];
target = args[2];
for hbox in (hgroup[2..length(hgroup) - 1])
$jtext:convert_to("html", hbox, target);
target:contribute_partial("<BR>");
target:contribute_newline();
endfor
$jtext:convert_to("html", hgroup[length(hgroup)], target);
target:contribute_partial("<P>");
target:contribute_newline();
return;
target:contribute_partial("<PRE WIDTH=\"80\">");
for hbox in (hgroup[2..length(hgroup)])
$jtext:convert_to("html", hbox, target);
target:contribute_newline();
endfor
target:contribute_partial("</PRE>");
target:contribute_newline();
.
#136:3
"Copied from List Tag (#1284):to_lemoo by Jay (#88) Mon Oct 11 02:03:43 1993 EDT";
hgroup = args[1];
target = args[2];
for hbox in (hgroup[2..length(hgroup)])
$jtext:convert_to("tkmoo", hbox, target);
target:contribute_newline();
endfor
target:contribute_newline();
.
#137:0
"@perm_utils:controls(who, what)";
"Is WHO allowed to hack on WHAT?";
return (args[1] == args[2].owner) || args[1].wizard;
"OLD VERSION";
owner = args[2].owner;
return ((args[1] == owner) || args[1].wizard) || ($object_utils:isa(owner, $corporation) && owner:controls(args[1], args[2], "", "object"));
.
#137:1
":apply(permstring,mods[,allowed]) => new permstring.";
"PERMSTRING is a permissions string, MODS is a concatenation of strings of the form +<letters>, !<letters>, or -<letters>, where <letters> is a string of letters as might appear in a permissions string (`+' adds the specified permissions, `-' or `!' removes them; `-' and `!' are entirely equivalent).";
"if ALLOWED is supplied, any letters in MODS not contained in ALLOWED raises E_INVARG";
{perms, mods, ?allowed = mods} = args;
if (!mods)
return mods;
elseif (!index("!-+", mods[1]))
if (m = match(mods, tostr("[^", allowed, "]")))
raise(E_INVARG, tostr("Unknown flag:  ", mods[m[1]]));
endif
return mods;
endif
i = 1;
while (i <= length(mods))
if (mods[i] == "+")
while (((i = i + 1) <= length(mods)) && (!index("!-+", mods[i])))
if (!index(allowed, mods[i]))
raise(E_INVARG, tostr("Unknown flag:  ", mods[i]));
endif
if (!index(perms, mods[i]))
perms = perms + mods[i];
endif
endwhile
else
"mods[i] must be ! or -";
while (((i = i + 1) <= length(mods)) && (!index("!-+", mods[i])))
if (!index(allowed, mods[i]))
raise(E_INVARG, tostr("Unknown flag:  ", mods[i]));
endif
perms = strsub(perms, mods[i], "");
endwhile
endif
endwhile
return perms;
.
#137:2
"controls_verb(who, what, verbname)";
"Is WHO allowed to hack on WHAT's VERBNAME?";
return (args[1] == verb_info(args[2], args[3])[1]) || args[1].wizard;
.
#137:3
"controls_verb(who, what, propname)";
"Is WHO allowed to hack on WHAT's PROPNAME?";
return (args[1] == property_info(args[2], args[3])[1]) || args[1].wizard;
.
#137:4
"Usage:  :invoked_by_function(the_function)";
"";
"Returns true if the calling verb was called by the built-in function named <the_function>, before any pass()-ing was done.";
{the_function} = args;
info = $code_utils:callers_before_pass(callers());
return info && (info[1][1..4] == {#-1, the_function, #-1, #-1});
.
#139:0
"send_to(target, address[, source])";
target = args[1];
address = args[2];
subject = $jaddress:extract("subject", address)[2];
db = $jaddress:extract("db", address);
if (db)
address[1] = $jaddress.resolved_help;
return $jaddress.resolved_help:send_to(target, address);
endif
if (space = index(subject, " "))
subject = subject[1..space - 1];
endif
"temporary and bad fix";
"temporary even worse fix, to allow db to be part of the help jaddress";
"#2:tell(\"target: \", target, \" which has the name \", target.name);";
dblist = $code_utils:help_db_list(target);
"#2:tell(\"db list: \", $string_utils:print(dblist));";
result = $code_utils:help_db_search(subject, dblist);
if (!result)
target:receive_vbox({$jtext.paragraph, "Sorry, but no help is available on `", subject, "'."});
elseif (result[1] == $ambiguous_match)
player:receive_vbox($jtext.paragraph, "Sorry, but the topic name `", subject, "' is ambiguous.");
else
help = result[1];
topic = result[2];
dblist = dblist[1 + (help in dblist)..length(dblist)];
if (1 == (text = help:get_topic(topic, dblist)))
"...get_topic took matters into its own hands...";
elseif (text)
if (text[1][1] == $jtext.title)
target:receive_vbox({$jtext.vgroup, @text});
elseif (text[1][1..2] == {$jtext.header, 1})
target:receive_vbox({$jtext.vgroup, {$jtext.title, topic ? tostr("Help on ", topic) | "Help summary"}, @text});
else
target:receive_vbox({$jtext.vgroup, {$jtext.title, topic ? tostr("Help on ", topic) | "Help summary"}, {$jtext.header, 1, topic || "summary"}, @text});
endif
endif
endif
return;
target:receive_vbox(((db = $jaddress:extract("db", address)) ? $string_utils:literal_object(db[2]) | $help):get_topic($jaddress:extract("subject", address)[2]));
.
#139:1
"Usage:  :make_link(topic)";
return this:make_formatted_link(args[1], tostr("`help ", args[1], "'"));
.
#139:2
"Usage:  :make_formatted_link(topic, format)";
return {$jtext.link.root, {this, {{"subject", args[1]}}}, args[2]};
.
#139:3
db = tostr(args[1]);
topic = args[2];
return {this, {{"db", db}, {"subject", topic}}};
.
#139:4
"Copied from Help Address (#1345):from_db_and_topic by Erik (#2) Sun Apr 17 18:17:05 1994 EDT";
topic = args[1];
return {this, {{"subject", topic}}};
.
#139:5
"Usage:  :from_pseudocommand(string)";
"";
"Given a command like `help mumbly', find a jaddress equivalent.";
string = args[1];
if (subs = match(string, "^help %('%|[^ <][^ ]*%)$"))
return this:make_link(substitute("%1", subs));
endif
.
#140:0
set_task_perms(caller_perms());
if (typeof(n = this:new()) == OBJ)
n:setup("make-note sink for " + player:name(), caller, caller_perms());
endif
return n;
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#140:1
if (caller != this.module)
raise(E_PERM);
endif
set_task_perms(this.creator);
if (typeof(note = $recycler:_create($note)) != OBJ)
player:tell("Unable to create note: ", note);
else
name = this.module:name();
note:set_name(name);
note:set_aliases({name});
note:moveto(player);
note:set_text(this:get_lines());
endif
return;
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#141:0
return args[2];
.
#142:0
return 0;
.
#142:1
"Copied from Generic Feature Object (#79):set_feature_ok by Doug (#73) Tue Apr 26 02:39:59 1994 EDT";
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
.
#142:2
pass(@args);
this:set_handler_ok(1);
.
#142:3
obv = pass(@args);
set_task_perms(this.owner);
for v in (verbs(this))
if (match(v, "^handle_event_"))
obv = {@obv, v};
endif
endfor
return obv;
.
#142:4
"Copied from Generic Feature Object (#79):help_msg by Doug (#73) Tue Apr 26 03:11:51 1994 EDT";
"Definition from #79";
all_help = $string_utils:pronoun_sub(this.help_text) || {};
if (typeof(all_help) == STR)
all_help = {all_help};
endif
helpless = {};
for vrb in (this:help_verbs(player))
if (typeof(vrb) == LIST)
vrb = vrb[2];
endif
if (loc = $object_utils:has_verb(this, vrb))
help = $code_utils:verb_documentation(this, vrb);
if (help)
if (all_help)
msg = verb_info(this, vrb)[3];
underline = $string_utils:space(length(msg), "=");
all_help = {@all_help, "", msg, underline, "", @help};
else
msg = verb_info(this, vrb)[3];
underline = $string_utils:space(length(msg), "=");
all_help = {msg, underline, "", @help};
endif
else
helpless = {@helpless, vrb};
endif
endif
endfor
if (all_help)
if (helpless)
all_help = {@all_help, "", ("No help found on " + $string_utils:english_list(helpless, "nothing", " or ")) + "."};
endif
else
all_help = {((("Sorry, but no help is available on " + this:name()) + "(") + tostr(this)) + ").", ("Try `examine " + this:name()) + "'."};
endif
"return {@all_help, \"----\"};";
return all_help;
"Copied from generic event handler (#1374):help_msg by Erik (#74) Sun Oct 23 18:01:01 1994 EDT";
.
#142:5
"return this.registry:match_handler(args[1]);";
if (!(string = args[1]))
return $nothing;
endif
handlers = $object_utils:descendants($event_handler);
if (!((ret = $string_utils:match_object(string, $no_one)) in handlers))
ret = $string_utils:match(string, handlers, "name", handlers, "aliases");
endif
return ret;
.
#143:0
if (callers())
raise(E_PERM, "Not called as server task");
endif
try
if (this:allow_connection(player))
handler = this:make_handler(player);
return handler;
else
boot_player(handler);
endif
except v (ANY)
boot_player(player);
endtry
.
#143:1
"Usage:  :make_handler(forwho)";
"";
forwho = args[1];
if (caller != this)
raise(E_PERM);
endif
if (typeof(handler = $recycler:_create(this.handler, this.handler.owner)) == ERR)
raise(handler);
endif
set_player_flag(handler, 1);
set_connection_option(player, "hold-input", 1);
return handler;
.
#143:2
"Usage:  :allow_connection()";
"";
"sure!";
who = args[1];
if (caller != this)
raise(E_PERM);
endif
return 1;
.
#143:3
"Usage:  start";
"        stop";
"";
"Wizards can start and stop this. ";
"";
if (!player.wizard)
player:tell("Permission denied.");
elseif (verb == "start")
player:tell(this:dnamec(), " ", this:service_start());
else
if (open = this:open_connections())
plural = (length(open) == 1) ? "" | "s";
if (!$command_utils:yes_or_no(((("There are currently " + tostr(length(open))) + " connection") + plural) + " open.  Abort them?"))
return player:tell("Shutdown aborted.");
endif
endif
player:tell(this:dnamec(), " ", this:service_stop());
endif
.
#143:4
"Usage:  :service_stop()";
"";
if ((!(caller in {this, $startup})) || (!caller_perms().wizard))
raise(E_PERM);
endif
if (this:listening_ports())
aborted = {};
for victim in (this:open_connections())
try
this:abort_connection(victim);
except (ANY)
boot_player(victim);
endtry
aborted = {@aborted, victim};
endfor
rval = pass(@args);
if (aborted)
plural = (length(aborted) == 1) ? "" | "s";
rval = (((rval + " and aborted ") + tostr(length(aborted))) + " connection") + plural;
endif
return rval;
else
return "not listening";
endif
.
#143:5
"Usage:  :open_connections()";
"";
cons = {};
if (caller == this)
for victim in (connected_players())
if ($object_utils:isa(victim, this.handler))
cons = {@cons, victim};
endif
endfor
endif
return cons;
.
#143:6
"Usage:  :abort_connection(what)";
"";
what = args[1];
if (caller == this)
what:abort();
else
raise(E_PERM);
endif
.
#143:7
user = args[1];
if (callers())
raise(E_PERM);
elseif (user < #0)
return;
endif
"had we bothered to keep track of the timeout task, we could nuke it here.  enh, doesn't seem too important and that's another alist to get screwed up.";
set_player_flag(user, 0);
try
user:disconnected();
except (ANY)
endtry
if ($object_utils:isa(user, this.handler))
$recycler:_recycle(user);
endif
.
#143:8
"Usage:  :timeout_connection(what)";
"";
what = args[1];
if (((caller == this) && $object_utils:isa(what, this.generc)) && (typeof(`idle_seconds(what) ! E_INVARG') != ERR))
try
what:timeout();
except (ANY)
boot_player(what);
endtry
endif
.
#143:9
"Usage:  :user_connected()";
"        :user_created  ()";
"";
user = args[1];
if (callers())
raise(E_PERM);
endif
user:connected();
.
#144:0
args[2]:contribute_partial(tostr("(paragraph (text \"", $string_utils:space(args[2]:linelen(), "-"), "\"))"));
args[2]:contribute_newline();
"args[2]:contribute_hr();";
.
#144:1
args[2]:contribute_partial("<HR>");
args[2]:contribute_newline();
.
#146:0
value = this:get(@args);
if (value)
return {tostr(value), {tostr("Subtract ", value, " tick", (value == 1) ? "" | "s", " from the time displayed for an eval.")}};
else
return {0, {"Don't subtract any ticks from the time displayed for an eval."}};
endif
"Copied from Mail Options (#63):show_manymsgs by Doug (#107) Sun Jun  5 17:27:05 1994 EDT";
.
#146:1
if (value = this:get(@args))
return {"", {"Environment string to evaluate before each eval command:", value}};
else
return {0, {"Don't evaluate any environment string before each eval command."}};
endif
"Copied from Mail Options (#63):show_@mail by Doug (#107) Sun Jun  5 17:30:32 1994 EDT";
.
#146:2
if (value = this:get(@args))
return {"", {"Substitutions to perform upon each eval command:", $string_utils:print(value)}};
else
return {0, {"Don't perform any substitutions upon each eval command."}};
endif
"Copied from Mail Options (#63):show_@mail by Doug (#107) Sun Jun  5 17:30:32 1994 EDT";
"Copied from Eval Options (#1459):show_env by Doug (#107) Sun Jun  5 17:34:50 1994 EDT";
.
#146:3
oname = args[1];
raw = args[2];
if (typeof(raw) == NUM)
if (raw == 1)
return "Please supply a number of ticks.";
endif
return {oname, raw};
endif
if ((value = $code_utils:tonum(raw)) == E_TYPE)
return tostr("`", raw, "'?  Number expected.");
endif
return {oname, value};
"Copied from Mail Options (#63):parse_manymsgs by Doug (#107) Sun Jun  5 17:53:25 1994 EDT";
.
#146:4
raw = args[2];
if (typeof(raw) != STR)
if (raw == 0)
args[2] = "";
else
return "Please supply an environment string.";
endif
endif
return args[1..2];
"Copied from Mail Options (#63):parse_@mail by Doug (#107) Sun Jun  5 17:59:32 1994 EDT";
.
#146:5
oname = args[1];
raw = args[2];
if (typeof(raw) == STR)
ret = $string_utils:to_value(raw);
if (ret[1])
raw = ret[2];
else
return ret[2];
endif
elseif (raw == 0)
return {oname, {}};
endif
return {oname, raw};
.
#146:6
"Don't return 0 for certain unset values.";
ret = pass(@args);
if ((name = args[2]) == "env")
if (ret == 0)
return "";
endif
elseif (name == "subs")
if (ret == 0)
return {};
elseif (typeof(ret[1]) == STR)
"A single substitution can be just a pair of strings.";
return {ret};
endif
endif
return ret;
.
#146:7
if (args[1] == "nodebug")
return {{"no_debug", args[2]}};
endif
return args;
.
#150:0
if ($__core_init_phase)
pass(@args);
this.readers = 1;
this.moderated = 0;
endif
.
#151:0
":news_display_seq_full(msg_seq[,preamble]) => {cur, last-read-date}";
"This is the news display routine.";
set_task_perms(caller_perms());
if (0)
preamble = {@args, ""}[2];
else
preamble = "";
endif
player:tell_lines({"", this:description()[1], ""});
cur = date = 0;
for x in (msgs = this:messages_in_seq(args[1]))
cur = x[1];
date = x[2][1];
if (0)
player:display_message(preamble ? strsub(preamble, "%d", tostr(cur)) | {}, this:to_text(@x[2]));
else
player:notify_lines({@preamble ? {strsub(preamble, "%d", tostr(cur))} | {}, @this:to_text(@x[2]), ""});
endif
if ((ticks_left() < 500) || (seconds_left() < 2))
suspend(0);
endif
endfor
return {cur, date};
.
#151:1
":to_text(@msg) => message in text form -- formatted like a $news entry circa October, 1993";
date = args[1];
by = args[2];
by = by[1..index(by, "(") - 2];
subject = (args[4] == " ") ? "NEWS FLASH" | $string_utils:uppercase(args[4]);
text = args[5..length(args)];
ctime = $time_utils:time_sub("$D, $N $3, $Y", date);
return {subject, tostr("  by ", by, " on ", ctime), @text};
.
#151:2
"@set-newspaper $news to <seq>";
if (!this:is_writable_by(player))
player:tell(E_PERM);
return;
endif
if (ms = this:parse_message_seq("1-$", iobjstr))
this.current_newspaper = $string_utils:explode(iobjstr);
player:tell("Set current newspaper to ", iobjstr, ".");
endif
.
#151:3
if (pass(@args))
return 1;
endif
"this.moderated is a list of admin groups.";
for g in (this.moderated)
if (g:is_member(args[1]))
return 1;
endif
endfor
return 0;
.
#151:4
if (typeof(this:parse_message_seq("new", @player:get_current_message(this) || {})) == STR)
return;
else
player:tell("There is new news.  Type `news' to read all news or `news new' to read just new news.");
endif
.
#151:5
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
else
if (r = pass(@args))
fork (0)
this:touch();
endfork
endif
return r;
endif
.
#151:6
"notify connected users that there's new news";
if (caller == this)
for p in ($code_utils:connected_players())
player = p;
this:check();
$command_utils:suspend_if_needed(0);
endfor
endif
.
#151:7
if ($__core_init_phase)
pass(@args);
this.readers = 1;
this.description = "It's the current issue of the News, dated %d.";
this.current_newspaper = {"1-$"};
this.moderated = {$steering_committee, $tech_group, $community_group};
else
return E_PERM;
endif
.
#151:8
desc = pass(@args);
raw = ctime(this.messages ? this.messages[length(this.messages)][2][1] | 0);
"         111111111122222";
"123456789012345678901234";
"Fri Nov 30 14:31:21 1990";
date = (raw[1..10] + ",") + raw[20..24];
ret = {};
for line in (desc)
ret = {@ret, strsub(line, "%d", date)};
endfor
return ret;
.
#152:0
target = args[2];
link = args[1];
"target:contribute_link(link);";
target:contribute_link(link);
target:contribute_partial(": ");
$jtext:convert_to("linemode", link[3], target);
.
#153:0
"make_link(object, verbnum, verbname, dobjspec, prepspec, iobjspec)";
return this:make_named_link(@args, tostr(args[1], ":", args[3]));
.
#153:1
set_task_perms($no_one);
target = args[1];
address = args[2];
ob = $string_utils:literal_object($jaddress:extract("objnum", address)[2]);
verbnum = $jaddress:extract("verbnum", address)[2];
verbname = $jaddress:extract("verbname", address)[2];
verbargs = $list_utils:slice($list_utils:map_arg($jaddress, "extract", {"dobjspec", "prepspec", "iobjspec"}, address), 2);
if (!((verb_info(ob, verbnum)[3] == verbname) && (verb_args(ob, verbnum) == verbargs)))
found = 0;
verbs = verbs(ob);
first = 1;
closest = 0;
points = 0;
while ((points != 3) && (vindex = verbname in verbs[first..length(verbs)]))
mp = 0;
va = verb_args(ob, tostr(vindex - 1));
for a in [1..3]
if (va[a] == verbargs[a])
mp = mp + 1;
endif
endfor
if (mp > points)
closest = vindex;
points = mp;
endif
endwhile
if (points)
verbnum = tostr(closest - 1);
else
verbnum = "";
endif
endif
if (!verbnum)
target:receive_vbox({$jtext.title, tostr(ob, ":", verbname, " at ", $network.MOO_name)});
target:receive_vbox({$jtext.paragraph, "Sorry, that verb does not exist.  You may wish to examine ", $jaddress.browser_object:make_link(ob), " again."});
return;
endif
target:receive_vbox({$jtext.title, tostr(ob, ":", verbname, " at ", $network.MOO_name)});
target:receive_vbox({$jtext.header, 1, tostr(ob, ":", verbname)});
code = `verb_code(ob, verbnum, 0, 1) ! ANY';
if (code)
docs = $code_utils:verb_documentation(ob, verbnum);
code = code[length(docs) + 1..length(code)];
for line in (docs)
target:receive_vbox({$jtext.paragraph, line});
endfor
xalpha = "abcdefghijklmnopqrstuvwxyz_";
id = tostr("[", xalpha, "]+");
core_props = {};
"core_props should go away.  xalpha too, probably.";
core_objs = {};
core_verbs = {};
plaintext = {$jtext.plaintext};
for line in (code)
for segment in (listdelete($string_utils:to_list(line, "$"), 1))
base = #0;
fullref = "$";
findprop = 1;
findverb = 0;
while (findprop)
if (subs = match(segment, id))
property = segment[subs[1]..subs[2]];
fullref = fullref + property;
base = base.(property);
if (valid(base))
core_objs = setadd(core_objs, {fullref, base});
next = segment[subs[2] + 1];
"can never be end of line in valid verb code, whee.";
fullref = fullref + next;
if (next == ".")
segment[1..subs[2] + 1] = "";
else
findprop = 0;
if (next == ":")
segment[1..subs[2] + 1] = "";
findverb = 1;
endif
endif
else
findprop = 0;
endif
else
findprop = 0;
endif
endwhile
if (findverb && (subs = match(segment, id)))
vname = segment[subs[1]..subs[2]];
core_verbs = setadd(core_verbs, {fullref + vname, base, vname});
endif
endfor
plaintext = listappend(plaintext, line);
endfor
target:receive_vbox(plaintext);
if (core_objs)
"need to insert a blank line somehow here.";
objects = {};
for o in (core_objs)
objects = listappend(objects, $jaddress.browser_object:make_named_link(@o));
endfor
target:receive_vbox({$jtext.paragraph, "Objects referred to: ", @$jtext:english_list(objects), "."});
endif
verbs = {};
for cv in (core_verbs)
if (loc = $object_utils:has_verb(@cv[2..3]))
loc = loc[1];
NUM = $code_utils:find_verb_named(loc, cv[3]);
verbs = listappend(verbs, $jaddress.browser_verb:make_named_link(loc, NUM, cv[3], @verb_args(loc, cv[3]), cv[1]));
endif
endfor
if (verbs)
target:receive_vbox({$jtext.paragraph, "Verbs referred to: ", @$jtext:english_list(verbs)});
endif
else
"Suppose we should put in a title/header, but who cares.";
target:receive_vbox({$jtext.paragraph, "I can't find that verb, or there was an error."});
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863663527, \"Erik\", #74, \"JHM\"}";
.
#153:2
"make_link(object, verbnum, verbname, dobjspec, prepspec, iobjspec, name)";
return {$jtext.link.root, {this, {{"objnum", tostr(args[1])}, {"verbnum", args[2]}, {"verbname", args[3]}, {"dobjspec", args[4]}, {"prepspec", args[5]}, {"iobjspec", args[6]}}}, args[7]};
.
#153:3
return {@pass(@args), $no_one, $string_utils, $jaddress, $list_utils, $jtext, $jtext.title, $network, $jtext.paragraph, $jaddress.browser_object, $jtext.header, $code_utils, $jtext.plaintext, $object_utils, $jaddress.browser_verb, $jtext.link, $jtext.link.root};
.
#154:0
"make_link(object, ancestor)";
if (n = length(properties(args[2])))
return {$jtext.link.root, {this, {{"objnum", tostr(args[1])}, {"ancestor", tostr(args[2])}}}, tostr(n, " propert", (n == 1) ? "y" | "ies")};
else
return "No properties";
endif
.
#154:1
"browse_properties(target, address)";
"A list of the properties defined on <ancestor> with their values as set on <object>.";
target = args[1];
address = args[2];
set_task_perms($no_one);
object = $string_utils:literal_object($jaddress:extract("objnum", address)[2]);
ancestor = $string_utils:literal_object($jaddress:extract("ancestor", address)[2]);
target:receive_vbox({$jtext.title, tostr(object, " from ", ancestor, " at ", $network.MOO_name)});
if (!$object_utils:isa(object, ancestor))
target:receive_vbox({$jtext.paragraph, $jaddress.browser_object:make_link(object), " is no longer a descendant of ", $jaddress.browser_object:make_link(ancestor), ".  You may wish to retrieve it again."});
return;
elseif ((props = properties(ancestor)) == E_PERM)
target:receive_vbox({$jtext.paragraph, $jaddress.browser_object:make_link(ancestor), " is unreadable."});
return;
elseif (!props)
target:receive_vbox({$jtext.paragraph, "No properties are defined on ", $jaddress.browser_object:make_link(ancestor), "."});
return;
endif
target:receive_vbox({$jtext.header, 2, {$jtext.hgroup, $jaddress.browser_object:make_link(object), ": Properties from ", $jaddress.browser_object:make_link(ancestor)}});
"Wanted <DL COMPACT> but my browsers don't support it.";
dlist = {$jtext.dlist};
for prop in (props)
$command_utils:suspend_if_needed(0);
info = property_info(object, prop);
if (info == E_PROPNF)
"nonatomicity, lose lose";
elseif (info == E_PERM)
dlist = listappend(dlist, {prop, "** unreadable **"});
else
pname = tostr(is_clear_property(object, prop) ? "^" | ",", prop);
(info[2][1] != "r") && (info[2][1..0] = " ");
(info[2][2] != "w") && (info[2][2..1] = " ");
val = object.(prop);
if (typeof(val) == OBJ)
val = $jaddress.browser_object:make_link(val);
else
val = $string_utils:print(val);
endif
dlist = listappend(dlist, {pname, {$jtext.hgroup, $jaddress.browser_object:make_link(info[1]), " ", info[2], " ", val}});
endif
endfor
target:receive_vbox(dlist);
.
#154:2
return {@pass(@args), $jtext, $jtext.link, $jtext.link.root, $no_one, $string_utils, $jaddress, $jtext.title, $network, $object_utils, $jtext.paragraph, $jaddress.browser_object, $jtext.header, $jtext.hgroup, $jtext.dlist, $command_utils};
.
#155:0
object = args[1];
if (!$perm_utils:invoked_by_function("move"))
raise(E_PERM);
endif
this:broadcast_event_enter(object);
.
#155:1
"Copied from generic room (#3):exitfunc by Doug (#2) Fri Jan 14 18:30:36 1994 EST";
object = args[1];
if ((!$perm_utils:invoked_by_function("move")) && (!$perm_utils:invoked_by_function("recycle")))
raise(E_PERM);
endif
this:broadcast_event_exit(object);
"if (object.owner != this.owner)";
"  this:remove_dependent(object);";
"endif";
return;
"Metadata 202106";
"Last-Modify: {833067191, \"Ken\", #75, \"JHM\"}";
.
#155:2
contents = length(args) ? args[1] | this:visible_of(this:contents());
if (contents)
player:tell("Contents:");
for thing in (contents)
player:tell("  ", thing:name_for_tell_contents());
endfor
elseif ((!this:contents()) && (msg = this:empty_msg()))
player:tell(msg);
endif
.
#155:3
return (msg = this.(verb)) ? $string_utils:pronoun_sub(msg) | "";
.
#155:4
"Return a phrase for \"in this room\".";
return this:name("d", {"in"});
name = this:iname();
words = $string_utils:explode(name);
if ($code_utils:get_prep(@words)[1])
return name;
else
return "in " + name;
endif
.
#155:5
return {@pass(@args), @this:contents()};
.
#155:6
things = this:visible_of(this:contents());
integrate = {};
if ("for now" || this.integration_enabled)
for i in (things)
if (this:ok_to_integrate(i))
integrate = {@integrate, i};
things = setremove(things, i);
endif
endfor
endif
pass(integrate);
this:tell_contents(things);
return;
"Metadata 202106";
"Last-Modify: {833557793, \"DavidSan\", #84, \"JHM\"}";
.
#155:7
name = args[1];
if (!name)
this:tell_contents();
elseif ((thing = this:match(name)) == $failed_match)
player:tell("You don't see any \"", name, "\" ", prepstr, " ", this:dname(), ".");
elseif (thing == $ambiguous_match)
player:tell("There are several things ", prepstr, " ", this:dname(), " one might call \"", name, "\".");
else
thing:look_self();
endif
"Copied from generic container (#1250):look_in by Doug (#107) Fri May  6 23:52:47 1994 EDT";
.
#155:8
return $integration_utils:visible_of(@args);
"Copied from generic room (#3):visible_of by Ragnar (#102) Sat May  7 01:04:11 1994 EDT";
.
#155:9
return setremove(this:contents(), player);
.
#155:10
return args[1]:integrate_in(this) && args[1]:integrate_container_msg();
.
#155:11
return 1;
.
#155:12
return $string_utils:capitalize(this:in_name(@args));
.
#155:13
"Return a phrase for \"in this room\".";
name = args[1];
words = $string_utils:explode(name);
if (is_clear_property(this, "in_msg") && $code_utils:get_prep(@words)[1])
return name;
else
return ((i = this:in_msg(@args)) ? i + " " | "") + name;
endif
"Copied from generic containing object (#1656):in_name by Erik (#74) Wed Apr 26 13:21:29 1995 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {912965855, \"Ken\", #75, \"JHM\"}";
.
#157:0
{connection} = args;
if (caller != this)
raise(E_PERM);
elseif (typeof(session = $recycler:_create(this.session)) != OBJ)
raise(session);
endif
session:set_connection(connection);
session:initialize_connection();
return session;
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#157:1
{session} = args;
if (!(caller in {this, session}))
raise(E_PERM);
elseif (!$object_utils:isa(session, this.session))
raise(E_INVARG);
elseif (session == this.session)
raise(E_INVARG);
else
$recycler:_recycle(session);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#157:2
{who} = args;
if (caller != this)
raise(E_PERM);
endif
return this:create_session(who);
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#157:3
{con} = args;
if (caller == con)
this:destroy_session(con);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#157:4
"string version number -> {major, minor}";
{version} = args;
if (m = match(version, "%([0-9]+%)%.%([0-9]+%)"))
return {tonum(substitute("%1", m)), tonum(substitute("%2", m))};
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#157:5
{client, server} = args;
{min1, max1} = client;
{min2, max2} = server;
min1 = (typeof(min1) == STR) ? this:parse_version(min1) | min1;
min2 = (typeof(min2) == STR) ? this:parse_version(min2) | min2;
max1 = (typeof(max1) == STR) ? this:parse_version(max1) | max1;
max2 = (typeof(max2) == STR) ? this:parse_version(max2) | max2;
if (!(((min1 && min2) && max1) && max2))
return;
else
if ((this:compare_version(max1, min2) <= 0) && (this:compare_version(max2, min1) <= 0))
if (this:compare_version(max1, max2) < 0)
return max2;
else
return max1;
endif
endif
endif
return 0;
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#157:6
"-1 if v1 > v2, 0 if v1 = v2, 1 if v1 < v2";
{v1, v2} = args;
if (v1 == v2)
return 0;
else
{major1, minor1} = v1;
{major2, minor2} = v2;
if (major1 == major2)
if (minor1 > minor2)
return -1;
else
return 1;
endif
elseif (major1 > major2)
return -1;
else
return 1;
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#157:7
{major, minor} = args;
return tostr(major, ".", minor);
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#157:8
{who} = args;
return `who.out_of_band_session ! E_PROPNF => $failed_match';
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#157:9
{who} = args;
if ($list_utils:assoc(caller, listeners()))
if ($recycler:valid(who.out_of_band_session))
`who.out_of_band_session:finish() ! ANY';
endif
who.out_of_band_session = this:initialize_connection(who);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#157:10
{who} = args;
if ($list_utils:assoc(caller, listeners()))
if ($recycler:valid(who.out_of_band_session))
`who.out_of_band_session:finish() ! ANY';
who.out_of_band_session = $nothing;
endif
else
raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#157:11
if ($list_utils:assoc(caller, listeners()))
if ($recycler:valid(session = player.out_of_band_session))
set_task_perms(player);
return session:do_out_of_band_command(@args);
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#157:12
return this.registry:(verb)(@args);
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#157:13
{who, @rest} = args;
if (valid(session = this:session_for(who)))
return session:(verb)(@rest);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#157:14
return {@pass(@args), this.package, this.session, this.registry, this.parser, $cord};
"Metadata 202106";
"Last-Modify: {895266713, \"Ken\", #75, \"JHM\"}";
.
#160:0
"Boots a player & logs the event";
if (!this:is_trusted(player))
return E_PERM;
else
if (!$command_utils:player_match_failed(dobj = player:my_match_player(dobjstr), dobjstr))
if (!dobj:is_listening())
player:tell($string_utils:nn(dobj), " isn't connected, and can't be @boot-ed.");
else
message = {dobj.last_connect_place, tostr("Reason: ", (iobjstr || $command_utils:read("a reason for booting " + dobj:name())) || "(None given.)")};
lock = verb[length(verb)] == "!";
dobj:tell($string_utils:pronoun_sub(this.booted_msg));
until = time() + (3600 * 24);
subject = tostr($string_utils:nn(player), " ", verb, "-ed ", $string_utils:nn(dobj));
if (lock)
message = {@message, ("All from that site banned until " + ctime(until)) + "."};
$login:bootlist($string_utils:connection_hostname(dobj.last_connect_place), until);
if (!$object_utils:isa(dobj, $guest))
$login:newt_player(dobj, until);
endif
endif
$mail_agent:send_message(player, $community_group.mail_recipient, subject, message);
boot_player(dobj);
player:tell("You booted ", $string_utils:nn(dobj), " for reason: ", iobjstr, ".");
endif
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863670990, \"Erik\", #74, \"JHM\"}";
.
#160:1
"@newt <player> [commentary]";
"";
"Turns a player into a newt.  It can get better...";
"";
"Does a permissions check, then calls $wiz_utils:do_newt to do the legwork and tell the user what happened.";
if (!this:is_trusted(caller_perms()))
return E_PERM;
endif
$wiz_utils:do_newt(@args);
.
#160:2
"@denewt <player> [commentary]";
"";
"Undoes the effects of @newt, sending commentary if given.  See $wiz_utils:do_denewt for details.";
if (!this:is_trusted(caller_perms()))
return E_PERM;
endif
$wiz_utils:do_denewt(@args);
.
#160:3
"@net-who prints all connected users and hosts.";
"@net-who player player player prints specified users and current or most recent connected host.";
"@net-who from hoststring prints all players who have connected from that host or host substring.  Substring can include *'s, e.g. @net-who from *.foo.edu.";
su = $string_utils;
if (!this:is_trusted(caller_perms()))
return E_PERM;
endif
if ((prepstr == "from") && dobjstr)
player:notify(tostr("Usage:  ", verb, " from <host string>"));
elseif (((prepstr != "from") || dobjstr) || (!iobjstr))
"Not parsing 'from' here...  Instead printing connected/recent users.";
footnotes = {};
if (!(pstrs = args))
unsorted = connected_players();
else
unsorted = listdelete($command_utils:player_match_result(su:match_player(pstrs), pstrs), 1);
endif
if (!unsorted)
return;
endif
alist = {};
nwidth = length("Player name");
for u in (unsorted)
pref = u.programmer ? "% " | "  ";
u.programmer && (footnotes = setadd(footnotes, "prog"));
$command_utils:suspend_if_needed(0);
if (u in connected_players())
lctime = ctime(time() - connected_seconds(u));
where = connection_name(u);
else
lctime = ctime(u.last_connect_time);
where = u.last_connect_place;
endif
u3 = {tostr(pref, u:name(), " (", u, ")"), lctime[5..10] + lctime[20..24]};
nwidth = max(length(u3[1]), nwidth);
where = $string_utils:connection_hostname(where);
if ($login:blacklisted(where))
where = "(*) " + where;
footnotes = setadd(footnotes, "black");
elseif ($login:graylisted(where))
where = "(+) " + where;
footnotes = setadd(footnotes, "gray");
endif
alist = {@alist, {@u3, where}};
endfor
alist = $list_utils:sort_alist_suspended(0, alist, 3);
$command_utils:suspend_if_needed(0);
headers = {"Player name", "Last Login", "From Where"};
before = {0, nwidth + 3, (nwidth + length(ctime(0))) - 11};
tell1 = "  " + headers[1];
tell2 = "  " + su:space(headers[1], "-");
for j in [2..3]
tell1 = su:left(tell1, before[j]) + headers[j];
tell2 = su:left(tell2, before[j]) + su:space(headers[j], "-");
endfor
player:notify(tell1);
player:notify(tell2);
active = 0;
for a in (alist)
$command_utils:suspend_if_needed(0);
tell1 = a[1];
for j in [2..3]
tell1 = su:left(tell1, before[j]) + a[j];
endfor
player:notify(tell1[1..min(length(tell1), 79)]);
endfor
if (footnotes)
player:notify("");
if ("prog" in footnotes)
player:notify(" %  == programmer.");
endif
if ("black" in footnotes)
player:notify("(*) == blacklisted site.");
endif
if ("gray" in footnotes)
player:notify("(+) == graylisted site.");
endif
endif
elseif (!index(where = iobjstr, "*"))
"Oh good... search for users from a site... the fast way.  No wild cards.";
nl = 0;
bozos = {};
sites = $site_db:find_all_keys(where);
while (sites)
s = sites;
sites = {};
for domain in (s)
for b in ($site_db:find_exact(domain))
if (typeof(b) == STR)
sites = setadd(sites, (b + ".") + domain);
else
bozos = setadd(bozos, b);
nl = max(length(tostr(b, valid(b) ? b:name() | "")), nl);
endif
endfor
endfor
endwhile
if (bozos)
player:notify(tostr(su:left("  Player", nl + 7), "From"));
player:notify(tostr(su:left("  ------", nl + 7), "----"));
for who in (bozos)
st = su:left(tostr(valid(who) ? (who.programmer ? "% " | "  ") + who:name() | "", " (", who, ")"), nl + 7);
comma = 0;
if ($object_utils:isa(who, $player))
for p in (who.all_connect_places)
if (comma && (length(p) >= (78 - length(st))))
player:notify(tostr(st, ","));
st = su:space(nl + 7) + p;
else
st = tostr(st, comma ? ", " | "", p);
endif
comma = 1;
$command_utils:suspend_if_needed(0);
endfor
else
st = st + (valid(who) ? "*** recreated ***" | "*** recycled ***");
endif
player:notify(st);
endfor
player:notify("");
player:notify(tostr(length(bozos), " player", (length(bozos) == 1) ? "" | "s", " found."));
else
player:notify(tostr("No sites matching `", where, "'"));
endif
else
"User typed 'from'.  Go search for users from this site.  (SLOW!)";
howmany = 0;
for who in (players())
$command_utils:suspend_if_needed(0);
matches = {};
for name in (who.all_connect_places)
if ((index(where, "*") && su:match_string(name, where)) || ((!index(where, "*")) && index(name, where)))
matches = {@matches, name};
endif
endfor
if (matches)
howmany = howmany + 1;
player:notify(tostr(who:name(), " (", who, "): ", su:english_list(matches)));
endif
endfor
player:notify(tostr(howmany || "No", " matches found."));
endif
.
#160:4
"Copied from generic wizard (#57):@newpassword by Ken (#2) Sat Jan 15 19:56:54 1994 EST";
"@new-password <player-name> is [<new-password>]";
"Set a player's password and optionally send mail to eir .email_address.";
"If <new-password> isn't supplied a random one is generated.";
if (!this:is_trusted(caller_perms()))
return E_PERM;
endif
dobj = player:my_match_player(dobjstr);
if (!valid(dobj))
$command_utils:player_match_failed(dobj, dobjstr);
else
if (iobjstr == "")
iobjstr = $wiz_utils:random_password(5);
endif
if ($command_utils:yes_or_no(tostr("About to change password for ", dobj:dname(), " (", dobj, ").  OK?")))
old = dobj.password;
dobj.password = crypt(iobjstr);
player:notify(tostr(dobj:name(), " (", dobj, ")'s password set to \"", iobjstr, "\".  Encrypted old password was \"", old, "\"."));
if (dobj.email_address)
if ($command_utils:yes_or_no(tostr("Do you want to email the new password to ", dobj.email_address, "?")))
$network:sendmail(dobj.email_address, "@new-password " + $string_utils:nn(dobj), @{"Cc: " + $login.registration_address, "Reply-To: " + $login.registration_address, ((((((((("The character \"" + dobj:name()) + "\" registered to this email address on ") + $network.MOO_name) + " (") + $network.site) + " ") + tostr($network.port)) + ") has a new password.  Your new password is \"") + iobjstr) + "\"."});
endif
else
player:tell(dobj:dnamec(), " has no registered email address, so you can't send ", dobj:po(), " ", dobj:pp(), " new password.");
endif
else
player:notify("Aborted.");
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863670995, \"Erik\", #74, \"JHM\"}";
.
#160:5
"Check to make sure they're on the CG or a wizard.";
if (!this:is_trusted(caller_perms()))
return E_PERM;
endif
if ($command_utils:player_match_failed(ungag_this = player:my_match_player(dobjstr), dobjstr))
return ungag_this;
endif
if ($command_utils:player_match_failed(gagger = player:my_match_player(iobjstr), iobjstr))
return gagger;
endif
iobj = gagger;
dobj = ungag_this;
if (ungag_this in gagger.gaglist)
gagger.gaglist = $list_utils:setremove_all(gagger.gaglist, ungag_this);
if (player != ungag_this)
message = "You have been removed from %i's gaglist by %n.";
ungag_this:tell($string_utils:pronoun_sub(message));
message = "You remove %d from %i's gaglist.";
player:tell($string_utils:pronoun_sub(message));
else
message = "You remove yourself from %i's gaglist.";
player:tell($string_utils:pronoun_sub(message));
endif
else
if (player != ungag_this)
message = "The %d %<d:isn't> in %i's gaglist.";
player:tell($string_utils:pronoun_sub(message, dobj));
else
message = "You aren't in %i's gaglist.";
player:tell($string_utils:pronoun_sub(message));
endif
endif
"Maybe log it somewhere.";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863671002, \"Erik\", #74, \"JHM\"}";
.
#160:6
"Creates a player.";
"Syntax:  @make-player name[,aliases] [email-address [password]]";
"If no password is given, generates a random password for the player.";
"Email-address is stored in $registration_db and on the player object.";
"Comments can be added by specifying the email address \"email@address comment\"";
if ((!this:is_trusted(player)) || (!this:is_trusted(caller_perms())))
return;
endif
$wiz_utils:do_make_player(@args);
"Copied from generic wizard (#57):@make-player by splat (#3024) Sun Nov 13 20:59:55 1994 EST";
.
#160:7
return $string_utils:pronoun_sub(this.(verb));
.
#161:0
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
.
#161:1
"@set-group <this> to <group>";
if ($command_utils:object_match_failed(iobj, iobjstr))
return;
elseif (!this:trusts(player))
player:tell("I can't let you do that, ", player:name(), ".");
return;
else
this:set_group(iobj);
player:tell("Group of ", this:name(), " set to ", iobj:dname(), ".");
endif
.
#161:2
return this.group:is_member(args[1]) || $perm_utils:controls(args[1], this);
"The second part may or may not be temporary.  Since I am writing the CG feature, I need to be able to test it even though I'm not on the CG...";
.
#161:3
"Noisy :trusts ... Displays error message if not";
if (!this:trusts(who = args[1]))
who:tell("I can't let you do that.  You're not a member of ", this.group:dname(), ".");
else
return 1;
endif
.
#161:4
return this:trusts(args[1]);
.
#162:0
return this.features;
.
#162:1
"feature_list([features])";
"Return a string of the names of features, or the features in the registry if no argument.";
return $string_utils:iname_list(args ? args[1] | this:features(), "none");
.
#162:2
"Usage:  :add_feature(feature[, whom])";
"";
"Add a feature to the registry, if `whom' owns the feature or is a wizard.";
"";
"Whom defaults to caller_perms().  If caller != this, whom must be equal to caller_perms().";
"";
"Returns the new list of features if successful, or a string explaining the error if not.";
feature = args[1];
if (length(args) > 1)
whom = args[2];
if ((caller != this) && (whom != caller_perms()))
return E_PERM;
endif
else
whom = caller_perms();
endif
if (feature in this:features())
return tostr(feature:dnamec(), " ", feature:verb_sub("is"), " already registered in ", this:dname(), ".");
elseif ((!feature.feature_ok) && (!$object_utils:has_callable_verb(feature, "feature_ok")))
return tostr(feature:dnamec(), " ", feature:verb_sub("is"), " not a valid feature object.  Please set its .feature_ok bit or give it a :feature_ok verb.");
elseif (!$perm_utils:controls(whom, feature))
return tostr(E_PERM);
endif
return this.features = {@this.features, feature};
.
#162:3
"remove_feature(feature)";
"Remove feature from the registry.";
"Returns the new list of features if successful, or a string explaining the error if not.";
feature = args[1];
if (!(i = feature in this:features()))
return tostr(feature:dnamec(), " ", feature:verb_sub("is"), " not registered in ", this:dname(), ".");
elseif (!$perm_utils:controls(caller_perms(), feature))
return tostr(E_PERM);
endif
return this.features = listdelete(this.features, i);
.
#162:4
return $string_utils:pronoun_sub(this.(verb), @args);
.
#162:5
"Open the registry to the table of contents.";
if (this.open != "closed")
player:tell(this:already_open_msg());
return;
endif
this:set_open("contents");
$you:say_action(this:open_msg());
.
#162:6
"Close the registry.";
if (this.open == "closed")
player:tell(this:already_closed_msg());
return;
endif
this:set_open("closed");
$you:say_action(this:close_msg());
.
#162:7
return this.(verb);
.
#162:8
"The contents page.";
"It would be nice to have chapters for categories or something.";
header = this:contents_header_msg();
underline = $string_utils:space(length(header), "-");
if (!(features = this:features()))
contents = {this:no_contents_msg()};
else
contents = this:feature_list_lines(player, features);
endif
return {header, underline, "", @contents};
.
#162:9
"The index page.";
return {"(This page intentionally left blank.)"};
.
#162:10
"A page with a feature object.";
feature = args ? args[1] | dobj;
if (typeof(help = feature:help_text()) != LIST)
help = {help};
endif
return help;
.
#162:11
desc = this:description(@args);
if (desc)
player:tell_lines(desc);
endif
if ($list_utils:grep_l(desc, this:name()))
it = this:gender_obj();
else
it = this;
endif
if (typeof(open = this.open) == OBJ)
dobj = open;
open = "feature";
endif
player:tell(this:(open + "_msg")(player, it));
.
#162:12
"Add a feature object to the registry.";
if ($command_utils:object_match_failed(dobj, dobjstr))
return;
endif
if (typeof(ret = this:add_feature(dobj, player)) == STR)
player:tell(ret);
return;
endif
if (this.open == "closed")
this:open();
endif
if (this.open != "empty")
iobjstr = "empty";
this:turn();
endif
$you:say_action(this:write_msg());
this:set_open(dobj);
.
#162:13
"Turn the registry to a given page.";
if (!iobjstr)
"Candidate for the questions project...";
player:tell(this:what_page_msg());
return;
endif
if (this.open == "closed")
this:open();
endif
if (iobjstr in {"contents", "index", "empty"})
this:set_open(iobjstr);
feature = #-1;
elseif (this:feature_match_failed(feature = this:match_feature(iobjstr), iobjstr))
return;
else
iobjstr = "feature";
this:set_open(feature);
dobj = feature;
endif
$you:say_action(this:(("turn_to_" + iobjstr) + "_msg")());
return 1;
.
#162:14
"match_feature(feature_name)";
"Return a registered feature matching feature_name.";
if (!(string = args[1]))
return $nothing;
endif
features = this:features();
if (!((ret = $string_utils:match_object(string, $no_one)) in features))
ret = $string_utils:match(string, features, "name", features, "aliases");
endif
if (ret == $failed_match)
for feature in (features)
if ((info = verb_info(feature, string)) && (info[3] in feature.commands))
if (ret == $failed_match)
ret = feature;
else
return $ambiguous_match;
endif
endif
endfor
endif
return ret;
.
#162:15
"Usage: feature_match_failed(match_result, string)";
"Prints a message if string does not match the match_result.  Generally used after the match_result is derived from a :match_feature(string).";
match_result = args[1];
if (typeof(match_result) != OBJ)
return 0;
endif
string = args[2];
tell = $perm_utils:controls(caller_perms(), player) ? "notify" | "tell";
if ((index(string, "#") == 1) && ($code_utils:toobj(string) != E_TYPE))
"...avoid the `I don't know which `#-2' you mean' message...";
if (!valid(match_result))
player:(tell)(tostr(string, " does not exist."));
endif
return !valid(match_result);
elseif (match_result == $nothing)
player:(tell)("You must supply a feature.");
elseif (match_result == $failed_match)
player:(tell)(tostr("There are no features in ", this:dname(), " matching \"", string, "\"."));
elseif (match_result == $ambiguous_match)
player:(tell)(tostr("There is more than one feature in ", this:dname(), " that matches \"", string, "\"."));
elseif (!valid(match_result))
player:(tell)(tostr(match_result, " does not exist."));
else
return 0;
endif
return 1;
"Copied from command utilities (#56):object_match_failed by Doug (#107) Tue May 17 23:12:53 1994 EDT";
.
#162:16
"An empty page.";
return {};
.
#162:17
"feature_list_lines([who[, features]])";
"Return a list of lines of feature names in two columns (based on who's linelength.";
"who defaults to player; features defaults to all registered features.";
who = args ? args[1] | player;
features = (length(args) > 1) ? args[2] | this:features();
features_alist = $list_utils:make_alist({features, $list_utils:map_verb(features, "name")});
features_alist = $list_utils:sort_alist(features_alist, 2);
long = {};
fe = {};
width = abs(who.linelen);
half = width / 2;
for c in (features_alist)
name = c[2];
if (length(name) > half)
long = {@long, name};
else
fe = {@fe, name};
endif
endfor
return {@$string_utils:columnize(fe, 2, width), @long};
.
#162:18
"Read the current page, or open it if it's closed.";
if (this.open == "closed")
this:open();
endif
open = this.open;
if (typeof(open) == OBJ)
dobj = open;
open = "feature";
endif
player:tell(this:(open + "_msg")());
if (page = this:(open)())
player:tell_lines({"", @page});
endif
.
#162:19
"Read a specific page in the registry.";
iobjstr = dobjstr;
if (this:turn())
this:read();
endif
.
#162:20
"The .open property can be one of \"closed\", \"contents\", \"index\", \"empty\", or the object number of a feature.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
elseif (length(args) != 1)
return E_ARGS;
elseif ((!((open = args[1]) in {"closed", "contents", "index", "empty"})) && (typeof(open) != OBJ))
return E_INVARG;
endif
return this.open = open;
.
#162:21
if ($__core_init_phase)
pass(@args);
this:set_open("closed");
else
$error:raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {863672878, \"Erik\", #74, \"JHM\"}";
.
#163:0
if ($__core_init_phase)
pass(@args);
for p in ({"www", "home-page", "web-entrance", "remote-control"})
delete_property(this, p);
endfor
else
$error:raise(E_PERM);
endif
.
#164:0
"wrapper for add_verb() to prevent writable verbs";
{o, info, vargs} = args;
{owner, perms, names} = info;
set_task_perms(caller_perms());
if (`!$server_options.permit_writable_verbs ! E_PROPNF' && index(perms, "w"))
raise(E_INVARG, "Writable verbs are not permitted.");
endif
return add_verb(o, info, vargs);
.
#164:1
"wrapper for set_verb_info() to prevent writable verbs";
{o, vn, info} = args;
{owner, perms, names} = info;
set_task_perms(caller_perms());
if (`!$server_options.permit_writable_verbs ! E_PROPNF' && index(perms, "w"))
raise(E_INVARG, "Writable verbs are not permitted.");
endif
return set_verb_info(o, vn, info);
.
#164:2
set_task_perms(caller_perms());
{o, v, code} = args;
if ((!code) || ((code[$] != "return;") && (index(code[$], "return ") != 1)))
code = {@code, "return 0 && \"Automatically Added Return\";"};
endif
oldcode = verb_code(o, v);
{oldcode, oldmeta} = this:metadata_from_code(oldcode);
who = caller_perms();
if ($__core_init_phase)
last_modify = {time(), "core initialization", who, "core"};
else
last_modify = {time(), who.name, who, $network.moo_name};
endif
newmeta = this:set_metadata_item(oldmeta, "Last-Modify", last_modify);
return set_verb_code(o, v, {@code, @this:metadata_to_code(newmeta)});
"Metadata 202106";
"Last-Modify: {865048058, \"Ken\", #75, \"JHM\"}";
.
#164:3
set_task_perms(caller_perms());
code = verb_code(@args);
if (idx = this:metadata_begin_index(code))
code = code[1..idx - 1];
endif
if (length(code) && (code[$] == "return 0 && \"Automatically Added Return\";"))
return code[1..$ - 1];
else
return code;
endif
"Metadata 202106";
"Last-Modify: {833903976, \"Ken\", #75, \"JHM\"}";
.
#164:4
"Usage: $verb_metadata(object, verb)";
"Returns metadata for a verb.";
set_task_perms(caller_perms());
c = verb_code(@args);
return this:metadata_from_code(c)[2];
.
#164:5
"Usage: $set_verb_metadata(o, v, metadata)";
"Sets metadata for a verb.  Updates Last-Modify.";
set_task_perms(caller_perms());
{o, v, metadata} = args;
{oldcode, oldmetadata} = this:metadata_from_code(verb_code(o, v));
if (oldcode[$] != "return;")
oldcode = {@oldcode, "return;"};
endif
"leave last modify info alone";
return set_verb_code(o, v, {@oldcode, @this:metadata_to_code(metadata)});
last_modify = {time(), who.name, who, $network.moo_name};
return set_verb_code(o, v, {@oldcode, @this:metadata_to_code(this:set_metadata_item(metadata, "Last-Modify", last_modify))});
"Metadata 202106";
"Last-Modify: {834113624, \"Ken\", #75, \"JHM\"}";
.
#164:6
":metadata_to_code(metadata)";
"";
"Return lines of code suitable for appending to a verb to store metadata";
"";
{metadata} = args;
code = {"\"Metadata 202106\";"};
for line in (metadata)
{name, datum} = line;
if (index(name, ":"))
raise(E_INVARG, "Datum label cannot contain :");
endif
d = toliteral(datum);
line = toliteral((name + ": ") + d) + ";";
code = {@code, line};
endfor
return code;
.
#164:7
{code} = args;
if (!(idx = this:metadata_begin_index(code)))
return {code, {}};
endif
idx = idx + 1;
metadata = {};
for i in [idx..length(code)]
line = code[i];
line = $no_one:eval(line)[2];
if (colon = index(line, ":"))
name = line[1..colon - 1];
data = line[colon + 2..$];
if (data[1] == "\"")
value = data[2..$ - 1];
else
value = $no_one:eval(data)[2];
endif
metadata = {@metadata, {name, value}};
endif
endfor
return {code[1..idx - 2], metadata};
"Metadata 202106";
"st-Modify: {\"Line 1:  Missing quote\", \"Line 1:  parse error\"}";
"ast-Modify: {\"Line 1:  Missing quote\", \"Line 1:  parse error\"}";
"Last-Modify: {833070654, \"Ken\", #75, \"JHM\"}";
.
#164:8
{code} = args;
idx = 0;
for i in [-length(code)..-1]
line = code[-i];
if (line == "\"Metadata 202106\";")
idx = -i;
break i;
endif
endfor
return idx;
.
#164:9
{metadata, name} = args;
return $list_utils:assoc(name, metadata);
.
#164:10
{metadata, name, value} = args;
if (i = $list_utils:iassoc(name, metadata))
metadata[i][2] = value;
else
metadata = {@metadata, {name, value}};
endif
return metadata;
.
#164:11
if (caller_perms() != #75)
raise(E_PERM);
endif
{o, v, metadata, lastmodify} = args;
{m_when, ?m_name = m_who:dname(), m_who, ?m_moo = $network.moo_name} = lastmodify[2];
lastmodify[2] = {m_when, m_name, m_who, m_moo};
metadata[$list_utils:iassoc("Last-Modify", metadata)] = lastmodify;
code = this:bf_verb_code(o, v);
if (length(code) && (code[$] == "return;"))
code == code[1..$ - 1];
endif
return set_verb_code(o, v, {@code, @this:metadata_to_code(metadata)});
"Metadata 202106";
"Last-Modify: {833905268, \"Ken\", #75, \"JHM\"}";
.
#164:12
set_task_perms(caller_perms());
func = verb[4..$];
if (why = this:("_protect_" + func)(@args))
raise(E_INVARG, why);
else
return call_function(func, @args);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {837492055, \"Ken\", #75, \"JHM\"}";
.
#164:13
{what} = args;
if (is_player(what) && $object_utils:isa(what, $player))
return "is_player() $players cannot be recycled.";
else
return 0;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {837491894, \"Ken\", #75, \"JHM\"}";
.
#164:14
{what, dest} = args;
"this will raise the same error as chparent if that's invalid";
if (!valid(what))
raise(E_INVARG);
endif
if ((is_player(what) && $object_utils:isa(what, $player)) && (!$object_utils:isa(dest, $player)))
return "is_player() $players cannot be chparented away from $player";
else
return 0;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {837492047, \"Ken\", #75, \"JHM\"}";
.
#164:15
"Usage:  :set_verb_metadata_item(object, verb, name, value)";
"";
"Sets metadata for a verb in place.";
{object, verb, name, value} = args;
set_task_perms(caller_perms());
this:set_verb_metadata(object, verb, this:set_metadata_item(this:verb_metadata(object, verb), name, value));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {876458000, \"Erik\", #74, \"JHM\"}";
.
#164:16
"Usage:  :verb_metadata_item(object, verb, name)";
"";
"Returns the metadata named 'name' for the given object and verb.  Actually, returns the pair {name, value}, or {} if there is no metadata with the given name.";
{object, verb, name} = args;
set_task_perms(caller_perms());
return this:metadata_item(this:verb_metadata(object, verb), name);
"Metadata 202106";
"Last-Modify: {876458031, \"Erik\", #74, \"JHM\"}";
.
#164:17
"Usage:  :delete_metadata_item(metadata, name)";
"";
"Returns the metadata, minus the item with the given name.";
{metadata, name} = args;
if (i = $list_utils:iassoc(name, metadata))
metadata[i..i] = {};
endif
return metadata;
return;
"Metadata 202106";
"last-modify: {876457610, \"Erik\", #74, \"JHM\"}";
.
#164:18
"Usage:  :delete_verb_metadata_item(object, verb, name)";
"";
"Deletes the item with the given name from the metadata for the given object and verb.";
{object, verb, name} = args;
set_task_perms(caller_perms());
this:set_verb_metadata(object, verb, this:delete_metadata_item(this:verb_metadata(object, verb), name));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {876457956, \"Erik\", #74, \"JHM\"}";
.
#171:0
if (verb == "verb_sub")
if (k = is_member(args[1], $list_utils:slice(this.verb_exceptions)))
return this.verb_exceptions[k][2];
endif
return $english:get_conj(args[1], 1);
endif
return $english:get_noun(args[1]);
"Metadata 202106";
"Last-Modify: {912983807, \"Ken\", #75, \"JHM\"}";
.
#174:0
":is_member(player)   =>  returns true iff player is a member of this group or some subgroup, either as a primary or secondary character.";
if (!(who = $registrar:all_chars(args[1])))
return who;
endif
for p in (who)
if (i = p in this.members)
return i;
endif
endfor
for g in (this.subgroups)
for p in (who)
if (i = g:is_member(p))
return {g, i};
endif
endfor
endfor
return 0;
.
#174:1
"Add a new member to the group.";
if (this:controls(caller_perms()))
if (args[1] in this.members)
return E_INVARG;
else
return this.members = {@this.members, args[1]};
endif
else
return E_PERM;
endif
.
#174:2
"Remove a member from the group.";
if (this:controls(caller_perms()) || (caller_perms() == args[1]))
if ((i = this:is_member(args[1])) && (typeof(i) == NUM))
return this.members = listdelete(this.members, i);
else
return E_INVARG;
endif
else
return E_PERM;
endif
.
#174:3
"Usage:  @addmember <member> to <group>";
"";
"Adds <member> to the list of people on <group>.";
if (!(caller_perms() in {player, #-1}))
$error:raise(E_PERM);
elseif (!dobjstr)
player:tell("Add whom to ", this:dname(), "?");
retval = E_ARGS;
elseif ($command_utils:player_match_failed(retval = whom = player:my_match_player(dobjstr), dobjstr))
else
set_task_perms(player);
if (typeof(retval = this:add_member(whom)) == LIST)
player:tell(whom:dnamec(), " added to ", this:dname(), ".");
elseif (retval == E_INVARG)
player:tell(whom:dnamec(), " ", whom:verb_sub("is"), " already a member of ", this:dname(), ".");
else
player:tell(retval);
endif
endif
return retval;
.
#174:4
"Usage:  @rmmember <member> from <group>";
"";
"Removes <member> from the list of people on <group>.";
if (!dobjstr)
player:tell("Remove whom from ", this:dname(), "?");
retval = E_ARGS;
elseif ($command_utils:player_match_failed(retval = whom = player:my_match_player(dobjstr), dobjstr))
else
set_task_perms(caller_perms());
if (typeof(retval = this:remove_member(whom)) == LIST)
player:tell(whom:dnamec(), " removed from ", this:dname(), ".");
elseif (retval == E_INVARG)
player:tell(whom:dnamec(), " ", whom:verb_sub("is"), " not a member of ", this:dname(), ".");
else
player:tell(retval);
endif
endif
return retval;
.
#174:5
"Preface the description with a header; include this.description; include the subgroups' descriptions, if they exist; and finish with a randomized member listing, if it's a leaf.";
desc = {name = $string_utils:uppercase(this:inamec()), $string_utils:space(length(name), args ? "-" | "=")};
desc = {@desc, "", @pass(@args)};
if (this.subgroups)
for sub in (this.subgroups)
desc = {@desc, "", @sub:description(1)};
endfor
else
desc = {@desc, "", ("Current members: " + this:member_list()) + "."};
endif
return desc;
.
#174:6
":all_members() => list of members of this admin group and all subgroups.";
members = this:members();
for s in (this.subgroups)
members = $set_utils:union(members, s:all_members());
endfor
return $list_utils:randomly_permute(members);
.
#174:7
":connected_members() => list of currently connected members of this admin group and all subgroups.";
return $set_utils:intersection(this:all_members(), connected_players());
.
#174:8
":member_list()";
"Return an English list of the names of all the members (and possibly their second characters) of this group.";
member_list = {};
for x in ($list_utils:randomly_permute(this.members))
$command_utils:suspend_if_needed(0);
names = x:iname();
if ((!(x in this.incognito)) && (others = $registrar:other_chars(x)))
names = ((names + " (a.k.a. ") + $string_utils:iname_list(others, "", " a.k.a. ", " a.k.a. ", "")) + ")";
endif
member_list = {@member_list, names};
endfor
return $string_utils:english_list(member_list, "nobody");
.
#174:9
for m in (members = this.members)
if (!is_player(m))
this.members = setremove(this.members, m);
this.incognito = setremove(this.incognito, m);
elseif (!(m in this.incognito))
members = {@members, @$registrar:other_chars(m)};
endif
endfor
return $list_utils:randomly_permute(members);
.
#174:10
"match_group(group name) => admin group with matching name";
groups = $object_utils:descendents(this);
return $string_utils:match(args[1], groups, "name", groups, "aliases");
.
#174:11
return this.members;
.
#174:12
"Set properties we don't want to be clear.";
this.subgroups = {};
this.members = {};
pass(@args);
.
#174:13
"Copied from command utilities (#56):object_match_failed by Doug (#2) Fri Dec 17 01:33:42 1993 EST";
"Usage: group_match_failed(group, string)";
"Prints a message if string does not match group.  Generally used after group is derived from a :match_group(string).";
match_result = args[1];
string = args[2];
tell = $perm_utils:controls(caller_perms(), player) ? "notify" | "tell";
if ((index(string, "#") == 1) && ($code_utils:toobj(string) != E_TYPE))
"...avoid the `I don't know which `#-2' you mean' message...";
if (!valid(match_result))
player:(tell)(tostr(string, " does not exist."));
endif
return !valid(match_result);
elseif (match_result == $nothing)
player:(tell)("You must give the name of some admin group.");
elseif (match_result == $failed_match)
player:(tell)(tostr("There is no admin group matching \"", string, "\"."));
elseif (match_result == $ambiguous_match)
player:(tell)(tostr("There is more than one admin group matching \"", string, "\"."));
elseif (!valid(match_result))
player:(tell)(tostr(match_result, " does not exist."));
else
return 0;
endif
return 1;
.
#174:14
":new_member_voters()";
"Who votes on new members for this group?";
"A vote must be unanimous for all members of the list returned.";
voters = {};
for group in (this.new_member_voter_groups)
for member in (group:member_set())
already = 0;
for other in ($registrar:all_chars(member))
if (other in voters)
already = 1;
endif
endfor
if (!already)
voters = {@voters, member};
endif
endfor
endfor
return voters;
.
#174:15
"@vacation [<admin>] from <admin group>";
"Remove <admin> from the active list of members of <admin group>, allowing em to be re-added later.  <admin> defaults to the player.";
if (!dobjstr)
"A player is allowed to remove emself.";
whom = player;
you = $you;
elseif ($command_utils:player_match_failed(whom = player:my_match_player(dobjstr), dobjstr))
return whom;
else
you = whom;
ok = 0;
for group in (this.new_member_voter_groups)
"    if (group:is_member(caller_perms()))";
"    change made by Michele on 15 nov 1994";
if (group:is_member(valid(caller_perms()) ? caller_perms() | caller))
ok = 1;
endif
endfor
if (!ok)
player:tell("Only members of ", $string_utils:dname_list(this.new_member_voter_groups, "nothing", " or "), " may remove a member of ", this:dname(), ".");
return E_PERM;
endif
endif
"where = 0;";
"for i in [1..length(this.members)]";
"x = this.members[i];";
"if (whom == x || (typeof(x) == LIST && who in x))";
"where = i;";
"endif";
"endfor";
"if (!where)";
where = this:is_member(whom);
if ((!where) || (typeof(where) == LIST))
player:tell(you:dnamec(), " ", you:verb_sub("isn't"), " an active member of ", this:dname(), ".");
return E_INVARG;
endif
this.vacationers = {@this.vacationers, this.members[where]};
this.members = listdelete(this.members, where);
player:tell(you:dnamec(), " ", you:verb_sub("is"), " now on vacation from ", this:dname(), ".");
return this.vacationers;
.
#174:16
"@return [<admin>] to <admin group>";
"Add <admin> back to the active list of members of <admin group>.  <admin> defaults to the player.";
if ((!dobjstr) || ((whom = player:my_match_player(dobjstr)) == player))
"A player is allowed to add emself back.";
whom = player;
you = $you;
elseif ($command_utils:player_match_failed(whom, dobjstr))
return whom;
else
you = whom;
ok = 0;
for group in (this.new_member_voter_groups)
"    if (group:is_member(caller_perms()))";
"    change made by michele on 15 nov 1994.";
if (group:is_member(valid(caller_perms()) ? caller_perms() | caller))
ok = 1;
endif
endfor
if (!ok)
player:tell("Only members of ", $string_utils:dname_list(this.new_member_voter_groups, "nothing", " or "), " may add a member of ", this:dname(), ".");
return E_PERM;
endif
endif
where = 0;
for i in [1..length(this.vacationers)]
x = this.vacationers[i];
if ((whom == x) || ((typeof(x) == LIST) && (who in x)))
where = i;
endif
endfor
if (!where)
player:tell(you:dnamec(), " ", you:verb_sub("isn't"), " an inactive member of ", this:dname(), ".");
return E_INVARG;
endif
this.members = {@this.members, this.vacationers[where]};
this.vacationers = listdelete(this.vacationers, where);
player:tell(you:dnamec(), " ", you:verb_sub("has"), " now returned from vacation from ", this:dname(), ".");
return this.members;
.
#174:17
"@incognito <player> for <admin group>";
"Don't include <player>'s alternate identities when listing members of <admin group>.";
if (!((caller_perms() in this:new_member_voters()) || this:is_member(caller_perms())))
player:tell(E_PERM);
elseif ($command_utils:player_match_failed(who = player:my_match_player(dobjstr), dobjstr))
elseif (!this:is_member(who))
player:tell(who:dnamec(), " ", who:verb_sub("isn't"), " a member of ", this:dname(), ".");
elseif (who in this.incognito)
player:tell(who:dnamec(), " ", who:verb_sub("is"), " already an incognito member of ", this:dname(), ".");
else
this.incognito = {@this.incognito, who};
player:tell(who:dnamec(), "'s alternate identities will no longer be revealed in member listings of ", this:dname(), ".");
endif
.
#174:18
"@reveal <player> for <admin group>";
"Include <player>'s alternate identities when listing members of <admin group>.";
if (!((caller_perms() in this:new_member_voters()) || this:is_member(caller_perms())))
player:tell(E_PERM);
elseif ($command_utils:player_match_failed(who = player:my_match_player(dobjstr), dobjstr))
elseif (!this:is_member(who))
player:tell(who:dnamec(), " ", who:verb_sub("isn't"), " a member of ", this:dname(), ".");
elseif (!(who in this.incognito))
player:tell(who:dnamec(), " ", who:verb_sub("isn't"), " an incognito member of ", this:dname(), ".");
else
this.incognito = setremove(this.incognito, who);
player:tell(who:dnamec(), "'s alternate identities will now be revealed in member listings of ", this:dname(), ".");
endif
.
#174:19
"controls(<who>)";
"Does <who> have permission to add/delete members to this group, etc.?";
return args[1] in this:new_member_voters();
.
#174:20
return this:description(@args);
"Copied from generic admin group (#2192):help_msg by Erik (#74) Sun Oct 23 18:13:54 1994 EDT";
.
#174:21
if ($__core_init_phase)
pass(@args);
"change $admin_group from children, so we don't have to worry about order";
if (this == $admin_group)
this.new_member_voter_groups = {};
this.members = {$first_wizard};
else
for p in ({"members", "leader", "vacationers", "incognito"})
clear_property(this, p);
endfor
endif
endif
.
#174:22
return {@pass(@args), $registrar, $error, $command_utils, $string_utils, $set_utils, $list_utils, $object_utils, $perm_utils, $code_utils, $you, $admin_group, @valid(this.mail_recipient) ? {this.mail_recipient} | {}};
.
#174:23
return tostr(this:name(), " ", strsub(verb, "_term", ""));
.
#174:24
return strsub(this.name, " ", "-");
.
#176:0
return {@pass(@args), this.feature};
.
#178:0
"Use the full name instead of any pronoun.";
"ps == subjective          foo";
"po == objective           foo";
"pr == reflexive           foo";
"pp == possessive          foo's";
"pq == possessive noun     foo's";
"c means capitalize.";
what = args[1];
return what:name(index("pq", verb[2]) ? "pd" | ("d" + verb[3..length(verb)]));
.
#179:0
"Usage:  :parse_send_args(msg, @posargs, @keywordargs)";
"";
"Transform a given message's arguments (mostly given positionally) into the correct form for MCP.  The cord type has an ordered list of argument keywords for all valid messages; these are matched with the arguments provided to produce an alist.  If more arguments are provided than keywords are available, then the remaining arguments should be {keyword, value} pairs.  This allows the passing of optional arguments and such.";
"";
"This verb returns an alist suitable for use with :client_notify().";
"";
"Examples:";
"  .messages_out = {{\"edit\", {\"name\", \"text\"}}}";
"  :parse_send_args(\"edit\", \"#123.foo\", {\"This is the first line.\"})";
"    => {{\"name\", \"#123.foo\"}, {\"text\", {\"This is the first line.\"}}}";
"  :parse_send_args(\"edit\", \"#123:foo\", {\"Hi!\"}, {\"type\", \"MOO-Code\"})";
"    => {{\"name\", \"#123.foo\"}, {\"text\", {\"Hi!\"}}, {\"type\", \"MOO-Code\"}}";
{msg, @rest} = args;
a = $list_utils:assoc(msg, this.messages_out);
if (!a)
raise(E_INVARG, "Invalid message");
endif
keywords = a[2];
lkeywords = length(keywords);
lrest = length(rest);
if (lrest < lkeywords)
raise(E_ARGS, "Incorrect number of message arguments");
endif
return {@$list_utils:make_alist({keywords, rest[1..lkeywords]}), @rest[lkeywords + 1..$]};
"Metadata 202106";
"Last-Modify: {895380695, \"Ken\", #75, \"JHM\"}";
.
#179:1
"Usage:  :parse_receive_args(msg, alist)";
"";
"Transform a messages arguments from an alist into a mostly-positional list.  The cord type has an ordered list of argument keywords for all valid messages; these are used to construct an ordered list of the items from the alist that correspond to those keywords.  If there are items in the alist that do not match a known keyword, they will be appended to the positional list with keywords attached.";
"";
"Examples:";
"  .messages_in = {{\"edit\", {\"name\", \"text\"}}}";
"  :parse_receive_args({{\"name\", \"#123.foo\"}, {\"text\", {\"Hi!\"}}})";
"    => {\"#123.foo\", \"Hi!\"}";
"  :parse_receive_args({{\"name\", \"#123.foo\"}, {\"text\", {\"Hi!\"}}, {\"type\", \"MOO-Code\"}})";
"    => {\"#123.foo\", \"Hi!\", {\"type\", \"MOO-Code\"}};";
{msg, alist} = args;
a = $list_utils:assoc(msg, this.messages_in);
if (!a)
"this should be caught upstream.";
raise(E_INVARG, "Invalid cord message");
endif
ret = {};
for keyword in (a[2])
i = $list_utils:iassoc(keyword, alist);
if (!i)
"this too should be caught.";
raise(E_INVARG, "Missing argument in cord message");
endif
ret = {@ret, alist[i][2]};
alist = listdelete(alist, i);
endfor
return {@ret, @alist};
"Metadata 202106";
"Last-Modify: {895249566, \"Ken\", #75, \"JHM\"}";
.
#179:2
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249566, \"Ken\", #75, \"JHM\"}";
.
#180:0
"get(site, dir, file) => {success, list_of_strings}";
"";
"site, dir, and  file are strings, success is nonzero if the command is successful, and list_of_strings is either the data received or a list containing a single string, the ftp reply code or \"000\" (if a connection can't be made).";
site = args[1];
dir = args[2];
file = args[3];
if (typeof(c = $network:open(site, this.port)) == ERR)
return {0, {"000"}};
endif
c = c[1];
l = read(c);
response = l[1..3];
if (!(response == "220"))
$network:close(c);
return {0, {response}};
endif
notify(c, "USER anonymous");
l = read(c);
response = l[1..3];
if (response in {"530", "500", "501", "421"})
$network:close(c);
return {0, {response}};
elseif (response in {"331", "332"})
notify(c, ("PASS " + "ftp@") + $network.site);
l = read(c);
response = l[1..3];
if (response in {"530", "501", "503", "530", "421", "332"})
$network:close(c);
return {0, {response}};
endif
endif
notify(c, "CWD " + dir);
l = read(c);
response = l[1..3];
if (response != "250")
$network:close(c);
return {0, {response}};
endif
notify(c, "PASV");
l = read(c);
response = l[1..3];
if (response != "227")
$network:close(c);
return {0, {response}};
endif
addrm = match(l, "(%([0-9]+%),%([0-9]+%),%([0-9]+%),%([0-9]+%),%([0-9]+%),%([0-9]+%))");
addr = substitute("%1.%2.%3.%4", addrm);
phigh = substitute("%5", addrm);
plow = substitute("%6", addrm);
p = (256 * tonum(phigh)) + tonum(plow);
if (typeof(d = $network:open(addr, p)) == ERR)
return {0, {"000"}};
else
d = d[1];
endif
notify(c, "RETR " + file);
l = read(c);
response = l[1..3];
if (!(response in {"125", "150", "226", "250"}))
r = {0, {response}};
else
text = {};
while ((line = read(d)) != E_INVARG)
text = {@text, line};
endwhile
r = {1, text};
endif
$network:close(c);
return r;
.
#182:0
"Usage:  @gethelp [<db>.][<topic>]";
"";
"Prints the raw text of topic from the appropriate help db, as a script for changing this help topic (somewhat like @dump...).  If no db is named, it looks for the topic wherever it lives; if no topic is named, the blank (\"\") topic (given when someone types `help' with no arguments) is retrieved.";
if (caller_perms() != player)
caller_perms():tell("Someone called @gethelp with your permissions...");
return E_PERM;
endif
set_task_perms(player);
name = this:name_help_topic(dobjstr, player);
dblist = name[1];
dbname = name[2];
topic = name[3];
if (this:help_dbs_match_failed(dblist, dbname))
return;
endif
match = this:match_help_topic(topic, dblist);
if (this:help_topic_match_failed(match, topic, dblist))
return;
endif
if (typeof(text = (db = match[1]):dump_topic(fulltopic = match[2])) == ERR)
"...ok...shoot me.  This is a -d verb...";
player:notify(tostr("Cannot retrieve `", fulltopic, "' on ", $code_utils:corify_object(db), ":  ", text));
else
player:notify_lines(text);
endif
.
#182:1
"Usage:  @copyhelp [<db>.][<subject>] to <db>[.<subject>]";
"";
"Copy the text of a help topic from one database to another.  If the source database is not specified, copy it from wherever it's found.  If the target subject is not specified, use the subject from the source database.";
if (caller_perms() != player)
caller_perms():tell("Someone called @copyhelp with your permissions...");
player:tell(E_PERM);
return E_PERM;
endif
set_task_perms(player);
"SOURCE";
name = this:name_help_topic(dobjstr, player);
dblist = name[1];
dbname = name[2];
topic = name[3];
if (this:help_dbs_match_failed(dblist, dbname))
return;
endif
from = this:match_help_topic(topic, dblist);
if (this:help_topic_match_failed(from, topic, dblist))
return;
elseif (typeof(text = (db = from[1]):topic_text(fulltopic = from[2])) == ERR)
"...ok...shoot me.  This is a -d verb...";
player:notify(tostr("Cannot retrieve `", fulltopic, "' on ", $code_utils:corify_object(db), ":  ", text));
endif
"DESTINATION";
name = this:name_help_db_or_topic(iobjstr, player);
dblist = name[1];
dbname = name[2];
if (name[3] == $nothing)
totopic = fulltopic;
else
totopic = name[3];
endif
if (!dbname)
to = db;
elseif (this:help_dbs_match_failed(dblist, dbname, 1))
return;
else
to = dblist[1];
endif
if (fulltopic != topic)
player:notify(tostr("(Found `", topic, "' listed as `", fulltopic, "'.)"));
endif
has = to:has_topic(totopic);
if (((name[3] == $nothing) && has) && (!$command_utils:yes_or_no(tostr(to:dnamec(), " already has an entry for `", topic, "'", @(topic == totopic) ? {} | {" (listed as `", totopic, "')"}, ".  Overwrite it?"))))
player:tell("Okay, help text unchanged.");
return;
elseif (!has)
if (!(e = to:add_topic(totopic, text)))
player:tell(e);
else
player:tell("Help topic `", totopic, "' added to ", to:dname(), ".");
endif
return;
endif
if (typeof(e = to:set_topic_text(totopic, text)) == ERR)
player:tell(e);
else
player:tell("Help on `", totopic, "' changed on ", to:dname(), ".");
endif
.
#182:2
"Usage:  @sethelp[-<value|text>] [<db>.][<topic>]";
"";
"Reads lines of input and installs them as <topic> on the given help db.  If no db is named, it looks for the topic wherever it lives; if no topic is named, the blank (\"\") topic (given when someone types `help' with no arguments) is retrieved.";
"";
"The name of the verb determines how to interpret the input; default is text.";
if (caller_perms() != player)
caller_perms():tell("Someone called @sethelp with your permissions...");
return E_PERM;
"let them eat spam";
endif
set_task_perms(player);
if (dash = index(verb, "-"))
type = verb[dash + 1..length(verb)];
else
type = "text";
endif
if (!type)
player:tell_lines($code_utils:verb_usage());
return E_INVARG;
endif
name = this:name_help_topic(dobjstr, player);
dblist = name[1];
dbname = name[2];
topic = name[3];
problem = 0;
if (this:help_dbs_match_failed(dblist, dbname))
problem = 1;
endif
match = this:match_help_topic(topic, dblist);
if (this:help_topic_match_failed(match, topic, dblist))
problem = 1;
endif
if (problem)
player:notify("Now ignoring input...");
$command_utils:read_lines();
player:tell("Text ignored.");
else
player:notify("Enter lines of text.");
text = $command_utils:read_lines();
if (type == "value")
value_list = $note_editor:to_value(@text);
if (value_list[1])
player:tell("Error on line ", value_list[1], ":  ", value_list[2]);
player:tell("Text not saved.");
return;
endif
text = value_list[2];
elseif ((type == "string") && (length(text) <= 1))
text = text ? text[1] | "";
endif
db = match[1];
fulltopic = match[2];
if (typeof(e = db:set_topic_text(fulltopic, text)) == ERR)
player:notify(tostr("Cannot set `", fulltopic, "' on ", db:dname(), ":  ", e));
else
player:notify("Set.");
endif
endif
.
#182:3
"Usage:  @helpedit [<db>.][<topic>]";
"";
"Edit a help text.";
if (caller_perms() != player)
caller_perms():tell("Someone called @helpedit with your permissions...");
player:tell(E_PERM);
return E_PERM;
endif
set_task_perms(player);
$help_editor:invoke(dobjstr, verb);
.
#182:4
"Usage:  :match_help_topic(STR <topic>, LIST <dbs>)";
"";
"Try to find the given help topic in the given list of databases.";
"";
"Return {db, topic} or {$ambiguous_match,{topic...}} or {}, just like $code_utils:help_db_search.";
"";
"Actually, this is exactly the same as :help_db_search, but I like having it here instead.";
search = $code_utils:help_db_search(@args);
if (search[1] == $failed_help)
topic = search[2];
if (topic[1..7] == "failed ")
return {};
elseif (topic[1..10] == "ambiguous ")
topics = $string_utils:explode(topic);
return {$ambiguous_match, topics[3..length(topics)]};
endif
endif
return search;
.
#182:5
"Usage:  :name_help_topic(STR [<db>.]<topic>, OBJ <who>)";
"";
"Try to parse the string into a help text reference.  This doesn't match for an actual topic; it just finds a list of possible databases (one or more that match the <db> spec, or all dbs known to <who> if none is given) and returns that and the topic name.";
"";
"Return value is {LIST dbs, STR dbname, STR topic}.";
"";
"Help dbs are found by literal object matching, then searching through <who>'s db list and this.known_dbs.";
topic = args[1];
dot = index(topic, ".");
dbname = topic[1..dot - 1];
topic[1..dot] = "";
dblist = $set_utils:union($code_utils:help_db_list(args[2]), $help.known_dbs);
if (dbname)
dblist = this:match_help_dbs(dbname, dblist);
endif
return {dblist, dbname, topic};
"Metadata 202106";
"Last-Modify: {868907025, \"Erik\", #74, \"JHM\"}";
.
#182:6
"Usage:  :verify_db(object)";
"";
"Makes a futile attempt to verify that the given object can in fact serve as a help database, by checking if it either (a) is a descendant of $generic_help or (b) implements the verbs required and named in `help $generic_help'.";
if ($object_utils:isa(object = args[1], $generic_help))
return 1;
endif
for v in ({"find_topics", "get_topic", "topic_text", "set_topic_text", "dump_topic"})
if (!$object_utils:has_verb(object, v))
return 0;
endif
endfor
return 1;
.
#182:7
"Usage:  :match_help_dbs(string, db list)";
"";
"Tries to match the given string as an object or against a list of named databases.  Returns a list of matches.";
"";
"If a reasonably exact match is found, it alone will be returned; otherwise, all partial matches will be.";
"This verb doesn't try to make sure that its return value is, in fact, a help database; just a valid object (or $nothing, or $ambiguous_match, or $failed_match).  However, this:help_db_match_failed will determine if the object at least makes an attempt to act like a help database.";
name = args[1];
if (!name)
return {};
elseif (valid(object = $string_utils:literal_object(name)))
if (this:verify_db(object))
return {object};
else
if (name[1] == "~")
for db in (children(this.generic_personal_db))
if (db.owner == object)
return {db};
endif
endfor
endif
return {};
endif
else
partials = {};
exacts = {name, name + " help", name + " help database"};
for object in (args[2])
for alias in (object.aliases)
if (alias in exacts)
return {object};
elseif (index(alias, name) == 1)
partials = setadd(partials, object);
endif
endfor
endfor
return partials;
endif
.
#182:8
"Usage:  help_dbs_match_failed(LIST dbs, STR name[, BOOL exact])";
"";
"Prints a message if there's no db, or if `exact' is true and there's more than one db.  Generally used after object is derived from a :match_help_dbs(name, dblist).";
set_task_perms(caller_perms());
match_result = args[1];
string = args[2];
tell = $perm_utils:controls(caller_perms(), player) ? "notify" | "tell";
if (!match_result)
if (valid(o = $string_utils:literal_object(string)))
if (string[1] == "~")
player:(tell)(tostr(o:dnamec(), " doesn't have a help database."));
else
player:(tell)(tostr(o:dnamec(), " is not a help database."));
endif
else
player:(tell)(tostr("There is no help database named \"", string, "\"."));
endif
elseif (args[3] && (length(match_result) != 1))
if (args[2])
player:(tell)(tostr("The name \"", string, "\" could refer to ", $string_utils:name_list(match_result, "", " or "), "."));
else
player:(tell)("You need to specify a help database.");
endif
else
return 0;
endif
return 1;
.
#182:9
"Usage:  :help_topic_match_failed(LIST result, STR topic, LIST <dbs>)";
"";
"Report failure.  Used after :match_help_topic.";
set_task_perms(caller_perms());
tell = $perm_utils:controls(caller_perms(), player) ? "notify" | "tell";
result = args[1];
topic = args[2];
if (!result)
player:(tell)(tostr("Couldn't find help on \"", topic, "\"."));
elseif (result[1] == $ambiguous_match)
player:(tell)(tostr("The name \"", topic, "\" could refer to help on ", $string_utils:english_list(result[2], "", " or "), "."));
else
return 0;
endif
return 1;
.
#182:10
"Usage:  :name_help_db_or_topic(STR <db>[.<topic>], OBJ <who>)";
"";
"Try to parse the string into a help db or text reference.  This doesn't match for an actual topic; just tries to figure out where it would be if it existed.";
"";
"Return value is {LIST dbs, STR dbname, $nothing || STR topic}.";
"";
"Help dbs are found by literal object matching, then searching through <who>'s db list and this.known_dbs.";
dbname = args[1];
dot = index(dbname, ".");
if (dot)
topic = dbname[dot + 1..length(dbname)];
dbname = dbname[1..dot - 1];
else
topic = "";
endif
dblist = $set_utils:union($code_utils:help_db_list(args[2]), $help.known_dbs);
if (dbname)
dblist = this:match_help_dbs(dbname, dblist);
endif
return {dblist, dbname, dot ? topic | $nothing};
"Metadata 202106";
"Last-Modify: {868907025, \"Erik\", #74, \"JHM\"}";
.
#182:11
"Usage:  @addhelp <db>.[<topic>]";
"";
"Adds a new topic to the help database.  If no topic is supplied, add the blank (\"\") topic (given when someone types `help' with no arguments).";
if (caller_perms() != player)
caller_perms():tell("Someone called @addhelp with your permissions...");
return E_PERM;
endif
set_task_perms(player);
name = this:name_help_topic(dobjstr, player);
dblist = name[1];
dbname = name[2];
topic = name[3];
if (this:help_dbs_match_failed(dblist, dbname, 1))
return E_INVARG;
endif
db = dblist[1];
match = this:match_help_topic(topic, dblist);
if (match)
partials = {};
if (match[1] == $ambiguous_match)
partials = match[2];
elseif (match[2] != topic)
partials = {match[2]};
endif
if (partials)
one = length(partials) == 1;
player:tell(db:dnamec(), " already has ", one ? "a" | "the following", " topic", one ? "" | "s", " that could match `", topic, "':");
for x in ($string_utils:columnize($help:sort_topics(partials), 3, 60))
player:notify(tostr("   ", x));
endfor
if (!$command_utils:yes_or_no("Add new topic anyway?"))
return;
endif
else
player:tell(db:dnamec(), " already has a `", topic, "' topic.");
return;
endif
endif
if (!(e = db:add_topic(topic, {})))
player:tell(e);
else
player:tell("Empty help topic `", topic, "' added to ", db:dname(), ".");
endif
.
#182:12
"Usage:  @rmhelp [<db>.][<topic>]";
"";
"Deletes a topic from the help database.  If no topic is supplied, remove the blank (\"\") topic (given when someone types `help' with no arguments).  If no database is supplied, remove the first matching topic found.";
"";
"After matching db and topic, @rmhelp queries the user before taking any action.";
if (caller_perms() != player)
caller_perms():tell("Someone called @gethelp with your permissions...");
return E_PERM;
endif
set_task_perms(player);
name = this:name_help_topic(dobjstr, player);
dblist = name[1];
dbname = name[2];
topic = name[3];
if (this:help_dbs_match_failed(dblist, dbname))
return;
endif
match = this:match_help_topic(topic, dblist);
if (this:help_topic_match_failed(match, topic, dblist))
return;
endif
db = match[1];
fulltopic = match[2];
if ($command_utils:yes_or_no(tostr("Remove topic `", fulltopic, "' from ", db:dname(), "?")))
if (!(e = db:delete_topic(fulltopic)))
player:tell(e);
else
player:tell("Deleted.");
endif
else
player:tell("OK.");
endif
.
#182:13
"Usage:  @linkhelp [<db>.][<source>] to [<db>.][<target>]";
"";
"Link one help topic to another, so people typing `help <target>' will see the text of `help <source>'.";
"";
"If the source database is not specified, copy it from wherever it's found.  If the target subject is not specified, use the blank topic; if the target database is not specified, use the same database as the source.";
if (caller_perms() != player)
caller_perms():tell("Someone called @copyhelp with your permissions...");
player:tell(E_PERM);
return E_PERM;
endif
set_task_perms(player);
"SOURCE";
name = this:name_help_topic(dobjstr, player);
dblist = name[1];
dbname = name[2];
topic = name[3];
if (this:help_dbs_match_failed(dblist, dbname))
return;
endif
from = this:match_help_topic(topic, dblist);
if (this:help_topic_match_failed(from, topic, dblist))
return;
endif
db = from[1];
fulltopic = from[2];
"DESTINATION";
name = this:name_help_topic(iobjstr, player);
dblist = name[1];
dbname = name[2];
totopic = name[3];
if (dbname)
if (this:help_dbs_match_failed(dblist, dbname, 1))
return;
endif
to = dblist[1];
else
to = db;
endif
if (fulltopic != topic)
player:notify(tostr("(Found `", topic, "' listed as `", fulltopic, "'.)"));
endif
has = to:has_topic(totopic);
if (has)
if (!$command_utils:yes_or_no(tostr(to:dnamec(), " already has an entry for `", totopic, "'.  Overwrite it?")))
player:tell("Okay, help text unchanged.");
return;
else
if (typeof(e = to:set_topic_text(totopic, {"*forward*", fulltopic})) == ERR)
player:tell(e);
else
player:tell("Help on `", totopic, "' changed on ", to:dname(), ".");
endif
endif
else
if (!(e = to:add_topic(totopic, {"*forward*", fulltopic})))
player:tell(e);
else
player:tell("Help topic `", totopic, "' added to ", to:dname(), ".");
endif
return;
endif
.
#182:14
"Usage:  @findhelp <topic>";
"";
"Looks around on known help databases for strings matching the given topic.";
dblist = $set_utils:union($code_utils:help_db_list(player), $help.known_dbs);
exacts = {};
partials = {};
partialcnt = 0;
for db in (dblist)
if (length(ts = db:find_topics(dobjstr)) == 1)
if ((ts == {dobjstr}) || ((typeof(ts[1]) == LIST) && (ts[1][1] == dobjstr)))
exacts = {@exacts, db};
endif
elseif (ts && (typeof(ts) == LIST))
dbname = $code_utils:corify_object(db);
topics = {};
for t in (ts)
topics = {@topics, (typeof(t) == STR) ? t | t[1]};
partialcnt = partialcnt + 1;
endfor
partials = {@partials, {dbname, topics}};
endif
endfor
if (exacts)
player:tell($string_utils:dnamec_list(exacts), (length(exacts) == 1) ? " has" | " have", " the topic `", dobjstr, "'.");
if (partials)
player:tell("In addition, you might also want to look at the following similar topic", (partialcnt == 1) ? ":" | "s:");
endif
elseif (partials)
player:tell("No help datbases contain exactly that topic.  However, you might want to look at the following similar topic", (partialcnt == 1) ? ":" | "s:");
else
player:tell("No help databases contain that topic, or anything like it.");
endif
colwidth = (abs(player.linelen) || 79) - 8;
for x in (partials)
player:notify("");
player:notify(tostr("In db `", x[1], ".' :"));
for y in ($string_utils:columnize(x[2], 3, colwidth))
player:notify(tostr("    ", y));
endfor
endfor
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {893174010, \"Ken\", #75, \"JHM\"}";
.
#182:15
"Usage:  @showhelp <db>.[<topic>]";
"";
"Finds the <db> in your help db list, and tries to show the right topic from there, as someone would get it with `help'.  It's complicated.";
if (caller_perms() != player)
caller_perms():tell("Someone called @gethelp with your permissions...");
return E_PERM;
endif
set_task_perms(player);
name = this:name_help_topic(dobjstr, player);
dblist = name[1];
dbname = name[2];
topic = name[3];
if (this:help_dbs_match_failed(dblist, dbname, 1))
return;
endif
db = dblist[1];
dblist = $code_utils:help_db_list();
if (start = db in dblist)
dblist[1..start - 1] = {};
else
player:tell(db:dnamec(), " isn't in your help database list; *pass* and *forward* may be broken.");
dblist = {db, @dblist};
endif
result = $code_utils:help_db_search(topic, dblist);
if (player.focus_object != player)
if (valid(player.focus_object))
player:notify("[Changing your focus object to yourself.]");
endif
player:set_focus_object(player);
endif
help = result[1];
topic = result[2];
if (typeof(topic) == STR)
jaddress = $jaddress.resolved_help:from_db_and_topic_and_dblist(help, topic, dblist);
else
jaddress = topic[2];
endif
player:receive_document(jaddress);
return;
match = this:match_help_topic(topic, dblist);
if (this:help_topic_match_failed(match, topic, dblist))
return;
endif
if (typeof(text = (db = match[1]):dump_topic(fulltopic = match[2])) == ERR)
"...ok...shoot me.  This is a -d verb...";
player:notify(tostr("Cannot retrieve `", fulltopic, "' on ", $code_utils:corify_object(db), ":  ", text));
else
player:notify_lines(text);
endif
.
#182:16
return {@pass(@args), $code_utils, $command_utils, $note_editor, $help_editor, $failed_help, $string_utils, $set_utils, $generic_help, $object_utils, this.generic_personal_db, $perm_utils, $help, $jaddress, $jaddress.resolved_help};
.
#183:0
return {$community_group};
.
#183:1
"Usage:  :ok_gag_announce(who, where)";
"";
"Is WHO allowed to hack @gag-announce parameters on WHERE?";
who = args[1];
where = args[2];
return $perm_utils:controls(who, where) || $community_group:is_member(who);
.
#183:2
who = args[1];
return this:in_any_group(who, this:(strsub(verb, "ok_", "groups_"))());
.
#183:3
return {$db_group, $unix_group, $community_group};
.
#183:4
return this:in_any_group(args[1], this:groups_shout());
.
#183:5
"called with (player, group(s)) to check if player is in any of the groups.";
"checks if groups is a list or single element.";
who = args[1];
groups = args[2];
if (typeof(groups) == LIST)
for g in (groups)
if (g:is_member(who))
return 1;
endif
endfor
return 0;
else
return groups:is_member(who);
endif
.
#183:6
"called with (group(s)) to return a list of members of all group(s).";
"checks if groups is a list or single element.";
groups = args[1];
if (typeof(groups) == LIST)
m = {};
for g in (groups)
m = $list_utils:append(m, g:members());
endfor
else
m = groups:members();
endif
return $list_utils:remove_duplicates(m);
.
#183:7
"to return a string of the form `members of the x, y, and z'";
groups = args[1];
if (typeof(groups) == LIST)
msg = tostr(strsub(verb, "_term", ""), " of ", $string_utils:dname_list(groups));
else
msg = groups:verb();
endif
return msg;
.
#184:0
"WIZARDLY";
if ((!(caller in {$note_editor, $help_editor})) || (caller_perms() != $help_editor.owner))
return E_PERM;
endif
set_task_perms(player);
return args[1][1]:set_topic_text(args[1][2], args[2]);
.
#184:1
string = args[1];
name = $help.author_feature:name_help_topic(dobjstr, player);
dblist = name[1];
dbname = name[2];
topic = name[3];
if ($help.author_feature:help_dbs_match_failed(dblist, dbname))
return 1;
endif
match = $help.author_feature:match_help_topic(topic, dblist);
if ($help.author_feature:help_topic_match_failed(match, topic, dblist))
return 1;
endif
return match;
"Metadata 202106";
"Last-Modify: {868907332, \"Erik\", #74, \"JHM\"}";
.
#184:2
"WIZARDLY";
if ((!(caller in {$note_editor, $help_editor})) || (caller_perms() != $help_editor.owner))
return E_PERM;
endif
set_task_perms(player);
text = args[1][1]:topic_text(args[1][2]);
if (typeof(text) in {LIST, ERR})
return text;
else
return E_TYPE;
endif
if (typeof(text) == LIST)
if (text && (typeof(text[1]) != STR))
return E_TYPE;
else
return text;
endif
elseif (typeof(text) == ERR)
return text;
else
return E_TYPE;
endif
.
#184:3
info = pass(@args);
info[4][1..9] = "@sethelp";
info[2] = ((("help " + args[1][2]) + " (from ") + $code_utils:corify_object(args[1][1])) + ")";
return info;
what = args[1];
db = what[1];
dbname = $code_utils:corify_object(what[1]);
topic = what[2];
name = (dbname + ".") + what[2];
text = args[2];
dump = db:dump_topic(topic);
return {"text", "help " + name, listdelete(dump, 1), dump[1]};
if ($list_utils:check_type(text, STR))
return {"text", "help " + name, text, dump[1]};
else
return {"text", "help " + name, dump, ";"};
endif
.
#185:0
"send_to(target, address[, source])";
target = args[1];
address = args[2];
subject = $jaddress:extract("subject", address)[2];
db = $jaddress:extract("db", address)[2];
dblist_string = $jaddress:extract("dblist", address)[2];
dblist = $code_utils:safe_eval(dblist_string)[2];
help = toobj(db);
topic = subject;
if (1 == (text = help:get_topic(topic, dblist)))
"...get_topic took matters into its own hands...";
elseif (!text)
target:receive_vbox({$jtext.vgroup, {$jtext.title, topic ? tostr("Help on ", topic) | "Help summary"}, {$jtext.header, 1, (topic || "summary") + " -- NOT FOUND"}, {$jtext.paragraph, tostr(help:dnamec(), " thinks it knows about `", topic, "' but something's messed up.")}, {$jtext.paragraph, tostr("Please tell ", (help.owner.wizard || (help.owner == $hacker)) ? "" | tostr(help.owner:dname(), " or "), "a documenter.")}});
elseif (typeof(text) == STR)
target:receive_vbox({$jtext.vgroup, {$jtext.title, topic ? tostr("Help on ", topic) | "Help summary"}, {$jtext.header, 1, topic || "summary"}, {$jtext.paragraph, text}});
elseif (typeof(text) == LIST)
if (text[1][1] == $jtext.title)
target:receive_vbox({$jtext.vgroup, @text});
elseif (text[1][1..2] == {$jtext.header, 1})
target:receive_vbox({$jtext.vgroup, {$jtext.title, topic ? tostr("Help on ", topic) | "Help summary"}, @text});
else
target:receive_vbox({$jtext.vgroup, {$jtext.title, topic ? tostr("Help on ", topic) | "Help summary"}, {$jtext.header, 1, topic || "summary"}, @text});
endif
endif
return;
"Copied from Resolved Help Address (#2514):send_to by Erik (#74) Sun Nov 13 14:05:50 1994 EST";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {913134421, \"core initialization\", #3023, \"core\"}";
.
#185:1
"Copied from Help Address (#1345):make_link by Jay (#73) Fri Apr  8 21:18:52 1994 EDT";
"Usage:  :make_link(topic)";
return this:make_formatted_link(args[1], tostr("`help ", args[1], "'"));
.
#185:2
"Copied from Help Address (#1345):make_formatted_link by Jay (#73) Fri Apr  8 21:18:57 1994 EDT";
"Usage:  :make_formatted_link(topic, format)";
return {$jtext.link.root, {this, {{"subject", args[1]}}}, args[2]};
.
#185:3
db = tostr(args[1]);
topic = args[2];
return {$jaddress.resolved_help, {{"db", db}, {"subject", topic}}};
.
#185:4
db = tostr(args[1]);
topic = args[2];
dblist = args[3];
dblist_string = $string_utils:print(dblist);
return {this, {{"db", db}, {"subject", topic}, {"dblist", dblist_string}}};
.
#186:0
":find_topics(subject)  => {topic [, topic]}";
"";
"Return a list of all topics in this db appropriate to subject.  The list can be empty.";
if (!args)
return {};
endif
what = args[1];
if (!what)
return {};
elseif (what[1] == "$")
"core reference";
o = $no_one:eval_d(what);
if (!o[1])
return {};
else
o = o[2];
endif
else
o = $code_utils:toobj(what);
endif
if ((typeof(o) != OBJ) || (!valid(o)))
"No per-object help on invalid objects, sorry...";
return {};
elseif (o:help_text())
"... gah, that was EXPENSIVE, help_msg sux rox";
"Is there a good way to see if an object has a help_msg?  Should help messages be restricted to static texts, not operated on by $pronoun_sub?  See #1:help_msg.";
return {tostr(o)};
endif
return {};
.
#186:1
what = args[1];
o = toobj(what);
text = o:help_text();
if (((typeof(text) == LIST) && text) && (typeof(text[1]) == STR))
header = tostr("Help on ", o:dname());
jtext = this:to_jtext(text, o:dname());
return {{$jtext.title, header}, {$jtext.header, 1, header}, @jtext};
elseif (typeof(text) == STR)
header = tostr("Help on ", o:dname());
return {{$jtext.title, header}, {$jtext.header, 1, header}, {$jtext.paragraph, text || "No help found."}};
else
return text;
endif
"elseif (typeof(text) == STR)";
"BROKEN";
return {};
.
#186:2
"Copied from feature object help database (#692):index by Erik (#37) Tue Apr 26 20:14:54 1994 EDT";
"{\"*index*\" [, title]}";
"This produces a columnated list of topics in this help db, headed by title.";
$command_utils:suspend_if_needed(0);
return {};
title = args[1] ? args[1][1] | tostr(this:name(), " (", this, ")");
su = $string_utils;
return {{$jtext.header, 3, title}, {$jtext.paragraph, "This database can't be indexed."}};
return {{$jtext.header, 3, title}, {$jtext.plaintext, @this:columnize(@this:sort_topics(this:find_topics())), ""}};
return {"", title, su:from_list($list_utils:map_arg(su, "space", su:explode(title), "-"), " "), @this:columnize(@this:sort_topics(this:find_topics()))};
.
#186:3
return {@pass(@args), $code_utils, $pronoun_sub, $jtext, $jtext.title, $jtext.header, $jtext.paragraph, $command_utils, $string_utils, $jtext.plaintext, $list_utils};
.
#188:0
if (!caller_perms().wizard)
return E_PERM;
else
args[1].size_quota = this.default_quota;
args[1].ownership_quota = this.large_negative_number;
endif
.
#188:1
return 0;
.
#188:2
"This figures out who is gonna own the stuff @create does.  If one arg, return caller_perms().  If two args, then if caller_perms().wizard, args[2].";
if ((length(args) == 1) || (!valid(args[2])))
return caller_perms();
elseif ((length(args) == 2) && $perm_utils:controls(caller_perms(), args[2]))
return args[2];
else
return E_INVARG;
endif
.
#188:3
"Here's the tricky one.  Collect all the user's characters' cached usage data and total quotas.  Compare same.  If usage bigger than quotas, return 0.  Else, add up the total number of objects that haven't been measured recently.  If greater than the allowed, return 0.  Else, reluctantly, return 1.";
who = args[1];
if (who.wizard || (who == $hacker))
"... sorry folks --Rog";
return 1;
endif
if (is_clear_property(who, "size_quota"))
return 0;
endif
"$recycler:check_quota_scam(who);  ---one would have to import this system from lambdamoo";
allwho = this:all_characters(who);
quota = 0;
usage = 0;
unmeasured = 0;
for x in (allwho)
quota = quota + x.size_quota[1];
usage = usage + x.size_quota[2];
unmeasured = unmeasured + x.size_quota[4];
endfor
if (usage >= quota)
return 0;
elseif (unmeasured >= this.max_unmeasured)
return E_MAXREC;
return 0;
else
return 1;
endif
.
#188:4
who = args[1];
if (this:can_peek(caller_perms(), who) && (length(all = this:all_characters(who)) > 1))
many = 1;
else
many = 0;
all = {who};
endif
if (many)
tquota = 0;
tusage = 0;
ttime = $maxint;
tunmeasured = 0;
tunmeasurable = 0;
endif
for x in (all)
quota = x.size_quota[1];
usage = x.size_quota[2];
timestamp = x.size_quota[3];
unmeasured = x.size_quota[4];
unmeasurable = 0;
if (unmeasured >= 100)
unmeasurable = unmeasured / 100;
unmeasured = unmeasured % 100;
endif
if (many)
player:tell(x.name, " quota: ", $string_utils:group_number(quota), "; usage: ", $string_utils:group_number(usage), "; unmeasured: ", unmeasured, "; no .object_size: ", unmeasurable, ".");
tquota = tquota + quota;
tusage = tusage + usage;
ttime = min(ttime, timestamp);
tunmeasured = tunmeasured + unmeasured;
tunmeasurable = tunmeasurable + unmeasurable;
endif
endfor
if (many)
this:display_quota_summary(who, tquota, tusage, ttime, tunmeasured, tunmeasurable);
else
this:display_quota_summary(who, quota, usage, timestamp, unmeasured, unmeasurable);
endif
.
#188:5
return args[1].size_quota[1];
.
#188:6
"Charge args[1] for the quota required to own args[2]";
if ((caller == this) || caller_perms().wizard)
usage_index = 2;
unmeasured_index = 4;
who = args[1];
what = args[2];
object_size = $object_utils:has_property(what, "object_size") ? what.object_size[1] | -1;
if (object_size <= 0)
who.size_quota[unmeasured_index] = who.size_quota[unmeasured_index] + 1;
else
who.size_quota[usage_index] = who.size_quota[usage_index] + object_size;
endif
else
return E_PERM;
endif
.
#188:7
"reimburse args[1] for the quota required to own args[2]";
"If it is a $garbage, then if who = $hacker, then we mostly ignore everything.  Who cares what $hacker's quota looks like.";
if ((caller == this) || caller_perms().wizard)
usage_index = 2;
unmeasured_index = 4;
who = args[1];
what = args[2];
if (parent(what) == $garbage)
return 0;
elseif (((valid(who) && is_player(who)) && $object_utils:has_property(what, "object_size")) && (!is_clear_property(who, "size_quota")))
object_size = what.object_size[1];
if (object_size <= 0)
who.size_quota[unmeasured_index] = who.size_quota[unmeasured_index] - 1;
else
who.size_quota[usage_index] = who.size_quota[usage_index] - object_size;
endif
endif
else
return E_PERM;
endif
.
#188:8
"Set args[1]'s quota to args[2]";
if ((caller_perms().wizard || (caller == this)) || this:can_touch(caller_perms()))
args[1].ownership_quota = this.large_negative_number;
"Size_quota[1] is the total quota permitted.";
return args[1].size_quota[1] = args[2];
else
return E_PERM;
endif
.
#188:9
"Return args[1]'s quotas.  second arg of 1 means add all second chars.";
who = args[1];
all = (length(args) > 1) && args[2];
if (all && ((caller == this) || this:can_peek(caller_perms(), who)))
all = this:all_characters(who);
else
all = {who};
endif
baseline = {0, 0, 0, 0};
for x in (all)
baseline[1] = baseline[1] + x.size_quota[1];
baseline[2] = baseline[2] + x.size_quota[2];
baseline[3] = min(baseline[3], x.size_quota[3]) || x.size_quota[3];
baseline[4] = baseline[4] + x.size_quota[4];
endfor
return baseline;
.
#188:10
who = args[1];
quota = args[2];
usage = args[3];
timestamp = args[4];
unmeasured = args[5];
unmeasurable = args[6];
player:tell($string_utils:pronoun_sub("The %N %<has> a total building quota of ", who), $string_utils:group_number(quota), " bytes.");
player:tell($string_utils:pronoun_sub("%P total usage was ", who), $string_utils:group_number(usage), " as of ", ctime(timestamp), ".");
if (usage > quota)
player:tell($string_utils:pronoun_sub("The %N %<is> over quota by ", who), $string_utils:group_number(usage - quota), " bytes.");
else
player:tell($string_utils:pronoun_sub("The %N may create up to ", who), $string_utils:group_number(quota - usage), " more bytes of objects, properties, or verbs.");
endif
if (unmeasured)
plural = unmeasured != 1;
player:tell("There ", plural ? tostr("are ", unmeasured, " objects") | "is 1 object", " which ", plural ? "are" | "is", " not yet included in the tally; this tally may thus be inaccurate.");
if (unmeasured >= this.max_unmeasured)
player:tell("The number of unmeasured objects is too large; no objects may be created until @measure new is used.");
endif
endif
if (unmeasurable)
plural = unmeasurable != 1;
player:tell("There ", plural ? tostr("are ", unmeasurable, " objects") | "is 1 object", " which do", plural ? "" | "es", " not have a .object_size property and will thus prevent additional building.", (who == player) ? "  Contact a wizard for assistance in having this situation repaired." | "");
endif
return;
"Metadata 202106";
"Last-Modify: {832100158, \"Nosredna\", #72, \"JHM\"}";
.
#188:11
"This wants to only be called by a wizard cuz I'm lazy.  This is just for @second-char anyway.";
if (caller_perms().wizard)
q = this:get_size_quota(args[1], 1);
return q[1] - q[2];
endif
.
#188:12
"This does the reimbursement work of the recycler, since we ignore $garbage in ordinary reimbursement.";
if (caller_perms().wizard)
this:reimburse_quota(@args);
else
return E_PERM;
endif
.
#188:13
who = args[1];
results = this:summarize_one_user(who);
total = results[1];
nuncounted = results[2];
nzeros = results[3];
oldest = results[4];
eldest = results[5];
player:tell(who.name, " statistics:");
player:tell("  ", $string_utils:group_number(total), " bytes of storage measured.");
if (valid(eldest))
player:tell("  Oldest measurement date ", ctime(oldest), " (", $string_utils:from_seconds(time() - oldest), " ago) of object ", eldest, " (", eldest.name, ")");
endif
if (nzeros || nuncounted)
player:tell("  Number of objects with no statistics recorded:  ");
player:tell("      ", nzeros, " recently created, ", nuncounted, " not descendents of #1");
endif
.
#188:14
return args[1].wizard;
.
#188:15
object = args[1];
return ((13 * 4) + length(object.name)) + 1;
.
#188:16
"Summarizes total space usage by one user (args[1]).  Optional second argument is a flag to say whether to re-measure all objects for this user; specify the number of seconds out of date you are willing to accept.  If negative, will only re-measure objects which have no recorded data.";
"Returns a list of four values:";
"  total : total measured space in bytes";
"  uncounted : Number of objects that were not counted because they aren't descendents of #1";
"  zeros : Number of objects which have been created too recently to have any measurement data at all (presumably none if re-measuring)";
"  most-out-of-date : the time() the oldest actual measurement was taken";
"  object-thereof: the object who had this time()'d measurement";
who = args[1];
if (length(args) == 2)
if (args[2] < 0)
earliest = 1;
else
earliest = time() - args[2];
endif
else
earliest = 0;
endif
nzeros = 0;
oldest = time();
eldest = #-1;
nuncounted = 0;
ncounted = 1;
total = 0;
for x in ((typeof(who.owned_objects) == LIST) ? who.owned_objects | {})
if (x.owner == who)
"Bulletproofing against recycling during suspends!";
"Leaves us open to unsummarized creation during this period, which is unfortunate.";
if ($object_utils:has_property(x, "object_size"))
size = x.object_size[1];
time = x.object_size[2];
if (time < earliest)
"Re-measure.  This side-effects x.object_size.";
this:object_bytes(x);
size = x.object_size[1];
time = x.object_size[2];
endif
if (time && (time < oldest))
oldest = time;
eldest = x;
elseif (!time)
nzeros = nzeros + 1;
endif
total = total + size;
ncounted = ncounted + 1;
else
nuncounted = nuncounted + 1;
endif
endif
$command_utils:suspend_if_needed(0);
endfor
if (!is_clear_property(who, "size_quota"))
"Cache the data, but only if they aren't scamming.";
who.size_quota[2] = total;
who.size_quota[3] = oldest;
who.size_quota[4] = (nuncounted * this.unmeasured_multiplier) + nzeros;
endif
return {total, nuncounted, nzeros, oldest, eldest};
.
#188:17
if (!$__core_init_phase)
return E_PERM;
else
this.exempted = {};
this.working = $first_wizard;
this.task_time_limit = 500;
this.report_recipients = {#2};
endif
.
#188:18
if ((caller != this) && (!caller_perms().wizard))
return E_PERM;
else
args[1].ownership_quota = 1;
endif
.
#188:19
if ((caller != this) && (!caller_perms().wizard))
return E_PERM;
else
args[1].ownership_quota = this.large_negative_number;
endif
.
#188:20
set_task_perms(caller_perms());
v = args[1];
return value_bytes(v);
t = typeof(v);
if (t == LIST)
b = ((length(v) + 1) * 2) * 4;
for vv in (v)
$command_utils:suspend_if_needed(2);
b = b + this:value_bytes(vv);
endfor
return b;
elseif (t == STR)
return length(v) + 1;
else
return 0;
endif
.
#188:21
"Algorithm:";
"  Base object takes up 13 words plus length of name.  (builtin props?)";
"  Each verb takes up 5 words overhead, plus length of its name, plus size of its code.";
"  Each property definition takes up 1 word plus length of property name, plus each property on the object takes up 4 words.  (Or, 5 per defined prop, 4 per inherited prop)";
"Note: each word is four bytes.";
foo = "delimit comments above from commented out code below";
"set_task_perms(caller_perms())";
o = args[1];
b = object_bytes(o);
if ($object_utils:has_property(o, "object_size"))
oldsize = is_clear_property(o, "object_size") ? 0 | o.object_size[1];
if ($object_utils:has_property(o.owner, "size_quota"))
if (oldsize)
o.owner.size_quota[2] = o.owner.size_quota[2] + (b - oldsize);
else
o.owner.size_quota[2] = o.owner.size_quota[2] + b;
if (o.owner.size_quota[4] > 0)
o.owner.size_quota[4] = o.owner.size_quota[4] - 1;
endif
endif
endif
o.object_size = {b, time()};
endif
return b;
if (0)
o = args[1];
b = this:object_overhead_bytes(o);
vs = verbs(o);
b = b + this:verb_overhead_bytes(o);
for vn in [1..length(vs)]
$command_utils:suspend_if_needed(5);
info = verb_info(o, vn);
b = (b + length(info[3])) + 1;
b = b + this:value_bytes(verb_code(o, vn));
endfor
ps = $object_utils:all_properties_suspended(o);
b = b + this:property_overhead_bytes(o, ps);
for p in (ps)
if (!is_clear_property(o, p))
$command_utils:suspend_if_needed(5);
b = b + this:value_bytes(o.(p));
endif
endfor
if ($object_utils:has_property(o, "object_size"))
oldsize = is_clear_property(o, "object_size") ? 0 | o.object_size[1];
if ($object_utils:has_property(o.owner, "size_quota"))
"Update quota cache.";
if (oldsize)
o.owner.size_quota[2] = o.owner.size_quota[2] + (b - oldsize);
else
o.owner.size_quota[2] = o.owner.size_quota[2] + b;
if (o.owner.size_quota[4] > 0)
o.owner.size_quota[4] = o.owner.size_quota[4] - 1;
endif
endif
endif
o.object_size = {b, time()};
endif
return b;
endif
.
#188:22
dobj = args[1];
who = valid(caller_perms()) ? caller_perms() | player;
if (!this:can_peek(who, dobj.owner))
return E_PERM;
endif
props = $object_utils:all_properties(dobj);
grand_total = obj_over = this:object_overhead_bytes(dobj);
output = {tostr("Object overhead:  ", obj_over)};
if (props)
total = 0;
lines = {};
output = {@output, "Properties, defined and inherited, sorted by size:"};
for x in (props)
if (!is_clear_property(dobj, x))
size = this:value_bytes(dobj.(x));
total = total + size;
if (size)
lines = {@lines, {x, size}};
endif
endif
endfor
lines = $list_utils:reverse($list_utils:sort_suspended(0, lines, $list_utils:slice(lines, 2)));
for x in (lines)
text = tostr("  ", x[1], ":  ", x[2]);
output = {@output, text};
endfor
output = {@output, tostr("Total size of properties:  ", total)};
grand_total = grand_total + total;
endif
prop_over = this:property_overhead_bytes(dobj, props);
output = {@output, tostr("Property overhead:  ", prop_over)};
grand_total = grand_total + prop_over;
if (verbs(dobj))
output = {@output, "Verbs, sorted by size:"};
total = 0;
lines = {};
for x in [1..length(verbs(dobj))]
vname = verb_info(dobj, x)[3];
size = this:value_bytes(verb_code(dobj, x));
total = total + size;
lines = {@lines, {vname, size}};
endfor
lines = $list_utils:reverse($list_utils:sort_suspended(0, lines, $list_utils:slice(lines, 2)));
for x in (lines)
text = tostr("  ", x[1], ":  ", x[2]);
output = {@output, text};
endfor
output = {@output, tostr("Total size of verbs:  ", total)};
grand_total = grand_total + total;
verb_over = this:verb_overhead_bytes(dobj);
output = {@output, tostr("Verb overhead:  ", verb_over)};
grand_total = grand_total + verb_over;
endif
output = {@output, tostr("Grand total:  ", grand_total)};
return output;
.
#188:23
o = args[1];
vs = verbs(o);
return (length(vs) * 5) * 4;
.
#188:24
o = args[1];
if (length(args) > 1)
ps = args[2];
else
ps = $object_utils:all_properties_suspended(o);
endif
return (this:value_bytes(properties(o)) - 4) + ((length(ps) * 4) * 4);
.
#188:25
set_task_perms(caller_perms());
who = this:parse_create_args(@args);
if (this:creation_permitted(who))
this:enable_create(who);
value = create(@args);
this:disable_create(who);
if (typeof(value) != ERR)
this:charge_quota(who, value);
this:own_object(who, value);
endif
return value;
else
return E_QUOTA;
endif
.
#188:26
":recent_object_bytes(x, n) -- return object size of x, guaranteed to be no more than n days old.  N defaults to this.cycle_days.";
object = args[1];
if (length(args) > 1)
since = args[2];
else
since = this.cycle_days;
endif
if (!valid(object))
return 0;
elseif (object.object_size[2] > (time() - (((since * 24) * 60) * 60)))
return object.object_size[1];
else
return this:object_bytes(object);
endif
.
#188:27
if (!caller_perms().wizard)
return E_PERM;
elseif ((task_id() != this.measurement_task) && $code_utils:task_valid(this.measurement_task))
"This is already running: this could either kill it and start a new";
"task OR do what it does do here, abort starting the new task.";
return 0;
else
start_time = time();
day = (60 * 60) * 24;
when = day - (time() % day);
fork newtaskid ((((8 * 60) * 60) + day) - (time() % day))
this:measurement_task();
endfork
this.measurement_task = task_id();
num_processed = 0;
usage_index = 2;
time_index = 3;
unmeasured_index = 4;
players = setremove(players(), $hacker);
lengthp = length(players);
index = this.working in players;
if (!index)
"Uh, oh, our guy got reaped while we weren't looking.  Better look for someone else.";
index = 1;
while ((this.working > players[index]) && (index < lengthp))
$command_utils:suspend_if_needed(0);
index = index + 1;
endwhile
this.working = players[index];
endif
stop = time() + this.task_time_limit;
early = time() - (day * this.cycle_days);
keep_going = 1;
while ((time() < stop) && keep_going)
who = players[index];
if (is_player(who) && $object_utils:has_property(who, "size_quota"))
"Robustness in the face of reaping...";
usage = 0;
unmeasured = 0;
earliest = time();
for o in (who.owned_objects)
if ((valid(o) && (o.owner == who)) && (!(o in this.exempted)))
"sanity check: might have recycled while we suspended!";
if ($object_utils:has_property(o, "object_size"))
if ((o.object_size[2] < early) || is_clear_property(o, "object_size"))
usage = usage + this:object_bytes(o);
else
usage = usage + o.object_size[1];
earliest = min(earliest, o.object_size[2]);
endif
else
unmeasured = unmeasured + 1;
endif
endif
$command_utils:suspend_if_needed(3);
endfor
if ((!is_clear_property(who, "size_quota")) || (!this.fascist))
who.size_quota[usage_index] = usage;
who.size_quota[unmeasured_index] = this.unmeasured_multiplier * unmeasured;
who.size_quota[time_index] = earliest;
elseif (this.fascist)
$mail_agent:send_message(this, this.report_recipients, "Quota Violation", {tostr(who, " has a clear .size_quota property."), $string_utils:names_of({who, @$object_utils:ancestors(who)})});
endif
elseif (is_player(who) && this.fascist)
"They don't have a size_quota property.  Whine.";
$mail_agent:send_message(this, this.report_recipients, "Quota Violation", {tostr(who, " doesn't seem to have a .size_quota property."), $string_utils:names_of({who, @$object_utils:ancestors(who)})});
endif
if (index >= lengthp)
index = 1;
else
index = index + 1;
endif
num_processed = num_processed + 1;
if (num_processed > lengthp)
keep_going = 0;
endif
this.working = players[index];
endwhile
$mail_agent:send_message(this, this.report_recipients, "quota-utils report", {tostr("About to measure objects of player ", this.working.name, " (", this.working, "), ", $string_utils:ordinal(this.working in players), " out of ", lengthp, ".  We processed ", num_processed, " players in this run, taking a total of ", $time_utils:english_time(time() - start_time), ".")});
"Remember the new forked task for tomorrow";
this.measurement_task = newtaskid;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {861390965, \"Nosredna\", #72, \"JHM\"}";
.
#188:28
":add_owned_object(who, what) -- adds what to whose .owned_objects.";
who = args[1];
what = args[2];
if ((typeof(who.owned_objects) == LIST) && (what.owner == who))
who.owned_objects = setadd(who.owned_objects, what);
endif
.
#189:0
"Usage:  :schedule_checkpoint()";
"";
if (!(caller in {this, #0, $startup}))
raise(E_PERM);
endif
next = this:next_checkpoint();
$dump_interval = next;
this:log(("Next checkpoint scheduled for " + ctime(time() + next)) + ".");
return;
"Metadata 202106";
"Last-Modify: {833066991, \"Ken\", #75, \"JHM\"}";
.
#189:1
"Usage:  :next_checkpoint([now])";
"";
if (!this.checkpoint_times)
"no change";
return $dump_interval;
endif
now = args ? ctime(args[1]) | ctime()[12..19];
{hour, minute, second} = $list_utils:map_builtin($string_utils:explode(now, ":"), "tonum");
ssm = time() % 86400;
times = {};
for t in (this.checkpoint_times)
times = {@times, t * 3600};
endfor
for i in (times)
if (i > ssm)
return i - ssm;
endif
endfor
return (86400 - ssm) + times[1];
"Metadata 202106";
"Last-Modify: {865120053, \"Ken\", #75, \"JHM\"}";
.
#189:2
"Usage:  :description()";
"";
times = {};
for a in (this.checkpoint_times)
times = {@times, $string_utils:lowercase(strsub($time_utils:ampm(a * 3600), ".", ""))};
endfor
scheduleinfo = tostr("Checkpoints are scheduled for ", $string_utils:english_list(times, "every " + $time_utils:english_time($dump_interval)), ".  The next checkpoint is scheduled for ", this:ftime(time() + this:next_checkpoint()), ".");
failure = this.last_failure;
success = this.last_success;
dbsize = ((("The current on-disk db size is " + $string_utils:group_number(s = db_disk_size())) + " bytes (") + floatstr(tofloat(s) / 1048576.0, 1)) + "M)";
if (success > failure)
success_m = ("The last successful checkpoint was " + this:ftime(success)) + ".  ";
rate_m = ((("  The checkpoint took " + $time_utils:english_time(this.last_success_time)) + " and the checkpoint rate was ") + floatstr(tofloat(s) / (tofloat(this.last_success_time) * 1024.0), 2)) + " K/sec.";
if (failure)
failure_m = ("  The last failed checkpoint was " + this:ftime(failure)) + ".";
else
failure_m = "";
endif
lastinfo = ((success_m + dbsize) + rate_m) + failure_m;
else
lastinfo = tostr("The last checkpoint FAILED ", this:ftime(failure), ".  The most recent successful checkpoint was ", this:ftime(success), ".");
endif
if (this.in_progress)
inprogress = {("A checkpoint that started " + this:ftime(this.in_progress)) + " is now in progress."};
else
inprogress = {};
endif
return {@pass(@args), scheduleinfo, @inprogress, lastinfo};
"Metadata 202106";
"Last-Modify: {865120807, \"Ken\", #75, \"JHM\"}";
.
#189:3
"Usage:  :checkpoint_started()";
"";
if (caller == #0)
this:log("Checkpoint started");
this.in_progress = time();
this:schedule_checkpoint();
endif
.
#189:4
"Usage:  :checkpoint_finished(success)";
"";
success = args[1];
if (caller != #0)
return;
endif
fork (0)
if (!success)
"aaaaa our checkpoint has FAILED";
"complain.  loudly.";
for dude in ($unix_group:members())
player = dude;
dude:send_self_netmail("checkpoint failed", {("The checkpoint of " + ctime()) + " has failed.   This should probably be fixed ASAP."});
endfor
endif
endfork
"schedule next checkpoint";
this.("last_" + (success ? "success" | "failure")) = time();
if (success)
this.last_success_time = time() - this.in_progress;
endif
this.in_progress = 0;
this:log("Checkpoint " + (success ? "completed successfully" | "failed"));
.
#189:5
"Usage:  :ftime(then)";
"";
then = args[1];
if (then == 0)
return "never";
endif
now = time();
diff = abs(now - then);
if (diff > 31536000)
fmt = "on $n $t $Y at $O:$M$p";
elseif (diff > 604800)
fmt = "on $n $t at $O:$M$p";
elseif (diff > 86400)
fmt = "on $D at $O:$M$p";
else
fmt = "at $O:$M$p";
endif
ftime = $time_utils:time_sub(fmt, then);
if (now > then)
ftime = ((ftime + " (") + $string_utils:from_seconds(now - then)) + " ago)";
else
ftime = ((ftime + " (in ") + $string_utils:from_seconds(then - now)) + ")";
endif
return ftime;
.
#189:6
"Usage:  :init_for_core()";
"";
if ($__core_init_phase)
this.last_success = 0;
this.last_failure = 0;
this.checkpoint_times = {};
endif
.
#189:7
"Usage:  :log()";
"";
if (caller == this)
$syslog:log(0, "checkpoint", tostr(@args));
endif
.
#189:8
if (caller == $startup)
if (this.in_progress)
this.in_progress = 0;
endif
endif
.
#190:0
return strsub(pass(@args), this.name, "J. Text");
.
#191:0
flag = args[1];
stuff = args[2];
chkstrs = length(flag) == 1;
for foo in (stuff)
if (typeof(foo) == LIST)
if (foo[1] == flag)
return 1;
endif
elseif (chkstrs && index(foo, flag))
return 1;
endif
endfor
return 0;
.
#191:1
flag = args[1];
stuff = args[2];
checkstrs = length(flag) == 1;
newstuff = {};
for foo in (stuff)
if (typeof(foo) == LIST)
if (foo[1] != flag)
newstuff = {@newstuff, foo};
endif
elseif (checkstrs)
while (gah = index(foo, flag))
foo = foo[1..gah - 1] + foo[gah + 1..length(foo)];
endwhile
if (foo)
newstuff = {@newstuff, foo};
endif
else
newstuff = {@newstuff, foo};
endif
endfor
return newstuff;
.
#191:2
cap = 0;
type = "o";
for foo in (args)
if (typeof(foo) == STR)
for i in [1..length(foo)]
if (foo[i] == "p")
type = "p";
elseif (foo[i] == "c")
cap = 1;
endif
endfor
elseif (foo[1] == "p")
type = "p";
elseif (foo[1] == "c")
cap = 1;
endif
endfor
return cap ? type | (type + "c");
.
#191:3
success = 1;
nargs = args[1];
result = "";
while (success && nargs)
foo = nargs[1];
nargs = listdelete(nargs, 1);
if (typeof(foo) == STR)
result = result + foo;
elseif ((length(foo) == 1) && (length(foo[1]) == 1))
result = result + foo[1];
else
success = 0;
endif
endwhile
return success && result;
.
#191:4
"Usage: $name_utils:fake_proper_name(string, @flags);";
"";
"Returns what <object>:name(@<flags>) would return if <object> were a child of $root named <string> with .proper set.";
return this.dummy:name({"spoof", args[1]}, @listdelete(args, 1));
.
#191:5
result = pass(@args);
return {@result, this.dummy};
.
#191:6
"Usage: sep_name_flags_final(flags, nargs) => {found, remaining}";
"separates any sequence of occurrences of the specified name flags at the end of <nargs>";
{flags, nargs} = args;
slf = "";
for flag in (flags)
if (length(flag) == 1)
slf = slf + flag;
endif
endfor
found = {};
while (nargs)
{@nargs, item} = nargs;
if (typeof(item) == LIST)
if (item[1] in flags)
found = {item, @found};
else
return {found, {@nargs, item}};
endif
elseif (slf)
lilfound = "";
while (item)
if (index(slf, item[$]))
lilfound = item[$] + lilfound;
item[$..$] = "";
elseif (lilfound)
return {{lilfound, @found}, {@nargs, item}};
else
return {found, {@nargs, item}};
endif
endwhile
found = {lilfound, @found};
else
return {found, {@nargs, item}};
endif
endwhile
return {found, nargs};
.
#192:0
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
.
#192:1
"Usage: write <topic> on sign";
"  Sets the default topic for a room.";
if (!dobjstr)
return player:tell("Usage: ", verb, " <topic> ", prepstr, " ", iobjstr);
elseif (player.location != this.location)
return player:tell("You can't reach that.");
elseif (!this.stack)
return player:tell("There is no paper to write on.");
else
this:set_topic(dobjstr);
$you:say_action($pronoun_sub.("two-letter"):parse(("%nD %n:(writes) `" + dobjstr) + "' on %td."));
endif
.
#192:2
"Usage: cover sign with <topic>";
"  Sets the current topic for a room.";
if (!iobjstr)
return player:tell("Usage: ", verb, dobjstr, " ", prepstr, " <topic>");
elseif (player.location != this.location)
return player:tell("You can't reach that.");
else
old = this:topic_msg();
this:push(iobjstr);
$you:say_action($pronoun_sub.("two-letter"):parse(((("%nN %n:(covers) %td reading `" + old) + "' with a slip of paper reading `") + this:topic_msg()) + "'."));
endif
.
#192:3
"description + sign";
pass(@args);
if (msg = this:topic_msg())
player:tell(this:dnamec(), " says, `", msg, "'");
else
player:tell(this:dnamec(), " is blank.");
endif
.
#192:4
"uncover sign";
"Remove the top sheet";
if (player.location != this.location)
return player:Tell("You can't reach that.");
endif
if (this.stack)
this:pop();
argstr = this:topic_msg();
$you:say_action(this.uncover_msg);
else
player:tell("There is no top sheet to pull off.");
endif
.
#192:5
"when the last person leaves, unset the 'current' topic message (revert to default)";
if (!valid(this.location))
$recycler:_recycle(this);
elseif (!$set_utils:intersection(this.location:contents(), $code_utils:connected_players()))
while (this.stack)
this:pop();
endwhile
endif
return pass(@args);
.
#192:6
"the sign can only be moved if it is nowhere and it is being moved to a $room";
if ((!valid(this.location)) && $object_utils:isa(args[1], $room))
move(this, @args);
endif
.
#192:7
"make a topic sign.  Only a $room can call this...";
if (!$object_utils:isa(caller, $room))
return E_PERM;
elseif ($object_utils:isa(caller.topic_sign, $topic))
"it already has a topic sign";
return E_PERM;
else
if (typeof(sign = $recycler:_create($topic)) == OBJ)
caller:set_topic_sign(sign);
sign:moveto(caller);
endif
return sign;
endif
.
#192:8
"eliminate a topic sign.  I can't think of a use for this, so I won't code it yet";
.
#192:9
"return current topic";
return ((stack = this.stack) && stack[length(stack)]) || this.default_topic;
.
#192:10
this:set_name($topic.name);
this:set_aliases($topic.aliases);
return pass(@args);
.
#192:11
if ($perm_utils:controls(caller_perms(), this))
this.location:set_topic_sign();
return pass(@args);
endif
.
#192:12
"erase this topic";
"I.e. remove the sign";
if (player.location != this.location)
return player:tell("You can't reach that.");
endif
if (this.default_topic && (!$perm_utils:controls(player, this.location)))
player:tell("The sign seems to be nailed down.  You just hurt your foot.  OW");
return;
endif
$you:say_action(this.remove_msg);
$recycler:_recycle(this);
.
#192:13
"return the integration message .. I want to make this integrate last, but";
if (msg = this:topic_msg())
return ("A sign here says, `Topic: " + msg) + "'";
else
return "";
endif
.
#192:14
"set the default message (carve it on wooden sign)";
if (this.stack)
return player:Tell("Remove the papers covering the wood first.");
elseif (!$perm_utils:controls(player, this.location))
return player:Tell("You can't do that.");
else
this:set_default_topic(dobjstr);
$you:say_action(this.carve_msg);
endif
.
#192:15
if (this.stack)
this.stack = listdelete(this.stack, length(this.stack));
this:set_private_from_topic();
if ((!this.stack) && (!this.default_topic))
fork (30)
if ($object_utils:isa(this, $topic) && ((!this.stack) && (!this.default_topic)))
this:room_announce_all("The groundskeeper comes by and picks up the blank topic sign.");
$recycler:_recycle(this);
endif
endfork
endif
endif
.
#192:16
if (caller in {this, this.location})
this.stack = {@this.stack, args[1]};
endif
this:set_private_from_topic();
.
#192:17
if (caller in {this, this.location})
(stack = this.stack) ? this.stack[length(stack)] = args[1] | (this.stack = {args[1]});
endif
.
#192:18
"Usage: shake sign";
"Shakes loose all of the papers from the sign.";
if (this.stack)
$you:say_action("%N %<shakes> the topic sign.");
while (this.stack)
this:pop();
endwhile
else
player:tell("There is no paper on the sign.");
endif
.
#192:19
return {@pass(@args), this.owner};
.
#192:20
topic = args[1];
$you:say_action(("The %n %<summons> the groundskeeper, who drops a topic sign off.  %S %<writes> `" + $string_utils:pronoun_quote(topic)) + "' on a slip of paper and %<puts> it on the sign.");
.
#192:21
player:tell(this:integrate_room_msg());
.
#192:22
topic = args[1];
if (topic[1..7] == "private")
return 1;
else
return 0;
endif
.
#192:23
if (this:matches_private(this:topic_msg()))
this.private = 1;
else
this.private = 0;
endif
.
#192:24
"hidden/obvious_verbs(who)";
"Returns a list of verbs on this that should be hidden/not hidden from examine.";
"The player who's examining is passed in, so objects can hide verbs from specific players.";
"Verbs are returned as {location, full_verb_name, args} or just full_verb_name.  full_verb name is what shows up in verb_info(object, verb)[3], for example \"dr*op th*row\".";
obvious = verb != "hidden_verbs";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
raise(E_PERM);
endif
vbs = {};
what = this;
while (what != $thing)
for i in [1..length(verbs(what))]
if ((!what:is_hidden_verb(i, @args)) == obvious)
info = verb_info(what, i);
vbs = setadd(vbs, {what, info[3], verb_args(what, i)});
endif
endfor
what = parent(what);
endwhile
return vbs;
"Copied from root class (#1):help_verbs by Erik (#74) Fri Jun 28 12:03:22 1996 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {835984201, \"Erik\", #74, \"JHM\"}";
.
#194:0
return args[1];
.
#194:1
return player.location:look_self();
.
#195:0
set_task_perms(caller_perms());
if (typeof(n = this:new()) == OBJ)
n:setup("spam-player sink for " + player:name(), caller, caller_perms());
n:set_target(player);
endif
return n;
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#195:1
if (caller != this.module)
raise(E_PERM);
endif
set_task_perms(this.creator);
for line in (this:get_lines())
while (!notify(this.target, line, 1))
suspend(2);
endwhile
if (ticks_left() < 50)
suspend(0);
endif
endfor
return;
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#195:2
if (caller != this.module)
raise(E_PERM);
endif
set_task_perms(this.creator);
notify(this.target, "# WARNING " + tostr(@args));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#195:3
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#197:0
"Usage:  :find_path(from, dest, who)";
"";
{from, dest, who, ?oktosuspend = 1} = args;
if (from == dest:where_am_i())
return {};
elseif (data = this:dijkstra(from, dest, who, oktosuspend))
{shortpath, shortcost, crooms, cdata, here} = data;
path = this:decode_space(cdata, here);
return path;
else
return 0;
endif
return;
"Metadata 202106";
"Last-Modify: {833222317, \"Ken\", #75, \"JHM\"}";
.
#197:1
who = args[1];
dest = args[2];
blocking = verb == "walkto_blocking";
if (path = who:find_path(who.location, dest))
if (blocking)
who:move_by_exits(path, dest);
else
fork (0)
who:move_by_exits(path, dest);
endfork
endif
endif
return typeof(path) == LIST;
.
#197:2
"We're at our destination, turn off brief so the player sees the desc.";
{who, path, dest, ?speed = 1} = args;
if (typeof(path) != LIST)
raise(E_INVARG);
endif
opath = path;
who:begin_walking(path, dest);
while walking (path)
step = path[1];
path = listdelete(path, 1);
path = who:walking_step(step, path, dest);
if (path && (suspend(speed) != 0))
break walking;
endif
endwhile
who:end_walking(opath, dest);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {864488335, \"Ken\", #75, \"JHM\"}";
.
#197:3
"dikstra(from, to, who)";
"Returns a bunch of information";
"{shortpath, shortcost, crooms, cdata, start point for decide}";
{from, destination, who, ?oktosuspend = 0} = args;
destination = destination:where_am_i();
queue = {{from, 0, 0, 1}};
crooms = {from};
cdata = {{0, $nothing, 0}};
"TO INFINITY ... AND BEYOND";
infinity = $maxint;
havepath = 0;
shortcost = infinity;
shortpath = infinity;
while searching (queue)
{room, source, cost, roomidx} = queue[1];
queue = listdelete(queue, 1);
for exit in (`room.exits ! E_PROPNF => {}')
if (!exit:obvious(who, destination))
continue exit;
endif
dest = exit.dest;
if ((dest == room) || (source && (dest == crooms[source])))
"no backtracking";
continue exit;
endif
"are we THERE yet?";
"Relax";
current_cost = cost + exit:walking_cost(who, from, destination, cdata[roomidx][2]);
if ((!(toroom = dest in crooms)) && (current_cost < shortcost))
"current cost is infinity, so we have a better path";
crooms = {@crooms, dest};
cdata = {@cdata, {roomidx, exit, current_cost}};
entry = {dest, roomidx, current_cost, length(crooms)};
if ((!queue) || (current_cost < queue[1][3]))
queue = {entry, @queue};
else
for i in [2..l = length(queue)]
if (current_cost < queue[i][3])
queue = listinsert(queue, entry, i);
break i;
endif
endfor
if (l == length(queue))
queue = {@queue, entry};
endif
endif
if ((dest == destination) && (!havepath))
havepath = current_cost;
shortcost = current_cost;
shortpath = this:decode_space(cdata, length(cdata));
endif
elseif (!toroom)
elseif (cdata[toroom][3] > current_cost)
"Replace that link, this one is better";
"First grab the old data";
{src, oldexit, oldcost} = cdata[toroom];
cdata[toroom] = {roomidx, exit, current_cost};
trimming = toroom;
endif
if ($command_utils:running_out_of_time())
if (havepath)
break searching;
else
if (oktosuspend)
suspend(0);
else
"give up!";
break searching;
endif
endif
endif
endfor
endwhile
if (havepath)
here = destination in crooms;
"{shortpath, shortcost, crooms, cdata, start point for decide}";
return {shortpath, shortcost, crooms, cdata, here};
else
return 0;
endif
.
#197:4
"Usage:  :_decode_path(cdata, start)";
"";
"#75:debug(cdata)";
cdata = args[1];
here = args[2];
path = {};
{src, exit, cost} = cdata[here];
while pathfinding (src)
path = {{exit.name, exit.source}, @path};
{src, exit, cost} = cdata[src];
endwhile
"#75:debug(path)";
return path;
.
#197:5
"Usage:  :_display_crooms(crooms, cdata)";
"";
crooms = args[1];
cdata = args[2];
data = {};
for i in [1..length(crooms)]
{src, exit, cost} = cdata[i];
data = {@data, {tostr(i), crooms[i]:name(), tostr(src), tostr(cost)}};
endfor
player:notify_lines($string_utils:columnize_with_headers({"N", "Room", "S", "C"}, data));
.
#197:6
"Usage:  :find_path_and_cost(from, dest, who)";
"";
from = args[1];
dest = args[2];
who = args[3];
if (from == dest)
return {{}, 0};
elseif (data = this:dijkstra(from, dest, who))
{shortpath, shortcost, crooms, cdata, here} = data;
path = this:decode_space(cdata, here);
cost = cdata[here][3];
return {path, cost};
else
return 0;
endif
.
#197:7
"bfs(from, to, who)";
"breadth-first search with the same format as dijstra";
"{shortpath, shortcost, crooms, cdata, start point for decide}";
{from, destination, who} = args;
destination = destination:where_am_i();
queue = {{from, 0, 0, 1}};
crooms = {from};
cdata = {{0, $nothing, 0}};
"TO INFINITY ... AND BEYOND";
infinity = $maxint;
havepath = 0;
shortcost = infinity;
shortpath = infinity;
while searching (queue)
{room, source, cost, roomidx} = queue[1];
queue = listdelete(queue, 1);
for exit in (room.exits)
if (!exit:obvious(who, destination))
continue exit;
endif
dest = exit.dest;
if ((dest == room) || (source && (dest == crooms[source])))
"no backtracking";
continue exit;
endif
"are we THERE yet?";
"Relax";
current_cost = cost + exit:walking_cost(who, from, destination, cdata[roomidx][2]);
if ((!(toroom = dest in crooms)) && (current_cost < shortcost))
"current cost is infinity, so we have a better path";
crooms = {@crooms, dest};
cdata = {@cdata, {roomidx, exit, current_cost}};
entry = {dest, roomidx, current_cost, length(crooms)};
queue = {@queue, entry};
if ((dest == destination) && (!havepath))
havepath = current_cost;
shortcost = current_cost;
shortpath = this:decode_space(cdata, length(cdata));
break searching;
endif
endif
if ($command_utils:running_out_of_time())
if (havepath)
break searching;
else
"suspend(0)";
"give up!";
break searching;
endif
endif
endfor
endwhile
if (havepath)
here = destination in crooms;
"{shortpath, shortcost, crooms, cdata, start point for decide}";
return {shortpath, shortcost, crooms, cdata, here};
else
return 0;
endif
.
#197:8
return {@pass(@args), $string_utils};
.
#198:0
"Usage:  :log(server_log, name, message)";
"";
server_log = args[1];
name = args[2];
message = args[3];
who = caller_perms();
what = caller;
if (this:can_write(who, what, name))
this:_log(who, what, server_log, name, message);
endif
.
#198:1
"Usage:  :can_write(perms, clr, name)";
"";
perms = args[1];
clr = args[2];
name = args[3];
entry = $list_utils:assoc(name, this.names);
if (!entry)
return 0;
endif
{name, permcheck} = entry;
if (l = $list_utils:assoc("caller", permcheck))
if (clr == l[2])
return 1;
endif
endif
return 0;
.
#198:2
"Usage:  :_log(perms, clr, server_log, name, message)";
"";
"for now, we ignore perms and caller";
perms = args[1];
clr = args[2];
server_log = args[3];
name = args[4];
message = args[5];
if (caller != this)
return;
endif
if (server_log)
server_log((("[syslog:" + name) + "] ") + message);
endif
logprop = ("_" + name) + "_log";
this.(logprop) = {@this.(logprop), {time(), message}};
if (length(this.(logprop)) > (l = this.(("_" + name) + "_size")))
this.(logprop) = this.(logprop)[$ - l..$];
endif
this:announce(name, message);
.
#198:3
if (!$list_utils:assoc(name = dobjstr, this.names))
return player:tell("There is no log named \"", name, "\".");
endif
wprop = ("_" + name) + "_watchers";
watchers = this.(wprop);
if (!(player in watchers))
if (verb != "watch")
player:tell("You are not watching ", name, ".");
else
if (this:can_read(name, player))
player:tell("You start watching ", name, ".");
watchers = setadd(watchers, player);
else
player:tell("You are not allowed to watch ", name, ".");
endif
endif
else
if (verb == "watch")
player:tell("You are already watching ", name, ".");
else
player:tell("You stop watching ", name, ".");
watchers = setremove(watchers, player);
endif
endif
this.(wprop) = watchers;
.
#198:4
"Usage:  :can_read(name, perms)";
"";
name = args[1];
perms = args[2];
if (name == "startup")
return perms.wizard;
elseif (name == "checkpoint")
return perms.wizard;
else
return 0;
endif
.
#198:5
"Usage:  :announce(name, message)";
"";
name = args[1];
message = args[2];
if (caller == this)
watchers = this.(("_" + name) + "_watchers");
msg = (((("[syslog:" + name) + "] <") + $time_utils:time_sub("$H:$M", time())) + "> ") + message;
for watcher in (watchers)
watcher:notify(msg);
endfor
endif
.
#198:6
if (!$list_utils:assoc(name = dobjstr, this.names))
return player:tell("No such log: ", name);
endif
if (this:can_read(name, player))
for line in (this.(("_" + name) + "_log"))
{t, msg} = line;
player:notify((ctime(t) + ": ") + msg);
endfor
endif
.
#198:7
"Usage:  :init_for_core()";
"";
if ($__core_init_phase)
this.names = {{"startup", {{"caller", $startup}}}, {"checkpoint", {{"caller", $checkpointer}}}};
this._checkpoint_watchers = {};
this._startup_watchers = {};
this._startup_log = {};
this._checkpoint_log = {};
endif
.
#199:0
if ($__core_init_phase)
clear_property(this, "password");
pass(@args);
else
$error:raise(E_PERM);
endif
.
#200:0
return {@pass(@args), @this.owned_objects};
.
#203:0
raise(E_VERBNF);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#203:1
raise(E_VERBNF);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#203:2
if ((caller != this.module) && (caller != this))
raise(E_PERM);
endif
{why, msg} = args;
player:tell("Module extraction abnormal termination: ", why, " ", msg);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#203:3
raise(E_VERBNF);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#203:4
if (caller != this.module)
raise(E_PERM);
endif
player:tell("WARNING " + tostr(@args));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#203:5
raise(E_VERBNF);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#204:0
"Usage:  :do_login_command()";
"";
return 0;
.
#204:1
return;
.
#204:2
"Usage:  :user_connected()";
"";
.
#204:3
if ((!(caller in {this, $startup})) || (!caller_perms().wizard))
raise(E_PERM);
elseif (a = $list_utils:assoc(this, listeners()))
return tostr("already listening on port ", a[2]);
endif
try
p = $network:listen(this, this.port);
rval = tostr("started listening on port ", p);
except (E_QUOTA)
rval = tostr("error binding to port ", this.port);
except (E_PERM)
rval = tostr("not allowed to bind to port ", this.port);
except v (ANY)
rval = tostr("error for $network:listen(", this, ", ", this.port, "): ", v[1]);
endtry
return rval;
.
#204:4
if ((!(caller in {this, $startup})) || (!caller_perms().wizard))
raise(E_PERM);
endif
unlistened = {};
if (ports = this:listening_ports())
for port in (ports)
try
$network:unlisten(port);
unlistened = {@unlistened, port};
except (ANY)
endtry
endfor
return tostr("stopped listening on port", (length(unlistened) == 1) ? " " | "s ", $string_utils:english_list(unlistened));
else
return "not listening";
endif
.
#204:5
"Usage:  :allow_connection()";
"";
return;
.
#204:6
return 0;
.
#204:7
"Usage:  :is_listening()";
"";
if (caller != this)
raise(E_PERM);
endif
ports = {};
for l in (listeners())
{what, port, welcome} = l;
if (what == this)
ports = {@ports, port};
endif
endfor
return ports;
.
#205:0
if (caller != this.module)
raise(E_PERM);
endif
this.lines = {@this.lines, tostr(@args)};
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#205:1
if (caller != this.module)
raise(E_PERM);
endif
this.lines = {@this.lines, @args[1]};
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#205:2
"Usage:  :get_lines()";
"";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.lines;
else
raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#206:0
"Usage:  :initialize()";
"";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
this:set_name(parent(this).name);
return pass(@args);
else
raise(E_PERM);
endif
.
#206:1
"Usage:  :abort  ()";
"        :timeout()";
"";
if (caller in {this.daemon, this})
boot_player(this);
endif
.
#206:2
"Usage:  :connected   ()";
"";
if (caller in {this, this.daemon})
if (this.timeout)
fork timeout_task (this.timeout)
`this:check_timeout() ! ANY';
endfork
this.timeout = timeout_task;
endif
endif
.
#206:3
"Usage:  :check_timeout()";
"";
if (caller == this)
if (typeof(`idle_seconds(this) ! ANY') == ERR)
return;
else
this:timeout();
endif
endif
.
#206:4
"Usage:  :disconnected()";
"";
if (caller in {this, this.daemon})
`kill_task(this.timeout) ! ANY';
endif
.
#206:5
if (caller != this)
set_task_perms(caller_perms());
endif
return notify(this, @args);
"hm, this shouldn't suspend";
if (ticks_left() < 4000)
suspend(0);
endif
if (args[1][1] == "#")
msg = "#" + args[1];
else
msg = args[1];
endif
while (!pass(msg, 1))
suspend(1);
endwhile
return;
"Metadata 202106";
"Last-Modify: {833175866, \"Ken\", #75, \"JHM\"}";
.
#206:6
if (caller != this)
set_task_perms(caller_perms());
endif
if ((ticks_left() < 4000) || (seconds_left() < 2))
suspend(1);
endif
while (!notify(this, args[1]))
suspend(1);
endwhile
return;
"Metadata 202106";
"Last-Modify: {833175885, \"Ken\", #75, \"JHM\"}";
.
#206:7
if (caller in {this, this.daemon})
if (index(verb, "safe"))
while ((buffered_output_length(this) != 0) && ((!this.timeout) || (connected_seconds(this) < this.timeout)))
suspend(1);
endwhile
endif
boot_player(this);
else
raise(E_PERM);
endif
return;
"Metadata 202106";
"Last-Modify: {833171777, \"Ken\", #75, \"JHM\"}";
.
#206:8
if (caller == this)
if (typeof(line = `read(this) ! ANY') == ERR)
this:abort();
kill_task(task_id());
endif
return line;
else
raise(E_PERM);
endif
return;
"Metadata 202106";
"Last-Modify: {833172094, \"Ken\", #75, \"JHM\"}";
.
#206:9
if (caller != this)
set_task_perms(caller_perms());
endif
{lines} = args;
for line in (lines)
notify(this, line);
endfor
return;
"Metadata 202106";
"Last-Modify: {833175898, \"Ken\", #75, \"JHM\"}";
.
#206:10
if (caller != this)
set_task_perms(caller_perms());
endif
{lines} = args;
for line in (lines)
if ((ticks_left() < 4000) || (seconds_left() < 2))
suspend(1);
endif
while (!notify(this, line))
suspend(1);
endwhile
endfor
return;
"Metadata 202106";
"Last-Modify: {833175908, \"Ken\", #75, \"JHM\"}";
.
#207:0
":set_message(msg_name,new_value, pronoun_sub style)";
"Does the actual dirty work of @<msg_name> object is <new_value>";
"changing the raw value of the message msg_name to be new_value.";
"Both msg_name and new_value should be strings, though their interpretation is up to the object itself.";
" => error value (use E_PROPNF if msg_name isn't recognized)";
" => string error message if something else goes wrong.";
" => 1 (true non-string) if the message is successfully set";
" => 0 (false non-error) if the message is successfully `cleared'";
if (!((caller == this) || $perm_utils:controls(caller_perms(), this)))
return E_PERM;
else
"no subbing for server messages";
return (this.(args[1] + "_msg") = args[2]) && 1;
endif
"Copied from root class (#1):set_message by Ken (#75) Thu Apr 11 19:24:41 1996 EDT";
.
#209:0
"Usage:  :schedule_checkpoint()";
"";
.
#209:1
"Usage:  :start_services()";
"";
if (caller_perms().wizard)
for service in (this.services)
try
this:log("Service ", service:name(), " started: ", service:service_start());
except v (ANY)
this:log("Service ", service:name(), " traced back...");
for line in ($code_utils:format_traceback(v[4], v[1]))
this:log("  ", line);
endfor
endtry
endfor
endif
return;
"Metadata 202106";
"Last-Modify: {831672690, \"Ken\", #75, \"JHM\"}";
.
#209:2
"Usage:  :server_start()";
"";
if (caller == #0)
$checkpointer:server_started();
$checkpointer:schedule_checkpoint();
fork (2)
this:start_services();
endfork
endif
return;
"Metadata 202106";
"Last-Modify: {831672729, \"Ken\", #75, \"JHM\"}";
.
#209:3
"Usage:  :add_service()";
"";
service = args[1];
set_task_perms(caller_perms());
this:set_services(setadd(this.services, service));
.
#209:4
"Usage:  :remove_service()";
"";
service = args[1];
set_task_perms(caller_perms());
this:set_services(setremove(this.services, service));
.
#209:5
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
.
#209:6
"Usage:  :log()";
"";
if (caller == this)
$syslog:log(1, "startup", tostr(@args));
endif
.
#209:7
"Usage:  :init_for_core()";
"";
if ($__core_init_phase)
this.services = {};
endif
.
#210:0
if (caller == this)
set_task_perms(caller_perms());
what = $recycler:_create(this);
return what;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#210:1
"Usage:  :setup(name, module, creator)";
"";
"Do basic setup on a new source object.  Name is the name to give the source; module is the module that created the source; and creator is the permissions used.";
"";
"This verb may be called only by 'this' or its parent (on the assumption that the parent is a subclass that just created it).";
if (!(caller in {this, parent(this)}))
raise(E_PERM);
endif
{name, module, creator} = args;
this:set_module(module);
this:set_creator(creator);
this:set_name(name);
"Copied from generic fetch-then-parse source (#239):setup by Erik (#2) Fri Jul 18 02:14:06 1997 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#210:2
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#211:0
"handles dumping the module system in a format suitable for spamming at a MOO";
"essentially @dump @dump @dump: warning, involves temporary properties on your character";
set_task_perms(player);
{sinkstr, @sinkargs} = $string_utils:explode(iobjstr);
sink = this:match_sink(sinkstr);
if (this:match_sink_failed(sink, sinkstr))
return;
endif
sink = sink[1];
pt = sink:parse_commandline(@sinkargs);
player:tell("Dumping ", this:dname(), " to ", pt:dname(), "...");
try
pname = "tmp_" + tostr(task_id());
pt:receive_line(("@prop me." + pname) + " {} r");
objects = {@this.objects, this};
sorting = 1;
"probably not the most efficient sort algorithm.";
while sorting (1)
for o in (objects)
p = parent(o);
if ((p in objects) > (o in objects))
objects = listappend(objects, o, p in objects);
objects = listdelete(objects, o in objects);
continue sorting;
endif
endfor
break sorting;
endwhile
for o in (objects)
this:dump_dump(pt, o, (parent(o) in objects) ? parent(o).name | "");
pt:receive_line(tostr(";me.", pname, " = {@me.", pname, ", {\"", this:obj_name(o), "\", _}}"));
endfor
module = this:dumped_ref(pname, "module");
pt:receive_line(tostr(";", module, ":first_install(player.", pname, ")"));
pt:receive_line("@rmprop me." + pname);
pt:receive_line(tostr("\"finished"));
pt:finish();
except v (ANY)
player:tell_lines($code_utils:format_traceback(v[4], v[1]));
player:tell("(Cleaning up module pack...)");
$recycler:_recycle(pt);
this:pack_abort(v[1], ": ", v[2]);
return;
endtry
$recycler:_recycle(pt);
player:tell("Done.");
"Copied from module system module (#3471):dump by Erik (#74) Wed Jul 23 17:43:51 1997 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869767634, \"Erik\", #74, \"JHM\"}";
.
#211:1
{tgt, what, ?prnt = 0} = args;
if (caller != this)
raise(E_PERM);
endif
set_task_perms(caller_perms());
parent = parent(what);
pstring = tostr(parent);
if (prnt)
pstring = "$root_class";
else
for p in (properties(#0))
if (#0.(p) == parent)
pstring = "$" + p;
endif
endfor
endif
targname = "_";
tgt:receive_line("@building-option +create-set-co");
tgt:receive_line(tostr("@create ", pstring, " named ", what:name(), ":", $string_utils:from_list(what.aliases, ",")));
if (prnt)
tgt:receive_line(tostr("@chparent _ to ", prnt));
endif
for p in (properties(what))
pquoted = $string_utils:print(p);
info = property_info(what, p);
value = what.(p);
uvalue = (typeof(value) == LIST) ? "{}" | 0;
tgt:receive_line(tostr("@prop ", targname, ".", pquoted, " ", uvalue || $string_utils:print_suspended(value), " ", info[2] || "\"\"", (info[1] == what.owner) ? "" | tostr(" ", "Core-Wizard")));
if (uvalue && value)
if (subs = match(p, "%(.+%)_msg$"))
tgt:receive_line(tostr("@", substitute("%1", subs), " ", targname, " is ", $string_utils:print(player:unparse_message(value))));
else
tgt:receive_line(tostr(";", targname, ".(", pquoted, ") = ", $string_utils:print_suspended(value)));
endif
endif
$command_utils:suspend_if_needed(0);
endfor
for a in ($object_utils:ancestors(what))
for p in (properties(a))
$command_utils:suspend_if_needed(1);
pquoted = $string_utils:print(p);
value = what.(p);
if (typeof(value) == ERR)
tgt:receive_line(tostr("\"", targname, ".(", pquoted, ") => ", $code_utils:error_name(value), " (", value, ")"));
elseif (!is_clear_property(what, p))
if (subs = match(p, "%(.+%)_msg$"))
tgt:receive_line(tostr("@", substitute("%1", subs), " ", targname, " is ", $string_utils:print(player:unparse_message(value))));
else
tgt:receive_line(tostr(";", targname, ".(", pquoted, ") = ", $string_utils:print_suspended(value)));
endif
endif
endfor
$command_utils:suspend_if_needed(1);
endfor
tgt:receive_line("");
vnum = 1;
while ((info = verb_info(what, vnum)) || (info == E_PERM))
if (index(info[3], "(old)"))
player:tell("\"Skipping ", what, ":\"", info[3], "\"...");
vnum = vnum + 1;
continue;
endif
suspend(1);
if (typeof(info) == ERR)
tgt:receive_line(tostr("\"", what, ": [", vnum, "] (", vnum - 1, ") --- ", info, "\";"));
else
if (i = index(vname = info[3], " "))
vname = vname[1..i - 1];
endif
if (vname[1] != "*")
vname = strsub(vname, "*", "");
endif
args = verb_args(what, vnum);
prep = (args[2] in {"any", "none"}) ? args[2] | $code_utils:short_prep(args[2]);
perms = (info[2] != ((args == {"this", "none", "this"}) ? "rxd" | "rd")) ? info[2] || "\"\"" | "";
if (info[1] == what.owner)
tail = perms ? tostr(" ", perms) | "";
else
tail = tostr(" ", perms || info[2], " ", info[1].wizard ? "Core-Wizard" | "hacker");
endif
tgt:receive_line(tostr("@verb ", targname, ":\"", info[3], "\" ", args[1], " ", prep, " ", args[3], tail));
if (code = verb_code(what, vnum, 1, 1))
tgt:receive_line(tostr("@program ", targname, ":", vname));
for c in (code)
tgt:receive_line(c);
$command_utils:suspend_if_needed(0);
endfor
tgt:receive_lines({".", ""});
endif
endif
vnum = vnum + 1;
$command_utils:suspend_if_needed(0);
endwhile
tgt:receive_line("\"***finished***");
"Copied from module system module (#3471):dump_dump by Erik (#74) Wed Jul 23 17:43:53 1997 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869696545, \"Erik\", #74, \"JHM\"}";
.
#211:2
{pname, what} = args;
return tostr("$list_utils:assoc(\"", (typeof(what) == STR) ? what | this:obj_name(what), "\", me.", pname, ")[2]");
"Copied from module system module (#3471):dumped_ref by Erik (#74) Wed Jul 23 17:44:11 1997 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869694251, \"Erik\", #74, \"JHM\"}";
.
#211:3
"Usage:  :first_install(data)";
"";
data = args[1];
if (!$perm_utils:controls(caller_perms(), this))
raise(E_PERM);
endif
for item in (data)
{name, object} = item;
`this.("obj_" + name) = object ! E_PROPNF';
`this.objects[name in this.object_names] = object ! E_RANGE';
endfor
module = this.obj_generic_module;
module.sinks = $object_utils:leaves(this.obj_sink_generic);
module.sources = $object_utils:leaves(this.obj_source_generic);
this:finish_unpack();
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869799766, \"Erik\", #74, \"JHM\"}";
.
#213:0
connection = caller;
for keyval in ($mcp.registry:packages())
{name, package} = keyval;
this:send_can(connection, name, @package.version_range);
endfor
this:send_end(connection);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#213:1
if (caller == this)
{connection, package, minv, maxv, @rest} = args;
if (valid(pkg = $mcp.registry:match_package(package)))
if (version = $mcp:compare_version_range({minv, maxv}, pkg.version_range))
connection:add_package(pkg, version);
endif
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#213:2
if (caller == this)
{connection, @rest} = args;
connection:end_negotiation();
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#215:0
"Usage:  :open(connection, type) => cord";
"";
"Open a cord between the calling object and something at the other end of the is_player() object given by `connection'.  Return the stub object representing the cord, or raise or return an error.";
{connection, type} = args;
if (!valid(session = $mcp:session_for(connection)))
return E_INVARG;
elseif (!session:handles_package($mcp.cord))
return E_INVARG;
elseif (!$object_utils:isa(type, this.type_root))
return E_INVARG;
endif
cord = $recycler:_create(type.cord_class);
if (typeof(cord) == ERR)
return $error:raise_or_return(cord);
endif
cord.type = type;
cord.id = tostr("I", this.next_id);
cord.our_side = caller;
"ask `connection' whether it's willing to put up with a new cord.";
if (!`session:cord_open(cord, caller) ! E_VERBNF => 1')
$recycler:_recycle(cord);
raise(E_PERM);
endif
cord.session = session;
cord.connection = connection;
res = $mcp.cord:send_open(session, cord.id, $mcp.cord:type_name(type), {"objnum", tostr(cord)});
if (typeof(res) == ERR)
$recycler:_recycle(cord);
return $error:raise_or_return(res);
endif
this.next_id = this.next_id + 1;
this.registry_ids = {tostr(cord.id), @this.registry_ids};
this.registry = {cord, @this.registry};
return cord;
"Metadata 202106";
"Last-Modify: {896808789, \"Erik\", #74, \"JHM\"}";
.
#215:1
"Usage:  :lookup(id)";
"";
{id} = args;
if (idx = tostr(id) in this.registry_ids)
return this.registry[idx];
else
return $nothing;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249566, \"Ken\", #75, \"JHM\"}";
.
#215:2
"Usage:  :mcp_receive(idstr, messagestr, assocs)";
"";
idstr = args[1];
messagestr = args[2];
assocs = args[3];
(caller == $mcp.cord) || $error:raise(E_PERM);
cord = this:lookup(idstr);
"valid(cord) && cord:receive(messagestr, assocs);";
valid(cord) && cord.our_side:("cord__" + messagestr)(cord, @cord.type:parse_receive_args(messagestr, assocs));
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249566, \"Ken\", #75, \"JHM\"}";
.
#215:3
"Usage:  :mcp_closed(idstr)";
"";
idstr = args[1];
if (caller != $mcp.cord)
raise(E_PERM);
endif
cord = this:lookup(idstr);
valid(cord) && cord:closed();
"version: 1.0 Fox Wed Jul  5 17:58:16 1995 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249566, \"Ken\", #75, \"JHM\"}";
.
#215:4
"Usage:  :closed(cord)";
"";
cord = args[1];
if ((caller != cord.our_side) && (caller != cord))
$error:raise(E_PERM);
endif
$recycler:_recycle(cord);
"version: 1.0 Fox Wed Jul  5 17:58:16 1995 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249566, \"Ken\", #75, \"JHM\"}";
.
#215:5
"Usage:  :cleanup(cord)";
"";
cord = args[1];
if ((caller != this) && (caller != cord))
$error:raise(E_PERM);
endif
if (idx = cord in this.registry)
this.registry = listdelete(this.registry, idx);
this.registry_ids = listdelete(this.registry_ids, idx);
endif
if (valid(c = cord.our_side))
c:cord_closed(cord);
endif
if (valid(c = cord.session))
`c:cord_closed(cord, cord.our_side) ! ANY';
endif
"version: 1.0 Fox Wed Jul  5 17:58:16 1995 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249566, \"Ken\", #75, \"JHM\"}";
.
#215:6
"Usage:  :connection(cord)";
"";
cord = args[1];
if (caller != cord.our_side)
return $error:raise_or_return(E_PERM);
endif
return cord.connection;
"Metadata 202106";
"Last-Modify: {895249566, \"Ken\", #75, \"JHM\"}";
.
#215:7
"Usage:  :module_finish_unpack()";
"";
if (!$perm_utils:controls(caller_perms(), this))
raise(E_PERM);
endif
try
add_property(#0, "cord", this, {this.owner, "r"});
except (E_INVARG)
`$cord = this ! ANY';
endtry
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249566, \"Ken\", #75, \"JHM\"}";
.
#215:8
"Usage:  :init_for_module()";
"";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
raise(E_PERMS);
endif
this.registry = {};
this.next_id = 1;
this.registry_ids = {};
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249566, \"Ken\", #75, \"JHM\"}";
.
#215:9
return {@pass(@args), this.root, this.type_root};
"Metadata 202106";
"Last-Modify: {895266012, \"Ken\", #75, \"JHM\"}";
.
#215:10
if ($__core_init_phase)
pass(@args);
this:init_for_module();
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895266085, \"Ken\", #75, \"JHM\"}";
.
#216:0
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
this.connection = args[1];
this:set_name("session for " + tostr(this.connection));
return 1;
else
return E_PERM;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#216:1
{request, authkey, data_tag, alist} = args;
if (caller != $mcp.parser)
raise(E_PERM);
elseif ($list_utils:assoc(data_tag, this.pending_multilines))
"it's not valid to begin two requests with the same data tag, drop it";
return;
endif
this.pending_multilines = {@this.pending_multilines, {data_tag, authkey, request, alist}};
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#216:2
{who, data_tag} = args;
if ((caller != this) && (caller != $mcp.parser))
raise(E_PERM);
elseif (!(n = $list_utils:iassoc(data_tag, this.pending_multilines)))
"drop it";
return;
else
{data_tag, authkey, request, alist} = this.pending_multilines[n];
this.pending_multilines = listdelete(this.pending_multilines, n);
return {request, authkey, alist};
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895380923, \"Ken\", #75, \"JHM\"}";
.
#216:3
{data_tag, keyword, value} = args;
if (caller != $mcp.parser)
raise(E_PERM);
elseif (!(n = $list_utils:iassoc(data_tag, this.pending_multilines)))
"drop it";
return;
elseif (!(nkey = $list_utils:iassoc(keyword, this.pending_multilines[n][4])))
"drop it";
return;
elseif (typeof(this.pending_multilines[n][4][nkey][2]) != LIST)
"not a multiline, drop it.";
return;
else
this.pending_multilines[n][4][nkey][2] = {@this.pending_multilines[n][4][nkey][2], value};
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#216:4
if (caller != $mcp)
raise(E_PERM);
else
set_task_perms(caller_perms());
if (message = $mcp.parser:parse(argstr, @args))
if (`player.MCP_snoop ! ANY')
player:tell("C->S: ", argstr);
endif
this:dispatch(@message);
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#216:5
if (caller == $mcp)
this:_signal_package_waiter(E_INVARG);
for package in ($list_utils:slice(this.packages))
fork (0)
package:finalize_connection();
endfork
endfor
return $mcp:finalize_connection(this);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#216:6
if (caller != $mcp)
raise(E_PERM);
else
this:send("mcp", {{"version", "2.1"}, {"to", "2.1"}});
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#216:7
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#216:8
{package, version} = args;
if (caller in {$mcp.negotiate, this})
if (n = $list_utils:iassoc(package, this.packages))
packages = this.packages;
packages[n][2] = version;
this:set_packages(packages);
else
this:set_packages({@this.packages, {package, version}});
endif
package:initialize_connection(version);
this:_signal_package_waiter(package, version);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#216:9
{package} = args;
if (assoc = $list_utils:assoc(package, this.packages))
return assoc[2];
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#216:10
{messages} = args;
package = caller;
{plist, mlist} = this.message_handlers;
prefix = this:package_name(package);
for message in (messages)
message = this:message_fullname(prefix, message);
if (idx = message in mlist)
if (plist[idx] != package)
raise(E_INVARG);
endif
else
plist = {@plist, package};
mlist = {@mlist, message};
endif
endfor
this.message_handlers = {plist, mlist};
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895262622, \"Ken\", #75, \"JHM\"}";
.
#216:11
{message, authkey, alist} = args;
if (caller == this)
if ((!this.phase) && (message == "mcp"))
authkey = $list_utils:assoc("authentication-key", alist);
minv = $list_utils:assoc("version", alist);
maxv = $list_utils:Assoc("to", alist);
if (((authkey && minv) && maxv) && $mcp:compare_version_range({minv[2], maxv[2]}, {$mcp.version, $mcp.version}))
this:set_authentication_key(authkey[2]);
this:add_package($mcp.negotiate, $mcp.negotiate.version_range[1]);
else
"woop woop break somehow";
return;
endif
this:set_phase(1);
$mcp.negotiate:do_negotiation();
elseif ((!this.phase) && (message in {"authentication-key", "authentication_key"}))
`player:set_authentication_key(authkey) ! ANY';
elseif (this.phase)
if ((this.authentication_key != E_NONE) && (authkey != this.authentication_key))
return;
endif
package = this:find_handler(message);
if (typeof(package) == OBJ)
set_task_perms(caller_perms());
package:dispatch(this:strip_prefix(this:package_name(package), message), alist);
endif
"figure out which package to dispatch to";
"do dispatch";
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {900725861, \"Ken\", #75, \"JHM\"}";
.
#216:12
{message, alist} = args;
who = caller_perms();
if (caller == this)
prefix = "";
elseif ($list_utils:assoc(caller, this.packages))
package = caller;
message = this:message_fullname(this:package_name(package), message);
else
raise(E_PERM);
endif
con = this.connection;
snoop = `this.connection.MCP_snoop ! E_PROPNF => 0';
for line in ($mcp.parser:unparse(message, this.authentication_key, alist))
notify(con, line);
if (snoop)
notify(con, "S->C: " + line);
endif
endfor
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#216:13
{message} = args;
if (assoc = $list_utils:passoc(message, this.message_handlers[2], this.message_handlers[1]))
return assoc[2];
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895262649, \"Ken\", #75, \"JHM\"}";
.
#216:14
return this.connection;
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#216:15
{package} = args;
return $mcp.registry:package_name(package);
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#216:16
{prefix, message} = args;
if (message)
message = (prefix + "-") + message;
else
message = prefix;
endif
return message;
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#216:17
{prefix, message} = args;
if (index(message, prefix + "-") == 1)
return message[length(prefix) + 2..$];
elseif (index(message, prefix) == 1)
return message[length(prefix) + 1..$];
elseif (message == prefix)
return "";
else
return message;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#216:18
if (caller == $mcp.negotiate)
this:_signal_package_waiter(0);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#216:19
{package, timeout} = args;
if (caller == this)
this.package_waiters = {@this.package_waiters, {package, task_id()}};
if (timeout < 0)
r = suspend();
else
r = suspend(timeout);
endif
this.package_waiters = setremove(this.package_waiters, {package, task_id()});
return r;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#216:20
{?package = $nothing, value} = args;
if (caller == this)
all = package == $nothing;
for keyval in (this.package_waiters)
{pkg, tid} = keyval;
if (all || (pkg == package))
`resume(tid, value) ! ANY';
endif
endfor
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#216:21
{package, ?timeout} = args;
timeout = `timeout ! E_VARNF => -1';
if (v = this:handles_package(package))
return v;
else
return this:_add_package_waiter(package, timeout);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#217:0
"take args and return a list in the format:";
"{true if contains multiline, { { keyword-name, data, multiline }, ... }";
alist = {};
if (length(alist) % 2)
raise(E_ARGS);
endif
contains_multiline = 0;
while (args)
{keyword, value, @args} = args;
if (keyword[$] != ":")
raise(E_INVARG, "invalid keyword: " + keyword);
else
if (keyword[$ - 1] == "*")
contains_multiline = 1;
value = {};
keyword = keyword[1..$ - 2];
else
keyword = keyword[1..$ - 1];
endif
alist = {@alist, {keyword, value}};
endif
endwhile
return {contains_multiline, alist};
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#217:1
"parse_mcp(@args) =>";
"relies on argstr being a version of @args unwordified";
"{request-name, contains-multiline, authentication-key, data-tag, { { keyword-name, data }, ... } }";
if (length(args) < 1)
raise(E_INVARG, "not enough arguments");
endif
request_name = args[1][4..$];
if (!request_name)
raise(E_INVARG, "no request name");
endif
if (request_name == "*")
return this:parse_mcp_continuation(@args[2..$]);
endif
"... if there is an authentication key, the length of args will be even ...";
if (length(args) % 2)
authentication_key = E_NONE;
message_args = args[2..$];
else
authentication_key = args[2];
message_args = args[3..$];
endif
{contains_multiline, alist} = this:parse_mcp_alist(@message_args);
if (contains_multiline)
if (tag = $list_utils:iassoc("_data-tag", alist))
"mulitline with a datatag, OK";
data_tag = alist[tag][2];
alist = listdelete(alist, tag);
else
raise(E_INVARG, "multiline fields with no data tag");
endif
else
data_tag = E_NONE;
endif
if (typeof(alist) == LIST)
return {request_name, contains_multiline, authentication_key, data_tag, alist};
else
return alist;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#217:2
{data_tag, keyword, @rest} = args;
value = argstr[(index(argstr, keyword) + length(keyword)) + 1..$];
keyword = keyword[1..$ - 1];
return {"*", data_tag, keyword, value};
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#217:3
"parse(@args) => parsed MCP message ready for dispatch or 0";
"                if there was nothing to dispatch for this message";
"                (as in multiline continuations, dispatch";
"                for those occurs at the END";
"returns {message, authkey, alist} or 0";
"argstr must equal the unmodified line from the client";
{argstr, @words} = args;
session = caller;
message = this:parse_mcp(@words);
if (message[1] == "*")
{n, data_tag, keyword, value} = message;
session:multiline_add_value(data_tag, keyword, value);
elseif ((message[1] == ":") || (message[1] == "END"))
{request, dummy, data_tag, dummy, dummy} = message;
return session:multiline_finish(player, data_tag);
else
{request, contains_multiline, authkey, data_tag, alist} = message;
if (contains_multiline)
session:multiline_begin(request, authkey, data_tag, alist);
else
return {request, authkey, alist};
endif
endif
return 0;
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#217:4
{request, authkey, alist} = args;
keyvals = "";
need_data_tag = 0;
multilines = {};
for keyval in (alist)
{keyword, value, ?maybe_ignore} = keyval;
if ((typeof(value) != STR) && (typeof(value) != LIST))
value = toliteral(value);
endif
if (typeof(value) == STR)
if (!match(value, this.unquoted_string))
value = toliteral(value);
endif
keyvals = (((keyvals + keyword) + ": ") + value) + " ";
elseif (typeof(value) == LIST)
need_data_tag = 1;
keyvals = (keyvals + keyword) + "*: \"\" ";
multilines = {@multilines, {keyword, value}};
endif
endfor
if (need_data_tag)
data_tag = this:next_datakey();
keyvals = (keyvals + "_data-tag: ") + data_tag;
endif
message = ("#$#" + request) + " ";
if (authkey)
message = (message + authkey) + " ";
endif
message = {message + keyvals};
if (need_data_tag)
prefix = ("#$#* " + data_tag) + " ";
for field in (multilines)
{keyword, value} = field;
for line in (value)
message = {@message, ((prefix + keyword) + ": ") + line};
endfor
endfor
message = {@message, "#$#: " + data_tag};
endif
return message;
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#217:5
datakey = tostr(random(), this.next_datakey);
this.next_datakey = this.next_datakey + 1;
return datakey;
"Metadata 202106";
"Last-Modify: {895249564, \"Ken\", #75, \"JHM\"}";
.
#218:0
{name, package} = args;
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
if (name in this.package_names)
raise(E_INVARG, "Another package with that name already exists");
elseif (package in this.packages)
raise(E_INVARG, "That package already is registered under a different name.");
else
this.package_names = {@this.package_names, name};
this.packages = {@this.packages, package};
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#218:1
{name} = args;
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
if (idx = name in this.package_names)
this.package_names = listdelete(this.package_names, idx);
this.packages = listdelete(this.packages, idx);
else
raise(E_INVARG, "Not a defined package");
endif
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#218:2
{name} = args;
if (idx = name in this.package_names)
return this.packages[idx];
else
return $failed_match;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#218:3
{package} = args;
if (idx = package in this.packages)
return this.package_names[idx];
else
return "";
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#218:4
return $list_utils:make_alist({this.package_names, this.packages});
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#218:5
"Usage:  :init_for_module()";
if (((!$__core_init_phase) && (caller != this)) && (!$perm_utils:controls(caller_perms(), this)))
raise(E_PERM);
endif
for name in (this.package_names)
if (!(name in this.core_package_names))
this:remove_package(name);
endif
endfor
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895266280, \"Ken\", #75, \"JHM\"}";
.
#218:6
corenames = this.core_package_names;
corepackages = {};
for name in (corenames)
corepackages = {@corepackages, this:match_package(name)};
endfor
return {@pass(@args), @corepackages};
"Metadata 202106";
"Last-Modify: {895266225, \"Ken\", #75, \"JHM\"}";
.
#218:7
if (!$perm_utils:controls(player, this))
player:tell("You don't have permission to add or remove MCP 2.1 packages.");
elseif ($command_utils:object_match_failed(dobj, dobjstr))
elseif (!$object_utils:isa(dobj, $mcp.package))
player:tell(dobj:dname("#"), " is not a valid MCP 2.1 package (descendant of ", $mcp.package, ").");
elseif (!$perm_utils:controls(player, dobj))
player:tell("You don't control ", dobj:dname(), " in order to add or remove it.");
else
name = dobj.name;
package = dobj;
try
if (verb == "@add-package")
this:add_package(name, package);
player:tell("Added ", package:name("#"), ".");
else
this:remove_package(name);
player:tell("Removed ", package:name("#"), ".");
endif
except v (ANY)
{code, message, value, tb} = v;
player:tell(code, ": ", message);
endtry
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895555250, \"Ken\", #75, \"JHM\"}";
.
#219:0
{addr} = args;
set_task_perms(caller_perms());
if (typeof(n = this:new()) == OBJ)
n:setup("read-http sink for " + addr, caller, caller_perms());
n:set_source(addr);
endif
return n;
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#219:1
if (caller != this.module)
raise(E_PERM);
endif
set_task_perms(this.creator);
try
data = $http:get_now(this.source);
except v (ANY)
raise(this.error, "Error reading module: " + v[2]);
endtry
this.data = data[2];
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869601528, \"Erik\", #74, \"JHM\"}";
.
#219:2
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#220:0
"Usage:  :recycle()";
"";
$cord:cleanup(this);
return pass(@args);
"Metadata 202106";
"Last-Modify: {895249566, \"Ken\", #75, \"JHM\"}";
.
#220:1
"Usage:  :closed()";
"";
(caller == $cord) || lose;
$cord:closed(this);
"version: 1.0 Fox Wed Jul  5 17:58:16 1995 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249566, \"Ken\", #75, \"JHM\"}";
.
#220:2
"Usage:  :send(msg, @posargs, @keywordargs)";
"";
"Send the given message across this cord.  The first N arguments should be given in order, where N is the number of required arguments defined for the message.  Additional arguments, if any, should be provided as {keyword, value} pairs.";
"";
"Examples:";
"  :send(\"edit\", \"#123.foo\", {\"Hi!\"})";
"  :send(\"edit\", \"#123:foo\", {\"Hi!\"}, {\"type\", \"MOO-Code\"})";
{msg, @rest} = args;
if (caller != this.our_side)
" raise(E_PERM);";
endif
return $mcp.cord:cord_send(msg, this.type:parse_send_args(@args));
"Metadata 202106";
"Last-Modify: {895249566, \"Ken\", #75, \"JHM\"}";
.
#220:3
"Usage:  :close();";
"";
"Close the cord.";
if (caller != this.our_side)
raise(E_PERM);
endif
$mcp.cord:cord_closed();
$cord:closed(this);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249566, \"Ken\", #75, \"JHM\"}";
.
#221:0
"Usage:  :send_content()";
"";
if ($perm_utils:controls(caller_perms(), args[1]))
pass(@args);
else
raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895554228, \"Ken\", #75, \"JHM\"}";
.
#221:1
"Usage:  :handle_set(session, reference, type, content)";
"";
{session, reference, type, content} = args;
if (caller != this)
raise(E_PERM);
endif
set_task_perms(session.connection);
try
if (type == "moo-code")
rval = session.connection:edit_set_program(reference, content);
elseif (reference == "sendmail")
rval = session.connection:edit_sendmail(reference, content);
else
rval = session.connection:edit_set_note_value(reference, type, content);
endif
player:notify_lines((typeof(rval) == LIST) ? rval | {rval});
except v (ANY)
player:notify_lines((typeof(v[2]) == LIST) ? v[2] | {v[2]});
"   player:notify_lines($code_utils:format_traceback(v[4], v[2]));";
endtry
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895554228, \"Ken\", #75, \"JHM\"}";
.
#222:0
"Usage:  :nominate_for_core()";
"";
objects = pass(@args);
for p in ($set_utils:diff(properties(this), this.non_core_props))
object = this.(p);
if ((typeof(object) == OBJ) && valid(object))
objects = setadd(objects, this.(p));
endif
endfor
return objects;
"Metadata 202106";
"Last-Modify: {877394536, \"Erik\", #74, \"JHM\"}";
.
#222:1
work = {this};
done = {};
while (work)
current = work[1];
work[1..1] = {};
if (!(current in done))
done = {@done, current};
$command_utils:suspend_if_needed(0);
new = current:nominate_for_core();
if (typeof(new) == LIST)
work = {@work, @new};
endif
endif
endwhile
return done;
"Copied from Dave (#87):root_nominate Fri Oct 28 18:45:48 1994 EDT";
"Copied from System Object (#0):nominate_for_core by Joe (#199) Mon Jun  2 16:59:40 1997 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {877394536, \"Erik\", #74, \"JHM\"}";
.
#222:2
"Usage:  :module_finish_unpack()";
"";
if (!$perm_utils:controls(caller_perms(), this))
raise(E_PERM);
endif
if ($code_utils:verb_location() == this)
"this is the generic, so we corify it.";
try
add_property(#0, "property_farm", this, {this.owner, "r"});
except (E_INVARG)
`$property_farm = this ! ANY';
endtry
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {877394536, \"Erik\", #74, \"JHM\"}";
.
#222:3
if ($__core_init_phase)
pass(@args);
nominate = this:nominate_for_core();
for p in (properties(this))
if ((typeof(o = this.(p)) == OBJ) && (!(o in {@nominate, $nothing, $ambiguous_match, $failed_match})))
delete_property(this, p);
endif
endfor
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895717661, \"Ken\", #75, \"JHM\"}";
.
#223:0
set_task_perms(caller_perms());
if (typeof(n = this:new()) == OBJ)
n:setup("read-player sink for " + player:name(), caller, caller_perms());
n:set_source(player);
endif
return n;
"Metadata 202106";
"Last-Modify: {869519754, \"Erik\", #74, \"JHM\"}";
.
#223:1
if (caller != this.module)
raise(E_PERM);
endif
set_task_perms(this.creator);
p = this.source;
notify(p, "[Input module; use `@abort' to abort the transaction]");
data = {};
begin = 0;
su = $string_utils;
while (1)
line = read(p);
if (line == "--- begin module ---")
begin = 1;
elseif (!begin)
raise(this.error, "Didn't see a --- begin module ---");
elseif (line == "@abort")
raise(this.error, "Transaction aborted.");
elseif (line == "--- end module ---")
this:set_data(data);
return length(data);
else
data = {@data, su:trim(line)};
endif
endwhile
notify(p, "[Module read.]");
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#223:2
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {869253135, \"Erik\", #74, \"JHM\"}";
.
#224:0
"This is the standard :set_foo verb.  It allows the property to be set if called by this or called with adequate permissions (this's owner or wizardly).";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..length(verb)]) = args[1];
else
return E_PERM;
endif
"version: 1.0 Fox Wed Jul  5 17:58:13 1995 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#224:1
"Usage:  :dispatch_request(who, authkey, name, arguments)";
"";
connection = caller;
{message, alist} = args;
if (verbname = this:message_name_to_verbname(message))
set_task_perms(caller_perms());
this:(verbname)(connection, @this:parse_receive_args(message, alist));
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#224:2
"Usage:  :match_request(request)";
"";
request = args[1];
if ($object_utils:has_verb(this, verbname = "mcp_" + request))
return verbname;
else
return 0;
endif
"version: 1.0 Fox Wed Jul  5 17:58:14 1995 EDT";
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#224:3
"Usage:  :initialize_connection()";
"";
{version} = args;
connection = caller;
messages = $list_utils:slice(this.messages_in);
connection:register_handlers(messages);
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#224:4
{message} = args;
if ($object_utils:has_callable_verb(this, vname = "handle_" + message))
return vname;
else
return 0;
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#224:5
connection = caller;
return 0;
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#224:6
"Usage:  :add_cord_type(cord_type)";
"";
{cord_type} = args;
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.cord_types = setadd(this.cord_types, cord_type);
else
raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#224:7
"Usage:  :remove_cord_type(cord_type)";
"";
{cord_type} = args;
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.cord_types = setremove(this.cord_types, cord_type);
else
raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
#224:8
{connection, @args} = args;
if (caller == this)
message = verb[6..$];
`connection:send(message, this:parse_send_args(message, @args)) ! E_VERBNF';
else
raise(E_PERM);
endif
return 0 && "Automatically Added Return";
"Metadata 202106";
"Last-Modify: {895249565, \"Ken\", #75, \"JHM\"}";
.
0 clocks
0 queued tasks
0 suspended tasks
1 active connections with listeners
2 0
