** LambdaMOO Database, Format Version 4 **
164
2455
0
4
2
71
36
38
#0
The System Object

24
2
-1
-1
-1
1
-1
46
15
do_login_command
2
173
-1
server_started
2
173
-1
core_objects
2
173
-1
init_for_core
2
173
-1
user_created user_connected
2
173
-1
user_disconnected user_client_disconnected
2
173
-1
bf_chparent
2
173
-1
bf_add_verb
2
173
-1
bf_add_property
2
173
-1
bf_recycle
2
173
-1
user_reconnected
2
173
-1
bf_set_verb_info
2
173
-1
checkpoint_started
2
173
-1
do_out_of_band_command
2
173
-1
checkpoint_finished
2
173
-1
199
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
max_seconds
max_ticks
hacker
generic_db
shutdown_message
shutdown_time
no_one
player_db
class_registry
player_class
gender_utils
trig_utils
time_utils
editor_help
mail_recipient
mail_agent
mail_editor
note_editor
verb_editor
generic_editor
match_utils
object_utils
lock_utils
gripe_recipients
letter
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
error
newt_log
toad_log
site_log
housekeeper
network
generic_biglist_home
feature
local
gopher
prog_options
build_options
mail_name_db
generic_utils
quota_utils
paranoid_db
no_connect_message
sysobj
byte_quota_utils
object_quota_utils
server_options
feature_warehouse
builder_help
mail_help
ftp
password_verifier
new_password_log
frand_class
mail_recipient_class
stage_talk
pasting_feature
core_history
matrix_utils
checkpoint_start_time
last_db_size
checkpoint_successful
last_dump_time
httpd
news_item
recording_system
intercom
moderated_room
classroom
bot
slide_projector
camera
vcr
tv
tape
tape_library
prog_tutorial
recitable_note
lecture
login_watcher
webpage
macmoose_utils
webprojector
anonymous_users
soc_verbs
recorder
readme
guide
room_db
core_version
core_name
core_homepage
core_customized
lag_meter
tool_box
character_request_room
character_request_list
census_FO
encore_utils
enCore_web_utils
enCore_web_object
SR_porter
channel
channel_FO
account_admin_FO
gopher_slate
webslate_utils
public_writable_note
room_directory
nowhere
note_board
real_guest_names
guest_name_suffix
mcp
license
enCore_web_class
enCore_web_application
Xpress_client
MOOtcan
search_engine
MOO_info
encore_help
Xpress_navigator
help_browser
who_browser
Xpress_MOO_Mailer
Xpress_Object_Editor
Xpress_Program_Editor
administration_module
MCP_dispatch
MCP_package
MCP_negotiate
MCP_cord
MCP_parser
MCP_session
MCP_registry
contributors
room_connection_points
assignment_server
assignment
assignment_help
enCore_CGI_Application
Xpress_Login
Inventory_Manager
205
1
4
2
5
1
10
2
1
1
11
2
1
1
12
2
1
0
1001870252
2
5
1
13
2
5
1
14
2
5
1
15
2
5
1
16
2
5
1
17
2
5
1
18
2
5
1
19
2
5
1
22
2
5
1
23
2
5
3
0
2
5
1
24
2
5
1
25
2
5
1
26
2
5
1
27
2
5
1
28
2
5
1
29
2
5
1
30
2
5
1
31
2
1
1
33
2
5
1
34
2
5
1
35
36
1
0
5
2
5
0
30000
2
5
1
36
2
5
1
37
2
5
2

2
5
0
0
2
5
1
38
2
1
1
39
2
1
4
1
4
4
2
generics
2
Generic objects intended for use as the parents of new objects
4
45
1
1
1
3
1
4
1
5
1
6
1
7
1
8
1
9
1
13
1
14
1
30
1
37
1
40
1
45
1
50
1
54
1
58
1
68
1
73
1
74
1
79
1
90
1
91
1
92
1
94
1
96
1
97
1
98
1
99
1
100
1
101
1
102
1
103
1
104
1
105
1
108
1
109
1
111
1
113
1
115
1
126
1
128
1
131
1
136
1
138
4
3
2
utilities
2
Objects holding useful general-purpose verbs
4
21
1
20
1
55
1
24
1
27
1
41
1
26
1
43
1
51
1
52
1
53
1
56
1
42
1
21
1
33
1
13
1
80
1
82
1
59
1
93
1
124
1
125
2
5
1
40
2
5
1
41
2
1
1
26
2
5
1
43
2
5
1
44
2
5
1
45
2
5
1
46
2
5
1
47
2
5
1
48
2
5
1
49
2
5
1
50
2
5
1
51
2
5
1
52
2
5
1
53
2
5
4
1
1
2
2
5
1
54
2
5
0
3600
2
5
1
55
2
5
1
56
2
5
1
6
2
5
1
57
2
5
1
58
2
5
1
59
2
5
1
60
2
5
1
-1
2
5
1
-3
2
5
1
-2
2
5
1
42
2
5
1
21
2
5
1
20
2
5
1
61
2
5
1
9
2
5
1
8
2
5
1
5
2
5
1
7
2
5
1
3
2
5
1
62
2
5
1
1
2
5
1
63
2
5
1
64
2
5
1
65
2
5
1
66
2
5
1
67
2
5
1
68
2
5
0
2147483647
2
5
0
-2147483648
2
5
1
69
2
5
1
70
2
5
1
70
2
5
1
70
2
5
1
71
2
5
1
72
2
5
1
73
2
1
1
74
2
5
1
-1
2
5
1
75
2
1
1
76
2
5
1
77
2
5
1
78
2
5
1
79
2
5
1
80
2
5
1
81
2
1
0
0
2
5
1
0
2
1
1
80
2
5
1
82
2
5
1
83
2
5
1
84
2
1
1
85
2
1
1
86
2
1
1
87
2
5
1
88
2
1
1
89
2
1
1
90
2
5
1
40
2
5
1
91
2
5
1
92
2
5
4
19
4
3
2
enCore 3.2
2
1.8.1
0
1001876038
4
3
2
enCore 3.1
2
1.8.1
0
1000852213
4
3
2
enCore 3.0.2
2
1.8.1
0
998391691
4
3
2
enCore 3.0.1
2
1.8.1
0
995638174
4
3
2
enCore 3.0
2
1.8.1
0
987347897
4
3
2
enCore 2.1.1
2
1.8.1
0
961336324
4
3
2
enCore 2.1
2
1.8.1
0
960582191
4
3
2
enCore 2.0.6
2
1.8.1
0
954635799
4
3
2
enCore 2.0.5
2
1.8.1
0
953998452
4
3
2
enCore 2.0.4
2
1.8.1
0
950453897
4
3
2
enCore 2.0.3
2
1.8.0r5
0
943788625
4
3
2
enCore 2.0.2
2
1.8.0r5
0
939547732
4
3
2
enCore 2.0.1
2
1.8.0r5
0
939031601
4
3
2
enCore 2.0
2
1.8.0r5
0
928255674
4
3
2
enCore 1.1
2
1.8.0p6
0
908817314
4
3
2
enCore 1.0
2
1.8.0p6
0
892671137
2
LambdaMOO
2
1.8.0p5
0
854992537
2
1
1
93
36
1
0
0
2
5
0
0
2
5
0
1
2
5
0
0
2
5
1
32
2
5
1
95
2
5
1
96
2
5
1
97
2
5
1
98
2
5
1
99
2
5
1
100
2
5
1
101
2
5
1
102
2
5
1
103
2
5
1
104
2
5
1
105
2
5
1
106
2
5
1
107
2
5
1
108
2
5
1
109
2
5
1
110
2
5
1
111
2
5
1
112
36
1
1
113
2
5
0
0
2
0
1
114
2
5
1
115
2
5
1
116
2
5
1
117
2
5
1
118
2
5
2
3.2
2
5
2
enCore
2
5
2
http://lingua.utdallas.edu/encore/
2
5
0
0
2
5
1
119
2
1
1
120
2
1
1
121
2
1
1
122
2
1
1
123
2
1
1
124
2
1
1
125
2
1
1
126
2
1
1
127
2
1
1
128
2
1
1
129
2
1
1
130
2
1
1
131
2
1
1
132
2
1
1
134
2
1
1
135
2
1
1
-1
2
5
1
136
2
1
0
0
2
5
2
[Guest]
2
5
1
137
2
5
4
1
2
License: enCore and enCore Xpress is Copyright (C) 1997-2001 of the enCore Open Source MOO Project. All rights reserved. The core of this program is Copyright (C) 1991-1997 of LambdaMOO. The enCore layer is Copyright (C) 1997-2001 of its respective authors. The Xpress client and GUI is is Copyright (C) 1998-2001 of Jan Rune Holmevik and Cynthia Haynes. The MOOtcan telnet applet is Copyright 1998-2001 of Sindre Srensen and Jan Rune Holmevik. The Virtual Assignment Server Environment is Copyright (C) 2000-2001 of Jason Nolan and the University of Toronto. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License See http://lingua.utdallas.edu/encore/gpl.html for details. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY.
2
5
1
138
2
1
1
146
2
1
1
147
2
1
1
148
2
1
1
149
2
1
1
150
2
1
1
151
2
1
1
152
2
1
1
153
2
1
1
154
2
1
1
155
2
1
1
156
2
1
1
157
2
1
1
158
2
1
1
139
2
5
1
140
2
5
1
141
2
5
1
142
2
5
1
143
2
5
1
144
2
5
1
145
2
5
4
1
2
Credits: Jan Rune Holmevik, Cynthia Haynes, Sindre Srensen, Ken Schweller, Mark Blanchard, Jorge Barrios, Amy Bruckman, Matthew Campbell, John Towell, Gustavo Glusman, Craig Leikis, Juli Burk, Michael Thompson, Rui Miguel Barbosa Pinto, Andrew Wilson, Ken Fox, Matthew Beermann, Jason Nolan, Noel Davis, Stephen Ashley, Herv Collin, Emma Jane Hogbin, and Claudijo Borovic.
2
5
4
0
2
5
1
133
2
1
1
159
2
1
1
160
2
1
1
161
2
1
1
162
2
1
1
163
2
1
5
2
4
4
1
2
The System Object
2
5
2
The known universe.
2
5
4
2
0
25324
0
1001876086
36
1
5
2
5
5
2
5
#1
Root Class

152
2
-1
-1
-1
-1
0
-1
37
initialize
2
173
-1
recycle
2
173
-1
set_name
2
173
-1
title
2
173
-1
titlec
2
173
-1
set_aliases
2
173
-1
match
2
173
-1
match_object
2
173
-1
set_description
2
173
-1
description
2
173
-1
look_self
2
173
-1
notify
2
173
-1
tell
2
173
-1
tell_lines
2
173
-1
accept
2
173
-1
moveto
2
173
-1
eject eject_nice eject_basic
2
173
-1
is_unlocked_for
2
173
-1
huh
2
173
-1
set_message
2
173
-1
do_examine
2
173
-1
examine_key
2
173
-1
examine_names
2
173
-1
examine_desc
2
173
-1
examine_contents
2
173
-1
examine_verbs
2
173
-1
get_message
2
173
-1
room_announce*_all_but
2
173
-1
init_for_core
2
173
-1
contents
36
173
-1
examine_verb_ok
2
173
-1
is_listening
2
173
-1
hidden_verbs
2
173
-1
examine_owner
2
173
-1
announce*_all_but
2
173
-1
tell_lines_suspended
2
173
-1
acceptable
2
173
-1
6
key
aliases
description
object_size
accessed
drawing
6
0
0
2
4
4
0
2
5
2

2
5
4
2
0
22829
0
1001876092
36
1
0
0
2
5
2

2
5
#2
Wizard

7
2
62
-1
120
57
-1
-1
0
1
mcd_save
185
4
2
4
164
1
0
1
2
1
4
1
10
1
11
1
12
1
13
1
14
1
15
1
16
1
17
1
18
1
19
1
22
1
23
1
24
1
25
1
26
1
27
1
28
1
29
1
30
1
31
1
33
1
34
1
35
1
36
1
37
1
38
1
39
1
40
1
41
1
43
1
44
1
45
1
46
1
47
1
48
1
49
1
50
1
51
1
52
1
53
1
54
1
55
1
56
1
6
1
57
1
58
1
59
1
60
1
42
1
21
1
20
1
61
1
9
1
8
1
5
1
7
1
3
1
62
1
1
1
63
1
64
1
65
1
66
1
67
1
68
1
69
1
70
1
71
1
72
1
73
1
74
1
75
1
76
1
77
1
78
1
79
1
80
1
81
1
82
1
83
1
84
1
85
1
86
1
87
1
88
1
89
1
90
1
91
1
92
1
93
1
32
1
95
1
96
1
97
1
98
1
99
1
100
1
101
1
102
1
103
1
104
1
105
1
106
1
107
1
108
1
109
1
110
1
111
1
112
1
113
1
114
1
115
1
116
1
117
1
118
1
119
1
120
1
121
1
122
1
123
1
124
1
125
1
126
1
127
1
128
1
129
1
130
1
131
1
132
1
134
1
135
1
136
1
137
1
138
1
146
1
147
1
148
1
149
1
150
1
151
1
152
1
153
1
154
1
155
1
156
1
157
1
158
1
139
1
140
1
141
1
142
1
143
1
144
1
145
1
133
1
159
1
160
1
161
1
162
1
163
1
94
4
167
2
builder
2
login
2
last_huh
2
guest_log
2
biglist
2
big_mail_recipient
2
limbo
2
registration_db
2
new_player_log
2
verb_help
2
core_help
2
prog_help
2
wiz_help
2
wiz_utils
2
site_db
2
math_utils
2
set_utils
2
builtin_function_help
2
new_prog_log
2
generic_help
2
guest
2
seq_utils
2
quota_log
2
you
2
hacker
2
generic_db
2
no_one
2
player_db
2
player_class
2
gender_utils
2
trig_utils
2
time_utils
2
editor_help
2
mail_recipient
2
mail_agent
2
mail_editor
2
note_editor
2
verb_editor
2
generic_editor
2
match_utils
2
object_utils
2
lock_utils
2
letter
2
list_utils
2
command_utils
2
player
2
wiz
2
prog
2
code_utils
2
help
2
perm_utils
2
building_utils
2
string_utils
2
news
2
note
2
container
2
thing
2
exit
2
room
2
player_start
2
root_class
2
recycler
2
garbage
2
mail_options
2
edit_options
2
display_options
2
generic_options
2
error
2
newt_log
2
toad_log
2
site_log
2
housekeeper
2
network
2
generic_biglist_home
2
feature
2
gopher
2
prog_options
2
build_options
2
mail_name_db
2
generic_utils
2
quota_utils
2
paranoid_db
2
sysobj
2
byte_quota_utils
2
object_quota_utils
2
server_options
2
feature_warehouse
2
builder_help
2
mail_help
2
ftp
2
password_verifier
2
new_password_log
2
frand_class
2
mail_recipient_class
2
stage_talk
2
pasting_feature
2
matrix_utils
2
httpd
2
news_item
2
recording_system
2
intercom
2
moderated_room
2
classroom
2
bot
2
slide_projector
2
camera
2
vcr
2
tv
2
tape
2
tape_library
2
prog_tutorial
2
recitable_note
2
lecture
2
login_watcher
2
webpage
2
macmoose_utils
2
webprojector
2
soc_verbs
2
recorder
2
readme
2
guide
2
room_db
2
lag_meter
2
tool_box
2
character_request_room
2
character_request_list
2
census_FO
2
encore_utils
2
enCore_web_utils
2
enCore_web_object
2
SR_porter
2
channel
2
channel_FO
2
account_admin_FO
2
gopher_slate
2
webslate_utils
2
public_writable_note
2
room_directory
2
note_board
2
mcp
2
enCore_web_class
2
enCore_web_application
2
Xpress_client
2
MOOtcan
2
search_engine
2
MOO_info
2
encore_help
2
Xpress_navigator
2
help_browser
2
who_browser
2
Xpress_MOO_Mailer
2
Xpress_Object_Editor
2
Xpress_Program_Editor
2
administration_module
2
MCP_dispatch
2
MCP_package
2
MCP_negotiate
2
MCP_cord
2
MCP_parser
2
MCP_session
2
MCP_registry
2
assignment_server
2
assignment
2
assignment_help
2
enCore_CGI_Application
2
Xpress_Login
2
Inventory_Manager
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
4
5
2
5
5
36
1
5
36
1
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
4
5
2
5
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
4
7
0
0
0
0
4
3
1
17
0
0
0
0
4
3
1
29
0
0
0
0
4
3
1
34
0
0
0
0
4
3
1
70
0
0
0
0
4
3
1
89
0
0
0
0
2
4
5
2
4
4
12
1
91
1
92
1
110
1
114
1
119
1
123
1
130
1
127
1
119
1
123
1
130
1
127
36
1
5
2
0
5
2
0
5
2
1
0
0
2
5
5
2
5
5
36
1
5
36
1
5
36
0
5
36
1
4
100
1
0
1
1
1
2
1
3
1
4
1
5
1
6
1
7
1
8
1
9
1
10
1
11
1
12
1
15
1
20
1
21
1
22
1
24
1
29
1
32
1
34
1
40
1
42
1
52
1
53
1
54
1
56
1
57
1
58
1
61
1
70
1
72
1
75
1
79
1
83
1
87
1
88
1
89
1
94
1
95
1
96
1
97
1
98
1
99
1
100
1
101
1
102
1
103
1
104
1
105
1
106
1
107
1
108
1
109
1
110
1
111
1
112
1
113
1
114
1
115
1
116
1
117
1
118
1
119
1
120
1
121
1
122
1
123
1
124
1
125
1
126
1
127
1
128
1
129
1
130
1
131
1
132
1
133
1
134
1
135
1
136
1
138
1
146
1
147
1
148
1
149
1
150
1
151
1
152
1
153
1
154
1
155
1
156
1
157
1
158
1
159
1
160
1
161
1
162
1
163
2
1
5
36
1
5
2
4
5
2
0
5
2
0
5
2
5
5
2
5
5
2
4
5
2
5
2
its
2
5
2
Its
2
5
5
2
5
5
2
5
5
2
5
0
2147483647
2
1
0
100
36
0
5
2
5
2
Itself
2
5
2
Its
2
5
2
It
2
5
2
It
2
5
2
itself
2
5
2
its
2
5
2
it
2
5
2
it
2
5
5
2
5
0
0
2
0
5
2
5
5
2
5
5
2
5
5
2
5
0
2147483647
2
1
4
4
0
50000
0
2047035
0
1001876086
0
0
36
0
0
0
2
0
5
2
0
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
4
5
2
1
5
2
5
5
2
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
0
5
2
4
5
2
4
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Wizard
2
1
2

2
5
4
2
0
8146
0
1001876092
36
1
5
2
5
5
2
5
#3
generic room

152
2
-1
-1
-1
126
50
5
48
confunc
2
173
-1
disfunc
2
173
-1
say
2
85
-2
emote
2
93
-2
announce
2
173
-1
match_exit
2
173
-1
add_exit
2
173
-1
tell_contents
2
173
-1
@exits
2
13
-1
look_self
2
173
-1
acceptable
2
173
-1
add_entrance
2
173
-1
bless_for_entry
2
173
-1
@entrances
2
9
-1
go
2
93
-2
l*ook
2
93
-2
announce_all
2
173
-1
announce_all_but
2
173
-1
enterfunc
2
173
-1
exitfunc
2
173
-1
remove_exit
2
173
-1
remove_entrance
2
173
-1
@add-exit
2
25
-1
@add-entrance
2
25
-1
recycle
2
173
-1
e east w west s south n north ne northeast nw northwest se southeast sw southwest u up d down
2
13
-1
@eject @eject! @eject!!
2
25
-1
ejection_msg oejection_msg victim_ejection_msg
36
173
-1
accept_for_abode
2
173
-1
@resident*s
2
25
-1
match
2
173
-1
@remove-exit
2
25
-1
@remove-entrance
2
25
-1
moveto
2
173
-1
who_location_msg
2
173
-1
exits entrances
2
173
-1
obvious_exits obvious_entrances
2
173
-1
here_huh
2
173
-1
room_announce*_all_but
2
173
-1
examine_commands_ok
2
173
-1
examine_key
2
173
-1
examine_contents
2
173
-1
free_entry
36
173
-1
dark
2
173
-1
tell_exits
2
173
-1
show_sketch
2
89
-2
_html
2
165
-1
creation_form_html
2
173
-1
16
who_location_msg
free_home
victim_ejection_msg
ejection_msg
oejection_msg
residents
free_entry
entrances
blessed_object
blessed_task
exits
dark
ctype
tell_exits
web_contents_msg
web_exits_msg
68
2
%T
2
5
0
0
2
5
2
You have been expelled from %i by %n.
2
5
2
You expel %d from %i.
2
5
2
%N unceremoniously %{!expels} %d from %i.
2
5
4
0
2
5
0
1
2
5
4
0
2
4
1
-1
2
5
0
0
2
5
4
0
2
4
0
0
2
5
0
3
2
5
0
1
2
5
2
<B>You see: </B>
2
5
2
<B>Links: </B>
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
room.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
5
2
4
4
1
2
generic room
2
5
5
2
5
4
2
0
33755
0
1001876092
36
1
0
1
2
5
5
2
5
#4
generic builder

144
2
-1
-1
-1
90
58
-1
34
@quota
2
25
-1
@create
2
89
-2
@recycle
2
25
-1
@recreate
2
89
13
@dig
2
89
-2
@audit
2
89
-2
@count
2
25
-1
@countDB
2
25
-1
@sort-owned*-objects
2
25
-1
@add-owned
2
25
-1
@verify-owned
2
9
-1
@unlock
2
25
-1
@lock
2
89
0
@newmess*age
2
89
-2
@unmess*age
2
89
-2
_messagify
2
173
-1
@kids
2
29
-1
@contents
2
25
-1
@par*ents
2
25
-1
@location*s
2
25
-1
@cl*asses
2
89
-2
classes_2
2
173
-1
_create
2
173
-1
_recycle
2
173
-1
@chparent
2
89
1
@check-chp*arent
2
89
1
@set*prop
2
89
1
build_option
2
173
-1
set_build_option
2
173
-1
@build-o*ptions @buildo*ptions @builder-o*ptions @buildero*ptions
2
89
-2
@measure
36
89
-2
init_for_core
2
173
-1
@hist*ogram
2
85
-2
_web_menu
2
173
-1
1
build_options
171
4
0
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
4
5
2
5
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
4
5
2
4
5
36
1
5
2
0
5
2
0
5
2
1
1
85
2
5
5
2
5
5
36
1
5
36
1
5
36
0
5
36
1
5
2
1
5
36
1
5
2
4
5
2
0
5
2
0
5
2
5
5
2
5
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
0
2147483647
2
1
0
0
36
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
0
5
2
5
5
2
5
5
2
5
5
2
5
0
2147483647
2
1
5
36
0
5
2
0
5
2
0
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
4
5
2
1
5
2
5
5
2
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
0
5
2
4
5
2
4
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
builder.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
5
2
4
4
1
2
generic builder
2
1
2
You see a player who should type '@describe me as ...'.
2
5
4
2
0
42005
0
1001876092
36
1
5
2
5
5
2
5
#5
generic thing

152
2
-1
-1
-1
126
8
6
7
g*et t*ake
2
45
-1
d*rop th*row
2
45
-1
moveto
2
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
2
173
-1
gi*ve ha*nd
2
109
1
examine_key
2
173
-1
take_html drop_html
2
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
60
2
You can't seem to drop %t here.
2
5
2
You drop %t.
2
5
2
tries to drop %t but fails!
2
5
2
drops %t.
2
5
2
picks up %t.
2
5
2

2
5
2
You take %t.
2
5
2
You can't pick that up.
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
thing.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
5
2
4
4
1
2
generic thing
2
5
5
2
5
4
2
0
7425
0
1001876092
36
1
5
2
5
5
2
5
#6
generic player

152
2
-1
-1
-1
126
40
7
129
init_for_core
2
173
-1
confunc
2
173
-1
disfunc
2
173
-1
initialize
2
173
-1
acceptable
2
173
-1
my_huh
2
173
-1
last_huh
2
173
-1
my_match_object
2
173
-1
tell_contents
2
173
-1
titlec
2
173
-1
notify
2
173
-1
notify_lines
2
173
-1
linesplit
2
173
-1
linelen
36
173
-1
@more
2
25
-1
@wrap
36
9
-2
@linelen*gth
36
25
-1
@pagelen*gth
2
25
-1
tell
2
173
-1
gag_p
2
173
-1
set_gaglist
2
173
-1
@gag*!
2
89
-2
@listgag @gaglist @gagged
2
29
-1
@ungag
2
29
-1
whodunnit
2
173
-1
@ch*eck-full
2
89
-2
@paranoid
2
89
-2
@sw*eep
2
9
-1
wh*isper
2
157
1
page -* '* +*
2
93
-2
receive_page
2
173
-1
page_origin_msg page_echo_msg page_absent_msg
36
173
-1
i inv*entory
2
9
-1
look_self
2
173
-1
home
2
9
-1
@sethome
2
9
-1
g*et take
2
45
-1
@move @teleport
2
93
1
@eject @eject! @eject!!
2
89
5
where*is @where*is
2
93
-2
@who who
2
93
-2
@wizards
2
29
-1
?* help info*rmation @help
2
93
-2
display_option
2
173
-1
edit_option
2
173
-1
set_mail_option set_edit_option set_display_option
2
173
-1
@mailo*ptions @mail-o*ptions @edito*ptions @edit-o*ptions @displayo*ptions @display-o*ptions
2
89
-2
set_name
2
173
-1
set_aliases
2
173
-1
@rename*#
2
89
1
@addalias*# @add-alias*#
2
89
1
@rmalias*# @rm-alias*#
2
89
5
@desc*ribe
2
89
13
@mess*ages
2
25
-1
@notedit
36
25
-1
@last-c*onnection
2
29
-1
set_gender
2
173
-1
@gender
2
25
-1
set_brief
2
173
-1
@mode
2
89
-2
@exam*ine
2
29
-1
exam*ine
2
25
-1
add_feature
36
173
-1
remove_feature
36
173
-1
@add-feature @addfeature
2
25
-1
@remove-feature @rmfeature
2
25
-1
@features
2
93
11
@features
2
25
-1
@memory
36
9
-1
@version
36
9
-1
@uptime
36
9
-1
@quit
2
9
-1
examine_commands_ok
2
173
-1
is_listening
2
173
-1
moveto
2
173
-1
announce*_all_but
2
173
-1
linewrap
36
173
-1
@set-note-string @set-note-text
2
25
-1
verb_sub
2
173
-1
ownership_quota
2
173
-1
tell_lines
2
173
-1
@lastlog
2
29
-1
set_linelength
2
173
-1
set_pagelength
2
173
-1
set_home
2
173
-1
@registerme
2
89
-2
ctime
2
173
-1
@age
36
25
-1
@edit
36
89
-2
erase_paranoid_data
2
173
-1
@move-new
2
89
-2
notify_lines_suspended
2
173
-1
_chparent
2
173
-1
@users
36
13
-1
@password
2
89
-2
recycle
2
173
-1
gc_gaglist
2
173
-1
news
2
89
-2
@html*code
2
89
-2
_html
2
173
-1
@join join
2
17
-1
@sketch
2
89
-2
set_sketch
2
173
-1
time date
2
9
-1
set_ts_client
2
173
-1
@interests @research
2
89
-2
finger @whois @info
2
16
-1
@drawings @maps @ascii
2
89
-2
@search
2
81
-2
do_listing
2
165
-1
@find-name
2
81
-2
>>
2
1
-1
say emote
2
1
-1
@deli*miter
2
89
-2
@pref*erences
2
1
-1
@about
2
9
-1
@kn*ock @inv*ite @busy
36
81
-2
@activity @doing doing
2
89
-2
name title
2
173
-1
_web_menu
2
173
-1
@URL
2
25
-1
start_log stop_log
2
173
-1
reset_Xpress_session
2
173
-1
check_arrive_and_depart_msgs
2
173
-1
@go
2
25
-1
@beep
2
25
-1
@hide
2
89
-2
set_invis
2
173
-1
xpress_edit
2
173
-1
83
features
previous_connection
email_address
last_disconnect_time
help
more_msg
linetask
linesleft
linebuffer
pagelen
owned_objects
linelen
current_folder
all_connect_places
last_connect_place
dict
brief
lines
page_absent_msg
pq
pqc
page_origin_msg
page_echo_msg
edit_options
last_connect_time
ownership_quota
gender
prc
ppc
poc
psc
pr
pp
po
ps
home
password
gaglist
paranoid
display_options
verb_subs
first_connect_time
size_quota
last_password_time
last_connect_attempt
page_count_start_time
html_view
interests_msg
odepart_msg
oarrive_msg
sketch
ts_client
homepage
real_name
display_drawings
old_location
memory
delimiter_msg
last_contacted
out_of_band_session
doing
web_access_code
java_font
java_font_size
xpress_on
sound_volume
mail_notify_sound
Xpress_layout
java_client_localecho
web_mail_signature
update_web_display
recording
Xpress_Confunc_Msg
Xpress_Log
Xpress_Notebook
invis
xpress_screen_size
xpress_screen_division
show_contextual_menu
override_object_styles
xpress_theme
xpress_bookmarks
designer
135
4
4
1
91
1
92
1
110
1
114
36
1
0
0
2
0
2

2
0
0
0
2
1
4
2
1
60
1
151
2
5
2
*** More ***  %n lines left.  Do @more [rest|flush] for more.
2
5
4
2
0
0
0
0
36
1
0
0
36
1
4
0
36
0
0
0
36
1
4
0
2
1
0
-79
36
1
0
1
2
4
4
0
2
0
2
?
2
0
4
0
2
5
0
0
2
5
0
0
2
4
2
%N is not currently logged in.
2
5
2
its
2
5
2
Its
2
5
2
You sense that %n is looking for you in %l.
2
5
2
Your message has been sent to %n
2
5
4
0
2
5
0
0
2
1
0
0
36
0
2
neuter
2
5
2
Itself
2
5
2
Its
2
5
2
It
2
5
2
It
2
5
2
itself
2
5
2
its
2
5
2
it
2
5
2
it
2
5
1
62
2
5
2
impossible password to type
2
0
4
0
2
5
0
0
2
5
4
0
2
5
4
0
2
5
0
2147483647
2
1
4
0
36
0
0
0
2
0
0
0
2
0
0
799707307
2
1
0
0
2
5
2

2
5
2
%N leaves.
2
5
2
%N arrives.
2
5
0
0
2
5
0
0
2
5
2

2
5
2

2
0
0
1
2
5
0
0
2
5
2

2
5
2

2
5
1
-1
2
4
1
-1
2
1
2

2
5
2

2
0
2
Courier
2
5
0
12
2
5
0
0
2
5
0
10
2
5
2
moomail.wav
2
5
2
vertical_layout
2
5
2
False
2
5
4
0
2
5
0
1
2
5
0
0
2
0
4
0
2
0
4
0
2
4
4
0
2
4
0
0
2
5
2
screen.availWidth,screen.availHeight
2
1
2
50%,50%
2
1
0
1
2
1
0
0
2
1
2
azure
2
1
4
0
2
0
0
0
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
player.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
5
2
4
4
1
2
generic player
2
1
2
You see a player who should type '@describe me as ...'.
2
5
4
2
0
145840
0
1001876092
36
1
5
2
5
5
2
5
#7
generic exit

152
2
-1
-1
-1
126
-1
133
12
invoke
2
173
-1
move
2
173
-1
recycle
2
173
-1
leave_msg oleave_msg arrive_msg oarrive_msg nogo_msg onogo_msg
2
173
-1
set_name
2
173
-1
set_aliases
2
173
-1
announce_all_but
2
173
-1
defaulting_oleave_msg
2
173
-1
moveto
2
173
-1
examine_key
2
173
-1
announce_msg
2
173
-1
xpress_edit
2
173
-1
9
obvious
source
dest
nogo_msg
onogo_msg
arrive_msg
oarrive_msg
oleave_msg
leave_msg
61
0
1
2
5
1
-1
2
5
1
-1
2
5
2

2
5
2

2
5
2

2
5
2

2
5
2

2
5
2

2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
exit.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
2
exit_locked.gif
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
5
2
4
4
1
2
generic exit
2
5
5
2
5
4
2
0
8823
0
1001876092
36
1
5
2
5
5
2
5
#8
generic container

152
2
-1
-1
-1
5
84
9
16
p*ut in*sert d*rop
2
157
3
re*move ta*ke g*et
2
157
5
look_self
2
173
-1
acceptable
2
173
-1
open
2
45
-1
@lock_for_open
2
105
0
is_openable_by
2
173
-1
close
2
45
-1
@unlock_for_open
2
41
-1
tell_contents
2
173
-1
set_opened
2
173
-1
@opacity
2
105
12
set_opaque
2
173
-1
oclose_msg close_msg oopen_msg open_msg oput_fail_msg put_fail_msg oremove_fail_msg oremove_msg remove_fail_msg remove_msg oput_msg put_msg oopen_fail_msg open_fail_msg empty_msg
36
173
-1
dark
2
173
-1
_html
2
173
-1
19
oclose_msg
close_msg
oopen_msg
open_msg
oput_fail_msg
put_fail_msg
opaque
dark
oremove_fail_msg
oremove_msg
remove_fail_msg
remove_msg
oput_msg
put_msg
oopen_fail_msg
open_fail_msg
empty_msg
opened
open_key
79
2
closes %d.
2
5
2
You close %d.
2
5
2
opens %d.
2
5
2
You open %d.
2
5
2

2
5
2
You can't put %d in that.
2
5
0
1
2
1
0
1
2
1
2

2
5
2
removes %d from %i.
2
5
2
You can't remove that.
2
5
2
You remove %d from %i.
2
5
2
puts %d in %i.
2
5
2
You put %d in %i.
2
5
2

2
5
2
You can't open that.
2
5
2
It is empty.
2
5
0
0
2
1
0
0
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
container.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
5
2
4
4
1
2
generic container
2
5
5
2
5
4
2
0
11345
0
1001876092
36
1
5
2
5
5
2
5
#9
generic note

152
2
-1
-1
-1
5
54
63
15
r*ead
2
45
-1
er*ase
2
45
-1
wr*ite
2
157
4
del*ete rem*ove
2
153
5
encrypt
2
105
0
decrypt
2
41
-1
text
2
173
-1
is_readable_by
2
173
-1
set_text
2
173
-1
is_writable_by
2
173
-1
mailme @mailme
2
41
-1
_html
2
173
-1
copy paste
2
41
-1
xpress_edit
2
173
-1
email_html
2
173
-1
6
writers
encryption_key
text
date
author_name
note_text_alignment
66
4
0
2
5
0
0
2
4
4
0
2
4
0
0
2
5
2

2
5
2
LEFT
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
note.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
5
2
4
4
1
2
generic note
2
5
2
There appears to be some writing on the note ...
2
5
4
2
0
11564
0
1001876092
36
1
5
2
5
5
2
5
#10
Login Commands

16
2
-1
-1
-1
1
-1
68
44
?
2
93
-1
wel*come @wel*come
2
93
-1
w*ho @w*ho
2
93
-1
co*nnect @co*nnect
2
93
-1
cr*eate @cr*eate
2
93
-1
q*uit @q*uit
2
93
-1
up*time @up*time
2
93
-1
v*ersion @v*ersion
2
93
-1
parse_command
2
173
-1
check_for_shutdown
2
173
-1
check_player_db
2
173
-1
_match_player
2
173
-1
notify
2
173
-1
tell
36
173
-1
player_creation_enabled
2
173
-1
newt_registration_string registration_string
2
173
-1
init_for_core
2
173
-1
special_action
2
164
-1
blacklisted graylisted redlisted spooflisted
2
173
-1
blacklist_add*_temp graylist_add*_temp redlist_add*_temp spooflist_add*_temp
2
173
-1
blacklist_remove*_temp graylist_remove*_temp redlist_remove*_temp spooflist_remove*_temp
2
173
-1
listname
2
173
-1
record_connection
2
173
-1
sample_lag
2
173
-1
is_lagging
2
173
-1
max_connections
2
173
-1
request_character
2
173
-1
req*uest @req*uest
2
93
-1
h*elp @h*elp
2
93
-1
maybe_print_lag
2
173
-1
current_lag
2
173
-1
maybe_limit_commands
2
173
-1
server_started
2
173
-1
uptime_since
2
173
-1
count_bg_players
2
173
-1
blacklisted_temp graylisted_temp redlisted_temp spooflisted_temp
2
173
-1
templist_expired
2
173
-1
temp_newt_registration_string
2
173
-1
add_interception
36
173
-1
delete_interception
36
173
-1
interception
36
173
-1
intercepted_password
2
173
-1
do_out_of_band_command doobc
36
173
-1
autoconnect
2
93
-1
39
welcome_message
newt_registration_string
registration_string
registration_address
create_enabled
bogus_command
blank_command
graylist
blacklist
redlist
who_masks_wizards
max_player_name
spooflist
ignored
max_connections
connection_limit_msg
lag_samples
request_enabled
help_message
last_lag_sample
lag_sample_interval
boot_process
lag_cutoff
lag_exemptions
newted
current_connections
current_numcommands
max_numcommands
temporary_newts
downtimes
print_lag
current_lag
temporary_blacklist
temporary_redlist
temporary_spooflist
temporary_graylist
intercepted_players
intercepted_actions
core_welcome
45
4
17
2
     __    __ __ ______  __    __    __      __ __ _____    ______ _____ 
2
    / /   / // // ____/ / /   / /   / /     / // // ___ \  / ____// ___ \
2
   / /___/ // // /____ / /___/ /   / / __  / // // /__/ / / /_   / /  / /
2
  / ____  // // //_  // ____  /   / / / / / // // _  __/ / __/  / /  / /
2
 / /   / // // /__/ // / __/_/__ / /_/ /_/ // // / \ \  / /___ / /__/ /
2
       ______ __    __ / /      ________ _____    ______
2
      / ____// /   / // /      / ____  // ___ \  / ____/
2
     / /_   /  \  / // /      / /   / // /__/ / / /_                    
2
    / __/  / /\ \/ // /      / /   / // _  __/ / __/               
2
   / /___ / /  \  // /_____ / /___/ // / \ \  / /___   VERSION 3.2 WITH XPRESS
2
  /_____//_/   /_//_______//_______//_/   \_\/_____/  GRAPHICAL USER INTERFACE
2
                     =========================================================
2
                     E D U C A T I O N A L   M O O   C O R E   D A T A B A S E
2
                     =========================================================
2
This software is Copyright (C) 1997-2001 of the enCore Open Source MOO Project. All rights reserved. The original core of this program is Copyright (C) 1991-1997 of LambdaMOO. The enCore layer is Copyright (C) 1997-2001 of its respective authors. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY.
2

2
Type 'connect wizard' to log in 
2
5
2
Your character is temporarily hosed.
2
5
2
Character creation is disabled.
2
5
2

2
5
0
0
2
5
2
?
2
1
2
welcome
2
1
4
2
4
0
4
0
2
0
4
2
4
0
4
0
2
0
4
2
4
0
4
0
2
0
0
0
2
0
0
40
2
5
4
2
4
0
4
0
2
0
4
0
2
5
0
99999
36
5
2
*** The MOO is too busy! The current lag is %l; there are %n connected.  WAIT FIVE MINUTES BEFORE TRYING AGAIN.
36
1
4
5
0
0
0
0
0
0
0
0
0
0
2
5
0
0
2
5
4
1
2
Sorry, but there's no help here yet.  Type `?' for a list of commands.
2
5
0
0
2
5
0
15
2
5
0
0
2
5
0
5
2
5
4
0
2
5
4
0
2
0
4
1
1
-41
2
5
4
1
0
2
2
5
0
20
2
5
4
0
2
4
4
0
2
5
0
0
2
5
0
6
2
1
4
2
4
0
4
0
2
0
4
2
4
0
4
0
2
0
4
2
4
0
4
0
2
0
4
2
4
0
4
0
2
0
4
0
36
0
4
0
36
0
4
17
2
     __    __ __ ______  __    __    __      __ __ _____    ______ _____ 
2
    / /   / // // ____/ / /   / /   / /     / // // ___ \  / ____// ___ \
2
   / /___/ // // /____ / /___/ /   / / __  / // // /__/ / / /_   / /  / /
2
  / ____  // // //_  // ____  /   / / / / / // // _  __/ / __/  / /  / /
2
 / /   / // // /__/ // / __/_/__ / /_/ /_/ // // / \ \  / /___ / /__/ /
2
       ______ __    __ / /      ________ _____    ______
2
      / ____// /   / // /      / ____  // ___ \  / ____/
2
     / /_   /  \  / // /      / /   / // /__/ / / /_                    
2
    / __/  / /\ \/ // /      / /   / // _  __/ / __/               
2
   / /___ / /  \  // /_____ / /___/ // / \ \  / /___   VERSION 3.2 WITH XPRESS
2
  /_____//_/   /_//_______//_______//_/   \_\/_____/  GRAPHICAL USER INTERFACE
2
                     =========================================================
2
                     E D U C A T I O N A L   M O O   C O R E   D A T A B A S E
2
                     =========================================================
2
This software is Copyright (C) 1997-2001 of the enCore Open Source MOO Project. All rights reserved. The original core of this program is Copyright (C) 1991-1997 of LambdaMOO. The enCore layer is Copyright (C) 1997-2001 of its respective authors. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY.
2

2
Type 'connect wizard' to log in 
2
5
5
2
4
4
1
2
Login Commands
2
5
2
This provides everything needed by #0:do_login_command.  See `help $login' on $core_help for details.
2
5
4
2
0
46441
0
1001876092
36
1
5
2
5
5
2
5
#11
Player Last_huh Verbs

16
2
-1
-1
-1
1
-1
10
4
@*
2
173
-1
give hand
2
173
-1
get take
2
173
-1
drop throw
2
173
-1
0
6
5
2
4
4
1
2
Player Last_huh Verbs
2
5
2
A repository of last-resort player verbs to be called by $player:last_huh
2
5
4
2
0
4630
0
1001876092
36
1
5
2
5
5
2
5
#12
Guest Log

0
2
-1
-1
-1
1
-1
11
4
enter
2
173
-1
last
2
173
-1
init_for_core
2
173
-1
find
2
173
-1
2
connections
max_entries
8
4
0
2
0
0
199
2
0
5
2
4
4
1
2
Guest Log
2
5
5
2
5
4
2
0
3770
0
1001876092
36
1
5
2
5
5
2
5
#13
Generic BigList Utilities

144
36
-1
-1
-1
79
-1
80
26
length
36
173
-1
find_nth
36
173
-1
find_ord
36
173
-1
set_nth
36
173
-1
kill
36
173
-1
insert_after insert_before
36
173
-1
extract_range
36
173
-1
delete_range
36
173
-1
keep_range
36
173
-1
insert_last
36
173
-1
start
36
173
-1
next
36
173
-1
_find_nth
36
173
-1
_find_ord
36
173
-1
_set_nth
36
173
-1
_skill
36
173
-1
_extract
36
173
-1
_merge
36
173
-1
_smerge
36
173
-1
_split
36
173
-1
_rmerge
36
173
-1
_scrunch
36
173
-1
_listfind_nth
36
173
-1
_insertfirst
36
173
-1
debug
36
173
-1
_call
2
173
-1
2
about
maxfanout
9
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.
36
5
0
7
36
5
4
73
2
Generic BigList Utilities
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 #5546 (Generic Biglist Resident) or $big_mail_recipient
2
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.
36
5
5
36
4
4
2
2
ghblu
2
biglist_utils
36
1
4
1
2
This is the Generic BigList Utilities utility package.  See `help $biglist' for more details.
36
5
4
2
0
22961
0
1001876092
36
1
5
36
5
5
36
5
#14
Generic Large-Capacity Mail Recipient

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

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

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

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

2
To convert an existing $mail_recipient-child (call it #MR) into a $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);
2

2
Reconstructing Damaged Big Mail Recipients
2
------------------------------------------
2
On rare occasions, the tree structure created by $biglist can be corrupted (this can happen on lists sufficiently large that a list-modification operation (e.g., @rmm, @renumber) runs out of ticks/seconds).  In the vast majority of such cases, your messages are all still there; it's simply that the tree we use for finding/searching them is messed up.
2

2
To recover messages from a damaged big mail recipient (#DBMR)
2
 --- read to the end before you start typing any commands ---
2

2
create a fresh $big_mail_recipient (#NEWBMR) and then do the following:
2

2
   ;#NEWBMR:restore_from(#DBMR)
2

2
When this finishes, #NEWBMR will contain all of the mail messages we were able to find.  (note that this will include messages that you had deleted from #DBMR but not expunged).  #NEWMBR should thenceforth be useable in place of #DBMR, however if #DBMR contains custom verbs and non-clear properties, these will also need to be copied over.
2

2
Alternatively, one may do
2

2
   @copyobject #DBMR to #TEMPBMR
2
   ;#DBMR:restore_from(#TEMPBMR)
2

2
to rebuild #DBMR in place.  This, however, will take about twice as long.
2

2
oooooooooooooooooooooooooooooooo
2
WARNING!!! WARNING!!! WARNING!!!
2
oooooooooooooooooooooooooooooooo
2

2
Calling #OBJ:restore_from(...) COMPLETELY AND IRREVOCABLY REMOVES ALL MESSAGES from the object that it is run on (#OBJ); you MUST be sure to EITHER have made a copy of #OBJ OR be doing the restore to a DIFFERENT object.
36
5
4
2
0
37167
0
1001876092
36
1
5
36
5
5
36
5
#15
Limbo

16
2
-1
-1
-1
1
-1
73
5
acceptable
2
173
-1
confunc
2
173
-1
who_location_msg
36
173
-1
moveto
36
173
-1
eject
2
173
-1
0
6
5
2
4
4
1
2
The Body Bag
2
5
5
2
5
4
2
0
2179
0
1001876092
36
1
5
2
5
5
2
5
#16
Registration Database

0
36
-1
-1
-1
37
-1
78
8
find* _only* _every*
36
173
-1
add
36
173
-1
init_for_core
36
173
-1
suspicious_address
2
173
-1
suspicious_userid
2
173
-1
describe_registration
2
173
-1
prune
2
173
-1
report_prune_progress
2
173
-1
6
registrar
prune_progress
prune_stop
total_pruned_people
total_pruned_characters
prune_task
15
1
2
36
5
2
zzz
36
5
2
zzz
36
5
0
22967
36
5
0
470
36
5
0
1327771136
36
5
2

36
5
5
36
1
4
4
2

2

4
0
4
0
36
0
5
36
4
4
1
2
Registration Database
36
5
5
36
5
4
2
0
7856
0
1001876092
36
1
5
36
5
5
36
5
#17
Player Creation Log

0
36
46
-1
29
14
-1
-1
5
display_seq_headers
2
173
-1
msg_summary_line
2
173
-1
init_for_core
2
173
-1
is_usable_by
2
173
-1
expire_old_messages
36
13
-1
0
30
0
1
36
5
1
13
36
5
1
36
36
1
2

36
1
5
36
5
0
0
36
1
4
0
36
0
4
1
1
17
36
5
5
36
5
5
36
5
5
36
5
4
1
1
2
36
1
4
0
36
1
5
36
1
5
36
1
4
0
36
0
5
36
5
5
36
5
4
0
36
1
5
36
5
4
0
36
1
5
36
0
5
36
0
5
36
0
0
0
36
4
4
2
2
Player Creation Log
2
PCL
36
1
2
Log of player creations.
36
5
4
2
0
3642
0
1001876092
36
1
5
36
5
5
36
5
#18
Verb Help DB

16
36
-1
-1
-1
1
-1
12
3
find_topics
36
173
-1
get_topic
2
173
-1
dump_topic
2
173
-1
1
help_msg
7
4
23
2
This is not a help database in the same way that children of $generic_help are. This object does the work when someone calls help in this way:
2

2
    help <object>:<verb>
2

2
It parses out the object and verb reference, pulls out the comments at the beginning of the verb, and returns them to the help system for nice display.
2

2
    :find_topics(string)
2
       tries to pull out an object:verb reference from string
2
       returns {string} if successful
2
       returns {} if not
2

2
    :get_topic(string)
2
       tries to pull out an object:verb reference from string (returns 0 if
2
          it fails to do so)
2
       tries to match the object
2
       checks the object to see if the verb exists
2
       pulls out the initial comments from the verb if they exist
2
       returns a meaningful list of strings to be displayed to the player
2

2
    :dump_topic(string)
2
       does the same as :get_topic above, but returns the verb documentation
2
          in dump form.
2
----
36
5
5
36
4
4
2
2
verbhelp
2
vh
36
5
2
A `help database' that knows about all of the documented verbs.
36
5
4
2
0
3990
0
1001876092
36
1
5
36
5
5
36
5
#19
Core Utility Help

16
36
-1
-1
-1
30
-1
85
3
find_topics
36
173
-1
get_topic
36
173
-1
dump_topic
2
173
-1
38
$login
$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
$housekeeper
$recycler
$error
$biglist
$guest_log
mail-resolution
$news
matching
MR-expiration
mail-expiration
$big_mail_recipient
46
4
77
2
$login
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
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 available 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
  .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
  .max_connections
2
    -- integer representing the maximum connected players permitted on this moo.
2
  .connection_limit_msg
2
    -- string printed out when this is reached.
2
  .lag_exemptions
2
    -- list of non-wizard players who may login anyway.
2

2
Other verbs
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
see `help blacklist'
2

36
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.
36
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.
36
5
4
38
2
Subscribing to Mail Recipients
2
------------------------------
2
There are two notions of being "subscribed" to a mailing list/recipient.
2

2
(1) 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

2
(2) Soft subscribed == keeping track of a current message for this recipient and (optionally) being on the recipient's .mail_notify list.
2

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

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.
36
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
36
5
4
32
2
Controlling Access to Mail Recipients
2
-------------------------------------
2
:is_writable_by(one) - one may alter/add/remove saved messages
2
:is_readable_by(one) - one may read messages.
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 from the owner who can do anything
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
  A mailing list is "public" if everyone can read it.
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
 (1) wizards having write access
2
     --- necessary in order for :receive_message to work
2
 (2) writers being able to read and send (the converse being a ludicrous 
2
     situation), 
2
 (3) persons on the mail_forward list of someone with reader access will also
2
     have read access (convenient).
36
5
4
20
2
Generic Mail Recipient
2
----------------------
2
A "mail recipient" is, by definition, an object that can be sent mail.
2
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 kind of 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
Note that, by default, a freshly created recipient is accessibly only by you.  If you wish to make a publically accessible recipient, set .readers=1.  Furthermore, if you want to allow a message on your recipient to be removed by its sender without your intervention, set .rmm_own_msgs=1.  Finally, in order for other players to be able to refer to your recipient by name, the object must reside in $mail_agent.  $mail_agent will not accept the object unless it has an actual description and a name distinct from all other mail recipient names/aliases.
2

2
Topics:
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 
2
                       and the story of .current_message
2
  MR-expiration   -- expiring and netmailing messages from recipients
36
5
4
24
2
Receiving Mail
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
By default this returns the maximum of the message numbers appearing in 
2
messages or .messages_going, incremented by 1.  If the recipient is a player
2
then the value returned will be 1 higher if it conflicts with the player's 
2
current message number for him/herself.
2

2
:receive_message(msg,sender)
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.  
2
`sender', the original sender, is supplied in case one wants different 
2
action depending on who is sending the message (e.g., mail-gagging).
2
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
  (1) the resent message is considered to be a distinct message having this 
2
      object as its "author" --- i.e., the From: line will necessarily be 
2
      different.
2
  (2) since this "forwarding" is invisible to the mailsystem, 
2
      there is no protection against loops and multiple copies.
2

36
5
4
34
2
Mail Transmission Format
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 @copym 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.
36
5
4
52
2
Resolving Mail Forwarding & Notification
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([from])
2
    should return either
2
     . a list of objects (either players or $mail_recipients)
2
         to which mail for this recipient will be redirected.
2
     . a string error message to be printed to the player sending the message.
2
         If this recipient is one of the original destinations (i.e., not the
2
         result of a previous forwarding), no mail is actually sent.
2

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

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

2
    object:notify_mail is called with the arguments 
2
    (sender,recipients,msgnumbers) where 
2
      recipients  == list of recipients including object in .mail_notify
2
      msgsnumbers == corresponding list of :receive_message return values
2
                 (or 0 if :receive_message is not actually called, which
2
                  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(from) 
2
    what :mail_forward should return for mail coming from unapproved senders
2
    This returns .moderator_forward (pronoun_subbed if a string) by default.
2

2
:moderator_notify(from)
2
    what :mail_notify should return for mail coming from unapproved senders
2
    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
  .mail_forward
2
  .mail_notify
2
  .moderated          (see `help MR-access')
2
  .moderator_forward
2
  .moderator_notify
36
5
4
24
2
Sending Mail
2
------------
2
$mail_agent:send_message(from,recipients,headers,body)
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
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
  (4) All notifications are delivered (using :notify_mail())
2

2
We return {0, @failed_recipients} if we bombed out at step 1.
2
Otherwise return {1, @actual_rcpts} indicating what mail was sent.
36
5
4
13
2
Mail System
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-resolve     -- how mail forwarding/notification works, in gory detail
2
receiving-mail   -- what :receive_message should do
2
mail-format      -- format of transmitted messages
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
36
5
4
34
2

2
Database of Players
2
-------------------
2
This is an instance of the Generic Database ($generic_db) that
2
holds the {name/alias,#objectid} pairs for every 
2
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
2
: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 
2
to get out of synch with reality.  In particular, there is currently no way
2
to block someone writing his own player :recycle() verb that neglects to 
2
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.
36
5
4
2
2
*index*
2
Core Utility Help Topics
36
5
4
64
2

2
Which :match...() Verb Do I Call?
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
     -- what you get looking for something that is inside LOC and named "X".
2
        By default, this looks through LOC.contents to find a unique object 
2
        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
(*)  LOC:match_object("X", YOU)           [YOU defaults to player]
2
(*)  YOU:my_match_object("X", LOC)        [LOC defaults to player.location]
2
     -- what YOU get being located at LOC and looking for something named "X".
2
        By default these both return $string_utils:match_object("X",LOC,YOU)
2

2
(*)  $string_utils:match_object("X", LOC, YOU) 
2
   -- what you *would* get *if* YOU were a typical player, YOU were inside LOC,
2
      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
    (1)  check for "X" being one of "", "me", "here", "$something", or "#n"
2
    (2)  try YOU:match("X") i.e., something in your inventory (maybe)
2
    (3)  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

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

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

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

2
Object Matching Failures
2
------------------------
2
As with other matching routines, one gets back 
2

2
  $failed_match in the case of no matching object
2
  $ambiguous_match in the case of more than one matching object
2
  $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
36
5
4
15
2
$no_one
2
-------
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().
2
In fact his sole purpose is to evaluate questionable code.
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) 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);  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.
2
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}).
36
5
4
19
2
Exits
2
-----
2
An exit can be renamed by either the owner of the exit or the owner of its source.
2

2
The standard verbs that are called in exit movement are:
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.
36
5
4
81
2
The Generic Room ($room)
2
----------------
2

2
(1)  Announcements
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
(2)  Command recovery
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
(3)  Residency
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

2
(4)  Looking
2

2
dark  - true => contents are not visible
2
ctype - 0..3 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
(5)  Entrance and exit.
2

2
:accept (object) - Called by move() and :moveto() before an object enters a room, if false is returned, movement is prevented.  Protocol permits this verb to make noise (though this is discouraged) as this is the only place the room will learn the object's original location.
2

2
:acceptable (object) - Called by verbs which wish to check whether movement will be possible.  Protocol prohibits this verb from making noise and requires it to return the same value as :accept would for the same arguments.
2

2
:is_unlocked_for (object) - interface with the @lock protocol.  Returns true or false depending on the state of locks for the object with the room.  Other things may prevent entrance even if this returns true.  Protocol prohibits this verb from making noise. 
2

2
:enterfunc (object) - called after entrance has succeeded.  Noise is fine.
2

2
:exitfunc (object) - called after an object has successfully left.  Noisemaking is fine.
2

2
(6)  Topology and Movement via Exits
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
(7)  Ejection
2

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

2
@eject
2

36
5
4
2
2
*forward*
2
$generic_help
36
5
4
79
2
Generic Database
2
----------------
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
Verbs supplied include
2

2
  :find(string)          => datum, $ambiguous_match or $failed_match
2
  :find_key(string)      => full string key,  $ambiguous_match or $failed_match
2
  :find_exact(string)    => datum or $failed_match (no partial matches)
2
  :find_all(string)      => list of all data corresponding to matching strings
2
  :find_all_keys(string) => list of all matching strings
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([4|3])
2
       removes all associations from the database.
2
       optional argument changes the type of the database 
2
       (4 is normal, 3 is a kludge for when the data are simply boolean flags
2
        i.e., this is a set of strings rather than a string-indexed array;
2
        more on this below)
2

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

2
N.B.  As entries get made, properties belonging to $generic_db.owner will be created on the db object itself.  These properties will be created having flags as specified by .node_perms, which by default is "r", but can be changed to "" should you want to ensure that randoms don't have access to the raw information.
2

2
Implementation notes
2
 - - - - - - - - - -
2
The 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.
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(3).  :clearall(4) 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.  3 and 4 are currently the only db types allowed.
36
5
4
201
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 the 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,
2
     just do (player in this.active) instead of this:loaded(player).  
2
     If you don't want your editor to make this distinction at all, do
2

2
        @stateprop texts={} for <youreditor>
2

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.  
2

2
Also be careful about suspend() and verbs that call suspend().  In particular, the player's index in the .active list can change during the suspend interval, so you must be sure to obtain the index (e.g., using :loaded()) again after the suspend() returns.
2

2
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).  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, initialize the property to {} and 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.  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 listset() call.  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.  
2

2
Sometimes you may wish to @stateprop a new property on an editor where active editing sessions exist.  @stateprop will fail if the property in question does not hold a list of the correct length (== length(editor.active); one value for each editing session).  You need to either give the @flush command to clear out all sessions and boot all players currently in the editor or somehow manually initialize the property to a list of appropriate values and pray that nobody enters/exits the editor between the property initialization and the @stateprop command --- this problem can be avoided by doing an eval() that does all of the initializations (beware of suspends()) and calls :set_stateprops directly.
2

2
Incidentally, the @flush command may be used at any time to clean out the 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 $generic_editor 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.
36
5
4
87
2
The Help System
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 three 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
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
  :dump_topic(string)
2
     like get_topic, but instead returns the raw text of a help topic
2
     as a (download/upload) script
2

2
In short if :find_topic reports that a particular db knows about a given topic
2
it returns the full topic name, so that :get_topic may be called on it later.
2
:dump_topic is used by maintainers (see $wiz:@gethelp) to edit help topics.
2

2
$generic_help and $help
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 where dblist is the list of 
2
      help objects that would have been consulted had the topic not been found 
2
      on this object.
2

2
  this.(topic) = other list of strings 
2
      multi-line help text
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
  {"*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
       In order for your help database to appear in the list presented
2
       by 'help index', your db object must be set to +r.
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*", object, verbname}
2
     - gets the 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.
36
5
4
62
2
Generic Option Package
2
----------------------
2
It occasionally happens that one has a command or set of commands for which one wishes to provide several options/flags that a player can set to customize the command's behavior for him/herself.  Making each option a separate property is a bit expensive, especially when the option in question is merely a boolean flag that gets 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
 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... --Rog)
36
5
4
24
2
Message Sequences
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).
36
5
4
24
2
Read verbs
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
  Does a @mail listing of the given message sequence.  If current message
2
  number is given and the sequence includes it, we mark it with a `>'.
2
  Likewise if the sequence includes any new messages (i.e., dated after 
2
  last_read_date), these are also indicated as such.
2

2
display_seq_full (message sequence, preamble)
2
  Does a @read listing of the given message sequence.  Each message is preceded
2
  by preamble.
2
  => {new current message number, new last_read_date}
2

2
:messages_in_seq (index)
2
  => {n, msg}
2
:messages_in_seq (message sequence)
2
  => {{n_1,msg_1},{n_2,msg_2},...}
2
  where the n_i are message numbers and the msg_i are messages in transmission
2
  format (see `help mail-format')
2

2
:list_rmm ()
2
  Does an `@unrmm list' listing of messages in .messages_going
36
5
4
17
2
Write verbs
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

36
5
4
25
2
Search verbs
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.
36
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.
36
5
4
28
2
$recycler
2
=========
2

2
Rather than having the server built-in recycle() and create() functions handle the creation and destruction of objects, a recycling center has been created to simulate these actions by  changing objects that would have been recycled into children of $garbage (The Generic Garbage Object) and making them owned by Hacker, and then when they're needed again, to avoid a raw create() command, those objects are given to whoever's asking for them.
2

2
Most Useful Verbs
2
-----------------
2

2
$recycler:_recycle( object )
2
  This will effectively recycle an object. (As a point of fact, it changes ownership of the object to Hacker and makes the object a child of $garbage.)  It handles .ownership_quota and .owned_objects properly.  Generally, use this instead of a recycle() in your verbs.
2

2
$recycler:_create( parent object [ , new owner object ] )
2
  This effectively creates an object (with the specified parent, if possible, and with the specified owner, if possible; these are the same restrictions as on the server create() builtin).  This is what should generally be used instead of create() in your programming.
2

2
$recycler:valid ( object )
2
  This is a variant of the server built-in valid() except that it handles the $garbage objects as well.  It returns a 1 if the object specified -is- valid and is -not- a $garbage object.
2

2
Other Notes
2
-----------
2

2
request <object> from <recycler>
2
  This is not an internal verb (it's !x).  It is, however, a command-line verb that can be used to request a specific object from the recycler.  It's also useful for the creation of objects like a Magic Number Repository.  When the object is removed from the recycler, the .announce_removal_msg is announced to the room if it's set (it's piped through $string_utils for pronoun substitution).
2

2
show-history <recycler>
2
  This is a wizardly verb which allows wizards to check the `history list' of the recycler. The history maintains the latest ($recycler.nhist) entries.
2

2
$recycler.orphans
2
  This maintains a list of objects for which the recreation process got mangled. It ought to be checked every once in a while to see what's up.
2
1
4
10
2
$error
2
======
2

2
The Error Generator, $error, may be used to automatically generate errors. This is particularly useful if you are working in a !d verb but have occasion to -want- to crash with traceback. To raise a specific error, use $error:raise(error type) -- for example, $error:raise(E_PERM) will produce traceback resulting from a Permission Denied error.
2

2
Random notes about $error:
2

2
+ The complete list of errors is stored in $error.names.
2
+ The seemingly useless :accept() verb on $error is so that $error:E_RECMOVE and $error:E_NACC will be guaranteed success (success meaning, of course, a termination by traceback).
2
+ There is, unfortunately, no way to raise the error E_NONE.
2
1
4
72
2
Generic BigList Utilities
2
----------------------------
2
$biglist is a collection of routines 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 $biglist one; $biglist 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 $generic_biglist_home or $big_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.
36
1
4
17
2
$guest_log
2

2
records guest connect/disconnect events.
2

2
.max_events  -- maximum number of connect/disconnect events kept
2
.connections -- actual list of events, the most recent ones first
2
    each element is of the form
2
      {object, is_connect, time, site}
2
    object is the particular guest that (dis)connected
2
    is_connect 1 or 0 according as this is a connect or not.
2

2
:find(who,when)
2
  => site name of WHO at the particular time
2
     (or 0 if WHO was not logged in then)
2

2
:last(n) 
2
  prints a listing of the last n events
36
5
4
2
2
*forward*
2
mail-resolve
36
5
4
32
2
*subst*
2
$news
2
-----
2
This object is a mail_recipient like any other (see `help $mail_recipient').  One may send messages to it and use the usual recipient manipulation commands (@mail, @read, @rmm,...).  
2

2
Selected messages on this recipient are ordered in reverse date order (i.e., most recent first) and concatenated to produce the "news" that is printed in response to the player `news' command.  Each news item is thus a distinct message.  The default $player:confunc checks to see if any new messages have been selected for inclusion in the "news" since the player last read the news.
2

2
The property $news.current_news holds the message sequence of messages currently considered as comprising "the news".  The following commands are used to change .current_news:
2

2
  @addnews <message-seq> to %[$news]
2
  @rmnews <message-seq> from %[$news]
2
  @setnews %[$news] to <message-seq>
2

2
@addnews includes the specified messages, @rmnews excludes the specified messages, and @setnews changes .current_news to be the given message sequence.  Note that these ONLY change whether a given message is marked as being "in the news" and do not actually add or remove messages from the mail recipient $news.
2

2
The procedure for adding a news article is
2

2
  Send a mail message to %[$mail_agent:name($news)]
2
  @addnews last to %[$news]
2
    This marks the message as belonging to the current newspaper.
2
    This also announces to any player who have not yet read this message 
2
    that there is a new version of the newspaper.
2

2
The preferred method for updating a news items is to send a new version of the item, @rmnews the old version and @addnews the new one.
2

2
The following ordinary mail commands behave differently
2
  @rmm   removes any reference to message from .current_news 
2
         in addition to removing the message itself from .messages
2
  @unrmm completely undoes the effect of the previous @rmm; 
2
         this includes restoring .current_news.
2

2
By default, the newspaper is moderated, however there is the possibility of unmoderating it (see `help MR-access'), letting arbitrary players send mail to it; administrators could then @addnews those items deemed worthy.
36
5
4
2
2
*forward*
2
object-matching
36
5
4
32
2
The mail messages stored in mail recipients may be removed automatically by a weekly expiration task. New core owners who wish to turn this feature on and cut down on the db space used by mail recipients (Remember, players are mail recipients too) should start this task:
2

2
    $wiz_utils:expire_mail_weekly
2

2
When this task runs, it searches through all mail recipients and checks their .expire_period property. This property contains a time, in seconds, that messages should expire after. If this property is set to 0, then no expiration will take place from that recipient. The task, then, will compile a list of messages that are older than the expire period (e.g., if the property is set to 30 days, messages older than 30 days from the current date will expire). The default expiration period for mail recipients is 30 days.
2

2
Normally, when the mail messages are expired from the recipient they are deleted. However, the owner of the recipient may elect to have the messages mailed to an email address prior to their erasure. There are two methods for doing this, one for players and one for children of $mail_recipient.
2

2
Players:
2

2
  Players may completely turn off mail expiration by setting the mail option `expire' off. If it is on, then $mail_agent.player_expire_time is the expire period used for mail on players. 
2
  If a player has a registered email address, he may simply turn the mail option `no_auto_forward' off in order to have the expired messages sent to his email address before they are deleted. (The reversed logic saves on db space). See `help mail-options' and `help @registerme'.
2

2
Mail Recipients:
2

2
  The command to set the expiration period of a mail recipient is:
2

2
    @set_expire <recipient> to <time>
2

2
The <time> specified can be in english format (30 days, 60 days, etc.). If left off (the owner types `@set_expire <recipient> to'), the command will print out the current expiration information for that recipient. A list owner may set the expiration period up to a maximum of 180 days.
2

2
  Similar to players, the owner of a mail recipient may have the mail sent to either his registered email address or to any other address he specifies before it expires. To prevent unsolicited email from going to a random address, confirmation must be made before the owner may set the address to something other than his registered address.
2

2
  The command is:
2

2
    @register <recipient> to <address>
2

2
If the owner leaves off the <address>, the current registered address (if any) and expiration period will be returned. Again, if the address specified is the player's registered address, nothing more need be done. If it is not, then an email message will be sent to that address containing a password. Presumably, the owner can retrieve that password and then log back into the MOO and type:
2

2
   @validate <recipient> with <password>
2

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

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

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

2
To convert an existing $mail_recipient-child (call it #MR) into a $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);
2

2
Reconstructing Damaged Big Mail Recipients
2
------------------------------------------
2
On rare occasions, the tree structure created by $biglist can be corrupted (this can happen on lists sufficiently large that a list-modification operation (e.g., @rmm, @renumber) runs out of ticks/seconds).  In the vast majority of such cases, your messages are all still there; it's simply that the tree we use for finding/searching them is messed up.
2

2
To recover messages from a damaged big mail recipient (#DBMR)
2
 --- read to the end before you start typing any commands ---
2

2
create a fresh $big_mail_recipient (#NEWBMR) and then do the following:
2

2
   ;#NEWBMR:restore_from(#DBMR)
2

2
When this finishes, #NEWBMR will contain all of the mail messages we were able to find.  (note that this will include messages that you had deleted from #DBMR but not expunged).  #NEWMBR should thenceforth be useable in place of #DBMR, however if #DBMR contains custom verbs and non-clear properties, these will also need to be copied over.
2

2
Alternatively, one may do
2

2
   @copyobject #DBMR to #TEMPBMR
2
   ;#DBMR:restore_from(#TEMPBMR)
2

2
to rebuild #DBMR in place.  This, however, will take about twice as long.
2

2
oooooooooooooooooooooooooooooooo
2
WARNING!!! WARNING!!! WARNING!!!
2
oooooooooooooooooooooooooooooooo
2

2
Calling #OBJ:restore_from(...) COMPLETELY AND IRREVOCABLY REMOVES ALL MESSAGES from the object that it is run on (#OBJ); you MUST be sure to EITHER have made a copy of #OBJ OR be doing the restore to a DIFFERENT object.
36
5
5
36
5
4
1
2
core-index
36
1
5
36
4
4
1
2
Core Utility Help
36
5
2
Help database for LambdaCore utility objects and generics.
36
5
4
2
0
94672
0
1001876092
36
1
5
36
5
5
36
5
#20
string utilities

24
2
-1
-1
-1
79
-1
55
71
space
36
173
-1
left
36
173
-1
right
36
173
-1
centre center
36
173
-1
columnize columnise
36
173
-1
from_list
36
173
-1
english_list
36
173
-1
names_of
36
173
-1
from_seconds
36
173
-1
trim
36
173
-1
triml
36
173
-1
trimr
36
173
-1
strip_chars
36
173
-1
strip_all_but
36
173
-1
uppercase lowercase
36
173
-1
capitalize capitalise
36
173
-1
literal_object
36
173
-1
match
36
173
-1
match_str*ing
36
173
-1
match_object
36
173
-1
match_player
36
173
-1
match_player_or_object
36
173
-1
find_prefix
36
173
-1
index_d*elimited
36
173
-1
is_integer is_numeric
36
173
-1
ordinal
36
173
-1
group_number
36
173
-1
english_number
36
173
-1
english_ordinal
36
173
-1
english_ones
36
173
-1
english_tens
36
173
-1
subst*itute
36
173
-1
substitute_d*elimited
2
13
-1
_cap_property
2
173
-1
pronoun_sub
2
173
-1
pronoun_sub_secure
36
173
-1
pronoun_quote
36
173
-1
alt_pronoun_sub
2
13
-1
explode
36
173
-1
words
36
173
-1
word_start
36
173
-1
to_value
36
173
-1
prefix_to_value
36
173
-1
_tolist
36
173
-1
_unquote
36
173
-1
_toscalar
36
173
-1
parse_command
2
173
-1
from_value
2
173
-1
print print_suspended
36
173
-1
reverse
36
173
-1
char_list
36
173
-1
regexp_quote
36
173
-1
connection_hostname_bsd
36
173
-1
connection_hostname
36
173
-1
from_value_suspended
2
173
-1
end_expression
36
173
-1
first_word
36
173
-1
common
36
173
-1
title_list*c list_title*c
36
173
-1
name_and_number nn name_and_number_list nn_list
36
173
-1
columnize_suspended columnise_suspended
36
173
-1
a_or_an
36
173
-1
index_all
36
173
-1
match_stringlist match_string_list
36
165
-1
from_ASCII
36
173
-1
to_ASCII
36
173
-1
abbreviated_value
36
173
-1
_abbreviated_value
36
173
-1
match_suspended
36
173
-1
incr_alpha
36
173
-1
is_float
36
173
-1
8
digits
ascii
alphabet
use_article_a
use_article_an
tab
full_ascii
latin1
15
2
0123456789
2
5
2
 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
2
5
2
abcdefghijklmnopqrstuvwxyz
2
5
4
5
2
unit
2
unix
2
one
2
once
2
utility
36
1
4
0
36
1
2
	
2
5
2

2
5
2

2
5
4
97
2
For a complete description of a given verb, do `help $string_utils:verbname'
2

2
    Conversion routines:
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
:from_value   (value [,quoteflag [,maxlistdepth]])   => "{foo1, foo2, foo3}"
2
:print        (value)                                => value in string
2
:abbreviated_value (value, options)                  => short value in string
2

2
:to_value       (string)     => {success?, value or error message}
2
:prefix_to_value(string)     => {rest of string, value} or {0, error message}
2

2
:english_number(42)          => "forty-two"
2
:english_ordinal(42)         => "forty-second"
2
:ordinal(42)                 => "42nd"
2
:group_number(42135 [,sep])  => "42,135"
2
:from_ASCII(65)              => "A"
2
:to_ASCII("A")               => 65
2
:from_seconds(number)        => string of rough time passed in large increments
2

2
:name_and_number(obj [,sep]) => "ObjectName (#obj)"
2
:name_and_number_list({obj1,obj2} [,sep])
2
                             => "ObjectName1 (#obj1) and ObjectName2 (#obj2)"
2
:nn is an alias for :name_and_number.
2
:nn_list is an alias for :name_and_number_list.
2

2
    Type checking:
2

2
:is_integer   (string) => return true if string is composed entirely of digits
2
:is_float     (string) => return true if string holds just a floating point
2

2
    Parsing:
2

2
:explode (string,char) -- string => list of words delimited by char
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 => list {first word, rest of string} or {}
2
:char_list  (string)   -- string => list of characters in string
2

2
:parse_command (cmd_line [,player] => mimics action of builtin parser
2

2
    Matching:
2

2
:find_prefix  (prefix, string-list)=>list index of element starting with prefix
2
:index_delimited(string,target[,case]) =>index of delimited string occurrence
2
:index_all    (string, target string)          => list of all matched positions
2
:common       (first string, second string)  => length of longest common prefix
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, location) => object then player matching
2
:literal_object (string)                       => match against #xxx, $foo
2
:match_stringlist (string, targets)            => match against static strings
2
:match_string (string, wildcard target,options)=> match against a wildcard
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[,lfiller[,rfiller]]) => centered string in field
2
:columnize/se  (list,n[,width])        => list of strings in n columns
2

2
    Substitutions
2

2
:substitute (string,subst_list [,case])   -- general substitutions.
2
:substitute_delimited (string,subst_list [,case])
2
                                          -- like subst, but uses index_delim
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

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
:names_of     (list of OBJ)  => string with names and object numbers of items.
2
:a_or_an      (word)         => "a" or "an" as appropriate for that word.
2
:reverse      (string)       => "gnirts"
2
:incr_alpha   (string)       => "increments" the string alphabetically
2

2
    A useful property:
2

2
.alphabet                    => "abcdefghijklmnopqrstuvwxyz"
2

2
Suspended versions (with _suspended at end of name) for
2
     :print     :from_value     :columnize/se      :match
2
5
5
2
4
4
2
2
string
2
utils
2
5
4
1
2
This is the string utilities utility package.  See `help $string_utils' for more details.
2
5
4
2
0
74932
0
1001876092
36
1
5
2
5
5
2
5
#21
building utilities

24
2
-1
-1
-1
79
-1
33
10
make_exit
2
173
-1
set_names
2
173
-1
recreate
2
173
-1
parse_names
2
173
-1
audit_object_category
2
173
-1
object_audit_string
2
173
-1
do_audit do_prospectus
2
173
-1
do_audit_item
2
173
-1
size_string
2
173
-1
init_for_core
2
173
-1
2
classes
class_string
9
4
45
1
1
1
3
1
4
1
5
1
6
1
7
1
8
1
9
1
13
1
14
1
30
1
37
1
40
1
45
1
50
1
54
1
58
1
68
1
73
1
74
1
79
1
90
1
91
1
92
1
94
1
96
1
97
1
98
1
99
1
100
1
101
1
102
1
103
1
104
1
105
1
108
1
109
1
111
1
113
1
115
1
126
1
128
1
131
1
136
1
138
2
5
4
12
2
p
2
R
2
E
2
N
2
C
2
T
2
F
2
M
2
H
2
D
2
U
2
O
2
5
4
13
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
5
5
2
4
4
2
2
building
2
utils
2
5
4
1
2
This is the building utilities utility package.  See `help $building_utils' for more details.
2
5
4
2
0
12730
0
1001876092
36
1
5
2
5
5
2
5
#22
Programmer Help

16
2
-1
-1
-1
30
-1
19
2
errors
2
173
-1
prepositions
2
173
-1
72
@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
mail
#
@programmer-options
@programmeroptions
@progoptions
@prog-options
options
utils
@killquiet
scattering
.flush
@rmverb#
@list#
@program#
@args#
@chmod#
@rename#
@addalias#
@add-alias#
@rmalias#
@rm-alias#
Writing_Verbs
Xpress_Program_Editor
debugging
80
4
2
2
*forward*
2
@check-chparent
2
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 fertile or likewise owned by you.
2

2
For objects with large numbers of descendants, these commands can be time-consuming.
2
5
4
2
2
*forward*
2
@grep
2
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.  See also the built-in function match()/rmatch().
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.
2
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.
2
5
4
9
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
@grep searches the given object(s) for verbs whose verbcode contains the given string as a substring of one of its lines.  
2
@egrep searches the given object(s) for verbs whose verbcode contains a substring matching the given regular expression (see `help regular-expressions').
2
5
4
2
2
*index*
2
Programmer Help Topics
2
5
4
15
2
*pass*
2
help
2

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'.
2
5
4
3
2
*prepositions*
2
The complete list of prepositions recognized by the command-line parser:
2

2
5
4
2
2
*forward*
2
eval
36
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
2
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.
36
1
4
17
2
A task is an execution of a MOO program.  There are five ways for tasks to be created in LambdaMOO:
2
   + Every time a player types a command, a task is created to execute that command; we call these 'command tasks'.
2
   + 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
   + 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
   + 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
   + 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
36
1
4
61
2
The following kinds of statements exist in the MOO programming language:
2

2
        ;
2
The null statement does nothing.
2

2
        expression ;
2
The expression statement evaluates the expression and then discards the value.
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
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
        FOR name IN ( expression ) statements ENDFOR
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
        FOR name IN [ expression .. expression ] statements ENDFOR
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
        WHILE ( expression ) statements ENDWHILE
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
        BREAK ;
2
        BREAK name ;
2
Each `break' statement indicates a specific surrounding loop; if <name> is not given, the statement refers to the innermost one. If it is given, <name> must be the name appearing right after the `for' or `while' keyword of the desired enclosing loop. When the `break' statement is executed, the indicated loop is immediately terminated and executing continues just as if the loop had completed its iterations normally.
2

2
        CONTINUE ;
2
        CONTINUE name ;
2
Allows you to terminate just the current iteration of a loop, making it immediately go on to the next one if any. 
2

2
        RETURN ;
2
        RETURN expression ;
2
The return statement evaluates 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
        TRY
2
          statements-0 ;
2
        EXCEPT variable-1 (codes-1)
2
          statements-1 ;
2
        EXCEPT variable-2 (codes-2)
2
          statements-2 ;
2
        ... (up to 255)
2
        ENDTRY
2
Each of the `statements-x' may be any number or combination of MOO statements and function calls. Each of the `codes-x' may be either the keyword `ANY' or else a comma-separated list of expressions that yield error codes. If the execution of `statements-0' raises an error listed in the `codes-x', then the statements associated in that EXCEPT clause where the code was listed are executed. When this occurs, `variable-x' is assigned this information about the error being raised:  {<error code>, <error message>, <value>, <traceback>}. If the error raised is not listed in any EXCEPT clause (which means the `ANY' keyword was not used), then the error continues to be raised. 
2

2
        TRY
2
          statements-0 ;
2
        FINALLY
2
          statements-last ;
2
        ENDTRY
2
In this construct, `statements-0' are executed. Then, whether an error was raised by their execution or not, `statements-last' are executed. If `statements-0' transfers control somewhere else, that transfer is interrupted so that `statements-last' can be run. If `statements-last' transfers control, then that overrides the first transfer. (Transfers include raising an error, returning from this verb, terminating the current iteration of a surrounding loop). `Statements-last' will always be executed, providing a good place for necessary cleanup code that will run even if `statements-0' doesn't simply run normally to completion.
2

2
        FORK ( expression ) statements ENDFORK
2
        FORK name ( expression ) statements ENDFORK
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.
36
1
4
26
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/@program -- entering the program for a verb
2
@list -- printing a listing of the program for a verb
2
@edit -- editing verb code
2

2
@show     -- looking at all the details of an object, a property, or a verb
2
@parents  -- listing the ancestors of an object
2
@kids     -- listing the children of an object
2
@contents -- listing the contents of an object
2
@chmod    -- changing the permissions on an object, a property, or a verb
2
@chparent -- changing the parent of an object
2
@rename   -- changing the name of a verb or object
2

2
eval -- executing MOO statements and expressions without writing a verb
36
1
4
18
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
        +       -
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))
36
1
4
5
2
The MOO programming language is described in excruciating detail in the LambdaMOO Programmer's Manual, available for FTP from ftp.parc.xerox.com in the file pub/MOO/ProgrammersManual.txt, and on the Worldwide Web at ftp://ftp.parc.xerox.com/pub/MOO/html/ProgrammersManual_toc.html.  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
36
1
4
139
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, ...)   -- calling a verb defined on this object's parent
2

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
eval(string)     -- parsing and executing strings as MOO code
2

2
typeof(value)      -- determining the data type of a value
2
 tostr(value, ...) -- converting any set of values into a string
2
 toint(value)      -- converting any non-list value into an integer
2
 tonum(value)      -- converting any non-list value into an integer (obsolete)
2
 tofloat(value)    -- converting any non-list value into a floating-point
2
 toobj(value)      -- converting any non-list value into an object
2
 toliteral(value)  -- converting any value into a literal string
2
length(value)      -- returns the length of a string or list
2

2
equal(val1, val2)          -- is val1 indistinguishable from val2
2
min(n1, n2, ...)           -- minimum of n1,n2,...
2
max(n1, n2, ...)           -- maximum of n1,n2,...
2
abs(n)                     -- absolute value of n
2
sin(n), cos(n), tan(n)     -- sine, cosine, tangent of n
2
asin(n), acos(n), atan(n)  -- arc-sine, arc-cosine, arc-tangent of n
2
sinh(n), cosh(n), tanh(n)  -- hyperbolic sine, cosine, tangent of n
2
exp(n)                     -- `e' raised to the power of n
2
log(n), log10(n)           -- natural or base 10 logarithm of n (n > 0)
2
sqrt(n)                    -- square root of n, rounded down
2
random(n)                  -- random integer between 1 and n inclusive
2
floatstr(float, precision, sci) -- format a floating-point into string
2
ceil(f)          -- smallest integer > float f as a floating-point
2
floor(f)         -- largest integer < float f as floating-point
2
trunc(f)         -- truncate f at the decimal point, as floating-point
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
match(str1, str2 [, case-matters])  -- match first regular expr str2 in str1
2
rmatch(str1, str2 [, case-matters]) -- match last regular expr str2 in str1
2
substitute(template, subs)          -- perform substitutions on template
2

2
decode_binary(bin-string [, fully]) -- convert from a binary string
2
encode_binary(arg, ...)             -- convert to a binary string
2
      crypt(string [, salt])        -- one-way string encryption
2
string_hash(text)                 -- MD5 cryptographically secure hash of text
2
binary_hash(bin-string)           -- same but for a binary string
2
 value_hash(value)                -- string_hash(toliteral(value))
2

2
 is_member(value, list)           -- is exact value (case sensitive) in 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

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-desc)   -- listing of server's internal `compile' 
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

2
open_network_connection(@args) -- open a connection to another network site (*)
2
notify(player, string)    -- sending text to a player's terminal
2
read()                    -- reading a line of input from the player (*)
2
buffered_output_length([player])      -- how much output pending
2
force_input(player, str [, at-front]) -- put str in player's output queue
2
flush_input(player [, show-messages]) -- clear a player's output queue
2
output_delimiters(player) -- return {prefix,suffix} set by PREFIX/SUFFIX cmds
2
set_connection_option(player, option, value) -- set I/O options for player
2
   connection_options(player) -- list current I/O options for player
2
    connection_option(player, option) -- return setting of option for player
2

2
   listen(conn, point [, print-messages]) -- create server listening point (*)
2
u nlisten(canon) -- close the server listening point described by canon (*)
2
listeners() -- list of all listening points
2

2
        raise(code [, message [, value]]) -- raise code just like an error
2
call_function(func-name, arg, ...)        -- call func-name
2
function_info([name])   -- descriptions of available built-in functions
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()      -- list of {obj, verb, owner, vloc, player}: this task's stack
2
suspend([secs])  -- suspending the current task for a number of seconds
2
resume(task [, value]) -- resume the given task with value returned
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()      -- a random number representing the currently-running task
2
queue_info([player]) -- who has tasks, or how many player has
2
queued_tasks() -- list of {id,start,0,20000,owner,obj,verb,line,this}
2
kill_task(id)  -- delete one of your tasks from the queue
2
task_stack(task-id [, include-line-numbers]) -- info about suspended task
2

2
server_log(string) -- add a comment to the server log file
2
server_version() -- a string of three numbers "major.minor.release"
2
memory_usage()   -- {{blocksize, nused, nfree}, ...}, the server's memory stats
2
shutdown(msg)    -- print msg and kill the server (*)
2
dump_database()  -- what it says (*)
2
db_disk_size()   -- size in bytes of recent full database
2

2
 value_bytes(value)  -- memory required to store value
2
object_bytes(object) -- memory required to store object
2

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

2
        number
2
        # integer
2
        # - integer
2
        "character string"
2
        error-name
2
Literal expressions return the obvious values: numbers (floating-point or integers), object numbers, strings, and errors.
2

2
        { expression , expression , ... , expression }
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
        name
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
            OBJ, STR, LIST, ERR, INT, FLOAT, NUM (same as INT)
2
            player, caller, this, verb, args
2
            argstr, dobj, dobjstr, prepstr, iobj, iobjstr
2
Their initial values are described in detail in the LambdaMOO Programmer's Manual.
2

2
        expression . name
2
        expression . ( expression )
2
        $ name
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
        expression : name ( arguments )
2
        expression : ( expression ) ( arguments )
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
        function ( arguments )
2
The function-call expression invokes one of the MOO primitive functions, as listed in 'help functions', passing the given arguments.
2

2
        expression [ expression ]
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 integer 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. The special character `$' maybe used for N as shorthand for the length of the string or list S.
2

2
        expression [ expression .. expression ]
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 integers.  If N1 <= N2, then both must be between 1 and the length of S, inclusive (the shorthand character `$' may be used); 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
        name = expression
2
        expression . name = expression
2
        expression . ( expression ) = expression
2
        $ name = expression
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
There is a special kind of assignment involving lists on the left hand side. See `help scattering' for details.
2

2
        expression + expression
2
        expression - expression
2
        expression * expression
2
        expression / expression
2
        expression % expression
2
        - expression
2
        expression ^ expression
2
The arithmetic expressions evaluate the subexpressions, all of which must return numbers of the same type (integer or floating-point), and then perform addition, subtraction, multiplication, division, remaindering, negation, or raising to a power, respectively.  For addition, the subexpressions may both return strings as well; in this case, the result is the concatenation of the two strings. For the last operation, raising to a power, if the first expression is an integer, the second must also be an integer. But if it is floating-point, then the second can be either floating point or an integer. This is the only type mixing permitted. You must do explicit type conversions with built-in functions (toint(), tofloat()) before evaluation mixed expressions on the other operations.
2

2
        expression == expression
2
        expression != expression
2
        expression < expression
2
        expression <= expression
2
        expression > expression
2
        expression >= expression
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
        expression ? expression | expression
2
        expression && expression
2
        expression || expression
2
        ! expression
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
        expression IN expression
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
        `expression-1 ! codes => expression-2'
2
NOTE: the open- and close-quotation marks are really part of the syntax; they must be typed in. `Codes' is either the keywoard ANY or a comma-separated list of expressions that when evaluated should yied a list of error codes to be caught if they're raised. If `expression-1' is evaluated without raising an error, then its value is the value of the entire expresion. If it raises an error that is listed in `codes', it is caught. If the `=> expression-2' part was included (it is optional), then it is evaluated and its value is the result of the entire expression. If `expression-2' was omitted, then the error is the value of the expression. If the error was not listed and caught, then the error contines to be raised.
2

2
The method for disambiguating the meaning of a complex MOO expression in the absence of sufficient parentheses is described in 'help precedence'.
2

36
1
4
37
2
Syntax:  eval <MOO-code>
2
         ; <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.
2

2
If you set the programmer option `eval_time' to 1 (see `help @prog-options'), then eval will print out how many ticks and seconds the program required.
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
   Haakon
2
   Wizard
2
   ArchWizard
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
You may customize your evaluation environment.  The player property .eval_env may contain statements to be executed prior to any evaluated program.  Two caveats:  This will throw off the tick count.  You can account for additional ticks inserted by your environment with the .eval_ticks property; just set it to the number of ticks you'd like subtracted from the total.  Additionally, if you make a syntax error in your program, the line reported will be wrong (it will count those initial statements), and if you make an error in the eval_env itself, you can be in deep trouble.  Despite these drawbacks, the eval_env property can be quite useful.  The following is a sample:
2

2
Eval_env:    "me=player;here=player.location;"
2
eval_ticks:  3
2

2
See also @setenv.
2

2
You can also define textual substitutions in a separate property, called eval_subs.  These are discouraged, however, for anything that can be done with variable assignments, because the overhead of replacing the strings in the evaluated program is significant.  However, some things, such as substituting characters which can't be typed easily on one keyboard (e.g. "[]" is difficult to type on some IBM keyboards), can only be done by textual substitutions.  Note that the eval substitutions are also interpreted by the verb editor when "eval_subs" is selected in your .edit_options property (see `help editors').  This adds to their overhead, but again, makes it possible for people to program who otherwise can't type the full character set.  Remember:  Don't use eval_subs unless you really have to!
36
1
4
3
2
*errors*
2
The complete list of error codes:
2

2
5
4
17
2
Syntax:  @verb <object>:<verb-name(s)>
2
         @verb <object>:<verb-name(s)> <dobj> [<prep> [<iobj>]]
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 wizards 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!)
36
1
4
8
2
Syntax:  @setenv <environment string>
2

2
Defines the environment for eval (property player.eval_env).  See "help eval"
2
for more information.
2

2
Example:
2

2
  @setenv me=player;here=player.location;
36
1
4
10
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
If there is more than one verb matching the given verb-name, this removes the most recently defined one.
2

2
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
To remove the ambiguity in verbs with the same name, @rmverb# is provided where you can specify the verb by its 1-based index in the verbs() list output. Use of this form is strongly encouraged.
2
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'.
2
5
4
35
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
   Objects owned by Frand (from #0 to #54949):
2
     P[ 23]    #47 Frand                          [Hyperspace Hovel]
2
     T        #152 Frand's trio of shoes          [Frand]
2
   KfT[ 10]   #391 Frand's notifier class         [Nowhere]
2
     T[  8]   #393 Frand's chessboard             [The Dining Room]
2
   KfT[ 11]   #775 Frand's generic game board     [Nowhere]
2
     T[  6]   #893 Ghost game                     [The Dining Room]
2
     T[ 16]   #894 Frand's mind bender            [The Dining Room]
2
     C        #997 polka-dot hole                 [Hyperspace Hovel]
2
     R[  1]  #1002 Hyperspace Hovel              
2
     E      #11958 out                            Monster Cage->*Dr. Empirico's Lab
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
        T       Thing
2
        E       Exit
2
        R       Room
2
        C       Container
2
        N       Note
2
        P       Player
2
        F       Feature
2
        M       Mail Recipient
2
        H       Help Database
2
        D       Database
2
        U       Utilities Package
2
        O       Options Package
2
        p       Parent object appropriate for players ("Player class")
2
        blank   Other
2
5
4
13
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 wizards can create properties with owners other than themselves.
2

2
'@property' can be abbreviated as '@prop'.
36
1
4
20
2
Syntax:  @program <object>:<verb-name>
2
         @program <object>:<verb-name> <dobj> <preposition> <iobj>
2
         @program# <object>:<verb-number>
2

2
Changes the MOO program associated with the named verb on the named object.
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
Or, you can use @program# if you know the verb's number. This is the 1-based index of the verb as it appears in the verbs() (or @verbs) output list.
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.
2
5
4
25
2
Syntax:  @list <object>:<verb>
2
         @list <object>:<verb> [with|without parentheses|numbers] [all]
2
         @list <object>:<verb> <dobj> <prep> <iobj>
2
         @list <object>:<verb> <start>..<end>
2
         @list# <object>:<verb-number> [any of the above]
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.  You can change the default behavior of this command via @prog-options (which see).
2

2
Normally, the @list command only shows the code for the named verb on the object itself or on the nearest ancestor that defines it; if you give the `all' option, it shows the code for the named verb on every ancestor that defines it, as well as on the object itself.
2

2
The 3rd form of the verb lists the verb matching the given dobj/prep/iobj specification if such exists.  
2
The 4th form prints only those lines in the specified range.
2

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

2
The 2nd-4th forms may be combined, e.g.,
2

2
  @list frobule:burfle this in front of any without numbers 1..10
2

2
which would be useful if `frobule' had more than one `burfle' verb and we wanted to see the first 10 lines of the one having `this' `in front of' `any' as its respective dobj/prep/iobj specifiers.
2

2
Or, if you know the verb's number, you can use @list# to unambiguously refer to the verb. The number comes from the 1-based index of the verb as it appears in the verbs() (or @verbs) output list.
36
1
4
26
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
@killq*uiet does all of the above without the pretty printout if more than one task is being killed.  It tells you the number of tasks that have been killed when it's finished.
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
36
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)
2
5
4
26
2
Syntax:  @forked[-v*erbose] [all wizards]
2

2
Gives a list of all of the forked tasks you own, along with detailed information about each one.  Wizardly characters may specify `all wizards' and see the queued tasks which are owned by a player with .wizard=1. This is useful to find a task that $wiz_utils:random_wizard has put in various queues, and to also find code which is running with wizardly permissions that shouldn't be.
2

2
The information displayed includes the following:
2

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

2
Start Time:
2
   The time after which the task will begin execution.
2

2
Owner:
2
   The player whose permissions under which the task is running.
2
   Unless you are a wizard, @forked will show only your tasks.
2

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

2
Line:
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
   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.
2

2
For a more verbose output, use `@forked-v*erbose'. This will display the same information as the above, but for any task which is not a fresh fork or foreground task, a callers()-style listing of the stack trace will be displayed.
36
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').
2
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.
2
5
4
14
2
Syntax:  @copy <object>:<verb> to [<newobject>][:<newverb>]
2
         @copy-move <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-move will delete the old verb after it has copied.  Useful for restructuring code/object hierarchies.
2

2
Examples:
2
  @copy me:verbname to myobject
2
  @copy me:test_verb to myobject:real_verb
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.
2
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.
2
5
4
28
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
        <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
The form @chmod# is used to unambiguously refer to a verb by its 1-based index as it appears in the verbs() (or @verbs()) output list.
2

2
To clear all of the permissions for an object, verb, or property, use "" as the second argument.
2

2
@chmod also accepts +, !, and - as modifiers for a single permission to add or subtract that permission from the current set.  (! and - are the same.)
2

2
Examples:
2

2
Set a verb to be Readable and Callable:
2
  @chmod chair:sit rx
2

2
Set a verb to be not Callable, without changing its other permissions:
2
  @chmod cookies:eat !x
2

2
Set an object to be Fertile in addition to any current bits:
2
  @chmod table +f
2
5
4
9
2
Syntax:  @args <object>:<verb-name> <dobj>
2
         @args <object>:<verb-name> <dobj> <prep>
2
         @args <object>:<verb-name> <dobj> <prep> <iobj>
2
         @args# <object>:<verb-number> [any above combinations]
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'.
2

2
To unambiguously refer to the verb on the object, in case there are more than one with the same name, use @args#. This takes the 1-based index of the verb as it appears in the verbs() (or @verbs()) output list.
2

36
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.
36
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
  => ""
2
5
4
2
2
*forward*
2
@disinherit
2
5
4
11
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
Note also that, though the name might seem to indicate otherwise, this command does not change the ownership of any object.
2
5
4
2
2
*forward*
2
@display-options
2
5
4
24
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
-thisonly      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
+thisonly      Specifying . (:) to retrieve all properties (verbs) will only
2
               display properties (verbs) defined on the object itself.
2
5
4
24
2
*pass*
2
@add-feature
2

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

2
 * E_INVARG if feature is not an object or is invalid
2

2
 * E_PERM if the object is not feature_ok
2

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

2
 * a true value otherwise
2

2
and calls feature:feature_remove, if the verb exists.
2
5
4
2
2
*forward*
2
@add-feature
2
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.
2
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.]
2
5
4
5
2
*pass*
2
mail
2
 - - - - -
2
See `help mail-system' for a description of the programming interface to the mail system.
2
In particular, see `help $mail_recipient' for information on creating new mail collections.
2
5
4
7
2
#<string>[.<property>|.parent] [exit|player|inventory] [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.
2
If neither .<property>|.parent nor <code> is specified, just return <thing>.
2
If .<property> is named, return <thing>.<property>.  .parent returns parent(<thing>).
2
If <code> is given, it is evaluated, with the value returned by the first part being substituted for %# in <code>.
2
For example, the command
2
  #JoeFeedback.parent player for tonum(%#)
2
will return 26026 (unless Joe has chparented since writing this).
2
5
4
2
2
*forward*
2
@prog-options
2
5
4
2
2
*forward*
2
@prog-options
2
5
4
2
2
*forward*
2
@prog-options
2
5
4
36
2
Syntax:  @prog-option
2
         @prog-option <option>
2

2
Synonyms:  @progoption, @programmer-option @programmeroption
2

2
The first form displays all of your programmer options
2
The second displays just that one option, which may be one of the flags listed below.  The programmer options control various annoying details of your programming commands (e.g., @list, eval, @copy, ...)
2

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

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

2
These respectively set and reset the specified flag
2

2
 -list_all_parens    @list shows only necessary parentheses by default
2
 +list_all_parens    @list shows all parentheses by default
2
 -list_no_numbers    @list gives line numbers by default
2
 +list_no_numbers    @list does not give line numbers by default
2
 -eval_time          eval does not show ticks/seconds consumed.
2
 +eval_time          eval shows ticks/seconds consumed.
2
 -copy_expert        @copy prints warning message.
2
 +copy_expert        @copy prints no warning message.
2

2
All flags default to the `-' settings.  
2
Finally, we have
2

2
         @prog-option verb_args [is] <dobj> <prep> <iobj>
2
         @prog-option verb_args="<dobj> <prep> <iobj>"
2
         @prog-option -verb_args
2
                      (equivalent to verb_args="none none none")
2
         @prog-option +verb_args
2
                      (equivalent to verb_args="this none this")
2

2
which all serve to specify the (direct/indirect)-object and preposition to use in a @verb command for which these are not given at all.
2
5
4
5
2
*pass*
2
options
2

2
  @prog-options    --- programming commands (@list, @verb, ...)
2
  @display-options --- @display (*)
2
5
4
2
2
*forward*
2
utilities
2
5
4
2
2
*forward*
2
@kill
36
5
4
54
2
It is often the case in MOO programming that you will want to access the elements of a list individually, with each element stored in a separate variables.  This desire arises, for example, at the beginning of almost every MOO verb, since the arguments to all verbs are delivered all bunched together in a single list.  In such circumstances, you could write statements like these:
2

2
first = args[1];
2
second = args[2];
2
if (length(args) > 2)
2
  third = args[3];
2
else
2
  third = 0;
2
endif
2

2
This approach gets pretty tedious, both to read and to write, and it's prone to errors if you mistype one of the indices. Also, you often want to check whether or not any extra list elements were present, adding to the tedium.
2

2
MOO provides a special kind of assignment expression, called `scattering assignment' made just for cases such as these. A scattering assignment expression looks like this:
2

2
{<target>, ...} = <expr>
2

2
where each <target> describes a place to store elements of the list that results from evaluating <expr>. A <target> has one of the following forms:
2

2
`variable'
2
   This is the simplest target, just a simple variable; the list element in the corresponding position is assigned to the variable.  This is called a `required' target, since the assignment is required to put one of the list elements into the variable.
2

2
`?variable'
2
   This is called an `optional' target, since it doesn't always get assigned an element. If there are any list elements left over after all of the required targets have been accounted for (along with all of the other optionals to the left of this one), then this variable is treated like a required one and the list element in the corresponding position is assigned to the variable. If there aren't enough elements to assign one to this target, then no assignment is made to this variable, leaving it with whatever its previous value was.
2

2
`?variable' = `default-expr'
2
   This is also an optional target, but if there aren't enough list elements available to assign one to this target, the result of evaluating `default-expr' is assigned to it instead. Thus, `default-expr' provides a default value for the variable. The default value expressions are evaluated and assigned working from left to right -after- all of the other assignments have been performed.
2

2
`@variable'
2
   By analogy with the @ syntax in list construction, this variable is assigned a list of all of the `leftover' list elements in this part of the list after all of the other targets have been filled in. It is assigned the empty list if there aren't any elements left over. This is called a `rest' target, since it gets the rest of the elements. There may be at most one rest target in each scattering assignment expression.
2

2
If there aren't enough list elements to fill all of the required targets, or if there are more than enough to fill all of the required and optional targets but there isn't a rest target to take the leftover ones, then E_ARGS is raised.
2

2
Here are some examples of how this works.  Assume first that the verb me:foo() contains the following code:
2

2
b = c = e = 17;
2
{a, ?b, ?c = 8, @d, ?e = 9, f} = args;
2
return {a, b, c, d, e, f};
2

2
Then the following calls return the given values:
2

2
me:foo(1)                        error-->   E_ARGS
2
me:foo(1, 2)                     =>   {1, 17, 8, {}, 9, 2}
2
me:foo(1, 2, 3)                  =>   {1, 2, 8, {}, 9, 3}
2
me:foo(1, 2, 3, 4)               =>   {1, 2, 3, {}, 9, 4}
2
me:foo(1, 2, 3, 4, 5)            =>   {1, 2, 3, {}, 4, 5}
2
me:foo(1, 2, 3, 4, 5, 6)         =>   {1, 2, 3, {4}, 5, 6}
2
me:foo(1, 2, 3, 4, 5, 6, 7)      =>   {1, 2, 3, {4, 5}, 6, 7}
2
me:foo(1, 2, 3, 4, 5, 6, 7, 8)   =>   {1, 2, 3, {4, 5, 6}, 7, 8}
2

2
Using scattering assignment, the example at the begining of this section could be rewritten more simply, reliably, and readably:
2

2
{first, second, ?third = 0} = args;
2

2
It is good MOO programming style to use a scattering assignment at the top of nearly every verb, since it shows so clearly just what kinds of arguments the verb expects.
2
5
4
5
2
Syntax:  .flush
2

2
Clear out all recent lines of input that haven't been processed yet by the server. Useful when you change your mind about lines you have typed that haven't run yet.
2

2
This command name can be changed by the `set_connection_option()' built-in function, or if $server_options.default_flush_command exists and is non-empty, that value is used. If it exists and -is- empty, then no flush command exists at all.
2
5
4
2
2
*forward*
2
@rmverb
2
5
4
2
2
*forward*
2
@list
2
5
4
2
2
*forward*
2
@program
2
5
4
2
2
*forward*
2
@args
2
5
4
2
2
*forward*
2
@chmod
2
5
4
2
2
*pass*
2
@rename
2
5
4
2
2
*pass*
2
@addalias
2
5
4
2
2
*pass*
2
@addalias
2
5
4
2
2
*pass*
2
@rmalias
2
5
4
2
2
*pass*
2
@rmalias
2
5
4
1
2
Help not available yet.
2
5
4
15
2
The Xpress Program Editor provides an easy and intuitive interface to programming in the MOO environment. The MOO comes with a built-in programming language that can be used in many creative ways, and the Xpress Program Editor makes it more powerful than ever. 
2

2
The Program Editor allows you to look at verbs and properties on objects, edit verb code and property values, as well as manipulate various permissions and flags. To use the Program Editor, you must first specify the name or number of the object you want to work with in the input field labeled Object in the top Program Editor menu. You can either type in the name of an object, or an object number. Please note that names of objects are only recognized if you are actually holding the object, that is, the object is in your inventory, or you are in the same room as the object. Special variables such as 'me' and 'here' are recognized. Object numbers, however, work system-wide. The Program Editor can also recognize special verb and property references. If you want to edit an existing verb on an object, type in the object name or number, followed by a colon and then type the verb name. (Example: coke:drink) Use the same syntax to reference properties except, use a period instead of a colon. (Example: coke.flavors).
2

2
When you click the View button, the object you have specified will be displayed in the bottom left part of the screen, and any verbs or properties on the object are displayed on the right. To program a new verb, click the button New Verb, and the Editor opens the new verb ready for editing. Give your verb a name, enter the verb code and click compile. If your code has errors in it, the compiler will tell you the type of error and where it occurred. To create a new property on the same object, simply click the button New Property, edit the property value and click Save Propery. To delete existing verbs or properties, select the item you want to delete from the list of verbs and properties, and click the Delete button in the editor form. 
2

2
Objects, verbs and properties all have certain permission flags that can be set in the Program Editor. For objects, the relevant flags are Read (R) and Fertile (F). If the R-flag is checked, then the object is readable by anyone, but editable only by the owner. If the F-flag is checked, then other people can use the object as a blueprint for creating new objects. 
2

2
For properties the permission flags are Read and Change (C). Read works in the same way as for objects, while the C-flag determines whether the property can be modified on objects that inherit the property. If for example, you want to create a property that should have the same value on all objects that inherit the property, then uncheck the C-flag to make it a constant.
2

2
For verbs the permission flags are Read, Execute (X) and Debug (D). If the R-flag is checked, then the verb code is readable by anyone. If the X-flag is checked, then the verb may be called by another verb, and if the D-flag is checked, then the MOO will print a trace back if the verb produces a runtime error.
2

2
If you are not certain what all this means at this point, just leave the flags at their default value. You can always modify them later if you need to.
2

2
The Program Editor will allow you to inspect all verbs and properties that are readable by you (i.e., all that have the R-flag checked). This means that you can look at readable verbs and properties on other peoples objects, but you will not be able to modify them. However, looking at other peoples verb code, in particular, is a good way to learn how to program in the MOO, so we encourage you to explore and learn as much as possible. The Program Editor also comes with Pavel Curtiss LambdaMOO Programming Manual for easy reference.
2
5
4
9
2
The compiler tells you if there is a problem with your verb, and where the problem is. The most common problems are parse errors due to missing or miss-aligned punctuation. Look at the line identified by the compiler, have you perhaps forgot a quote-mark? Look at the line directly above, have you remembered the semi-colon? If you cannot find any problems in the area pointed out by the compiler, chances are that you forgot to close a loop or statement. Check to make sure that every *if* has a corresponding *endif*, and so forth.
2

2
Other common errors are type mismatches. These occur when your variables does not have contents of an expected type. I.e. your variable is an integer when a string was expected. Type mismatch errors can occur both at compile time and runtime and might be difficult to identify. If your verb compiles okay, but throws a type mismatch exception on runtime, insert a few :tell statements in your code to display the value of your variables at different points.
2

2
Other common errors are references to non-existing verbs and variables. These are usually caused by typos and are relatively easy to spot and fix. 
2

2
Debugging complex verbs can be a frustrating experience, but keep in mind that even the best programmer make mistakes, and debugging is part of the fun of programming.
2

2
See LambdaMOO Programmer's manual for more on debugging.
2
5
5
2
5
4
1
2
prog-index
36
1
5
2
4
4
1
2
Programmer Help
2
5
2
This provides help on the programmer commands available on $prog and related topics.
2
5
4
2
0
93771
0
1001876092
36
1
5
2
5
5
2
5
#23
Wizard Help

16
36
-1
-1
-1
30
-1
22
0
57
@guests
@log
@egrep
@unnewt
@denewt
@newt
@grep
$site_db
graylist
blacklist
@recycle
wiz-index
@dump-database
@players
@net-who
@@who
make-core-database
@quota
@detoad
@untoad
@toad
@grepcore
@who-calls
@abort-shutdown
@shutdown
@programmer
@shout
@chown
redlist
@blacklist
@graylist
@redlist
@make-guest
@spooflist
spooflist
@make-player
@register
@new-password
@deprogrammer
forked-tasks
mail-lists
@grant
adding-help-text
further-reading
@temp-newt
site-info
recycling-players
advertised
news-items
routine_tasks
@chown#
enCore_MOO_administration
enCore_Preferences
enCore_Account_Creation
Xpress_Bookmark_Editor
xpress_MOTD_editor
Xpress_Generics_Editor
65
4
6
2

2
@guests now  [shows information about currently connected guests]
2
@guests all  [shows all entries in $guest_log]
2
@guests <n>  [shows the last <n> entries of $guest_log]
2

2
Note, some wizards prefer to use verbs on $guest_log manually, particularly :last().
36
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
36
5
4
2
2
*forward*
2
@grep
36
5
4
2
2
*forward*
2
@denewt
36
5
4
10
2
Syntax:    @denewt <player> [commentary]
2

2
Synonyms:  @unnewt
2
           @get-better
2

2
@denewt reverses the effects of @newt, moving a player'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.
36
5
4
28
2
*subst*
2
Syntax:  @newt <player> [commentary]
2
         @temp-newt <player> for <period>
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  [%[$wiz.newt_msg]]
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  [%[$wiz.newt_victim_msg]]
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.
2

2
The @temp-newt variant of @newt permits you to specify a time period during which this player may not use the MOO.  Time units must be acceptable to $time_utils:parse_english_time_interval.
36
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.
36
5
4
21
2
Database of places
2
------------------
2
i.e., places people have connected from.
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
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., namely 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.
36
5
4
2
2
*forward*
2
blacklist
36
5
4
35
2

2
The Site Blacklist
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
2
                .graylist merely annotates it in @net-who listings).
2
  .spooflist -- guests from these sites cannot use @request to request 
2
                a character
2

2
The lists are kept in a special format so it is highly recommended that you 
2
either use $wiz:@*list/@un*list or the following verbs to query/update the 
2
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', `gray', or `spoof'.
2

2
There are also temporary versions of the above four lists, stored in associated $login.temporary_*list in the same format, except two additional bits of data are stored.  The time the temporary *listing started, and the duration that it will last.  In addition there exists:
2

2
  $login:*list_add_temp(domain or subnet, start time, duration)
2
  $login:*list_remove_temp(domain or subnet)
2

2
When the normal $login:*listed verb is called, both the regular *list and the temporary *list are checked.  If the host is on the temporary list, then the length of MOO up time since the start time is checked against the duration.  If expired, the host is removed from the temporary *list and a false value is returned (meaning that the host is not *listed).
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
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.
36
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.
36
5
4
2
2
*index*
2
Wizard Help Topics
36
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.
36
5
4
3
2
Syntax:  @players [with objects]
2

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

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'), 
2
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.
36
5
4
2
2
*forward*
2
@net-who
36
5
4
3
2
Syntax:  make-core-database
2

2
...makes a core database (surprise).  Film at 11...
36
5
4
7
2
*pass*
2
@quota
2

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

2
This second and more interesting form of the verb is used to set a player's quota.  Mail will be sent to $quota_log, and also $local.public_quota_log if there is one and if the "public" argument is given; if a reason is supplied, it will be included in the message.  If the number is prefixed with a +, it's taken as an amount to add to the player's current quota; if not, it's an absolute amount.
36
5
4
2
2
*forward*
2
@untoad
36
5
4
13
2
Syntax:  @untoad <object> [as <name>,<alias>,<alias>...]
2

2
Synonym: @detoad
2

2
Turns the object into a player.  
2
If the name/alias... specification is given, the object is also renamed.
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.
2
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.
2
Note that the .wizard flag is not set under any circumstances.
36
5
4
26
2
*subst*
2
Syntax:  @toad   <player>  [graylist|blacklist|redlist]
2
         @toad!  <player>
2
         @toad!! <player>
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.
2
Also, this command does not let you @toad yourself.
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  [%[$wiz.toad_msg]]
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  [%[$wiz.toad_victim_msg]]
2
  Printed to the victim.
2

2
These are pronoun_subbed with victim == dobj.
36
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?)
36
5
4
2
2
*forward*
2
@grepcore
36
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.
36
5
4
5
2
Syntax:  @shutdown [in <m>] [<text>]
2

2
This is the friendly way to do a server shutdown; it arranges for the actual shutdown to take place `m' minutes hence (default two).  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.
36
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.
36
5
4
3
2
Syntax:  @shout <text>
2

2
Broadcasts the given text to all connected players.
36
5
4
14
2
Syntax:  @chown <object>            [to] <owner>
2
         @chown <object>.<propname> [to] <owner>
2
         @chown <object>:<verbname> [to] <owner>
2
         @chown# <object>:<verbnumber> [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. Referring to a verb isn't as straightforward since two verbs on the same object can have the same name. So, @chown# is provided where you can refer to a verb by it's 1-based index in the output of the verbs() builtin.
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.
36
5
4
2
2
*forward*
2
blacklist
36
5
4
18
2
Syntax:  @redlist   [<domain or subnet> [for <duration>] [commentary]]
2
         @blacklist [<domain or subnet> [for <duration>] [commentary]]
2
         @graylist  [<domain or subnet> [for <duration>] [commentary]]
2
         @spooflist [<domain or subnet> [for <duration>] [commentary]]
2

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

2
With no argument, the current contents of the corresponding 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
To add a domain or subnet to a *list only temporarily, include a `for <duration>' statement before any commentary.  The <duration> should be in english form such as 1 day or 1 month 2 weeks or 1 year 3 months 2 weeks 4 days.  No commas should separate increments in the duration.  See `help $time_utils:parse_english_time_interval' for more details.  If you are not temporarily *listing a domain or subnet, but are including a commentary, be sure that the commentary does not start with the word `for'.
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 lists.
36
5
4
2
2
*forward*
2
@blacklist
36
5
4
2
2
*forward*
2
@blacklist
36
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'.
36
5
4
2
2
*forward*
2
@blacklist
36
5
4
2
2
*forward*
2
blacklist
36
5
4
11
2
@make-player name[,aliases] [email-address [commentary]]
2
Creates a player.
2
Generates a random password for the player.
2
Email-address is stored in $registration_db and on the player object.
2
Comments should be enclosed in quotes.
2

2
Example: @make-player George sanford@frobozz.com "George shares email with Fred Sanford (Fred #5461)"
2

2
If the email address is already in use, prompts for confirmation.  If the name is already in use, prompts for confirmation.  (Say no, this is a bug: it will break if you say yes.)  If you say no at one of the confirming prompts, character is not made.
2

2
If network is enabled (via $network.active) then asks if you want to mail the password to the user after character is made.
36
5
4
5
2
Information about $wizard:@register
2
----
2
Registers a player.
2
Syntax:  @register name email-address [additional commentary]
2
Email-address is stored in $registration_db and on the player object.
36
5
4
4
2
@new-password player is [password]
2
Sets a player's password; omit password string to have one randomly generated.  Prints the encrypted old string when done for error recovery.  [No current software will allow you to give the encrypted string as input.]
2

2
Offers to send mail to the user with the new password, if the user has a registered email address and the network is enabled.
36
5
4
5
2
Information about $wiz:@deprog*rammer
2
----
2
@deprogrammer victim [for <duration>] [reason]
2

2
Removes the prog-bit from victim.  If a duration is specified (see help $time_utils:parse_english_time_interval), then the victim is put into the temporary list. He will be automatically removed the first time he asks for a progbit after the duration expires.  Either with or without the duration you can specify a reason, or you will be prompted for one. However, if you don't have a duration, don't start the reason with the word `For'.
36
5
4
3
2
If you are a wizard, '@forked' with no arguments will spam you with all the forked tasks that there are (this is useful sometimes, but it's nice to know ahead of time).
2

2
To see just your own, type '@forked me'.  To see just one player's, type '@forked <player>'.
36
5
4
9
2
You probably want to subscribe to (or at least be familiar with) the following mailing lists:
2

2
*Player-Creation-Log
2
*New-Prog-Log
2
*Quota-Log
2
*News
2
*Site-Locks
2
*Password-Change-Log
2

36
5
4
9
2
Information about generic wizard(#218):@grant/@grants*/@transfer
2
----
2
@grant <object> to <player>
2
@grants <object> to <player>   --- same as @grant but may suspend.
2
@transfer <expression> to <player> -- like 'grant', but evalutes a possible list of objects to transfer.
2

2
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).
2

2
This verb does the transfer whether the recipient has enough quota for it or not.
36
5
4
5
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'.
2

2
To get a list of the object numbers associated with various $help databases, type 'help index'.
2

2
If you need to modify existing help text, and need to find which help database the relevant property is defined on, use 'help full-index'.  (Note, it's spammy, but tells you what you need to know.)
36
5
4
7
2
Other topics of interest to wizards:
2

2
$login
2
$guest_log
2
$no_one
2
$recycler
2
$help
36
5
4
5
2
Information about $wiz:@temp-newt
2
----
2
@temp-newt victim [for duration] [reason]
2

2
Temporarily newts victim.  If a duration is specified (see help $time_utils:parse_english_time_interval), then the victim is put into the temporary list. E will be automatically removed the first time e tries to connect after the duration expires.  You will be prompted for a reason for the newting, but as of this writing, specifying a reason from the command line isn't an option.
36
5
4
1
2
To look at where a player is currently connecting from, use @netwho.  To see previous connect sites, look at <player>.all_connect_places.
36
5
4
7
2
General procedure:
2

2
  Make sure e doesn't own anything.
2
  @toad em
2
  @recycle em
2

2
It makes a real mess if you don't clean up .owned_objects.  See $wiz_utils:initialize_owned, but note, running this verb takes maybe three hours (at last report) and adds to lag.  This is why we frown so severely on leaving blood on the carpet.
36
5
4
9
2
Some wizards choose not to be among those listed when a player types '@wizards' (or similar).
2

2
The property $wiz.advertised defaults to 1; set it to 0 to remove yourself from the list.
2

2
To keep your non-wizard character off the list, set your wizard character's .public_identity character to 0.  To get it back on, set .public_identity to the object number of your non-wizard character.
2

2
$wiz_utils:is_wizard returns true for the wizard and the corresponding .public_identity player.  Both will likewise appear in $wiz_utils:connected_wizards_unadvertised() and $wiz_utils:all_wizards_unadvertised().
2

2
:is_wizard is for checking permissions on wizard feature-objects and the like, while :all/connected_wizards_unadvertised wouble be for things like wizard-shouts (e.g., the one issued by $player:recycle).
36
5
4
12
2
*subst*
2
To add a news item:
2

2
Send regular mail to *news with the message you want in the news.  Then:
2

2
  @addnews <message-number> to %[tostr($news)]
2

2
To remove a news item:
2

2
  @rmnews <message-number> from %[tostr($news)]
2

2
Note, the message date doesn't show up, so you might consider adding a date to the message body itself.
36
5
4
29
2
There are a number of routine daily or weekly tasks that can help keep your MOO clean or otherwise well-maintained.
2

2

2
$byte_quota_utils:schedule_measurement_task
2
        If you are using byte quota, this will schedule your quota measurement task.  Every night, every item on the moo which has not been measured in the last $byte_quota_utils.cycle_days will be measured.  A report will be mailed to $byte_quota_utils.report_recipients.  You may wish to edit this verb to change the time that it runs---it will run at midnight PST.
2

2
$wiz_utils:expire_mail_weekly
2
        If you wish to expire old mail from users and mailing lists, run this verb.  Once a week (scheduled from the first time you type ;$wiz_utils:expire_mail_weekly(), not at a particular hour) it will go through and expire mail based on players' @mail-options settings.
2

2
$wiz_utils:flush_editors
2
        Once a week this will remove all sessions which were begun more than 30 days ago in the note, verb, and mail editors.  Schedule is from when first typed.
2

2
$paranoid_db:weekly
2
        This will go through the @paranoid database and remove entries for players who have not connected within the past three days, and for those users who have turned off the @paranoid function.  Schedule is at 11pm PST.
2

2
$login:sample_lag
2
        This will provide an estimate of the CPU portion of what is normally called "lag"---that is, the delay between entering a command and having that command fulfilled.
2

2
$housekeeper:continuous
2
        If you wish to provide players with the ability to have individual items transported to a known starting location, use this verb.
2

2

2
Additionally, there are tasks that you don't have to start manually, but which get started by various actions in the MOO.
2

2
$network:add_queued_mail
2
        This indicates that there was a temporary failure to deliver email.  If this task is constantly in the queue, it is worth checking $network.queued_mail, deleting those which will never be delivered.  Queued mail does not expire.
2

2
$housekeeper:move_players_home
2
        This task is used to consolidate the tasks spawned by disconnecting players---they get a 5 minute grace period to log back in before they are moved back home.
36
5
4
2
2
*forward*
2
@chown
36
5
4
1
2
The enCore Xpress MOO administration module was designed to make it easy to perform various wizard functions such as account and database-core administration. Click on a button in the menu to perform the specified function. 
36
5
4
7
2
The Xpress enCore Preference application was designed to make it easy to change system-wide preferences for your MOO. You may also use the @configure command to do the same thing, but this command does not have as many options. 
2

2
It is very important that you immediately give the #2 wizard character a password. If you don't do this, anyone can use that character without a password and do irreparable damage to your system!
2

2
Generally, it is also not recommended that you use the #2 wizard character on a regular basis. Create a new programmer or wizard character for yourself and use that one instead.
2

2
NOTE! You must save changes to each category before proceeding to the next. 
36
5
4
14
2
With the Xpress account management system you can:
2

2
<B>Create a new account:</B> First select the player type you wish to create. Your MOO's default player class is always the default choice. Next fill in player name, email address, and real name, and then click on the button named 'Create New Account'.
2

2
<B>Update or delete an existing account:</B> Type in the name of the character you wish to work with in the top right text field and click the find-button. If the character is found, the relevant information will be displayed so you can edit it easily. At this point you can change both character class and the other information displayed. To save your changes click the 'update account' button.
2

2
If you want to delete the character and all their objects, first find the character you want to delete and then click the delete account button.
2

2
<B>Create multiple accounts:</B>Click the button named Multiple Accounts to bring up the multiple account creation form. First select character type for all the new accounts, then type in name, email address, and real name (optional) for each new account. Use a new line for each account. Example:
2

2
<TT>Tim tim@utdallas.edu Tim Leary 
2
Jenny jenn@utdallas.edu Jenny Smith</TT>
2

2
When you submit the creation form, Xpress first checks to see if the character names you've chosen are available. If one or more are already being used, you will be notified of this, and you must select different names than the ones indicated before submitting again. If everything is okay, Xpress will create all the characters you have specified and return a convenient character creation report with passwords. If you are creating accounts for a whole class you can send this report to the class teacher. 
36
5
4
7
2
The Xpress bookmarks make it easy for your users to find their way around the MOO. With the Xpress Bookmark Editor you can easily set up and manage categories of bookmarks that your users may access from the Xpress toolbar button. To create new bookmarks, first type in a title for the category of bookmarks you wish to create. For example 'Points of Interest'. Then enter object numbers of rooms and other objects you wish to add to this category on separate lines in the window. Example:
2

2
#11 
2
#256 
2
#4023
2

2
Once you have created a category, you can easily edit it and add or remove bookmarks by selecting the 'Edit Bookmark' menu in the Xpress Bookmark Editor.
36
5
4
3
2
Message of the Day is a quick and easy way to display a message to users when they log in. The message can be up to 255 characters in length and will only be shown to users once. The message of the day will be shown to users of both the Xpress client as well as other MOO clients.
2

2
IMPORTANT: You cannot use apostrophes or quote marks in the message. If you do, javascript will not display your message at all.
36
5
4
3
2
With the Generics Editor you can easily manage the generic objects users can access from the Xpress Object Editor. You can set up several convenient categories that users can choose from. You can add to or remove generic objects from any of these categories at any time. The default categories are rooms, basic objects, and educational objects.
2

2
If you add new objects to the generics list you must make sure that they are made fertile. Type @chmod #obj +f on the command line to set the fertile flag on an object, or use the Xpress Program Editor. 
36
5
5
36
5
4
1
2
wiz-index
36
1
5
36
4
4
1
2
Wizard Help
36
5
4
1
2
This describes the various commands available on $wiz.
36
5
4
2
0
36341
0
1001876092
36
1
5
36
5
5
36
5
#24
Wizard Utilities

16
2
-1
-1
-1
79
-1
27
41
set_programmer
2
173
-1
set_player
2
173
-1
set_owner
2
173
-1
set_property_owner
2
173
-1
unset_player
2
173
-1
set_property_flags
2
173
-1
_set_property_flags
2
173
-1
random_password
2
173
-1
queued_tasks
2
173
-1
player_cmd_perms
2
173
-1
isnewt
2
173
-1
initialize_owned
2
173
-1
verify_owned_objects
2
173
-1
connected_wizards connected_wizards_unadvertised
36
173
-1
all_wizards_advertised all_wizards all_wizards_unadvertised
36
173
-1
rename_all_instances
2
173
-1
missed_help
2
173
-1
show_missing_help
2
173
-1
init_for_core
2
173
-1
show_netwho_listing
2
173
-1
show_netwho_from_listing
2
173
-1
check_player_request check_reregistration
2
173
-1
make_player
2
173
-1
send_new_player_mail
2
173
-1
do_make_player
2
93
-2
do_register
2
173
-1
do_new_password
2
173
-1
set_owner_new
2
173
-1
boot_idlers
2
173
-1
grant_object
2
173
-1
connection_hash
2
173
-1
newt_player
2
173
-1
unset_programmer
2
173
-1
is_wizard
36
173
-1
expire_mail
2
13
-1
expire_mail_weekly
2
173
-1
check_prog_restricted
2
173
-1
expire_mail_players
2
173
-1
expire_mail_lists
2
173
-1
flush_editors
2
173
-1
random_wizard
2
173
-1
16
default_programmer_quota
default_player_quota
missed_help_strings
missed_help_counters
record_missed_help
programmer_restricted
boot_task
boot_exceptions
programmer_restricted_temp
suicide_string
next_perm_index
wizards
old_task_perms_user
expiration_progress
system_chars
new_core_message
23
0
7
2
5
0
7
2
5
4
0
2
1
4
0
2
1
0
0
2
5
4
0
2
5
0
585440461
2
5
4
0
2
5
4
0
2
5
2
You don't *really* want to commit suicide, do you?
2
5
0
1
2
5
4
1
1
2
2
5
4
0
2
5
1
-1
2
5
4
3
1
36
1
38
1
71
2
5
4
143
2
Getting Started with your LambdaCore MOO
2
========================================
2

2
Thank you for choosing LambdaCore!
2

2
Initial Setup Notes
2
-------------------
2

2
The "welcome" screen, seen when a player connects.
2
  -- this is stored in $login.welcome_message
2

2
Do you want on-line character creation?
2
  -- this is stored in $login.create_enabled
2
     for more detailed information, edit $login:player_creation_enabled
2

2
Do you want to limit the number of players on the MOO at once?
2
  -- look at $login.max_connections
2
     the `connection_limit' message on $login is the message printed
2
     when this limit is reached.
2

2
Do you want a different default player class?
2
  -- set $player_class to a different value
2
     *do not* change $player
2

2
You should also set the following:
2
  $network.postmaster
2
    -- your email address, or the email address of the person who will 
2
       handle your email
2
  $network.site
2
    -- the machine your MOO is running on (e.g. "lambda.moo.mud.org")
2
  $network.port
2
    -- the port your MOO is running on (e.g. 8888)
2
  $network.MOO_Name
2
    -- the name of your MOO (e.g. "LambdaMOO")
2
  $site_db.domain
2
  -- this is set to the `domain' of your address
2
     (eg `foo.com' for `moo.foo.com')
2

2
If you compiled the server with open_network_connection() enabled (allowing the MOO to open up connections with other computers on the network), then you should set
2
  $network.active = 1
2
     This will enable @newpassword, @registerme, @password, @mailme, @netforward, and others to send mail from the MOO.
2

2
-------------------------------------------------------------------
2

2
Setting Yourself Up
2
-------------------
2

2
Set a password for yourself.
2
  -- @password <new-password>
2

2
Set a description for yourself.
2
  -- @describe me as <anything>
2

2
Set a gender for yourself.
2
  -- @gender <gender>
2

2
There are, also, a large number of messages you can set on yourself.  Setting them will enhance the virtual reality.
2

2
-------------------------------------------------------------------
2

2
About Guests
2
------------
2

2
To make a new Guest character:
2
  -- @make-guest <guestname>
2
     will make a new guest with the name you specify with `_Guest' appended
2
     and some other standard but useful aliases
2

2
This is the easiest way to make Guest characters.  The most important things to remember about Guests, if you want to make them yourself, are:
2
  -- make them owned by nonwizards, and not owned by themselves
2
  -- make sure they've got .password == 0, and that .password is nonclear
2
  -- at least one Guest must always be named `Guest'; this can be an alias
2

2
To set the default description and gender for a guest:
2
  -- set .default_description to the description the guest should start with
2
  -- set .default_gender to the gender the guest should start with
2
  -- remember to set .description and .gender too, for the guest's first use
2

2
-------------------------------------------------------------------
2

2
Adding to the Newspaper
2
-----------------------
2

2
The newspaper is a special mailing list.  To add a post to the newspaper, send mail to it (as *News or $news), and then note the number of your post (let's call it <x> and:
2
  -- @addnews <x> to *News
2
... in general, `@addnews $ to *News' will work as well.
2

2
-------------------------------------------------------------------
2

2
Quota
2
-----
2

2
By default, LambdaCore runs with object-based quota.  This is the quota system documented in the LambdaMOO Programmer's Manual, directly supported by the server.  If you're satisfied with object-based quota, which serves some people's needs better than others', you needn't change anything.  However, an in-DB quota system, limiting users by total database space as opposed to total objects, has been designed, and is included here.
2

2
To enable byte-based quota:
2
  -- set $quota_utils to $byte_quota_utils
2
  -- start the measurement task; see `help routine_tasks' for more information (Note: this help topic contains information about more than just the quota task; it should be read regardless of how quota is set).
2

2
It's best that you make this switch before users start, because converting existing object-based users to byte-based users is an awkward (and inherently arbitrary and political) move.  You'd need to decide how much space your builders deserve, and it's all a mess.
2

2
-------------------------------------------------------------------
2

2
Making Programmers
2
------------------
2

2
The command to turn someone into a programmer is `@programmer'  Its syntax is `@programmer <user>'.  For example:
2
  -- @programmer Haakon
2
The `@programmer' verb will prompt you if the user isn't set up with a description and a gender.
2

2
No code to automatically grant programmer bits is included with LambdaCore.
2

2
Making Wizards
2
--------------
2

2
THINK CAREFULLY.
2

2
Be very careful before giving someone a wizard bit.  That person can do gross damage to your database, and fixable but serious damage to the machine it runs on.  That person can quite possibly open outbound network connections from your machine, and thus commit acts for which your host system will be blamed.  That person can ruin your MOO's as-yet-untarnished reputation.
2

2
Wizards have technical power, the ability to change anything within the database, to create anything within the database.  Be careful with the idea of a `Social Wizard' -- a nontechnical person holding a wizard bit is fairly likely to, at some point, accidentally do something destructive.  It's a good idea not to socialize as your wizard character, for the same reason, to make it less likely to be accidentally destructive.
2

2
That said, in general you don't turn an existing character into a wizard, you make a -new- character to be the wizard.  This is because the existing character probably owns code and objects which could be destructive if suddenly made wizardly; it's a good security measure to make a fresh player.  So, to make a fresh player:
2
  -- @make-player (see `help @make-player' for more information)
2
     this will make you a new player. for this example, #123
2

2
To make #123 a wizard:
2
  -- @programmer #123
2
     (a nonprogrammer wizard is a truly strange beast)
2
  -- ;#123.wizard = 1;
2
  -- @chparent #123 to $wiz
2
  -- ;#123.public_identity = <the player's nonwizard character's object number>
2

2
-------------------------------------------------------------------
2

2
Good luck with your new LambdaCore database!
2

2
Visit us at LambdaMOO: lambda.moo.mud.org 8888
2

2
Join the international mailing list for MOO coders: send an email message to moo-cows-request@xerox.com with the word `subscribe' as the body of your message.
2

2
Do good things.
2

2
The LambdaMOO Wizards
2
[authored February 7, 1997]
2
1
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[, suspend-ok])
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[, suspend-ok])
2
    change the permissions on a given property and propagate these to 
2
    *all descendants*.  property ownership is changed on descendants 
2
    where necessary.
2
5
5
2
4
4
1
2
Wizard Utilities
2
5
4
1
2
This is the Wizard Utilities utility package.  See `help $wiz_utils' for more details.
2
5
4
2
0
55659
0
1001876092
36
1
5
2
5
5
2
5
#25
Site DB

0
36
-1
-1
-1
37
-1
16
8
find* _only* _every*
36
173
-1
add
36
173
-1
load
2
173
-1
domain_literal
36
173
-1
init_for_core
36
173
-1
prune
2
173
-1
report_prune_progress
2
173
-1
prune_fixup
2
173
-1
6
domain
prune_progress
prune_stop
total_pruned_people
total_pruned_sites
prune_task
15
2
localdomain
36
1
2
rei.ee
36
5
0
254
36
5
0
51021
36
5
0
28719
36
5
0
1012403705
36
5
2

36
5
5
36
1
4
4
2

2

4
0
4
0
36
0
5
36
4
4
3
2
sitedb
2
site
2
db
36
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').
36
5
4
2
0
8120
0
1001876092
36
1
5
36
5
5
36
5
#26
Math Utilities

16
36
-1
-1
-1
79
-1
43
36
xsin
36
173
-1
xcos
36
173
-1
factorial
36
173
-1
pow
36
173
-1
fibonacci
36
173
-1
geometric
36
173
-1
divmod
36
173
-1
combinations
36
173
-1
permutations
36
173
-1
simpson
36
173
-1
parts
36
173
-1
sqrt
36
173
-1
div
36
173
-1
mod
36
173
-1
exp
36
173
-1
aexp
36
173
-1
random
36
173
-1
random_range
36
173
-1
is_prime
36
173
-1
AND XOR
36
173
-1
OR
36
173
-1
NOT
36
173
-1
BLFromInt
36
173
-1
IntFromBL
36
173
-1
gcd greatest_common_divisor
36
173
-1
lcm least_common_multiple
36
173
-1
are_rel_prime are_relatively_prime
36
173
-1
base_conversion
36
173
-1
norm
36
173
-1
sum
36
173
-1
sin
36
173
-1
cos
36
173
-1
tan
36
173
-1
arcsin asin
36
173
-1
arccos acos
36
173
-1
arctan atan
36
173
-1
7
base_alphabet
tangents
factor
taylor
and
xor
sines
14
2
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
36
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
36
5
0
10000
36
5
0
100
36
5
4
16
4
16
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
4
16
0
0
0
1
0
0
0
1
0
0
0
1
0
0
0
1
0
0
0
1
0
0
0
1
0
0
0
1
0
0
0
1
4
16
0
0
0
0
0
2
0
2
0
0
0
0
0
2
0
2
0
0
0
0
0
2
0
2
0
0
0
0
0
2
0
2
4
16
0
0
0
1
0
2
0
3
0
0
0
1
0
2
0
3
0
0
0
1
0
2
0
3
0
0
0
1
0
2
0
3
4
16
0
0
0
0
0
0
0
0
0
4
0
4
0
4
0
4
0
0
0
0
0
0
0
0
0
4
0
4
0
4
0
4
4
16
0
0
0
1
0
0
0
1
0
4
0
5
0
4
0
5
0
0
0
1
0
0
0
1
0
4
0
5
0
4
0
5
4
16
0
0
0
0
0
2
0
2
0
4
0
4
0
6
0
6
0
0
0
0
0
2
0
2
0
4
0
4
0
6
0
6
4
16
0
0
0
1
0
2
0
3
0
4
0
5
0
6
0
7
0
0
0
1
0
2
0
3
0
4
0
5
0
6
0
7
4
16
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
8
0
8
0
8
0
8
0
8
0
8
0
8
0
8
4
16
0
0
0
1
0
0
0
1
0
0
0
1
0
0
0
1
0
8
0
9
0
8
0
9
0
8
0
9
0
8
0
9
4
16
0
0
0
0
0
2
0
2
0
0
0
0
0
2
0
2
0
8
0
8
0
10
0
10
0
8
0
8
0
10
0
10
4
16
0
0
0
1
0
2
0
3
0
0
0
1
0
2
0
3
0
8
0
9
0
10
0
11
0
8
0
9
0
10
0
11
4
16
0
0
0
0
0
0
0
0
0
4
0
4
0
4
0
4
0
8
0
8
0
8
0
8
0
12
0
12
0
12
0
12
4
16
0
0
0
1
0
0
0
1
0
4
0
5
0
4
0
5
0
8
0
9
0
8
0
9
0
12
0
13
0
12
0
13
4
16
0
0
0
0
0
2
0
2
0
4
0
4
0
6
0
6
0
8
0
8
0
10
0
10
0
12
0
12
0
14
0
14
4
16
0
0
0
1
0
2
0
3
0
4
0
5
0
6
0
7
0
8
0
9
0
10
0
11
0
12
0
13
0
14
0
15
36
5
4
16
4
16
0
0
0
1
0
2
0
3
0
4
0
5
0
6
0
7
0
8
0
9
0
10
0
11
0
12
0
13
0
14
0
15
4
16
0
1
0
0
0
3
0
2
0
5
0
4
0
7
0
6
0
9
0
8
0
11
0
10
0
13
0
12
0
15
0
14
4
16
0
2
0
3
0
0
0
1
0
6
0
7
0
4
0
5
0
10
0
11
0
8
0
9
0
14
0
15
0
12
0
13
4
16
0
3
0
2
0
1
0
0
0
7
0
6
0
5
0
4
0
11
0
10
0
9
0
8
0
15
0
14
0
13
0
12
4
16
0
4
0
5
0
6
0
7
0
0
0
1
0
2
0
3
0
12
0
13
0
14
0
15
0
8
0
9
0
10
0
11
4
16
0
5
0
4
0
7
0
6
0
1
0
0
0
3
0
2
0
13
0
12
0
15
0
14
0
9
0
8
0
11
0
10
4
16
0
6
0
7
0
4
0
5
0
2
0
3
0
0
0
1
0
14
0
15
0
12
0
13
0
10
0
11
0
8
0
9
4
16
0
7
0
6
0
5
0
4
0
3
0
2
0
1
0
0
0
15
0
14
0
13
0
12
0
11
0
10
0
9
0
8
4
16
0
8
0
9
0
10
0
11
0
12
0
13
0
14
0
15
0
0
0
1
0
2
0
3
0
4
0
5
0
6
0
7
4
16
0
9
0
8
0
11
0
10
0
13
0
12
0
15
0
14
0
1
0
0
0
3
0
2
0
5
0
4
0
7
0
6
4
16
0
10
0
11
0
8
0
9
0
14
0
15
0
12
0
13
0
2
0
3
0
0
0
1
0
6
0
7
0
4
0
5
4
16
0
11
0
10
0
9
0
8
0
15
0
14
0
13
0
12
0
3
0
2
0
1
0
0
0
7
0
6
0
5
0
4
4
16
0
12
0
13
0
14
0
15
0
8
0
9
0
10
0
11
0
4
0
5
0
6
0
7
0
0
0
1
0
2
0
3
4
16
0
13
0
12
0
15
0
14
0
9
0
8
0
11
0
10
0
5
0
4
0
7
0
6
0
1
0
0
0
3
0
2
4
16
0
14
0
15
0
12
0
13
0
10
0
11
0
8
0
9
0
6
0
7
0
4
0
5
0
2
0
3
0
0
0
1
4
16
0
15
0
14
0
13
0
12
0
11
0
10
0
9
0
8
0
7
0
6
0
5
0
4
0
3
0
2
0
1
0
0
36
5
4
361
0
175
0
349
0
523
0
698
0
872
0
1045
0
1219
0
1392
0
1564
0
1736
0
1908
0
2079
0
2250
0
2419
0
2588
0
2756
0
2924
0
3090
0
3256
0
3420
0
3584
0
3746
0
3907
0
4067
0
4226
0
4384
0
4540
0
4695
0
4848
0
5000
0
5150
0
5299
0
5446
0
5592
0
5736
0
5878
0
6018
0
6157
0
6293
0
6428
0
6561
0
6691
0
6820
0
6947
0
7071
0
7193
0
7314
0
7431
0
7547
0
7660
0
7771
0
7880
0
7986
0
8090
0
8192
0
8290
0
8387
0
8480
0
8572
0
8660
0
8746
0
8829
0
8910
0
8988
0
9063
0
9135
0
9205
0
9272
0
9336
0
9397
0
9455
0
9511
0
9563
0
9613
0
9659
0
9703
0
9744
0
9781
0
9816
0
9848
0
9877
0
9903
0
9925
0
9945
0
9962
0
9976
0
9986
0
9994
0
9998
0
10000
0
9998
0
9994
0
9986
0
9976
0
9962
0
9945
0
9925
0
9903
0
9877
0
9848
0
9816
0
9781
0
9744
0
9703
0
9659
0
9613
0
9563
0
9511
0
9455
0
9397
0
9336
0
9272
0
9205
0
9135
0
9063
0
8988
0
8910
0
8829
0
8746
0
8660
0
8572
0
8480
0
8387
0
8290
0
8192
0
8090
0
7986
0
7880
0
7771
0
7660
0
7547
0
7431
0
7314
0
7193
0
7071
0
6947
0
6820
0
6691
0
6561
0
6428
0
6293
0
6157
0
6018
0
5878
0
5736
0
5592
0
5446
0
5299
0
5150
0
5000
0
4848
0
4695
0
4540
0
4384
0
4226
0
4067
0
3907
0
3746
0
3584
0
3420
0
3256
0
3090
0
2924
0
2756
0
2588
0
2419
0
2250
0
2079
0
1908
0
1736
0
1564
0
1392
0
1219
0
1045
0
872
0
698
0
523
0
349
0
175
0
0
0
-175
0
-349
0
-523
0
-698
0
-872
0
-1045
0
-1219
0
-1392
0
-1564
0
-1736
0
-1908
0
-2079
0
-2250
0
-2419
0
-2588
0
-2756
0
-2924
0
-3090
0
-3256
0
-3420
0
-3584
0
-3746
0
-3907
0
-4067
0
-4226
0
-4384
0
-4540
0
-4695
0
-4848
0
-5000
0
-5150
0
-5299
0
-5446
0
-5592
0
-5736
0
-5878
0
-6018
0
-6157
0
-6293
0
-6428
0
-6561
0
-6691
0
-6820
0
-6947
0
-7071
0
-7193
0
-7314
0
-7431
0
-7547
0
-7660
0
-7771
0
-7880
0
-7986
0
-8090
0
-8192
0
-8290
0
-8387
0
-8480
0
-8572
0
-8660
0
-8746
0
-8829
0
-8910
0
-8988
0
-9063
0
-9135
0
-9205
0
-9272
0
-9336
0
-9397
0
-9455
0
-9511
0
-9563
0
-9613
0
-9659
0
-9703
0
-9744
0
-9781
0
-9816
0
-9848
0
-9877
0
-9903
0
-9925
0
-9945
0
-9962
0
-9976
0
-9986
0
-9994
0
-9998
0
-10000
0
-9998
0
-9994
0
-9986
0
-9976
0
-9962
0
-9945
0
-9925
0
-9903
0
-9877
0
-9848
0
-9816
0
-9781
0
-9744
0
-9703
0
-9659
0
-9613
0
-9563
0
-9511
0
-9455
0
-9397
0
-9336
0
-9272
0
-9205
0
-9135
0
-9063
0
-8988
0
-8910
0
-8829
0
-8746
0
-8660
0
-8572
0
-8480
0
-8387
0
-8290
0
-8192
0
-8090
0
-7986
0
-7880
0
-7771
0
-7660
0
-7547
0
-7431
0
-7314
0
-7193
0
-7071
0
-6947
0
-6820
0
-6691
0
-6561
0
-6428
0
-6293
0
-6157
0
-6018
0
-5878
0
-5736
0
-5592
0
-5446
0
-5299
0
-5150
0
-5000
0
-4848
0
-4695
0
-4540
0
-4384
0
-4226
0
-4067
0
-3907
0
-3746
0
-3584
0
-3420
0
-3256
0
-3090
0
-2924
0
-2756
0
-2588
0
-2419
0
-2250
0
-2079
0
-1908
0
-1736
0
-1564
0
-1392
0
-1219
0
-1045
0
-872
0
-698
0
-523
0
-349
0
-175
0
0
0
175
36
5
4
56
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
  aexp(x) -- calculates 10000 e^(x/10000)
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
  norm(a,b,c,d,...) -- returns sqrt(a^2+b^2+c^2+...)
2
  sum(a,b,c,d,...) -- returns the sum of all arguments.
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
  base_conversion(num|string, oldbase, newbase [,sens]) -- converts the number
2
      given as first arg from oldbase to the newbase.
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.
2

2
Bitwise Conversions:
2
  BlFromInt(d) -- converts a decimal number d to a list of 1's and 0's, 32-bit
2
  IntFromBl(b) -- converts a list of 1's and 0's (any precision) to decimal
36
5
5
36
4
4
4
2
Math Utilities
2
Math_Utils
2
trigonometric utilites
2
trig_utils
36
5
4
1
2
This is the Math Utilities utility package.  See `help $math_utils' for more details.
36
5
4
2
0
32504
0
1001876092
36
1
5
36
5
5
36
5
#27
Set Utilities

16
36
-1
-1
-1
79
-1
41
8
union
36
173
-1
intersection
36
173
-1
diff*erence
36
173
-1
contains
36
173
-1
exclusive_or xor
36
173
-1
difference_suspended diff_suspended
36
173
-1
equal
36
173
-1
intersection_preserve_case
36
173
-1
0
7
4
22
2
This object is useful for operations that treat lists as sets (i.e.,
2
without concern about order and assuming no duplication).
2

2
 union(set, set, ...)        => union
2
 intersection(set, set, ...) => intersection
2
 intersection_preserve_case(base set, set, set, ...)
2
        => intersection with the case of the base set's elements preserved
2

2
 diff*erence(set1, set2, ..., setn)
2
        => result of removing all elements of sets 2..n from set 1.
2

2
 difference_suspended(set1, set2, ..., setn)
2
        => same as above except it suspends as needed.
2

2
 exclusive_or(set, set, set, ...)
2
        => all elements that are contained in exactly one of the sets
2

2
 contains(set1, set2, ..., setn)
2
        => true if and only if all of sets 2..n are subsets of set 1
2

2
 equal(set1, set2)
2
        => true if and only if set1 and set2 are equal
36
5
5
36
4
4
2
2
Set Utilities
2
set_utilities
36
5
4
1
2
This is the Set Utilities utility package.  See `help $set_utils' for more details.
36
5
4
2
0
5606
0
1001876092
36
1
5
36
5
5
36
5
#28
Builtin Function Help

16
36
-1
-1
-1
30
-1
23
0
126
match()
rmatch()
substitute()
sqrt()
server_log()
pass()
builtin-index
open_network_connection()
connection_name()
shutdown()
dump_database()
memory_usage()
reset_max_object()
renumber()
server_version()
output_delimiters()
callers()
kill_task()
queued_tasks()
read()
suspend()
task_id()
seconds_left()
ticks_left()
caller_perms()
set_task_perms()
eval()
boot_player()
notify()
idle_seconds()
connected_seconds()
connected_players()
set_player_flag()
is_player()
players()
set_verb_code()
verb_code()
delete_verb()
add_verb()
set_verb_args()
verb_args()
set_verb_info()
verb_info()
verbs()
delete_property()
add_property()
set_property_info()
property_info()
properties()
move()
max_object()
recycle()
children()
parent()
valid()
chparent()
create()
setremove()
setadd()
listset()
listdelete()
listinsert()
listappend()
strcmp()
rindex()
index()
crypt()
strsub()
length()
ctime()
time()
random()
abs()
max()
min()
toobj()
tonum()
tostr()
typeof()
clear_property()
is_clear_property()
set_connection_option()
queue_info()
is_member()
equal()
toliteral()
value_bytes()
acos()
asin()
atan()
binary_hash()
string_hash()
buffered_output_length()
call_function()
ceil()
connection_options()
connection_option()
sin()
cos()
tan()
sinh()
cosh()
tanh()
db_disk_size()
decode_binary()
disassemble()
encode_binary()
exp()
floatstr()
floor()
flush_input()
force_input()
function_info()
listen()
listeners()
log()
log10()
object_bytes()
raise()
resume()
task_stack()
tofloat()
toint()
trunc()
unlisten()
value_hash()
134
4
22
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 STRING 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 numbers, the start and end indices in <subject> 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")          =>  {1, 2, {{0, -1}, ...}, "foo"}
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")  =>  {1, 4, {{2, 3}, {0, -1}, ...}, "foobar"}
2

2
See `help regular-expressions' for information on the syntax and semantics of patterns.
36
5
4
2
2
*forward*
2
match()
36
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.
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."
36
5
4
3
2
Syntax:  sqrt (FLOAT <x>)  => FLOAT
2

2
Returns the square root of <x>.  If <x> is negative, then E_INVARG is raised.
36
5
4
3
2
Syntax:  server_log (STR <message> [, <is-error>])  => none
2

2
The text in <message> is sent to the server log.  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.
36
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, the root object (an ancestor of every other object) defines a :description() verb 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 the called verb are the ones given to pass() 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(@args) + "  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 the above example, `pass()' would have worked just as well, since :description() is not normally given any arguements.  However, it is a good idea to get into the habit of using `pass(@args)' rather than `pass(args[1])' or `pass()' even if the verb being pass()ed to is already known to take a set number of arguments or none at all.  For one thing, though the args may be irrelevant to the code that you've written, it may be that the corresponding verb on the parent has been rewritten to take additional arguments, in which case you will want your verb to continue to work...
36
5
4
2
2
*index*
2
Server Built-in Functions
36
5
4
11
2
Syntax:  open_network_connection (<value>, ...)   => OBJ
2

2
Establishes a network connection to the place specified by the arguments and 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 in the LambdaMOO Programmers Manual 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 like any other 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.
36
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

2
  "port <lport> from <host>, port <port>"
2

2
where <lport> is the listening port on which the connection arrived, <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

2
  "User <#number>"
2

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.
36
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, the given <message> is printed to all connected players.  If the programmer is not a wizard, then E_PERM is raised.
36
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.
36
5
4
17
2
Syntax:  memory_usage ()   => list
2

2
On some versions of the server, this returns statistics concerning the server
2
consumption of system memory.  The result is a list of lists, each in the
2
following format:
2

2
    {<block-size>, <nused>, <nfree>}
2

2
where <block-size> is the size in bytes of a particular class of memory
2
fragments, <nused> is the number of such fragments currently in use in the
2
server, and <nfree> is the number of such fragments that have been reserved
2
for use but are currently free.
2

2
On servers for which such statistics are not available, `memory_usage()'
2
returns `{}'.  The compilation option `USE_SYSTEM_MALLOC' controls
2
whether or not statistics are available; if the option is provided, statistics
2
are not available.
36
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.
36
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.
36
5
4
22
2
Syntax:  server_version ()   => str
2

2
Returns a string giving the version number of the MOO server in the following
2
format:
2

2
    "<major>.<minor>.<release>"
2

2
where <major>, <minor>, and <release> are all decimal numbers.
2

2
The major version number changes very slowly, only when existing MOO code might
2
stop working, due to an incompatible change in the syntax or semantics of the
2
programming language, or when an incompatible change is made to the database
2
format.
2

2
The minor version number changes more quickly, whenever an upward-compatible
2
change is made in the programming language syntax or semantics.  The most
2
common cause of this is the addition of a new kind of expression, statement, or
2
built-in function.
2

2
The release version number changes as frequently as bugs are fixed in the
2
server code.  Changes in the release number indicate changes that should only
2
be visible to users as bug fixes, if at all.
36
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 LambdaMOO Programmers Manual for more information about the output prefix and suffix.
36
5
4
14
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, and <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 is 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

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.
36
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.
36
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>, <ticks>, <clock-id>,
2
     <programmer>, <verb-loc>, <verb-name>, <line>, <this>}
2

2
where <task-id> is a numeric identifier for this queued task, <start-time> is the time after which this task will begin execution (in `time()' format), <ticks> is the number of ticks this task will have when it starts (always 20,000 now, though this is changeable. This makes this value obsolete and no longer interesting), <clock-id> is a number whose value is 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 <ticks> and <clock-id> fields are now obsolete and are retained only for backward-compatibility reasons.  They may disappear in a future version of the server.
36
5
4
33
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 <conn> is provided, then the programmer must either be a wizard or the owner of <conn>, if <conn> 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 <conn> 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 the 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

2
  set_connection_option(<conn>, "hold-input", 1)
2

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

2
  set_connection_option(<conn>, "hold-input", 0)
2

2
to allow commands once again to be read and interpreted normally.
2

2
As an example, consider the following, which refers to the verbs programmed in the `suspend()' example in `help suspend()':  
2

2
    .program   #0:read_twice_A
2
    s = read();        /* success depends on programmer's permissions */
2
    #0:callee_A();     /* callee_A does not suspend */
2
    t = read();        /* success depends on programmer's permissions */
2
    .
2

2
    .program   #0:read_twice_B
2
    s = read();        /* success depends on programmer's permissions */
2
    #0:callee_B();     /* callee_B does suspend */
2
    t = read();        /* fails, returning E_PERM */
2
    .
2

2
In three of the four calls to `read()', success depends on on the programmer's permissions.  However, the second `read()' in `#0:read_twice_B' always fails and raises E_PERM.  This is because the task was suspended, even though `#0:read_twice_B' did not do the actual suspending.  Hence, if you want to read some input (by using `read()' directly or by calling other verbs which do the reading), you must make sure that the task is not suspended before the reading.  This includes making sure that any verbs you call, directly or indirectly, also do not suspend.
2

2
It is possible to call `read()' many times in the same command task, so long as the task does not call `suspend()' or an explicit argument is given.  The best use for `read()' with an explicit argument is in conjunction with `open_network_connection()', if it is enabled.
36
5
4
38
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', 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
Once `suspend()' has been used in a particular task, then the `read()' function will always raise E_PERM in that task.  For more details, see the description of `read()'.
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 in the LambdaMOO Programmers Manual on server assumptions about the database for details.
36
5
4
3
2
Syntax:  task_id ()   => INT
2

2
Returns the numeric identifier for the currently-executing task.  Such numbers are randomly selected for each task and can therefore safely be used in circumstances where unpredictability is required.
36
5
4
2
2
*forward*
2
ticks_left()
36
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 fork another task to continue a long-lived computation.
36
5
4
6
2
Syntax:  caller_perms ()   => obj
2

2
Returns the permissions in use by the verb that called the currently-executing
2
verb.  If the currently-executing verb was not called by another verb (i.e., it
2
is the first verb called in a command or server task), then
2
`caller_perms()' returns `#-1'.
36
5
4
5
2
Syntax:  set_task_perms (OBJ <player>)   => none
2

2
Changes the permissions with which the currently-executing verb is running to be those of <player>.  If <player> is not of type OBJ, then E_INVARG is raised.  If the programmer is neither <player> 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.
36
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}
36
5
4
9
2
Syntax:  boot_player (obj <player>)   => none
2

2
Immediately terminates 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 returned.  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 corresponding call is simply skipped.
36
5
4
7
2
Syntax:  notify (OBJ conn, STR string [, no-flush]) => 0 or 1
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 output 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 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.
36
5
4
2
2
*forward*
2
connected_seconds()
36
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.
36
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, includes the object numbers associated with all current connections, including those that are outbound and/or not yet logged-in.
36
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 users can connect to <object> by name when they log into the server.
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).
36
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.
36
5
4
3
2
Syntax:  players ()   => list
2

2
Returns a list of the object numbers of all player objects in the database.
36
5
4
2
2
*forward*
2
verb_code()
36
5
4
8
2
Syntax:  verb_code (OBJ <object>, STR <verb-name> [, <fully-paren> [, <indent>]])   => LIST
2
     set_verb_code (OBJ <object>, STR <verb-name>, LIST <code>)   => LIST
2

2
These functions get and set (respectively) the MOO-code program associated with the verb named <verb-name> 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 named <verb-name>, 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.
36
5
4
3
2
Syntax:  delete_verb (obj <object>, str <verb-name>)   => none
2

2
Removes the verb named <verb-name> 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 named <verb-name>, then E_VERBNF is raised.
36
5
4
14
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
2
bits and name(s) are given by <info> in the same format as is returned by
2
`verb_info()'.  The new verb's direct-object, preposition, and indirect-object
2
specifications are given by <args> in the same format as is returned by
2
`verb_args()'.  The new verb initially has the empty program associated with 
2
it; this program does nothing but return an unspecified value.
2

2
If <object> is not valid, or <info> does not specify a legitimate owner
2
and permission bits, or <args> is not a legitimate syntax specification,
2
then `E_INVARG' is retuned.  If the programmer does not have write
2
permission on <object> or if the owner specified by <info> is not the
2
programmer and the programmer is not a wizard, then `E_PERM' is returned.
36
5
4
2
2
*forward*
2
verb_args()
36
5
4
12
2
Syntax:  verb_args (OBJ <object>, STR <verb-name>)   => LIST
2
     set_verb_args (OBJ <object>, STR <verb-name>, LIST <args>)   => none
2

2
These two functions get and set (respectively) the direct-object, preposition, and indirect-object specifications for the verb named <verb-name> on the given <object>.  If <object> is not valid, then E_INVARG is raised.  If <object> does not define a verb named <verb-name>, 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_info()' and expected as the third argument to `set_verb_info()'.  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"})
36
5
4
2
2
*forward*
2
verb_info()
36
5
4
8
2
Syntax:  verb_info (OBJ <object>, STR <verb-name>)   => LIST
2
     set_verb_info (OBJ <object>, STR <verb-name>, LIST <info>)   => none
2

2
These two functions get and set (respectively) the owner, permission bits, and name(s) for the verb named <verb-name> on the given <object>.  If <object> is not valid, then E_INVARG is raised.  If <object> does not define a verb named <verb-name>, 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()'. The latter function 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.
36
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.
36
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.
36
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 <object> already has a property named <prop-name> or <info> does not specify a legitimate owner and permission bits, 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.
36
5
4
2
2
*forward*
2
property_info()
36
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 and <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 or <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.
36
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.
36
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'.
36
5
4
6
2
Syntax:  max_object ()   => obj
2

2
Returns the largest object number yet assigned to a created object.  Note that
2
the object with this number may no longer exist; it may have been recycled.
2
The next object created will be assigned the object number one larger than the
2
value of `max_object()'.
36
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 number, then `recycle()' treats that value as a "quota" and increments it by one, storing the result back into the `ownership_quota' property.
36
5
4
2
2
*forward*
2
parent()
36
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.
36
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
36
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 returned.
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.
36
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 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

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

2
If the intended owner of the new object has a property named `ownership_quota' and the value of that property is a number, 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.
36
5
4
2
2
*forward*
2
setadd()
36
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}
36
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 primarly 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.
36
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"}
36
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}
36
5
4
2
2
*forward*
2
listinsert()
36
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 number.  If the two strings are identical, `strcmp()' returns zero.  Otherwise, `strcmp()' returns a positive number.  The ASCII character ordering is used for the comparison.
36
5
4
2
2
*forward*
2
index()
36
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
36
5
4
19
2
Syntax:  crypt (str <text> [, str <salt>])   => str
2

2
Encrypts the given <text> using the standard UNIX encryption method.  If
2
provided, <salt> should be a two-character string for use as the extra
2
encryption ``salt'' in the algorithm.  If <salt> is not provided, a random
2
pair of characters is used.  In any case, the salt used is also returned as the
2
first two characters of the resulting encrypted string.
2

2
Aside from the possibly-random selection of the salt, the encryption algorithm
2
is entirely deterministic.  In particular, you can test whether or not a given
2
string is the same as the one used to produced a given piece of encrypted text;
2
simply extract the first two characters of the encrypted text and pass the
2
candidate string and those two characters to `crypt()'.  If the result is
2
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"
36
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"
36
5
4
8
2
Syntax:  length (<list or string>)   => int
2

2
Returns the number of characters in <list or string>.  
2

2
    length("foo")       =>   3
2
    length("")          =>   0
2
    length({1, 2, 3})   =>   3
2
    length({})          =>   0
36
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.
36
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.
36
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.
36
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.
36
5
4
2
2
*forward*
2
min()
36
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.
36
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
36
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
36
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()' is better for this purpose.
36
5
4
11
2
Syntax:  typeof (<value>)   => INT
2

2
Takes any MOO value and returns a number 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.
36
5
4
4
2
Syntax:  clear_property (OBJ <object>, STR <prop-name>)  => none
2
      is_clear_property (OBJ <object>, STR <prop-name>)  => INT
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.
36
5
4
2
2
*forward*
2
clear_property()
36
5
4
15
2
Syntax:  set_connection_option (OBJ conn, STR option, value)   => none
2

2
Controls a number of optional behaviors associated with 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
   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
   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
   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 built-in command parser as `binary strings', in whatever size chunks come back from the operating system. (See the early section in the LambdaMOO Programmers Manual 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
   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 in the LambdaMOO Programmers Manual on server assumptions about the database for details.
36
5
4
3
2
queue_info([obj user])
2

2
Returns the number of forked tasks that <user> has at the moment.  Since it doesn't say which tasks, security is not a significant issue.  If no argument is given, then gives a list of all users with task queues in the server.  (Essentially all connected players + all open connections + all users with tasks running in the background.)
36
5
4
9
2
Syntax:  is_member (ANY 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
Raises E_ARGS if two values are given or if more than two values are given. Raises E_TYPE if the second argument is not a list. Otherwise returns the index of <value> in <list>, or 0 if it's not in there.
2

2
  is_member(3, {3, 10, 11})                 => 1
2
  is_member("a", {"A", "B", "C"})           => 0
2
  is_member("XyZ", {"XYZ", "xyz", "XyZ"})   => 3
36
5
4
9
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
Raises E_ARGS if none, one, or more than two arguments are given.
2

2
equal(1, 2)                   => 0
2
equal("ChIcKeN", "chicken")   => 0
2
equal("ABC123", "ABC123")     => 1
36
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>. If no arguments or more than one argument is given, E_ARGS is raised.
2

2
Examples:
2
toliteral(43)                       =>  "43"
2
toliteral(1.0/3.0)                  =>  "0.33333333333333"
2
toliteral(#17)                      =>  "#17"
2
toliteral(E_PERM)                   =>  "E_PERM"
2
toliteral({"A", "B", {"C", 123}})   =>  "{\"A\", \"B\", {\"C\", 123}}"
36
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>.
36
5
4
3
2
Syntax:  acos (FLOAT <x>)   => FLOAT
2

2
Returns the arc-cosine (inverse cosine) of x, in the range [0..pi]. Raises E_INVARG if x is outside the range [-1.0..1.0].
36
5
4
3
2
Syntax:  asin (FLOAT <x>)   => FLOAT
2

2
Returns the arc-sine (inverse sine) of x, in the range [-pi/2..pi/2]. Raises E_INVARG if x is outside the range [-1.0..1.0].
36
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.
36
5
4
12
2
Syntax:  binary_hash (STR bin-string)   => STR
2
         string_hash (STR text)         => 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

2
string_hash(x) == string_hash(y)
2

2
then, almost certainly
2

2
equal(x, y)
2

2
This can be useful, for example, in certain networking applications:  after sending a large piece of text across a connection, also send across 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.
36
5
4
2
2
*forward*
2
binary_hash()
36
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.
36
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.
36
5
4
3
2
Syntax:  ceil (FLOAT <x>)   => FLOAT
2

2
Returns the smallest integer not less than x, as a floating-point number.
36
5
4
3
2
Syntax:  connection_options (OBJ conn, STR name)   => LIST
2

2
Return 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.
36
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.
36
5
4
5
2
Syntax:  cos (FLOAT x)   => FLOAT
2
         sin (FLOAT x)   => FLOAT
2
         tan (FLOAT x)   => FLOAT
2

2
Returns the cosine, sine, or tangent of <x>, respectively.
36
5
4
2
2
*forward*
2
sin()
36
5
4
2
2
*forward*
2
sin()
36
5
4
5
2
Syntax:  cosh (FLOAT x)   => FLOAT
2
         sinh (FLOAT x)   => FLOAT
2
         tanh (FLOAT x)   => FLOAT
2

2
Returns the hyperbolic cosine, sine, or tangent of <x>, respectively.
36
5
4
2
2
*forward*
2
sinh()
36
5
4
2
2
*forward*
2
sinh()
36
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.
36
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 proved 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 LambdaMOO programmer's manual 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}
36
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.
36
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 sections in the LambdaMOO Programmer's Manual 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"
36
5
4
3
2
Syntax:  exp (FLOAT x)   => FLOAT
2

2
Returns `e' raised to the power of <x>.
36
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.
36
5
4
3
2
Syntax:  floor (FLOAT x)   => FLOAT
2

2
Returns the largest integer not greater than x, as a floating-point number.
36
5
4
3
2
Syntax:  flush_input (OBJ conn [, show-messages])   => none
2

2
Performs the same actions as if the connection <conn>'s definied 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 messages to <conn> listing the flushed lines, if any.  See the chapter in the LambdaMOO Programmer's Manual on server assumptions about the database for more information about a connection's defined flush command.
36
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_INVARG if <conn> does not specify a current connection and E_PERM if the programmer is neither <conn> nor a wizard.
36
5
4
17
2
Syntax:  function_info ([STR name])   => LIST
2

2
Returns descriptions of the various 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. E_INVARG is raised 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 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.
36
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 asthese verbs are called on #0 for normal connections. (See the chapter in the LambdaMOO Programmer's Manual 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 <canaon> 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, there can be only one listening point at a time; <point> can be any value at all and <canon> is always zero.
36
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.)
36
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.
36
5
4
2
2
*forward*
2
log()
36
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 its non-clear properties and by the verbs and properties defined directly on the object. Raises E_INVARG if <object> is not a valid object and E_PERM if the programmer is not a wizard.
36
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 `tostr(<code>)', and <value>, which defaults to zero, are made available to any `try-except' statements to catch the error. If the error is not caught, then <message> will appear on the first line of the traceback printed to the user.
36
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.
36
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.
36
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 objects 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()' raises E_INVARG.
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
36
5
4
2
2
*forward*
2
tonum()
36
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 equalavent to `ceil()'; otherwise, it is equivalent to `floor()'.
36
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.
36
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.
36
5
5
36
5
4
1
2
builtin-index
36
1
5
36
4
4
1
2
Builtin Function Help
36
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.
36
5
4
2
0
90808
0
1001876092
36
1
5
36
5
5
36
5
#29
New-Prog-Log

0
2
46
-1
34
45
89
14
7
init_for_core
2
173
-1
receive_message
2
173
-1
display_seq_headers display_seq_full
2
173
-1
from_msg_seq
2
173
-1
to_msg_seq
2
173
-1
%to_msg_seq subject_msg_seq
2
173
-1
%from_msg_seq
2
173
-1
1
keyword
27
2
PROGRAMMER
2
5
5
2
5
0
0
36
1
4
0
36
0
0
1
2
5
5
2
5
5
2
5
5
2
5
4
1
1
2
36
1
4
0
36
1
5
36
1
5
36
1
4
0
36
0
5
2
5
5
2
5
4
0
36
1
5
2
5
4
0
36
1
5
36
0
5
36
0
5
36
0
5
2
4
4
3
2
New-Prog-Log
2
New_Prog_Log
2
NPL
36
1
2
Record of who's been made a @programmer.
2
5
4
2
0
6365
0
1001876092
36
1
5
2
5
5
2
5
#30
Generic Help Database

144
36
-1
-1
-1
1
60
139
12
find_topics
2
173
-1
get_topic
2
173
-1
sort_topics
36
173
-1
columnize
36
173
-1
forward pass
36
173
-1
subst
36
173
-1
index
36
173
-1
initialize
2
173
-1
verbdoc
2
173
-1
dump_topic
2
173
-1
objectdoc
36
173
-1
find_index_topics
36
173
-1
2
index
index_cache
8
4
0
36
5
4
0
36
1
5
36
4
4
1
2
Generic Help Database
36
5
2
A help database of the standard form in need of a description. See `help $generic_help'...
36
5
4
2
0
9445
0
1001876092
36
1
5
36
5
5
36
5
#31
Generic Guest

16
36
-1
-1
-1
90
-1
4
24
boot
2
173
-1
disfunc
2
173
-1
defer
2
173
-1
mail_catch_up
2
173
-1
create
36
89
-2
eject
36
173
-1
log
36
173
-1
confunc
2
173
-1
log_disconnect
2
173
-1
@last-c*onnection
2
29
-1
my_huh
2
173
-1
@read @peek
36
89
-2
set_current_folder
36
173
-1
init_for_core
2
173
-1
set_name set_aliases
2
173
-1
extra_confunc_msg
2
173
-1
do_reset
2
173
-1
@request
2
89
-2
connection_name_hash
2
173
-1
look_self
2
165
-1
set_guest_name
2
173
-1
_web_menu
2
173
-1
check
2
173
-1
check_login
2
173
-1
8
default_gender
default_description
request
extra_confunc_msg
free_to_use
default_name
default_aliases
xpress_login_interval
178
2
neuter
36
1
4
1
2
By definition, guests appear nondescript.
36
1
0
0
2
0
2

36
5
0
1
36
1
2

36
5
2

36
5
0
180
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
5
5
36
5
5
36
5
5
36
5
5
36
4
5
36
5
5
36
5
2
%t (%[#t]) is a guest character.
36
5
5
36
5
5
36
5
5
36
5
5
36
4
5
36
4
4
3
1
91
1
92
1
114
36
1
5
2
0
5
2
0
5
2
1
0
0
36
5
5
36
5
5
36
1
5
36
1
5
36
0
5
36
1
5
2
1
0
79
36
1
5
36
4
5
2
0
5
2
0
5
36
5
5
36
5
0
30
36
4
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
0
0
36
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
0
2
0
5
36
5
0
1
36
5
5
36
5
5
36
5
5
2
1
4
4
0
0
0
0
0
0
0
0
36
0
5
2
0
5
2
0
5
2
1
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
4
5
2
1
5
36
5
5
2
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
0
5
36
4
5
36
4
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
2
guest.gif
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
36
5
5
36
5
5
36
5
5
36
4
4
1
2
Generic Guest
2
1
4
1
2
By definition, guests appear nondescript.
36
5
4
2
0
25028
0
1001876092
36
1
5
36
5
5
36
5
#32
enCore Xpress Web Server

16
2
-1
-1
-1
138
-1
146
12
do_login_command
2
173
-1
get
2
165
-1
start_webserver
2
173
-1
init_for_core
2
173
-1
authenticate
2
173
-1
collect_user_agent_headers
2
173
-1
collect_user_agent_POST
2
173
-1
watch
2
109
12
log
2
173
-1
generate_server_response_header
2
173
-1
determine_content_type
2
173
-1
generate_traceback
2
173
-1
20
protocol
authentication_method
200_OK
400_Bad_Request
403_Forbidden
404_Not_Found
501_Not_Implemented
allow
content_type
pragma
expires
server_name
total_homepage_hits
total_logins
total_pages_served
inline_image_suffixes
embed_file_suffixes
watchers
moo_content_type
500_Internal_Server_Error
66
2
HTTP/1.0 
2
5
2
Client Side HTTP Cookies
2
5
4
2
4
1
2
HTTP/1.0 200 OK
4
1
2

2
5
4
2
4
1
2
HTTP/1.0 400 Bad Request
4
8
2

2
<HTML>
2
<HEAD>
2
   <TITLE>400 Bad Request</TITLE>
2
</HEAD>
2
<BODY BGCOLOR="#FFFFFF">
2
<H1>Bad Request</H1>
2
The URL request could not be understood by the server.<P>
2
5
4
2
4
1
2
HTTP/1.0 403 Forbidden
4
8
2

2
<HTML>
2
<HEAD>
2
   <TITLE>403 Forbidden</TITLE>
2
</HEAD>
2
<BODY BGCOLOR="#FFFFFF">
2
<H1>Forbidden</H1>
2
You do not have permission to access the requested URL.<P>
2
5
4
2
4
1
2
HTTP/1.0 404 Not Found
4
8
2

2
<HTML>
2
<HEAD>
2
   <TITLE>404 Not Found</TITLE>
2
</HEAD>
2
<BODY BGCOLOR="#FFFFFF">
2
<H1>Not Found</H1>
2
The requested URL was not found on this server.<P>
2
5
4
2
4
1
2
HTTP/1.0 501 Not Implemented
4
8
2

2
<HTML>
2
<HEAD>
2
   <TITLE>501 Not Implemented</TITLE>
2
</HEAD>
2
<BODY BGCOLOR="#FFFFFF">
2
<H1>Not Implemented</H1>
2
Your HTTP request is not supported by this server.<P>
2
5
2
GET, POST, HEAD
2
5
2
text/html
2
5
2
No-Cache
2
5
2
Expires: Thu, 15 Apr 1999 12:00:00 GMT
2
5
2
enCoreXpress/
2
5
0
0
2
5
0
0
2
5
0
0
2
5
4
4
2
gif
2
jpg
2
jpeg
2
jpe
2
5
4
22
2
rpm
2
wav
2
rmf
2
aif
2
aiff
2
au
2
snd
2
mid
2
mpg
2
mpeg
2
mpe
2
png
2
qt
2
mov
2
dcr
2
dir
2
dxr
2
flc
2
avi
2
swf
2
pdf
2
mp3
2
5
4
0
2
0
2
moo/html
2
5
4
2
4
1
2
HTTP/1.0 500 Internal Server Error
4
8
2

2
<HTML>
2
<HEAD>
2
   <TITLE>500 Internal Server Error</TITLE>
2
</HEAD>
2
<BODY BGCOLOR="#FFFFFF">
2
<H1>Internal Server Error</H1>
2
An internal server error was encountered. If this problem persists please notify a MOO administrator.<P>
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2

2
1
2

2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
5
2
4
4
1
2
WWW System Object
2
5
2
For help on using the speller, use 'help spelling' and 'help @spell'.
2
5
4
2
0
21720
0
1001876092
36
1
5
2
5
5
2
5
#33
sequence utilities

16
36
-1
-1
-1
79
-1
13
21
add remove
36
173
-1
contains
36
173
-1
complement
36
173
-1
union
36
173
-1
tostr
36
173
-1
for
36
173
-1
extract
36
173
-1
tolist
36
173
-1
from_list
36
173
-1
from_sorted_list
36
173
-1
first
36
173
-1
last
36
173
-1
size
36
173
-1
from_string
36
173
-1
firstn
36
173
-1
lastn
36
173
-1
range
36
173
-1
expand
36
173
-1
contract
36
173
-1
_union
36
173
-1
intersection
36
173
-1
0
7
4
37
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)        => sequence consisting of integers not in seq
2
  :union    (seq,seq,...) => union of all sequences
2
  :intersect(seq,seq,...) => intersection of all sequences
2
  :contract (seq,cseq)              (see `help $seq_utils:contract')
2
  :expand   (seq,eseq[,include])    (see `help $seq_utils:expand')
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
(*) i.e., integers in the range [$minint+1..$maxint].  The implementation depends on $minint never being included in a sequence.
36
5
5
36
4
4
3
2
sequence utilities
2
seq_utils
2
squ
36
5
4
1
2
This is the sequence utilities utility package.  See `help $seq_utils' for more details.
36
5
4
2
0
17134
0
1001876092
36
1
5
36
5
5
36
5
#34
Quota-Log

0
2
46
-1
70
45
-1
29
1
init_for_core
2
173
-1
0
26
4
0
2
5
0
0
36
1
4
0
36
0
0
1
2
5
2
%n (%#) can't send to moderated list %t (%[#t]) directly.
2
5
4
0
2
5
4
0
2
5
4
1
1
2
36
1
4
0
36
1
0
2592000
36
1
0
0
36
1
4
0
36
0
5
2
5
5
2
5
4
0
36
1
5
2
5
4
0
36
1
5
36
0
5
36
0
5
36
0
5
2
4
4
4
2
Quota-Log
2
Quota_Log
2
QL
2
Quota
36
1
2
Record of whose quota has been messed with and why.
2
5
4
2
0
1698
0
1001876092
36
1
5
2
5
5
2
5
#35
you

16
36
-1
-1
-1
94
-1
-1
3
verb_sub
36
173
-1
say_action
36
165
-1
fixpos
36
173
-1
2
conjugations
help_msg
19
4
4
4
2
2
is
2
are
4
2
2
was
2
were
4
2
2
does
2
do
4
2
2
has
2
have
36
1
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.
36
5
2
2nd
36
5
2
Yours
36
5
2
yours
36
5
2
Your
36
5
2
your
36
5
2
Yourself
36
5
2
yourself
36
5
2
You
36
5
2
you
36
5
2
You
36
5
2
you
36
5
5
36
4
4
1
2
you
36
5
4
1
2
An object useful for pronoun substitution for switching between third and second person.  See `help $you' for details.
36
5
4
2
0
4562
0
1001876092
36
1
5
36
5
5
36
5
#36
Hacker

19
36
-1
-1
-1
58
-1
71
1
init_for_core
2
173
-1
0
175
5
36
1
5
36
1
5
36
1
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
0
0
36
1
5
36
5
5
36
5
5
36
5
5
36
5
5
36
4
5
36
5
5
2
1
4
1
1
2
36
5
5
36
5
5
36
5
5
36
5
5
36
4
5
36
4
5
36
1
5
2
0
5
2
0
5
2
1
0
0
36
5
5
36
5
5
36
1
5
36
1
5
36
0
5
36
1
0
0
2
1
5
36
1
5
36
4
5
2
0
5
2
0
5
36
5
5
36
5
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
2147483647
2
1
0
-9999
36
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
1
-1
36
5
5
2
0
5
36
5
5
36
5
5
36
5
5
36
5
0
2147483647
2
1
4
4
0
100000008
0
-21524926
0
850834254
0
41988
36
0
5
2
0
5
2
0
5
2
1
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
4
5
2
1
5
36
5
5
2
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
0
5
36
4
5
36
4
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
36
5
5
36
5
5
36
5
5
36
4
4
1
2
Hacker
2
1
2
A system character used to own non-wizardly system verbs , properties, and objects in the core.
36
5
4
2
0
3412
0
1001876092
36
1
5
36
5
5
36
5
#37
Generic Database

144
36
-1
-1
-1
1
39
15
19
find find_key
36
173
-1
find_exact
36
173
-1
find_all find_all_keys
36
173
-1
_only
36
173
-1
_every
36
173
-1
_every_key
36
173
-1
insert
36
173
-1
delete
36
173
-1
delete2
36
173
-1
set_node
36
173
-1
make_node
2
173
-1
kill_node
2
173
-1
clearall
2
173
-1
clearall_big
36
173
-1
_kill_subtrees
36
173
-1
depth
36
173
-1
count_entries
36
173
-1
count_chars
36
173
-1
count
36
153
3
3
node_perms
data
 
9
2
r
36
5
0
4
36
1
4
4
2

2

4
0
4
0
36
0
5
36
4
4
1
2
Generic Database
36
5
2
A generic `database' (well, really more like a string-indexed array if you want the truth...). See `help $generic_db' for details.
36
5
4
2
0
16090
0
1001876092
36
1
5
36
5
5
36
5
#38
Everyman

19
36
-1
-1
-1
40
-1
-1
6
eval
2
173
-1
moveto
36
173
-1
eval_d
2
173
-1
call_verb
2
173
-1
bad_eval
2
173
-1
set_*
36
173
-1
0
145
5
36
5
4
0
36
4
5
36
5
5
2
1
2
Everyman ($no_one) can not receive mail.
36
5
5
36
5
5
36
5
4
0
36
5
5
36
4
4
0
36
4
5
36
1
5
2
0
2
$no_one
2
0
5
2
1
0
0
36
5
5
36
5
5
36
1
5
36
1
5
36
0
5
36
1
4
0
2
1
5
36
1
5
36
4
5
2
0
5
2
0
5
36
5
5
36
5
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
2
... no one out there to see it.
36
5
5
36
5
0
2147483647
2
1
0
-10000
36
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
1
-1
36
5
5
2
0
5
36
5
5
36
5
5
36
5
5
36
5
0
2147483647
2
1
4
4
0
0
0
0
0
987347720
0
0
36
0
5
2
0
5
2
0
5
2
1
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
4
5
2
1
5
36
5
5
2
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
0
5
36
4
5
36
4
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
36
5
5
36
5
5
36
5
5
36
4
4
4
2
Everyman
2
everyone
2
no_one
2
noone
2
1
2
The character used for "safe" evals.
36
5
4
2
0
6467
0
1001876092
36
1
5
36
5
5
36
5
#39
Player Database

16
36
-1
-1
-1
37
-1
25
6
load
36
173
-1
check
36
45
-1
init_for_core
36
173
-1
available
36
173
-1
suspend_restart
2
173
-1
why_bad_name
2
173
-1
6
stupid_names
frozen
reserved
 H
 e
 n
15
4
28
2
with
2
using
2
at
2
to
2
in
2
into
2
on
2
onto
2
upon
2
out
2
from
2
inside
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
of
2
me
2
you
2
here
36
5
0
0
36
5
4
0
36
1
4
4
2

2

4
2
2
Hacker
2
housekeeper
4
2
1
36
1
71
36
1
4
4
2
very
2

4
2
2
everyone
2
Everyman
4
2
1
38
1
38
36
1
4
4
2
o
2

4
2
2
noone
2
no_one
4
2
1
38
1
38
36
1
5
36
5
5
36
1
4
4
2

2
Hen
4
1
2
Wizard
4
1
1
2
36
0
5
36
4
4
3
2
player_db
2
plyrdb
2
pdb
36
5
4
2
2
A database containing all player names and aliases.  
2
See `help $player_db' for more information.
36
5
4
2
0
7767
0
1001876092
36
1
5
36
5
5
36
5
#40
Generic Mail Receiving Player

144
2
-1
-1
-1
6
90
-1
56
mail_forward
2
173
-1
receive_message
2
173
-1
display_message
2
173
-1
parse_message_seq from_msg_seq %from_msg_seq to_msg_seq %to_msg_seq subject_msg_seq body_msg_seq kept_msg_seq unkept_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 msg_seq_to_msg_num_list msg_seq_to_msg_num_string rm_message_seq undo_rmm expunge_rmm renumber keep_message_seq
2
173
-1
msg_summary_line
36
173
-1
msg_text
2
173
-1
notify_mail
2
173
-1
current_message
2
173
-1
get_current_message
2
173
-1
set_current_message
2
173
-1
make_current_message
2
173
-1
kill_current_message
2
173
-1
current_folder
2
173
-1
set_current_folder
2
173
-1
parse_folder_spec
2
173
-1
parse_mailread_cmd
2
173
-1
@mail
2
93
-2
@read @peek
2
93
-2
@next @prev
2
89
-2
@rmm*ail
2
89
-2
@renumber
2
25
-1
@unrmm*ail
2
89
-2
@send
2
93
-2
@answer @repl*y
2
89
-2
@forward
2
93
-2
@gripe
2
89
-2
@typo @bug @suggest*ion @idea @comment
2
89
-2
@skip
2
89
-2
@subscribe*-quick @unsubscribed*-quick
2
89
-2
mail_catch_up
2
173
-1
@rn check_mail_lists @subscribed
2
13
-1
mail_option
2
173
-1
@unsub*scribe
2
89
-2
send_self_netmail
2
173
-1
@netforw*ard
2
93
-2
@@sendmail
2
89
-2
@keep-m*ail @keepm*ail
2
89
-2
my_match_recipient
2
173
-1
expire_old_messages
2
173
-1
msg_full_text
2
173
-1
@resend
2
89
-2
make_current_message_new
2
173
-1
expirable_msg_seq
2
173
-1
format_for_netforward
2
173
-1
format_for_netforward_debug
2
173
-1
@nn
2
13
-1
@unread
2
89
-2
@refile @copym*ail
2
89
-2
@quickr*eply @qreply
2
89
-2
@mail-all-new*-mail
2
13
-1
@read-all-new*-mail @ranm
2
29
-1
@quick*send @qsend
2
89
-2
init_for_core
2
173
-1
confunc
2
173
-1
@add-notify
2
89
1
mail_notify
2
173
-1
10
_mail_task
messages_going
mail_lists
mail_notify
mail_forward
mail_options
message_keep_date
messages_kept
current_message
messages
145
0
0
2
5
4
0
2
4
4
0
2
5
4
2
4
0
4
0
2
1
4
0
2
5
4
0
2
5
0
0
2
5
4
0
2
5
4
2
0
0
0
0
2
4
4
0
2
4
5
36
1
5
2
0
5
2
0
5
2
1
1
86
2
5
5
2
5
5
36
1
5
36
1
5
36
0
5
36
1
5
2
1
5
36
1
5
2
4
5
2
0
5
2
0
5
2
5
5
2
5
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
0
2147483647
2
1
5
36
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
0
5
2
5
5
2
5
5
2
5
5
2
5
0
2147483647
2
1
5
36
0
5
2
0
5
2
0
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
4
5
2
1
5
2
5
5
2
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
0
5
2
4
5
2
4
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Generic Mail Receiving Player
2
1
5
2
5
4
2
0
67507
0
1001876092
36
1
5
2
5
5
2
5
#41
gender utilities

16
36
-1
-1
-1
79
-1
26
8
set
2
173
-1
add
2
173
-1
get_pronoun
36
173
-1
get_conj*ugation
36
173
-1
_verb_plural
36
173
-1
_verb_singular
36
173
-1
_do
36
173
-1
pronoun_sub
2
173
-1
15
is_plural
have
be
pronouns
genders
ps
po
pp
pq
pr
psc
poc
ppc
pqc
prc
22
4
11
0
0
0
0
0
0
0
0
0
0
0
0
0
1
0
1
0
1
0
1
0
1
36
5
4
11
2
has
2
has
2
has
2
has
2
has
2
has
2
have
2
have
2
have
2
have
2
have
36
5
4
11
2
is
2
is
2
is
2
is
2
is
2
is
2
are
2
am
2
are
2
are
2
are
36
5
4
10
2
ps
2
po
2
pp
2
pq
2
pr
2
psc
2
poc
2
ppc
2
pqc
2
prc
36
5
4
10
2
neuter
2
male
2
female
2
either
2
Spivak
2
splat
2
plural
2
egotistical
2
royal
2
2nd
36
5
4
10
2
it
2
he
2
she
2
s/he
2
e
2
*e
2
they
2
I
2
we
2
you
36
5
4
10
2
it
2
him
2
her
2
him/her
2
em
2
h*
2
them
2
me
2
us
2
you
36
5
4
10
2
its
2
his
2
her
2
his/her
2
eir
2
h*
2
their
2
my
2
our
2
your
36
5
4
10
2
its
2
his
2
hers
2
his/hers
2
eirs
2
h*s
2
theirs
2
mine
2
ours
2
yours
36
5
4
10
2
itself
2
himself
2
herself
2
(him/her)self
2
emself
2
h*self
2
themselves
2
myself
2
ourselves
2
yourself
36
5
4
10
2
It
2
He
2
She
2
S/He
2
E
2
*E
2
They
2
I
2
We
2
You
36
5
4
10
2
It
2
Him
2
Her
2
Him/Her
2
Em
2
H*
2
Them
2
Me
2
Us
2
You
36
5
4
10
2
Its
2
His
2
Her
2
His/Her
2
Eir
2
H*
2
Their
2
My
2
Our
2
Your
36
5
4
10
2
Its
2
His
2
Hers
2
His/Hers
2
Eirs
2
H*s
2
Theirs
2
Mine
2
Ours
2
Yours
36
5
4
10
2
Itself
2
Himself
2
Herself
2
(Him/Her)self
2
Emself
2
H*self
2
Themselves
2
Myself
2
Ourselves
2
Yourself
36
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
36
5
5
36
4
4
1
2
Gender_Utilities
36
5
4
1
2
This is the gender utilities utility package.  See `help $gender_utils' for more details.
36
5
4
2
0
12851
0
1001876092
36
1
5
36
5
5
36
5
#42
permissions utilities

24
2
-1
-1
-1
79
-1
21
4
controls
2
173
-1
apply
36
173
-1
caller
2
173
-1
controls_prop*erty controls_verb
2
173
-1
0
7
4
16
2
Miscellaneous routines for permissions checking
2

2
For a complete description of a given verb, do `help $perm_utils:verbname'
2

2
:controls(who,what) -- can who write on object what
2
:controls_property(who,what,propname) -- can who write on what.propname
2
These routines check write flags and also the wizardliness of `who'.
2

2
(these last two probably belong on $code_utils)
2

2
:apply(permstring,mods)
2
  -- used by @chmod to apply changes (e.g., +x) 
2
     to a given permissions string
2

2
:caller()
2
  -- returns the first caller in the callers() stack distinct from `this'
2
5
5
2
4
5
2
5
4
1
2
This is the permissions utilities utility package.  See `help $perm_utils' for more details.
2
5
4
2
0
3332
0
1001876092
36
1
5
2
5
5
2
5
#43
time utilities

16
36
-1
-1
-1
79
-1
51
17
day
36
13
-1
month
36
13
-1
ampm
36
13
-1
to_seconds
36
173
-1
sun
36
173
-1
from_ctime
36
173
-1
dhms dayshoursminutesseconds
36
173
-1
english_time
36
173
-1
from_day
36
173
-1
from_month
36
173
-1
dst_midnight
36
173
-1
time_sub
36
173
-1
mmddyy ddmmyy
36
173
-1
parse_english_time_interval
36
173
-1
seconds_until_date
36
165
-1
seconds_until_time
36
165
-1
short_format
36
173
-1
12
monthlens
timezones
stsd
ctcd
ct
corr
dayabbrs
days
months
monthabbrs
zones
time_units
19
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
36
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
36
5
0
2427
36
5
0
7276
36
5
0
7934
36
5
0
-122
36
5
4
7
2
Sun
2
Mon
2
Tue
2
Wed
2
Thu
2
Fri
2
Sat
36
5
4
7
2
Sunday
2
Monday
2
Tuesday
2
Wednesday
2
Thursday
2
Friday
2
Saturday
36
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
36
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
36
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
36
5
4
7
4
5
0
31536000
2
year
2
years
2
yr
2
yrs
4
5
0
2628000
2
month
2
months
2
mo
2
mos
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
36
5
4
30
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, h, 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
parse_english_time_interval("n1 u1 n2 u2...")
2
                                     => seconds in interval
2
seconds_until_time("hh:mm:ss")       => number of seconds from now until then
2
seconds_until_date("month",day,"hh:mm:ss",flag 
2
                                     => number of seconds from now until then
2
                                        (see verb help for details)
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
mmddyy        ([c]time)              => date in format MM/DD/YY
2
ddmmyy        ([c]time)              => date in format DD/MM/YY
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)                 
36
5
5
36
4
4
2
2
time utilities
2
time
36
5
4
1
2
This is the time utilities utility package.  See `help $time_utils' for more details.
36
5
4
2
0
21258
0
1001876092
36
1
5
36
5
5
36
5
#44
Editor Help

16
36
-1
-1
-1
30
-1
28
0
45
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
uncc
not-to
53
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).
36
5
4
2
2
*index*
2
Editor Help Topics
36
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

36
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
    ^^^^
36
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.
36
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...
36
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.
36
5
4
2
2
*forward*
2
unpublish
36
5
4
2
2
*forward*
2
unpublish
36
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.
36
5
4
6
2
Syntax:  pub*lish
2

2
By default, only you (and wizards) can read the text you are editing.
2
This command makes your text readable by the entire world (see `help view').
2
This is useful if you need help from someone or if you just want to show off your programming acumen.
2
Use `unpublish' to make your text private again.
36
5
4
12
2
(NOTE EDITOR)
2
Syntax:  mode
2
         mode string
2
         mode list
2
         
2
There are (currently) two 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
The other is list mode, in which text is always saved as a list of strings.
2
The mode is set when the text is first loaded (string mode if the text is a string, list mode otherwise), but can be changed using this command.
2

2
The first form above (i.e., without any arguments) reports the current mode.
36
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'.
36
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

36
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.
36
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.
36
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 ""
36
5
4
2
2
*pass*
2

36
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.]
36
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...
36
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.
36
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.
36
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.
36
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.
36
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.
36
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.
36
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.
36
5
4
6
2
Syntax:  pri*nt
2

2
Display your text without line numbers.
2

2
(MAIL ROOM)
2
Display your message including headers.
36
5
4
8
2
Syntax:  send
2

2
(MAIL ROOM)
2
Send the message, then exit the mail room if all of the addresses on the To: line turn out to be valid and usable (you can use the `who' command to check these in advance, though the status of recipients may change without warning).
2
If the To: line turns out to contain invalid recipients or recipients that are not usable by you, the message will not be sent and you will remain in the mail room.
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.
2

2
Note that there may be particularly long delays when sending to recipients with large forwarding/notification lists or when sending on occasions when the MOO is heavily loaded in general.  In such a case, it is possible to continue editing the message while the send is in progress; any such edits affect only the text in the editor.  In particular, the text of the message currently being sent remains as it was when you first typed the send command.  However, any editing will mark the text as "changed" meaning that you will need to explicitly `abort' or `quit' in order to leave the mail room even if the send concludes successfully.
36
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.
36
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.
36
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.
36
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.]
36
5
4
4
2
Syntax:  join        [<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') supresses this and joins the lines as is.  <range> defaults to the two lines surrounding the insertion point.
36
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.
36
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>.
36
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>.
36
5
4
3
2
Syntax:  w*hat
2

2
Prints information about the editing session.
36
5
4
3
2
Syntax:  abort
2

2
Abandons this editing session and any changes.
36
5
4
2
2
*forward*
2
quit
36
5
4
2
2
*forward*
2
quit
36
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?
36
5
4
2
2
*forward*
2
also-to
36
5
4
2
2
*forward*
2
not-to
36
5
4
6
2
Syntax:  not-to [<recipients>]
2

2
Synonym: uncc
2

2
(MAIL ROOM)
2
Removes the specified recipients from the To: line of your message.
36
5
5
36
5
4
1
2
edit-index
36
1
5
36
4
4
1
2
Editor Help
36
5
0
0
36
5
4
2
0
21214
0
1001876092
36
1
5
36
5
5
36
5
#45
Generic Mail Recipient

144
36
-1
-1
-1
1
34
94
42
set_aliases
36
173
-1
look_self
36
173
-1
is_writable_by
36
173
-1
is_readable_by
36
173
-1
is_usable_by
36
173
-1
mail_notify
36
173
-1
mail_forward
36
173
-1
moderator_forward
36
173
-1
add_forward
36
173
-1
delete_forward
36
173
-1
add_notify
36
173
-1
delete_notify
36
173
-1
receive_message
36
173
-1
ok
36
173
-1
ok_write
36
173
-1
parse_message_seq from_msg_seq %from_msg_seq to_msg_seq %to_msg_seq subject_msg_seq body_msg_seq kept_msg_seq unkept_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 msg_seq_to_msg_num_list msg_seq_to_msg_num_string
36
173
-1
length_date_gt
36
173
-1
rm_message_seq
36
173
-1
undo_rmm expunge_rmm renumber keep_message_seq
36
173
-1
own_messages_filter
36
173
-1
messages
36
173
-1
date_sort
36
173
-1
_fix_last_msg_date
36
173
-1
moderator_notify
36
173
-1
msg_summary_line
36
173
-1
__check
36
173
-1
__fix
2
173
-1
init_for_core
2
173
-1
initialize
2
173
-1
mail_name_old mail_name short_mail_name
36
173
-1
mail_names
36
173
-1
expire_old_messages
36
173
-1
moveto
36
173
-1
accept_subname
36
173
-1
mail_name_new mail_name
36
173
-1
@mail-n*ame
36
105
12
@rm-mail-n*ame @remove-mail-n*ame
36
153
5
@add-mail-n*ame
36
153
1
msg_full_text
36
173
-1
@set_expire
36
109
1
@register @netregister
2
109
1
@validate
36
109
0
20
moderator_notify
last_msg_date
messages_going
moderated
moderator_forward
writers
readers
mail_notify
mail_forward
expire_period
last_used_time
messages
rmm_own_msgs
guests_can_send_here
names
accept_subname
messages_kept
registered_email
email_validated
validation_password
26
4
0
36
5
0
0
36
1
4
0
36
0
4
0
36
5
2
%n (%#) can't send to moderated list %t (%[#t]) directly.
36
5
4
0
36
5
4
0
36
5
4
0
36
1
2
%t (%[#t]) is a generic recipient.
36
1
0
2592000
36
1
0
0
36
1
4
0
36
0
0
1
36
5
0
0
36
5
4
0
36
1
0
1
36
5
4
0
36
1
2

36
0
0
1
36
0
2

36
0
5
36
4
4
1
2
Generic Mail Recipient
36
1
2
This can either be a mailing list or a mail folder, depending on what mood you're in...
36
5
4
2
0
34632
0
1001876092
36
1
5
36
5
5
36
5
#46
Mail Distribution Center

16
36
-1
17
-1
1
-1
45
58
resolve_addr
36
173
-1
sends_to
36
173
-1
send_message
36
173
-1
raw_send
2
173
-1
mail_forward mail_notify
36
173
-1
touch
36
173
-1
look_self
36
173
-1
acceptable
36
173
-1
check_names
36
173
-1
match_old match
36
173
-1
match_recipient
36
173
-1
match_failed
36
173
-1
make_message
36
173
-1
name
36
173
-1
name_list
36
173
-1
parse_address_field
36
173
-1
display_seq_full
2
173
-1
display_seq_headers
2
173
-1
rm_message_seq
2
173
-1
undo_rmm
2
173
-1
expunge_rmm list_rmm
2
173
-1
renumber
2
173
-1
msg_summary_line
36
173
-1
parse_message_seq
2
173
-1
_parse_from _parse_to
36
173
-1
_parse_date
36
173
-1
new_message_num
2
173
-1
length_all_msgs
2
173
-1
length_date_le
2
173
-1
length_date_gt
2
173
-1
length_num_le
2
173
-1
exists_num_eq
2
173
-1
from_msg_seq
2
173
-1
%from_msg_seq
2
173
-1
to_msg_seq
2
173
-1
%to_msg_seq
2
173
-1
subject_msg_seq
2
173
-1
body_msg_seq
2
173
-1
messages_in_seq
2
173
-1
to_text
36
173
-1
is_readable_by is_writable_by is_usable_by
36
173
-1
reserved_pattern
36
173
-1
is_recipient
36
173
-1
keep_message_seq
2
173
-1
kept_msg_seq unkept_msg_seq
2
173
-1
set_mail_name add_mail_name
36
173
-1
remove_mail_name
36
173
-1
match_new match
36
173
-1
includes
36
173
-1
_accept_subname
36
173
-1
match_subname
2
173
-1
match_recipient_new
36
173
-1
msg_seq_to_msg_num_string
2
173
-1
msg_seq_to_msg_num_list
2
173
-1
send_log_message
36
173
-1
parse_misc_headers
36
173
-1
resend_message
2
173
-1
init_for_core
2
165
-1
5
options
reserved_patterns
player_expire_time
player_default_@mail
max_mail_notify
11
4
11
2
include
2
noinclude
2
all
2
sender
2
nosubject
2
expert
2
enter
2
sticky
2
@mail
2
manymsgs
2
replyto
36
5
4
0
36
1
0
2592000
36
5
2
last:15
36
5
0
15
36
5
5
36
4
4
2
2
Mail Distribution Center
2
Postmaster
36
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

36
5
4
2
0
52863
0
1001876092
36
1
5
36
5
5
36
5
#47
Mail Room

16
36
-1
-1
-1
50
-1
-1
26
working_on
36
173
-1
parse_invoke
36
173
-1
init_session
36
173
-1
pri*nt
36
25
-1
message_with_headers
36
173
-1
subj*ect:
36
89
-2
set_subject
36
173
-1
sending
36
173
-1
to*:
36
89
-2
also*-to: cc*:
36
89
-2
not-to*: uncc*:
36
89
-2
parse_recipients
36
173
-1
recipient_names
36
173
-1
make_message
36
173
-1
name_list
36
173
-1
parse_msg_headers
36
173
-1
check_answer_flags
36
173
-1
reply-to*: replyto*:
36
89
-2
send
2
9
-1
who
36
29
-1
showlists
36
25
-1
subsc*ribe
36
89
1
unsubsc*ribe
36
89
5
retain_session_on_exit
36
173
-1
no_littering_msg
36
173
-1
local_editing_info
36
173
-1
4
replytos
recipients
subjects
sending
93
4
0
36
0
4
0
36
0
4
0
36
0
4
0
36
0
4
0
36
1
4
0
36
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
join*l
2
fill
4
12
2
y*ank
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
36
5
4
0
36
5
2
Message body is empty.
36
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

36
5
0
0
36
1
0
1
36
5
2
You need to either SEND it or ABORT it before you can start another message.
36
5
4
8
4
2
2
sending
0
0
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
36
1
2
%N flattens out into a largish 32 cent postage stamp and floats away.
36
5
2
A largish 32 cent postage stamp floats into the room and fattens up into %n.
36
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!
36
5
5
36
5
5
36
5
2
You're not editing anything!
36
5
4
0
36
0
4
0
36
1
4
0
36
0
4
0
36
0
4
0
36
1
2
%L [mailing]
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
1
36
5
4
0
36
4
1
-1
36
5
0
562290767
36
5
4
0
36
4
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
36
5
5
36
5
5
36
5
5
36
4
4
1
2
Mail Room
36
5
5
36
5
4
2
0
23072
0
1001876092
36
1
5
36
5
5
36
5
#48
Note Editor

16
36
-1
-1
-1
50
-1
47
12
e*dit
36
25
-1
save
36
25
-1
init_session
36
173
-1
working_on
36
173
-1
parse_invoke
36
173
-1
note_text
2
173
-1
set_note_text
2
173
-1
note_match_failed
36
173
-1
w*hat
36
9
-1
mode
36
25
-1
local_editing_info
36
173
-1
set_*
36
173
-1
2
strmode
objects
91
4
0
36
1
4
0
36
5
4
0
36
1
4
0
36
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
join*l
2
fill
4
7
2
y*ank
2
w*hat
2
mode
2
e*dit
2
save
2
abort
2
q*uit,done,pause
36
5
4
0
36
5
2
Note is devoid of text.
36
5
4
3
4
2
2
e*dit
2
<note>
4
2
2
save
2
[<note>]
4
2
2
mode
2
[string|list]
36
5
0
0
36
1
5
36
5
2
You need to ABORT or SAVE this note before editing any other.
36
5
4
6
4
2
2
strmode
0
0
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
36
1
2
A small swarm of 3x5 index cards arrives, engulfs %n, and carries %o away.
36
5
2
A small swarm of 3x5 index cards blows in and disperses, revealing %n.
36
5
4
3
2
Partially edited text will be here when you get back.
2
To return, give the `@notedit' command with no arguments.
2
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!
36
5
2
Note has not been modified since the last save.
36
5
2
There are changes.
36
5
2
Use the EDIT command to select a note.
36
5
4
0
36
0
4
0
36
1
4
0
36
0
4
0
36
0
4
0
36
1
2
%L [editing notes]
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
1
36
5
4
0
36
4
1
-1
36
5
0
1968131562
36
5
4
0
36
4
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
36
5
5
36
5
5
36
5
5
36
4
4
2
2
Note Editor
2
nedit
36
5
5
36
5
4
2
0
10688
0
1001876092
36
1
5
36
5
5
36
5
#49
Verb Editor

16
36
-1
-1
-1
50
-1
48
12
e*dit
36
25
-1
com*pile save
36
73
-2
working_on
36
173
-1
init_session
36
173
-1
parse_invoke
36
173
-1
fetch_verb_code
2
173
-1
set_verb_code
2
173
-1
local_editing_info
2
173
-1
verb_name
2
173
-1
verb_args
2
173
-1
comment
36
89
-2
uncomment
36
89
-2
2
objects
verbnames
91
4
0
36
0
4
0
36
0
4
0
36
1
4
0
36
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
join*l
2
fill
4
6
2
y*ank
2
w*hat
2
e*dit
2
com*pile
2
abort
2
q*uit,done,pause
36
5
4
0
36
5
2
Verb body is empty.
36
5
4
2
4
2
2
e*dit
2
<obj>:<verb>
4
2
2
com*pile
2
[as <obj>:<verb>]
36
5
0
0
36
1
5
36
5
2
You need to either COMPILE or ABORT this verb before you can start on another.
36
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
36
1
2
You hear the bips of keyclick, the sliding of mice and the hum of computers in the distance as %n fades slowly out of view, heading towards them.
36
5
2
There are the light bips of keyclick and the sliding of mice as %n fades into view, shoving %r away from the console, which promptly fades away.
36
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!
36
5
2
The verb has no pending changes.
36
5
2
You have changed the verb since last successful compile.
36
5
2
First, you have to select a verb to edit with the EDIT command.
36
5
4
0
36
0
4
0
36
1
4
0
36
0
4
0
36
0
4
0
36
1
2
%L [editing verbs]
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
1
36
5
4
1
1
119
36
4
1
2
36
5
0
471792151
36
5
4
0
36
4
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
0
1
2
0
5
2
0
5
36
5
5
36
5
5
36
5
5
36
4
4
4
2
Verb Editor
2
vedit
2
verbedit
2
verb edit
36
5
5
36
5
4
2
0
14451
0
1001876092
36
1
5
36
5
5
36
5
#50
Generic Editor

144
36
-1
-1
-1
3
49
62
68
say
36
93
-2
emote
36
93
-2
enter
36
25
-1
lis*t view
36
89
-2
ins*ert n*ext p*revious .
36
25
-1
del*ete
36
89
-2
f*ind
36
93
-2
s*ubst
36
93
-2
m*ove c*opy
36
89
-2
join*literal
36
89
-2
fill
36
89
-2
pub*lish perish unpub*lish depub*lish
36
9
-1
w*hat
36
13
-1
abort
36
9
-1
done q*uit pause
36
13
-1
huh2
2
173
-1
insertion
36
173
-1
set_insertion
36
173
-1
changed retain_session_on_exit
36
173
-1
set_changed
36
173
-1
origin
36
173
-1
set_origin
36
173
-1
readable
36
173
-1
set_readable
36
173
-1
text
36
173
-1
load
36
173
-1
working_on
36
173
-1
ok
36
173
-1
loaded
36
173
-1
list_line
36
173
-1
insert_line
36
173
-1
append_line
36
173
-1
join_lines
36
173
-1
parse_number
36
173
-1
parse_range
36
173
-1
parse_insert
36
173
-1
parse_subst
36
173
-1
invoke
36
173
-1
suck_in
36
173
-1
new_session
2
173
-1
kill_session
2
173
-1
reset_session
2
173
-1
kill_all_sessions
2
173
-1
acceptable
36
173
-1
enterfunc
36
173
-1
exitfunc
36
173
-1
@flush
36
109
-2
@stateprop
36
153
11
@rmstateprop
36
153
5
initialize
36
173
-1
init_for_core
2
173
-1
set_stateprops
36
173
-1
description
36
173
-1
commands_info
36
173
-1
match_object
36
173
-1
who_location_msg
36
173
-1
nothing_loaded_msg no_text_msg change_msg no_change_msg no_littering_msg depart_msg return_msg previous_session_msg
36
173
-1
announce announce_all announce_all_but tell_contents
36
173
-1
fill_string
36
173
-1
here_huh
36
173
-1
match
2
173
-1
get_room
36
173
-1
invoke_local_editor
2
173
-1
_stateprop_length
2
173
-1
print
2
9
-1
accept
36
173
-1
y*ank
2
89
-2
do_flush
36
173
-1
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
89
4
0
36
1
4
0
36
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
join*l
2
fill
4
4
2
y*ank
2
w*hat
2
abort
2
q*uit,done,pause
36
5
1
44
36
5
2
There are no lines of text.
36
5
4
16
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
join*l
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

4
2
2
y*ank
2
from <text-source>
36
5
0
0
36
1
0
0
36
5
2

36
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
36
1
2
%N heads off to the Generic Editing Room.
36
5
2
%N comes back from the Generic Editing Room.
36
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.
36
5
2
There have been no changes since the last save.
36
5
2
Text has been altered since the last save.
36
5
2
You're not currently editing anything.
36
5
4
0
36
0
4
0
36
1
4
0
36
0
4
0
36
0
4
0
36
1
2
%L [editing]
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
1
36
5
4
0
36
4
1
-1
36
5
0
1397392137
36
5
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
36
5
5
36
5
5
36
5
5
36
4
4
3
2
Generic Editor
2
gedit
2
edit
36
5
4
0
36
5
4
2
0
51541
0
1001876092
36
1
5
36
5
5
36
5
#51
matching utilities

16
36
-1
-1
-1
79
-1
52
8
match
36
173
-1
match_nth
36
173
-1
match_verb
2
173
-1
match_list
36
173
-1
parse_ordinal_reference parse_ordref
36
173
-1
parse_possessive_reference
36
173
-1
object_match_failed
36
165
-1
init_for_core
2
173
-1
4
ordn
ordw
ordinal_regexp
matching_room
11
4
10
2
first
2
second
2
third
2
fourth
2
fifth
2
sixth
2
seventh
2
eighth
2
ninth
2
tenth
36
5
4
10
2
1st
2
2nd
2
3rd
2
4th
2
5th
2
6th
2
7th
2
8th
2
9th
2
10th
36
5
2
%<%(first%|second%|third%|fourth%|fifth%|sixth%|seventh%|eighth%|ninth%|tenth%|1st%|2nd%|3rd%|4th%|5th%|6th%|7th%|8th%|9th%|10th%)%>
36
5
1
-1
36
1
4
11
2
$match_utils defines the following verbs:
2

2
match
2
match_nth
2
match_verb
2
match_list
2
parse_ordinal_reference (alias parse_ordref)
2
parse_possessive_reference
2
object_match_failed
2

2
For more documentation, see help $match_utils:<specific verb>.
36
5
5
36
4
4
1
2
matching utilities
36
5
5
36
5
4
2
0
9103
0
1001876092
36
1
5
36
5
5
36
5
#52
object utilities

16
2
-1
-1
-1
79
-1
53
29
has_property
2
173
-1
all_properties all_verbs
2
173
-1
has_verb
2
173
-1
has_callable_verb
2
173
-1
match_verb
2
173
-1
isa
36
173
-1
ancestors
36
173
-1
ordered_descendants
36
173
-1
contains
36
173
-1
all_contents
36
173
-1
findable_properties
2
173
-1
owned_properties
2
173
-1
property_conflicts
2
173
-1
descendants_with_property_suspended
2
173
-1
locations
2
173
-1
all_properties_suspended all_verbs_suspended
2
173
-1
connected
36
173
-1
isoneof
36
173
-1
defines_verb
2
173
-1
defines_property
2
173
-1
has_any_verb has_any_property
2
173
-1
has_readable_prop*erty hrp
2
173
-1
descendants descendents
36
173
-1
leaves
36
173
-1
branches
36
173
-1
descendants_suspended descendents_suspended
2
173
-1
leaves_suspended
2
173
-1
branches_suspended
2
173
-1
fertile_objects fertile_objects_suspended
2
173
-1
0
7
4
43
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
  property_conflicts (object,newparent) => can object chparent to newparent?
2
  isoneof     (object,list)     => true if object :isa class in list of parents
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
  locations         (object) => list of location hierarchy above object
2

2
Verifying verbs and properties:
2
  has_property(object,pname) => false/true   according as object.(pname) exists
2
  has_readable_property(object,pname) => false/true if prop exists and is +r
2
  defines_property(object,pname) => does object *define* this property
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
  defines_verb(object,vname) => does this object *define* this verb
2
  match_verb  (object,vname) => false/{location, newvname}
2
                               (identify location and usable name of verb)
2

2
Player checking:
2
  connected         (object) => true if object is a player and is connected
2

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
2
5
5
2
4
4
1
2
object utilities
2
5
4
1
2
This is the object utilities utility package.  See `help $object_utils' for more details.
2
5
4
2
0
20766
0
1001876092
36
1
5
2
5
5
2
5
#53
lock utilities

16
2
-1
-1
-1
79
-1
56
11
init_scanner
2
173
-1
scan_token
2
173
-1
canonicalize_spaces
2
173
-1
parse_keyexp
2
173
-1
parse_E
2
173
-1
parse_A
2
173
-1
eval_key
2
173
-1
match_object
2
173
-1
unparse_key
2
173
-1
eval_key_new
2
173
-1
parse_A_new
2
173
-1
5
player
input_index
input_length
input_string
index_incremented
12
1
2
2
5
0
83
2
5
0
82
2
5
2
me - Will lock the room so nobody can come in. @unlock here will unlock your room.
2
5
0
82
2
5
4
13
2
These routines are used when locking objects, and when testing an object's lock before allowing use (such as in an exit).
2

2
:parse_keyexp   (string keyexpression, object player)
2
        => returns an object or list for the new key as defined by the
2
           keyexpression or a string describing the error if it failed.
2

2
:eval_key       (LIST|OBJ key, testobject)
2
        => returns true if the given testobject satisfies the key.
2

2
:unparse_key    (LIST|OBJ key)
2
        => returns a string describing the key in english/moo-code terms.
2

2
For more information on keys and locking, read `help locking', `help keys', and `help @lock'.
2
5
5
2
4
4
1
2
lock utilities
2
5
2
This the lock utilities package, used by the MOOwide locking mechanisms. See `help $lock_utils' for more details.
2
5
4
2
0
9776
0
1001876092
36
1
5
2
5
5
2
5
#54
generic letter

144
2
-1
-1
-1
9
-1
95
3
burn
2
41
-1
burn_succeeded_msg oburn_succeeded_msg burn_failed_msg oburn_failed_msg
2
173
-1
do_burn
2
173
-1
4
oburn_succeeded_msg
oburn_failed_msg
burn_failed_msg
burn_succeeded_msg
70
2
stares at %t; %[tps] bursts into flame and disappears, leaving no ash.
2
5
0
0
2
5
2
%T might be damp.  In any case, %[tps] won't burn.
2
5
2
%T burns with a smokeless flame and leaves no ash.
2
5
5
2
5
5
36
4
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
36
5
5
36
5
5
36
5
2
This is a private letter.
36
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2

2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
5
36
4
4
1
2
generic letter
36
5
2
Some writing on the letter explains that you should 'read letter', and when you've finished, 'burn letter'.
36
5
4
2
0
3190
0
1001876092
36
1
5
2
5
5
2
5
#55
list utilities

16
36
-1
-1
-1
79
-1
24
40
make
36
173
-1
range
36
173
-1
map_prop*erty
2
173
-1
map_verb
2
173
-1
map_arg
2
173
-1
map_builtin
2
173
-1
find_insert
36
173
-1
remove_duplicates
36
173
-1
arrayset
36
173
-1
setremove_all
36
173
-1
append
36
173
-1
reverse
36
173
-1
_reverse
36
173
-1
compress
36
173
-1
sort
36
173
-1
sort_suspended
2
173
-1
slice
36
173
-1
assoc
36
173
-1
iassoc
36
173
-1
iassoc_suspended
2
173
-1
assoc_prefix
36
173
-1
iassoc_prefix
36
173
-1
iassoc_sorted
36
173
-1
sort_alist
36
173
-1
sort_alist_suspended
2
173
-1
randomly_permute
36
173
-1
count
2
173
-1
flatten
36
173
-1
longest shortest
36
173
-1
check_nonstring_tell_lines
36
173
-1
reverse_suspended
2
173
-1
_reverse_suspended
2
173
-1
randomly_permute_suspended
2
173
-1
swap_elements
36
173
-1
random_item random_element
36
173
-1
assoc_suspended
2
173
-1
passoc
2
173
-1
max_length
2
165
-1
make_alist
2
173
-1
flat_list_of_STR
2
173
-1
1
nonstring_tell_lines
8
4
0
36
1
4
43
2
append            (list,list,..) => result of concatenating the given lists
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
count             (elt,list)     => count of elt found in list.
2
flatten           (list)         => flatten all recursive lists into one list
2
randomly_permute  (list)         => list with elements randomly permuted
2
longest           (list)         => longest in list (consisting of str or list)
2
shortest          (list)         => shortest in list (as above)
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
map_builtin (objectlist, function)  => applies function to all in objectlist
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
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
iassoc_sorted(targ,slist[,i]) => index of last element in sortedlist <= targ
2
slice             (alist[,i]) => list of i-th elements
2
sort_alist        (alist[,i]) => alist sorted on i-th elements.
2

2
-- Functions that suspend --
2

2
Each of these either suspends(0) as needed or takes an interval in seconds for the suspend as a first argument. See help $list_utils:<verb>.
2

2
sort_suspended          iassoc_suspended          sort_alist_suspended
2
reverse_suspended       randomly_permute_suspended
36
5
5
36
4
4
1
2
list_utilities
36
5
4
1
2
This is the list utilities utility package.  See `help $list_utils' for more details.
36
5
4
2
0
26880
0
1001876092
36
1
5
36
5
5
36
5
#56
command utilities

16
2
-1
-1
-1
79
-1
42
16
object_match_failed
2
173
-1
player_match_result player_match_failed
2
173
-1
read
2
173
-1
read_lines
2
173
-1
yes_or_no
2
173
-1
read_lines_escape
2
173
-1
suspend
2
173
-1
running_out_of_time
36
173
-1
suspend_if_needed
2
173
-1
dump_lines
36
173
-1
explain_syntax
2
173
-1
do_huh
2
165
-1
task_info
2
173
-1
init_for_core
2
173
-1
kill_if_laggy
36
173
-1
validate_feature
36
173
-1
2
lag_samples
feature_task
9
4
0
2
1
2
hey, neat, no feature verbs have been run yet!
36
0
4
34
2
$command_utils is the repository for verbs that are of general usefulness to authors of all sorts of commands.  For more details about any of these verbs, use `help $command_utils:<verb-name>'.
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

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
:read_lines_escape(escapes[,help])
2
                -- Like read_lines, except you can provide more escapes
2
                   to terminate the read.
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.
2
5
5
2
4
5
2
5
4
1
2
This is the command utilities utility package.  See `help $command_utils' for more details.
2
5
4
2
0
18634
0
1001876092
36
1
5
2
5
5
2
5
#57
generic wizard

16
2
-1
-1
-1
58
2
36
44
@chown*#
2
89
-2
@shout
2
89
-2
@grant @grants* @transfer
2
89
1
@programmer
2
25
-1
make-core-database
2
25
-1
@shutdown
2
89
-2
@dump-d*atabase
2
9
-1
@who-calls
2
89
-2
mcd_2
2
13
-1
@toad @toad! @toad!!
2
89
-2
@untoad @detoad
2
89
-2
@quota
2
89
12
@players
2
89
-2
kill_aux_wizard_parse
2
173
-1
@grepcore @egrepcore
2
89
-2
@net-who @@who
2
89
-2
@make-player
2
89
-2
@abort-sh*utdown
2
89
-2
toad_msg toad_victim_msg programmer_msg programmer_victim_msg newt_msg newt_victim_msg
2
173
-1
moveto
2
173
-1
@newt
2
89
-2
@unnewt @denewt @get-better
2
89
-2
@register
2
89
-2
@new-password @newpassword
2
89
12
@log
2
89
-2
@guests
2
25
-1
@rn mail_catch_up check_mail_lists current_message set_current_message get_current_message make_current_message kill_current_message @nn
2
13
-1
@blacklist @graylist @redlist @unblacklist @ungraylist @unredlist @spooflist @unspooflist
2
89
-2
@corify
2
89
13
@make-guest
2
25
-1
@temp-newt
2
89
11
@deprog*rammer
2
89
-2
display_list
2
173
-1
parse_templist_duration
36
173
-1
check_site_entries
2
173
-1
@lock-login @unlock-login @lock-login!
2
89
-2
@boot
2
25
-1
@configure*-core
2
9
-1
@email
2
89
1
@mvprop
2
81
1
@renprop
2
81
1
@make-mail*-list
2
25
-1
init_for_core
2
173
-1
_web_menu
2
173
-1
9
newt_victim_msg
newt_msg
public_identity
programmer_msg
programmer_victim_msg
toad_victim_msg
toad_msg
mail_identity
advertised
184
2

2
5
2
%n @newts %d (%[#d])
2
5
1
-1
2
5
2
%d is now a programmer.
2
5
2
You are now a programmer.
2
5
2
Have a nice life...
2
5
2
%n @toads %d (%[#d])
2
5
1
-1
2
4
0
1
2
5
5
36
1
5
36
1
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
4
5
2
4
4
8
1
91
1
92
1
110
1
114
1
119
1
123
1
130
1
127
36
1
5
2
0
5
2
0
5
2
1
1
23
2
5
5
2
5
5
36
1
5
36
1
5
36
0
5
36
1
5
2
1
5
36
1
5
2
4
5
2
0
5
2
0
5
2
5
5
2
5
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
0
2147483647
2
1
5
36
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
really impossible password to type
2
0
5
2
5
5
2
5
5
2
5
5
2
5
0
2147483647
2
1
5
36
0
5
2
0
5
2
0
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
4
5
2
1
5
2
5
5
2
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
0
5
2
4
5
2
4
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
wizard.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
5
2
4
4
1
2
player
2
1
2
You see a wizard who chooses not to reveal its true appearance.
2
5
4
2
0
77860
0
1001876092
36
1
5
2
5
5
2
5
#58
generic programmer

144
2
-1
-1
-1
4
57
-1
38
@prop*erty
2
89
-2
@chmod*#
2
89
-2
@args*#
2
89
-2
eval*-d
2
89
-2
@rmprop*erty
2
89
-2
@verb
2
89
-2
@rmverb*#
2
25
-1
@forked
2
25
-1
@kill @killq*uiet
2
25
-1
@copy @copy-x @copy-move
2
89
1
_kill_task_message
2
173
-1
@prog*ram @program#
2
89
-2
@setenv
2
89
-2
@pros*pectus pros*pectus
2
93
-2
@d*isplay
2
25
-1
@db*size
2
9
-1
@gethelp
2
93
-2
@grep @egrep
2
89
-2
@s*how
2
89
-2
@check-p*roperty
2
25
-1
set_eval_env
36
173
-1
@clearp*roperty @clprop*erty
2
25
-1
@disown @disinherit
2
89
-2
eval_cmd_string
2
173
-1
@dump
2
89
-2
#*
2
89
-2
eval_value_to_string
2
173
-1
@progo*ptions @prog-o*ptions @programmero*ptions @programmer-o*ptions
2
89
-2
prog_option
2
173
-1
set_prog_option
2
173
-1
@list*#
2
89
-2
set_eval_subs
2
13
-1
@verbs*
2
25
-1
@forked-v*erbose
2
25
-1
@code
2
81
-2
@properties @props
2
89
-2
@generic-kids
2
25
-1
_web_menu
2
173
-1
4
eval_subs
eval_ticks
eval_env
prog_options
175
4
0
36
1
0
3
36
1
2
here=player.location;me=player
36
1
4
0
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
4
5
2
4
5
36
1
5
2
0
5
2
0
5
2
1
4
4
1
22
1
28
1
18
1
19
2
5
5
2
5
5
36
1
5
36
1
5
36
0
5
36
1
5
2
1
5
36
1
5
2
4
5
2
0
5
2
0
5
2
5
5
2
5
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
0
2147483647
2
1
0
0
36
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
0
5
2
5
5
2
5
5
2
5
5
2
5
0
2147483647
2
1
5
36
0
5
2
0
5
2
0
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
4
5
2
1
5
2
5
5
2
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
0
5
2
4
5
2
4
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
programmer.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
5
2
4
4
2
2
generic
2
programmer
2
1
2
You see a player who is too experienced to have any excuse for not having a description.
2
5
4
2
0
66862
0
1001876092
36
1
5
2
5
5
2
5
#59
code utilities

16
36
-1
-1
-1
79
-1
93
58
eval_d
2
85
-2
toint tonum
36
173
-1
toobj
36
173
-1
toerr
36
173
-1
error_name
36
173
-1
show_object
2
173
-1
show_property
2
173
-1
show_verbdef
2
173
-1
explain_verb_syntax
2
173
-1
verb_p*erms verb_permi*ssions
2
173
-1
verb_loc*ation
36
173
-1
verb_documentation
2
173
-1
set_verb_documentation
2
173
-1
parse_propref
2
173
-1
parse_verbref
2
173
-1
parse_argspec
36
173
-1
prepositions
36
173
-1
short_prep
36
173
-1
full_prep
36
173
-1
get_prep
36
173
-1
_fix_preps
36
173
1
find_verb_named
2
173
-1
find_last_verb_named
2
173
-1
find_callable_verb_named
2
173
-1
verbname_match(new)
36
173
-1
find_verbs_containing
2
173
-1
_find_verbs_containing
2
173
-1
find_verbs_matching
2
173
-1
_find_verbs_matching
2
173
-1
_grep_verb_code
2
173
-1
_egrep_verb_code
2
173
-1
_parse_audit_args
36
173
-1
help_db_list
2
173
-1
help_db_search
36
173
-1
corify_object
36
173
-1
inside_quotes
2
173
-1
verb_or_property
2
173
-1
task_valid
2
173
-1
task_owner
2
173
-1
argstr
2
173
-1
verbname_match
36
173
-1
substitute
36
173
-1
show_who_listing
2
173
-1
_egrep_verb_code_all
2
173
-1
_grep_verb_code_all
2
173
-1
verb_usage
2
173
-1
verb_frame
36
173
-1
verb_all_frames
36
173
-1
move_verb
2
173
-1
move_prop*erty
2
173
-1
eval_d_util
2
165
-1
display_callers
2
173
-1
callers_text
2
173
-1
set_property_value set_verb_or_property
2
165
-1
owns_task
2
173
-1
dflag_on
2
173
-1
update_last_modified
2
165
-1
queued_tasks_matching
2
165
-1
10
_version
_multi_preps
_other_preps_n
_other_preps
_short_preps
_all_preps
builtin_props
error_names
error_list
prepositions
17
2
1.8.1
36
5
4
7
2
off
2
from
2
out
2
on
2
on top
2
in
2
in front
36
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
36
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
36
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
36
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
36
5
4
9
2
name
2
r
2
w
2
f
2
programmer
2
wizard
2
owner
2
location
2
contents
2
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
36
5
4
16
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
3
15
36
5
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
36
5
4
62
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
toint(string)           => integer (or E_TYPE if string is not a integer)
2
toobj(string)           => object (or E_TYPE if string is not an object)
2
toerr(integer 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_frame()      => callers()-style frame for the current verb.
2
verb_all_frames() => entire callers() stack including current verb.
2
verb_usage([object,verbname]) => returns first line of verb doc, usually usage
2
verb_documentation([object,verbname]) => documentation at beginning of
2
           verb code, if any -- default is the calling verb
2
set_verb_documentation(object,verbname,text) => sets text at beginning of 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 0 if not found
2
find_last_verb_named     (object,name[,n]) => verb number of last verb match
2
find_callable_verb_named (object,name[,n]) => verb number or 0 if not found
2
find_verbs_containing (pattern[,object|objlist]) => does work for @grep
2
find_verbs_matching (pattern[,object|objlist]) => does work for @egrep
2
move_verb (from obj,name,to obj[,newname]) => move a verb from object to object
2

2
move_prop (from obj,name,to obj[,newname]) => move a property to another object
2

2
   Verbs that do the actual dirty work for command lines verbs:
2

2
@show           => show_object  (object)
2
                   show_property(object,propname)
2
                   show_verbdef (object,verbname)
2
explain_syntax  => explain_verb_syntax(thisname,verbname,@verbargs)
2
eval*-d         => eval_d(code)
2
help            => help_db_list([player])
2
                   help_db_search(string topic, dblist)
2
@who            => show_who_listing(players [,more_players])
2
@check-full     => display_callers([callers() output])
2

2
   Random but useful verbs
2

2
verb_or_property(object,name[,@args]) => result of verb or property call,
2
                                         or E_PROPNF
2
corify_object(object)     => if the object is corified, returns $<name>
2
task_valid(INT task_id)   => returns true if task_id is currently running.
2
task_owner(INT task_id)   => returns owner of task_id, if running
2
owns_task(OBJ who,INT task_id) => returns whether who owns task_id (if running)
2
argstr(verb,args[,argstr] => returns a corrected argstr (see full verb help)
2
substitute(string,subs)   => subs in form {{"target", "sub"}, {...}, ...}
36
5
5
36
4
4
2
2
code
2
utils
36
5
4
1
2
This is the code utilities utility package.  See `help $code_utils' for more details.
36
5
4
2
0
55721
0
1001876092
36
1
5
36
5
5
36
5
#60
Help Database

16
36
-1
-1
-1
30
-1
44
9
player_quota
2
173
-1
prog_quota
2
173
-1
get_topic
2
173
-1
find_topics
2
173
-1
full_index
36
173
-1
index_list
36
173
-1
wizard_list
2
173
-1
dump_topic
36
173
-1
find_full_index_topic
36
173
-1
121
@uptime
wizard-list
@wrap
full-index
 index
gen-index
@pagelength
@more
programming
@linelength
@gaglist
::
@comment

spoofing
privacy
@examine
security
@sweep
@paranoid
@check
@eject
@quit
whereis
@suggest
@idea
@bug
@typo
@notedit
editors
@memory
"
:
@lastlog
@version
miscellaneous
insert
information
?
put
remove
burn
letters
decrypt
encrypt
delete
erase
write
read
examine
hand
throw
take
@messages
pronouns
messages
descriptions
@describe
tinymud
@gripe
@listgag
@ungag
@gag
go
@password
@sethome
@who
introduction
give
news
gagging
@move
inventory
@gender
@rename
notes
look
drop
get
manipulation
help
movement
home
say
whisper
page
emote
players
summary
@edit-options
@editoptions
@add-feature
@remove-feature
@features
features
@rmalias
@addalias
commands
 name
@request-character
player-names
@registerme
@eject!
gopher
options
@age
@edit
@addfeature
communication
objects
 aliases
@check-full
@users
alias
@add-alias
@mode
wizard-names
backspace
spivak
manners
output
129
4
4
2
Syntax:  @uptime
2

2
   The @uptime command displays the amount of time since the last restart of the server.
2
   Note to programmers:  The last restart time of the server is stored in $last_restart_time.
36
5
4
4
2
*subst*
2

2
%;this:wizard_list()
2

36
5
4
2
2
*forward*
2
@linelength
36
1
4
1
2
*full_index*
36
5
4
1
2
*index_list*
36
5
4
2
2
*index*
2
General Help Topics
36
5
4
21
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, so
2
you may wish to set @linelength as well, and ensure wrapping is on with @wrap
2
on.  (See help @linelength and help @wrap.)  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.
2
1
4
19
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
%[strsub(player.more_msg,"%%n","37")]
2

2
indicating (in this case) 37 more lines of text waiting to be read.  
2
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,
2
assuming you've set @pagelength correctly, unless there are not that many
2
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
2
1
4
3
2
MOO contains a rich programming language for the creation of interesting rooms, exits, and other objects.
2

2
Not every player is allowed to program in MOO, including (at the moment, anyway) you.  If you would like to be, find a wizard and convince them that you've got good ideas that the MOO needs.  Good luck!
36
5
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.
36
5
4
2
2
*forward*
2
@listgag
36
5
4
2
2
*forward*
2
emote
36
5
4
2
2
*forward*
2
@typo
36
5
4
5
2
*forward*
2
summary
2

2
Type 'help <topic>' for information on a particular topic.
2

36
5
4
2
2
*forward*
2
security
36
1
4
15
2
Some things you should be aware of:
2

2
 -*-*- OMNISCIENT WIZARDS AND SYSADMINS: -*-*-
2
Wizards can look at absolutely *anything* in the MOO database.  
2
The arch-wizard and the sysadmin for the MOO-server host have complete access not only to the MOO database itself but to many other possibly-relevant things.
2
The above mentioned parties (wizards et al), while they will endeavor to be discreet about anything incidental that turns up, nevertheless reserve the right to look at anything they want, if only for the sake of being able to resolve technical problems.
2

2
 -*-*- LOGGING: -*-*- 
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 is 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.
36
5
4
12
2
Syntax:  @examine <object>
2
         @exam <object>
2

2
Prints several useful pieces of information about the named object, including the following:
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').]
36
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.
36
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 wizards, who presumably wouldn't stoop to bugging.
36
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.
36
5
4
23
2
Syntax:   @check <options>
2

2
where <options> is one or more of:
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".
2

2
          @check-full <options>
2
where <options is either 
2
-- the number of lines to be displayed
2
-- a search string: only lines containing that string will be displayed.
2

2
Used when you are suspicious about the origin of some of the messages your character has just heard.
2

2
Output from @check is in columns that contain, in order, the monitor's best guess as to:
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.
2

2
Output from @check-full is in columns that contains a description of all the verbs that were responsible for the noise heard, not just the best guess as to who was responsible.
2

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.
36
5
4
11
2
Syntax: @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
`@eject <object>' removes <object> from the current room, whereas `@eject <object> from <place>' removes the object from the specified location (in most cases, <place> will need to be specified 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
The verbs @eject! and @eject!! are provided for those rare situations in which @eject does not work.  @eject! does not check for .home properties, sending the offending object to #-1 immediately, but with a notification.  @eject!! is just like @eject! but provides no notification to the object.
2

2
See 'help room-messages' for a list of messages one can set regarding the @eject command.
36
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').
36
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.
36
5
4
2
2
*forward*
2
@typo
36
5
4
2
2
*forward*
2
@typo
36
5
4
2
2
*forward*
2
@typo
36
5
4
12
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
If you're sending a bug report to someone because you got an error traceback when you used some object of theirs, please give them enough information to work on the problem.  In particular, please tell them *exactly* what you typed and *all* of the error messages that were printed to you, including the entire traceback, up to the line `(End of traceback.)'.  Without this information, it is nearly impossible for the programmer to discover, let alone fix, the problem.
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.  
2
Most adventuring scenario rooms fall into this latter category.
36
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
For the first form, <note-object> must be a descendant of $note.  
2
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.
36
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.
36
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 a wizard, if anyone, cares about this.
36
1
4
2
2
*forward*
2
say
36
1
4
2
2
*forward*
2
emote
36
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.
36
1
4
3
2
Syntax:  @version
2

2
Prints out the version number for the currently-executing MOO server.
36
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
36
1
4
2
2
*forward*
2
put
36
1
4
2
2
*forward*
2
help
36
1
4
2
2
*forward*
2
help
36
1
4
4
2
Syntax:  put <object> into <container>
2
         insert <object> in <container>
2

2
Moves the named object into the named container.  Sometimes the owners of the object and/or the container will not allow you to do this.
36
1
4
2
2
*forward*
2
take
36
1
4
3
2
Syntax:  burn <letter>
2

2
Destroy the named letter irretrievably.  Only players who can read the letter can do this.
36
1
4
1
2
A letter is a special kind of note (see 'help notes') with the added feature that it can be recycled by anyone who can read it.  This is often useful for notes from one player to another.  You create the letter as a child of the generic letter, $letter (see 'help @create' and 'help write'), encrypt it so that only you and the other player can read it (see 'help encrypt') and then either give it to the player in question or leave it where they will find it.  Once they've read it, they can use the 'burn' command to recycle the letter; see 'help burn' for details.
36
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.
36
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.
36
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.
36
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.
36
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.
36
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.
36
1
4
10
2
Syntax:  examine <object>
2
         exam <object>
2

2
Prints several useful pieces of information about the named object, including the following:
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
36
1
4
2
2
*forward*
2
give
36
1
4
2
2
*forward*
2
drop
36
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.
36
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.
36
1
4
67
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 %p knee."
2

2
The '%p' 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 refer to arbitrary string properties on these objects, or get the object numbers themselves.
2

2
The complete set of substitutions is as follows:
2

2
        %% => `%'  (just in case you actually want to talk about percentages).
2
    Names:
2
        %n => the player
2
        %t => this object (i.e., the object issuing the message,... usually)
2
        %d => the direct object from the command line
2
        %i => the indirect object from the command line
2
        %l => the location of the player
2
    Pronouns:
2
        %s => subject pronoun:          either `he',  `she', or `it'
2
        %o => object pronoun:           either `him', `her', or `it'
2
        %p => posessive pronoun (adj):  either `his', `her', or `its'  
2
        %q => posessive pronoun (noun): either `his', `hers', or `its'
2
        %r => reflexive pronoun:  either `himself', `herself', or `itself'
2
    General properties:
2
        %(foo) => player.foo 
2
        %[tfoo], %[dfoo], %[ifoo], %[lfoo]
2
               => this.foo, dobj.foo, iobj.foo, and player.location.foo
2
    Object numbers:
2
        %#  => player's object number
2
        %[#t], %[#d], %[#i], %[#l]
2
            => object numbers for this, direct obj, indirect obj, and location.
2

2
In addition there is a set of capitalized substitutions for use at the 
2
beginning of sentences.  These are, respectively, 
2

2
   %N, %T, %D, %I, %L for object names, 
2
   %S, %O, %P, %Q, %R for pronouns, and
2
   %(Foo), %[dFoo] (== %[Dfoo] == %[DFoo]),... for general properties
2

2
Note: there is a special exception for player .name's which are assumed to
2
already be capitalized as desired.
2

2
There may be situations where the standard algorithm, i.e., upcasing the first letter, yields something incorrect, in which case a "capitalization" for a particular string property can be specified explicitly.  If your object has a ".foo" property that is like this, you need merely add a ".fooc" (in general .(propertyname+"c")) specifying the correct capitalization.  This will also work for player .name's if you want to specify a capitalization that is different from your usual .name
2

2
Example:  
2
Rog makes a hand-grenade with a customizable explode message.
2
Suppose someone sets grenade.explode_msg to:
2

2
  "%N(%#) drops %t on %p foot.  %T explodes.  
2
   %L is engulfed in flames."
2

2
If the current location happens to be #3443 ("yduJ's Hairdressing Salon"),
2
the resulting substitution may produce, eg.,
2

2
  "Rog(#4292) drops grenade on his foot.  Grenade explodes.  
2
   YduJ's Hairdressing Salon is engulfed in flames."
2

2
which contains an incorrect capitalization.  
2
yduJ may remedy this by setting #3443.namec="yduJ's Hairdressing Salon".
2

2
Note for programmers:  
2
 In programs, use $string_utils:pronoun_sub().
2
 %n actually calls player:title() while %(name) refers to player.name directly.
36
1
4
18
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
        @<message-name> <object> is "<message>"
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
        @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
36
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
36
1
4
13
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
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
The description of an object is kept in its .description property.  
2
For descriptions of more than one paragraph, .description can be a list of strings.
36
1
4
45
2
This is yduJ's table of tinymud commands and their equivalents in LambdaMOO.  A longer document, with discussions of the different verbs and how they have changed, is available via FTP from ftp.parc.xerox.com as pub/MOO/contrib/docs/TinyMUD-LambdaMOO-equivs.  All the commands mentioned here have help nodes on LambdaMOO.
2

2
The following commands are basically the same in MOO and MUD.
2

2
    drop(throw), get(take), go, help, home, inventory, look, news, say (",:)
2

2

2
The following commands have no equivalent:
2

2
    kill, rob, score, @force
2

2

2
The following commands have the same names and do similar things, but are changed in some way (both syntactic and semantic differences, sometimes quite substantial differences):
2

2
    @examine, give, page, read, whisper, @create, @dig,
2
    @lock, @password, @unlock, @describe
2

2

2
The following commands have rough equivalents in LambdaMOO but the name is different:
2

2
    TinyMUD name            LambdaMOO name
2
    ------------            --------------
2
    QUIT                    @quit
2
    gripe                   @gripe
2
    goto/move               go
2
    WHO                     @who
2
    @fail                   @take_failed, @nogo, @drop_failed
2
    @find                   @audit
2
    @link                   @dig, @sethome, @add-exit, @add-entrance
2
    @name                   @rename
2
    @ofail                  @otake_failed, @onogo, @odrop_failed
2
    @open                   @dig
2
    @osuccess               @oleave, @oarrive, @otake_succeeded, 
2
                            @odrop_succeeded
2
    @success                @leave, @arrive, @take_succeeded
2
                            @drop_succeeded
2
    @teleport               @move
2

2

2
Here are some commands for which no equivalent exists, or for which the equivalent is a complicated set of actions.
2

2
    @set, @stats, @unlink
2

2

2
Documentation on most of the LambdaMOO commands mentioned above can be acquired using 'help <command-name>'.  A notable exception is the commands like @oarrive and @take_failed that set textual messages on objects.  These are described under 'help messages'.
36
1
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.
36
5
4
4
2
Syntax:  @listgag [all]
2
         @gaglist [all]
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.  With the optional "all" parameter it will also scan the database for players who are gagging you.  This may induce lag, so caution is advised with this option.
36
1
4
9
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
Munchkin types:
2
  @ungag Grover
2
and is once again able to hear Grover's witty remarks.  Sigh...
36
1
4
12
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
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.  Example:  Noisy Robot prints "Hi there" every 15 seconds.   In order to avoid seeing that, Munchkin types:
2
  @gag Noisy
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.)
36
1
4
8
2
Syntax: go <direction> ...
2

2
Invokes the named exits in the named order, moving through many rooms in a single command.
2

2
Example:
2
Munchkin types:
2
  go n e e u e e s e
2
and moves quite rapidly from the Living Room all the way to the Bovine Illuminati Atrium, all in one command.
36
1
4
9
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 wizards 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.
2

2
If your character does get stolen, a wizard can change it for you and tell you the new password in secret.  You may have to provide your email address for verification.
2

2
Only the first 8 characters of a password are significant.
36
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.
36
1
4
11
2
*subst*
2
Syntax: @who
2
        @who <player> [<player> ... ]
2

2
The first form lists all of the currently-connected players, along with the amount of time they've been connected, the amount of time they've been idle, and their present location in the MOO.
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 connect/idle times.
2

2
@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 "%[$room.who_location_msg]" (i.e., the room name).
2

2
If the list of players to display is longer than 100, this command will not show its normal output, since it can be quite expensive to compute.  In such cases, you might want to use the @users command instead; see `help @users' for more information.
36
1
4
13
2
Most commands have the form of simple English sentences:
2
    <verb>
2
    <verb>  <direct object>
2
    <verb>  <direct object>  <preposition>  <indirect object>
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
36
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.
36
5
4
10
2
*subst*
2
Syntax: news
2
        news all
2
        news new
2
        news contents
2
        news archive
2

2
Read the latest edition of the %[$network.MOO_name] Newspaper, which carries articles concerning recent changes to the MOO server or to the main public classes, or other articles of interest to the MOO at large.
2

2
The default behavior for the `news' command is to act like `news new' but this may be changed by setting the @mail-option news to one of `all' or `new' or `contents'.  `news all' displays all current news articles.  `news new' only displays articles you have not yet read.  `news contents' displays the authors and subjects of all current news.  `news archive' displays back issues of the newspaper which are deemed worth reading by every citizen at any time.
36
5
4
5
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
36
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.  The special case where <thing> is 'me' is useful for teleporting yourself around.
2

2
If @move doesn't work and you own the room where the object is located, try using @eject instead.
36
5
4
4
2
Syntax:  inventory
2
         i
2

2
Prints a list showing every object you're carrying.
36
5
4
8
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.
2

2
It should be noted that some of the "genders" on the standard gender list need verb conjugation in order to work properly and much of the MOO isn't set up for this (...yet).  For example, you should expect to see `they is' a fair amount if you @gender yourself `plural'.
36
5
4
27
2
Syntax: @rename <object>        to [name-and-alias],<alias>,...,<alias>
2
        @rename <object>        to [name]:<alias>,...,<alias>
2
        @rename <object>.<property> to <new-property-name>
2
        @rename <object>:<verb-name> 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
Note that for renaming players, more stringent rules apply.  See `help player-names'.  Certain other kinds of objects (e.g., mail recipients) also enforce their own rules w.r.t what they can be named.
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'.  Note, however, that it will be impossible to use "Rover the Wonder Dog" to rever to the dog: if you don't include the name in the aliases, confusion can result.  It might have been better to start off with
2
  @rename #4237 to "Rover the Wonder Dog",Rover,dog
2

2
Since he didn't, Munchkin now 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
The third form of the @rename command is also for use by programmers, to change the name of a property they own to <new-property-name>.
2

2
The fourth form of the @rename command is 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.
2

2
The fifth form, @rename#, is for unambiguously referring to a verb on an object in case there is more than one with the same name. The verb number is the 1-based index of the verb as it appears in the verbs() (or @verbs) output list.
36
5
4
15
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

2
@notedit -- 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.  If you'd like to make it possible for a reader of your note to destroy it (this is a common desire for notes to other individual players), then you might want to look at 'help letters'.
36
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.
36
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.
36
5
4
2
2
*forward*
2
take
36
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
36
5
4
18
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
      `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.
36
1
4
5
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>' 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
In addition to such vanilla movement, some areas may contain objects allowing teleportation and almost all areas permit the use of the 'home' command to teleport you to your designated home (see 'help home' for more details).
36
1
4
6
2
*subst*
2
Syntax: home
2

2
Instantly teleports you to your designated home room.
2
Initially, this room is %[tostr($player_start.name," (",$player_start,")")].
2
You can change your designated home; see 'help @sethome' for details.
36
1
4
12
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
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!"
36
1
4
2
2
whisper "<text>" to <player>
2
sends the message "<yourname> whispers, "<text>" to you " to <player>, if they are in the room.
36
1
4
27
2
*subst*
2
Syntax:  page <player> [[with] <text>]
2

2
Sends a message to a connected player, telling them your location and, optionally, <text>.
2

2
Example:
2
Munchkin types:
2
        page Frebble with "Where are you?"
2
Frebble sees:
2
        You sense that Munchkin is looking for you in the Kitchen.
2
        He pages, "Where are you?"
2
Munchkin sees:
2
        Your message has been received.
2

2
Advanced Features:
2
Page refers to the following messages on the players involved (see 'help messages'):
2

2
@page_origin [%[$player.page_origin_msg]]
2
  Determines how the recipient is told of your location.
2

2
@page_echo   [%[$player.page_echo_msg]]
2
  Determines the response received by anyone who pages you.
2

2
@page_absent [%[$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 (%d) refers to the recipent of the page and the indirect object (%i) refers to the sender.  You should only change these messages if you want to add to the Virtual Reality feel of the MOO for your character.
36
1
4
18
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
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.
36
1
4
8
2
There are a number of commands for modifying various characteristics of the object representing you in the MOO, your 'player'.  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 player's gender
2
@password -- changing your player's password
2
@sethome -- changing your designated home room
2
@rename -- changing your name and/or aliases
2
@linelength -- adding word-wrap to the lines you see
36
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
players -- 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
@pagelength -- what to do if lines scroll off your screen too fast
2
@linelength -- what to do if lines are truncated
2
tinymud -- a list of equivalences between MOO and TinyMUD concepts/commands
36
5
4
26
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.
2
-no_parens       include all parentheses in verb code.
2
+no_parens       include only necessary parentheses in verb code.
2

2
+parens        is a synonym for -no_parens
2
+noisy_insert  is a synonym for -quiet_insert
36
5
4
2
2
*forward*
2
@edit-options
36
5
4
4
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'.
36
5
4
2
2
*forward*
2
@add-feature
36
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.
36
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 with the @features command, and add or remove features from your list with the @add-feature and @remove-feature commands.
36
5
4
10
2
Syntax: @rmalias <alias>[,...,<alias>] from <object>
2
        @rmalias <alias>[,...,<alias>] from <object>:<verb-name>
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.
2
Note that @rmalias will not affect the object's name, only its aliases.
2

2
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.
2

2
The third form, @rmalias#, is for unambiguously referring to a verb on an object that might have more than one verb with the same name. The verb-number is the 1-based index of the verb as it appears in the verb() (or @verbs) output list.
36
5
4
25
2
Syntax: @addalias <alias>[,...,<alias>] to <object>
2
        @addalias <alias>[,...,<alias>] to <object>:<verb-name>
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
Example:
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, 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.  Certain other classes of objects (e.g., mail-recipients) also enforce rules about what aliases may be given them.
2

2
Example:
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).
2

2
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.
2

2
The third form, @addalias#, is for unambiguously referring to a verb on an object in case there are more than one with the same name. The verb number is the 1-based index of the verb as it appears in the verbs() (or @verbs) output list.
36
5
4
5
2
*forward*
2
summary
2

2
Type 'help <topic>' for information on a particular topic.
2

36
5
4
4
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
Help is available on the following commands:
2
@rename -- change the names or aliases of an object or yourself.
2
@addalias, @rmalias -- add and remove aliases.
36
5
4
7
2
Usage:    @request <player-name> for <email-address>
2

2
Example:  @request Munchkin for msneed@baum.edu
2

2
This command is available to Guest characters only.
2

2
The @request command requests a new character, registered for your email address. Please use your primary address for this, as your password will be sent to the address provided.
36
5
4
6
2
*subst*
2
A player name must be a single word, must not contain any spaces, backslashes, or quotes, nor can it begin with the characters #, *, (, or ).  Finally it cannot be one that is in use by any other player nor any of the words on the following list:
2

2
%;;lns={};for l in ($string_utils:columnize({@$player_db.stupid_names,@$player_db.reserved},6)) lns={@lns,"  "+l}; endfor return lns;
2

2
Note that these rules apply as well to your single-word aliases, since those can equally well be used to refer to you in commands that match on player names (@who, whereis, ...).  There are no restrictions on your multi-word aliases, however the commands that expect player names will not recognize them.
36
5
4
6
2
  @registerme as <email-address>
2
This verb changes your registered email_address property. It will modify the registration, and then, to validate the email address, it will assign a new password and mail the password to the given email_address.
2
If, for some reason, this is a problem for you, contact a wizard or registrar to get your email address changed.
2

2
  @registerme
2
Prints your registered email address.
36
5
4
2
2
*forward*
2
@eject
36
5
4
3
2
Gopher is an internet service for information retrieval. There are many gopher servers across the internet, providing a wide variety of information of all sorts: network news, weather, and White House press releases, campus class information, and scientific papers.
2

2
The programmer interface to Gopher is contained in the object $gopher (`help $gopher').
36
5
4
5
2
Options allow you to customize the behavior of various commands.  Options are grouped into separate option packages that each affects a given class of related commands.  Each has its own help topic:
2

2
  @mail-options    --- mail commands (@mail, @read, @next, @prev, @send...)
2
  @edit-options    --- editing commands (@edit and commands within the editor)
2
  @build-options   --- building commands (@create, @dig, @recycle)
36
5
4
4
2
Syntax:  @age [player]
2

2
Displays the MOO age of the player if the player specified first connected after initial connections were recorded.
2
MOO age is computed from the moment the player first connected until the current time.
36
5
4
9
2
Syntax:  @edit <object>.<property>
2
         @edit <object>:<verb-name> [<dobj> [<prep> [<iobj>]]]
2
         @edit <object>
2

2
Enters a MOO editor, as appropriate.
2

2
Chooses the MOO Note editor for the named property, or the MOO verb editor for the named verb.  If no property or verb name is given, assumes property .text for a note object, or .description for any other object.
2

2
See 'help editors' for more detail.
36
5
4
2
2
*forward*
2
@add-feature
36
5
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 wizards' most recent set of general announcements
2
@gripe   -- sending complaints to the wizards
2
@typo @bug @idea @suggest
2
         -- 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.
36
5
4
7
2
Objects are the fundamental building blocks of the MOO.  Every object has a unique number, a name, an owner, a location, and various other properties.  An object can always be referred to by its number, and sometimes by its name or one of its aliases -- if you are in the same location as the object, for example, and also in some other special cases.
2

2
For help on creating an object, see 'help @create'.
2

2
For help on recycling an object, see 'help @recycle'.
2

2
For help on finding information about specific objects, see 'help @display', 'help @show', and 'help $object_utils'.
36
1
4
5
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.
2

2
Typing `exam object' 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.)
2

2
See also `help #', `help @addalias', and `help @rmalias'.
36
1
4
2
2
*forward*
2
@check
36
5
4
3
2
Syntax:  @users
2

2
Prints out the number of users currently connected and a list of their names, in alphabetical order.
36
5
4
2
2
*forward*
2
 name
36
5
4
2
2
*forward*
2
@addalias
36
5
4
3
2
Syntax:  @mode <brief | verbose>
2

2
Sets your current mode to either brief or verbose.  In brief mode, when you enter into a room, you will not see the room's description unless you explicitly type `look'.  Verbose is the default mode.
36
1
4
2
2
*forward*
2
wizard-list
36
5
4
10
2
Players sometimes have difficulty getting their backspace key to work.  This is an outside-MOO problem:  Whatever access software you have determines how the line you type is edited before the MOO ever sees it.  If your backspace key won't work here, you will probably need to consult with some documentation or a guru at your end.
2

2
The above notwithstanding, here are a few things to try instead of backspace:
2

2
   ctrl-h            (another way of typing backspace)
2
   del               (delete character)
2
   ctrl-backspace    (another way of typing delete character)
2
   ctrl-w            (delete word left)
2
   ctrl-u            (delete entire line)
2
   ctrl-r            (redraw line)
36
5
4
8
2
The spivak pronouns were developed by mathematician Michael Spivak for use in his books.  They are the most simplistic of the gender neutral pronouns (others being "neuter" and "splat") and can be easily integrated into writing.  They should be used in a generic setting where the gender of the person referred to is unknown, such as "the reader."  They can also be used to describe a specific individual who has chosen not to identify emself with the traditional male or female gender.
2

2
The spivak pronouns are
2
E      - subjective
2
Em     - objective
2
Eir    - possessive (adjective)
2
Eirs   - possessive (noun)
2
Emself - reflexive
36
5
2
No manners or code of conduct has been written for this MOO yet.
36
5
4
19
2
-------------------------------
2
Controlling output from the MOO
2
-------------------------------
2

2
It is the usual problem of new users that they don't have (yet) a good client
2
program for connecting to the MOO. Therefore, when they telnet to it, what they
2
type gets mixed with the output from the MOO - very confusing!
2
To prevent this, do the following:
2
 - to type a statement without being bothered by MOO output, type a quote (")
2
   or the command 'say'. When you're prompted for your statement, you can type
2
   undisturbed.
2
 - to emote, type a colon (:) or the command 'emote'. Wait for the emote prompt
2
   and then type it.
2
 - to issue any command (say and emote included), type '>>'. Wait for the
2
   confirmation that output is suspended, and then issue your command.
2

2
Note: there is a 2 minutes timeout to the output suspension.
2

2
Designed and written by John Towell and Gustavo Glusman.
36
5
5
36
5
4
1
2
gen-index
36
1
5
36
4
4
0
36
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.
36
5
4
2
0
81304
0
1001876092
36
1
5
36
5
5
36
5
#61
News

16
2
62
116
2
8
-1
102
14
articles
2
173
-1
item_no
2
173
-1
check
2
173
-1
date
2
173
-1
index
2
173
-1
acceptable
2
173
-1
notify_update
2
173
-1
enterfunc
2
173
-1
notify_change
2
173
-1
@addnews
2
93
-2
@rmnews
2
29
-1
init_for_core
2
173
-1
_html
36
165
-1
notify_Xpress_change
2
173
-1
1
blessed_task
80
0
0
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
0
0
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
0
1
2
1
1
96
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
news.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
2
BOTTOM
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
0
11
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
News
2
news
2
5
2
This is the current issue of the MOO newsletter. Type 'news' to see the table of contents.
2
5
4
2
0
9130
0
1001876092
36
1
5
2
5
5
2
5
#62
enCore Starting Point

16
36
-1
61
-1
3
-1
98
5
disfunc
2
173
-1
enterfunc
2
173
-1
match
36
173
-1
init_for_core
2
173
-1
keep_clean
2
173
-1
1
core_description
69
4
1
2
Welcome to your new enCore MOO. This is the point where people start when they log on, and also the point where you start to build new locations in your MOO. Before you begin to build, however, you should read the enCore README news file, and configure your new MOO by typing '@configure'. Have fun!
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
4
0
36
4
5
36
5
5
36
5
4
0
36
4
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
36
5
5
36
5
5
36
5
5
36
4
4
0
36
5
4
1
2
Welcome to your new enCore MOO. This is the point where people start when they log on, and also the point where you start to build new locations in your MOO. Before you begin to build, however, you should read the enCore README news file, and configure your new MOO by typing '@configure'. Have fun!
36
5
4
2
0
5863
0
1001876092
36
1
5
36
5
5
36
5
#63
Recycling Center

16
36
-1
-1
-1
5
-1
74
16
_recreate
2
173
-1
_recycle
2
173
-1
_create
2
173
-1
addhist
2
173
-1
show*-history
2
45
-1
request
2
153
5
setup_toad
2
173
-1
add_orphan
36
173
-1
remove_orphan
36
173
-1
valid
2
173
-1
init_for_core
2
173
-1
resurrect
2
173
-1
reclaim_lost_souls
2
173
-1
look_self
36
173
-1
check_quota_scam
2
173
-1
gc
36
173
-1
5
orphans
announce_removal_msg
nhist
history
lost_souls
65
4
0
36
1
2

36
5
0
50
36
0
4
0
36
0
4
0
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
36
5
5
36
5
5
36
5
5
36
4
4
2
2
Recycling Center
2
Center
36
5
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.
36
5
4
2
0
11640
0
1001876092
36
1
5
36
5
5
36
5
#64
Generic Garbage Object

16
36
-1
-1
-1
-1
-1
-1
4
description
2
173
-1
look_self
2
173
-1
title titlec
2
173
-1
tell
2
173
-1
2
aliases
object_size
2
4
1
2
garbage
36
1
4
2
0
1409
0
1001876092
36
5
#65
Mail Options

16
36
-1
-1
-1
68
-1
66
18
actual
36
173
-1
parse_@mail
36
173
-1
parse_sticky parse_manymsgs
36
173
-1
parse_replyto
36
173
-1
show_manymsgs
36
173
-1
show_sticky
36
173
-1
show_@mail
36
173
-1
show_replyto
36
173
-1
show
36
173
-1
check_replyto
36
173
-1
show_netmail
2
173
-1
check_netmail
2
173
-1
show_expire
36
173
-1
parse_expire
36
173
-1
init_for_core
2
165
-1
check_news
36
173
-1
parse_news
36
173
-1
show_news
36
173
-1
15
show_include
show_all
show_nosubject
show_expert
show_enter
type_manymsgs
type_@mail
type_replyto
type_expire
show_followup
show_resend_forw
choices_rn_order
show_no_auto_forward
show_expert_netfwd
show_no_dupcc
25
4
2
2
Original message will not be included in replies
2
Original message will be included in replies
36
5
4
2
2
Replies will go to original sender only.
2
Replies will go to original sender and all previous recipients.
36
5
4
2
2
Mail editor will initially require a subject line.
2
Mail editor will not initially require a subject line.
36
5
4
2
2
Novice mail user...
2
Expert mail user...
36
5
4
2
2
Mail editor will not start with an implicit `enter' command.
2
Mail editor will start with an implicit `enter' command.
36
5
4
1
0
0
36
5
4
2
0
2
4
1
0
2
36
5
4
2
0
1
4
1
0
1
36
5
4
1
0
0
36
5
4
2
2
No special reply action for messages with non-player recipients.
2
Replies go only to first non-player recipient if any.
36
5
4
2
2
@resend puts player in Resent-By: header
2
@resend puts player in From: header (like @forward)
36
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.
36
5
4
2
2
@netforward when expiring messages
2
do not @netforward messages when expiring mail
36
5
4
2
2
@netforward confirms before emailing messages
2
@netforward doesn't confirm before emailing messages
36
5
4
2
2
i want to read mail to me also sent to lists i read
2
don't send me personal copies of mail also sent to lists i read
36
1
4
18
2
include
2
all
2
followup
2
nosubject
2
expert
2
enter
2
sticky
2
@mail
2
manymsgs
2
replyto
2
netmail
2
expire
2
resend_forw
2
rn_order
2
no_auto_forward
2
expert_netfwd
2
news
2
no_dupcc
36
1
2
!include!noinclude!all!sender!nosubject!expert!enter!sticky!@mail!manymsgs!replyto!netmail!expire!followup!resend_forw!rn_order!no_auto_forward!expert_netfwd!news!no_dupcc!
36
1
4
2
2
noinclude
2
sender
36
1
0
19
36
5
5
36
4
4
1
2
Mail Options
36
5
2
Options for mailing
36
5
4
2
0
12682
0
1001876092
36
1
5
36
5
5
36
5
#66
Edit Options

16
36
-1
-1
-1
68
-1
67
2
actual
36
173
-1
show
36
173
-1
4
show_quiet_insert
show_eval_subs
show_local
show_no_parens
14
4
2
2
Report line numbers on insert or append.
2
No echo on insert or append.
36
5
4
2
2
Ignore .eval_subs when compiling verbs.
2
Use .eval_subs when compiling verbs.
36
5
4
2
2
Use in-MOO text editors.
2
Ship text to client for local editing.
36
5
4
2
2
include all parentheses when fetching verbs.
2
includes only necessary parentheses when fetching verbs.
36
5
4
4
2
quiet_insert
2
eval_subs
2
local
2
no_parens
36
1
2
!quiet_insert!eval_subs!local!no_parens!parens!noisy_insert!
36
1
4
2
2
parens
2
noisy_insert
36
1
0
20
36
5
5
36
4
4
1
2
Edit Options
36
5
5
36
5
4
2
0
1888
0
1001876092
36
1
5
36
5
5
36
5
#67
Display Options

16
36
-1
-1
-1
68
-1
77
0
3
show_blank_tnt
show_shortprep
show_thisonly
13
4
2
2
Treat `this none this' verbs like the others.
2
Blank out the args on `this none this' verbs.
36
5
4
2
2
Display prepositions in full.
2
Use short forms of prepositions.
36
5
4
2
2
./: will show ancestor properties/verbs if none on this.
2
./: will not show ancestor properties/verbs.
36
5
4
3
2
blank_tnt
2
shortprep
2
thisonly
36
1
2
!blank_tnt!shortprep!thisonly!
36
1
4
0
36
1
5
36
5
5
36
4
4
1
2
Display Options
36
5
5
36
5
4
2
0
841
0
1001876092
36
1
5
36
5
5
36
5
#68
Generic Option Package

144
36
-1
-1
-1
1
65
69
12
get
36
173
-1
set
36
173
-1
parse
36
173
-1
_name
36
173
-1
add_name
36
173
-1
remove_name
36
173
-1
show
36
173
-1
actual
36
173
-1
istype
36
173
-1
islistof
36
173
-1
desc_type
36
173
-1
parsechoice
36
173
-1
4
names
_namelist
extras
namewidth
10
4
0
36
1
2
!
36
1
4
0
36
1
0
15
36
5
5
36
4
4
1
2
Generic Option Package
36
5
2
an option package in need of a description.  See `help $generic_option'...
36
5
4
2
0
12761
0
1001876092
36
1
5
36
5
5
36
5
#69
Error Generator

16
36
-1
-1
-1
1
-1
72
20
raise
36
173
-1
E_NONE
36
173
-1
E_TYPE
36
173
-1
E_DIV
36
173
-1
E_PERM
36
173
-1
E_PROPNF
36
173
-1
E_VERBNF
36
173
-1
E_VARNF
36
173
-1
E_INVIND
36
173
-1
E_RECMOVE
36
173
-1
E_MAXREC
36
173
-1
E_RANGE
36
173
-1
E_ARGS
36
173
-1
E_NACC
36
173
-1
E_INVARG
36
173
-1
E_QUOTA
2
173
-1
accept
36
173
-1
name
36
173
-1
toerr
36
173
-1
match_error
36
173
-1
2
names
all_errors
8
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
36
5
4
16
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
3
15
36
1
5
36
4
4
1
2
Error Generator
36
5
4
3
2
Object to automatically generate errors.
2

2
raise(error) actually raises the error.
36
5
4
2
0
7490
0
1001876092
36
1
5
36
5
5
36
5
#70
Site-Locks

0
2
46
-1
89
45
-1
122
1
init_for_core
2
173
-1
0
26
4
0
2
5
0
0
36
1
4
0
36
0
0
1
2
5
2
%n (%#) can't send to moderated list %t (%[#t]) directly.
2
5
4
0
2
5
4
0
2
5
4
1
1
2
36
1
4
0
36
1
0
2592000
36
1
0
0
36
1
4
0
36
0
5
2
5
5
2
5
4
0
36
1
5
2
5
4
0
36
1
5
36
0
5
36
0
5
36
0
5
2
4
4
1
2
Site-Locks
36
1
2
Notes on annoying sites.
2
5
4
2
0
1622
0
1001876092
36
1
5
2
5
5
2
5
#71
housekeeper

19
36
-1
-1
-1
58
-1
-1
24
look_self
36
173
-1
cleanup
36
173
-1
replace
36
173
-1
cleanup_list
36
29
-1
add_cleanup
36
93
-2
remove_cleanup
36
29
-1
controls
36
173
-1
continuous
36
173
-1
litterbug
36
173
-1
is_watching
36
173
-1
send_home
36
173
-1
moveit
2
173
-1
ejectit
2
173
-1
is_object_cleaned
36
173
-1
is_litter
36
173
-1
init_for_core
2
173
-1
clean_status
36
173
-1
is_cleaning
36
173
-1
time
36
173
-1
acceptable
2
173
-1
move_players_home
2
173
-1
move_em
2
173
-1
take_away_msg drop_off_msg
36
173
-1
set_moveto_task
36
173
-1
17
recycle_bins
owners
cleaning
litter
eschews
public_places
task
requestors
destination
clean
testing
player_queue
take_away_msg
drop_off_msg
move_player_task
moveto_task
cleaning_index
192
4
0
36
5
4
1
1
2
36
5
1
-1
36
5
4
0
36
5
4
0
36
5
4
0
36
5
0
0
36
5
4
0
36
5
4
0
36
5
4
0
36
1
0
0
36
5
4
0
36
1
2
%[tpsc] arrives to cart %n off to bed.
36
5
2
%[tpsc] arrives to drop off %n, who is sound asleep.
36
5
0
0
36
1
0
0
36
5
0
0
36
5
4
0
36
1
5
36
1
2
here=player.location;me=player
36
1
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
1
5
36
5
5
36
5
5
36
5
5
36
5
5
36
4
5
36
5
5
2
1
4
1
1
2
36
5
5
36
5
5
36
5
5
36
5
5
36
4
5
36
4
5
36
1
5
2
0
5
2
0
5
2
1
0
0
36
5
5
36
5
5
36
1
5
36
1
5
36
0
5
36
1
4
0
2
1
0
-80
36
1
5
36
4
5
2
0
5
2
0
5
36
5
5
36
5
5
36
4
2
The housekeeper is too busy putting away all of the junk all over LambdaMoo that there isn't time to listen to pages and stuff like that so your page isn't listened to, too bad.
36
5
2
the housekeeper's
36
5
2
The housekeeper's
36
5
5
36
5
5
36
5
5
36
5
0
2147483647
2
1
0
-9993
36
0
5
36
5
2
'Self
36
5
2
The housekeeper's
36
5
2
The housekeeper
36
5
2
The housekeeper
36
5
2
'self
36
5
2
the housekeeper's
36
5
2
the housekeeper
36
5
2
the housekeeper
36
5
5
36
5
2
Impossible password to type
2
0
5
36
5
5
36
5
5
36
5
5
36
5
0
2147483647
2
1
4
4
0
183000
0
0
0
987347720
0
0
36
0
0
0
2
0
5
2
0
5
2
1
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
4
5
2
1
5
36
5
5
2
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
0
5
36
4
5
36
4
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
36
5
5
36
5
5
36
5
5
36
4
4
1
2
housekeeper
2
1
2
A very clean, neat, tidy person who doesn't mind lugging players and their gear all over the place.
36
5
4
2
0
22309
0
1001876092
36
1
5
36
5
5
36
5
#72
Network Utilities

16
2
-1
-1
-1
1
-1
75
24
parse_address
2
173
-1
local_domain
2
173
-1
open
2
173
-1
close
2
173
-1
sendmail
2
29
-1
trust
2
173
-1
init_for_core
2
173
-1
raw_sendmail
2
29
-1
invalid_email_address
2
173
-1
invalid_hostname
2
173
-1
email_will_fail
2
173
-1
read
2
173
-1
is_open
36
173
-1
incoming_connection
2
173
-1
return_address_for
2
173
-1
server_started
2
173
-1
is_outgoing_connection
2
173
-1
notify
2
173
-1
suspend_if_needed
2
173
-1
error
2
173
-1
help_msg
36
173
-1
adjust_postmaster_for_password
2
173
-1
add_queued_mail
36
173
-1
send_queued_mail
2
173
-1
23
site
large_domains
connect_connections_to
postmaster
port
MOO_name
valid_host_regexp
maildrop
trusts
reply_address
active
valid_email_regexp
invalid_userids
debugging
errors_to_address
suspicious_userids
usual_postmaster
password_postmaster
queued_mail
queued_mail_task
envelope_from
blank_envelope
webport
29
2
yoursite
2
1
4
0
2
1
4
0
2
0
2
postmastername@yourhost
2
5
0
7777
2
5
2
enCore
2
5
2
^%([-_a-z0-9]+%.%)+%(gov%|edu%|com%|org%|int%|mil%|net%|%nato%|arpa%|[a-z][a-z]%)$
2
5
2
localhost
2
5
4
1
1
36
2
1
2
moomailreplyto@yourhost
2
5
0
0
2
5
2
^[-a-z0-9_!.%+$'=/]*[-a-z0-9_!%+$'=]$
2
5
4
7
2

2
sysadmin
2
root
2
postmaster
2
system
2
operator
2
bin
2
5
0
0
2
5
2
moomailerrors@yourhost
2
5
4
12
2

2
sysadmin
2
root
2
postmaster
2
bin
2
SYSTEM
2
OPERATOR
2
guest
2
me
2
mailer-daemon
2
webmaster
2
sysop
2
5
2
postmastername@yourhost
2
5
2
postmastername@yourhost
2
5
4
0
36
0
0
1213365816
36
1
2
postmastername@yourhost
2
5
0
0
2
5
0
7000
2
5
0
0
2
4
4
1
2
Network Utilities
2
5
4
74
2
Utilities for dealing with network connections
2
---------------
2
Creating & tracking hosts:
2

2
:open(host, port [, connect-connection-to]) => connection
2
    open a network connection (using open_network_connection).
2
    If 'connect-connection-to' is a player object, the
2
    connection will be connected to that object when it
2
    gets the first line of input.
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
                
2
5
4
2
0
22748
0
1001876092
36
1
5
2
5
5
2
5
#73
Generic BigList Resident

144
36
-1
-1
-1
1
-1
18
7
_make
2
173
-1
_kill
2
173
-1
_get
36
173
-1
_put
36
173
-1
_genprop
36
173
-1
_ord
36
173
-1
init_for_core
2
173
-1
3
_genprop
mowner
_mgr
9
2
a
36
5
1
36
36
5
1
13
36
5
5
36
4
4
3
2
biglist
2
resident
2
gblr
36
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.
36
5
4
2
0
3633
0
1001876092
36
1
5
36
5
5
36
5
#74
Generic Feature Object

144
36
-1
-1
-1
5
91
88
10
help_msg
36
173
-1
look_self
36
173
-1
using this
36
173
-1
examine_commands_ok
2
173
-1
set_feature_ok
36
173
-1
hidden_verbs
36
173
-1
set_feature_verbs
36
173
-1
initialize
36
173
-1
init_for_core
2
173
-1
feature_remove
2
173
-1
4
warehouse
help_msg
feature_verbs
feature_ok
64
1
84
36
1
2
The Generic Feature Object--not to be used as a feature object.
36
5
4
1
2
Using
36
1
0
1
36
1
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
36
5
5
36
5
5
36
5
5
36
4
4
4
2
Generic Feature Object
2
Generic .Features_Huh Object
2
Feature Object
2
.Features_Huh Object
36
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.
36
5
4
2
0
7272
0
1001876092
36
1
5
36
5
5
36
5
#75
Gopher utilities

16
2
-1
-1
-1
1
132
79
17
get_now
2
173
-1
parse
2
173
-1
show_text
2
173
-1
type
2
173
-1
summary
2
173
-1
get
2
173
-1
clear_cache
2
173
-1
unparse
2
173
-1
interpret_error
2
173
-1
trusted
2
173
-1
_textp
2
173
-1
_mail_text
2
173
-1
init_for_core
2
173
-1
display_cache
2
45
-1
get_cache
2
173
-1
cache_entry
2
173
-1
help_msg
2
173
-1
5
cache_requests
cache_times
cache_values
limit
cache_timeout
11
4
0
2
1
4
0
2
1
4
0
2
1
0
2000
2
5
0
900
2
1
0
0
2
4
4
1
2
Gopher utilities
2
5
4
30
2
An interface to Gopher internet services.
2
Copyright (c) 1992,1993 Grump,JoeFeedback@LambdaMOO.
2

2
This object contains just the raw verbs for getting data from gopher servers and parsing the results. Look at #50122 (Generic Gopher Slate) on LambdaMOO for one example of a user interface. 
2

2
:get(site, port, selection)
2
  Get data from gopher server: returns a list of strings, or an error if it couldn't connect. Results are cached.
2

2
:get_now(site, port, selection)
2
  Used by $gopher:get. Arguments are the same: this actually gets the 
2
  data without checking the cache. (Don't call this, since the
2
  caching is important to reduce lag.)
2
  
2
:show_text(who, start, end, site, port, selection)
2
  Requires wiz-perms to call.
2
  like who:notify_lines($gopher:get(..node..)[start..end])
2

2
:clear_cache()
2
  Erase the gopher cache.
2

2
:parse(string)
2
  Takes a directory line as returned by $gopher:get, and return a list
2
  {host, port, selector, label}
2
   host, port, and selector are what you send to :get.
2
  label is a string, where the first character is the type code.
2

2
:type(char)
2
   returns the name of the gopher type indicated by the character, e.g.
2
   $gopher:type("I") => "image"
2

2
5
4
2
0
13220
0
1001876092
36
1
5
2
5
5
2
5
#76
Programmer Options

16
36
-1
-1
-1
68
-1
-1
8
actual
36
173
-1
show
36
173
-1
show_verb_args
36
173
-1
check_verb_args
36
173
-1
parse_verb_args
36
173
-1
show_@prop_flags
36
173
-1
check_@prop_flags
2
173
-1
parse_@prop_flags
2
173
-1
5
show_eval_time
show_list_all_parens
show_list_no_numbers
show_copy_expert
type_@prop_flags
15
4
2
2
eval does not show ticks/seconds consumed.
2
eval shows ticks/seconds consumed.
36
5
4
2
2
@list shows only necessary parentheses by default
2
@list shows all parentheses by default
36
5
4
2
2
@list gives line numbers by default
2
@list omits line numbers by default
36
5
4
2
2
@copy prints warning message.
2
@copy omits warning message.
36
5
4
1
0
2
36
5
4
6
2
list_all_parens
2
list_no_numbers
2
eval_time
2
copy_expert
2
verb_args
2
@prop_flags
36
1
2
!list_all_parens!list_no_numbers!eval_time!copy_expert!list_numbers!verb_args!@prop_flags!
36
1
4
1
2
list_numbers
36
1
0
15
36
5
0
0
36
4
4
1
2
Programmer Options
36
5
4
1
2
Option package for $prog commands.  See `help @prog-options'.
36
5
4
2
0
4790
0
1001876092
36
1
5
36
5
5
36
5
#77
Builder Options

16
36
-1
-1
-1
68
-1
76
5
check_create_flags
36
173
-1
show_create_flags
36
173
-1
parse_create_flags
36
173
-1
show_dig_room show_dig_exit
36
173
-1
parse_dig_room parse_dig_exit
36
173
-1
3
show_bi_create
type_dig_room
type_dig_exit
13
4
2
2
@create/@recycle re-use object numbers.
2
@create/@recycle call create()/recycle() directly.
36
5
4
1
0
1
36
5
4
1
0
1
36
5
4
4
2
dig_room
2
dig_exit
2
create_flags
2
bi_create
36
1
2
!dig_room!dig_exit!create_flags!bi_create!
36
1
4
0
36
1
0
20
36
5
5
36
4
4
1
2
Builder Options
36
5
4
1
2
Option package for $builder commands.  See `help @build-options'.
36
5
4
2
0
3793
0
1001876092
36
1
5
36
5
5
36
5
#78
Mail Name DB

0
36
-1
-1
-1
37
-1
118
4
add
36
173
-1
remove
36
173
-1
load
2
173
-1
init_for_core
2
173
-1
0
9
5
36
5
5
36
1
4
4
2

2

4
0
4
0
36
0
0
0
36
4
4
1
2
Mail Name DB
36
5
2
... for doing mail-recipient name lookups.
36
5
4
2
0
3038
0
1001876092
36
1
5
36
5
5
36
5
#79
Generic Utilities Package

144
2
-1
-1
-1
1
20
81
0
1
help_msg
7
4
1
2
This is the Generic Utility Object.  One presumes it should have text in it explaining the use of the utility object in question.
2
5
0
0
2
4
4
1
2
Generic Utilities Package
2
5
2
This is a placeholder parent for all the $..._utils packages, to more easily find them and manipulate them. At present this object defines no useful verbs or properties. (Filfre.)
2
5
4
2
0
611
0
1001876092
36
1
5
2
5
5
2
5
#80
Byte Quota Utilities

16
36
-1
-1
-1
79
-1
82
36
initialize_quota
36
173
-1
init_for_core
2
173
-1
adjust_quota_for_programmer
36
173
-1
bi_create
2
173
-1
enable_create
2
173
-1
disable_create
2
173
-1
parse_create_args
36
173
-1
creation_permitted verb_addition_permitted property_addition_permitted
36
173
-1
all_characters
36
173
-1
display_quota
36
173
-1
get_quota
36
173
-1
charge_quota
36
173
-1
reimburse_quota
36
173
-1
set_quota
36
173
-1
get_size_quota
36
173
-1
display_quota_summary
36
173
-1
quota_remaining
36
173
-1
preliminary_reimburse_quota
36
173
-1
value_bytes
2
173
-1
object_bytes object_size
2
173
-1
do_summary
36
157
0
summarize_one_user
36
173
-1
recent_object_bytes
2
173
-1
measurement_task
2
173
-1
can_peek
36
173
-1
can_touch
36
173
-1
do_breakdown
2
173
-1
object_overhead_bytes
36
173
-1
property_overhead_bytes
2
173
-1
verb_overhead_bytes
2
173
-1
add_owned_object
2
173
-1
measurement_task_nofork
2
173
-1
measurement_task_body
2
173
-1
schedule_measurement_task
2
173
-1
task_perms
2
173
-1
property_exists
2
173
-1
14
default_quota
large_negative_number
max_unmeasured
unmeasured_multiplier
working
cycle_days
task_time_limit
byte_based
exempted
task_repeat
repeat_cycle
too_large
large_objects
report_recipients
21
4
4
0
50000
0
0
0
0
0
1
36
5
0
-10000
36
5
0
10
36
5
0
100
36
5
1
2
36
5
0
5
36
5
0
500
36
5
0
1
36
5
4
0
36
5
0
1
36
5
0
0
36
5
0
1000000
36
5
4
0
36
5
4
1
1
2
36
5
4
66
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, separated from the scheduling in case you just want to run it once.  Calls the body and then reports via moomail.
2
 :schedule_measurement_task() -- actually schedules it.  Look here to change the start time.
2
 :measurement_task_body(timeout) -- does the real work, working for no longer than timeout seconds.
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.
2
 .repeat_cycle -- boolean.  0 means have a vanilla cycle (goes through all players() exactly once measuring their objects measured more than .cycle_days ago).  1 means to have a much more complex algorithm: The first cycle, it only measures stuff owned by people who have logged in within .cycle_days.  If, in .task_time_limit seconds, it measures all objects not measured in cycle_days owned by such people, it will run again measuring those objects which have not been measured in cycle_days - 1, considering people who have logged in within 4 * cycle_days, repeating until it has used up its seconds.  ("Doing some of tomorrow's work.")  Selecting .repeat_cycle = 1 is appropriate only for large MOOs.
2
 .exempted -- list of objects to never measure (useful if there are huge objects).  Suggested huge objects include $player_db and $site_db.
2
 .measurement_task -- indicates the task_id() of the most recent measurement task -- used to prevent duplicate invocation.
2
 .report_recipients -- recipients of the daily reports.  Set to {} to disable reporting entirely.
2

2
See help @measure and help @quota for the command line verbs.
2

2

2
Porter's notes:  If you are planning on porting this system to another MOO, here are the things to grab in addition to @dumping all of $quota_utils:
2

2
The following verbs have been changed on $prog:
2
@prop*erty @verb @copy (@add-alias @copy-move as well)
2

2
The following verbs have been changed on $wiz:
2
@programmer @quota
2

2
The following verbs have been changed on $wiz_utils:
2
set_programmer set_owner make_player
2

2
The following verbs have been changed on $builder:
2
@quota _create
2

2
This verb probably should have gone on $builder.
2
@measure
2

2
The followig verbs have been changed on $recycler
2
_recycle _create setup_toad
2

2
The following verb has been changed on $login:
2
create
2

2
And don't forget $object_quota_utils, which has the object based implementation.
36
5
0
0
36
4
4
1
2
Byte Quota Utilities
36
5
4
1
2
This is the Byte Quota Utilities utility package.  See `help $quota_utils' for more details.
36
5
4
2
0
32472
0
1001876092
36
1
5
36
5
5
36
5
#81
@paranoid database

16
36
-1
-1
-1
1
-1
83
10
ensure_props_exist
36
173
-1
init_for_core
36
173
-1
add_data
36
173
-1
get_data
36
173
-1
erase_data
36
173
-1
set_kept_lines
36
173
-1
gc
36
173
-1
help_msg
2
173
-1
weekly
2
173
-1
is_paranoid
2
173
-1
0
6
0
0
36
4
4
2
2
@paranoid database
2
paranoid
36
5
4
19
2

2
This object stores the @paranoid data from :tell.  Normally it is not necessary to access these things directly.  All verbs are controlled by a caller_perms() check.  All data is stored in the old .responsible format.
2

2
:add_data(who,data) adds one line's worth of data to the collection, trimming from the front as necessary.
2

2
:get_data(who) retrieves the entire batch of data.
2

2
:erase_data(who) sets the data to {}
2

2
:set_kept_lines(who,number) Changes the number of kept lines.  Maximum is 20.
2

2
Core verbs that call the above are this are $player:tell, @check, @paranoid, and :erase_paranoid_data.
2

2
Internal:  
2
   Properties used are
2
   tostr(player)+"lines"
2
   tostr(player)+"pdata"
2
   :ensure_props_exist(who,linesname,dataname):  creates the above
2
   :GC() --- loops over all data and verifies they're for players.
36
5
4
2
0
5896
0
1001876092
36
1
5
36
5
5
36
5
#82
Object Quota Utilities

16
36
-1
-1
-1
79
-1
59
14
initialize_quota
36
173
-1
init_for_core
2
173
-1
adjust_quota_for_programmer
36
173
-1
bi_create
2
173
-1
creation_permitted
36
173
-1
verb_addition_permitted property_addition_permitted
36
173
-1
display_quota
36
173
-1
get_quota quota_remaining
36
173
-1
charge_quota
36
173
-1
reimburse_quota
36
173
-1
set_quota
36
173
-1
preliminary_reimburse_quota
36
173
-1
can_peek
36
173
-1
can_touch
36
173
-1
1
byte_based
8
0
0
36
5
2
This is the default package that interfaces to the $player/$prog quota manipulation verbs.
36
5
0
0
36
4
4
1
2
Object Quota Utilities
36
5
4
1
2
This is the Object Quota Utilities utility package.  See `help $object_quota_utils' for more details.
36
5
4
2
0
6820
0
1001876092
36
1
5
36
5
5
36
5
#83
Server Options

16
2
-1
-1
-1
1
-1
87
2
help_msg
36
173
-1
init_for_core
2
173
-1
10
protect_chparent
protect_add_verb
protect_add_property
protect_recycle
permit_writable_verbs
protect_set_verb_info
queued_task_limit
help_msg
support_numeric_verbname_strings
connect_msg
16
0
1
2
5
0
1
2
5
0
1
2
5
0
1
2
5
0
0
2
5
0
1
2
5
0
100
2
5
4
32
2
		Server Options <$server_options>
2
                --------------------------------
2

2
messages: 'boot_msg', 'connect_msg', 'create_msg', 'recycle_msg', 'redirect_from_msg', 'redirect_to_msg', and 'timeout_msg'.
2
A number of the messages printed to a connection by the server under various circumstances can now be customized or eliminated from within the DB.  In each case, a property on $server_options is checked at the time the message would be printed.  If the property does not exist, the standard message is printed.  If the property exists and its value is not a string, then no message is printed at all.  Otherwise, the string is printed in place of the standard message.  The following list covers all of the newly customizable messages, showing for each the name of the relevant property on $server_options, the default/standard message, and the circumstances under which the message is printed:
2
'boot_msg'		"*** Disconnected ***"
2
The function boot_player() was called on this connection.
2
'connect_msg'		"*** Connected ***"
2
The user object that just logged in on this connection existed before #0:do_login_command() was called.
2
'create_msg'		"*** Created ***"
2
The user object that just logged in on this connection did not exist before #0:do_login_command() was called.
2
'recycle_msg'		"*** Recycled ***"
2
The logged-in user of this connection has been recycled.
2
'redirect_from_msg'	"*** Redirecting connection to new port ***"
2
The logged-in user of this connection has just logged in on some other connection.
2
'redirect_to_msg'	"*** Redirecting old connection to this port ***"
2
The user who just logged in on this connection was already logged in on some other connection.
2
'timeout_msg'		"*** Timed-out waiting for login. ***"
2
This in-bound network connection was idle and un-logged-in for at least CONNECT_TIMEOUT seconds (as defined in options.h).
2

2

2
Some properties on $server_options can change the server behavior:
2

2
'bg_seconds', 'bg_ticks', 'fg_seconds', and 'fg_ticks'.
2
If those properties exist and are numbers, the server use them instead of the constants DEFAULT_BG_SECONDS, DEFAULT_BG_TICKS, DEFAULT_FG_SECONDS and DEFAULT_FG_TICKS (respectively) defined at compile time in "options.h"; they are looked up anew every time a task begins or resumes execution. Those define ticks (basic operations)/real-time seconds any task is allowed to use without suspending. 'fg' constants/properties are used only for 'foreground' tasks (those started by either player input or the server's initiative and that have never suspended); the 'bg' constants/properties are used only for 'background' tasks (forked tasks and those of any kind that have suspended).
2

2
'max_stack_depth' This allow to change in-db the the maximum verb-call depth. Originillay the maximum verb-call depth is defined at compile time by the DEFAULT_MAX_STACK_DEPTH constant in "options.h". The maximum stack depth for any task is set at the time that task is created and cannot be changed thereafter. This implies that suspended tasks, even after being saved in and restored from the DB, are not affected by later changes to $server_options.max_stack_depth. 
2

2
'queued_task_limit' if this property exist and its value is non-negative, then it is used as the maximum of tasks a verb-owner (more exactly the user's perms the verb run with) can queue (through fork() and suspend()). This setting is overriden if the user has a 'queued_task_limit' property and if its value is non-negative. E_QUOTA is raised of either forking or suspending when the user is over quota for tasks.
2

2
'protect_...' On every call to a built-in function 'foo', if the property $server_options.protect_foo exists and is true, and the programmer is not a wizard, then the server checks for the existence of #0:bf_<fuction> and calls that. If it doesn't exist then E_PERM is raised, i.e. the built-in function is made wiz-only.
2
                --------------------------------
2
5
0
0
36
1
2
*** Connected ***
2
5
0
0
2
4
4
1
2
Server Options
2
5
5
2
5
4
2
0
6512
0
1001876092
36
1
5
2
5
5
2
5
#84
Feature Warehouse

16
36
-1
91
-1
8
-1
61
1
list
36
157
3
0
79
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
0
0
2
1
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
1
2
1
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
36
5
5
36
5
5
36
5
0
0
36
4
4
2
2
Feature Warehouse
2
warehouse
36
5
5
36
5
4
2
0
2268
0
1001876092
36
1
5
36
5
5
36
5
#85
Builder Help DB

16
36
-1
-1
-1
30
-1
86
1
init_for_core
2
173
-1
51
builder-index
@locations
@sort-owned
@verify-owned
@add-owned
@recreate
@parents
@contents
key-representation
keys
@unlock
@lock
locking
@classes
@audit
@count
@quota
@create
creation
@dig
@recycle
building
@dump
@setprop
@set
@builder-options
@builderoptions
@buildoptions
@build-options
room-messages
@resident
exit-messages
@add-entrance
@add-exit
topology
@entrances
@exits
containers
rooms
@remove-entrance
@remove-exit
@unlock_for_open
@lock_for_open
@opacity
container-messages
thing-messages
common_quota
object-quota
@measure
Xpress_Object_Sharing
Xpress_generics
59
4
2
2
*index*
2
Builder Help Topics
36
5
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)
36
5
4
5
2
Syntax:  @sort-owned  [ object | size ]
2

2
Sorts your .owned_objects property so @audit shows up sorted.  See help @audit for more information.
2

2
@sort-owned object will sort by object number (the default).  @sort-owned size will sort by size of object as periodically recorded.
36
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.
36
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 objects is really owned by you and otherwise belongs in your .owned_objects property.  See help @audit for more information.
36
5
4
7
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
You may use "called" instead of "named" in this command, if you wish.
2

2
The <parent> and <name spec> arguments are as in @create.
36
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
  @parents Haakon
2
  Haakon(#2)   generic wizard(#218)   generic programmer(#217)   generic 
2
  player(#6)   Root Class(#1)
36
5
4
8
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
  @contents here
2
  The Entrance Hall(#19) contains:
2
  Strasbourg Clock(#71)   mirror at about head height(#7444)
36
5
4
10
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
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}}}
36
1
4
24
2
LambdaMOO supports 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'.]
36
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.
36
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.
36
1
4
21
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.
2

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.
2

2
The various standard classes of objects use locking as follows:
2
  + Rooms and containers refuse to allow any object inside them if they're locked for it.
2
  + Exits refuse to transport any object that they're locked for.
2
  + Things (including notes and letters) cannot be moved to locations that they're locked for.
2

2
There are two sides to locking:
2
  + How is it specified whether one object is locked for another one?
2
  + What is the effect of an object being locked?
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.
2

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.]
2

2
There is a default way to specify locks on objects; the following help topics cover the relevant commands:
2

2
@lock -- setting a lock on an object
2
@unlock -- clearing the lock on an object
2
keys -- describes the language used to describe lock keys
36
1
4
8
2
Syntax:  @classes
2
         @classes <class-name> ...
2

2
The wizards have identified several useful classes of objects in the database.  The @classes command is used to see which classes exist and what their member objects are.
2

2
The first form simply lists all of the defined classes along with short descriptions of the membership of each.
2

2
The second form prints an indented listing of that subset of the object parent/child hierarchy containing the objects in the class(es) you specify.
36
1
4
27
2
Syntax:  @audit [<player>] [for <string>] [from <number>] [to <number>] 
2

2
`@audit'        prints a report of all of the objects you own.
2
`@audit player' prints the same report for another player.
2

2
The `for' string restricts the search to objects whose names begin with that string.
2
It is also possible to restrict the range of object numbers to include only those above a given number (`from') or below a given number (`to').
2

2
All forms of @audit print a report:
2

2
   #14 Gemba                          [The Pool]
2
  #144 Popgun                         [Gemba]
2
 #1479 Cockatoo                      *[The Living Room]
2
 #1673 Bottom of Swimming Pool       
2
 #2147 Cavern                        <-*west
2
 #2148 tunnel                         Bottom of Swimming ->Cavern
2

2
The first column is the object's number, the second its name. The third column shows the object's location: Gemba is in The Pool, and is carrying the Popgun (#144).
2
For exits, the third column shows source ->dest.
2
For rooms, the third column shows any entrances owned by someone else.
2
Object location, exit sources and destinations owned by another player are preceded by a *.
2

2
@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 @verify-owned, @sort-owned, and @add-owned.
2

2
See also @prospectus, which gives some additional information.
36
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.
36
1
4
2
2
*forward*
2
object-quota
36
1
4
16
2
Syntax:  @create <class-name> named "<names>"
2
         @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.  <class-name> is one of the four standard classes of objects: $note, $letter, $thing, or $container.  As time goes on, more 'standard classes' may be added.  If the parent you have in mind for your new object isn't one of these, you may use the parent's name (if it's in the same room as you) or else its object number (e.g., #4562).
2

2
You may use "called" instead of "named" in this command, if you wish.
2

2
An object must be fertile to be used as a parent-class.  See help @chmod for details.
2

2
The <names> are given in the same format as in the @rename command:
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.
36
1
4
11
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
@classes -- listing all of the public classes available for your use
2
@realm -- listing the parenting tree for objects owned by one or more players
2
@move -- moving your objects from place to place
36
1
4
24
2
Syntax:  @dig "<new-room-name>"
2
         @dig <exit-spec> to "<new-room-name>"
2
         @dig <exit-spec> to <old-room-object-number>
2

2
This is the basic building tool.  The first form of the command creates a new room with the given name.  The new room is not connected to anywhere else; it is floating in limbo.  The @dig command tells you its object number, though, so you can use the @move command to get there easily.
2

2
The second form of the command not only creates the room, but one or two exits linking your current location to (and possibly from) the new room.  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 new room, and the second form when you also want an exit back, from the new room to your current room.  In any case, the <names> piece is just a list of names for the exit, separated by commas; these are the names of the commands players 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
The third form of the command is just like the second form except that no new room is created; you instead specify by object number the other room to/from which the new exits will connect.
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 hunky-dorey.  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
Examples:
2
    @dig "The Conservatory"
2
creates a new room named "The Conservatory" and prints out its object number.
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
    @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
    @dig up,u to #7164
2
creates an exit leading from the player's current room to #7164, which must be an existing room.
36
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.
36
5
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
36
1
4
7
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
   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.
2
1
4
4
2
Syntax:  @set <object>.<prop-name> to <value>
2

2
Changes the value of the specified object's property to the given value.
2
You must have permission to modify the property, either because you own the property or if it is writable.
36
5
4
3
2
*forward*
2
@setprop
2
@set is a valid abbreviation for @setprop.
36
5
4
2
2
*forward*
2
@build-options
36
5
4
2
2
*forward*
2
@build-options
36
5
4
2
2
*forward*
2
@build-options
36
5
4
41
2
Syntax:  @build-option
2
         @build-option <option>
2

2
Synonyms:  @buildoption, @builder-option @builderoption
2

2
The first form displays all of your builder options
2
The second displays just that one option, which may be one of the flags listed below.  The builder options control various annoying details of your building commands (e.g., @create, ...)
2

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

2
         @build-option create_flags [is] <flags>
2
         @build-option create_flags=<flags>
2
         @build-option -create_flags
2
                      (equivalent to create_flags="")
2

2
where flags is some substring of "rwf".  This option determines the read/write/fertility permissions of an object freshly created with @create or @recreate (see `help @create' and `help @recreate' and `help @chmod').  E.g., to make every object you create henceforth readable by default, do
2

2
         @build-option create_flags=r
2

2
For controlling the behavior of @dig, we have
2

2
         @build-option  dig_room=<room>
2
         @build-option  dig_room [is] <room>
2
         @build-option -dig_room
2
                      (equivalent to dig_room=$room)
2
         @build-option  dig_exit=<exit>
2
         @build-option  dig_exit [is] <exit>
2
         @build-option -dig_exit
2
                      (equivalent to dig_exit=$exit)
2

2
The following respectively set and reset the specified flag option
2

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

2
Currently the only builder flag option available is
2
 -bi_create     @create/@recycle re-use object numbers.
2
 +bi_create     @create/@recycle call create()/recycle() directly 
2

2
we prefer that you not use +bi_create, since this drives up the object numbers.
36
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.
36
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.
36
5
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  [%[$exit.leave_msg]]
2
  Printed to the player just before they successfully use the exit.
2

2
@oleave  [%[$exit.oleave_msg||"has left."]]
2
  Printed to others in the source room when a player successfully uses the exit.
2

2
@arrive  [%[$exit.arrive_msg]]
2
  Printed to the player just after they successfully use the exit.
2

2
@oarrive  [%[$exit.oarrive_msg||"has arrived."]]
2
  Printed to others in the destination room when a player successfully uses the exit.
2

2
@nogo  [%[$exit.nogo_msg||"You can't go that way."]]
2
  Printed to the player when they fail in using the exit.
2

2
@onogo  [%[$exit.onogo_msg]]
2
  Printed to others when a player fails in using the exit.
36
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.
36
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 typing 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.
36
1
4
10
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
@remove-exit -- removing exits from your room
2
@remove-entrance -- removing entrances from your room
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
36
1
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.
36
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 typing its name, like 'east'.
36
5
4
15
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
Containers may be open or closed, using the verbs 'open container' and 'close container'.  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 a container by creating a child of the standard container, $container (see 'help @create').
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
Containers have opacity.  See 'help @opacity' for more information.
36
1
4
1
2
Rooms may be made by builders, using the DIG verb. By default, all rooms are instances of _the_ room, $room, or #3, which you can examine to see how it works. If you require a room to have a more specific behaviour, you can make a subclass of room.
36
1
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.
36
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.
36
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.
36
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.
36
5
4
9
2
Syntax:
2
  @opacity <container> is <integer>
2

2
The opacity can take on one of three values:
2
   0:  The container is transparent and you can always see into it.
2
   1:  The container is opaque, and you cannot see into it when closed
2
   2:  The container is a black hole, and you can never see into it whether closed or open.  
2

2
The default @opacity is 1.
36
5
4
49
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 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
@empty[%[$container.empty_msg]]
2
  Printed in place of the contents list when the container is empty.
2

2
@open  [%[$container.open_msg]]
2
  Printed to the player who successfully opens the container.
2

2
@oopen  [%[$container.oopen_msg]]
2
  Printed to others in the same room if the player successfully opens the container.
2

2
@open_fail  [%[$container.open_fail_msg]]
2
  Printed to the player who cannot open the container.
2

2
@oopen_fail  [%[$container.oopen_fail_msg]]
2
  Printed to others in the room when a player fails to open a container.
2

2
@close  [%[$container.close_msg]]
2
  Printed to the player who closes a container.
2

2
@oclose  [%[$container.oclose_msg]]
2
  Printed to others in the room when a player closes a container.
2

2
@put  [%[$container.put_msg]]
2
  Printed to a player when an object is successfully placed in a container.
2

2
@oput  [%[$container.oput_msg]]
2
  Printed to others in the room when a player successfully places an object in a container.
2

2
@put_fail  [%[$container.put_fail_msg]]
2
  Printed when a player fails to put an object in a container.
2

2
@oput_fail  [%[$container.oput_fail_msg]]
2
  Printed to others in the room when a player fails to place an object in a container.
2

2
@remove  [%[$container.remove_msg]]
2
  Printed when a player succeeds in removing an object from a container.
2

2
@oremove  [%[$container.oremove_msg]]
2
  Printed to others in the room when a player succeeds in removing an object from a container.
2

2
@remove_fail  [%[$container.remove_fail_msg]]
2
  Printed when a player fails to remove an object from a container.
2

2
@oremove_fail  [%[$container.oremove_fail_msg]]
2
  Printed to others in the room when a player fails to remove an object from a container.
36
5
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  [%[$thing.take_failed_msg]]
2
  Printed to a player who fails to take the object.
2

2
@otake_failed [%[$thing.otake_failed_msg]]
2
  Printed to others in the same room if a player fails to take the object.
2

2
@take_succeeded  [%[$thing.take_succeeded_msg]]
2
  Printed to a player who succeeds in taking the object.
2

2
@otake_succeeded  [%[$thing.otake_succeeded_msg]]
2
  Printed to others in the same room if a player succeeds in taking the object.
2

2
@drop_failed  [%[$thing.drop_failed_msg]]
2
  Printed to a player who fails to drop the object.
2

2
@odrop_failed [%[$thing.odrop_failed_msg]]
2
  Printed to others in the same room if a player fails to drop the object.
2

2
@drop_succeeded  [%[$thing.drop_succeeded_msg]]
2
  Printed to a player who succeeds in dropping the object.
2

2
@odrop_succeeded  [%[$thing.odrop_succeeded_msg]]
2
  Printed to others in the room if a player succeeds in dropping the object.
36
1
4
7
2
Syntax:  @quota
2

2
Each player has a limit as to how many objects that player may create, called their 'quota'.  Every object they create lowers the quota by one and every object they recycle increases it by one.  If the quota goes to zero, then that player may not create any more objects (unless, of course, they recycle some first).
2

2
The @quota command prints out your current quota.
2

2
The quota mechanism is intended to solve a long-standing problem in many MUDs: database bloat.  The problem is that a large number of people build a large number of dull objects and areas that are subsequently never used or visited.  The database becomes quite large and difficult to manage without getting substantially more interesting.  With the quota system, we can make it possible for players to experiment and learn while simultaneously keeping random building to acceptable levels.
2
1
4
4
2
*forward*
2
common_quota
2

2
To get a larger quota, talk to a wizard.  They will take a look at what you've done with the objects you've built so far and make a determination about whether or not it would be a net gain for the MOO community if you were to build some more things.  If so, they will increase your quota; if not, they will try to explain some ways in which you could build things that were more useful, entertaining, or otherwise interesting to other players.  Wizards may be more impressed by objects which are interactive and employ a fair number of verbs.
36
5
4
18
2
Syntax:
2
  @measure object <object name>
2
  @measure summary [player]
2
  @measure new [player]
2
  @measure breakdown <object name>
2
  @measure recent [number of days] [player]
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!
2

2
@measure recent will let you re-measure objects of yours or another player's which have not been measured in the specified number of days (the default is the ordinary cycle of the measurement task).
36
5
4
5
2
You can share your objects with other Xpress users. To add a shared owner to an object, open the Xpress Object Editor, select the object, click on the button named 'Sharing' and add that person's object number to the list of shared owners. It's important that you only enter one shared owner per line! Shared owners can only edit properties that are available through the Xpress Object Editor. They cannot edit or modify other properties or verbs on your object, nor can they recycle the object. 
2

2
Sharing is a good way to collaborate on projects in the MOO. Be careful however, and make sure you can trust the people you add as shared owners of your objects. You can remove a shared owner from an object at any time simply by deleting their object number from the list of shared owners.
2

2
NOTE: Only one shared owner at a time can work on a shared object. If two or more owners edit the same property at the same time, the person that saves last will override any changes made by other shared owners. Also note that you may only work on shared objects in Xpress.
36
5
4
1
2
Generic objects are objects that have been designated as templates that you can use as a basis for creating your own objects. In the MOO there are several such objects. Some or all of them are found in the Xpress Object Editor. The generic objects are organized into different categories depending on what kind of objects they are. To create a new object based on a generic object simply select a category and then pick the type of object you want to make by clicking on it.
36
5
5
36
5
4
1
2
builder-index
36
1
0
0
36
4
4
2
2
Builder Help DB
2
BHD
36
5
2
This help database contains topics about the generic builder and building commands.
36
5
4
2
0
40986
0
1001876092
36
1
5
36
5
5
36
5
#86
Mail Commands Help Db

16
36
-1
-1
-1
30
-1
151
0
38
mail-index
@mailoptions
@mail-options
mail-forwarding
@forward
@subscribe
@rn
@unsubscribe
@skip
zombie-messages
message-sequences
@unrmmail
@reply
@renumber
@prev
@next
@answer
@rmmail
@read
@send
mail
@mail
@peek
@subscribed
@netforward
@keep-mail
@keepmail
@resend
@qsend
@qreply
@quickreply
@quicksend
@nn
@mail-all-new-mail
@read-all-new-mail
@refile
@copymail
@add-notify
46
4
2
2
*index*
2
Mail System Help Topics
36
5
4
2
2
*forward*
2
@mail-options
36
1
4
93
2
Syntax:  @mail-option
2
         @mail-option <option>
2

2
Synonym:  @mailoption
2

2
The first form displays all of your mail options
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
 -followup         @replys go as directed by `all' flag
2
 +followup         @replys go to first nonplayer recipient if any
2
                    (if there are no non-player recipients, use `all' flag).
2
 -nosubject        @send forces you to provide a Subject: line
2
 +nosubject        allow entering the mail editor without giving a subject line
2
 -expert           novice mail user (various annoying messages will be printed)
2
 +expert           expert mail user (suppress printing of annoying messages)
2
 -enter            start each mail editing session in the usual command mode.
2
 +enter            start each mail editing session with an implicit `enter'
2
                    command
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
 -no_auto_forward  @netforward when expiring messages
2
 +no_auto_forward  do not @netforward messages when expiring mail
2
 -expert_netfwd    @netforward confirms before emailing messages
2
 +expert_netfwd    @netforward doesn't confirm before emailing messages
2

2
For "sticky", `mail command' is one of @mail, @read, @prev, @next, @answer.
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 
2
default.  Initially, this is "last:15", but other reasonable choices include
2
"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
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
2

2
To control expiration of messages (see `help @keep-mail') we have
2

2
        @mail-option expire [is] <time-interval>
2
        @mail-option expire=<time-interval>
2

2
<time-interval> can either be a number of seconds or something with units in it, e.g.,
2

2
        @mail-option expire 13 days
2
        @mail-option expire 1 year 39 days
2

2
A negative number or
2

2
        @mail-option +expire
2

2
disables message expiration entirely
2

2
	@mail-option -expire
2

2
sets your message expiration time to the current default.
36
5
4
30
2
There are 3 personal properties that you can use to customize how your mail is composed and forwarded
2

2
.mail_forward 
2
 -- list of objects that will receive any mail that gets sent to you.
2
    Objects on this list should either be players or descendants of 
2
    $mail_recipient.
2
    If this list is nonempty, you will not receive any mail yourself unless
2
    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
 -- list of objects to be notified whenever mail is sent to you.
2
    This list may include anything that has a :notify_mail() verb.
2
    Notification will take place regardless of whether or how your mail
2
    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
 -- this controls lots of miscellaneous things.  Use the @mail-option command
2
    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.
36
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
36
5
4
22
2
Syntax:  @subscribe *<collection> [with|without notification] [before|after *<collection>]
2
         @subscribe
2
         @subscribe-quick
2

2
The second and third form of the command gives a list of all mail collections that are readable by you. The third form omits the mail collection description.
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
2
which actually adds you to the .mail_forward list for a given collection/
2
mailing-list, so that mail sent to the list actually shows up in your own
2
mail collection.
2
We're probably going to phase out the Mail Room `subscribe' command...
36
5
4
5
2
Syntax:  @rn
2

2
For each collection of mail messages that you read other from your own, a last-read-time is kept.  This 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
Etymologists' note:  If you thought @rn had anything to do with the popular UNIX newsreading program `rn', it's just your imagination.
36
5
4
2
2
*forward*
2
@skip
36
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.
36
5
4
2
2
*forward*
2
@unrmmail
36
5
4
55
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
  new       unread messages if any
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
 kept:                       messages marked as kept (see `help @keep-mail')
2
 unkept:                     messages not marked as kept
2

2
<date>  is either a weekday, "today", "yesterday", 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).
36
1
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.
36
5
4
2
2
*forward*
2
@answer
36
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.
36
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, print (and decrement current message by) that many messages.
36
5
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.
36
1
4
17
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', `all', or `followup' in the command line (or your .mail_options).
2
  `sender'   replies to sender only
2
  `all'      replies to sender and all original recipients
2
  `followup' replies to first original recipient that is a non-player
2
             (no effect if there are no non-player recipients).
2

2
`include' includes the text of the original message in your reply, `noinclude' does not.  
2

2
`sender', `all', `followup', `include', and `noinclude' can all 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').  
36
5
4
10
2
Syntax:  @rmmail [<message-sequence>] [from *<recipient>]
2

2
Deletes the indicated messages from your MOO email collection.
2
By default, your current message is deleted.
2
See `help message-sequence' for the full list of possible arguments.
2
You get the message numbers for use here by typing the '@mail' command.
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...).
36
5
4
14
2
Syntax:  @read <message-number>
2
         @read
2
         @peek ...
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'.
2
As with the @mail command you may @read messages on any publically readable collection (child of $mail_recipient).
2

2
The @peek command is a variant of @read that works no differently except that it refrains from setting your `current message'.  This is useful, for example, if you want to see some particular message on a collection without losing track of where you are in it.
36
5
4
10
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.   
2
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
Use `say' (") to insert lines in the body of your message.  
2
See `help editors' for details.
2

2
Giving this command without arguments resumes editing the previous unsent draft message if one exists.
36
5
4
34
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:
2

2
@mail     -- seeing a table of contents for a collection of email messages
2
@read     -- reading individual messages 
2
@next     -- reading the 'next'     message
2
@prev     -- reading the 'previous' message
2

2
@send     -- composing and sending a message to other players
2
@answer   -- replying to one of the messages in a collection
2
@forward  -- resending one of the messages in a collection somewhere else
2

2
@rmmail   -- discarding some subset of a collection
2
@unrmmail -- undoing the most recent @rmm on a collection
2
@renumber -- renumbering the messages in a collection
2
@keep-mail - marking messages in a collection as exempt from expiration
2

2
@mail-option -- describes and sets various customization flags
2

2
help mail-forwarding
2
 -- describes mail forwarding
2
help message-sequences
2
 -- describes message-sequences arguments to @mail, @read, @rmm, and @answer.
2

2
For viewing collections other from your own, the following commands are useful:
2

2
@rn          -- list those collections that have new messages on them
2
@subscribe   -- indicate that you want @rn to report on a given collection
2
                  and add yourself to its .mail_notify list
2
@skip        -- ignore any remaining new messages in a given collection
2
@unsubscribe -- ignore a given collection entirely from now on
2
                  and remove yourself from its .mail_notify list
2
@unsubscribed-- show the mailing lists that you aren't subscribed to.
2
@subscribed  -- like @rn, but shows all lists that you are subscribed to
2
                even if they have no new activity
36
5
4
16
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
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.
2
The general format is
2

2
         @mail <message-sequence> [on *<recipient>]
2

2
<recipient> must name some child of $mail_recipient (e.g., a mailing list);
2
<message-sequence> can be a sequence of message numbers; you can also scan the recipient for articles that fit a given description, as specified 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.
36
1
4
2
2
*forward*
2
@read
36
5
4
3
2
Syntax: @subscribed
2

2
Like @rn, but shows you ALL mailing lists to which you are subscribed, even those which have no new messages.
36
5
4
16
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
Before messages are sent, you are asked to confirm the email unless you have set your `expert_netfwd' mail option on.  See `help @mail-options' for details.
2

2
If you do not have a valid registered email address, you will not be able to use this command and must first @registerme or get a wizard to register your address. `help @registerme' for details.
36
5
4
25
2
Syntax:  @keep-mail [<message-sequence>]
2
         @keep-mail none
2

2
Synonym: @keepmail
2

2
As a space-saving measure, there is a task that runs periodically and from all player collections removes those messages that are
2
  (1) marked as read (i.e., dated before the last-read-date), 
2
  (2) older than a certain expire time, and
2
  (3) not specifically marked as "kept".
2

2
"kept" messages will show up in your @mail listing with an `=' to the right of the message number.
2

2
The first form of the @keep-mail command marks the indicated messages as kept.  The message-sequence argument is as with other mail commands (see `help message-sequences').  @keep-mail without any arguments marks your current message as kept.
2

2
The second form of the command is used to remove all such marks from your mail collection.
2

2
    @mail kept:
2

2
will produce a list of all messages marked as kept (see `help message-sequences').
2

2
You can use @mail-option expire (see `help @mail-option') to change your expire time.
2

2
You can use the @netforward command (see `help @netforward') to send mail to your email address for local archival.
2

2
[...At the time of this writing, player mail is taking up a large fraction of LambdaMOO's database.  It would be appreciated if you not @keep any more mail than you have to...]
36
5
4
2
2
*forward*
2
@keep-mail
36
5
4
2
2
*forward*
2
@forward
36
5
4
2
2
*forward*
2
@quicksend
36
5
4
2
2
*forward*
2
@quickreply
36
5
4
6
2
Syntax:   @quickreply  <msg> [on *<recipient>] [<flags>...]
2
          @quickreply  <msg> [<flags>...] [on *<recipient>] 
2

2
Synonym:  @qreply
2

2
The @quickreply command allows you to do a short reply to a mail message without heading for the mail room.  As with @reply, the subject line on the reply message is taken from the original.  <flags> are as with @reply, except that `include' is not recognized --- if you're going to include the original message, you *have* to trim it down, and that requires the editor.
36
5
4
6
2
Syntax:   @quicksend <player> [subj="<text>"] <one-line-message>...
2
          @quicksend <player> [subj="<text>"]
2

2
Synonym:  @qsend
2

2
The @quicksend command allows you to send a short mail message without heading for the mail room.  With the second form of the command you will be prompted for the body of the message (and a subject line unless your `nosubject' mailoption is set (see `help @mail-option') or you already gave a subject line).
36
5
4
15
2
Alternative mail reading commands.
2

2
Syntax:  @nn
2

2
This command finds a folder containing an unread message, displays the first such, and updates your last-read-time for that folder.  Your personal mail and all @subscribed folders are checked.
2

2
Syntax:  @mail-all-new-mail
2

2
Displays headers of all unread messages on all of your folders (i.e., your personal folder and all @subscribed folders).
2

2
Syntax:  @read-all-new-mail
2

2
Displays all unread messages on all of your folders (i.e., your personal folder and all @subscribed folders).  This command ends with a prompt, "Did you get all of that? [Enter `yes' or `no']."  Answering "yes" causes all of your last-read times to be updated.  You will probably want to check for <<<n lines flushed>>> indications before you answer this question.
2

2
@read-all-new-mail (with a "yes" answer at the end) is equivalent to doing a large number of @nn's.
36
5
4
2
2
*forward*
2
@nn
36
5
4
2
2
*forward*
2
@nn
36
5
4
6
2
Syntax:  @refile <message-seq> [on mail-recipient] to mail-recipient
2
         @copymail <message-seq> [on mail-recipient] to mail-recipient
2

2
Moves a message sequence directly from one mail recipient (defaulting as per the sticky @mail-option) to another, without adding headers or reordering.
2

2
NOTE:  The recipient of @refiled messages may well have its mail out of order.  This can confuse a variety of features of the mail system, which expects its messages to always be in chronological order.  Care should be exercised with @refile/@copymail.
36
5
4
2
2
*forward*
2
@refile
36
5
4
6
2
Usage:  @add-notify me to player
2
    Sends mail to player saying that I want to be added to their mail notification property.
2
Usage:  @add-notify player to me
2
    Makes sure that player wants to be notified, if so, adds them to my .mail_notify property.
2

2
In order for one person to be notified that another person has new mail, both the mail recipient and the notification recipient should agree that this is an OK transfer of information.  This verb facilitates that transaction.
36
5
5
36
5
4
1
2
mail-index
36
1
0
0
36
4
4
1
2
Mail Commands Help Db
36
5
2
This help database contains topics relating to the general use of the mail system.
36
5
4
2
0
34420
0
1001876092
36
1
5
36
5
5
36
5
#87
FTP utilities

16
2
-1
-1
-1
1
-1
30
16
open
2
173
-1
close
2
173
-1
do_command
2
173
-1
wait_for_response
2
173
-1
controls
2
173
-1
get_messages
2
173
-1
open_data
2
173
-1
get_data
2
173
-1
put_data
2
173
-1
trusted
2
173
-1
listen
2
173
-1
close_data
2
173
-1
get
2
173
-1
init_for_core
2
173
-1
put
2
173
-1
data_connection
2
173
-1
3
trusted
port
connections
9
0
1
2
5
0
21
2
5
4
0
2
0
0
0
2
4
4
1
2
FTP utilities
2
5
5
2
5
4
2
0
9131
0
1001876092
36
1
5
2
5
5
2
5
#88
password verifier

16
2
-1
-1
-1
5
-1
96
13
help_msg
36
173
-1
reject_password
2
173
-1
trivial_check
36
173
-1
check_length
36
173
-1
check_name
36
173
-1
check_email
2
173
-1
check_hosts
2
173
-1
check_dictionary
36
173
-1
check_for_funky_characters
36
173
-1
check_against_moo
36
173
-1
_is_funky_case
36
173
-1
check_obscure_combinations
36
173
-1
init_for_core
2
173
-1
9
minimum_password_length
check_against_name
check_against_email
check_against_hosts
check_against_dictionary
require_funky_characters
help_msg
check_against_moo
check_obscure_stuff
69
0
0
2
1
0
0
2
1
0
0
2
1
0
0
2
1
0
0
2
1
0
0
2
1
4
8
2
Password Verifier
2
==================
2

2
To check for the validity of a password, use
2
  :reject_password( password [, for-whom? ] )
2
... If it returns a true value, that value will contain the string representing the reason why the password was rejected.  If it returns a false value, the password is OK.
2

2
The toggle switches for this checking are:
36
1
0
0
2
1
0
0
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
4
2
password verifier
2
password
2
verifier
2
pwd
2
5
2
The password verifier verifies passwords.
2
5
4
2
0
11661
0
1001876092
36
1
5
2
5
5
2
5
#89
Password-Change-Log

0
2
46
-1
122
29
-1
-1
3
receive_message
2
173
-1
to_msg_seq
2
173
-1
init_for_core
2
173
-1
0
27
2
PASSWORD CHANGED
2
5
5
2
5
0
0
36
1
4
0
36
0
0
1
2
5
5
2
5
5
2
5
5
2
5
4
1
1
2
36
1
4
0
36
1
5
36
1
5
36
1
4
0
36
0
5
2
5
5
2
5
4
0
36
1
5
2
5
4
0
36
1
5
36
0
5
36
0
5
36
0
0
0
2
4
4
2
2
Password-Change-Log
2
Passwords
36
1
2
The log of wizardly/Registrar password changes.
2
5
4
2
0
2206
0
1001876092
36
1
5
2
5
5
2
5
#90
Generic Player

144
36
-1
-1
-1
40
31
38
73
@rooms
36
13
-1
names_of
36
173
-1
lookup_room
36
173
-1
teleport
36
173
-1
teleport_messages
36
173
-1
@move
36
93
-2
index_room
36
173
-1
@addr*oom
36
29
-1
@rmr*oom
36
29
-1
@find
36
29
-1
find_verb
36
173
-1
@ways
36
29
-1
findexits
36
173
-1
checkexits
36
173
-1
self_port_msg player_port_msg thing_port_msg join_msg
36
173
-1
oself_port_msg self_arrive_msg oplayer_port_msg player_arrive_msg victim_port_msg othing_port_msg thing_arrive_msg object_port_msg
36
173
-1
msg_sub
36
173
-1
obvious_exits
36
173
-1
tell_ways
36
173
-1
tell_obj
36
173
-1
parse_out_object
36
173
-1
enlist
36
173
-1
@spellm*essages @spellp*roperties
2
89
-2
@at
36
93
-2
at_players
36
173
-1
do_at_all
36
173
-1
do_at
36
173
-1
print_at_items
36
173
-1
at_item
36
173
-1
internal_at
36
173
-1
confunc
2
173
-1
disfunc
2
173
-1
@addword @adddict
2
89
-2
@spell @cspell @complete
2
89
-2
@rmword
2
89
-2
@rmdict
2
89
-2
find_property
36
173
-1
find_verbs_on
36
173
-1
find_properties_on
36
173
-1
property_inherited_from
36
173
-1
@ref*use
36
89
-2
@unref*use @allow
36
89
-2
@refusals
36
73
-2
@refusal-r*eporting
36
89
-2
parse_refuse_arguments
36
173
-1
time_word_to_seconds
36
173
-1
parse_time_length
36
173
-1
parse_time
36
173
-1
clear_refusals
36
173
-1
set_default_refusal_time
36
173
-1
refusable_actions
36
173
-1
translate_refusal_synonym
36
173
-1
default_refusals_text_filter
36
173
-1
refusals_text
36
173
-1
player_to_refusal_origin
36
173
-1
refusal_origin_to_name
36
173
-1
check_refusal_actions
36
173
-1
add_refusal
36
173
-1
remove_refusal
36
173
-1
remove_expired_refusals
36
173
-1
refuses_action
36
173
-1
refuses_action_*
36
173
-1
report_refusal
36
173
-1
wh*isper
36
157
1
receive_page
36
173
-1
page_echo_msg
36
173
-1
moveto acceptable
36
173
-1
receive_message
36
173
-1
whisper_refused_msg page_refused_msg mail_refused_msg
36
173
-1
last_huh
2
173
-1
ping_features
2
173
-1
set_owned_objects
2
173
-1
init_for_core
2
173
-1
25
at_room_width
at_number
join_msg
object_port_msg
victim_port_msg
thing_arrive_msg
othing_port_msg
thing_port_msg
player_arrive_msg
oplayer_port_msg
player_port_msg
self_arrive_msg
oself_port_msg
self_port_msg
rooms
refused_origins
refused_extra
default_refusal_time
report_refusal
refused_actions
refused_until
page_refused
page_refused_msg
whisper_refused_msg
mail_refused_msg
170
0
30
36
5
0
0
36
5
2
You join %n.
36
5
2
teleports you.
36
5
2
teleports you.
36
5
2
%T teleports %n in.
36
5
2
%T teleports %n out.
36
5
2
You teleport %n.
36
5
2
%T teleports %n in.
36
5
2
%T teleports %n out.
36
5
2
You teleport %n.
36
5
2
%<teleports> in.
36
5
2
%<teleports> out.
36
5
2

36
5
4
0
36
1
4
0
36
1
4
0
36
1
0
604800
36
1
0
0
36
1
4
0
36
1
4
0
36
1
0
0
36
1
2
%N refuses your page.
36
5
2
%N refuses your whisper.
36
5
2
%N refuses your mail.
36
5
5
36
5
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
4
5
36
4
5
36
1
5
2
0
5
2
0
5
2
1
0
0
36
5
5
36
5
5
36
1
5
36
1
5
36
0
5
36
1
5
2
1
5
36
1
5
36
4
5
2
0
5
2
0
5
36
5
5
36
5
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
2147483647
2
1
5
36
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
0
5
36
5
5
36
5
5
36
5
5
36
5
0
2147483647
2
1
4
4
0
50000
0
0
0
0
0
1
36
0
5
2
0
5
2
0
5
2
1
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
4
5
2
1
5
36
5
5
2
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
0
5
36
4
5
36
4
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
36
5
5
36
5
5
36
5
5
36
4
4
2
2
Frand's player class
2
player class
2
1
2
You see a player who should type '@describe me as ...'.
36
5
4
2
0
64056
0
1001876092
36
1
5
36
5
5
36
5
#91
Stage-Talk Feature

144
36
84
-1
92
74
-1
92
6
`* to
36
93
-2
stage [*
36
93
-2
stage ]*
36
93
-2
stage <*
36
93
-2
bb
2
85
-2
think
2
85
-2
0
64
5
36
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.
36
5
4
5
2
`
2
[
2
]
2
-
2
<
36
1
5
36
1
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
36
5
5
36
5
5
36
5
5
36
4
4
1
2
Stage-Talk Feature
36
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.
36
5
4
2
0
5923
0
1001876092
36
1
5
36
5
5
36
5
#92
Pasting Feature

144
36
84
-1
110
74
-1
110
3
@paste
36
93
-2
|*
36
93
-2
@pasteto @paste-to
36
29
-1
0
64
5
36
1
2
The Pasting Feature is mostly useful to people with fancy clients (such as Emacs) or who connect using a windowing system that allows them to copy text they've already seen.  It's intended to give people a way to quote verbatim text at other people in the room.
36
5
4
3
2
@paste
2
|
2
@paste-to
36
1
5
36
1
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
36
5
5
36
5
5
36
5
5
36
4
4
1
2
Pasting Feature
36
5
2
Verbs useful to people using a windowing system to paste text at people.
36
5
4
2
0
4978
0
1001876092
36
1
5
36
5
5
36
5
#93
Vector and Matrix Utils

16
36
-1
-1
-1
79
-1
124
28
vector_add vector_sub vector_mul vector_div
36
173
-1
matrix_add matrix_sub
36
173
-1
transpose
36
173
-1
determinant
36
173
-1
inverse
36
173
-1
identity
36
173
-1
null
36
173
-1
is_square
36
173
-1
is_null
36
173
-1
is_identity
36
173
-1
cross_prod outer_prod vector_prod
36
173
-1
norm length
36
173
-1
submatrix
36
173
-1
dot_prod inner_prod scalar_prod
36
173
-1
dimension*s
36
173
-1
order
36
173
-1
scalar_vector_add scalar_vector_sub scalar_vector_mul scalar_vector_div
36
173
-1
subtended_angle
36
173
-1
column
36
173
-1
matrix_mul
36
173
-1
scalar_matrix_mul scalar_matrix_div
36
173
-1
is_matrix
36
173
-1
is_vector
36
173
-1
is_reflexive is_areflexive
36
173
-1
is_symmetric is_asymmetric
36
173
-1
is_transitive is_atransitive
36
173
-1
_relation_result
36
173
-1
is_partial_ordering
36
173
-1
1
note
8
2
Please contact Uther@LambdaMOO if you make changes to this object, so he can make the changes on Lambda and elsewhere.
36
5
4
62
2
Utility verbs for manipulating lists as vectors (one dimensional lists) or as matrices (two dimensional lists).
2

2
Some definitions:
2
A VECTOR is a list of INTs or a list of FLOATs. Each element in the list represents the vector's cartesian coordinate as measured from its tail to its tip. (For instance, {3, 4} represents a vector in the x-y plane with an x component of 3 and a y component of 4. {-2, 5, 10} represents a vector in 3-space with a x component of -2, a y component of 5 and a z component of 10.)
2
A MATRIX is a list of VECTORs, all of which have the same number (and type) of components.
2

2
Vector verbs:
2
:vector_add        (V1 [,V2 ...]) => VN such that VN[n] = V1[n] + V2[n]...
2
:vector_sub        (V1 [,V2 ...]) => VN such that VN[n] = V1[n] - V2[n]...
2
:scalar_vector_mul (V, S)         => VN such that VN[n] = V[n] * S...
2
:scalar_vector_div (V, S)         => VN such that VN[n] = V[n] / S...
2
:dot_prod          (V1, V2)       => NUM sum of the products of the 
2
:inner_prod                          corresponding elements of the two
2
                                     vectors.
2
:cross_prod        (V1, V2)       => VN, the vector perpendicular to both V1
2
:outer_prod                          and V2 with length equal to the area of
2
                                     the parallelogram spanned by V1 and V2.
2
:subtended_angle   (V1, V2)       => FLOAT smallest radian angle defined by
2
                                     V1 and V2.
2
:length            (V)            => FLOAT length of the vector. 
2
:norm
2

2
Matrix and Vector verbs:
2
:dimensions (M) => LIST of dimensional sizes
2
:order      (M) => NUM of dimensions
2

2
Matrix verbs:
2
:matrix_add (M1 [,M2 ...]) => MN such that MN[m][n] = M1[m][n] + M2[m][n]...
2
:matrix_sub (M1 [,M2 ...]) => MN such that MN[m][n] = M1[m][n] - M2[m][n]...
2
:matrix_mul (M1, M2)       => MN such than MN[m][n] = the dot product of the  
2
                              mth row of M1 and the nth column of M2.
2
:scalar_matrix_mul (M, S)  => MN such that MN[m][n] = M[m][n] * S...
2
:scalar_matrix_div (M, S)  => MN such that MN[m][n] = M[m][n] / S...
2
:transpose  (M1)           => M2 such that the rows in M1 are the columns in
2
                              M2 and vice versa.
2
:identity   (INT <size>)   => Identity matrix (I) of dimensions <size> by 
2
                              <size>.
2
:null       (INT <size>)   => Null matrix (O) of dimensions <size> by <size>.
2
:is_square  (M)            => 1 iff dimensions of M are equal.
2
:column     (M, INT <n>)   => LIST the nth column of M.
2

2
Square Matrix verbs:
2
:determinant (M) => NUM the determinant of the square matrix.
2
:inverse     (M) => the matrix that M multiplied by :inverse(M) yields I.
2
:is_identity (M) => 1 iff M is I.
2
:is_null     (M) => 1 iff M is O.
2

2
Relation verbs:
2
:is_reflexive   (M) => 1 if M is a reflexive relation, -1 if areflexive,
2
                       0 otherwise.
2
:is_areflexive  (M) => 1 if M is an areflexive relation, -1 if reflexive,
2
                       0 otherwise.
2
:is_symmetric   (M) => 1 if M is a symmetric relation, -1 if asymmetric,
2
                       0 otherwise.
2
:is_asymmetric  (M) => 1 if M is an asymmetric relation, -1 if symmetric,
2
                       0 otherwise.
2
:is_transitive  (M) => 1 if M is a transitive relation, -1 if atransitive,
2
                       0 otherwise.
2
:is_atransitive (M) => 1 if M is an atransitive relation, -1 if transitive,
2
                       0 otherwise.
2
:is_partial_ordering (M) => 1 if M is a reflexive, asymmetric, transitive
2
                            relation.
36
5
0
0
36
4
4
3
2
Vector and Matrix Utils
2
vector
2
matrix
36
5
2
This is a utilities package for dealing with lists as representations of vectors and matrices. Type `help $matrix_utils' for more details.
36
5
4
2
0
29797
0
1001876092
36
1
5
36
5
5
36
5
#94
Generic Gendered Object

144
2
-1
-1
-1
1
35
37
2
set_gender
2
173
-1
@gen*der
2
105
12
11
gender
pqc
pq
ppc
pp
prc
pr
poc
po
psc
ps
17
2
neuter
2
5
2
its
2
5
2
its
2
5
2
Its
2
5
2
its
2
5
2
Itself
2
5
2
itself
2
5
2
It
2
5
2
it
2
5
2
It
2
5
2
it
2
5
0
0
2
4
4
1
2
Generic Gendered Object
2
5
5
2
5
4
2
0
1715
0
1001876092
36
1
5
2
5
5
2
5
#95
News Item

16
2
120
-1
96
9
116
108
7
set_text
2
173
-1
read_by
2
173
-1
mark
2
173
-1
display_item
2
173
-1
initialize
2
173
-1
r*ead
2
45
-1
init_for_core
2
173
-1
1
read_by
67
4
0
2
5
5
2
5
5
2
4
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
0
1
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Generic News Item
2
5
2
The MOO newsletter
2
5
4
2
0
4148
0
1001876092
36
1
5
2
5
5
2
5
#96
Generic Recording System

144
2
120
-1
97
5
-1
97
19
p*ress push
2
153
4
create_note
2
173
-1
start_recorder
2
173
-1
stop_recorder
2
165
-1
start stop
2
173
-1
check_recorder
2
173
-1
look_self
2
173
-1
insert
2
173
-1
display
2
173
-1
erase_display
2
173
-1
refresh_display
2
173
-1
on_off
2
173
-1
title
2
173
-1
select
2
173
-1
mic_on mic_off
2
173
-1
check_mic
2
173
-1
broadcast
2
173
-1
announce
2
173
-1
init_for_core
2
173
-1
4
recorders
help_msg
design
on
64
4
0
2
5
4
25
2
The Lingua MOO Recording and Intercom System is designed to record activity in up to five connected rooms and provide means of intercommunication between the control room and the connected rooms. The system comes with a control panel and a set of recording and intercom devices. The two main functions of this system, as well as the setup procedure, are explained below.
2

2
SETUP:
2
Before you can use your new recording system, you need to set it up with one or more recording and intercom devices. Follow these four steps to accomplish this setup process:
2

2
1) Type '@create $intercom named whatever' to make as many intercoms as you need (up to five such devices can be controlled by one panel) and be sure to make a note of the object number of each new intercom. 
2

2
2) Next you need to assign your new intercom devices to the particular panel you wish to use them with. To do this, type: '@set name-of-panel.recorders to {#XXX, #XXX, #XXX, #XXX, #XXX}.' and insert the object numbers of the intercom devices where it says #XXX. (Example: @set panel.recorders to {#100, #101, #102})
2

2
3) Then you must assign the object number of your control panel to all the intercom devices. When you have found this number, type: '@set name-of-intercom.control_panel to #XXX'. (Example: @set intercom1.control_panel to #99)
2

2
4) Lastly, you need to drop the intercom devices in the rooms in which you intend to use them. It might be a good idea to lock the panel and intercom devices to the rooms in which you intend to use them, as this will prevent people from picking them up and moving them around. Type '@lock item with here' to do this.
2

2
RECORDING:
2
You start and stop the recorders in the connected rooms by pressing buttons 1 or 2 on the control panel. A submenu will appear from which you can start/stop individual recorders, or you can type 'all' to start/stop all the recorders connected to the system. Every time you start a recorder, a log will be created for you automatically, provided that you have sufficient free quota. When you stop the recorders you will be asked if you want the system to forward the logs to your registered email address. You can also download the logs at any time by using the @mailme function. The logs are stored on you so you can read them at any time (even during recording) by typing 'read logname'. Please remember that logs can take up a lot of space so please @recycle them when you no longer need them.
2

2
MICROPHONES:
2
You can listen to the activity in one or more rooms by switching on the microphones in those rooms. You control the microphones by pressing buttons 3 or 4 on the control panel. A submenu will appear from which you can switch on/off individual microphones, or you can type 'all' to switch on/off all the microphones connected to the system. To talk to one or more rooms you need to switch on the microphone in the room where the control panel is. You can now talk to people in all rooms where microphones are on. To talk to one specific room only you need to switch off the microphones in the other rooms (except the one in the control room) first. To broadcast a message to all rooms turn all the microphones on.   
2

2
POWER ON/OFF:
2
You can easily reset the system by turning the power off and then back on. If there are any recordings in progress you will be asked if you want the system to forward them to your email address before shutting down.
2

2
The control panel display will give you instant information about which recorders and microphones are on or off at any given time. Every time you start/stop a recorder or switch on/off a microphone people in the affected rooms will be informed.
2

2
Jan@LinguaMOO
2
5
2
Jan@LinguaMOO, 1996
2
5
0
0
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Generic Panel
2
5
5
2
5
4
2
0
21094
0
1001876092
36
1
5
2
5
5
2
5
#97
Generic Recorder and Intercom Device

144
2
120
-1
98
5
-1
100
4
tell
2
173
-1
title
2
173
-1
look_self
2
173
-1
init_for_core
2
173
-1
4
mic
control_panel
on
note
64
0
0
2
5
1
-1
2
5
0
0
2
5
1
-1
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Intercom
2
5
2
 
2
5
4
2
0
3994
0
1001876092
36
1
5
2
5
5
2
5
#98
Generic Moderated Room

144
2
120
-1
99
3
-1
99
27
ask
2
89
-2
mod*erator
2
13
-1
q*uestions
2
89
-2
for*mat
2
89
-2
announce_question
2
173
-1
up u
2
9
-1
down d
2
9
-1
clap cheer
2
9
-1
say emote
2
85
-2
look_self
2
173
-1
tell_contents
2
173
-1
headset
2
89
-2
exitfunc
2
173
-1
sit
2
9
-1
program
2
89
-2
enterfunc
2
173
-1
display_program
2
173
-1
auth*orized
2
93
-2
check_player
2
173
-1
lock
2
93
-2
stand
2
13
-1
find_listeners
2
173
-1
announce
2
173
-1
announce_all
2
173
-1
disfunc
2
173
-1
announce_all_but
2
173
-1
init_for_core
2
173
-1
19
silent
moderator
queue
panel
authorized
program
panel_description
speakers
gags
topic
audience
help_msg
panel_locked
sitters
design
moderator_msg
panel_msg
panel_color
moderator_color
87
0
0
2
5
0
0
2
5
4
0
2
5
4
0
2
5
4
0
2
5
4
0
2
5
4
3
2
You see a table facing the audience and a row of executive chairs behind
2
it.  On the table are several pitchers of water and some flowers.
2
Type 'down' to get down from the stage.
2
5
2

2
5
4
0
2
5
2

2
5
4
0
2
5
4
55
2
INTRODUCTION
2

2
The LinguaMOO auditorium was designed to accomodate both open format and moderated discussions. When the room is configured in open format it works just like any other room in that everyone in the room can hear every-one else. When configured for panel sessions, however, only persons seated in the panel can be heard throughout the room. The audience on the main floorcan still talk and emote among themselves, but they can now only address the panel via the command `ask'. Questions are sent to the moderator who in turn can retrieve them and field them to the panel. A special command, `headset' can be used to filter out text not coming from the panel in both open and panel format. This command is convenient if you want to listen to the speakers without being disturbed by what others in the audience say.
2

2
In addition to the standard MOO communication commands, the following commands have been implemented in this room:
2

2

2
SPECIAL WIZARD COMMAND
2

2
Authorized add|remove player 
2

2
Moderators need to be authorized by a wizard. The reason for this is twofold. First of all this will give the wizard a chance to train the new moderator and make sure that he or she understands how the room works before hosting a meeting. Secondly, the authorization system will prevent just anybody from taking over the role as moderator while the meeting is in session and thus, possibly create confusion.
2

2
MODERATOR COMMANDS
2

2
Moderator
2

2
Type this command to assume the role of moderator.
2

2
Format open|panel
2

2
Sets the room format to open or panel. Format without arguments will display the room's current format. In open format everyone can hear everyone else, whereas in panel format only the speakers can address the whole room. What is being said in the audience cannot be heard in the panel while the room is in panel format.
2

2
Lock on|off
2

2
Enables you to lock entry to the panel. Useful in case you need to  make sure that only speakers can enter the panel and talk to the  whole room during a panel format session.
2

2
Program setup|erase
2

2
The program setup command will guide you through a set of questions which will enable you to compose the program for your event. Program erase will erase a previously stored program, whereas program without argument will display the currect program.
2

2
Questions
2

2
With this command you operate the question queueing system. You can list all questions in the queue by typing 'q', Pop off the first question by typing 'q pop', or pop off a specific question by typing 'q pop #'.  Questions will be erased automatically after having been sent to the panel.
2

2
PLAYER COMMANDS
2

2
Ask
2

2
When the room is in panel format you need to use this command in order to send a question to the panel. Your question will be sent to the moderator who in turn will field it to the panel. Example: 'ask I have a question...could you say more about blah, blah, blah?'
2

2
Headset on|off
2

2
This command is useful if you want to see only what is being said in the  panel. While you have the headset on, all that's being said in the audience will be filtered out both when the room is in panel and open format.
2

2
Program
2

2
This command will display the meeting program if it exists.
2

2
Sit
2

2
Another command for displaying an existing meeting program.
2

2
Design Jan@LinguaMOO
2
Suggestions and bug reports are welcome
2
5
0
0
2
5
4
0
2
5
2
Jan@LinguaMOO, 1995
2
5
2
<B>Moderator: </B>
2
5
2
<B>Panelists: </B>
2
5
2
LightGreen
2
1
2
Salmon
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
4
0
2
4
1
2
2
5
0
901188107
2
5
4
0
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
moderated_room.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
0
1
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Auditorium
2
5
4
1
2
You enter a spacious auditorium with rows of chairs and a small platform stage . Type 'up' to enter the stage.
2
5
4
2
0
30495
0
1001876092
36
1
5
2
5
5
2
5
#99
Generic Classroom

144
2
120
-1
100
3
-1
111
45
player_loc
2
173
-1
object_loc
2
173
-1
is_table
2
173
-1
l*ook
2
89
-2
readboard
2
165
-1
writeb*lackboard
2
89
-2
eraseb*lackboard
2
25
-1
cleanb*lackboard
2
9
-1
@sign
2
89
-2
@addfur*niture
2
89
-2
@rmfur*niture
2
89
-2
sit
2
89
-2
stand
2
93
-2
exitfunc
2
173
-1
look_self
2
173
-1
correct
2
173
-1
enterfunc
2
173
-1
say
2
89
-2
speak*up su speak_up speak-up
2
89
-2
put
2
89
4
g*et t*ake
2
25
-1
em*ote
2
93
-2
@stifle
2
89
-2
`* -* to
2
89
-2
@fix
2
9
-1
acceptable
2
173
-1
door
2
89
-2
find_class
2
165
-1
@setup
2
89
-2
@mkclass
2
89
-2
@rmclass
2
89
-2
reg*ister
2
25
-1
unreg*ister
2
25
-1
is_authorized
2
173
-1
@authorize
2
25
-1
@unauthorize
2
25
-1
@restric*tions
2
89
-2
@stat*us
2
9
-1
@tut*orial
2
9
-1
title
2
173
-1
accept
2
173
-1
printb
2
13
-1
moveb
2
89
1
init_for_core
2
173
-1
lock_html unlock_html
2
173
-1
18
help_msg
door_open
authorized
restrictions
session_msg
designer
classes
current
t_text
success_msg
room_sucess_msg
empty_msg
printout_failed
writing
emote_ok
session
GMT_correction
tables
86
4
26
2
                          How To Use This Room
2
     To create new tables, desks, shelves, bulletin boards etc. use the 
2
'@addfurniture' command. You can 'look', 'sit', or 'put' things on most
2
objects you create. While sitting you are heard only by others sitting 
2
with you. If you 'stand' or 'speakup' everyone can hear you. 'Look' at
2
objects to see what's on them. 
2
GENERAL                     
2
   look blackboard        sit Big Desk             @status
2
   writeb Hi There!       stand                    put map on Bulletin
2
   eraseb 4               speakup I think that..   get map
2
   to Ken Hi there!       look clock               look Big Desk 
2
SPECIAL
2
   cleanb                   - erase entire blackboard  
2
   moveb 2 to 4             - move item 2 on blackboard to position 4 
2
   printb                   - get a printout of blackboard contents
2
   register/unregister Ken  - add/rm Ken from *current* class
2
   @addfurn/@rmfurn Desk    - add/remove a piece of describable furniture
2
   door open/closed         - restrict entry to persons in *current* class
2
   @mkclass/@rmclass Math   - add 'Math' to list of classes taught here
2
   @setup Math              - makes 'Math' the *current* class 
2
   @stifle on/off           - disable seated persons emoting to everyone
2
   @sign on/off             - show 'this.session_msg' at @who command
2
   @authorize/@unauth Ken   - add/rm Ken from list of authorized users 
2
   @restrictions on/off     - restrict Special verbs to authorized users
2
   @tutorial                - view a short tutorial on using classroom
2
   @fix                     - correct seating mixups if they occur
2
5
0
1
2
1
4
0
2
1
0
0
2
1
2
(In Session)
2
5
2
ken/cdr@COLLEGETOWN/DU/MediaMOO
2
1
4
0
2
1
0
0
2
1
4
54
2
* * * * * *   Using the GENERIC CLASSROOM : A Brief Tutorial   * * * * * * *
2
     The Generic Classroom was created to give teachers greater flexibility,
2
 creativity and control in conducting MOO classes.  Here are the basics of 
2
classroom use. You may type 'help here' at any time for a brief summary of 
2
the room commands.
2
DESIGNING THE CLASSROOM:
2
     You may create your own desks, tables, shelves, bulletin boards, walls
2
or whatever using the @addfurniture command. Let's suppose you wished to 
2
add a Lab Table to the room.  After typing '@addfurniture Lab Table' you 
2
would be asked to describe the table and indicate whether it is 'sittable'
2
or not. While most furniture is 'sittable' some things like walls and
2
bulletin boards are clearly not.  Students can 'sit Lab Table', 'put
2
<anything> on Lab Table', 'get <anything>', or 'look Lab Table'.  Objects on
2
the Lab Table will only be seen by persons sitting there or persons who look
2
at the table.  Walls, shelves, and bulletin boards are obvious places to 
2
'put' things like calendars, lab equipment, or notes. To remove furniture 
2
type '@rmfurniture <object>'. Note that each room has a built in blackboard 
2
and a clock.  Just 'look blackboard' or 'look clock'.  You can write on the 
2
blackboard by typing 'writeb hello class!'. 
2
SETTING UP A CLASS ROSTER:
2
     To set up an 'Intro to Theater' class type '@mkclass Intro to Theater'. 
2
This creates a class roster for registering your students.  If you would like 
2
Ken to be able to teach a different class in your room you must '@authorize 
2
Ken'.  This enables Ken to use the @mkclass and other commands to create his 
2
own class. You can now register your students.  Type '@setup Intro' to 
2
activate Intro to Theater as the 'current' class and then type  'register 
2
Bill', 'register Mary', and so on. Students registered in the 'current' class 
2
can  enter the classroom even when the door is closed. To allow only 
2
authorized persons to make furniture, register students or setup classes just 
2
type '@restrictions on'. You may type '@status' at any time to see which class 
2
is 'current', who is registered or authorized, and whether restrictions are in 
2
place.
2
MODERATING THE CLASS:
2
     One of the main advantages of using the Generic Classroom is to moderate 
2
extraneous talking and emoting.  Have the students 'sit Lab Table' or 
2
whereever.  While they are seated they can only be heard by others sitting 
2
with them.  This enables students to talk with each other or carry on small 
2
group discussions without disturbing others.  Students who wish to be heard 
2
throughout the room need only 'speakup <anything>', or 'stand' to be heard. 
2
Emoting can be limited to tablemates only or made visible to the entire room 
2
through the use of '@stifle on/off'.
2
It is a good idea to type '@door close' and '@session on' when you begin 
2
a class so folks not registered in the 'current' class cannot drop in 
2
uninvited. The @session command informs others that your class is '(in 
2
session)' when they do an @who listing.
2
MISC:
2
     There are numerous additional commands that can be used in the Generic 
2
Classroom. Type 'help here' for examples of their use.
2
AN INVITATION:
2
     The Generic Classroom was designed to incorporate the ideas and wishes of 
2
many MOO teachers.  It will continue to evolve as we all make new teaching 
2
discoveries. We invite *you* to participate in the development of this 
2
experimental teaching technology by bringing forward your critiques and 
2
suggestions. They are most welcome!  -ken/cdr 
2
5
2
The printer begins to hum... As it dies down you are handed a warm white sheet of paper containing the data from the %t
2
5
2
The printer begins to hum as %n prints out the data from the %t...
2
5
2
There is nothing on the board to printout...
2
5
2
Sorry, you can't seem to make a printout. Perhaps you are out of quota...
2
5
4
0
2
1
0
0
2
1
0
0
2
5
0
-1
2
5
4
3
4
5
2
Teacher's Desk
4
0
4
0
2
You see an old oaken desk.
0
1
4
5
2
Big Table
4
0
4
0
2
You see a big table in the middle of the room.
0
1
4
5
2
Bulletin Board
4
0
4
0
2
You see a bulletin board for announcements.
0
0
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
4
1
2
2
5
0
901188107
2
5
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
classroom.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
Generic Classroom
2
genclass
2
5
4
4
2
You see a rather empty classroom. There's a blackboard on the front wall and a
2
clock above it. Type 'help here' or '@tutorial' to find out more about this
2
room.
2

2
5
4
2
0
37293
0
1001876092
36
1
5
2
5
5
2
5
#100
Generic Bot

144
2
120
-1
101
5
-1
101
18
hush
2
41
-1
act*ivate
2
41
-1
addw*ord
2
41
-1
rmw*ord
2
153
5
seew*ords
2
41
-1
addp*at
2
41
-1
rmp*at
2
153
5
seep*ats
2
41
-1
mvpat
2
153
4
addr*andom
2
41
-1
rmr*andom
2
153
5
seer*andoms
2
41
-1
addquestionr*esponse
2
41
-1
rmquestionr*esponse
2
153
5
seequestionr*esponses
2
41
-1
tell
2
173
-1
context_response
2
173
-1
init_for_core
2
173
-1
10
pats
keywords
randresponses
questionresponses
guest_responses
on
wakeup_msg
designer
bogusnum
help_msg
70
4
10
4
2
2
your %(.*%)
4
3
2
my %1? Why do you wish to know?
2
I would rather not discuss my '%1' if it's allright with you...
2
Tell me yours first!
4
2
2
what is %(.*%)
4
2
2
Don't you know?
2
%1? I'm not sure i understand..
4
2
2
.* you are %(.*%)
4
2
2
Why do you think I am %1?
2
Perhaps it is you who are %1!
4
2
2
my %(%w*%)
4
3
2
Please tell me more about your %1...
2
Is your %1 important to you?
2
What about your %1?
4
2
2
why not
4
1
2
Hmm, thats a good question, why not indeed!
4
2
2
I %(.*%) you
4
3
2
You %1 me? Why?
2
Why do you say you %1 me?
2
I really %1 you too.. <grin>
4
2
2
%(%w*%) is %(.*%)
4
3
2
Suppose %1 were not %2? What then?
2
What is so %2 about %1?
2
%1? how so?
4
2
2
it's %(.*%)
4
2
2
'%1' you say? How so?
2
Why is it %1?
4
2
2
I am %(.*%)
4
3
2
You say you are %1?
2
%1? How so?
2
Why are you %1?
4
2
2
I'm %(.*%)
4
1
2
You are %1? How come?
2
5
4
10
4
3
2
hi
0
0
4
2
2
Hi, how are you?
2
Hello, how's it going?
4
3
2
bye
0
0
4
1
2
Goodbye, catch ya later
4
3
2
grins
0
0
4
1
2
What's so funny?
4
3
2
yes
0
0
4
2
2
You seem quite positive on the subject...
2
Yes? Hmm, that's interesting...
4
3
2
no
0
0
4
3
2
Why are you so negative?
2
No? why not?
2
OK.. i guess I'll have to accept 'NO' for an answer...
4
3
2
Turing
0
0
4
1
2
Ah! You speak of Turing the famous British mathematician and logician!
4
3
2
help
0
0
4
1
2
I'll try to help you.. but as a bot I really don't know a lot...
4
3
2
never
0
0
4
1
2
Never? Surely you exaggerate...
4
3
2
thanks
0
0
4
1
2
You are most welcome...
4
3
2
lag
0
0
4
2
2
Ugh.. I really hate lag..
2
Oooooo... please don't mention the L** word..
2
5
4
7
2
Do you have any hobbies?
2
I see, please continue...
2
What exactly are we talking about?
2
Can you go over that again please..
2
Um, i get the feeling this conversation is not going anywhere..
2
oh yeah?
2
hmm, is that so..
2
5
4
3
2
Hmm, not sure I know..
2
That's an interesting question...
2
Gosh, I'm not sure I can answer that...
2
5
4
4
2
So, I see you are a guest..
2
Where are you from guest?
2
First time here guest?
2
I see you are not a regular here..can I help you in any way?
2
5
0
0
2
7
2
Gee thanks for waking me up! I must have dozed off..
2
5
2
ken/cdr@collegetown
2
5
0
0
2
5
4
55
2
<B>PROGRAMMING COMMANDS</B>
2

2
1. ADDING A WORD FOR YOUR BOT TO RESPOND TO:
2
   To see what words your bot already responds to type 'seewords botname'.
2
   To teach your bot to respond to 'donut' with either 'I like donuts too.' or
2
  'Donuts are very tasty!' just type 'addword botname' and enter the keyword
2
  'donut'.  Then enter the appropriate responses a line at a time.  End with
2
   a single period on a line by itself.
2

2
2. ADDING A PATTERN FOR YOUR BOT TO RESPOND TO:
2
   Suppose you wished your bot to hear something like
2
       MY DONUT ISN'T VERY TASTY
2
   and respond with 
2
       WHAT'S SO GREAT ABOUT A TASTY DONUT?
2
   To do this you must teach your bot to respond to the pattern 
2
       MY a ISN'T VERY b.
2
   To understand what patterns look like, type 'seepat botname' and study the
2
   examples. For additional assistance on understanding the syntax of patterns
2
   type 'help regular'.  When you think you are ready to add a pattern type 
2
   '@addpat botname'  and enter the following line when asked to do so: 
2
       my %(%w*%) isn't very %(.*%)
2
   Then type in the response form:
2
       What's so great about a %2 %1?
2
   Add as many response forms as you wish on separate lines.  End with a 
2
   period on a single line.
2

2
3. ADDING RANDOM RESPONSES:
2
   These responses are triggered whenever your bot can't find a keyword, a 
2
   pattern, or a question. To see the responses already programmed type: 
2
   'seeresponses botname'. To add a new random response type: 'addrandom
2
   botname' and enter a new response.
2

2
4. ADDING A RANDOM RESPONSE TO A QUESTION
2
   When your bot senses a question is being asked it responds with a random
2
   'answer'.  To see the random question responses already programmed into
2
   your bot type 'seequestionresponses botname'.  To add a new response
2
   type  'addquestionresponse botname'.
2

2
5. To remove words, patterns, random responses or answers use the appropriate 
2
   'rm-' command.  For example, to remove pattern number 5 on your bot just
2
    type 'rmpat 5 from bot'.
2

2
6. Your bot responds to patterns in the order in which it matches them. If you
2
   wish to move pattern number 7 to a position nearer the beginning of the
2
   list you might type something like 'mvpat 7 on botname'. You will then
2
   be asked to enter a line number to move pattern to.
2

2
7. CAUTION:  This bot was designed for serious educational and experimental
2
   purposes.  It makes an excellent 'guide' or 'tutor' and is an interesting
2
   vehicle for the study of the limits of language understanding using an
2
   'Eliza' approach.  BUT.. it has great spam potential since it responds to
2
   nearly everything it hears.  'Hush botname' when not needed and please be
2
   considerate of others around you ..
2

2
PLEASE FORWARD COMMENTS AND BUGS TO KEN SCHWELLER,  KEN/CDR @COLLEGETOWN
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
bot.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
0
1
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
generic bot
2
5
4
9
2
You see a Turing Robot designed to interact in vigorous 'Eliza-like'
2
conversation with other folks in its vicinity. It has key words, sentence
2
patterns, random responses, and question responses - all user programmable.
2
It has some ability to recognize where it is and to whom it is talking. 
2
Type @exam botname to see all the available commands. For detailed assistance
2
in programming this bot type 'help botname'. For a discussion of the issues 
2
involved in testing machine intelligence see Turing's paper 'Computing
2
Machinery and Intelligence'.  To start up the bot, drop it, activate it, 
2
and say 'hi'. Please report bugs to ken/cdr@CollegeTown.
2
5
4
2
0
21996
0
1001876092
36
1
5
2
5
5
2
5
#101
Generic Slide Projector

144
2
120
-1
102
5
-1
104
3
show
2
153
4
rev*iew
2
153
4
init_for_core
2
173
-1
10
s1
s2
s3
s4
s5
s6
s7
s8
s9
s10
70
4
15
2
                                   
2
 
2
                          A Brief Introduction
2
 
2
                                    to
2
 
2
                              Creating Slides
2
                                     
2
                                    for 
2

2
              
2
                             MOO Presentations
2
 
2
             (Type  'show 2 on <projector name>' to continue:) 
2
  
2
7
4
8
2
          It is easy to create new slides! To create slide #1, for 
2
          example, just '@notedit <projector name>.s1'.  You can
2
          create up to 10 slides (.s1 through .s10).  Your slide
2
          can display up to 22 lines of text.
2
           
2
 
2

2
              (type  'show 3 on <projector name>' to continue.)
2
7
4
15
2
          Another way to create a set of slides is to build an external
2
     file and copy it into the MOO.  If you choose this option you need
2
     only preface each section of text with the following commands:
2

2
     @set <projectorname>.s1 to {}       (necessary to erase old slide)
2
     @notedit <projectorname>.s1
2
     enter
2
          < text >
2
          < text >
2
     .
2
     save
2
     q
2

2
     The slide set is then created automatically when you log in and 'paste'
2
     the file.     (Type  'show 4 on <projector name>' to continue:) 
2
7
4
16
2
             Here is what a sample slide (slide #2) might look like 
2
             in a text file: Note the prefix and suffix code lines.
2

2
 @set myslide.s2 to {} 
2
 @notedit myslide.s2 
2
 enter                           
2
                      Here is a picture of Ken!
2
                               ________
2
                              /  O O  \\
2
                              \\  U    /   
2
                                -------   
2
 .                     
2
 save
2
 q
2
     
2
              (Type  'show 5 on <projector name>' to continue:) 
2
7
4
7
2
                    Perhaps the easiest way to make a tray of slides
2
               is to write all the text lines first for each slide 
2
               and then go back and add to each slide the proper
2
               prefix and suffix lines.  You can have up to 10 slides.
2
               Each slide may have up to 22 lines of text. 
2
         
2
               (Type  'show 6 on <projector name>' to continue:)
2
7
4
9
2
                    When you have finished your text file with all the 
2
               slides you want just cut it and paste it into your MOO.
2
               The slides will be automatically put in your slide 
2
               projector and will be ready for use!  You can edit the
2
               slides or renumber them at any time by simply reediting
2
               the file and repasting it.
2
       
2
               If you have any questions please contact Ken/cdr for
2
               assistance.  Good luck!!    -Ken
2
7
4
20
2
              __  _-==-=_,-.
2
             /--`' \\_@-@.--<
2
     Tiggers `--'\\ \\   <___/.
2
     can do       \\ \\\\   \" /
2
     ANYTHING!     >=\\\\_/`<
2
       ____       /= |  \\_|/
2
     _'    `\\   _/=== \\___/
2
          `___/ //\\./=/~\\====\\
2
         \\   // /   | ===:
2
         |  ._/_,__|_ ==:        __
2
          \\/    \\\\ \\\\`--|       / \\\\
2
           |    _     \\\\:      /==:-\\
2
           `.__' `-____/       |--|==:
2
             \\    \\ ===\\      :==:`-'
2
              _>    \\ ===\\    /==/
2
             /==\\   |  ===\\__/--/
2
             <=== \\  /  ====\\ \\\\/
2
             _`--  \\/  === \\/--'
2
            |       \\ ==== |
2
             -`------/`--' /
2
7
4
0
2
7
4
0
2
7
4
0
2
7
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
projector.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
0
1
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
generic slide projector
2
genslide
2
5
4
6
2
Congratulations on your new slide projector! Suppose your new projector
2
is named MyProjector.  The following commands are available:
2
  'show 3 on MyProjector'    - shows slide 3 to everyone in room
2
  'review 5 on MyProjector'  - shows you slide 5 privately
2
  'show 1 on MyProjector'    - starts you on a slide creating tutorial
2
If you have any problems feel free to contact cdr/ken. Good Luck!
2
5
4
2
0
8171
0
1001876092
36
1
5
2
5
5
2
5
#102
Generic Video Camera

144
2
120
-1
103
8
-1
103
14
look_self
2
173
-1
start
2
41
-1
stop
2
41
-1
tell
2
173
-1
p*ut in*sert d*rop
2
153
3
re*move ta*ke g*et
2
153
5
pan
2
41
-1
time*stamp
2
41
-1
see*manual
2
41
-1
frame
2
153
0
unframe
2
41
-1
zoom
2
105
4
ss
2
105
0
init_for_core
2
173
-1
6
on
actions
tapeloaded
tapenum
designers
framees
85
0
0
2
7
4
0
2
7
0
0
2
7
0
0
2
7
2
cdr@mediaMOO
2
5
4
0
2
7
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
Generic Video Camera
2
vidcam
2
5
2
Congratulations on owning a brand new Video Camera! To learn how to operate your new camera just 'seemanual <camera name>'.
2
5
4
2
0
10383
0
1001876092
36
1
5
2
5
5
2
5
#103
Generic VCR

144
2
120
-1
104
8
-1
115
11
look_self
2
173
-1
play
2
41
-1
FF
2
41
-1
stop
2
41
-1
rewind
2
41
-1
p*ut in*sert d*rop
2
153
3
re*move ta*ke g*et
2
153
5
hookup
2
105
1
unhook
2
41
-1
manual
2
41
-1
init_for_core
2
173
-1
8
tapeloaded
on
tapenum
hookedup
speed
ffon
designers
gentv
87
0
0
2
7
0
0
2
7
0
0
2
7
0
0
2
7
0
15
2
7
0
0
2
7
2
cdr@mediaMOO
2
5
1
104
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
Generic VCR
2
genvcr
2
5
2
Congratulations on owning a high tech VCR! To operate your new vcr hook it up to a tv and load in a tape! Type 'manual vcr' for complete operating instructions. 
2
5
4
2
0
9005
0
1001876092
36
1
5
2
5
5
2
5
#104
Generic TV

144
2
120
-1
105
5
-1
105
11
turnon
2
41
-1
turnoff
2
41
-1
tune
2
105
1
look_self
2
173
-1
sch*edule
2
41
-1
play
2
153
4
pause
2
41
-1
res*ume
2
41
-1
autopause
2
41
-1
dumptape
2
41
-1
init_for_core
2
173
-1
8
on
channel
index
pause
tapenum
designers
library
studio
68
0
1
2
7
0
11
2
7
0
9
2
7
0
0
2
7
0
0
2
7
2
cdr@mediaMOO
2
5
1
106
2
5
1
-1
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
Generic TV
2
gentv
2
5
2
   Congratulations on your new TV!  Type  'schedule <tvname>' to see  the tapes available for viewing.  You can also hook a VCR up to your new tv and view cdr/format tapes. Enjoy!
2
5
4
2
0
8636
0
1001876092
36
1
5
2
5
5
2
5
#105
Generic Video Tape

144
2
120
-1
106
5
107
106
7
erase
2
41
-1
label
2
105
0
look_self
2
173
-1
protect
2
41
-1
unprotect
2
41
-1
setlength
2
105
1
init_for_core
2
173
-1
9
actions
used
labl
designers
loc
index
protected
len
cdrid
69
4
1
4
2
2
pause
0
0
2
7
0
0
2
7
2
BLANK
2
7
2
cdr@mediaMOO
2
5
0
0
2
7
0
1
2
7
0
0
2
7
0
100
2
7
1
2
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
Generic Video Tape
2
vidtape
2
5
2
You see a video tape.  It works in cameras and vcrs designed by cdr.
2
5
4
2
0
5010
0
1001876092
36
1
5
2
5
5
2
5
#106
Tape Library

16
2
120
-1
107
5
-1
109
5
look_self
2
173
-1
add
2
153
1
remove
2
153
5
moveto
2
173
-1
init_for_core
2
173
-1
2
tapes
gentape
62
4
1
1
107
2
5
1
105
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
Tape Library
2
library
2
5
2
You see  a listing of all the tapes presently in the Tape Library.  These tapes can be viewed at anytime on any TV set. Consult your TV manual for playing instructions.
2
5
4
2
0
4033
0
1001876092
36
1
5
2
5
5
2
5
#107
Ken's MOO Programming Tutorial

16
2
120
-1
108
105
-1
-1
1
init_for_core
2
173
-1
0
69
4
54
4
2
2
pause
0
0
4
2
2
* * * * * * * * * *
0
0
4
2
2
       Presenting
0
0
4
2
2
Coding with Ken!
0
0
4
2
2
A MOO Programming Tutorial
0
0
4
2
2
for Absolute Beginners
0
0
4
2
2
Part 1
0
0
4
2
2
* * * * * * * * * *
0
15
4
2
2
Ken says, "Hi!  I'm Ken. I'm here to help you learn to program in MOO!  This tutorial is for Absolute Beginners,  no previous programming experience in any language is assumed!"
0
15
4
2
2
Ken says, "MOO, despite all the Bovine jokes you may have heard or made yourself, actually stands for 'Multi-user Object Oriented' programming language..."
0
15
4
2
2
Ken says, "MOO is kind of a cross between C++ and LISP, but much easier to learn.  When you learn to program in  MOO you can build all kinds of fun things!"
0
15
4
2
2
Ken says, "This is an INTERACTIVE tutorial! You will actually be coding a program while you watch the tape!"
0
15
4
2
2
Ken says, "Here's how it works. I will suggest a line or two of code for you to write.  I will then ask you to pause the tape and actually enter the code before resuming your viewing. "
0
15
4
2
2
Ken says, "To pause or resume viewing this tape at any time type   PAUSE TVNAME  or  RESUME TVNAME.   Why not practice doing it now.. "
0
15
4
2
2
Ken says, "Were you able to pause and resume without problems? Great! Let's get started."
0
15
4
2
2
Ken says, "Oh, I just remembered. . Many people prefer to have the tv pause automatically after each line.  If you would like to try this type 'autopause tvname'.  Type resume to continue each time..."
0
15
4
2
2
Ken says, "For our first programmed object let's make a Box of Donuts!  It's real easy. Just type  '@create $thing named Box of  Donuts,donuts'.  Pause the tape and make that box right now! I'll wait for you..."
0
15
4
2
2
Ken says, "Did you do it? Type 'inv' to look at your inventory to make sure you've got it.  You should see it listed there. "
0
15
4
2
2
Ken says, "In the language of object oriented programming you just made a 'child' of the generic object $thing. The word 'donuts' is the alias or nickname for your object."
0
15
4
2
2
Ken says, "Lets make sure that nickname 'donuts' really works. Type 'drop donuts'."
0
15
4
2
2
Ken says, "Now 'look donuts'. "
0
15
4
2
2
Ken says, " Hee, hee....Not a very appetizing description  so far is it.."
0
15
4
2
2
Ken says, "Lets change that boring description! Type @describe donuts as "You see a box of yummy scrumptious donuts begging to be eaten".  Pause the tape and do it.  I'll wait..."
0
15
4
2
2
Ken says, "The next thing we'll do is create several kinds of donuts for inside the box. Type a line like the one that follows adding as many donut descriptions as you wish."
0
15
4
2
2
@property donuts.kinds {"yummy cherry donut","tasty mooberry croissant","creamy custard donut"}
0
15
4
2
2
Ken says, "What we just did was add a property called 'kinds' to the object 'box of donuts'.  This property is a list of 3 donut descriptions."
0
15
4
2
2
Ken says, "Now let's see if you were successful.  Type  '@dump donuts'    Do you see a property called 'kinds' with the correct donut descriptions?  If you don't, try adding the property again..."
0
15
4
2
2
Ken says, "Now that we have added the property, let's practice talking like real MOO programmers and describe what we did"
0
15
4
2
2
Ken says, "Repeat after me: 'We added the PROPERTY 'kinds' to the OBJECT 'donuts' and INITIALIZED the  property's VALUE to a LIST of  CHARACTER STRINGS describing various donuts'    Hey!  Are we cool or what!"
0
15
4
2
2
Ken says, "Now let's see if we can add an EAT verb to our donuts object so we can gobble up some of those donuts!   Our EAT verb  might be called like this 'eat donuts'.  "
0
15
4
2
2
Ken says, "To create the verb type '@verb donuts:eat this'       Pause and do it now."
0
15
4
2
2
Ken says, "Now lets make the verb DO something!  When a player eats a donut let's have the player choose a donut kind at random and gobble it up."
0
15
4
2
2
Ken says, "I will show you the complete program but don't type it in just yet...  Here it is:"
0
15
4
2
2
donut_choice = this.kinds[random(3)];
0
15
4
2
2
player.location:announce_all(player.name," gobbles up a ",donut_choice,".");
0
15
4
2
2
Ken says, "Let's talk about what's happening here before we try to enter the code..."
0
15
4
2
2
Ken says, " In line 1 random(3) generates a random number from 1 to 3. ( If you had 5 donuts in your box you would make this random(5).)  Let's suppose the function came up with the number 2.."
0
15
4
2
2
Ken says, "The variable 'donut_choice would then be set to the second item in the list 'this.kinds', that is, the value of 'donut_choice' would now be "tasty mooberry croissant""
0
15
4
2
2
Ken says, "When a player eats a randomly selected donut everyone in the room should be informed of this event.  This is the job of the second line."
0
15
4
2
2
Ken says, "In line 2 'player.location'   is the location of the player who is eating the donut, usually some room."
0
15
4
2
2
Ken says, "The verb 'announce_all'  is a verb defined for all rooms and it means 'tell everybody in the room' something or other.. "
0
15
4
2
2
Ken says, "If Jill had typed 'eat donut' all players in the room (including Jill) would see 'Jill gobbles up  a tasty mooberry croissant.'"
0
15
4
2
2
Ken says, "Now lets program that verb!  You will need to type in the three lines I will show you followed by a period on a line by itself."
0
15
4
2
2
Ken says, "The first line actually creates a new verb named EAT and assigns it to the object 'donuts'.  You will be prompted for the remaining two lines. Don't forget to end with a period on a separate line.."
0
15
4
2
2
Ken says, "Pause the tape after reviewing the following line and try entering them."
0
15
4
2
2
@program donuts:eat
0
15
4
2
2
donut_choice = this.kinds[random(3)];
0
15
4
2
2
player.location:announce_all(player.name," gobbles up a ",donut_choice,".");
0
15
4
2
2
Ken says, "If you were successful you will get my FAVORITE message '0 errors, verb programmed'. Congratulations! "
0
15
4
2
2
Ken says, "If you were not so lucky you got some cryptic message about a parse error or whatnot. Just try again.."
0
15
4
2
2
Ken says, " Did you remember the semicolons at the ends of the lines? Did you get the full colon in the right place? Pause the tape if you need to try again. I'll wait..."
0
15
4
2
2
Ken says, "Now let's try out that verb. Type 'eat donuts' 3 or four times.  Yum Yum!  Congratulations! You are on your way to becoming a true blue MOO programmer!"
0
15
4
2
2
Ken says, "Next time we will introduce some more fun features of the MOO language and we'll learn how to use the editor! Until then, HAPPY MOOING!"
0
15
4
2
2
Ken says, "Bye bye!"
0
15
2
7
0
1
2
7
2
Coding with Ken #1
2
7
5
2
5
5
2
7
5
2
7
5
2
7
5
2
7
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
TutorialTape1
2
5
2
You see a video tape.  It works in cameras and vcrs designed by cdr.
2
5
4
2
0
9586
0
1001876092
36
1
5
2
5
5
2
5
#108
Generic Recitable Note

144
2
120
-1
109
9
-1
134
5
recite
2
41
-1
@setdelay
2
105
1
pause inter*rupt
2
45
-1
stop
2
41
-1
init_for_core
2
173
-1
3
delay
recital
index
69
0
5
2
5
0
0
2
5
0
1
2
5
5
2
5
5
2
4
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
0
1
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
Generic Recitable Note
2
note
2
5
2
There appears to be some writing on the note ...
2
5
4
2
0
4273
0
1001876092
36
1
5
2
5
5
2
5
#109
Generic Lecture

144
2
120
-1
111
5
-1
112
9
setpause
2
105
1
resume
2
45
-1
pause
2
41
-1
deliver
2
41
-1
r*ead
2
41
-1
erase
2
41
-1
g*ive
2
153
4
init_for_core
2
173
-1
xpress_edit
2
173
-1
6
text
index
pause
delay
designer
help_msg
66
4
0
2
7
0
1
2
1
0
0
2
1
0
5
2
5
2
cdr/ken@mediamoo/DU
2
5
4
18
2

2
                HOW TO PREPARE AND GIVE A MOO LECTURE
2

2
1)  Create your LECTURE using the Note Editor.
2
    Type '@notedit <lecture name>.text'  When through editing type 'save' 
2
    and 'q' to quit.
2
2)  Edit your lecture with the same command:  @notedit <lecture name>.text
2
3)  To deliver lecture automatically type 'deliver lecture'. You may then
2
    'pause lecture', or 'resume lecture'
2
4)  Length of pauses during lecture can be set with 'setpause lecture to 5'.
2
5)  To deliver lines one at a time 'give <line number|next> on <lecture>'
2
    You may deliver your lines in any order and at any pace you wish. 
2
6)  To peek at your complete lecture at any time type 'read <lecture>'. 
2
    No one else will see you read it.
2
7)  @exam <lecture> for additional useful verbs.
2
8)  Anyone holding your lecture can edit it.  This is useful for
2
    collaborative writing.
2
                            GOOD LUCK!
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
lecture.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
0
1
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
Generic Lecture
2
genlecture
2
5
2
Ever want to deliver a lecture or speech to a MOO audience and find yourself slowed down and frustrated by the need for rapid error free typing? No more! Now you can prepare your remarks IN ADVANCE and deliver them in any order and at any pace you desire.  Type 'help <lecture name>' for a full instruction manual on HOW TO GIVE MOO LECTURES!
2
5
4
2
0
7467
0
1001876092
36
1
5
2
5
5
2
5
#110
Login Watcher

0
2
84
-1
114
74
-1
114
21
hello
2
173
-1
goodbye
2
173
-1
add_user
2
173
-1
@login
2
93
-2
@interesting
2
93
-2
@uninteresting
2
93
-2
name_and_number_list
2
173
-1
@watch @watchidle
2
29
-1
interesting
2
173
-1
int
2
173
-1
@wwho
2
13
-1
gc_intlist
2
173
-1
add_relaying_task
2
173
-1
delete_relaying_task
2
173
-1
set_*
2
173
-1
notify tell
2
173
-1
find_relaying_target
2
173
-1
hello2
2
173
-1
announce3
2
173
-1
announce2
2
173
-1
init_for_core
2
173
-1
8
listening
users
interesting
options
non_player_listeners
relaying_task
relaying_target
relaying_tasks
72
4
0
2
5
4
0
2
5
4
0
2
5
4
0
2
5
4
0
2
5
0
0
2
5
1
-1
2
5
4
0
2
5
5
36
1
5
2
5
4
0
36
1
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Login Watching Feature
2
5
2
The Login Watching Feature will inform you when people log on or off the MOO
2
5
4
2
0
18800
0
1001876092
36
1
5
2
5
5
2
5
#111
Generic Web Page

144
2
120
-1
113
3
-1
121
3
description
2
173
-1
init_for_core
2
173
-1
_html
2
165
-1
0
68
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
4
1
2
2
5
0
901188107
2
5
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
webpage.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Generic Web Page
2
5
4
15
2
<HTML>
2
<HEAD>
2
   <TITLE>Generic CypherTEXT Web Page</TITLE>
2
   <!-- Jan Rune Holmevik. April 15. 1999 -->
2
</HEAD>
2
<BODY BGCOLOR="white" TEXT="black"
2
LINK="blue" VLINK="purple" ALINK="red">
2
</HEAD>
2
<CENTER>
2
<H2>Welcome to the CypherTEXT Web Page</H2>
2
</CENTER>
2
<P> 
2
This is a special webpage-room that gives you greater freedom to create your cyphertexts the way you want them. Whereas other MOO objects are marked up in HTML by the MOO, the webpage affords you the freedom to design your room any way you want. Simply type in HTML code in the description of this room.
2
<P>
2
Any objects in your webpage-room, or exits from it will be added automatically to the bottom of the page.
2
5
4
2
0
4709
0
1001876092
36
1
5
2
5
5
2
5
#112
MacMOOSE Utils

0
2
-1
-1
-1
5
-1
113
39
code_info
2
165
-1
new_session
2
173
-1
verb_status
2
165
-1
list_code
2
165
-1
help
2
85
-2
prefix_notify_lines
2
173
-1
declare_code
2
165
-1
to_next_tag
36
173
-1
set_code
2
165
-1
to_next_tag_str
36
173
-1
prop_object
2
165
-1
prop_info
2
165
-1
prop_eval
2
165
-1
prop_resolve_internal
2
173
-1
prop_resolve
2
173
-1
find_propref_object
36
173
-1
trim_prop
36
173
-1
parse_propref
36
173
-1
resolve_compound_propref
36
173
-1
declare_prop
2
165
-1
prop_to_parent
2
173
-1
tokenize
36
173
-1
coerce_list_string
36
173
-1
coerce
2
165
-1
set_prop
2
165
-1
object_info
2
165
-1
findable_properties
2
173
-1
scrunch_list
36
173
-1
verbs
2
165
-1
object_parents
2
165
-1
verify_mail_recipients
2
165
-1
send_mail
2
165
-1
verb_info_and_args
2
165
-1
scrunch_list_and_reverse
36
173
-1
list_from_tokens
36
173
-1
reassemble_line
36
173
-1
print_no_quotes
36
173
-1
simple_tokenize
36
173
-1
simple_verb_name
36
173
-1
9
default_prefix
moosep
version
default_verb_dobj
default_verb_iobj
default_verb_prep
filter_underscore_props_default
filter_underscore_verbs_default
error_codes
69
2
_&_
36
1
0
0
36
1
2
1.0b1MOO
36
1
2
none
36
1
2
none
36
1
2
none
36
1
0
1
36
1
0
1
36
1
4
14
2
E_NONE
2
E_TYPE
2
E_DIV
2
E_PERM
2
E_PROPNF
2
E_VERBNF
2
E_INVIND
2
E_RECMOVE
2
E_MAXREC
2
E_RANGE
2
E_ARGS
2
E_NACC
2
E_INVARG
2
E_QUOTA
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
MacMOOSE Utils
2
5
2
MacMOOSE_utils by Amy Bruckman, asb@media.mit.edu.  This code lets you use the advanced editing features of the MacMOOSE client program.  This version of $MacMOOSE_utils supports only MOOs, not MOOSEs.  MacMOOSE is a trademark of the MIT Media Lab.  Copyright 1996-7, MIT Media Lab, all rights reserved.  This code is distributed free for non-commercial use only.  It is provided as-is; use at your own risk.  There is no warranty express or implied as to its suitability for any purpose.  The most recent version is available at http://asb.www.media.mit.edu/people/asb/MacMOOSE/MacMOOSE_utils.html
2
5
4
2
0
59297
0
1001876092
36
1
5
2
5
5
2
5
#113
Generic Web Projector

144
2
120
-1
115
5
-1
131
8
peek
2
41
-1
dis*play
2
157
4
view
2
153
4
init_for_core
2
173
-1
xpress_edit
2
173
-1
start stop reset
2
41
-1
slideshow
2
173
-1
@setdelay
2
105
1
7
slides
index
designer
help_msg
display_in_xpress
on
delay
67
4
3
2
http://lingua.utdallas.edu/encore/
2
http://lingua.utdallas.edu:7000/
2
http://www.bvu.edu/faculty/schweller
2
7
0
1
2
1
2
cdr/ken@mediamoo/DU
2
5
4
19
2
HOW TO USE YOUR WEB PROJECTOR
2

2
- To create your WEB SLIDES click on the Xpress Objects button and select Edit Web Slides from the drop down menu.
2

2
- Type in URLs for web pages you wish to share, one URL per line. Save your slides.
2

2
- To show slide 1 to everyone in room type 'display 1 on projector_name' or 'display next on projector_name'
2

2
- To view a slide by yourself 'view 1 on projector_name'
2

2
- To peek at your slides list at any time type 'peek projector_name'
2

2
- You can run a slide show of all web pages on the projector by typing 'start projector_name'
2

2
- You can stop the slide show at any time with the command 'stop projector_name.' To resume slide show, type 'start projector_name'.
2

2
- To reset the projector to slide 1, type 'reset projector_name'
2

2
- To change the delay between slides in the slide show, type '@setdelay projector_name to X', where X is the number of seconds you want the delay to be.
2
5
0
1
2
5
0
0
2
5
0
20
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
webprojector.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
0
1
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Generic Web Projector
2
5
2
Congratulations on your new Web Projector. Using this object you can display fully graphical World Wide Web pages to anyone else in the room using enCore Xpress or Surf and Turf. For details: 'view 1 on projectorname' Type: 'help projectorname' for additional assistance.
2
5
4
2
0
9082
0
1001876092
36
1
5
2
5
5
2
5
#114
Social Verbs Feature

0
2
84
-1
-1
74
-1
119
19
_social _comfort _wink _yawn _wave _cackle _giggle _cry _poke _shrug _blush _cringe _smirk _nod _smile _laugh _sigh _chuckle _hug _kiss _bow _vnod _grin _brb _clap _cheer _pine _froll
2
173
-1
social comfort wink yawn wave cackle giggle cry poke shrug blush cringe smirk nod smile laugh sigh chuckle hug kiss bow vnod brb grin clap cheer pine froll
2
29
-1
discern_everything
2
173
-1
everything_ok?
2
173
-1
_unsocial _bounce _cuddle _curtsey _hkiss _highf _ruffle _shiver _snuggle _tackle _tickle _wiggle _cgrin _salute _boot _claugh _purr _brow _booga _xgrin _manner _dict _wait _tap _pout _gasp _thwap _groan _mgroan _moan _amoan
2
173
-1
feelings
2
5
-1
unsocial bounce cuddle curtsey hkiss highf ruffle shiver snuggle tackle tickle wiggle cgrin salute boot claugh purr brow booga xgrin wait tap pout gasp thwap groan mgroan moan amoan
2
29
-1
useful note pnote hmm manner dictionary list phone visitor sync afk ok lag
2
29
-1
_useful _note _pnote _hmm _manner _dictionary _list _phone _visitor _sync _afk _ok _lag
2
173
-1
hold wundie rose
2
29
-1
_hold _wundie _rose
2
173
-1
super_sub
2
173
-1
init_for_core
2
173
-1
lei-c
2
89
-2
lei-g
2
93
-2
lei-a
2
93
-2
lei-t
2
93
-2
lei-l
2
93
-2
lei
2
93
-2
72
custom
bounce
cuddle
curtsey
snuggle
claugh
pout
tackle
purr
gasp
tickle
brow
thwap
hkiss
wiggle
booga
groan
highf
cgrin
xgrin
mgroan
ruffle
salute
wait
moan
shiver
boot
tap
amoan
note
useful
hmm
manner
dictionary
list
pnote
cheer
poke
vnod
cry
brb
clap
comfort
cringe
wink
yawn
wave
cackle
giggle
shrug
blush
smirk
nod
smile
laugh
sigh
chuckle
hug
kiss
bow
grin
pine
froll
hold
visitor
afk
sync
phone
ok
wundie
rose
lag
136
4
0
2
5
4
7
2
**B O I N G**
2
%n bounces up and down.
2
You bounce into %d.
2
%n smiles and bounces into you!
2
%n bounces into %d. %S smiles to %r.
2
**B O I N G**
2
%n bounces up and down, doubling back on %r.
2
1
4
7
2
You hold yourself.
2
%n holds %r.
2
You cuddle with %d. Awwww!
2
%n wraps %p arms around you and cuddles with you.
2
%n cuddles with %d. Awwww!
2
You hold yourself.
2
%n holds %r.
2
1
4
7
2
You curtsey.
2
%n curtseys nicely.
2
You curtsey to %d.
2
%n curtseys to you.
2
%n curtseys politely to %d.
2
You curtsey.
2
%n curtseys, somewhat awkwardly.
2
1
4
7
2
You hold yourself.
2
%n holds %r.
2
You snuggle with %d. Awwww!
2
%n wraps %p arms around you and snuggles with you.
2
%n snuggles with %d. Awwww!
2
You hold yourself.
2
%n holds %r.
2
1
4
7
2
You laugh with contempt at the masses surrounding you.
2
%n laughs contemptuously at %p surroundings.
2
You laugh with comtempt at puny %d.
2
%n laughs at you in contempt.
2
%n laughs with contempt at %d.
2
You laugh harshly at yourself.
2
%n laughs at %r in contempt.
2
1
4
7
2
You pout.
2
%n pouts.
2
You pout at %d.
2
%n pouts at you.
2
%n pouts at %d.
2
You pout at yourself.
2
%n pouts at %r.
2
1
4
7
2
You look around for someone to tackle.
2
%n cries out "Hut!" and then looks embarrassed.
2
You tackle %d to the ground.
2
%n tackled you to the ground!
2
%n tackles %d to the ground.
2
You fall down.
2
%n falls flat on %p face.
2
1
4
7
2
MWRRRRRRRRRR...
2
%n purrs contentedly.
2
You grin at %d and purr.
2
%n grins at you and purrs contentedly.
2
%n grins at %d and purrs contentedly.
2
MWRRRRRRRRRR...
2
%n smiles a secret smile and purrs contentedly.
2
1
4
7
2
You gasp in astonishment!
2
%n gasps in astonishment!
2
You gasp in astonishment at %d!
2
%n gasps in astonishment at you!
2
%n gasps in astonishment at %d!
2
You gasp in astonishment at yourself!
2
%n gasps in astonishment at %r!
2
1
4
7
2
You wiggle your fingers in the air.
2
%n wiggles %p fingers in the air.
2
You tickle %d, giving no quarter.
2
%n tickles you relentlessly.
2
%n tickles %d for several minutes.
2
You tickle yourself, or try to.
2
%n runs %p fingers all over %p body, wiggling %p fingers and smiling.
2
1
4
0
2
1
4
7
2
You make banging motions with a rolled-up newspaper.
2
%n makes banging motions with a rolled-up newspaper.
2
You thwap %d with a rolled-up newspaper.  "Bad! Bad! Bad!!!"
2
%n thwaps you with a rolled-up newspaper.  "Bad! Bad! Bad!!!"
2
%n thwaps %d with a rolled-up newspaper.  "Bad! Bad! Bad!!!"
2
You thwap yourself.
2
%n thwaps %r.
2
1
4
7
2
You look absently about for someone to kiss the hand of.
2
%n looks absently about.
2
You kiss %d's hand.
2
%n kisses your hand sweetly.
2
%n kisses %d's hand. It's so cute!
2
You kiss your own hand.
2
%n kisses %p own hand, for no discernable reason.
2
1
4
7
2
You wiggle your bottom.
2
%n wiggles %p bottom.
2
You wiggle your bottom at %d.
2
%n wiggles %p bottom at you!
2
%n naughtily wiggles %p bottom at %d.
2
You wiggle your bottom.
2
%n wiggles %p bottom awkwardly.
2
1
4
7
2
You look for someone to *HUG*.
2
%n looks *PUZZLED*.
2
You *HUG* %d!
2
%n *HUGS* you!
2
%n *HUGS* %d!
2
You *HUG* yourself!
2
%n smiles and *HUGS* %r!
2
1
4
7
2
You groan.
2
%n groans.
2
You groan at %d.
2
%n groans at you.
2
%n groans at %d.
2
You groan at yourself.
2
%n groans at %r.
2
1
4
7
2
You look about for someone to high five.
2
%n looks kinda funky but somewhat confused.
2
You slap %d some skin!
2
%n slaps you some skin in a high 5.
2
%n grins and slaps %d some skin in a high five.
2
You slap your hands together. Wheeee!
2
%n claps %p hands together above %p head.
2
1
4
7
2
You grin and become immaterial for a moment, leaving only the smile.
2
%n grins evilly and begins to fade away, leaving only the grin.
2
You grin at %d and become immaterial for a moment.
2
%n grins at you and then fades away, leaving only %p smiling teeth.
2
%n grins evilly at %d and then fades away, leaving only the smile.
2
You fade entirely away.
2
%n fades away.
2
1
4
7
2
You grin. Twice.
2
%n grins. Twice.
2
You grin at %d. Twice.
2
%n grins at you. Then %s grins at you again, for no discernable reason.
2
%n grins at %d. Then %s grins at %[dpo] again, for no discernable reason.
2
You grin at yourself.
2
%n grins to %r, twice. You are suspicious.
2
1
4
7
2
You groan miserably.
2
%n groans miserably.
2
You groan miserably at %d.
2
%n groans miserably at you.
2
%n groans miserably at %d.
2
You groan miserably at yourself.
2
%n groans miserably at %r.
2
1
4
7
2
You wave your arms around, looking for something to ruffle.
2
%n looks confused and waves %p arms around.
2
You ruffle %d's hair.
2
%n ruffles your hair playfully.
2
%n ruffles %d's hair.
2
You mess up your own hair.
2
%n messes up %p own hair.
2
1
4
7
2
You salute smartly.
2
%n salutes smartly.
2
You salute %d as a fellow being of the MOO.
2
%n salutes you in the manner of the Courts of this MOO.
2
%n salutes %d smartly.
2
You salute yourself.
2
%n salutes in an awkward manner.
2
1
4
7
2
You wait impatiently.
2
%n waits impatiently.
2
You wait impatiently for %d.
2
%n waits impatiently for you.
2
%n waits impatiently for %d.
2
You wait for something, but you're not sure what.
2
%n waits for something, but %s isn't sure what.
2
1
4
7
2
You moan.
2
%n moans.
2
You moan at %d.
2
%n moans at you.
2
%n moans at %d.
2
You moan at yourself.
2
%n moans at %r.
2
1
4
7
2
You shiver with delight.
2
%n shivers with delight.
2
You smile at %d and shiver with delight.
2
%n smiles at you and shivers with delight.
2
%n smiles at %d sweetly and shivers with delight.
2
You shiver with delight.
2
%n smiles a secret smile and shivers with delight.
2
1
4
7
2
You kick into the air.
2
%n practices kicking.
2
You boot %d in the head.
2
%n booted you in the head!
2
%n boots %d in the head!
2
You kick yourself.
2
%n kicks %r.
2
1
4
7
2
You tap your foot impatiently.
2
%n taps %p foot impatiently.
2
You tap your foot impatiently at %d.
2
%n taps %p foot at you impatiently.
2
%n taps %p foot at %d impatiently.
2
You tap your foot impatiently at yourself.
2
%n taps %p foot impatiently at %r.
2
1
4
7
2
You moan in agony.
2
%n moans in agony.
2
You moan agonizingly at %d.
2
%n moans agonizingly at you.
2
%n moans agonizingly at %d.
2
You moan in agony at your puny self.
2
%n moans in agony at %p puny self.
2
1
4
7
2
You look around for something to make a mental note of.
2
%n looks around for something to make a mental note of.
2
You make a mental note of what %d said.
2
%n makes a mental note of what you said.
2
%n makes a mental note of what %d said.
2
You make a mental note of what you said.
2
%n makes a mental note of what %s said.
2
1
4
0
2
1
4
7
2
You hmm.
2
%n hmms.
2
You hmm at %d.
2
%n hmms at you.
2
%n hmms at %d.
2
You hmm at yourself.
2
%n hmms at %r.
2
1
4
7
2
You look about, glaring at the poor manners exhibited.
2
%n looks about, glaring at the poor manners exhibited.
2
You chastise %d for %[dpp] poor table manners.
2
%n chides you about your table manners.
2
%n chides %d about %[dpp] table manners. %D looks hurt.
2
You worry about comitting a grave social error.
2
%n shifts uncomfortably, wary of making a social faux pas.
2
1
4
7
2
You scramble madly for your dictionary.
2
%n scrambles madly for %p dictionary.
2
You point %d to your dictionary.
2
%n points you to %p dictionary.
2
%n points %d to %p dictionary.
2
You confuse yourself with your big words and scramble for your dictionary.
2
%n confuses %r with %p big words and scrambles for %p dictionary.
2
1
4
7
2
You strain to hear something, but to no avail.
2
%n strains to hear something, but to no avail.
2
You listen carefully to %d.
2
%n listens carefully to you.
2
%n listens carefully to %d.
2
You listen carefully to yourself.
2
%n listens carefully to %r.
2
1
4
7
2
You scramble madly, looking for something to write on.
2
%n looks around frantically for something to write on.
2
You take out a notepad and write down what %d said.
2
%n takes out a notepad and writes down what you said.
2
%n takes out a notepad and writes down what %d said.
2
You take out a notepad and write a note to yourself.
2
%n takes out a notepad and writes a note to %r.
2
1
4
7
2
You cheer loudly.
2
%n cheers loudly.
2
You cheer for %d.
2
%n cheers for you.
2
%n cheers for %d.
2
You cheer yourself on.
2
%n cheers %r on.
2
1
4
0
2
1
4
7
2
You nod vigorously.
2
%n nods vigorously.
2
You nod vigorously in agreement with %d's ideas.
2
%n nods vigorously in agreement with your ideas.
2
%n nods vigorously in agreement with %d's ideas.
2
You nod vigorously in agreement with your ownideas.
2
%n nods vigorously, arrogantly agreeing with %r.
2
1
4
7
2
You cry.
2
%n cries.
2
You cry at %d.
2
%n cries at you.
2
%n cries at %d.
2
You cry to yourself.
2
%n cries to %r.
2
1
4
7
2
%n will be right back.
2
%n will be right back.
2
You tell %d that you will be right back.
2
%n tells you that %s will be right back.
2
%n tells %d that %s will be right back.
2
%n will be right back.
2
%n will be right back.
2
1
4
0
2
1
4
7
2
You act real sympathetic and comforting.
2
%n smiles understandingly and makes comforting gestures.
2
You comfort %d.
2
%n tries to comfort you.
2
%n tries to comfort %d.
2
You try to comfort yourself in self-pity.
2
%n tries to comfort %r in self-pity.
2
1
4
0
2
5
4
0
2
5
4
0
2
5
4
0
2
5
4
0
2
5
4
0
2
5
4
0
2
5
4
0
2
5
4
0
2
5
4
0
2
5
4
0
2
5
4
0
2
5
4
0
2
5
4
0
2
5
4
7
2
You open your arms, apparently waiting a hug.
2
%n opens %q arms, apparently waiting a hug.
2
You hug %d in a warm and loving embrace.
2
%n hugs you in a warm and loving embrace.
2
%n hugs %d in a warm and loving embrace.
2
You hug yourself.
2
%n hugs %r.
2
5
4
7
2
You pucker your lips expectantly.
2
%n puckers %p lips, perhaps expecting a kiss?
2
You kiss %d sweetly
2
%n kisses you sweetly.
2
%n kisses %d platonically.
2
You attempt to kiss yourself.
2
%n foolishly attempts to kiss %r.
2
5
4
7
2
You bow gracefully.
2
%n bows gracefully.
2
You bow gracefully at %d.
2
%n bows gracefully at you.
2
%n bows gracefully at %d.
2
You bow gracefully at yourself.
2
%n bows gracefully at %r.
2
5
4
0
2
5
4
7
2
You pine sadly away.
2
%n sits and sighs and mopes, apparently feeling lonely for someone.
2
You pine sadly away for %d
2
%n looks longingly at you and sighs, pining quite away.
2
%n looks longingly at %d and sighs, pining quite away.
2
Huh??
2
%n sits and sighs and mopes, apparently feeling lonely for someone.
2
5
4
7
2
You roll around on the floor.
2
%n rolls around on the floor.
2
You roll into the legs of %d, causing %[dpo] to fall to the floor.
2
%n rolls about on the floor, suddenly rolling into your legs and causing you to fall!
2
%n rolls about on the floor, suddenly rolling into the legs of %d, causing %[dpo] to fall to the floor!
2
You roll around on the floor.
2
%n rolls around on the floor.
2
5
4
7
2
You look for someone to hold around.
2
%n looks for someone to hold around.
2
You hold %d close.
2
%n puts %p arms around you and holds you close.
2
%n holds %d close.
2
You wrap your arms around yourself.
2
%n holds %r.
2
5
4
7
2
%n just got a visitor IRL, but %s will be right back.
2
%n just got a visitor IRL, but %s will be right back.
2
You tell %d that you just got a visitor IRL, but that you will be right back.
2
%n tells you that %s just got a visitor IRL, but %s will be right back.
2
%n tells %d that %s just got a visitor IRL, but that %s will be right back.
2
You silently curse RL visitors who impose on your MOOing.
2
%n silently curse RL visitors who impose on %p MOOing.
2
5
4
7
2
%n will be away from %p keyboard for a bit.
2
%n will be away from %p keyboard for a bit.
2
You tell %d that you will be away from your keyboard for a bit.
2
%n tells you that %s will be away from %p keyboard for a bit.
2
%n tells %d that %s will be away from %p keyboard for a bit.
2
%n will be away from %p keyboard for a bit.
2
%n will be away from %p keyboard for a bit.
2
5
4
7
2
In a sudden flash of insight you read the minds of everyone in the room.
2
In a sudden flash of insight %n reads the mind of everyone in the room!
2
You sync with %d.
2
%n syncs with you and reads your mind.
2
%n syncs with %d.
2
You read your own mind, WOW!
2
%n reads %p own mind, WOW!
2
5
4
7
2
%n just got a phone call IRL, but %s will be right back.
2
%n just got a phone call IRL, but %s will be right back.
2
You tell %d that you just got a phone call IRL, but that you will be right back.
2
%n tells you that %s just got a phone call, but that %s will be right back.
2
%n tells %d that %s just got a phone call, and that %s will be right back.
2
You silently curse phone calls that impose on your MOOing.
2
%n silently curse RL phone calls that impose on %p MOOing.
2
5
4
0
2
5
4
7
2
You jump up and down in excitement while you exclaim WUNDIE!
2
%n jumps up and down in excitement and exclaims WUNDIE!
2
You smile happily at %d and exclaim WUNDIE!
2
%n smiles happily at you and exclaims WUNDIE!
2
%n smiles happily at %d and exclaims WUNDIE!
2
%n smiles happily and exclaims WUNDIE!
2
%n smiles happily and exclaims WUNDIE!
2
5
4
7
2
You take out a beautiful bouquet of yellow @}-->----- and gracefully hand them out.
2
%n takes out a beautiful bouquet of yellow @}-->----- and hands them out gracefully.
2
You hand %d a beautiful yellow @}-->-----
2
%n hands you a beautiful yellow @}-->-----
2
%n hands %d a beautiful yellow @}-->-----
2
You take out a beautiful yellow @-->----- and admire it.
2
%n takes out a beautiful yellow @}-->----- and admires it.
2
5
4
7
2
You curse the network lag.
2
%n curses the network lag.
2
You tell %d that you are lagging at the moment.
2
%n tells you that %s is lagging at the moment.
2
%n tells %d that %s is lagging.
2
You curse the network lag.
2
%n curses the network lag.
2
5
5
36
1
5
2
5
4
3
2
social
2
unsocial
2
useful
36
1
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
New Social Verbs
2
nsv
2
5
2
The ultimate in social verbs. Heh.
2
5
4
2
0
41302
0
1001876092
36
1
5
2
5
5
2
5
#115
Generic Recorder and Player

144
2
120
-1
119
8
-1
120
14
create_note
2
173
-1
start_recorder
2
173
-1
stop_recorder
2
165
-1
title
2
173
-1
tell_contents
2
173
-1
tell
2
165
-1
p*ut in*sert d*rop
2
153
3
play
2
153
4
setdelay
2
105
1
look_self
2
173
-1
init_for_core
2
173
-1
_html
2
165
-1
start stop
2
41
-1
set
2
105
1
7
recording
playing
log
delay
design
help_msg
private
86
0
0
2
5
0
0
2
5
1
-1
2
5
0
3
2
5
2
jan@LinguaMOO
2
5
4
13
2
With this simple device you can record and play back conversations in the MOO. The following commands are available:
2

2
START: This command starts the recorder. A note on which to record will be created for you automatically. Just type in a name for your log and the recorder will start. <I>Example: start recorder.</I>
2

2
STOP: This command stops both the recorder and the player if they are on. <I>Example: stop recorder</I>
2

2
PLAY: Using this command you can play any note in your player. Look at it to see the contents. If, for example, you wish to play note number two, type: <I>play 2 on recorder"</I>
2

2
SETDELAY: You can set the playback speed with this command. <I>Example: setdelay recorder to 3</I>
2

2
SET: Command used to control usage of the recording device. When you create a new recorder it is set to public usage by default which means that anyone can operate it. You can restrict its usage to the owner and wizards only by setting it to private. <I>Example: set recorder to private</I> and <I> set recorder to public</I>.
2

2
Design, Jan@LinguaMOO, 1997-99
2
5
0
0
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
0
0
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
0
1
2
1
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
recorder.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Generic Recorder and Player
2
5
2
A simple recording and playback device. Type 'help $recorder' for operating instructions.
2
5
4
2
0
16206
0
1001876092
36
1
5
2
5
5
2
5
#116
enCore Administrators Guide

0
2
61
-1
117
95
-1
117
1
init_for_core
2
173
-1
0
67
4
0
2
5
5
2
5
5
2
4
4
131
2
=============================
2
HIGH WIRED ENCORE
2
EDUCATIONAL MOO CORE DATABASE
2
ADMINISTRATORS GUIDE
2
=============================
2

2
COPYRIGHT AND LICENSE NOTICE 
2
---------------------------- 
2
This software is Copyright (C) 1997-2001 of Jan Rune Holmevik and Cynthia Haynes. All rights reserved. The original core of this program is Copyright (C) 1991-1997 of LambdaMOO. The enCore layer is Copyright (C) 1997-2001 of its respective authors.  Built-in support for the MCP/2.1 protocol developed for JHCore is copyright (C) 1998 of Ken Fox. The MCP/2.1 implementation is also available separately, and under a separate licence at: http://www.awns.com/mcp/
2

2
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or any later version.
2

2
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. (http://lingua.utdallas.edu/encore/gpl.html)
2

2
This software is in no way affiliated with the University of Michigan Press, the University of Bergen, the University of Texas at Dallas, or any other institutions or organizations mentioned in this document or on the web sites associated with this program. 
2

2
ACKNOWLEDGEMENTS 
2
---------------- 
2
The High Wired enCore is based on LambdaCore ver. 02 Feb. 1997. We are grateful to the LambdaMOO community and wiz team for sharing this core with us and the rest of the Internet community. We also want to extend our thanks to all those who have generously contributed code to the High Wired enCore. In addition we want to thank those enCore users who have sent us bug reports and feedback so we can continue to improve this software. Lastly we want to thank the University of Texas at Dallas for providing us with technical resources in the development of enCore, and the University of Bergen, Norway for institutional and financial support.
2

2

2
INTRODUCTION 
2
------------ 
2
The High Wired Educational MOO Core Database, enCore, is a constructivist virtual reality environment designed for educational use. It is based on the Feb 02 1997 version of the original LambdaCore Database and supplemented with a number of useful educational tools and enhancements, such as support for the enCore Xpress and MacMOOSE interfaces. The enCore was developed in order to give educators a MOO environment that they can immediately put to productive use in online teaching and learning. The most recent version of the enCore database is always available from the enCore home page at: http://lingua.utdallas.edu/encore/
2

2
The enCore is complemented by our books _High Wired: On the Design Use, and Theory of Educational MOOs_ (http://141.211.86.203/FMPro?-db=s-main.fp5&-lay=all&-format=book-main.html&-error=error.html&control=09665&-find) available from University of Michigan Press, and _MOOniversity: A Students Guide to Online Learning Environments_ (http://vig.abacon.com/catalog/abbooks/0,2371,0205271146,00.html) from Allyn & Bacon. In these books you will find more in-depth information about the use and administration of MOOs as well as specific information on use of MOOs in education.
2

2
As a new MOO administrator we encourage you to subscribe to the enCore mailing list for announcements and discussion of the enCore software. To subscribe to this list send an email to majordomo@utdallas.edu with the following line in the body of your message: subscribe encore
2

2

2
GETTING STARTED 
2
--------------- 
2
Before you can start using your MOO you need to set/edit a few preferences. After you connect to your MOO for the first time you should type:
2

2
@configure-core
2

2
This command will take you through the initialization of the most important preference so that you can start using your new MOO productively right away. 
2

2
In order for enCore Xpress to work properly it is *very* important that you specify the correct URL pointing to the directories holding the MOOtcan applet and the Xpress image and sound files. 
2

2
You can now log into your MOO with Xpress by pointing your web browser to a URL that should look something like this http://your-site.edu:7000/
2

2
HOW TO MAKE NEW CHARACTERS 
2
-------------------------- 
2

2
- Encore Xpress' new MOO administration module makes character creation simple. Just log on with Xpress and click on the menu button namned Wizard. If you prefer to create characters the old fashioned way, simply follow the steps outlined below.
2

2
- '@make-player name email address real name' This command will create a new character. If you have enabled outbound networking (e.g. compiled the server with outbound networking on and set $network.active to 1) the password will be emailed automatically to the new user's email address. If not, you must write down the password and mail it to the new user via email. Example: 
2

2
@make-player Jan jan@someplace.no "Jan Rune Holmevik"
2

2
- '@make-guest name' Will make a new guest character with the name you specify with '_Guest' appended. Example: 
2

2
@make-guest First - create a guest with the name First_Guest. 
2

2
Note: If you have enabeled the enCore guest self naming system (see below) you still need to give the guest characters default names using the procedure outlined above.
2

2
- '@programmer name' Will make the character a programmer. Example: 
2

2
@programmer jan
2

2

2
MAKING WIZARDS 
2
-------------- 
2
Being a wizard carries not only absolute powers in the MOO world, but also a great responsibility. As the owner of the MOO you must therefore make sure that the people you choose for your wizard team can be trusted 100% and that they know the amount of work that is involved. Ideally, you should only choose people that you know well and that you know to be mature and responsible. Dont ever choose a person to become a wizard in your MOO just because of his or her technical knowledge or skills. Once you have wizzed someone you have basically given that person access to every bit of information in the MOO including wizard commands that might be used to destroy it. So before you make the decision to make someone a wizard, you should trust and know that person completely.
2

2
To make a new wizard follow these steps. You should never wiz an already existing character. Always start with a new player character.
2

2
- @make-player name email-address real-name a new player character with object number, say #100, is created
2

2
- @programmer #100
2

2
- @chparent #100 to $wiz
2

2
- @set #100.wizard to 1
2

2
- @set #100.public_identity to [the player's nonwiz character's object#]
2

2

2
GENERIC EDUCATIONAL TOOLS 
2
------------------------- 
2
You can find all of enCore's educational objects in the Box of Educational Tools which you will find in the enCore Starting Point. Most of these educational objects have help texts, so just type 'help obj-name' to learn more about how to use them. (You must hold the objects for the command to work) 
2

2
Example:
2

2
help Generic Moderated Room, or help #98
2

2
Some tools have special helptexts or manuals. Type '@examine object-name' to find out more about this.
2

2
Important! The Generic Objects should never be used themselves. Instead, use them as templates for new objects. For example, if you want to make a new tv, type 
2

2
@create $tv named New TV
2

2
For quick and easy access to the tools included in the enCore database, you may also simply type @create.
2

2
NEWS SYSTEM 
2
----------- 
2
The High Wired enCore comes with a different news system than the one found in the standard LambdaCore. It consists of two components, the Newspaper itself ($news), and a news item ($news_item). To post something to the newspaper follow these three steps:
2

2
- '@create $news_item named whatever' - this creates a new article. 
2
- '@edit whatever' - Type the text of your article, save and quit. 
2
- '@move whatever to $news' - Moves the article to the newspaper.
2

2

2
QUOTA 
2
----- 
2
By default, enCore runs with byte-based-quota. This quota system is different from the default object-based quota system in the LambdaCore. Instead of measuring quota in objects, this system measures how many bytes each person actually uses, and thus, byte-based-quota is much more accurate than object-based-quota. New builders start with default quota of 50,000 bytes. 
2

2
CHARACTER REQUEST SYSTEM 
2
------------------------ 
2
A new character @request system has been implemented. If you disable automatic character creation, (recommended) all guests are sent to a special room, $character_request_room, where they must fill in a form which is then sent to an in-MOO mail list called Character Request List. You subscribe to this list with the command '@subscribe *cr'
2

2
GUEST SELF NAMING SYSTEM
2
------------------------
2
In enCore MOOs guests may be allowed to use a name of their own choosing instead of the pre-defined guest names. If you wish to give your guests this ability you must enable it with the @configure command. By default, the suffix [Guest] is added to all guest names in self naming mode. The @configure command will let you change this suffix to whatever you want, or, remove altogether.
2

2

2
THE ENCORE XPRESS SYSTEM 
2
------------------------ 
2
The High Wired enCore comes with a built-in, WWW-interface system called Xpress that can provide a rich multi-media content for your MOO. Through the Xpress interface people can browse your MOO as a non-interactive hypertext, or use the integrated Xpress MOO client to communicate with others online. enCore Xpress comes with a variety of graphical point-and-click applications that will make mailing, editing and programming really easy and hassle free. The web interface allows you to connect webpages, images, sound files, Real Audio and video streams, Shockwave animations, java applets and more to objects in your MOO.   
2

2
The enCore Xpress web interface is listening for incoming connections on port 7000, so the URL for your MOO's homepage is http://somemachine.somesite.edu:7000. The URL of any object in the MOO is determined by the object's number. For example, the URL to the enCore Starting Point is: http://somemachine.somesite.edu:7000/62.
2

2

2
MACMOOSE CLIENT SUPPORT 
2
----------------------- 
2
For Macintosh users, Amy Bruckman's MacMOOSE client program will make it easier to use the MOO and its many advanced editing features (A PC version of the program is in the works). The enCore comes fully equipped with the latest MacMOOSE system. All you have to do is download the client program itself. You can find it at http://www.cc.gatech.edu/fac/Amy.Bruckman/MacMOOSE/. With Andrew Wilson's tkMOO-light client you can also use some of the advanced editing features provided by MacMOOSE on UNIX, Windows, or Macintosh computers. You can download the tkMOO-light client from http://www.awns.com/tkMOO-light/
2

2
Good Luck With Your New Educational MOO, 
2
Jan Rune Holmevik and Cynthia Haynes,
2
Coordinators of the enCore Open Source MOO Project
2
4
0
1001876080
2
5
2
Wizard
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
0
3
2
0
5
2
0
5
2
5
5
2
5
5
2
5
5
2
4
4
1
2
enCore Administrators Guide
2
5
5
2
5
4
2
0
13746
0
1001876092
36
1
5
2
5
5
2
5
#117
MOOniversity Lite

0
2
61
-1
-1
95
-1
-1
1
init_for_core
2
173
-1
0
67
4
0
2
5
5
2
5
5
2
4
4
122
2
==========================
2
MOOniversity LITE
2
BEGINNER'S GUIDE TO MOOING
2
==========================
2

2
MOO (Multi-User Domain Object Oriented) is a computer program that allows multiple users to connect via the Internet to a shared database of rooms and other objects and interact with each other and the database in synchronous time.
2

2
This guide is designed to get you started with MOOing. Some of the basic MOO commands are explained two different ways because some users will be using TELNET programs to access the MOO, and some users will be using the new enCore Xpress MOO client. We have written the guide to benefit all users. 
2

2
Once you have connected type 'help' to get an index of help topics, or click on the HELP button on your Xpress toolbar. To get specific help on communication, for example, you can type 'help communication' or select the topic you want in the Xpress Help Browser search box. For more in-depth information about use of educational MOOs we refer you to our books _High Wired: On the Design, Use, and Theory of Educational MOOs_ (http://www.press.umich.edu/titles/09665.html) and _MOOniversity: A Students Guide to Online Learning Environments_ (http://vig.abacon.com/catalog/abbooks/0,2371,0205271146,00.html).
2

2

2
A FEW BASIC COMMANDS 
2
-------------------- 
2

2
To see how the room you're in looks like, just type 'look' or click on the LOOK button on the Xpress toolbar. To get information about the objects around you, including yourself and other users, you can click on the object link in the right-hand web area window, or type:
2

2
look object 
2
Example: look news - to see the description of that object
2

2
examine object 
2
Example: examine news - to see the description, and additional information, like the owner of the object, and some commands on how to use it.
2

2
If you have a player account (i.e., you are not a guest), one of the first things you should do after you connect is to set your personal preferences such as gender, description and so on. To do this simply type:
2

2
@preferences
2

2
If you are using Xpress, click on the Options button. Fill in the information as desired. If you have questions about the fields to fill in, click on the lightbulb for HELP.
2

2
COMMUNICATING WITH OTHER PEOPLE 
2
------------------------------- 
2
To talk to someone in the same room as you are, you type a quotation mark '"' followed by what you want to say, like this:
2

2
"Hello there!
2

2
You see this in the output frame: You say, "Hello there!" 
2
Everyone else in the room sees: Ann says "Hello there!"
2

2
You can use body language, also known as emoting, by typing a colon followed by text like this:
2

2
:smiles
2

2
You see this text in the output frame: Ann smiles.
2
Everyone else in the room sees: Ann smiles
2

2
Often, when several people are in the same room, you may want to address a particular person. To do that type 'to' followed by the name of the person you want to talk to, and then what you want to say.
2

2
to John Good to see you again :-).
2

2
John, you, and everyone else sees: Ann [to John]: Good to see you again
2

2
To communicate with people who are not in the same place you are, you can 'page' them:
2

2
page John Do you have time for a question? 
2

2
The 'page' command can be shortened to '-' like this:
2

2
-john Hi, may I come over?
2

2
You can also 'remote emote' by typing:
2

2
+john smiles and waves
2

2

2
MOVING AROUND 
2
------------- 
2
In every room exits or entrances are listed either at the bottom of your screen, or if there is a map or image, perhaps marked by a words or letters on the map itself, or directional words like in, out, up, down. To move in a given direction simply type the name of the direction or exit. For example, if you see an exit named 'west', you type 'west' to go there. If a code on the map says 'LIB', you type LIB to go there. If you are using Xpress, the exits or entrances will appear as links, so you simply click on the link.
2

2
If you get lost, you can always get back to where you started by typing 'home'. A fast and convenient way to move around is to '@join' other users. To see who is in the MOO type 
2

2
@who
2

2
If you are using Xpress, click on the WHO button.
2

2
If you see another user online named John, you can join him by typing:
2

2
@join John
2

2
Or click on the link in the location column of the Xpress Who window.
2

2
Another way to move quickly between locations in the MOO is to use the '@go' command if you know the name of the place. If, for example, you want to go to a place called 'Classroom', you type:
2

2
@go Classroom
2

2
People come to the MOO for many different reasons, some come to work, others to hang out or visit with friends. Out of courtesy, you should always ask before you join someone. For example, if you want to talk to John, you should type 
2

2
@knock John
2

2
If he wants to talk to you he can reply with 
2

2
@invite player-name 
2

2
Depending on the configuration of the MOO, real names and email addresses of users may be available. For more information about a given user you can type:
2

2
@whois name 
2

2
Or, if you are using Xpress, click on the WHO button, then the person's name link.
2

2
PRIVACY AND SAFETY 
2
------------------ 
2
An occasional problem on MOOs is harassment from anonymous guests or other users. To deal with this problem there are certain commands that you can use.
2

2
@gag person - This will effectively filter out anything the person says or pages until you type @ungag person.
2

2
@eject! person - Use this command to expel someone from a room you own.
2

2
@lock here with me - Will lock the room so nobody can come in. @unlock here will unlock your room. Or, if you are using Xpress, you can click on the padlock icon to lock your room.
2

2
You can also contact the wizards and ask them for help
2

2
@wizards - Will tell you the name or names of wizards who are online.
2

2
MOO CLIENTS 
2
----------- 
2
If you are using telnet to connect to the MOO, the text you type in may get disrupted and broken up by the output from the MOO. This can be very confusing and frustrating, and the best way to deal with it is to use the enCore Xpress client. There are also many other MOO clients available, see for example http://web.nwe.ufl.edu/~tari/connections/client-info.html. 
2

2
To log out type: @quit (or click on the QUIT button in Xpress)
2

2
Finally, remember that one of the most useful help commands in MOO is this one: say Hi, I'm new here. Do you have a minute for a question?
2

2
Good luck and happy MOOing. 
2
Cynthia Haynes and Jan Rune Holmevik
2
4
0
1001876080
2
5
2
Wizard
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
0
3
2
0
5
2
0
5
2
5
5
2
5
5
2
5
5
2
4
4
1
2
MOOniversity Lite
2
5
5
2
5
4
2
0
9053
0
1001876092
36
1
5
2
5
5
2
5
#118
Room Database

0
2
-1
-1
-1
37
-1
-1
2
update
2
173
-1
init_for_core
2
173
-1
2
last_update
updating
11
0
0
2
5
0
0
2
5
5
2
5
5
36
1
4
4
2

2

4
0
4
0
36
0
0
0
2
4
4
1
2
Room Database
2
5
5
2
5
4
2
0
1405
0
1001876092
36
1
5
2
5
5
2
5
#119
Lag Meter FO

0
2
120
-1
123
74
-1
123
21
description
2
173
-1
about
2
45
-1
turn
2
89
-2
startup
2
173
-1
lag_cycle
2
173
-1
initialize
2
173
-1
reset
2
109
-2
read
2
45
-1
reading
2
173
-1
decimal1
2
173
-1
trend_of
2
173
-1
trend
2
45
-1
@lag
2
13
-1
help_msg
2
173
-1
get_lags
2
173
-1
lags_reading
2
173
-1
lags
2
141
4
@lags
2
13
-1
first_index
2
173
-1
touch1
2
173
-1
init_for_core
2
173
-1
9
about
reset_usage_msg
cycles
cycle_scheduled
total
samples
sample_count
cycle_time
active
73
4
11
2
The meter makes a rough real-time measurement of the MOO server lag. Server lag happens when there are many player commands and many background tasks to be processed, and the MOO can't keep up with them all. The meter does not measure network lag. If you have a bad network connection, your total lag may be much greater than the server lag.
2

2
    read meter                        - see the one-line meter display
2
    lags on meter                     - see the sampled lags, in seconds
2
    turn meter on                     - turn on the meter
2
    turn meter off                    - turn off the meter
2
    reset meter                       - zap the meter's stats, and start over
2
    reset meter to <time> <samples>   - change the meter's averaging behavior
2

2
The <time> is a number of seconds, the time interval over which the meter calculates the average lag. <Samples> is the number of samples the meter takes over that interval. If <time> is small, like 60 seconds, the meter is very sensitive to rapid fluctuations in the lag.
2

2
5
2
Usage: 'reset meter', 'reset meter to <time> <samples>', where <time> is a duration in seconds and <samples> is the number of samples to take over that time.
2
5
0
13033021
2
1
0
875540096
2
5
0
0
2
1
4
4
0
0
0
0
0
0
0
0
2
1
0
4
2
5
0
5
2
5
0
1
2
5
5
36
1
5
2
5
4
2
2
@lag
2
@lags
36
1
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
Lag Meter
2
meter
2
5
2
This meter takes a rough measurement of the server lag, that is, how much delay the MOO game itself is causing because of everything that's happening. Type 'about meter' for instructions.
2
5
4
2
0
16528
0
1001876092
36
1
5
2
5
5
2
5
#120
Box of Educational Tools

0
2
62
95
-1
8
-1
-1
1
init_for_core
2
173
-1
0
79
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
0
0
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
0
1
2
1
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
0
7
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
Box of Educational Tools
2
box
2
5
2
A large box for storing educational tools and other objects.
2
5
4
2
0
2042
0
1001876092
36
1
5
2
5
5
2
5
#121
Applying for a Character

0
2
-1
-1
-1
3
-1
-1
1
init_for_core
2
173
-1
0
68
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
4
5
2
5
5
2
5
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Applying for a Character
2
5
2
A quiet place where you can complete your character request. Please be sure that you have read and understood the purpose and theme of this MOO, as well as the code of conduct expected.
2
5
4
2
0
2059
0
1001876092
36
1
5
2
5
5
2
5
#122
Character-Request-List

0
2
46
-1
-1
45
-1
-1
1
init_for_core
2
173
-1
0
26
5
2
5
0
0
36
1
5
36
0
5
2
5
5
2
5
5
2
5
5
2
5
5
36
1
4
0
36
1
5
36
1
5
36
1
5
36
0
5
2
5
0
1
2
5
4
0
36
1
5
2
5
5
36
1
5
36
0
5
36
0
5
36
0
0
0
2
4
4
2
2
Character Request List
2
cr
36
1
2
List where new character requests are sent.
2
5
4
2
0
1215
0
1001876092
36
1
5
2
5
5
2
5
#123
Census FO

0
2
120
-1
127
74
-1
127
2
census
2
13
-1
init_for_core
2
173
-1
1
not_in_census
65
4
0
2
5
5
36
1
5
2
5
4
0
36
1
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Census FO
2
5
2
Useful feature for displaying statistics on MOO usage.
2
5
4
2
0
3682
0
1001876092
36
1
5
2
5
5
2
5
#124
enCore Utilities

0
2
-1
-1
-1
79
-1
125
1
make_title
36
165
-1
0
7
5
2
5
0
0
2
4
4
1
2
enCore Utilities
2
5
5
2
5
4
2
0
775
0
1001876092
36
1
5
2
5
5
2
5
#125
enCore Web Utilities

0
2
-1
-1
-1
79
-1
-1
45
generate_head
36
165
-1
generate_footer
36
165
-1
generate_links
36
165
-1
is_locked
2
165
-1
tell_locked
36
165
-1
get_title get_subtitle
2
165
-1
generate_table
36
165
-1
center
36
165
-1
preformat
36
165
-1
get_text
36
165
-1
get_text_linebreak*s
36
165
-1
get_page_properties
36
165
-1
resolve_url
36
165
-1
get_file_type
36
165
-1
insert_line_break*s
36
173
-1
make_sound
2
173
-1
append_close_button
2
173
-1
parse_url
2
173
-1
form
2
173
-1
javascript_window_open
2
173
-1
insert_JavaScript
2
173
-1
get_banner
2
173
-1
move_via_web
2
173
-1
link_with_object_argument
2
173
-1
font_size
2
165
-1
set_font_properties
2
165
-1
insert_context_sensitive_help
2
173
-1
italics
2
165
-1
detect_urls
2
173
-1
decode_url
2
173
-1
parse_url_encoded_form_data
2
173
-1
parse_multipart_form_data
2
165
-1
parse_form
2
173
-1
substitute*_suspended
2
173
-1
get_icon
2
173
-1
anonymous_access
2
165
-1
permission_to_edit
2
173
-1
make_alert
2
173
-1
can_handle
2
173
-1
page_assembled
2
173
-1
make_body
2
173
-1
make_page
2
173
-1
make_head
2
173
-1
make_style_sheet
2
173
-1
align
2
173
-1
1
html_entity_subs
8
4
6
4
2
2
<
2
&lt;
4
2
2
>
2
&gt;
4
2
2
&
2
&amp;
4
2
2
(C)
2
&copy;
4
2
2
(R)
2
&reg;
4
2
2
"
2
&quot;
2
5
5
2
5
0
0
2
4
4
1
2
enCore Web Utilities
2
5
5
2
5
4
2
0
52471
0
1001876092
36
1
5
2
5
5
2
5
#126
enCore Web Object

144
2
-1
-1
-1
138
3
32
8
_html
2
165
-1
v*iew
2
41
-1
dis*play
2
41
-1
connect
2
105
1
@webpref*erences
2
33
-1
update_hits
2
173
-1
lock_html unlock_html
2
173
-1
examine_html
2
173
-1
6
web_height
web_width
web_align
table_layout_if_possible
extra_html
display_ascii
52
2
200
2
5
2
200
2
5
2
TOP
2
5
0
0
2
5
4
0
2
5
0
1
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2

2
1
2

2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
2
encore_web_object.css
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
enCore Web Class
2
5
5
2
5
4
2
0
24204
0
1001876098
36
1
5
2
5
5
2
5
#127
SR Porter v4.6

0
2
120
-1
128
74
-1
129
46
get_verb_defs
2
165
-1
get_verb_codes
2
165
-1
install_verbs
2
165
-1
open
2
173
-1
get_verb_args
2
165
-1
get_prop_names
2
165
-1
remote_eval
2
165
-1
strip_verb_name
2
173
-1
get_prop_info
2
165
-1
install_dprops
2
165
-1
get_prop_data
2
165
-1
get_iprop_names
2
165
-1
install_iprops
2
165
-1
init_remote
2
173
-1
@netcopy
2
29
-1
@propcopy
2
29
-1
read
2
165
-1
get_verb_defs_args
2
165
-1
@verbcopy
2
29
-1
get_spec_prop_names
2
165
-1
check_network
2
173
-1
trust
2
173
-1
is_wizard
2
173
-1
is_open
2
165
-1
remote_command
2
173
-1
close
2
173
-1
handle_login_result
2
173
-1
handle_local_object
2
173
-1
check_remote_object
2
173
-1
get_info
2
173
-1
create_local_object
2
173
-1
check_cmdline_called ccc
2
173
-1
eval_cmd_string ecs
2
165
-1
msg
2
173
-1
nodice_msg
2
173
-1
_add_verb
2
173
-1
_add_property
2
173
-1
_set_verb_info
2
173
-1
_has_verb
2
173
-1
check_args
2
173
-1
@srp-addname
2
13
-1
@srp-rmname
2
13
-1
@srp-listnames
2
13
-1
db_lookup
2
173
-1
sentinel
2
165
-1
init_for_core
2
173
-1
15
builtin_props
trusts
nodice_msg
init_commands
lostcon_msg
remote_prefix
remote_suffix
copyright
msg_prefix
debug
login_timeout
db_names
db_data
winmoo
idle_timeout
79
4
4
2
name
2
r
2
w
2
f
2
5
4
0
2
0
2
Sorry, you are not authorized to use %t. Contact %n for more information
2
5
4
7
2

2

2

2
@more flush
2
@wrap off
2
@pagelength off
2
@ansi-options -all
2
5
2
The connection has been closed.  Port aborted.
2
1
2
SRP-PREFIX
2
1
2
SRP-SUFFIX
2
1
2
SR Porter v4.6 Copyright (C) 1995-1998 by Michael Thompson.
2
1
2
==> 
2
1
0
0
2
1
0
15
2
1
4
3
2
bay
2
mtn
2
qmud
2
0
4
3
4
2
2
baymoo.org
0
8888
4
2
2
mountain.arkay.net
0
8888
4
2
2
qmud.warzone.com
0
3000
2
0
0
0
2
1
0
30
2
1
5
36
1
4
131
2
Commands:
2
My answer to Autoporter which I couldn't get to work anywhere I tried.
2
Operation is simple, @addfeature this.  There are three commands that
2
do the actual porting @netcopy, @verbcopy and @propcopy.   Detailed
2
help is available in each verb by way of 'help srp:verb'
2

2
There are three more commands that allow you to manipulate the porter's
2
internal MOO address database.  This database stores your commonly used
2
MOO's address and ports accessible by a small alias.  The commands are
2
@srp-addname, @srp-rmname, and @srp-listnames.  Help is available in
2
each command.  Using one of the 'names' in the database when a porting
2
command asks for a host name will use the database to obtain the MOO's
2
information.  Remote usernames and passwords are not, and will not be
2
stored.
2

2
Note that the porter can only get what is readable by you on the remote 
2
MOO.  That means if a object is -r you can not get any of it's defined 
2
properties.  The account you use remotely also has to be a programmer.
2
----------------------------------------------------------------------
2

2
Configuring :
2
Wizards take note while this feature is wiz-only, wizard's public 
2
identies and any player in the list .trusts can use the feature.
2
If you want to change this modify :trust (and/or :is_wizard).
2

2
trusts : List of non wizard, non aka users allowed to use this.
2
debug  : Users will be spammed with lots of debug messages.
2
init_commands : List of commands sent to the other MOO to initialize
2
                 the remote player.
2
login_timeout : How long should the porter wait before giving up when
2
                 trying to log in.
2
idle_timeout  : How idle can the connection be before the porter aborts.
2

2

2
Revision History :
2
4.5  : Improved handling of complicated verb names (multiple aliases,
2
       *, etc).  Improvements to take advantage of 1.8.0 capabilities.
2
       MOO name database added.  Support for WinMOO, and older core 
2
       databases.  Added documentation to each verb.  More configurable.
2
       Detection of a 1.8.0 server remotely.  Better error handling.
2
4.3  : Intermediate copy on the way to the 4.5/GNU/enCore release.
2
4.1  : Small patch to make it more friendly in byte_quota situations.
2
       Ppl with $server_options.protect_* and no v1.8.* bf wrappers 
2
       read the install notes!  Added a few more MOOs to the list and
2
       a reviews section (mail me yours).
2
4.0  : First public release, any questions on this one should be sent
2
       to Shadow_RAM@(whatever)MOO, or 'shadowr@ipass.net'
2
3.95 : Final Beta, tweaked everything, increased security, and made a 
2
       lot of small changes I forgot (this last part holds true for
2
       all revisions)
2
3.85 : Beta, cleaned up all the code, put redudant stuff into seperate
2
       verbs to cut down on size, small touchups on the networking code
2
3.75 : ?? no copies still exist for me to check...
2
 the missing versions ...
2
3.00 : Beta @verbcopy/@propcopy almost perfected, reworked the network
2
       code, helped reduce some speed problems
2
2.75 : Alpha, introduction of @verbcopy/@propcopy after user requests,
2
       changed many times in debate on how to implement @propcopy
2
2.5  : Final Beta, first copies to be used elseMOO, only had @netcopy
2
       but it was fairly bug free, small patches brought this up to
2
       around 2.6 in some instances making it a full release
2
2.0 and earlier : this is based on memory because my new copies overwrote
2
       the original while I was working, first version I hacked up in 
2
       about 1.5 days, only had @netcopy, extreme security holes, etc.
2
       later revisions cleaned that up and got it ready to be carried to
2
       a few select test sites
2
----------------------------------------------------------------------
2

2
Thanks :
2
Many thanks to Rk and Kender, Arch-wizzen of Arkay/Mountain MOO for 
2
letting me develop and test on their MOOs, and for putting up with me
2
in general.  Thanks to any other testers/unfortunate victims of SRP.
2
Additional thanks to Kender for letting me continue to develop
2
new versions of SRP at Mountain MOO.
2

2
MOOs SRP is on :
2
BayMOO                  baymoo.org              8888
2
MountainMOO             mountain.arkay.net      8888
2
CountryMOO              cmoo.com                5555
2
ArkayMOO                mango.arkay.net         7777
2
DSRMOO                  (private)               7777
2
ATHEMOO                 moo.hawaii.edu          9999
2
Rupert                  directfx.com            9040
2
ImagiMOO                (private)               7777
2
Shadowed Realities      journey.wskcc.com       1234
2
ReligionMOO             mango.arkay.net         4444
2
CyberMOO                ham.nws.net             5050
2
Walden3 MOO             kauila.k12.hi.us        7777
2
PolyMOO                 private                 8888
2
Area51                  private                 7777
2
MOOphoria               directfx.com            8888
2
CampCanDoMOO            laroche.edu             4444
2
QuakeMUD                qmud.warzone.com        3000
2
SagacityMOO             saga.umkc.edu           4444
2
RainbowMOO              saga.umkc.edu           7777
2
Voyages of the 
2
  Space Barrel          forum2.org              7777
2
SecondChanceMOO         131.238.71.105          4444
2
*** Now included in the enCore educational MOO core database.
2

2
What ppl are saying about SRP:
2
Angus (Wizard@BayMOO) : "This thing rocks. I would not be
2
                         surprised if SR becomes as famous 
2
                         as Pavel."
2
Rk (Bum... er um Wizard@BayMOO) : "SRP is cool"
2
HAk (Super Kiss-Upper) : "yeah...but i've never ported 
2
                          without SR porter (the best porter
2
                          around =+)"
2
Doran (Wizard@MountainMOO) : 
2
                        Someone says, "tf rules for porting.. "
2
                        Doran says, "No, SR rules for porting ;)"
2
----------------------------------------------------------------------
2

2
Where to get the code:
2
http://www.geocities.com/SiliconValley/5789/moos.html
2
or
2
http://www.geocities.com/SiliconValley/5789/srpv45.txt
2
The actual objects are also available at MountainMOO and BayMOO, and
2
are owned by Shadow_RAM.  
2

2
Legalise :
2
Portions of this object contain code from the LambdaMOO Core Database.
2
With the exception of the aforementioned code, all code contained on this 
2
object is Copyright (C) 1995-1997 by Michael Thompson.   SR Porter is
2
distributed under the GNU Public License version 2.  The full version of 
2
the license is available at 
2
http://www.geocities.com/SiliconValley/5789/srp-gnu.txt
2

2
I would also appreciate it if you emailed me a notice tell me where you
2
have installed the SR Porter for my records. (This way I can keep 
2
tabs/inform ppl of new releases)
2
5
4
0
36
1
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
SR Porter v4.6
2
SRP
2
5
4
2
2
It's a small box with a bunch of buttons on one side, and a thin-wire 
2
ethernet BNC jack on the back.
2
5
4
2
0
64826
0
1001876098
36
1
5
2
5
5
2
5
#128
Generic Channel

144
2
120
-1
129
138
-1
126
11
ok_listen
2
173
-1
add_listener
2
173
-1
remove_listener
2
173
-1
@xac*cept
2
153
1
add_talker
2
173
-1
remove_talker
2
173
-1
@xre*ject
2
153
1
transmit
2
165
-1
listeners
2
173
-1
list_online
2
173
-1
init_for_core
2
173
-1
7
comm_fo
accept
reject
c_lis
c_talk
restricted
help_msg
53
1
129
2
5
4
0
2
0
4
0
2
0
4
0
2
0
4
0
2
0
0
0
2
5
4
6
2
The channels provide IRC-like chat features in the MOO. You can create
2
new channels by typing:
2

2
@create $channel named <whatever>
2

2
In order to use a channel people must have the $channel_FO (See help $channel_fo
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
<H3><CENTER>Welcome to the enCore Web Interface</CENTER></H3><HR SIZE=1 NOSHADE>
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Generic Channel
2
5
5
2
5
4
2
0
6512
0
1001876098
36
1
5
2
5
5
2
5
#129
Channel Communication FO

0
2
120
-1
130
74
-1
130
14
@xs*witch
2
28
-1
match_channel
2
173
-1
remove_talker_all
2
173
-1
remove_listener_all
2
173
-1
@xshout
2
93
-2
@xco*nnect
2
29
-1
@xdi*sconnect
2
29
-1
x xm xmit @xm
2
93
-2
x:* xemote
2
85
-2
c_channel
2
173
-1
hello
2
165
-1
@xwho
2
85
-2
@xchannels
2
5
-1
init_for_core
2
173
-1
1
gen_chan
65
1
128
2
5
5
36
1
4
14
2
With this feature you can chat with people on IRC-like channels in the MOO.
2

2
Available commands are:
2

2
x <text>               to say something on the channel you are listening to
2
x:<emote>              to emote something on the channel
2

2
@xchannels             to see what channels are available
2
@xconnect <Channel>    to connect to a channel
2
@xswitch <Channel>     to switch to a new channel
2
@xdisconnect <Channel> to disconnect from a channel
2
@xwho                  to see who is listening on the same channel as you
2
@xshout <text>         to shout on all channels (wiz only)
2

2
5
4
0
36
1
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Channel Communication FO
2
5
2
Type help $channel_FO for information on how to use this object.
2
5
4
2
0
12416
0
1001876098
36
1
5
2
5
5
2
5
#130
Account Administration FO

0
2
120
-1
131
74
-1
-1
12
@account-adm*inistration
2
13
-1
feature_ok
2
173
-1
find_unconnecteds
2
173
-1
trust
2
173
-1
is_protected
2
173
-1
find_idlers
2
173
-1
idle_check
2
173
-1
protect
2
173
-1
report
2
173
-1
_send_mail
2
173
-1
_purge
2
173
-1
init_for_core
2
173
-1
7
protected
purge_queue
idle_threshold
unconnected_threshold
lastrun
goodbye_mail
purge_warning
71
4
4
1
2
1
36
1
38
1
71
2
5
4
0
2
5
0
15638400
2
5
0
7776000
2
5
0
0
2
5
4
6
2
Dear user:
2

2
Your MOO account has been closed due to inactivity. You may reapply for a new account any time by logging on as a guest and type '@request character_name for email_address.' Thank you for your participation in our MOO.
2

2
Regards,
2
The MOO administrators
2
5
4
6
2
Dear user:
2
 
2
Our latest census shows that you have not visited our MOO in the last 6 months. If you plan to continue using your account here please log on to reactivate it. Thanks for your interest in our MOO. We hope to see you online again soon.
2

2
Regards
2
The MOO Administrators
2
5
5
36
1
2
Feature for administration of user account
2
5
4
12
2
@account-administration
2
feature_ok
2
trust
2
protect
2
_purge
2
is_protected
2
idle_check
2
report
2
find_unconnecteds
2
find_idlers
2
useless_check
2
_send_mail
36
1
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Purging FO
2
5
2
A set of utilities for purging characters that are no longer in use.
2
5
4
2
0
13031
0
1001876098
36
1
5
2
5
5
2
5
#131
Generic Gopher slate

144
2
120
-1
134
5
-1
135
38
p*ick
2
157
4
reset
2
45
-1
pop back
2
93
-2
location_string
2
165
-1
stack
2
45
-1
busy
2
173
-1
match_choice
2
173
-1
jump goto
2
157
4
details
2
157
4
set_pointer
2
165
-1
do_pick
2
173
-1
remember
2
157
4
forget delete
2
157
4
look_self
2
173
-1
_tell_desc
2
173
-1
next prev*ious
2
157
4
initialize
2
173
-1
announce_op
2
173
-1
_place
2
173
-1
_textp
2
173
-1
r*ead
2
93
-2
lock unlock
2
45
-1
text
2
173
-1
update
2
45
-1
_mail_text
2
173
-1
show_results
2
173
-1
ignore watch
2
45
-1
show
2
109
1
_is_busy
2
173
-1
control
2
45
-1
release
2
45
-1
is_locked
2
173
-1
match_command
2
165
-1
work
2
129
0
mailme
2
93
-2
header
2
173
-1
offset
2
173
-1
init_for_core
2
173
-1
13
seen
length
help_msg
locked
ignoring
watching
controlled
work_with_msg
value
stack
busy
remembered
desclines
73
4
2
4
1
1
2
4
1
1
111
2
1
0
20
2
5
4
58
2
Moving around:
2
 pick <item> on slate
2
    select the given menu item (either a number or partial name).
2
    If it is a text item, it will show it to you.
2
 <number> on slate
2
    e.g., 12 on slate. You can omit `pick' when chosing items
2
    by their number.
2
 back slate [for n]
2
    go back up a level; with n supplied, goes back n levels
2
 reset slate
2
    reset slate to the default list of `remember'-ed nodes
2
 goto host [port [path]] on slate
2
    make a direct jump to a specified host. Please be careful --
2
    at the moment this slows everyone down if the host isn't valid.
2

2
Controlling noise:
2
 ignore slate
2
    stop listening when other people fiddle with the slate
2
 watch slate
2
    start watching while other people fiddle with the slate
2
 show slate to <player>
2
    show the contents of the slate to someone even if they're not watching
2

2
Modifying the `reset' list:
2
 remember [<item>] on slate
2
    adds item to the list you get when you `reset' slate
2
    will prompt you for title
2
 remember on slate
2
    remembers the current menu choice rather than any 
2
    particular item
2
 forget <item> on slate
2
    (Only when the slate is `reset')
2
    deletes the given item
2

2
In long menus and text:
2
 next [<n>] on slate
2
 prev [<n>] on slate
2
    move you forward/backward in the set of visible menu items.
2
    You can give a `number of pages' to move forward.
2
 read slate
2
    show you the entire contents of the slate
2
 read <item> on slate
2
    if <item> is a text menu, it will show the text without
2
    actually changing the state of the slate.
2

2
Miscellaneous:
2
 stack slate
2
    show stack, where `back' will go
2
 details <item> on slate
2
    show host, port number, and selection string for a given item.    
2
 mailme slate
2
    if you have a valid registration address: send mail with the
2
    slate contents to your email address.
2
 mailme <item> on slate
2
    this will mail you the <item>, if it is text.
2

2
When you first make a gopher slate, you will need to use `goto'
2
and then `remember' to set up the default list of nodes.
2
5
0
0
2
1
4
0
2
1
4
2
1
2
1
111
2
1
1
-1
2
1
2
%N %<starts> to work with %d.
2
5
4
3
2
1Library of Congress 		marvel.loc.gov	70
2
1University of Texas at Dallas		gopher.utdallas.edu	70
2
1Veronica Searches	1/Other Gopher and Information Servers/Veronica	gopher.tc.umn.edu	70
2
1
4
0
2
1
0
0
2
1
4
3
2
1Library of Congress 		marvel.loc.gov	70
2
1University of Texas at Dallas		gopher.utdallas.edu	70
2
1Veronica Searches	1/Other Gopher and Information Servers/Veronica	gopher.tc.umn.edu	70
2
1
4
3
2
1. Library of Congress  (menu)
2
2. University of Texas at Dallas (menu)
2
3. Veronica Searches (menu)
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
Generic Gopher Slate
2
ggs
2
5
4
3
2
A laptop size computer, with various controls on it.
2
Please remember to 'reset' <gopher-name> when you are done viewing.
2

2
5
4
2
0
35952
0
1001876098
36
1
5
2
5
5
2
5
#132
Webslate Utilities

0
2
-1
-1
-1
75
-1
-1
4
get
2
173
-1
get_now
2
165
-1
get_unparsed
2
173
-1
reset
2
173
-1
0
11
4
0
2
1
4
0
2
1
4
0
2
1
5
2
5
5
2
1
0
0
2
4
4
1
2
www browser
2
5
4
30
2
An interface to Gopher internet services.
2
Copyright (c) 1992,1993 Grump,JoeFeedback@LambdaMOO.
2

2
This object contains just the raw verbs for getting data from gopher servers and parsing the results. Look at #50122 (Generic Gopher Slate) for one example of a user interface. 
2

2
:get(site, port, selection)
2
  Get data from gopher server: returns a list of strings, or an error if it couldn't connect. Results are cached.
2

2
:get_now(site, port, selection)
2
  Used by $gopher:get. Arguments are the same: this actually gets the 
2
  data without checking the cache. (Don't call this, since the
2
  caching is important to reduce lag.)
2
  
2
:show_text(who, start, end, site, port, selection)
2
  Requires wiz-perms to call.
2
  like who:notify_lines($gopher:get(..node..)[start..end])
2

2
:clear_cache()
2
  Erase the gopher cache.
2

2
:parse(string)
2
  Takes a directory line as returned by $gopher:get, and return a list
2
  {host, port, selector, label}
2
   host, port, and selector are what you send to :get.
2
  label is a string, where the first character is the type code.
2

2
:type(char)
2
   returns the name of the gopher type indicated by the character, e.g.
2
   $gopher:type("I") => "image"
2

2
5
4
2
0
5554
0
1001876098
36
1
5
2
5
5
2
5
#133
Assignment Server

0
2
-1
-1
-1
126
-1
159
5
_html
2
173
-1
project_creation_html
2
173
-1
acceptable
2
173
-1
upload_html
2
173
-1
manage_html
2
173
-1
7
html_target
programmers_notes
author
filelimit
dirlimit
uploads_enabled
script_path
59
2
_blank
2
0
4
9
2
For further information, contact Matthew Beermann, aka WizTraveller, mbeerman@cse.unl.edu. All code Copyright (C) 2000, Jason Nolan and the University of Toronto.
2

2
:_HTML
2

2
This is the main verb which displays the listing of active projects. Each entry includes the project's title, course, designer, and links to admin_edit, admin_manage, and student_edit. Entries are sorted alphabetically by title. Projects are not included in this listing if they have been archived or removed from inside the assignment server. At the bottom is a button to create a new project.
2

2
:PROJECT_CREATION_HTML
2

2
This verb generates a basic form for creating a new project. Users may only do this if they are logged in and are authorized to do so (see the help on $wiz:@designer). They must also have sufficient quota to create a new object. The project creation form asks for the project's title, course, number of sections, and description. It creates the object, moves it into the assignment server, and returns a redirect to the first admin_edit page.
2
0
2
Matthew Beermann (mbeerman@cse.unl.edu)
2
5
0
716800
2
5
0
5120000
2
5
0
0
2
5
2
cgi-bin/vase/vase.cgi
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
Assignment Server
2
server
2
5
5
2
5
4
2
0
17645
0
1001876098
36
1
5
2
5
5
2
5
#134
Generic Public Writable Note

0
2
120
-1
135
9
-1
-1
2
write
2
153
4
init_for_core
2
173
-1
2
anonymous
help_msg
68
0
1
2
5
4
3
2
This is a simple 'chain-story like' object. To write on it use the commands 'write on object-name'. Text in the first command is where you insert the sentence(s) you wish to write, and object-name in both examples refer to the actual name of the object. For example, if we have a chain-story called book, a player can add text to it either by typing 'write this is my text on book', or, 'write on book'. If the player uses the second command, s/he will be prompted for input by the system. 
2

2
The owner of this object can decide if authors can be anonymous or if their names should be appended to the sentences they add. The command '@set object-name.anonymous to 0' will enable identification of authorship. By default the chain-story object is set up with anonymous authors.
2
5
5
2
5
5
2
4
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
Generic Chain Story
2
gcs
2
5
2
You see a big book with many blank pages. There is a pen nearby. Type 'write on name' to write.
2
5
4
2
0
3465
0
1001876098
36
1
5
2
5
5
2
5
#135
Generic Room Directory

0
2
120
-1
136
5
-1
136
2
look_self
2
45
-1
init_for_core
2
173
-1
2
page_length
help_msg
62
0
20
2
5
4
1
2
To use this directory simply put it in a room and it will automatically list all connected rooms and the exits leading to them. This directory is particularly useful if you have a map in a room with many exits, and there is not enough space on the map to mark them all. 
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
Generic Room Directory
2
grd
2
5
2
You see a blank room directory waiting to be dropped in a room and used.
2
5
4
2
0
3467
0
1001876098
36
1
5
2
5
5
2
5
#136
Generic Note Board

144
2
120
-1
-1
5
-1
-1
11
po*st
2
153
4
re*move
2
153
5
valid
2
173
-1
look_self
2
173
-1
private_msg restricted_msg post_msg opost_msg oremove_msg remove_msg
2
165
-1
create_note
2
173
-1
read
2
153
4
conf*igure
2
41
-1
check_status
2
45
-1
_html
2
173
-1
init_for_core
2
173
-1
10
help_msg
private
restricted
private_msg
restricted_msg
post_msg
opost_msg
remove_msg
oremove_msg
design
70
4
29
2
This note board will allow people to post 'new' or existing notes on it. New notes are created automatically when someone wants to post something, whereas existing notes must be held in order to be successfully posted. The note board has three configurations:
2

2
Private    : In private mode only the owner of the note board may post to it.
2
             Public posting in this mode is not allowed.
2
Restricted : In restricted mode only registered users, i.e. players, builders,
2
             programmers and wizards may post to the board. (Default)
2
Public     : In public mode anyone can post to the board. Note board owners 
2
             should know that in public mode, notes posted by guests are 
2
             created from the owner's own quota.
2

2
Commands for the Note Board:
2

2
- To view the contents of the note board, type:
2
   look board-name
2
   
2
- To read a note on the board, type:
2
   read <note number> on <board>
2
   
2
- To post an existing note to the board type:
2
   post note-name on <board name>
2
   
2
- To create a new note and post it on the board, type
2
   post new on <board name>, You will be prompted for subject and body text.    
2
   
2
- To remove a note from the board type:
2
   remove <note number> from <board name>
2
   
2
- To set the board to either of the three modes noted above, type
2
   configure <board-name>
2
5
0
0
2
5
0
1
2
5
2
Sorry, only the owner may post notes on this board.
2
5
2
Sorry, guests cannot post notes to this board.
2
5
2
You post a note on %i.
2
5
2
posts a note on %i.
2
5
2
You remove a note from %i.
2
5
2
removes a note from %i.
2
5
2
jan@LinguaMOO
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
noteboard.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Generic Note Board
2
5
2
A simple board for posting notes. Type 'help $note_board' for instructions.
2
5
4
2
0
14573
0
1001876098
36
1
5
2
5
5
2
5
#137
MCP 2.1

0
36
-1
-1
-1
1
-1
138
15
create_session
36
173
-1
destroy_session
36
173
-1
initialize_connection
2
173
-1
finalize_connection
2
173
-1
parse_version
2
173
-1
compare_version_range
2
173
-1
compare_version
2
173
-1
unparse_version
2
173
-1
session_for
2
173
-1
user_created user_connected user_reconnected
2
173
-1
user_disconnected user_client_disconnected
2
173
-1
do_out_of_band_command
2
173
-1
package_name match_package packges
36
173
-1
handles_package wait_for_package
36
173
-1
nominate_for_core
36
173
-1
7
parser
session
version
package
negotiate
registry
cord
13
1
143
36
5
1
144
36
1
4
2
0
2
0
1
36
5
1
140
2
1
1
141
2
1
1
145
36
5
1
142
36
1
0
0
36
4
4
1
2
MCP 2.1
36
5
5
36
5
4
2
0
7972
0
1001876098
36
1
5
36
5
5
36
5
#138
enCore Web Class

144
2
-1
-1
-1
1
128
-1
4
creation_form_html
2
173
-1
xpress_edit
2
173
-1
make_contextual_menu
2
173
-1
build*_page pre_assemble
2
173
-1
40
url_address
icon
table_border
table_width
icon_height
icon_width
icon_alignment
icon_border
banner
footer
web_bgcolor
web_background
web_text_color
web_link_color
web_alink_color
web_vlink_color
web_font
web_font_size
title_font_size
subtitle_font_size
title_font_color
title_table_bgcolor
table_bgcolor
title_table_background
fixed_width_font
fixed_width_font_size
table_vertical_alignment
default_doctype
doctype_frameset
external_stylesheet
use_external_stylesheet
underline_links
locked_icon
allow_override_styles
icon_size
hits
shared_owners
audio_url
Media_Content_Alignment
Text_Alignment
46
2

2
5
2

2
5
2
0
2
5
2
100%
2
5
2

2
5
2

2
5
2
BOTTOM
2
5
2
0
2
5
2
<H3><CENTER>Welcome to the enCore Web Interface</CENTER></H3><HR SIZE=1 NOSHADE>
2
1
2
<HR SIZE=1 NOSHADE><CENTER> Powered with High Wired enCore </CENTER>
2
1
2
White
2
1
2

2
1
2
Black
2
1
2
Blue
2
1
2
Red
2
1
2
Purple
2
1
2
Sans-Serif
2
1
2
14px
2
1
2
24px
2
1
2
18px
2
1
2
Black
2
1
2

2
1
2

2
1
2

2
1
2
Monospace
2
1
2
12px
2
1
2
TOP
2
1
2
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2
1
2
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN">
2
1
2

2
1
0
0
2
1
0
1
2
1
2
lock.gif
2
1
0
1
2
1
0
32
2
1
0
0
2
0
4
0
2
0
2

2
5
2
CENTER
2
5
2
LEFT
2
5
0
0
2
4
4
1
2
enCore Web Class
2
5
5
2
5
4
2
0
9901
0
1001876098
36
1
5
2
5
5
2
5
#139
generic message dispatch object

0
36
-1
-1
-1
1
140
143
3
parse_send_args
36
173
-1
parse_receive_args
36
173
-1
set_messages_in set_messages_out
36
173
-1
2
messages_in
messages_out
8
4
0
36
1
4
0
36
1
0
0
36
4
4
1
2
generic message dispatch object
36
5
5
36
5
4
2
0
4218
0
1001876098
36
1
5
36
5
5
36
5
#140
generic MCP package

0
36
-1
-1
-1
139
141
-1
9
set_version_range
36
165
-1
match_request
36
173
-1
initialize_connection
36
173
-1
message_name_to_verbname
36
173
-1
finalize_connection
36
173
-1
add_cord_type
36
173
-1
remove_cord_type
36
173
-1
send_*
36
173
-1
dispatch
2
173
-1
2
version_range
cord_types
10
4
2
2
1.0
2
1.0
36
1
4
0
36
1
5
36
1
5
36
1
0
0
36
4
4
1
2
generic MCP package
36
5
5
36
5
4
2
0
5184
0
1001876098
36
1
5
36
5
5
36
5
#141
mcp-negotiate

0
36
-1
-1
-1
140
-1
142
3
do_negotiation
36
173
-1
handle_can
36
173
-1
handle_end
36
173
-1
0
10
4
2
2
1.0
2
2.0
36
1
5
36
1
4
2
4
2
2
can
4
3
2
package
2
min-version
2
max-version
4
2
2
end
4
0
36
1
4
2
4
2
2
can
4
3
2
package
2
min-version
2
max-version
4
2
2
end
4
0
36
1
0
0
36
4
4
1
2
mcp-negotiate
36
5
5
36
5
4
2
0
2225
0
1001876098
36
1
5
36
5
5
36
5
#142
mcp-cord

0
36
-1
-1
-1
140
-1
-1
9
next_id
36
173
-1
cord_send
2
173
-1
cord_closed
2
173
-1
handle_
36
173
-1
handle_closed
36
173
-1
find_type
36
173
-1
send_open
36
173
-1
finalize_connection
2
173
-1
type_name
36
173
-1
2
next_id
cords
12
0
1
36
1
4
0
36
1
5
36
1
4
0
36
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
36
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
36
1
0
0
36
4
4
1
2
mcp-cord
36
5
5
36
5
4
2
0
5130
0
1001876098
36
1
5
36
5
5
36
5
#143
MCP 2.1 parser

0
36
-1
-1
-1
1
-1
144
6
parse_mcp_alist
36
173
-1
parse_mcp
36
173
-1
parse_mcp_continuation
36
173
-1
parse
36
173
-1
unparse
36
173
-1
next_datakey
36
173
-1
2
next_datakey
unquoted_string
8
0
9445
36
1
2
^[]a-zA-Z0-9-%~`!@#$^&()=+{}[|';?/><.,]+$
2
1
0
0
36
4
4
1
2
MCP 2.1 parser
36
5
5
36
5
4
2
0
5244
0
1001876098
36
1
5
36
5
5
36
5
#144
generic MCP 2.1 session

0
36
-1
-1
-1
1
-1
145
22
set_connection
36
165
-1
multiline_begin
36
173
-1
multiline_finish
36
173
-1
multiline_add_value
36
173
-1
do_out_of_band_command
2
173
-1
finish
36
173
-1
initialize_connection
36
173
-1
set_packages set_authentication_key set_phase
36
173
-1
add_package
36
173
-1
handles_package
36
173
-1
register_handlers
36
173
-1
dispatch
2
173
-1
send
2
173
-1
find_handler
36
173
-1
connection
36
173
-1
package_name
36
173
-1
message_fullname
36
173
-1
strip_prefix
36
173
-1
end_negotiation
36
173
-1
_add_package_waiter
36
173
-1
_signal_package_waiter
36
173
-1
wait_for_package
36
173
-1
7
connection
pending_multilines
packages
authentication_key
phase
package_waiters
message_handlers
13
1
-1
36
1
4
0
36
0
4
0
36
1
3
0
36
0
0
0
36
1
4
0
36
0
4
2
4
0
4
0
36
1
0
0
36
4
4
1
2
generic MCP 2.1 session
36
5
5
36
5
4
2
0
13162
0
1001876098
36
1
5
36
5
5
36
5
#145
MCP package registry

0
36
-1
-1
-1
1
-1
137
8
add_package
36
173
-1
remove_package
36
173
-1
match_package
36
173
-1
package_name
36
173
-1
packages
36
173
-1
init_for_module init_for_core
36
173
-1
nominate_for_core
36
173
-1
@add-package @remove-package
2
153
1
3
package_names
packages
core_package_names
9
4
2
2
mcp-negotiate
2
mcp-cord
36
1
4
2
1
141
1
142
36
1
4
2
2
mcp-negotiate
2
mcp-cord
36
1
0
0
36
4
4
1
2
MCP package registry
36
5
5
36
5
4
2
0
5281
0
1001876098
36
1
5
36
5
5
36
5
#146
enCore Web Application

0
2
-1
-1
-1
138
147
-1
2
blank_html
2
173
-1
get_frame_name
2
173
-1
14
frame_marginwidth
frame_marginheight
frameborder
browser_toolbar
browser_location
browser_directories
browser_status
browser_menubar
browser_scrollbars
browser_resizable
browser_copyhistory
browser_window_height
browser_window_width
frame_scrolling
60
0
4
2
5
0
4
2
5
0
1
2
5
2
no
2
1
2
no
2
1
2
no
2
1
2
yes
2
1
2
yes
2
1
2
yes
2
1
2
yes
2
1
2
yes
2
1
2
600
2
1
2
800
2
1
2
auto
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2

2
1
2

2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
2
encore_web_application.css
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
enCore Web Application
2
5
5
2
5
4
2
0
2889
0
1001876098
36
1
5
2
5
5
2
5
#147
enCore Xpress MOO Client

0
2
-1
-1
-1
146
-1
149
16
init_for_core
2
173
-1
vertical_layout horizontal_layout
2
173
-1
simple_layout
2
173
-1
quit_html
2
173
-1
menu_html
2
173
-1
check_moo_mail
2
173
-1
java_html
2
173
-1
web_html
2
173
-1
display_user_location
2
173
-1
character_application_html
2
173
-1
guest_preferences_html
2
173
-1
guide_html
2
173
-1
initialize_session
2
173
-1
Xpress_Confunc
2
173
-1
view_themes
2
173
-1
check_motd
2
173
-1
40
script_language
external_baseurl
sounds_folder
java_client_folder
java_client_archive
java_client_filename
javascript_functions
screen_layouts
mail_notification_sounds
sound_volumes
web_fonts
web_colors
web_font_sizes
java_fonts
HTTP_09_enabled
on
allow_internet_mail
java_font_sizes
java_client_width
java_client_height
java_client_alignment
java_client_font
java_client_font_size
window_close_timeout
validate_script
guest_names_and_manners
checkName
MOTD_Msg
MOTD_viewer_list
texts_folder
stylesheets_folder
themes_folder
icons_folder
system_images_folder
Screen_Sizes
Screen_Divisions
themes
icon_sizes
motd_header
web_alignment_options
100
2
JavaScript1.1
2
5
2
http://localhost/encore/
2
5
2
sounds/
2
5
2
mootcan/
2
5
2
mootcan-012.jar
2
5
2
MOOtcan.class
2
5
4
111
2
function _get_java_client_params(list_type) {
2
  var _params = "";
2
  if ("flat" == list_type) {
2
     for (var _pair in _java_client_param_pairs) {
2
       _params = _params + _java_client_param_pairs[_pair][0] + '="' + _java_client_param_pairs[_pair][1] + '" ';
2
     }
2
  }
2
  else {
2
     for (var _pair in _java_client_param_pairs) {
2
        _params = _params + '<PARAM NAME=' + _java_client_param_pairs[_pair][0] + ' VALUE="' + _java_client_param_pairs[_pair][1] + '">\n';
2
     }
2
  }
2
  return _params;
2
}
2

2
function _MRJAdvise() {
2
   // Version 4.X Netscape browsers for Mac does not support Java 1.1. Advise users to upgrade or install MRJ Plugin.
2
   parent.close();
2
   alert('Sorry, your browser is too old to run this system. We recommend that you either use a newer version, or that you download and install the MRJ Java Plug-In and MRJ version 2.2.1 or newer.');
2
window.open("http://lingua.utdallas.edu/encore/xpress_system_requirements.html");
2
   
2
}
2

2
function _javaTag() {
2
   if ((_isNetscape()) && (_javaPluginAvailable())) {
2
      return _EMBEDTag();
2
   }
2
   else {
2
      if ((_isNetscape()) && (_isMac())) {
2
         // _MRJAdvise();
2
      }
2
      return _APPLETTag();
2
   }
2
}
2

2
function _javaPluginAvailable() {
2
   var _mimetype = navigator.mimeTypes["application/x-java-vm"];
2
   if (_mimetype) {
2
      _plugin = _mimetype.enabledPlugin;
2
      return(null != _plugin);
2
   }
2
   else {
2
      return false;
2
   }
2
}
2

2
function _isNetscape() {
2
   return ("Netscape" == navigator.appName);
2
}
2

2
function _isMac() {
2
   return (-1 < navigator.appVersion.indexOf('Mac'));
2
}
2

2
function _getMaxSize() {
2
   if (_isNetscape()) {
2
      if (_javaPluginAvailable()) {
2
         var _marginWidth = 2;
2
         return [innerWidth-_marginWidth*2, innerHeight-_marginWidth*2];
2
      }
2
      else {
2
         return ["100%", "100%"];
2
      }
2
   }
2
   else { // we assume MSIE-style DOM-things
2
      return [myBody.clientWidth, myBody.clientHeight];
2
   }
2
}
2

2
function _getWidth() {
2
   return _getMaxSize()[0];
2
}
2

2
function _getHeight() {
2
   return _getMaxSize()[1];
2
}
2

2
function _EMBEDTag() {
2
   var _tag = 
2
   '<EMBED\n' +
2
   ' ' + _get_java_client_params('flat') + '\n' +
2
   ' TYPE="application/x-java-vm"\n' +
2
   ' NAME="MOOtcan"\n' +
2
   ' PLUGINSPAGE=' + _pluginspage + '\n' +
2
   ' BORDER="5"\n' +
2
   ' WIDTH=' + _getWidth() + '\n' +
2
   ' HEIGHT=' + _getHeight() + '\n' +
2
   ' ARCHIVE=' + _java_client_archive + '\n' +
2
   ' CODE=' + _java_client_classname + '\n' +
2
   ' CODEBASE=' + _java_client_codebase + '\n' +
2
   ' URL=' + _java_client_codebase + '>\n' +
2
   '</EMBED>\n';
2
   return _tag;
2
}
2

2
function _APPLETTag() {
2
   var _tag = 
2
   '<APPLET\n' +
2
   ' ARCHIVE=' + _java_client_archive + '\n' +
2
   ' CODE=' + _java_client_classname + '\n' +
2
   ' CODEBASE=' + _java_client_codebase + '\n' +
2
   ' WIDTH=' + _getWidth() + '\n' +
2
   ' HEIGHT=' + _getHeight() + '\n' +
2
   ' ALIGN=top\n' +
2
   ' ALT="">\n' + _get_java_client_params('tagged') + 
2
   '  Sorry, you need a browser that supports Java to use this system.\n' +
2
   '</APPLET>\n';
2
   return _tag;
2
}
2

2
document.write(_javaTag());
2
5
4
3
4
2
2
vertical_layout
2
Vertical Orientation
4
2
2
horizontal_layout
2
Horizontal Orientation
4
2
2
simple_layout
2
Chat Area Only
2
5
4
6
4
2
2
moomail.wav
2
Standard
4
2
2
mail.wav
2
Male Voice
4
2
2
elvira.wav
2
Female Voice
4
2
2
hal9000.wav
2
HAL 9000
4
2
2
jetson.wav
2
Jetson
4
2
2

2
Off
2
5
4
4
4
2
0
100
2
Loud
4
2
0
50
2
Normal
4
2
0
10
2
Low
4
2
0
0
2
Sound Off
2
5
4
6
2
Serif
2
Sans-serif
2
Cursive
2
Fantasy
2
System
2
Monospace
2
5
4
17
2
Aqua
2
Black
2
Blue
2
Fuchsia
2
Gray
2
Green
2
Lime
2
Maroon
2
Navy
2
Olive
2
Purple
2
Red
2
Silver
2
SlateGray
2
Teal
2
White
2
Yellow
2
5
4
8
2
9px
2
10px
2
12px
2
14px
2
16px
2
18px
2
24px
2
32px
2
5
4
5
2
System
2
Courier
2
Dialog
2
Helvetica
2
TimesRoman
2
5
0
1
2
5
0
1
2
5
0
0
2
5
4
6
0
9
0
10
0
11
0
12
0
13
0
14
2
5
2
100%
2
5
2
100%
2
5
2
top
2
5
2
Courier
2
5
2
10
2
5
2
3000
2
5
4
23
2
function validateForm(){
2
  if(document.application.real_name.value=="") {
2
    alert('You forgot to tell us your real name. Please try again!')
2
    document.application.real_name.focus()
2
    return false }
2
  
2
  if(document.application.name.value=="") {
2
    alert('In order for us to create a new character for you, we need to know what name you wish to use in the MOO.!')
2
    document.application.name.focus()
2
    return false }
2

2
  if(document.application.email_address.value=="") {
2
    alert('Unless we know your email address we cannot send you the password for your new character !')
2
    document.application.email_address.focus()
2
    return false }
2

2
  if(document.application.reason.value=="") {
2
    alert('We would like to know a little bit about why you wish to join this MOO. Please include a few lines about this in your application !')
2
    document.application.reason.focus()
2
    return false }
2

2
  return true
2
} 
2
5
4
1
2
In this MOO you can use your own name instead of an anonymous guest name. The name you choose must be unique, however. If it's being used by someone else, you must choose another name. Also, please note that names must not contain any open spaces.
2
5
4
8
2
function checkName(){
2
  if(document.pref.name.value=="") {
2
    alert('You did not specify a name. Please try again !');
2
    document.pref.name.focus();
2
    return false; 
2
  }
2
  return true;
2
} 
2
5
2

2
5
4
0
2
4
2
texts/
2
1
2
stylesheets/
2
1
2
images/themes/
2
1
2
images/icons/
2
1
2
images/system/
2
1
4
4
4
2
2
screen.availWidth,screen.availHeight
2
Full Screen
4
2
2
1024,768
2
1024 by 768 pixels
4
2
2
800,600
2
800 by 600 pixels
4
2
2
640,480
2
640 by 480 pixels
2
1
4
5
4
2
2
30%,70%
2
Talk Area 30%, Web Area 70%
4
2
2
40%,60%
2
Talk Area 40%, Web Area 60%
4
2
2
50%,50%
2
Talk Area 50%, Web Area 50%
4
2
2
60%,40%
2
Talk Area 60%, Web Area 40%
4
2
2
70%,30%
2
Talk Area 70%, Web Area 30%
2
1
4
10
4
2
2
Amber
2
amber
4
2
2
Azure
2
azure
4
2
2
Ice
2
ice
4
2
2
Lemon
2
lemon
4
2
2
Lime
2
lime
4
2
2
Magenta
2
magenta
4
2
2
Poppy
2
poppy
4
2
2
Sky
2
sky
4
2
2
Steel
2
steel
4
2
2
Tangerine
2
tangerine
2
1
4
5
4
2
2
Tiny
0
16
4
2
2
Small
0
24
4
2
2
Normal
0
32
4
2
2
Large
0
48
4
2
2
Extra Large
0
64
2
1
2
MESSAGE OF THE DAY:
2
5
4
4
4
2
2
Left
2
LEFT
4
2
2
Center
2
CENTER
4
2
2
Right
2
RIGHT
4
2
2
Justify
2
JUSTIFY
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
2
no
2
1
5
2
1
2
screen.availHeight
2
1
2
screen.availWidth
2
1
5
2
1
5
2
5
5
2
5
0
0
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
2
background-gray.jpg
2
1
2
black
2
1
2
blue
2
1
2
red
2
1
2
purple
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
0
0
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
enCore Xpress MOO Client
2
5
5
2
5
4
2
0
40246
0
1001876098
36
1
5
2
5
5
2
5
#148
MOOtcan Client Module

0
2
-1
-1
-1
161
-1
-1
1
standalone_html
2
173
-1
3
java_client_width
java_client_height
java_client_alignment
64
2
600
2
5
2
360
2
5
2
TOP
2
5
5
2
0
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
2
no
2
1
5
2
1
2
380
2
1
2
610
2
1
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
MOOtcan Client Module
2
5
5
2
5
4
2
0
3061
0
1001876098
36
1
5
2
5
5
2
5
#149
enCore Search Engine

0
2
-1
-1
-1
146
-1
150
1
search_html
2
173
-1
0
60
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
2
500
2
1
5
2
1
5
2
5
2

2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
enCore Search Engine
2
5
5
2
5
4
2
0
4021
0
1001876098
36
1
5
2
5
5
2
5
#150
MOO_info

0
2
-1
-1
-1
146
-1
152
1
about_html
2
173
-1
0
60
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
2
500
2
1
5
2
1
5
2
5
2

2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
MOO_info
2
5
5
2
5
4
2
0
3616
0
1001876098
36
1
5
2
5
5
2
5
#151
enCore Help

16
2
-1
-1
-1
30
-1
160
0
22
Introduction
Help_Browser
Who_Browser
MOO_Mailer
Searching
Mail_Editor
Quick_Editing
Object_Editor
Creating_Rooms
Exit_Message_Editing
Player_Message_Editing
Mailing_Lists
password
Xpress_Screen_Layout
Macintosh_Troubleshooting
Collaborative_Web_Browsing
Xpress_Navigator
Bookmarks
NoteBook
Logging
VASE
Inventory_Manager
30
4
3
2
enCore Xpress is a new web-based MOO client that brings a feature-rich, and graphically enhanced hypertextual interface to your MOO experience. It is a truly platform-independent system that brings together the power of MOO, HTML, Java and JavaScript in one easy to use application. enCore Xpress resides remotely on the MOO you are using, and you run it in your web browser, thus, there is nothing to download or install. Just point your web browser to the MOO's login page and you are on your way. With enCore Xpress the true creative power of MOO is fully unleashed in ways that will make your MOOing more fun and productive than ever before, and at an unsurpassed ease-of-use.
2

2
The enCore Xpress client window has three areas. On top is the menu area with buttons for various MOO applications such as the Help Browser, the Who Browser, the Mail Browser and Editor and so forth. This menu line is generated based on the player class you belong to. Thus, if you are a builder, you will see buttons for applications that are relevant to all players and builders, but not to programmers and wizards. To the left is the chat area where text that you and others type is displayed. This area has two separate fields, one where you type in text (bottom), and one where the MOO displays text. The area to the right of the enCore Xpress window is the hypertext output area. This is where the MOO will display descriptions of rooms, players, and other objects that you explore.
2
5
4
9
2
General Help Topics
2

2
With enCore Xpress it is easy to find the MOO help you need when you want it. With the easy to use help browser you can select the help topic you wish to explore, and point and click your way through the comprehensive MOO help database. A list of help topics relevant to you is displayed to the right in the help browser window.  Clicking on one of these help topics will show you a list of key words under that topic. Select the key word you want by clicking on it, and the associated text will be displayed in the bottom right area of the help browser window. 
2

2
You can also search for help by typing a key word in the search field in the top area of the help browser window. The results of your search will depend on whether the key word you entered is found in the relevant help databases. 
2

2
Contextual Help
2

2
Another great way to learn more about the many functions and features available to you in the MOO is to take advantage of the contextual help system. Many of the objects that you encounter as you wander around the MOO, for example, have detailed help texts associated with them. In enCore Xpress, a light bulb icon is used to indicate that further help is available. Click on the light bulbs to read these additional help texts.
2
5
4
3
2
The Who Browser will show you a listing of people currently connected to the MOO. It will also tell you how long they have been connected, how long it has been since they were last active, and where they are in the MOO. You can click on people's names to find out more about them, or you can join them by clicking on the room they are in (please ask to join them first!).
2

2
The character icons that you see in the Who Browser and elsewhere in the MOO denotes different player types. 
2
5
4
1
2
The enCore Xpress MOO mailer makes it easy to read and manage your MOO mail. The horizontal top area is the MOO mailer menu area. Whenever you wish to send a MOO mail to someone, click on the button called compose mail. See help on Mail Editor for more. The area to the left is where your mailbox or boxes appears. Unless you have subscribed to any of the MOO mailing lists, only your InBox will be visible. Clicking on any of the mailboxes will display their contents in the top right area. To read any of the messages listed, simply click on the subject of the desired message, and the text will appear in the bottom right area of the mail browser.
2
5
4
1
2
The Xpress Search engine may be used to search for objects and players in the MOO. To search for an object by name, simply type in your search string and click the 'search' button. Xpress Search will display all objects with names that matches your search string. You may also search for players either by character name, real name, or email address. Select the appropriate search criterion, and enter your search string. Note, if you only know part of a person's name, Xpress Search will show you all players who's name matches your search string. Please note that in a big MOO, a search may take a while so please be patient.
2
5
4
3
2
To send a MOO mail to someone, you must use his or her MOO name as the address. For example, if you wish to send a mail to Cynthia, you type the name 'Cynthia' in the TO field. You can send the same MOO mail message to several people by typing their names separated by blank spaces in the TO field (i.e., Cynthia Jan Heidi). 
2

2
You can also send MOO mail to mailing lists in the MOO. Use the name of the list preceded by an asterisk as address. For example: To send a MOO mail to a list called general, you must type *general in the TO field.
2
5
4
15
2
The Xpress editing option is a fast convenient way to edit one or more of the most important properties on an object. You get to it by clicking on the Objects icon in the main Xpress menu, or by clicking the Xpress edit icon (pen) in the contextual menu. Depending on the object you are working with, the Xpress Editor will display different options, but the following are common to all objects in the MOO.
2

2
Name and Aliases: Every object must have a name. In the case of player objects, the names must be unique so as to avoid confusion. An object can also have one or more aliases. These should follow the name of the object separated by commas.
2

2
Multi-media Content: All objects can have external multimedia content associated with them. This might be images, audio or video files, animations and so forth. You may connect two such resources to any object that you own. Audio content remains invisible and will play automatically whenever someone looks at the object. Visible content is shown in the Xpress web display area. If the size of the display area does not fit your image or embedded resource, you can change the height and width specifications. Leaving one or the other blank results in your browser sizing your image based on the size given. For embedded contents such as Flash animations or Quicktime files, both height and width must be specified. Please note that multi-media content requires users to have the appropriate plug-ins for handling that content installed in their browsers. For this reason, you should only use the most common file formats for your multi-media content to ensure that people will see it without having to download and install additional plugins. If the external resource is a web page, a link to it will be added to the description of the object.
2

2
Icon: Icons are used to make it easier for people to see what kind of objects they are dealing with. If you don't want to use the default icons, you can use icons of your own by providing a URL pointing to an external icon-file. 
2

2
Description: In a space where text is as important as in the MOO, the descriptions of objects are paramount to the whole MOO experience. You should therefore take your time to write creative and interesting descriptions for all your objects.
2

2
ASCII: ASCII drawings are optional but may be very helpful to people who are using MOO clients other than Xpress. If you have an image attached to an object and want Xpress-users to see only the image and not the ASCII graphics, you can do so by setting 'display ASCII graphics' to 'no'.
2

2
Appearance: You can control how your objects look like by editing the appearance of the object. The appearance options include font type size and color, link colors and background color or image. 
2

2
For help on object sharing see Xpress Object Sharing.
2
5
4
3
2
The enCore Xpress Object Editor makes it easy to assert your creative side. Click on the button called Create Object, and in the right area of the window you will see three categories of objects; Rooms, Basic Objects, and Educational Objects. These are so-called generic objects, or blueprints, that you can use as basis for your own objects. 
2

2
To create a new object, click on one of the categories of generic objects, select an object, give the new object a name, and click the Create button. It is as simple as that. See also help on creating rooms.
2
5
4
3
2
Rooms are a special kind of object, and the creation procedure is a little bit more involved than for other objects. The first room you create will not have any exits or entrances at all. It will simply not be connected to any other room in the MOO. Once you already have one room, however, the Creation Manager will give you the option to connect new rooms to your existing ones. If you want to do this when creating a new room, you must specify which room you wish to connect it to, and the names of an exit and an entrance between the two rooms.
2

2
At some point you should have one of your rooms connected to the rest of the MOO so others may find it more easily. For this you need to contact a MOO administrator and ask them about the best place to hook up with the rest of the MOOniverse.
2
5
4
24
2
Several kinds of messages can be set on an exit object. They are printed to various audiences at certain times whenever an attempt is made to go through the exit. Creative exit messages will certainly make your MOO creations more interesting for others to explore.
2

2
The complete set of name and pronoun variables you can use in exit messages is as follows:
2

2
Names:
2
  %n => the player
2
  %t => this object (i.e., the object issuing the message,... usually)
2
  %d => the direct object from the command line
2
  %i => the indirect object from the command line
2
  %l => the location of the player
2
Pronouns:
2
  %s => subject pronoun:          either `he',  `she', or `it'
2
  %o => object pronoun:           either `him', `her', or `it'
2
  %p => posessive pronoun (adj):  either `his', `her', or `its'  
2
  %q => posessive pronoun (noun): either `his', `hers', or `its'
2
  %r => reflexive pronoun:  either `himself', `herself', or `itself'
2

2
In addition there is a set of capitalized substitutions for use at the 
2
beginning of sentences.  These are, respectively, 
2

2
   %N, %T, %D, %I, %L for object names, 
2
   %S, %O, %P, %Q, %R for pronouns, and
2

2
Note: there is a special exception for player .name's which are assumed to already be capitalized as desired.
2
5
4
26
2
You can edit the messages that others see when, for example, you enter or leave a room, or when you page someone. Changing these messages will make them unique and interesting. In order to properly use names and pronouns in these messages, however, you need to use variables to represent them. 
2

2
For example: If you your name is Ann and you want to change your arrive message to read 'Ann comes surfing in on her floppy disk', you need to actually type '%N comes surfing in on %p floppy disk'.
2

2
The complete set of name and pronoun variables is as follows:
2

2
Names:
2
%n => the player
2
%t => this object (i.e., the object issuing the message,... usually)
2
%d => the direct object from the command line
2
%i => the indirect object from the command line
2
%l => the location of the player
2
Pronouns:
2
%s => subject pronoun: either `he', `she', or `it'
2
%o => object pronoun: either `him', `her', or `it'
2
%p => posessive pronoun (adj): either `his', `her', or `its' 
2
%q => posessive pronoun (noun): either `his', `hers', or `its'
2
%r => reflexive pronoun: either `himself', `herself', or `itself'
2

2
In addition there is a set of capitalized substitutions for use at the 
2
beginning of sentences. These are, respectively, 
2

2
%N, %T, %D, %I, %L for object names, 
2
%S, %O, %P, %Q, %R for pronouns, and
2

2
Note: there is a special exception for player names, which are assumed to already be capitalized as desired.
2
5
4
1
2
The MOO may have a number of mailing lists, or news groups, that you can subscribe to. Available lists in your MOO are listed under the Mail List button in the Xpress MOO Mailer. You subscribe to, and unsubscribe from, mailing lists simply by selecting and deselecting them from the set of available lists shown. Mail lists that you are subscribed to will show up as mail boxes in your Xpress MOO Mailer. 
2
5
4
7
2
For security reasons, you are required to type both your current (soon to be old) password and the new password you wish to use.
2

2
Your password is stored in an encrypted form in the MOO database; in principle, not even the wizards can tell what it is, though they can change it, of course.  We recommend that your password not be your name or a common word. In general, the best and most secure passwords are at least six characters long, with a combination of upper and lower case letters and numerals.
2

2
If you should forget your password, please contact one of the MOO administrators to get a new one.
2

2
Only the first 8 characters of a password are significant.
2
5
4
9
2
To suit your own preference, you can modify the way enCore Xpress presents information on your screen. There are four different screen sizes, three different layouts, and five different screen divisions to choose from. 
2

2
Vertical Orientation: This is the default layout. The talk and web area will appear side by side beneath the Xpress menu.
2

2
Horizontal Orientation: The web area will appear horizontally below the Xpress menu, with the talk area directly beneath it.
2

2
Simple Layout: Only the Xpress menu and the talk area will appear on your screen. When this layout is chosen, text that would normally appear in the web area will appear directly in the talk area instead.
2

2
Please note that layout changes will not take effect until you reconnect.
2
5
4
45
2
<B>enCore Xpress and the Macintosh</B>
2

2
Although the enCore Xpress system was designed specifically to work equally well on Windows, UNIX and Macintosh systems, there are, unfortunately, certain problems beyond our control that affect Macintosh users. These problems are primarily due to weak support for Java and Javascript in Netscape's and Microsoft's Macintosh browsers. Until these companies start to make Macintosh browsers of the same quality and capability as their PC browsers, Mac-users must follow the guidelines below to ensure proper Java performance.
2

2
<B>System Requirements</B>
2

2
The minimum requirements for using Xpress on a Macintosh is Netscape Communicator version 4.08 or newer, or Microsoft Internet Explorer version 4.5 or newer. Both browsers can be downloaded for free from:
2

2
	Netscape: http://home.netscape.com/computing/download/index.html
2
	Internet Explorer: http://www.microsoft.com/mac/ie/
2

2
<B>Macintosh Runtime for Java (MRJ)</B>
2

2
MRJ is Apple's own Java Applet execution system and it is the best system available on the Mac. The next thing the Mac-user should do, therefore, is to check whether MRJ version 2.1.1 or newer is installed on their system. If not, go to Apple's MRJ site below and download and install the latest version. 
2

2
   http://www.apple.com/java/
2

2
IMPORTANT: In order to enable Netscape to use MRJ instead of Netscape's own built-in Java execution system, you must *also* download and install the MRJ plugin as explained below.   
2
   
2
<B>How To Use MRJ with Netscape Communicator:</B>
2

2
Although Netscape Communicator will run Xpress without MRJ, long lines will not be wrapped properly, and non-US keyboard mappings will not work. This may cause inconvenience and confusion, and we recommend that you follow the steps below to alleviate the problem.
2

2
1) Point your browser to the following URL to download the Netscape MRJ plugin:
2

2
	ftp://ftp.mozilla.org/pub/OJI/MRJPlugin.sit.hqx
2
	
2
2) Your browser should automatically unpack the file "MRJPlugin.sit.hqx" as the folder "MRJPlugin. If not, unpack the archive with Stuffit Expander or a similar program.
2

2
3) Open the MRJPlugin folder and move the two files named MRJ Plugin and MRJPlugin.jar to the folder called Plug-ins inside your Netscape folder.
2

2
4) Open Preferences under the Netscape Edit menu and select Applications (option under the navigator heading in the left preference window), then click the OK button.
2

2
5) Quit Netscape Communicator, restart it, and log on to the MOO.
2
 
2

2
<B>How to use MRJ with Internet Explorer:</B>
2

2
Internet Explorer will not run Xpress without MRJ version 2.1.1 or newer, so first download and install MRJ from Apple's distribution site above.
2

2
1) In Internet Explorer, open Preferences under the Edit menu and click on the options called Java in the left hand preference window. 
2

2
2) Make sure the checkbox called Enable Java is selected, and choose Apple MRJ from the popup menu labeled 'Java Virtual Machine.' Finally click on the OK button.
2

2
3) Quit Internet Explorer, restart it, and log on to the MOO.
2
5
4
7
2
The enCore Xpress system makes it easy to share web pages with others, or surf the web collaboratively. There are two basic ways in which web pages can be shared.
2

2
The @URL command
2
Typing the command @url followed by a URL (i.e., http://lingua.utdallas.edu/) will display that web page on the screens of everyone who are in the same room as you are. If you want to share a web page with just one other person, who can be anywhere in the MOO, you simply add that person's name to the URL separated by a space. Please use this feature responsibly. You should let folks know that you are about to share a URL before typing the @URL command.
2

2
The Web Projector
2
This object lets you prepare a set of URLs that can be displayed at any time by anyone who use the Web Projector object. See help on the Web Projector object itself for more on how to set up web slides.
2
5
4
1
2
The Xpress Navigator is a place holder for miscellaneous useful applications such as Bookmarks, NoteBook, and Log. Click on the buttons in the Xpress Navigator menu to open an Xpress Navigator application. 
2
5
4
9
2
The Xpress bookmarks make it easy to quickly find your way around the MOO. There are two categories of bookmarks. 
2

2
The first category is for your personal bookmarks. You can add or remove any location (room) in the MOO to or from your list of personal bookmarks. 
2

2
The second category consists of bookmarks that the MOO administrators have set up for you. These bookmarks may be organized in one or more categories and provide convenient quick clicks to various resources in the MOO. 
2

2
To use a bookmark, simply click on a category to see the list of bookmarks in that category, then click the bookmark you want. If the bookmark leads to a room in the MOO, you will be transported there.
2

2
Please note: The Xpress bookmarks only work inside the MOO and should not be confused with the general bookmark feature in your web browser.
2
5
4
3
2
Your personal Xpress MOO Notebook is available under the Xpress button in the Xpress toolbar. You can use the notebook to keep a record of your MOO activities, or for keeping notes of all sorts. Your notebook can be saved either online in the MOO, or via email. These options are avalilable under the Notebook action menu.
2

2
IMPORTANT! You must always save any changes that you have made to your note book before closing it, or before selecting another Xpress application. If you forget to do this, your most recent changes will be lost!
2
5
4
9
2
Your personal Xpress MOO Log application is available under the Xpress button in the Xpress toolbar. To log, or record, a MOO session, open the Log application and select 'Start logging' from the action menu. You can stop logging at any time by selecting the 'Stop logging' action. 
2

2
Please note: If you forget to stop your log application, it will automatically stop when you log off, or get disconnected from the MOO. This means that if you wish to continue logging next time you connect, you must start the Log application again.
2

2
Logs usually take up a lot of space in the MOO, so when you are done with your recording you must download it via email (i.e., email it to yourself) or clear/erase it. These two options are available under the action menu. Or, if you prefer, you can also copy and paste the text of the log into a separate Word file in another application.
2

2
When you have saved your log off-line you can erase it to start a new one. 
2

2
IMPORTANT! Use the Log application responsibly. Always inform people that you are logging conversations with them, and never publish a log without the consent of all parties involved.
2
5
4
1
2
The Virtual Assignment Server Environment (VASE) is a complete online, MOO-based assignment system. With VASE, educators can easily set up assignments for their students to work on online. Students complete and hand in assignments  online, making this an ideal distance learning tool within the social learning space of the enCore MOO. To use VASE click on the Xpress button in the Xpress toolbar and then click on VASE in the Xpress Navigator menu that comes up.
2
5
4
7
2
The Xpress Inventory Manager gives you an overview of the objects you own and where they are. In addition, this application allows you to take or drop objects, edit objects and lock/unlock or recycle objects. Please take care so you don't recycle objects by accident as recycling cannot be undone!
2

2
The Inventory Manager also shows you how much building quota you have left. If you are an active builder in the MOO, you will sooner or later use up the initial quota that has been allotted to you. When this happens, you should take a careful look at the objects you own to see if you might free up quota by recycling objects you no longer use. Keep in mind that logs in particular can become quite large (both logs that you record with a recorder and the personal logging feature). If you have no objects to recycle, feel free to contact a MOO administrator and ask for more quota.
2

2
If you own more than a few objects, your list of objects will be broken down into several parts. Your newest objects will appear first. Jump to sections of older objects by selecting from the numbers in the quick access menu which will appear below the quota information.
2

2
Some objects cannot be viewed in Xpress. These objects have no links or icons associated with them, and must be manipulated via the command line in the talk window.
2
5
5
2
5
5
36
1
0
0
2
4
4
1
2
enCore Help
2
5
5
2
5
4
2
0
24848
0
1001876098
36
1
5
2
5
5
2
5
#152
Xpress Navigator

0
2
-1
-1
-1
146
-1
153
7
bookmarks_html
2
173
-1
main_html
2
173
-1
menu_html
2
173
-1
Notebook
2
173
-1
MOO-Log
2
173
-1
save_via_email
2
173
-1
add_bookmark remove_bookmark
2
173
-1
1
bookmarks
61
4
1
4
2
2
Xpress Quick Clicks
4
2
1
62
1
61
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
2
600
2
1
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Xpress MOO Navigator Module
2
5
5
2
5
4
2
0
16264
0
1001876098
36
1
5
2
5
5
2
5
#153
Help Browser

0
2
-1
-1
-1
146
-1
154
5
main_html
2
173
-1
menu_html
2
173
-1
help_databases_html
2
173
-1
help_contents_html
2
173
-1
view_html
2
173
-1
2
context_help_width
context_help_height
62
2
450
2
5
2
450
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
2
500
2
1
5
2
1
5
2
5
2
help.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Help Browser
2
5
5
2
5
4
2
0
9664
0
1001876098
36
1
5
2
5
5
2
5
#154
Who Browser

0
2
-1
-1
-1
146
-1
155
1
main_html
2
173
-1
1
move_alert
61
4
7
2
function confirmMove(){ 
2
    if (confirm('You are about to be moved to another location. Please remember that people come to the MOO for many different reasons, some to work or study, others to hang out and visit with friends. Please be considerate and first type @knock name of person you wish to join. Click OK to move now, or cancel to abort.')) {
2
   setTimeout('self.close()',3000);
2
		return true; 
2
   }
2
	return false;
2
}
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
bottom
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
2
BOTTOM
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Who Browser
2
5
5
2
5
4
2
0
5286
0
1001876098
36
1
5
2
5
5
2
5
#155
Xpress MOO Mailer

0
2
-1
-1
-1
146
-1
156
11
main_html
2
173
-1
menu_html
2
173
-1
folders_html
2
173
-1
contents_html
2
173
-1
view_html
2
173
-1
delete_mail_html
2
173
-1
compose
2
173
-1
send_mail
2
173
-1
sendmail
2
29
-1
subscribe
2
173
-1
permission_to_read
2
173
-1
1
messages_to_display
61
0
15
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
2
600
2
1
5
2
1
5
2
5
2
mail.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Xpress MOO Mailer
2
5
5
2
5
4
2
0
24776
0
1001876098
36
1
5
2
5
5
2
5
#156
Xpress Object Editor

0
2
-1
-1
-1
146
-1
157
37
main_html
2
173
-1
menu_html
2
173
-1
view_html
2
173
-1
recycle_object
2
173
-1
move_to_room
2
173
-1
list_objects_html
2
173
-1
classes_html
2
173
-1
create_html
2
173
-1
create_new_object
2
173
-1
make_exit
2
173
-1
init_for_core
2
173
-1
edit_sharing
2
173
-1
share_object
2
173
-1
editor_html
2
173
-1
save_property
2
173
-1
rename
2
173
-1
change_password
2
173
-1
set_property
2
173
-1
Edit_Name
2
173
-1
Edit_URL
2
173
-1
Edit_Icon
2
173
-1
Edit_Description
2
173
-1
Edit_ASCII
2
173
-1
Edit_appearance
2
173
-1
Edit_Note_Text
2
173
-1
Edit_Lecture_Text
2
173
-1
edit_password
2
173
-1
change_password
2
173
-1
Edit_Gender
2
173
-1
Edit_Player_Info
2
173
-1
Edit_theme
2
173
-1
Edit_Screen_Layout
2
173
-1
Edit_Java_Settings
2
173
-1
Edit_Sound_Settings
2
173
-1
Edit_Player_Messages
2
173
-1
Edit_Exit_Options
2
173
-1
Edit_Web_Slides
2
173
-1
2
generics
xpress_edit_icon
62
4
3
4
2
2
Rooms
4
4
1
3
1
99
1
98
1
111
4
2
2
Basic Objects
4
3
1
5
1
8
1
9
4
2
2
Educational Objects
4
7
1
115
1
100
1
101
1
113
1
108
1
109
1
136
2
5
2
xpress_edit.gif
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
2
600
2
1
2
800
2
1
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Xpress Object Editor
2
5
5
2
5
4
2
0
56960
0
1001876098
36
1
5
2
5
5
2
5
#157
Xpress Program Editor

0
2
-1
-1
-1
146
-1
158
23
main_html
2
173
-1
menu_html
2
173
-1
view_verbs
2
173
-1
create_new_verb
2
173
-1
edit_verb
2
173
-1
compiler_html
2
173
-1
cancel_html
2
173
-1
editor
2
173
-1
parse_args
2
173
-1
edit_object
2
173
-1
set_object
2
173
-1
list_objects
2
173
-1
view_properties
2
173
-1
delete_warning
2
173
-1
edit_property
2
173
-1
set_property
2
173
-1
save_property_value
2
173
-1
coerce
2
165
-1
coerce_list_string
2
173
-1
delete_property
2
173
-1
create_new_property
2
173
-1
delete_verb
2
173
-1
update_last_modified
2
173
-1
8
default_new_verb_name
default_verb_permissions
default_verb_arguments
programmers_manual
temporary_items
default_new_property_name
default_property_permissions
default_property_value
68
2
newVerb
2
5
2
rd
2
5
4
3
2
none
2
none
2
none
2
5
2
ProgrammersManual_toc.html
2
5
4
2
4
0
4
0
2
5
2
newProperty
2
5
2
rc
2
5
0
0
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Xpress Verb Editor
2
5
5
2
5
4
2
0
47836
0
1001876098
36
1
5
2
5
5
2
5
#158
Xpress Administration

0
2
-1
-1
-1
146
-1
161
29
main_html
2
173
-1
menu_html
2
173
-1
view_html
2
173
-1
create update delete
2
173
-1
do_purge
2
173
-1
make_guest
2
173
-1
make_player
2
173
-1
update_player
2
173
-1
find_player
2
173
-1
update_owner
2
173
-1
mail_report
2
173
-1
batch_make_player
2
173
-1
edit_xpress_bookmarks_html
2
173
-1
create_bookmark update_bookmark delete_bookmark
2
173
-1
edit_motd_html
2
173
-1
edit_generics_html
2
173
-1
create_generics update_generics delete_generics
2
173
-1
core_settings
2
173
-1
save_settings
2
173
-1
Edit_Network
2
173
-1
edit_user_system_settings
2
173
-1
Edit_Telnet_Welcome_Screen
2
173
-1
Edit_Xpress_Settings
2
173
-1
Edit_Xpress_login
2
173
-1
Edit_Xpress_User_Settings
2
173
-1
Edit_Xpress_Object_Layout
2
173
-1
Edit_Xpress_Application_Layout
2
173
-1
Layout_Form
2
173
-1
edit_login
2
173
-1
0
60
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
2
600
2
1
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
enCore MOO Administration Module
2
5
5
2
5
4
2
0
55430
0
1001876098
36
1
5
2
5
5
2
5
#159
Generic Assignment

0
2
-1
-1
-1
126
-1
-1
12
admin_edit_html
2
173
-1
find_insert
2
173
-1
admin_edit2_html
2
173
-1
admin_edit3_html
2
173
-1
_html
2
173
-1
header_info
2
173
-1
student_edit_html
2
173
-1
view_html
2
173
-1
admin_manage_html
2
173
-1
admin_edit_help_html admin_edit2_help_html admin_edit3_help_html admin_manage_help_html student_edit_help_html evaluation_help_html evaluation2_help_html this none this
2
173
-1
evaluation_html
2
173
-1
notify_readable notify_handin notify_evaluation
2
173
-1
31
title
subtitle
course
designer
designer_email
desc
criteria
references
bibliography
url
admin_edit_layout
show_designer_realname
show_designer_mooname
show_participant_realname
show_participant_mooname
numsections
logo
admin_edit2_layout
format
directions
students
handed_in
archived
html_target
programmers_notes
handed_in_times
author
readable
evaluations
public
notifications
83
2

2
5
2

2
5
2

2
5
2

2
5
2

2
5
4
0
2
5
4
0
2
5
4
0
2
5
4
0
2
5
2

2
5
4
124
2
<SCRIPT LANGUAGE="JavaScript">
2
<!--
2

2
function verifyNum(what) {
2
  if(isNaN(parseInt(what.value, 10))) {
2
               alert("The value of this field must be a number.");
2
               what.value = what.defaultValue;
2
             what.focus();
2
       } else if(what.value == "0") {
2
            alert("You must have at least one section in your project.");
2
             what.value = what.defaultValue;
2
             what.focus();
2
       }
2
}
2

2
// -->
2
</SCRIPT>
2

2
<CENTER><H1>Achieve Project Editor</H1>
2
<FORM METHOD="POST" ENCTYPE="multipart/form-data">
2
<INPUT TYPE="BUTTON" VALUE="Show Available Help" onClick="javascript:window.open('admin_edit_help.html', 'help', 'directories=no,location=no,menubar=no,personalbar=no,resizable=yes,scrollbars=yes,status=no,toolbar=no')">
2
<H2>General Information</H2>
2

2
<TABLE CELLPADDING=5 BORDER=1>
2

2
<TR>
2
<TD>Project Title</TD>
2
<TD><INPUT TYPE="TEXT" SIZE=60 NAME="title"></TD>
2
</TR>
2

2
<TR>
2
<TD>Project Subtitle</TD>
2
<TD><INPUT TYPE="TEXT" SIZE=60 NAME="subtitle"></TD>
2
</TR>
2

2
<TR>
2
<TD>Course Designation</TD>
2
<TD><INPUT TYPE="TEXT" SIZE=60 NAME="course"></TD>
2
</TR>
2

2
<TR>
2
<TD>Project Designer</TD>
2
<TD><INPUT TYPE="TEXT" SIZE=60 NAME="designer"></TD>
2
</TR>
2

2
<TR>
2
<TD>Designer's Email</TD>
2
<TD><INPUT TYPE="TEXT" SIZE=60 NAME="designer_email"></TD>
2
</TR>
2

2
<TR>
2
<TD>Project Description</TD>
2
<TD><TEXTAREA COLS=60 ROWS=10 WRAP="soft" NAME="desc">
2
</TEXTAREA></TD>
2
</TR>
2

2
<TR>
2
<TD>Evaluation Criteria</TD>
2
<TD><TEXTAREA COLS=60 ROWS=10 WRAP="soft" NAME="criteria">
2
</TEXTAREA></TD>
2
</TR>
2
<TR>
2
<TD>Suggested Online References</TD>
2
<TD><TEXTAREA COLS=60 ROWS=10 WRAP="soft" NAME="references">
2
</TEXTAREA></TD>
2
</TR>
2

2
<TR>
2
<TD>Suggested Bibliography</TD>
2
<TD><TEXTAREA COLS=60 ROWS=10 WRAP="soft" NAME="bibliography">
2
</TEXTAREA></TD>
2
</TR>
2

2
<TR>
2
<TD>Further Information URL</TD>
2
<TD><INPUT TYPE="TEXT" SIZE=60 NAME="url"></TD>
2
</TR>
2

2
</TABLE>
2

2
<H2>Appearance</H2>
2

2
<TABLE CELLPADDING=5 BORDER=1>
2

2
<TR>
2
<TD>Logo URL</TD>
2
<TD><INPUT TYPE="TEXT" SIZE=60 NAME="logo"></TD>
2
</TR>
2

2
<TR>
2
<TD>Background Color</TD>
2
<TD><INPUT TYPE="TEXT" SIZE=30 NAME="web_bgcolor">
2
<FONT SIZE=2><A HREF="http://developer.netscape.com/docs/manuals/htmlguid/colortab.htm" TARGET="_blank">(Table of Color Values)</A></FONT></TD>
2
</TR>
2

2
<TR>
2
<TD>Text Color</TD>
2
<TD><INPUT TYPE="TEXT" SIZE=30 NAME="web_text_color"></TD>
2
</TR>
2

2
<TR>
2
<TD>Link Color</TD>
2
<TD><INPUT TYPE="TEXT" SIZE=30 NAME="web_link_color"></TD>
2
</TR>
2

2
<TR>
2
<TD>Background Image URL</TD>
2
<TD><INPUT TYPE="TEXT" SIZE=60 NAME="web_background"><BR>
2
<FONT SIZE=2>Note: This property overrides the background color.</FONT/77></TD>
2
</TR>
2

2
<TR>
2
<TD>&nbsp;</TD>
2
<TD><INPUT TYPE="CHECKBOX" NAME="show_designer_realname"> Show Designer's Name<BR>
2
<INPUT TYPE="CHECKBOX" NAME="show_designer_mooname"> Show Designer's MOO Account<BR>
2
<INPUT TYPE="CHECKBOX" NAME="show_participant_realname"> Show Participant's Name<BR>
2
<INPUT TYPE="CHECKBOX" NAME="show_participant_mooname"> Show Participant's MOO Account<BR>
2
<INPUT TYPE="TEXT" SIZE=2 onBlur="javascript:verifyNum(this);" NAME="numsections"> Number of sections in this project</TD>
2
</TR>
2

2
</TABLE>
2
<P></P>
2
<INPUT TYPE="BUTTON" VALUE="<<< Back" onClick="javascript:history.go(-1);"><INPUT TYPE="RESET" VALUE="Revert to Saved"><INPUT TYPE="SUBMIT" VALUE="Save and Continue >>>"></CENTER>
2
</FORM>
2
0
0
1
2
5
0
1
2
5
0
1
2
5
0
1
2
5
2
1
2
5
2

2
5
4
12
2
<SELECT>
2
<OPTION>Choose one</OPTION>
2
<OPTION VALUE="title">Title</OPTION>
2
<OPTION VALUE="subtitle">Subtitle</OPTION>
2
<OPTION VALUE="paragraph">Paragraph</OPTION>
2
<OPTION VALUE="inset_paragraph">Inset Paragraph</OPTION>
2
<OPTION VALUE="bulleted_list">Bulleted List</OPTION>
2
<OPTION VALUE="numbered_list">Numbered List</OPTION>
2
<OPTION VALUE="url">URL</OPTION>
2
<OPTION VALUE="image">Image</OPTION>
2
<OPTION VALUE="audio">Audio</OPTION>
2
</SELECT>
2
0
4
0
2
1
4
0
2
1
4
0
2
1
4
0
2
0
0
0
2
5
2
_blank
2
0
4
63
2
For further information, contact Matthew Beermann, aka WizTraveller, mbeerman@cse.unl.edu. All code Copyright (C) 2000, Jason Nolan and the University of Toronto.
2

2
GENERAL NOTES
2

2
When the system sees ".html" in an address, it replaces it with "_html" before searching for the relevant verb. Addresses of the form /objnum/ can be accessed without the user logging in; addresses ending in /objnum/verbname require the user to log in first.
2

2
Assignments are not fertile, and must be created through the Virtual Assignment Server. When viewed, they spawn into a new window. All HTML verbs are called with the arguments {user, {vars, vals}}, where vars and vals are parallel lists of variable/value keys as submitted by the browser. Multi-lined textarea input comes as a list, any empty input as the empty list, and all else as strings.
2

2
:_HTML
2

2
This is the main project page, generated for addresses ending in /objnum/. It displays the following information: Description, Criteria, Online References (as a bulleted list), Bibliography (as a bulleted list), Further Information URL. If the system notices webpage addresses in the text, it links them using $encore_web_utils:detect_urls. If students have begun handing in projects, they will be listed as links towards the bottom. A link at the bottom is also provided to the student edit page, and if viewed by a logged in designer, links to admin edit and admin manage.
2

2
:ADMIN_EDIT_HTML
2

2
This is the first page of the admin editing process. It allows users to edit the value of the following properties: title, subtitle, course, designer, designer_email, desc, criteria, references, bibliography, url, logo, web_bgcolor, web_text_color, web_background, show_designer_realname, show_designer_mooname, show_participant_realname, show_participant_mooname, and numsections. All of these are defined on $assignment except for web_*.
2

2
When loaded, this verb cycles through the text in .admin_edit_layout and substitutes in the values listed above. When a form is submitted, it saves each value back to its property, first coercing them into the proper datatype. Text inputs are strings, Textarea inputs are lists of strings, and Checkbox inputs are booleans. Note that numsections is saved as a string, despite what the name might suggest.
2

2
Designers can access this page at any time. Changing the title also @renames the project. After saving, it generates a redirect to the next page.
2

2
:ADMIN_EDIT2_HTML
2

2
This second page is used to lay out assignments. It generates sections of eight item selection boxes each, up to the number specified in .numsections. It uses .admin_edit2_layout as the template for each of these selection boxes, and gives them each an (unused) name so that they will be submitted properly.
2

2
The results of this form are saved as an 8xN array in the .format property, where each item is a string representing a datatype. This page is locked out once students have started working on the project (the value of .students is non-empty). After saving, it generates a redirect to the next page.
2

2
:ADMIN_EDIT3_HTML
2

2
This page is used to edit the directions for each seciton. It generates a number of textareas, corresponding to the length of the .format property. This information is stored as a list of strings to the .directions property. After saving, it generates a redirect back to the main project index.
2

2
:ADMIN_MANAGE_HTML
2

2
This page contains various management utilities. It generates two lists side by side, one for undoing a user's handing in, and another to delete a user's data entirely. For security reasons the un-hand in items are linked to that user's data, but the delete user items are not.
2

2
Also available from this page are archive project and delete project. Archiving a project raises the .archived flag and makes a project uneditable, as well as removing it from the virtual assignment server listing. The delete project button will recycle the project entirely.
2

2
After executing one or more of these functions, the verb returns a redirect back to the main virtual assignment server index.
2

2
:STUDENT_EDIT_HTML
2

2
This is the main student project editing page. It cannot be accessed until the project has a format stored, if the project has been handed it, or if the project has been archived.
2

2
The verb cycles through the datatypes listed in .format, creating the appropriate sort of input box in each table row. The relevant entry in .directions is listed in its own box at the top of each section. Currently saved values are then pasted into each input. Additionally, there are text entry boxes at the very top for the participant's real name and email, regardless of whether these will eventually be displayed.
2

2
When the participant saves eir data, it's stored in the format {participant, participant_email, {data}}, where data is a precise match to .format. This information is put in assignment.#objnum, where objnum is the user's object number. If necessary, this property is created (-rc) and the object number is appended to .students. Additionally, if the user saved using the Hand In option, their object number is appended to .handed_in and the time() to .handed_in_times.
2

2
After saving, this project returns a redirect to the viewing page.
2

2
:VIEW_HTML
2

2
This is the main page for viewing what information a student has entered in its laid out form. Only the student may view this page before it is handed in. It respects the .show_participant_mooname and .show_participant_realname values.
2

2
:*_HELP_HTML
2

2
This verb retrieves the relevant help file from the server using FileIO and returns it. It is called by the assorted Show Available Help buttons on the editing pages.
2

2
:HEADER_INFO
2

2
Returns a generic header that is used on multiple pages. This includes: logo, title, subtitle, course, designer, designer email, and designer MOOname.
2

2
:FIND_INSERT
2

2
This verb searches through a page of HTML data and searches for a given string. When found, it inserts a piece of data as a VALUE= parameter, unless told otherwise, such as with CHECKED or SELECTED. Case sensitive. See the documentation at the beginning of the verb for precise arguments.
2
0
4
0
2
0
2
Matthew Beermann (mbeerman@cse.unl.edu)
2
5
4
0
2
0
4
0
2
0
0
1
2
5
0
1
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
2
2
Generic Assignment
2
assignment
2
5
5
2
5
4
2
0
49003
0
1001876098
36
1
5
2
5
5
2
5
#160
Assignment Help

16
2
-1
-1
-1
30
-1
-1
0
7
admin_edit_help_html
admin_edit2_help_html
admin_edit3_help_html
admin_manage_help_html
student_edit_help_html
evaluation_help_html
evaluation2_help_html
15
4
244
2
<!doctype html public "-//w3c//dtd html 4.0 
2
transitional//en">
2
<html>
2
<head>
2
   <meta http-equiv="Content-Type" 
2
content="text/html; charset=iso-8859-1">
2
   <meta name="Author" 
2
content="Matthew Beermann (mbeerman@inetnebr.com)">
2
   <meta 
2
name="GENERATOR" content="Mozilla/4.61 [en] (Win95; U) [Netscape]">
2
 
2
<title>Achieve Project Editor Help, Page 1</title>
2
</head>
2
<body 
2
text="#000000" bgcolor="#CCFFFF" link="#0000EE" vlink="#551A8B" 
2
alink="#FF0000">
2

2
<center>
2
<h1>
2
Achieve Project Editor Help<br>
2
Page 
2
1</h1></center>
2
This first page of the project editing process asks 
2
you for some of the
2
basic information about your project. This 
2
information is entirely optional
2
- you may change it at any time, and 
2
if you leave the field blank it will
2
be omitted from the final 
2
project. The following is a explanation of each
2
of the 
2
fields.
2
<br>
2
<hr WIDTH="100%">
2
<center>
2
<h2>
2
<b>General 
2
Information</b></h2></center>
2
<b>Title:</b> The title of your 
2
project. This will be used to identify
2
your project on the Virtual 
2
Assignment Server listing, and hence should
2
be something distinctive 
2
and recognizable.
2
<p><b>Subtitle:</b> The subtitle, if any, of your 
2
project.
2
<p><b>Course Designation:</b> If your project is associated 
2
with a particular
2
course, this field could include such information 
2
as the course title,
2
course number, and/or associated 
2
department.
2
<p><b>Project Designer:</b> Your real 
2
name.
2
<p><b>Designer's Email:</b> Your email address. If given, 
2
participants
2
will be able to click on your name to send you email. 
2
Note that all information
2
up to this point, as well as the project 
2
logo, will appear at the top of
2
each page associated with your 
2
project.
2
<p><b>Project Description:</b> This is the main area in 
2
which to describe
2
your project. It can be as long as you wish, and 
2
should include all the
2
information that participants will need in 
2
order to complete your project.
2
Also include in this area information 
2
such as due dates, percentage of
2
total grade, related projects, etc. 
2
This field (along with all other text
2
entry boxes in the project 
2
system) will wrap your text for you, so it's
2
only necessary to press 
2
&lt;Enter> at the end of an item or 
2
paragraph.
2
<br>&nbsp;
2
<center><table BORDER=10 CELLSPACING=0 
2
CELLPADDING=5 WIDTH="75%" 
2
BGCOLOR="#FFFF99" >
2
<tr>
2
<td>
2
<center><b>THE WIZ'S WORDS THE 
2
WIZE</b></center>
2
The very first line of your description will be 
2
listed next to your project
2
in the Virtual Assignment Listing, so you 
2
should try to make it a capsule
2
summary of the 
2
project.</td>
2
</tr>
2
</table></center>
2

2
<p><b>Evaluation Criteria:</b> 
2
In this section, discuss how (if at all)
2
the participants' work will 
2
be evaluated when they are finished.
2
<p><b>Suggested Online 
2
References:</b> List any webpages or other online
2
resources that 
2
participants should consult for aid in completing the project.
2
Each 
2
line in this field will appear as a bulleted list 
2
item.
2
<p><b>Suggested Bibliography:</b> List any other (offline) 
2
resources, including
2
books, magazines, people, or anything else you 
2
feel is relevant. Each line
2
in this field will appear as a bulleted 
2
list.
2
<p><b>Further Information URL:</b> If there is an external 
2
webpage which
2
contains further information information about your 
2
project, give its address
2
here.
2
<p>
2
<hr 
2
WIDTH="100%">
2
<center>
2
<h2>
2
<b>Appearance</b></h2></center>
2
This 
2
section contains assorted cosmetic information about how your 
2
project
2
should appear. Note that these changes will <i>not</i> affect 
2
the designer's
2
editing pages, only the final project 
2
pages.
2
<p><b>Logo URL:</b> The web address of an image you'd like to 
2
have used
2
as your project's logo. Sorry, but we can't host these 
2
files on our own
2
server, so you'll have to find somewhere to put them 
2
yourself.
2
<br>&nbsp;
2
<center><table BORDER=10 CELLSPACING=0 
2
CELLPADDING=5 WIDTH="75%" 
2
BGCOLOR="#FFFF99" >
2
<tr>
2
<td>
2
<center><b>THE WIZ'S WORDS TO THE 
2
WIZE</b></center>
2
Try to use a small, squarish image as your 
2
project's logo. A large image
2
may make the page's text look cramped 
2
on smaller monitors.</td>
2
</tr>
2
</table></center>
2

2
<p><b>Background 
2
Color:</b> Enter the color you'd like to use as your project's
2
background. 
2
Most everyday color names will work, such as Red, Green, and
2
Blue, 
2
and odder ones, such as Khaki or Papaya Whip. For a complete 
2
list,
2
click on the link next to the field. (There is an alternate, 
2
alphanumeric
2
method of specifying color names, hence "#000000" is 
2
black and "#FFFFFF"
2
is white.)
2
<p><b>Text Color:</b> The color that 
2
the text of your project will appear
2
in. Black is usually safest, but 
2
other combinations are possible, such
2
as white text on a black 
2
background. See the discussion on colors above.
2
<p><b>Background 
2
Image URL:</b> The web address of an image you'd like
2
used as your 
2
project's background. This image will be tiled, or repeated
2
over and 
2
over across the page. If given, it will override the 
2
background
2
color.
2
<p><b>Show Designer's Name/MOO Account:</b> These 
2
checkboxes control whether
2
your real name, MOO account name, both, or 
2
neither will be shown on your
2
project. If you do not wish 
2
participants to know who you are, uncheck both
2
of these.
2
<p><b>Show 
2
Particpant's Name/MOO Account:</b> These checkboxes control
2
whether 
2
you (or anyone else) will be able to view the real names/MOO 
2
account
2
names of participants who complete your 
2
projects.
2
<br>&nbsp;
2
<center><table BORDER=10 CELLSPACING=0 
2
CELLPADDING=5 WIDTH="75%" 
2
BGCOLOR="#FFFF99" >
2
<tr>
2
<td>
2
<center><b>THE WIZ'S WORDS TO THE 
2
WIZE</b></center>
2
If you uncheck all of these boxes, you will be 
2
creating a "double blind"
2
situation, useful for things like anonymous 
2
surveys. Please consult with
2
a wizard before creating such an 
2
assignment, as they require special handling
2
by the 
2
system.</td>
2
</tr>
2
</table></center>
2

2
<p><b>Number of Sections in 
2
Project:</b> This very important number controls
2
how many sections 
2
you would like to create for your project. You must have
2
at least one 
2
section. For a full description of sections and how to use
2
them, 
2
please see the next page.
2
<br>&nbsp;
2
<center><table BORDER=0 
2
CELLSPACING=0 CELLPADDING=0 COLS=3 WIDTH="100%" >
2
<tr>
2
<td></td>
2

2
<td 
2
ALIGN=CENTER>
2
<h2>
2
<b><a href="javascript:window.close();" 
2
onMouseOver="window.status='Close Help Window'; return true;" 
2
onMouseOut="window.status=window.defaultStatus; return 
2
true;">Close
2
Help Window</a></b></h2>
2
</td>
2

2
<td 
2
ALIGN=RIGHT>
2
<h2>
2
<b><a href="admin_edit2_help.html">Next 
2
Page</a></b></h2>
2
</td>
2
</tr>
2
</table></center>
2

2
</body>
2
</html>
2
5
4
309
2
<!doctype html public "-//w3c//dtd html 4.0 
2
transitional//en">
2
<html>
2
<head>
2
   <meta http-equiv="Content-Type" 
2
content="text/html; charset=iso-8859-1">
2
   <meta name="Author" 
2
content="Matthew Beermann (mbeerman@inetnebr.com)">
2
   <meta 
2
name="GENERATOR" content="Mozilla/4.61 [en] (Win95; U) [Netscape]">
2
 
2
<title>Admin Project Editor Help, Page 2</title>
2
</head>
2
<body 
2
text="#000000" bgcolor="#CCFFFF" link="#0000EE" vlink="#551A8B" 
2
alink="#FF0000">
2

2
<center>
2
<h1>
2
<b>Achieve Project Editor 
2
Help<br>
2
Page 2</b></h1></center>
2
The second page of the project 
2
editing process is where you lay out the
2
format of your assignment. 
2
Projects are split into an arbitrary number
2
as sections, as many as 
2
you may need. The following is an explanation of
2
different ways you 
2
might set up your project.
2
<br>
2
<hr WIDTH="100%">
2
<h3>
2
What is a 
2
section?</h3>
2
A section is... well, a section could be anything you 
2
want it to be. They
2
are simply arbitrary units to help you organize 
2
your project into coherent
2
parts. You can have as many sections in 
2
your project as you want. What
2
constitutes a section depends largely 
2
on the nature of your project, but
2
we have some examples below to 
2
help you out.
2
<h3>
2
What are all of these different 
2
options?</h3>
2

2
<center>
2
<h1>
2
This is a 
2
title.</h1></center>
2

2
<center>
2
<h2>
2
<i>This is a 
2
subtitle.</i></h2></center>
2
This is a paragraph. This is a paragraph. 
2
This is a paragraph. This is
2
a paragraph. This is a paragraph. This 
2
is a paragraph. This is a paragraph.
2
This is a paragraph. This is a 
2
paragraph. This is a paragraph. This is
2
a paragraph. This is a 
2
paragraph. This is a paragraph. This is a paragraph.
2
<ul>This is an 
2
inset paragraph. This is an inset paragraph. This is an
2
inset 
2
paragraph. This is an inset paragraph. This is an inset 
2
paragraph.
2
This is an inset paragraph. This is an inset paragraph. 
2
This is an inset
2
paragraph. This is an inset paragraph. This is an 
2
inset paragraph. This
2
is an inset paragraph</ul>
2

2
<ul>
2
<li>
2
This is a 
2
bulleted list item.</li>
2

2
<li>
2
This is a bulleted list 
2
item.</li>
2

2
<li>
2
This is a bulleted list 
2
item.</li>
2
</ul>
2

2
<ol>
2
<li>
2
This is a numbered list 
2
item.</li>
2

2
<li>
2
This is a numbered list item.</li>
2

2
<li>
2
This is a 
2
numbered list item.</li>
2
</ol>
2

2
<center><img 
2
SRC="http://achieve.utoronto.ca/images/achieve_column1-NEW.gif" 
2
height=128 width=113 align=CENTER>
2
(This is an 
2
image.)</center>
2

2
<p><b>Link:</b> <a 
2
href="http://achieve.utoronto.ca/">http://achieve.utoronto.ca/</a>
2
(This 
2
is a URL.)
2
<br>&nbsp;
2
<center><table BORDER=10 CELLSPACING=0 
2
CELLPADDING=5 WIDTH="75%" 
2
BGCOLOR="#FFFF99" >
2
<tr>
2
<td>
2
<center><b>THE WIZ'S WORDS TO THE 
2
WIZE</b></center>
2
If the participant types a web page address 
2
anywhere in the project, the
2
system will recognize it and make it 
2
into a clickable link. Hence, the
2
Link type is often unnecessary, 
2
unless you really want to set the 
2
link
2
apart.</td>
2
</tr>
2
</table></center>
2

2
<p>
2
<hr 
2
WIDTH="100%">
2
<h3>
2
So how does this layout thing work 
2
anyway?</h3>
2
Let's start with something simple. Say your project will 
2
allow participants
2
to write a standard, three-part essay paper. 
2
Regardless of what the paper
2
is about, the general outline might look 
2
something like this:
2
<p>Title
2
<p>Subtitle ("Introduction") + 
2
Paragraphs
2
<p>Subtitle + Paragraphs
2
<p>Subtitle + 
2
Paragraphs
2
<p>Subtitle + Paragraphs
2
<p>Subtitle ("Conclusion") + 
2
Paragraphs
2
<p>Subtitle ("Citations") + Numbered List
2
<p>Subtitle 
2
("Bibliography") + Bulleted List
2
<p>You could enter the above 
2
information directly into the project layout
2
page, and the result 
2
would be a picture perfect three-part essay paper.
2
However, this 
2
layout could be compressed a great deal more. The body paragraphs
2
of 
2
a formal essay look pretty much alike, don't they? Since each 
2
section
2
can hold up to eight different items, we could combine the 
2
middle three
2
sections into one:
2
<p>Subtitle + Paragraphs + Subtitle + 
2
Paragraphs + Subtitle + Paragraphs
2
<br>&nbsp;
2
<center><table 
2
BORDER=10 CELLSPACING=0 CELLPADDING=5 WIDTH="75%" 
2
BGCOLOR="#FFFF99" >
2
<tr>
2
<td>
2
<center><b>THE WIZ'S WORDS TO THE 
2
WIZE</b></center>
2
The Paragraph data type is really just an 
2
arbitrarily long block of text
2
- the participants can enter as many 
2
paragraphs into a "Paragraph" as 
2
they
2
wish.</td>
2
</tr>
2
</table></center>
2

2
<p>The key to designing a 
2
good project is first to envision your project,
2
and then to simplify 
2
and abstract it until it's a collection of these very
2
simple data 
2
types. Here are some more examples for you to work from and
2
to give 
2
you ideas. The first is an art gallery to present a series of 
2
images;
2
the second is a project which asks participants to search the 
2
web for particular
2
information; the third is a paper which includes 
2
lengthy block quotations;
2
and the last is a personal evaluation paper 
2
which asks participants to
2
generate and then rank their best 
2
qualities.
2
<p>
2
<hr WIDTH="100%">
2
<h3>
2
An Art 
2
Gallery</h3>
2
Title
2
<p>Image + Subtitle
2
<p>Image + Subtitle
2
<p>Image + 
2
Subtitle
2
<p>... etc ...
2
<h3>
2
A Web Scavenger 
2
Hunt</h3>
2
Title
2
<p>Subtitle (title of webpage) + Link (address of 
2
page) + Paragraph (description
2
of page)
2
<p>Subtitle (title of 
2
webpage) + Link (address of page) + Paragraph (description
2
of 
2
page)
2
<p>Subtitle (title of webpage) + Link (address of page) + 
2
Paragraph (description
2
of page)
2
<p>... etc ...
2
<h3>
2
A Paper with 
2
Block Quotes</h3>
2
Title
2
<p>Subtitle + Paragraphs + Inset Paragraph 
2
(block quote) + Paragraphs
2
<p>Subtitle + Paragraphs + Inset Paragraph 
2
(block quote) + Paragraphs
2
<p>Subtitle + Paragraphs + Inset Paragraph 
2
(block quote) + Paragraphs
2
<p>... etc ...
2
<h3>
2
A Personal Qualities 
2
Ranking</h3>
2
Title
2
<p>Paragraph (quality discussion) + Numbered 
2
List
2
<p>Paragraph (quality discussion) + Numbered List
2
<p>Paragraph 
2
(quality discussion) + Numbered List
2
<p>... etc ...
2
<p>
2
<hr 
2
WIDTH="100%">
2
<center><table BORDER=10 CELLSPACING=0 CELLPADDING=5 
2
WIDTH="75%" BGCOLOR="#FFFF99" >
2
<tr>
2
<td>
2
<center><b>THE WIZ'S WORDS 
2
TO THE WIZE</b></center>
2
Before you even start to enter a layout into 
2
the system, sit down with
2
a paper and pencil and sketch out an 
2
outline for your project. It makes
2
it much easier to see how many 
2
sections you will need and what types of
2
data you should 
2
use.</td>
2
</tr>
2
</table></center>
2

2
<p>The third and final page of the 
2
project editing process allows you to
2
enter instructions to your 
2
participants. More information can be found
2
on the next 
2
page.
2
<br>&nbsp;
2
<center><table BORDER=0 CELLSPACING=0 CELLPADDING=0 
2
COLS=3 WIDTH="100%" >
2
<tr>
2
<td ALIGN=LEFT>
2
<h2>
2
<b><a 
2
href="admin_edit_help.html">Previous Page</a></b></h2>
2
</td>
2

2
<td 
2
ALIGN=CENTER>
2
<h2>
2
<b><a href="javascript:window.close();" 
2
onMouseOver="window.status='Close Help Window'; return true;" 
2
onMouseOut="window.status=window.defaultStatus; return 
2
true;">Close
2
Help Window</a></b></h2>
2
</td>
2

2
<td 
2
ALIGN=RIGHT>
2
<h2>
2
<b><a href="admin_edit3_help.html">Next 
2
Page</a></b></h2>
2
</td>
2
</tr>
2
</table></center>
2

2
</body>
2
</html>
2
5
4
113
2
<!doctype html public "-//w3c//dtd html 4.0 
2
transitional//en">
2
<html>
2
<head>
2
   <meta http-equiv="Content-Type" 
2
content="text/html; charset=iso-8859-1">
2
   <meta name="Author" 
2
content="Matthew Beermann (mbeerman@inetnebr.com)">
2
   <meta 
2
name="GENERATOR" content="Mozilla/4.61 [en] (Win95; U) [Netscape]">
2
 
2
<title>Achieve Project Editor Help, Page 3</title>
2
</head>
2
<body 
2
text="#000000" bgcolor="#CCFFFF" link="#0000EE" vlink="#551A8B" 
2
alink="#FF0000">
2

2
<center>
2
<h1>
2
<b>Achieve Project Editor 
2
Help<br>
2
Page 3</b></h1></center>
2
The final page of the project 
2
editing process is where you enter the specific,
2
section by section 
2
directions to your participants.
2
<br>
2
<hr 
2
WIDTH="100%">
2
<center><table BORDER=10 CELLSPACING=0 CELLPADDING=5 
2
WIDTH="75%" BGCOLOR="#FFFF99" >
2
<tr>
2
<td>
2
<center><b>THE WIZ'S WORDS 
2
TO THE WIZE</b></center>
2
Once participants have started working on 
2
your project, you may <b>NOT</b>
2
change the project's layout. The 
2
system will skip straight from the first
2
editing page to this 
2
one.</td>
2
</tr>
2
</table></center>
2

2
<p>The directions that you enter 
2
on this page will appear to the participants
2
at the top of each 
2
section. Since this is the main information that they
2
will have in 
2
front of them when completing your project, it should be as
2
clear and 
2
complete as possible. If you want participants to enter exactly
2
what 
2
you specify into a given field ("Introduction", "Conclusion", or 
2
"Bibliography",
2
for instance), then say so.
2
<p>Ideally, participants 
2
should not have to consult you about the project,
2
but we suggest that 
2
you have your email address listed with the project
2
in case they need 
2
to contact you for further clarification. Also note that
2
these 
2
directions are optional; if you've explained a repeating 
2
section
2
once, you can if you wish leave subsequent directions 
2
blank.
2
<br>&nbsp;
2
<center><table BORDER=10 CELLSPACING=0 
2
CELLPADDING=5 WIDTH="75%" 
2
BGCOLOR="#FFFF99" >
2
<tr>
2
<td>
2
<center><b>THE WIZ'S WORDS TO THE 
2
WIZE</b></center>
2
When you press "Save and View" at the bottom of 
2
this page, you'll be taken
2
to the main index page for your new 
2
project. Make a careful note of this
2
page's address; it's the one you 
2
should give out to your project's 
2
participants.</td>
2
</tr>
2
</table></center>
2

2
<br>&nbsp;
2
<center><table 
2
BORDER=0 CELLSPACING=0 CELLPADDING=0 COLS=3 WIDTH="100%" >
2
<tr>
2
<td 
2
ALIGN=LEFT>
2
<h2>
2
<b><a href="admin_edit2_help.html">Previous 
2
Page</a></b></h2>
2
</td>
2

2
<td ALIGN=CENTER>
2
<h2>
2
<b><a 
2
href="javascript:window.close();" onMouseOver="window.status='Close 
2
Help Window'; return true;" 
2
onMouseOut="window.status=window.defaultStatus; return 
2
true;">Close
2
Help 
2
Window</a></b></h2>
2
</td>
2

2
<td></td>
2
</tr>
2
</table></center>
2

2
</body>
2
</html>
2
5
4
105
2
<!doctype html public "-//w3c//dtd html 4.0 
2
transitional//en">
2
<html>
2
<head>
2
   <meta http-equiv="Content-Type" 
2
content="text/html; charset=iso-8859-1">
2
   <meta name="Author" 
2
content="Matthew Beermann (mbeerman@inetnebr.com)">
2
   <meta 
2
name="GENERATOR" content="Mozilla/4.61 [en] (Win95; U) [Netscape]">
2
 
2
<title>Achieve Project Management Help</title>
2
</head>
2
<body 
2
text="#000000" bgcolor="#CCFFFF" link="#0000EE" vlink="#551A8B" 
2
alink="#FF0000">
2

2
<center>
2
<h1>
2
<b>Achieve Project Management 
2
Help</b></h1></center>
2
<b>Undo Hand In Project:</b> Use this when a 
2
participant accidentally turns
2
in a project before they meant to, or 
2
when you think they need to go back
2
and work on it 
2
further.
2
<p><b>Delete Participant Data:</b> This will irrevocably 
2
delete any data
2
the participant has entered into the system. After 
2
you've made all the
2
selections you wish in both columns, press "Make 
2
These Changes" to save
2
the information.
2
<br>&nbsp;
2
<center><table 
2
BORDER=10 CELLSPACING=0 CELLPADDING=5 WIDTH="75%" 
2
BGCOLOR="#FFFF99" >
2
<tr>
2
<td>
2
<center><b>THE WIZ'S WORDS TO THE 
2
WIZE</b></center>
2
No one is allowed to see a given participant's work 
2
before he or she has
2
handed it in, not even you as the designer, 
2
unless the participant allows you to.
2
However, you can click on the
2
names under Undo Hand In Project to see 
2
what that person has turned in.
2
There is no similar function for 
2
Delete Participant Data.</td>
2
</tr>
2
</table></center>
2

2
<p><b>Send Notifications:</b> These notifications will be sent directly to you and your participants via network mail, so make sure that the MOO has your current email address. You can turn this option on and off at any time.
2

2
<p><b>Public:</b> Consider the nature of your project before setting or unsetting this flag. Specifically, if participant data must be kept private, then be sure it is unchecked. On the other hand, if participants are 'publishing' their work by handing it in, then leave it checked.
2

2
<p><b>Archived:</b> You can think of this as the 'advertising' setting. If checked, your project will be publicly advertised and usable by anyone browsing the project system. If not, it will be unadvertised and unusable, suitable for projects that have been completed.
2

2
<p><b>Delete This Project:</b> <font 
2
color="#FF0000">WARNING: This will
2
permanently and irrevocably delete 
2
this project and all participant data
2
associated with it.</font> If 
2
you need to have the results of your project
2
on file, be sure to make 
2
backup copies before doing this.
2
<br>&nbsp;
2
<center><table BORDER=10 
2
CELLSPACING=0 CELLPADDING=5 WIDTH="75%" 
2
BGCOLOR="#FFFF99" >
2
<tr>
2
<td>
2
<center><b>THE WIZ'S WORDS TO THE 
2
WIZE</b></center>
2
There is an alternate method of removing a project 
2
from the public listing
2
without archiving it. Type the following 
2
command into the MOO's input window:
2
"@move #xxx to here", where xxx 
2
is the object number of your project (it
2
appears after 
2
achieve.utoronto.ca:2221 in your project's webpage address).
2
Now 
2
participants will only be able to access your project by clicking 
2
on
2
its name in your room's contents listing, or by using the 
2
project's 
2
webpage
2
address.</td>
2
</tr>
2
</table></center>
2

2
<center>
2
<h2>
2
<b><a 
2
href="javascript:window.close();" onMouseOver="window.status='Close 
2
Help Window'; return true;" 
2
onMouseOut="window.status=window.defaultStatus; return 
2
true;">Close
2
Help Window</a></b></h2></center>
2

2
</body>
2
</html>
2
5
4
197
2
<!doctype html public "-//w3c//dtd html 4.0 
2
transitional//en">
2
<html>
2
<head>
2
   <meta http-equiv="Content-Type" 
2
content="text/html; charset=iso-8859-1">
2
   <meta name="Author" 
2
content="Matthew Beermann (mbeerman@cse.unl.edu)">
2
   <meta 
2
name="GENERATOR" content="Mozilla/4.61 [en] (Win95; U) [Netscape]">
2
 
2
<title>Achieve Project System Help</title>
2
</head>
2
<body 
2
text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B" 
2
alink="#FF0000">
2

2
<center>
2
<h1>
2
<b>Achieve Project System 
2
Help</b></h1></center>
2
Since each project is unique, your main guide 
2
in undertaking a project
2
should be the instructions
2
given to you 
2
by the project's designer, both on the main index page,
2
and at the 
2
top of each section.
2
<br>That said, the following are general 
2
guidelines to help you in this
2
process.
2
<br>
2
<hr 
2
WIDTH="100%">
2
<p><b>Participant Name(s):</b> Your real name, or if 
2
applicable, the names
2
of the people in your group. Note that this 
2
information will only be displayed
2
if the project's designer has this 
2
option selected.
2
<p><b>Participant Email:</b> Your email address. If 
2
you are part of a group,
2
please agree on one email address to use for 
2
all of you, preferrably that
2
of the person whose MOO account you used 
2
to log in.
2
<p><b>Sections:</b> A section represents some sort of 
2
coherent unit within
2
the project. The designer's
2
<br>directions, if 
2
any, on how to complete each section are listed at the
2
top of each 
2
section. Often, the
2
<br>same layout will be repeated over and over 
2
again; if they do not have
2
their own directions, work
2
<br>should be 
2
based on previous examples.
2
<p><b>Title:</b> This is where you put 
2
the title of your project.
2
<p><b>Subtitle:</b> Fields of this type 
2
have many different functions,
2
but most often they serve as the 
2
subtitle of the section. Pay special attention
2
to any directions 
2
given, as the designer may want you to enter something
2
very specific 
2
into this field.
2
<p><b>Paragraph:</b> These boxes are used to enter 
2
text, very much like
2
a word processor program. Despite the name, you 
2
can enter as many paragraphs
2
of information into them as you wish. As 
2
you type, your text will automatically
2
be word-wrapped; only press 
2
&lt;Enter> at the end of a paragraph.
2
<br>&nbsp;
2
<center><table 
2
BORDER=10 CELLSPACING=0 CELLPADDING=5 WIDTH="75%" 
2
BGCOLOR="#FFFF99" >
2
<tr>
2
<td>
2
<center><b>THE WIZ'S WORDS TO THE 
2
WIZE</b></center>
2
One of the unfortunate side-effects of the way 
2
webpages work is that extra
2
white space gets deleted. This means that 
2
you won't be to tab indent at
2
the beginning of a paragraph. Instead, 
2
we recommend double-spacing your
2
paragraphs for clarity's 
2
sake.</td>
2
</tr>
2
</table></center>
2

2
<p><b>Inset Paragraph:</b> Just 
2
like a Paragraph, except that text will
2
be slightly inset with 
2
respect to the rest of the page. Often, these are
2
used for things 
2
like lengthy quotations.
2
<p><b>URL:</b> The address (Universal 
2
Resource Locator) for a webpage somewhere
2
on the Internet. They 
2
usually (though not always) begin with http:// and
2
end with htm or 
2
html.
2
<p><b>Image:</b> This should be the address to an image file 
2
somewhere
2
on the Internet. They usually (though not always) begin 
2
with http:// and
2
end with gif or jpg. It will appear centered on the 
2
page. Note that it's
2
considered good Netiquette to ask for permission 
2
before using an image
2
from a site that doesn't belong to 
2
you.
2

2
<p><b>Audio:</b> This should be the address to an audio file somewhere on the Internet. They usually (though not always) begin with http:// and end with wav or mid. It will appear centered on the page with controls that allow the viewer to start and stop the sound.
2
Though there are other types of audio files out there, you should stick with WAV or MIDI files if you want them to work on everybody's computer. The advice about Netiquette above also applies.</p>
2

2
<p><b>Bulleted List:</b> Each line of text that you enter, 
2
separated by
2
returns, will appear on the final page as a list item in 
2
a bulleted list.
2
<p><b>Numbered List:</b> Each line of text that you 
2
enter, separated by
2
returns, will appear on the final page as a list 
2
item in an ordered (1,
2
2, 3) list.
2
<br>&nbsp;
2
<center><table 
2
BORDER=10 CELLSPACING=0 CELLPADDING=5 WIDTH="75%" 
2
BGCOLOR="#FFFF99" >
2
<tr>
2
<td>
2
<center><b>THE WIZ'S WORDS TO THE 
2
WIZE</b></center>
2
Any webpage address you enter anywhere into the 
2
project will be recognized
2
by the system and converted into a 
2
clickable link.</td>
2
</tr>
2
</table></center>
2

2
<p><b>Revert to 
2
Saved:</b> This will undo any changes that you have made,
2
and restore 
2
the information as last you saved it.
2
<p><b>Save and View:</b> This 
2
will save your work and show you what it
2
looks like so far when laid 
2
out properly. These projects are meant to be
2
worked on a little at a 
2
time, so if you've done a lot of work, it might
2
be wise to save what 
2
you have so far.
2
<p><b>Save and Hand In Project:</b> This will save 
2
any last changes you've
2
made, and then hand your project in. Once 
2
you've done this, you will not
2
be able to edit it further, and it 
2
will become visible to the project's
2
designer (and everyone 
2
else).
2
<br>&nbsp;
2
<center><table BORDER=10 CELLSPACING=0 
2
CELLPADDING=5 WIDTH="75%" 
2
BGCOLOR="#FFFF99" >
2
<tr>
2
<td>
2
<center><b>THE WIZ'S WORDS TO THE 
2
WIZE</b></center>
2
If you happen to know HTML, the programming 
2
language that makes webpages
2
work, feel free to use it. Some of the 
2
commoner codes are &lt;B>bold&lt;/B>
2
for <b>bold</b>, 
2
&lt;I>italics&lt;/I> for <i>italics</i>, and 
2
&lt;U>underline&lt;/U>
2
for <u>underline</u>. These will show up in 
2
their proper style on the final
2
page for your 
2
project.</td>
2
</tr>
2
</table></center>
2

2
<p><b>Allow designer to view and comment:</b> Normally, the project's designer can only view and comment on your work AFTER you hand it in.
2
If you check this box, then they can offer advice and feedback on your work in progress. Doing so is tantamount to requesting help, so only do it if you really mean it!</p>
2
<p><b>A Final Note:</b> If you 
2
still have questions that this page can't
2
answer, feel free to 
2
contact the project's designer (listed at the top
2
of each page), or a 
2
member of the MOO's wizard staff.
2
</body>
2
</html>
2
5
4
26
2
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
2
<html>
2
<head>
2
   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
2
   <meta name="Author" content="Matthew Beermann">
2
   <meta name="GENERATOR" content="Mozilla/4.76 [en] (Win98; U) [Netscape]">
2
   <title>Achieve Project Evaluation System Help</title>
2
</head>
2
<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B" alink="#FF0000">
2

2
<center>
2
<h1>
2
Achive Project Evaluation System Help</h1></center>
2

2
<hr WIDTH="100%">
2
<p>This page is pretty darn simple. At the bottom of each section of your
2
project, you'll see a box outlined in <b><font color="#FF0000">red</font></b>
2
that contains the project designer's comments on your work. What exactly
2
these <i>mean</i> depends a great on the specific project. If you haven't
2
handed your work in yet, they're probably just ideas and pointers to help
2
you on your way. If you <i>have</i> handed your work in, they could be
2
your grade, but then again that's not relevant for all projects. If you're
2
not sure what the project designer's comments are supposed to mean, double
2
check the description page, and then ask them directly.
2
</body>
2
</html>
2
5
4
47
2
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
2
<html>
2
<head>
2
   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
2
   <meta name="Author" content="Matthew Beermann">
2
   <meta name="GENERATOR" content="Mozilla/4.76 [en] (Win98; U) [Netscape]">
2
   <title>Achieve Project Evaluation System Help</title>
2
</head>
2
<body>
2

2
<center>
2
<h1>
2
Achieve Project Evaluation System Help</h1></center>
2

2
<hr WIDTH="100%">
2
<p>This page allows you to evaluate participants' work on your project.
2
It only becomes available after either a) the participant has flagged their
2
work as readable, or b) the participant has handed their work in. As usual,
2
the project is split into sections displaying the participant's work, but
2
at the end of each section you will see a large text box surrounded by
2
a <font color="#FF0000">red</font> border. This is where you should enter
2
your comments on that section. When you're done, just press <b>Save These
2
Comments</b> at the bottom of the page and you'll be returned to the project
2
index.
2
<p>What you choose to say is entirely up to you, but a few pointers are
2
in order: If you set out evaluation criteria in the project's description,
2
follow them, and if you didn't, consider going back and doing that now.
2
Remember that if a participant hasn't handed in their work yet, it should
2
be considered <i>in progress</i>, and your comments phrased accordingly.
2
If it's not relevant to evaluate a particular section, it's okay to leave
2
it blank. And finally, use the last evaluation box at the bottom of the
2
page for any summary comments about the project as a whole.
2
<br>&nbsp;
2
<center><table BORDER=10 CELLSPACING=0 CELLPADDING=5 WIDTH="75%" BGCOLOR="#FFFF99" >
2
<tr>
2
<td>
2
<center><b>THE WIZ'S WORDS TO THE WIZE</b></center>
2
If you checked "Send Notifications" on the project management page, then
2
you'll receive an e-mail when a participant requests comments on a project
2
or hands their work in. And <i>they'll</i> receive a note when you add
2
to or change your evaluation of their work, so make sure it's ready to
2
be seen before you press Save!</td>
2
</tr>
2
</table></center>
2

2
</body>
2
</html>
2
5
5
2
5
5
36
1
0
0
2
4
4
2
2
Assignment Help
2
help
2
5
5
2
5
4
2
0
38046
0
1001876098
36
1
5
2
5
5
2
5
#161
enCore_CGI_Application

0
2
-1
-1
-1
146
162
163
0
1
allow_anonymous_access
61
0
1
2
0
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
enCore_CGI_Application
2
5
5
2
5
4
2
0
1157
0
1001876098
36
1
5
2
5
5
2
5
#162
Xpress Login

0
2
-1
-1
-1
161
-1
148
5
init_for_core
2
173
-1
login_page
2
173
-1
welcome_screen
2
173
-1
login
2
173
-1
character_application_html
2
173
-1
4
welcome_screen
checkclient
submit_hack
validate_Script
65
4
19
2
<HTML>
2
<HEAD>
2
  <TITLE>Welcome to enCore</TITLE>
2
  <STYLE TYPE="text/css" MEDIA="screen">
2
    <!--
2
    BODY, P { color: Black; font-family: Sans-Serif; font-size: 14px; background-color: White;  }
2
    H1 { font-size: 200%; }
2
    A { font-family: Sans-Serif; font-size: 14px; }
2
    A:link { color: Blue }
2
    A:visited { color: Purple }
2
    A:hover { color: Red }
2
    -->
2
  </STYLE>
2
</HEAD>
2
<BODY>
2
<H1>Welcome to enCore version 3.2</H1>
2
<P>For more information on how to use, and get the most out of your new educational MOO, take a look at our edited collection of essays <A HREF="http://www.press.umich.edu/titles/09665.html" TARGET=_BLANK><I>High Wired: On the Design, Use, and Theory of Educational MOOs</I></A> available from University of Michigan Press, and our student text book, <A HREF="http://vig.abacon.com/catalog/abbooks/0,2371,0205271146,00.html" TARGET=_BLANK><I>MOOniversity: A Guide to Online Learning Environments</I></A>, from Allyn And Bacon.</P><P>Don't forget to subscribe to the enCore administrator's email list. Please go to <A HREF="http://listar.utdallas.edu/" TARGET="_BLANK">http://listar.utdallas.edu</A> to subscribe.</P><P>Below are a few links to enCore-related web sites.</P><UL><LI><A HREF="http://lingua.utdallas.edu/encore/gpl.html" TARGET=_BLANK>Read the GNU General Public License</A></LI><LI><A HREF="http://lingua.utdallas.edu/encore" TARGET=_BLANK>Visit the <I>High Wired</I> enCore web site</A></LI><LI><A HREF="http://lingua.utdallas.edu:7000/" TARGET=_BLANK>Visit Lingua MOO (The Mothership)</A></LI></UL><P>Good luck with your new educational MOO</P><P><A HREF="http://wwwpub.utdallas.edu/~cynthiah/" TARGET=_BLANK>Cynthia Haynes</A> and <A HREF="http://lingua.utdallas.edu/jan/" TARGET=_BLANK>Jan Rune Holmevik</A></P><P>Coordinators enCore Open Source MOO Project.</P>
2
</BODY>
2
</HTML>
2
5
4
14
2
<script LANGUAGE="JavaScript">
2
<!-- Hide script from old browsers
2
function checkClient() {
2
   var browser = navigator.appName;
2
   var version = navigator.appVersion;
2
   
2
   if ((browser != 'Netscape' || browser != 'Microsoft Internet Explorer') && parseInt(version) < 4) {
2
      alert('Sorry. The enCore Xpress system can only be used with Netscape Communicator version 4.08 or newer, or Microsoft Internet Explorer version 4.0 or newer. The latest versions of both browsers can be downloaded for free from the Internet.');
2
      return false; 
2
   }
2
   return true;
2
}
2
// -->
2
</script>
2
5
4
9
2
function submit1() {
2
     document.passwordForm.passwordField.focus();
2
     return false;
2
}
2

2
function submit2() {
2
     document.passwordForm.useridField.value = document.useridForm.useridField.value;
2
     return true;
2
}
2
5
4
18
2
function validateForm(){
2
 if(document.application.real_name.value=="") {
2
    alert('You forgot to tell us your real name. Please try again!')
2
   document.application.real_name.focus()
2
    return false }
2
  
2
 if(document.application.name.value=="") {
2
    alert('In order for us to create a new character for you, we need to know what name you wish to use in the MOO.!')
2
    document.application.name.focus()
2
    return false }
2

2
  if(document.application.email_address.value=="") {
2
    alert('Unless we know your email address we cannot send you the password for your new character !')
2
    document.application.email_address.focus()
2
    return false }
2

2
  return true
2
} 
2
5
5
2
0
5
2
5
5
2
5
2
0
2
5
2
yes
2
1
2
yes
2
1
2
yes
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
2
SlateGray
2
1
5
2
1
2
Black
2
1
2
Black
2
1
2
Silver
2
1
2
Black
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
0
0
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Xpress Login
2
5
5
2
5
4
2
0
18272
0
1001876098
36
1
5
2
5
5
2
5
#163
Xpress Inventory Manager

0
2
-1
-1
-1
146
-1
-1
5
view_inventory
2
173
-1
make_inventory
2
173
-1
display_quota
2
173
-1
make_object_menu
2
173
-1
init_for_core
2
173
-1
1
max_view
61
0
8
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
2
600
2
1
2
700
2
1
5
2
1
5
2
5
5
2
5
2
1
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
2
BOTTOM
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
1
5
2
0
5
2
0
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
Xpress Inventory Manager
2
5
5
2
5
4
2
0
8724
0
1001876098
36
1
5
2
5
5
2
5
#0:0
"...This code should only be run as a server task...";
if (callers())
return E_PERM;
endif
if (typeof(h = $network:incoming_connection(player)) == OBJ)
"connected to an object";
return h;
elseif (h)
return 0;
endif
host = $string_utils:connection_hostname(connection_name(player));
if ($login:redlisted(host))
boot_player(player);
server_log(tostr("REDLISTED: ", player, " from ", host));
return 0;
endif
"...checks to see if the login is spamming the server with too many commands...";
if (!$login:maybe_limit_commands())
args = $login:parse_command(@args);
return $login:(args[1])(@listdelete(args, 1));
endif
.
#0:1
if (callers())
return;
endif
$last_restart_time = time();
if ($xpress_client.on)
$httpd:start_webserver();
endif
"Last modified Thu Apr  8 10:35:55 1999 CDT by Wizard (#2).";
.
#0:2
saved = {#0};
for p in (properties(#0))
v = #0.(p);
if ((typeof(v) == OBJ) && valid(v))
saved = setadd(saved, v);
endif
endfor
for o in (saved)
p = parent(o);
while (valid(p))
saved = setadd(saved, p);
p = parent(p);
endwhile
endfor
return $list_utils:sort(saved);
.
#0:3
if (caller_perms().wizard)
pass();
if ("uptime_since" in verbs(this))
delete_verb(this, "uptime_since");
endif
if ("do_command" in verbs(this))
delete_verb(this, "do_command");
endif
core_name_and_version = ($core_name + " ") + $core_version;
$core_history = {{core_name_and_version, server_version(), time()}, @$core_history};
$shutdown_message = "";
$shutdown_time = 0;
$dump_interval = 3600;
$last_dump_time = 0;
$checkpoint_successful = 1;
$last_db_size = 0;
$checkpoint_start_time = 0;
$core_customized = 0;
$anonymous_users = 0;
$real_guest_names = 0;
$room_connection_points = {};
$guest_name_suffix = "[Guest]";
$gripe_recipients = {player};
$class_registry = {{"generics", "Generic objects intended for use as the parents of new objects", $object_utils:fertile_objects_suspended(), {"utilities", "Objects holding useful general-purpose verbs", children($generic_utils)}}};
for v in ({"do_login_command", "server_started"})
c = {};
for i in (verb_code(this, v))
c = {@c, strsub(i, "$local.login", "$login")};
endfor
set_verb_code(#0, v, c);
endfor
endif
"Last modified Fri Apr 13 18:49:06 2001 CDT by Wizard (#2).";
.
#0:4
if (callers())
return;
endif
user = args[1];
try
"Added to provide guests with the opportunity to select their own names, Jan";
"Added an error check on user.location:confunc to prevent breaking login in certain situations. Jan 3/31/01";
if (((user in children($guest)) && $real_guest_names) && (user.ts_client == 0))
user:set_guest_name(user);
endif
set_task_perms(user);
try
user.location:confunc(user);
user:confunc();
except error (ANY)
$player_start:confunc(user);
user:confunc();
endtry
fork (0)
$login_watcher:hello(user);
endfork
except id (ANY)
user:tell("Confunc failed: ", id[2], ".");
for tb in (id[4])
user:tell("... called from ", tb[1], ":", tb[2], ", line ", tb[6]);
endfor
user:tell("(End of traceback)");
endtry
$mcp:(verb)(@args);
"Last modified Fri Apr 13 18:49:50 2001 CDT by Wizard (#2).";
.
#0:5
if (callers())
return;
endif
if (args[1] < #0)
"not logged in user.  probably should do something clever here involving Carrot's no-spam hack.  --yduJ";
return;
endif
$mcp:(verb)(@args);
user = args[1];
user.last_disconnect_time = time();
set_task_perms(user);
fork (0)
$login_watcher:goodbye(user);
endfork
`user:disfunc() ! ANY => 0';
`user.location:disfunc(user) ! ANY => 0';
"Last modified Sun Oct  4 19:50:25 1998 CDT by Wizard (#2).";
.
#0:6
"chparent(object, new-parent) -- see help on the builtin. This verb is called by the server when $server_options.protect_chparent exists and is true and caller_perms are not wizardly.";
who = caller_perms();
{what, papa} = args;
if (typeof(what) != OBJ)
retval = E_TYPE;
elseif (!valid(what))
retval = E_INVARG;
elseif (typeof(papa) != OBJ)
retval = E_TYPE;
elseif ((!valid(papa)) && (papa != #-1))
retval = E_INVIND;
elseif (!$perm_utils:controls(who, what))
retval = E_PERM;
elseif ((is_player(what) && (!$object_utils:isa(papa, $player_class))) && (!who.wizard))
retval = E_PERM;
elseif ((is_player(what) && (!$object_utils:isa(what, $player_class))) && (!who.wizard))
retval = E_PERM;
elseif ((children(what) && $object_utils:isa(what, $player_class)) && (!$object_utils:isa(papa, $player_class)))
retval = E_PERM;
elseif ((!valid(papa)) || ($perm_utils:controls(who, papa) || papa.f))
retval = `chparent(@args) ! ANY';
else
retval = E_PERM;
endif
return ((typeof(retval) == ERR) && $code_utils:dflag_on()) ? raise(retval) | retval;
.
#0:7
"add_verb() -- see help on the builtin for more information. This verb is called by the server when $server_options.protect_add_verb exists and is true and caller_perms() are not wizardly.";
who = caller_perms();
what = args[1];
info = args[2];
if (typeof(what) != OBJ)
retval = E_TYPE;
elseif (!valid(what))
retval = E_INVARG;
elseif ((!$perm_utils:controls(who, what)) && (!what.w))
"caller_perms() is not allowed to hack on the object in question";
retval = E_PERM;
elseif (!$perm_utils:controls(who, info[1]))
"caller_perms() is not permitted to add a verb with the specified owner.";
retval = E_PERM;
elseif (index(info[2], "w") && (!$server_options.permit_writable_verbs))
retval = E_INVARG;
elseif (!$quota_utils:verb_addition_permitted(who))
retval = E_QUOTA;
elseif (((what.owner != who) && (!who.wizard)) && (!$quota_utils:verb_addition_permitted(what.owner)))
retval = E_QUOTA;
elseif (!who.programmer)
retval = E_PERM;
else
"we now know that the caller's perms control the object or the object is writable, and we know that the caller's perms control the prospective verb owner (by more traditional means)";
retval = `add_verb(@args) ! ANY';
endif
return ((typeof(retval) == ERR) && $code_utils:dflag_on()) ? raise(retval) | retval;
.
#0:8
"add_property() -- see help on the builtin for more information. This verb is called by the server when $server_options.protect_add_property exists and is true and caller_perms() are not wizardly.";
who = caller_perms();
{what, propname, value, info} = args;
if (typeof(what) != OBJ)
retval = E_TYPE;
elseif (!valid(what))
retval = E_INVARG;
elseif ((!$perm_utils:controls(who, what)) && (!what.w))
retval = E_PERM;
elseif (!$perm_utils:controls(who, info[1]))
retval = E_PERM;
elseif (!$quota_utils:property_addition_permitted(who))
retval = E_QUOTA;
elseif (((what.owner != who) && (!who.wizard)) && (!$quota_utils:property_addition_permitted(what.owner)))
retval = E_QUOTA;
"elseif (!who.programmer)";
"  return E_PERM;     I wanted to do this, but $builder:@newmessage relies upon nonprogs being able to call add_property.  --Nosredna";
elseif ((propname in {"object_size", "size_quota", "queued_task_limit"}) && (!who.wizard))
retval = E_PERM;
else
"we now know that the caller's perms control the object (or the object is writable), and that the caller's perms are permitted to control the new property's owner.";
retval = `add_property(@args) ! ANY';
endif
return ((typeof(retval) == ERR) && $code_utils:dflag_on()) ? raise(retval) | retval;
.
#0:9
"recycle(object) -- see help on the builtin. This verb is called by the server when $server_options.protect_recycle exists and is true and caller_perms() are not wizardly.";
if (!valid(what = args[1]))
retval = E_INVARG;
elseif (!$perm_utils:controls(who = caller_perms(), what))
retval = E_PERM;
elseif ((p = is_player(what)) && (!who.wizard))
for p in ($wiz_utils:connected_wizards_unadvertised())
p:tell($string_utils:pronoun_sub("%N (%#) is currently trying to recycle %t (%[#t])", who, what));
endfor
retval = E_PERM;
else
if (p)
$wiz_utils:unset_player(what);
endif
retval = `recycle(what) ! ANY';
endif
return ((typeof(retval) == ERR) && $code_utils:dflag_on()) ? raise(retval) | retval;
.
#0:10
if (callers())
return;
endif
if ($object_utils:isa(user = args[1], $guest))
"from $guest:boot";
oldloc = user.location;
move(user, $nothing);
"..force enterfunc to be called so that the newbie gets a room description.";
move(user, user.home);
user:do_reset();
if ($object_utils:isa(oldloc, $room))
oldloc:announce("In the distance you hear someone's alarm clock going off.");
if (oldloc != user.location)
oldloc:announce(user.name, " wavers and vanishes into insubstantial mist.");
else
oldloc:announce(user.name, " undergoes a wrenching personality shift.");
endif
endif
set_task_perms(user);
`user:confunc() ! ANY';
else
if (user.ts_client)
user:reset_Xpress_session();
endif
endif
$mcp:(verb)(@args);
"Last modified Fri Oct  1 06:51:09 1999 CDT by Wizard (#2).";
.
#0:11
"set_verb_info() -- see help on the builtin for more information. This verb is called by the server when $server_options.protect_set_verb_info exists and is true and caller_perms() are not wizardly.";
{o, v, i} = args;
if (typeof(vi = `verb_info(o, v) ! ANY') == ERR)
"probably verb doesn't exist";
retval = vi;
elseif (!$perm_utils:controls(cp = caller_perms(), vi[1]))
"perms don't control the current verb owner";
retval = E_PERM;
elseif ((typeof(i) != LIST) || (typeof(no = i[1]) != OBJ))
"info is malformed";
retval = E_TYPE;
elseif ((!valid(no)) || (!is_player(no)))
"invalid new verb owner";
retval = E_INVARG;
elseif (!$perm_utils:controls(cp, no))
"perms don't control prospective verb owner";
retval = E_PERM;
elseif (index(i[2], "w") && (!`$server_options.permit_writable_verbs ! E_PROPNF, E_INVIND => 1'))
retval = E_INVARG;
else
retval = `set_verb_info(o, v, i) ! ANY';
endif
return ((typeof(retval) == ERR) && $code_utils:dflag_on()) ? raise(retval) | retval;
.
#0:12
"...This code should only be run as a server task...";
if (callers())
return E_PERM;
endif
this.checkpoint_start_time = time();
this.last_db_size = db_disk_size();
timestr = ctime()[12..19];
a = $string_utils:center("Checkpoint started at " + timestr, 47);
for i in (connected_players())
if (i.wizard)
i:tell(("*" + a) + "*");
endif
endfor
"Last modified Fri Oct  1 06:46:28 1999 CDT by Wizard (#2).";
.
#0:13
"hacked by SA to fix a bug with Macmoose";
if (callers())
return E_PERM;
elseif (!valid(player))
return;
else
set_task_perms(player);
endif
if ((length(args) > 1) && (args[1] == "#$#MacMOOSE"))
oob_verb = args[2];
tokens = $macmoose_utils:simple_tokenize(argstr);
oob_args = ((len = length(tokens)) > 2) ? tokens[3..len] | {};
$macmoose_utils:(oob_verb)(@oob_args);
else
return $mcp:(verb)(@args);
endif
"Last modified Fri Jun  9 15:18:59 2000 CDT by Wizard (#2).";
.
#0:14
"...This code should only be run as a server task...";
if (callers())
return E_PERM;
endif
tt = time() - $checkpoint_start_time;
size = db_disk_size();
diff = size - this.last_db_size;
if (diff < 0)
diffstring = "A decrease";
diff = -diff;
elseif (diff > 0)
diffstring = "An increase";
else
diffstring = "A change";
endif
if (args[1])
$checkpoint_successful = 1;
a = "Finished in " + $time_utils:english_time(tt);
b = ("The database is " + tostr(size)) + " bytes on disk";
c = ((diffstring + " of ") + tostr(diff)) + " bytes";
for i in ($wiz_utils:connected_wizards())
i:tell_lines({("*" + $string_utils:center(a, 47)) + "*", ("*" + $string_utils:center(b, 47)) + "*", ("*" + $string_utils:center(c, 47)) + "*"});
endfor
$last_dump_time = time();
else
$checkpoint_successful = 0;
$mail_agent:send_message(#2, "Checkpoint Unsuccessful", "Last checkpoint was unsuccessful.");
for i in ($wiz_utils:connected_wizards())
i:tell("*** Checkpoint was unsuccessful!");
endfor
endif
"Last modified Fri Oct  1 06:46:28 1999 CDT by Wizard (#2).";
.
#1:0
if (typeof(this.owner.owned_objects) == LIST)
this.owner.owned_objects = setadd(this.owner.owned_objects, this);
endif
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
if (is_clear_property(this, "object_size"))
"If this isn't clear, then we're being hacked.";
this.object_size = {0, 0};
endif
this.key = 0;
else
return E_PERM;
endif
.
#1:1
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
try
if ((typeof(this.owner.owned_objects) == LIST) && (!is_clear_property(this.owner, "owned_objects")))
this.owner.owned_objects = setremove(this.owner.owned_objects, this);
$recycler.lost_souls = setadd($recycler.lost_souls, this);
endif
except (ANY)
"Oy, doesn't have a .owned_objects??, or maybe .owner is $nothing";
"Should probably do something...like send mail somewhere.";
endtry
else
return E_PERM;
endif
.
#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...";
if ((!caller_perms().wizard) && (is_player(this) || ((caller_perms() != this.owner) && (this != caller))))
return E_PERM;
else
return (typeof(e = `this.name = args[1] ! ANY') != ERR) || e;
endif
.
#1:3
return this.name;
.
#1:4
return $object_utils:has_property(this, "namec") ? this.namec | $string_utils:capitalize(this:title());
.
#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";
"  => 1        if aliases are set exactly as expected (default)";
"  => 0        if aliases were set differently than expected";
"              (children with custom :set_aliases should be aware of this)";
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
c = this:contents();
return $string_utils:match(args[1], c, "name", c, "aliases");
.
#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
if (player.ts_client)
if (player.update_web_display)
descrip = {tostr("You view ", this.name, "..."), tostr(" <http://", $network.site, ":", $network.webport, "/", tonum(this), "/>.")};
return descrip;
else
player.update_web_display = 1;
return tostr("You view ", this.name, "...");
endif
else
return this.description;
endif
"Last modified Sat Apr 14 17:19:52 2001 CDT by Wizard (#2).";
.
#1:10
desc = this:description();
if ((player.display_drawings && this.drawing) && (!player.ts_client))
player:tell_lines(this.drawing);
endif
if (desc)
player:tell_lines(desc);
else
player:tell("You see nothing special.");
endif
.
#1:11
if (is_player(this))
notify(this, args[1]);
endif
.
#1:12
this:notify(tostr(@args));
.
#1:13
lines = args[1];
if (typeof(lines) == LIST)
for line in (lines)
this:tell(line);
endfor
else
this:tell(lines);
endif
.
#1:14
set_task_perms(caller_perms());
return this:acceptable(@args);
.
#1:15
set_task_perms(this.owner);
return `move(this, args[1]) ! ANY';
.
#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.";
"eject_basic(victim) --- victim goes to $nothing or $player_start according as victim is a player; victim:moveto is not called.";
what = args[1];
nice = verb != "eject_basic";
perms = caller_perms();
if ((!perms.wizard) && (perms != this.owner))
return E_PERM;
elseif ((!(what in this.contents)) || what.wizard)
return 0;
endif
if ((((nice && $object_utils:has_property(what, "home")) && (typeof(where = what.home) == OBJ)) && (where != this)) && (is_player(what) ? `where:accept_for_abode(what) ! ANY' | `where:acceptable(what) ! ANY'))
"where = what.home";
else
where = is_player(what) ? $player_start | $nothing;
endif
if (nice)
fork (0)
"...Some objects like to know they've been moved...";
"... this is the best we can do.  Remember :moveto() may be broken.";
`what:moveto(where) ! ANY => 0';
endfork
endif
return `move(what, where) ! ANY => 1';
.
#1:17
return (this.key == 0) || $lock_utils:eval_key(this.key, args[1]);
.
#1:18
set_task_perms((caller_perms() != #-1) ? caller_perms() | player);
$command_utils:do_huh(verb, args);
.
#1:19
":set_message(msg_name,new_value)";
"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
return `this.(args[1] + "_msg") = args[2] ! ANY' && 1;
endif
.
#1:20
"do_examine(examiner)";
"the guts of examine";
"call a series of verbs and report their return values to the player";
who = args[1];
"if (caller == this || caller == who)";
if (caller == who)
"set_task_perms();";
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
return E_PERM;
endif
.
#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(this.name, " (aka ", $string_utils:english_list({tostr(this), @this.aliases}), ")")};
.
#1:23
"examine_desc(who) - return the description, probably";
"who is the player examining";
"this should probably go away";
desc = this:description();
if (desc)
if (typeof(desc) != LIST)
desc = {desc};
endif
return desc;
else
return {"(No description set.)"};
endif
.
#1:24
"examine_contents(examiner)";
"by default, calls :tell_contents.";
"Should probably go away.";
who = args[1];
if (caller == this)
try
this:tell_contents(this.contents, this.ctype);
except (ANY)
"Just ignore it. We shouldn't care about the contents unless the object wants to tell us about them via :tell_contents ($container, $room)";
endtry
endif
.
#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.";
"Modified 23/3/01 by Jan to include object name and possible iobj in verb listings.";
if (caller != this)
return E_PERM;
endif
{who, ?name = this.name} = args;
vrbs = {};
commands_ok = `this:examine_commands_ok(who) ! ANY => 0';
dull_classes = {$root_class, $room, $player, $prog, $builder};
what = this;
hidden_verbs = this:hidden_verbs(who);
while (what != $nothing)
$command_utils:suspend_if_needed(0);
if (!(what in dull_classes))
for i in [1..length(verbs(what))]
$command_utils:suspend_if_needed(0);
info = verb_info(what, i);
syntax = verb_args(what, i);
if (this:examine_verb_ok(what, i, info, syntax, commands_ok, hidden_verbs))
{dobj, prep, iobj} = syntax;
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 (j = index(vname, "* "))
vname = tostr(vname[1..j - 1], "anything", vname[j + 1..$]);
endwhile
if (vname[$] == "*")
vname = vname[1..$ - 1] + "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 ($code_utils:verb_or_property(this, "help_msg"))
vrbs = {@vrbs, tostr("  help ", dobjstr)};
endif
return vrbs && {"Obvious verbs:", @vrbs};
"Last modified Fri Apr 13 19:04:26 2001 CDT by Wizard (#2).";
.
#1:26
":get_message(msg_name)";
"Use this to obtain a given user-customizable message's raw value, i.e., the value prior to any pronoun-substitution or incorporation of any variant elements --- the value one needs to supply to :set_message().";
"=> error (use E_PROPNF if msg_name isn't recognized)";
"=> string or list-of-strings raw value";
"=> {2, @(list of {msg_name_n,rawvalue_n} pairs to give to :set_message)}";
"=> {1, other kind of raw value}";
"=> {E_NONE, error message} ";
if (!((caller == this) || $perm_utils:controls(caller_perms(), this)))
return E_PERM;
elseif (((t = typeof(msg = `this.(args[1] + "_msg") ! ANY')) in {ERR, STR}) || (((t == LIST) && msg) && (typeof(msg[1]) == STR)))
return msg;
else
return {1, msg};
endif
.
#1:27
try
this.location:(verb)(@args);
except (ANY)
endtry
.
#1:28
if (caller_perms().wizard)
vnum = 1;
while (vnum <= length(verbs(this)))
$command_utils:suspend_if_needed(0);
info = verb_info(this, vnum);
if (index(info[3], "(old)"))
delete_verb(this, vnum);
else
if (i = index(info[3], "(core)"))
`delete_verb(this, info[3][1..i - 1]) ! ANY';
set_verb_info(this, info[3], {@info[1..2], info[3][1..i - 1]});
else
vnum = vnum + 1;
endif
endif
endwhile
endif
"Last modified Sun Aug 17 11:11:29 1997 CDT by Wizard (#2).";
.
#1:29
"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:30
"examine_verb_ok(loc, index, info, syntax, commands_ok, hidden_verbs)";
"loc is the object that defines the verb; index 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}.";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
{loc, index, info, syntax, commands_ok, hidden_verbs} = args;
vname = info[3];
return (((((syntax[2..3] != {"none", "this"}) && (!index(vname, "("))) && (commands_ok || ("this" in syntax))) && `verb_code(loc, index) ! ANY') && (!(vname in hidden_verbs))) && (!({loc, vname, syntax} in hidden_verbs));
else
return E_PERM;
endif
.
#1:31
"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:32
"hidden_verbs(who)";
"returns a list of verbs on this that should be 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)[2], for example \"d*op th*row\".";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
hidden = {};
what = this;
while (what != $nothing)
for i in [1..length(verbs(what))]
info = verb_info(what, i);
if (!index(info[2], "r"))
hidden = setadd(hidden, {what, info[3], verb_args(what, i)});
endif
endfor
what = parent(what);
endwhile
return hidden;
else
return E_PERM;
endif
.
#1:33
"examine_owner(examiner)";
"Return a list of strings to be told to the player, indicating who owns this.";
return {tostr("Owned by ", this.owner.name, ".")};
.
#1:34
return;
.
#1:35
lines = args[1];
if (typeof(lines) == LIST)
for line in (lines)
this:tell(line);
$command_utils:suspend_if_needed(0);
endfor
else
this:tell(lines);
endif
.
#1:36
return 0;
"intended as a 'quiet' way to determine if :accept will succeed. Currently, some objects have a noisy :accept verb since it is the only thing that a builtin move() call is guaranteed to call.";
"if you want to tell, before trying, whether :accept will fail, use :acceptable instead. Normally, they'll do the same thing.";
.
#3:0
if ((((cp = caller_perms()) == player) || $perm_utils:controls(cp, player)) || (caller == this))
"Need the first check because guests don't control themselves";
this:look_self(player.brief);
this:announce($string_utils:pronoun_sub("%N %<has> connected.", player));
endif
.
#3:1
if ((((cp = caller_perms()) == player) || $perm_utils:controls(cp, player)) || (caller == this))
this:announce($string_utils:pronoun_sub("%N %<has> disconnected.", player));
"need the first check since guests don't control themselves";
if ((this != player.home) && (!$object_utils:isa(player, $guest)))
"guest disfuncs are handled by $guest:disfunc. Don't add them here";
$housekeeper:move_players_home(player);
endif
endif
.
#3:2
try
player:tell("You say, \"", argstr, "\"");
this:announce(player.name, " ", $gender_utils:get_conj("says", player), ", \"", argstr, "\"");
except (ANY)
"Don't really need to do anything but ignore the idiot who has a bad :tell";
endtry
.
#3:3
if ((argstr != "") && (argstr[1] == ":"))
this:announce_all(player.name, argstr[2..length(argstr)]);
else
this:announce_all(player.name, " ", argstr);
endif
.
#3:4
for dude in (setremove(this:contents(), player))
$command_utils:suspend_if_needed(0);
try
dude:tell(@args);
except (ANY)
"Just skip the dude with the bad :tell";
continue dude;
endtry
endfor
"Last modified Fri Feb  6 08:41:13 1998 CST by Wizard (#2).";
.
#3:5
what = args[1];
if (what)
yes = $failed_match;
for e in (this.exits)
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());
return `this.exits = setadd(this.exits, args[1]) ! E_PERM' != E_PERM;
.
#3:7
{contents, ctype} = args;
if (((!this.dark) && (contents != {})) && (player.ts_client == 0))
if (ctype == 0)
player:tell("Contents:");
for thing in (contents)
player:tell("  ", thing:title());
endfor
elseif (ctype == 1)
for thing in (contents)
if (is_player(thing))
player:tell($string_Utils:pronoun_sub("%n ", $gender_utils:get_conj("is", thing), " here.", thing));
else
player:tell("You see ", thing:title(), " here.");
endif
endfor
elseif (ctype == 2)
player:tell("You see ", $string_utils:title_list(contents), " here.");
elseif (ctype == 3)
players = things = {};
for x in (contents)
if (is_player(x))
players = {@players, x};
else
things = {@things, x};
endif
endfor
if (things)
player:tell("You see ", $string_utils:title_list(things), " here.");
endif
if (players)
player:tell($string_utils:title_listc(players), (length(players) == 1) ? " " + $gender_utils:get_conj("is", players[1]) | " are", " here.");
endif
endif
endif
"Last modified Thu Apr  8 12:29:03 1999 CDT by Wizard (#2).";
.
#3:8
if (!$perm_utils:controls(valid(caller_perms()) ? caller_perms() | player, this))
player:tell("Sorry, only the owner of a room may list its exits.");
elseif (this.exits == {})
player:tell("This room has no conventional exits.");
else
try
for exit in (this.exits)
try
player:tell(exit.name, " (", exit, ") leads to ", valid(exit.dest) ? exit.dest.name | "???", " (", exit.dest, ") via {", $string_utils:from_list(exit.aliases, ", "), "}.");
except (ANY)
player:tell("Bad exit or missing .dest property:  ", $string_utils:nn(exit));
continue exit;
endtry
endfor
except (E_TYPE)
player:tell("Bad .exits property. This should be a list of exit objects. Please fix this.");
endtry
endif
.
#3:9
{?brief = 0} = args;
if (!player.ts_client)
player:tell(this:title());
endif
if (!brief)
pass();
endif
this:tell_contents(setremove(this:contents(), player), this.ctype);
if (this.tell_exits)
this:tell_exits();
endif
"Last modified Thu Apr  8 12:28:43 1999 CDT by Wizard (#2).";
.
#3:10
what = args[1];
return this:is_unlocked_for(what) && (((this:free_entry(@args) || ((what == this.blessed_object) && (task_id() == this.blessed_task))) || (what.owner == this.owner)) || ((typeof(this.residents) == LIST) && (what in this.residents)));
"Last modified Fri Apr 13 19:03:56 2001 CDT by Wizard (#2).";
.
#3:11
set_task_perms(caller_perms());
return `this.entrances = setadd(this.entrances, args[1]) ! E_PERM' != E_PERM;
.
#3:12
if (caller in {@this.entrances, this})
this.blessed_object = args[1];
this.blessed_task = task_id();
endif
.
#3:13
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
try
for exit in (this.entrances)
try
player:tell(exit.name, " (", exit, ") comes from ", valid(exit.source) ? exit.source.name | "???", " (", exit.source, ") via {", $string_utils:from_list(exit.aliases, ", "), "}.");
except (ANY)
player:tell("Bad entrance object or missing .source property: ", $string_utils:nn(exit));
continue exit;
endtry
endfor
except (E_TYPE)
player:tell("Bad .entrances property. This should be a list of exit objects. Please fix this.");
endtry
endif
.
#3:14
if ((!args) || (!(dir = args[1])))
player:tell("You need to specify a direction.");
return E_INVARG;
elseif (valid(exit = player.location: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.";
suspend(0);
if (player.location == old_room)
"player didn't move or get moved while we were suspended";
player.location:go(@listdelete(args, 1));
endif
endif
elseif (exit == $failed_match)
player:tell("You can't go that way (", dir, ").");
else
player:tell("I don't know which direction `", dir, "' you mean.");
endif
.
#3:15
if ((dobjstr == "") && (!prepstr))
this:look_self();
elseif ((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))
if (dobjstr == "")
iobj:look_self();
elseif ((thing = iobj:match(dobjstr)) == $failed_match)
player:tell("I don't see any \"", dobjstr, "\" ", prepstr, " ", iobj.name, ".");
elseif (thing == $ambiguous_match)
player:tell("There are several things ", prepstr, " ", iobj.name, " one might call \"", dobjstr, "\".");
else
thing:look_self();
endif
endif
endif
.
#3:16
for dude in (this:contents())
$command_utils:suspend_if_needed(0);
try
dude:tell(@args);
except (ANY)
"Just ignore the dude with the stupid :tell";
continue dude;
endtry
endfor
"Last modified Fri Feb  6 08:41:13 1998 CST by Wizard (#2).";
.
#3:17
":announce_all_but(LIST objects to ignore, text)";
{ignore, @text} = args;
contents = this:contents();
for l in (ignore)
$command_utils:suspend_if_needed(0);
contents = setremove(contents, l);
endfor
for listener in (contents)
$command_utils:suspend_if_needed(0);
try
listener:tell(@text);
except (ANY)
"Ignore listener with bad :tell";
continue listener;
endtry
endfor
"Last modified Fri Feb  6 08:41:13 1998 CST by Wizard (#2).";
.
#3:18
object = args[1];
if (is_player(object) && (object.location == this))
player = object;
this:look_self(player.brief);
endif
if (object == this.blessed_object)
this.blessed_object = #-1;
endif
.
#3:19
return;
.
#3:20
exit = args[1];
if (caller != exit)
set_task_perms(caller_perms());
endif
return `this.exits = setremove(this.exits, exit) ! E_PERM' != E_PERM;
.
#3:21
exit = args[1];
if (caller != exit)
set_task_perms(caller_perms());
endif
return `this.entrances = setremove(this.entrances, exit) ! E_PERM' != E_PERM;
.
#3:22
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
.
#3:23
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
.
#3:24
"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)
try
x:moveto(this.location);
except (ANY)
continue x;
endtry
endfor
endif
"... try sending them home...";
for x in (this.contents)
if (is_player(x))
if ((typeof(x.home) == OBJ) && valid(x.home))
try
x:moveto(x.home);
except (ANY)
continue x;
endtry
endif
if (x.location == this)
move(x, $player_start);
endif
elseif (valid(x.owner))
try
x:moveto(x.owner);
except (ANY)
continue x;
endtry
endif
endfor
pass(@args);
else
return E_PERM;
endif
.
#3:25
set_task_perms((caller_perms() == #-1) ? player | caller_perms());
exit = this:match_exit(verb);
if (valid(exit))
exit:invoke();
elseif (exit == $failed_match)
player:tell("You can't go that way.");
else
player:tell("I don't know which direction `", verb, "' you mean.");
endif
.
#3:26
set_task_perms(player);
if ($command_utils:object_match_failed(dobj, dobjstr))
return;
elseif (dobj.location != this)
is = $gender_utils:get_conj("is", dobj);
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.name, " tried to ", verb, " you.");
return;
endif
iobj = this;
player:tell(this:ejection_msg());
this:((verb == "@eject") ? "eject" | "eject_basic")(dobj);
if (verb != "@eject!!")
dobj:tell(this:victim_ejection_msg());
endif
this:announce_all_but({player, dobj}, this:oejection_msg());
.
#3:27
return $gender_utils:pronoun_sub(this.(verb));
.
#3:28
who = args[1];
return (valid(who) && ((this.free_home || $perm_utils:controls(who, this)) || ((typeof(residents = this.residents) == LIST) ? who in this.residents | (who == this.residents)))) && this:acceptable(who);
.
#3:29
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 (typeof(this.residents) != LIST)
this.residents = {this.residents};
endif
if (!dobjstr)
"First, remove !valid objects from this room...";
for x in (this.residents)
if ((typeof(x) != OBJ) || (!valid(x)))
player:tell("Warning: removing ", x, ", an invalid object, from the residents list.");
this.residents = setremove(this.residents, x);
endif
endfor
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..$];
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)
is = $gender_utils:get_conj("is", result);
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:30
target = {@this:contents(), @this:exits()};
return $string_utils:match(args[1], target, "name", target, "aliases");
.
#3:31
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:32
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:33
if ((caller in {this, this.owner}) || $perm_utils:controls(caller_perms(), this))
return pass(@args);
else
return E_PERM;
endif
.
#3:34
return (msg = `this.(verb) ! ANY') ? $string_utils:pronoun_sub(msg, args[1]) | "";
.
#3:35
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb);
else
return E_PERM;
endif
.
#3:36
exits = {};
for exit in (`(verb == "obvious_exits") ? this.exits | this.entrances ! ANY => {}')
if (`exit.obvious ! ANY')
exits = setadd(exits, exit);
endif
endfor
return exits;
.
#3:37
":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.";
set_task_perms(caller_perms());
if (args[2] || ($failed_match == (exit = this:match_exit(verb = args[1]))))
"... okay, it's not an exit.  we give up...";
return 0;
elseif (valid(exit))
exit:invoke();
else
"... ambiguous exit ...";
player:tell("I don't know which direction `", verb, "' you mean.");
endif
return 1;
.
#3:38
this:(verb[6..$])(@args);
.
#3:39
return this == args[1].location;
.
#3:40
"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:title(), " will accept only objects matching the following key:"), tostr("  ", $lock_utils:unparse_key(this.key))};
endif
.
#3:41
"examine_contents(who)";
if (caller == this)
this:tell_contents(this.contents, this.ctype);
endif
.
#3:42
return this.free_entry;
.
#3:43
return this.(verb);
.
#3:44
if (this.tell_exits)
if (oe = this:obvious_exits())
oestr = "";
for huh in (oe)
oestr = (((oestr + ", [") + huh.name) + "] to ") + huh.dest.name;
endfor
oestr = oestr[2..length(oestr)];
player:tell("Obvious exits:", oestr);
endif
endif
return;
"Last modified Thu Apr  8 12:29:03 1999 CDT by Wizard (#2).";
.
#3:45
contents = setremove(this.contents, player);
for dude in (contents)
if (is_player(dude) && dude.sketch)
dude:tell(argstr);
endif
endfor
.
#3:46
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Verb that generates links based on contents and exits in a room";
"===========================================================";
user = args[1];
if (!$encore_web_utils:page_assembled(html = pass(user)))
contents = exits = links = {};
html = {@html, "<P>"};
if (this.contents != {})
contents = $encore_web_utils:generate_links(user, this.contents);
contents = $encore_web_utils:insert_line_breaks({this.web_contents_msg, @contents});
endif
if (this.exits != {})
"Modified by Traveller 7/15/01 to use the dynamic :obvious_exits() instead of the static exit.obvious";
exits = this:obvious_exits();
exits = $encore_web_utils:generate_links(user, exits);
exits = $encore_web_utils:insert_line_breaks({this.web_exits_msg, @exits});
endif
if (contents && exits)
links = {contents, exits};
elseif (contents && (!exits))
links = {contents};
elseif (exits && (!contents))
links = {exits};
endif
table = $encore_web_utils:generate_table(user, links, this);
if (links)
table = $list_utils:append({"<!-- MOO Hyperlinks>"}, table, {"<!-- end>"});
endif
html = $list_utils:append(html, table);
"Move user to new location if java client on";
if (user.ts_client && (user.location != this))
$encore_web_utils:move_via_web(user, this);
endif
endif
return html;
"Last modified Sun Sep 30 13:16:26 2001 CDT by Wizard (#2).";
.
#3:47
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Generate a form for creating a new room";
"===========================================================";
if (caller != $Xpress_Object_Editor)
return E_PERM;
endif
{user, object} = args;
start_form = pass(@args);
form = {};
{help_button, help_window_function} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "Creating_Rooms");
help_window_function = $encore_web_utils:insert_javascript(help_window_function);
owned_rooms = {};
if (match($string_utils:print(user.location.shared_owners), tostr(user)) || user.wizard)
owned_rooms = {@owned_rooms, user.location};
endif
for OBJ in (user.owned_objects)
"Find all the users' non-fertile rooms";
if ($object_utils:isa(OBJ, $room) && (!OBJ.f))
owned_rooms = {@owned_rooms, OBJ};
endif
endfor
if (!owned_rooms)
"User wants to create their first room, add valid connection points";
owned_rooms = $list_utils:append(owned_rooms, $room_connection_points);
endif
if (owned_rooms)
form = {@form, "<P><INPUT TYPE=radio NAME=connect VALUE=0>Do not connect my new room to another room."};
form = {@form, "<P><INPUT TYPE=radio NAME=connect VALUE=1 CHECKED>Connect my new room to:<P>"};
form = {@form, "<SELECT NAME=origin>"};
for room in (owned_rooms)
if (room == user.location)
form = {@form, tostr("<OPTION VALUE=", toint(room), " SELECTED>", room.name)};
else
form = {@form, tostr("<OPTION VALUE=", toint(room), ">", room.name)};
endif
endfor
form = {@form, "</SELECT>"};
else
"This is the user's first room and no connection point has been defined.";
form = {@form, tostr("<INPUT TYPE=hidden NAME=connect VALUE=\"0\">")};
form = {@form, tostr("<INPUT TYPE=hidden NAME=origin VALUE=\"\">")};
form = {@form, tostr("<INPUT TYPE=hidden NAME=entrance VALUE=\"\">")};
form = {@form, tostr("<INPUT TYPE=hidden NAME=exit VALUE=\"\">")};
form = {@form, tostr("This will be your first room in ", $network.MOO_name, ". As such, it will not be connected to the rest of the MOO yet. You will therefore need to contact one of the MOO administrators and ask them to connect it for you.")};
endif
result = $list_utils:append(help_window_function, start_form, help_button, form);
return result;
"Last modified Fri Apr 13 19:01:27 2001 CDT by Wizard (#2).";
.
#4:0
set_task_perms(player);
if (dobjstr == "")
dobj = player;
else
dobj = $string_utils:match_player(dobjstr);
endif
if (!valid(dobj))
player:notify("Show whose quota?");
return;
endif
$quota_utils:display_quota(dobj);
.
#4:1
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))
"New menu of popular generics make it easier for new users to create stuff. Jan 4/9/98";
finished = 0;
title = $encore_utils:make_title($network.MOO_name + " Creation Menu");
while (!finished)
player:tell_lines(title);
player:tell("What do you want to create?");
player:tell();
player:tell("Basic Objects  Special Educational Objects");
player:tell("-------------  ---------------------------------------");
player:tell("1) A Thing     5) A Robot           12) A Web Projector");
player:tell("2) A Container 6) A Lecture         13) A Web Browser");
player:tell("3) A Note      7) A Video tape      14) A Note Board");
player:tell("4) A Letter    8) A Video camera    15) A Recorder");
player:tell("               9) A VCR             16) A Recording System");
player:tell("              10) A TV              17) A Room Directory");
player:tell("              11) A Slide Projector 18) A Chain Story");
player:tell("                                    19) A Recitable Note");
player:tell();
player:tell("Please enter your choice (1-19), or type Q to quit");
ans = read(player);
if ((toint(ans) >= 1) && (toint(ans) <= 19))
finished = 1;
player:tell("Please type a name for this new object");
namestr = read(player);
if (ans == "1")
parentstr = "$thing";
elseif (ans == "2")
parentstr = "$container";
elseif (ans == "3")
parentstr = "$note";
elseif (ans == "4")
parentstr = "$letter";
elseif (ans == "5")
parentstr = "$bot";
elseif (ans == "6")
parentstr = "$lecture";
elseif (ans == "7")
parentstr = "$tape";
elseif (ans == "8")
parentstr = "$camera";
elseif (ans == "9")
parentstr = "$vcr";
elseif (ans == "10")
parentstr = "$tv";
elseif (ans == "11")
parentstr = "$slide_projector";
elseif (ans == "12")
parentstr = "$webprojector";
elseif (ans == "13")
parentstr = "$web_slate";
elseif (ans == "14")
parentstr = "$note_board";
elseif (ans == "15")
parentstr = "$recorder";
elseif (ans == "16")
parentstr = "$recording_system";
elseif (ans == "17")
parentstr = "$room_directory";
elseif (ans == "18")
parentstr = "$public_writable_note";
elseif (ans == "19")
parentstr = "$recitable_note";
endif
else
return player:tell("Creation procedure aborted...");
endif
endwhile
else
parentstr = $string_utils:from_list(args[1..pos - 1], " ");
namestr = $string_utils:from_list(args[pos + 1..$], " ");
endif
if (!namestr)
return player:notify("You must provide a name. Procedure aborted.");
endif
if (parentstr[1] == "$")
parent = #0.(parentstr[2..$]);
if (typeof(parent) != OBJ)
player:notify(tostr("\"", parentstr, "\" does not name an object."));
return;
endif
else
parent = player:my_match_object(parentstr);
if ($command_utils:object_match_failed(parent, parentstr))
return;
endif
endif
object = player:_create(parent);
if (typeof(object) == ERR)
player:notify(tostr(object));
return;
endif
for f in ($string_utils:char_list(player:build_option("create_flags") || ""))
object.(f) = 1;
endfor
"move() shouldn't, but could bomb. Say if player has a stupid :accept";
`move(object, player) ! ANY';
$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
player:notify(tostr("You now have ", object.name, aka, " with object number ", object, " and parent ", parent.name, " (", parent, ")."));
"Last modified Wed Apr 15 12:59:41 1998 CDT by Wizard (#2).";
.
#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.name, " (", dobj, ") has ", nv, " verb", (nv == 1) ? "" | "s", ".  Sure you want to recycle it?");
else
prompt = tostr("Are you sure you want to recycle ", dobj.name, " (", dobj, ")?");
endif
if ($command_utils:yes_or_no(prompt))
name = dobj.name;
result = player:_recycle(dobj);
if (typeof(result) == ERR)
player:notify(tostr(result));
else
player:notify(tostr(name, " (", dobj, ") recycled."));
endif
else
player:notify("Okay.");
endif
endif
"Last modified Sun Mar 17 07:31:36 1996 CST by Jan (#2).";
.
#4:3
"@recreate <object> as <parent-class> named [name:]alias,alias,...";
"  effectively recycles and creates <object> all over again.";
set_task_perms(player);
as = prepstr in args;
named = "named" in args;
if ((named <= (as + 1)) || (named == length(args)))
named = "called" in args;
endif
if ((named <= (as + 1)) || (named == length(args)))
player:notify_lines({tostr("Usage:  ", verb, " <object> as <parent-class> named [name:]alias,...,alias"), "", "where <parent-class> is one of the standard classes ($note, $letter, $thing, or $container) or an object number (e.g., #999), or the name of some object in the current room.  The [name:]alias... specification is as in @create.", "", "You can use \"called\" instead of \"named\", if you wish."});
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..$], " ");
if (parentstr[1] == "$")
parent = #0.(parentstr[2..$]);
if (typeof(parent) != OBJ)
player:notify(tostr("\"", parentstr, "\" does not name an object."));
return;
endif
else
parent = player:my_match_object(parentstr);
if ($command_utils:object_match_failed(parent, parentstr))
return;
endif
endif
if (!(e = $building_utils:recreate(dobj, parent)))
player:notify(tostr(e));
return;
endif
for f in ($string_utils:char_list(player:build_option("create_flags") || ""))
dobj.(f) = 1;
endfor
"move() shouldn't, but could, bomb. Say if player has a stupid :accept";
`move(dobj, player) ! ANY';
$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);
nargs = length(args);
if (nargs == 1)
room = args[1];
exit_spec = "";
elseif ((nargs >= 3) && (args[2] == "to"))
exit_spec = args[1];
room = $string_utils:from_list(args[3..$], " ");
elseif (argstr && (!prepstr))
room = argstr;
exit_spec = "";
else
player:notify(tostr("Usage:  ", verb, " <new-room-name>"));
player:notify(tostr("    or  ", verb, " <exit-description> to <new-room-name-or-old-room-object-number>"));
return;
endif
if (room != tostr(other_room = toobj(room)))
room_kind = player:build_option("dig_room");
if (room_kind == 0)
room_kind = $room;
endif
other_room = player:_create(room_kind);
if (typeof(other_room) == ERR)
player:notify(tostr("Cannot create new room as a child of ", $string_utils:nn(room_kind), ": ", other_room, ".  See `help @build-options' for information on how to specify the kind of room this command tries to create."));
return;
endif
for f in ($string_utils:char_list(player:build_option("create_flags") || ""))
other_room.(f) = 1;
endfor
other_room.name = room;
other_room.aliases = {room};
move(other_room, $nothing);
player:notify(tostr(other_room.name, " (", other_room, ") created."));
elseif (nargs == 1)
player:notify("You can't dig a room that already exists!");
return;
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)
exit_kind = player:build_option("dig_exit");
if (exit_kind == 0)
exit_kind = $exit;
endif
exits = $string_utils:explode(exit_spec, "|");
if ((length(exits) < 1) || (length(exits) > 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
do_recreate = !player:build_option("bi_create");
to_ok = $building_utils:make_exit(exits[1], player.location, other_room, do_recreate, exit_kind);
if (to_ok && (length(exits) == 2))
$building_utils:make_exit(exits[2], other_room, player.location, do_recreate, exit_kind);
endif
endif
.
#4:5
"Usage:  @audit [player] [from <start>] [to <end>] [for <matching string>]";
set_task_perms(player);
dobj = $string_utils: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..$];
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);
.
#4:6
if (!dobjstr)
dobj = player;
elseif ($command_utils:player_match_result(dobj = $string_utils:match_player(dobjstr), dobjstr)[1])
return;
endif
set_task_perms(player);
if (typeof(dobj.owned_objects) == LIST)
count = length(dobj.owned_objects);
player:notify(tostr(dobj.name, " currently owns ", count, " object", (count == 1) ? "." | "s."));
if ($quota_utils.byte_based)
player:notify(tostr("Total bytes consumed:  ", $string_utils:group_number($quota_utils:get_size_quota(dobj)[2]), "."));
endif
else
player:notify(tostr(dobj.name, " is not enrolled in the object ownership system.  Use @countDB instead."));
endif
.
#4:7
if (!dobjstr)
dobj = player;
elseif ($command_utils:player_match_result(dobj = $string_utils:match_player(dobjstr), dobjstr)[1])
return;
endif
set_task_perms(player);
count = 0;
for o in [#1..max_object()]
if ($command_utils:running_out_of_time())
player:notify("Counting...");
suspend(5);
endif
if (valid(o) && (o.owner == dobj))
count = count + 1;
endif
endfor
player:notify(tostr(dobj.name, " currently owns ", count, " object", (count == 1) ? "." | "s."));
.
#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)
if ((!dobjstr) || (index("object", dobjstr) == 1))
ret = $list_utils:sort_suspended(0, player.owned_objects);
elseif (index("size", dobjstr) == 1)
ret = $list_utils:reverse_suspended($list_utils:sort_suspended(0, player.owned_objects, $list_utils:slice($list_utils:map_prop(player.owned_objects, "object_size"))));
endif
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_objects 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
$command_utils:suspend_if_needed(2, tostr("Suspending @verify-owned ... ", x));
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;
player:notify(tostr("Unlocked ", dobj.name, "."));
except error (ANY)
player:notify(error[2]);
endtry
.
#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;
player:notify(tostr("Locked ", dobj.name, " to this key:"));
player:notify(tostr("  ", $lock_utils:unparse_key(key)));
except error (ANY)
player:notify(error[2]);
endtry
endif
.
#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)
player:notify(tostr("Usage:  ", verb, " <message-name> [<message>] [on <object>]"));
return;
endif
object = valid(iobj) ? iobj | player;
name = this:_messagify(dobjwords[1]);
value = dobjstr[length(dobjwords[1]) + 2..$];
nickname = "@" + name[1..$ - 4];
e = `add_property(object, name, value, {player, "rc"}) ! ANY';
if (typeof(e) != ERR)
player:notify(tostr(nickname, " on ", object.name, " is now \"", object.(name), "\"."));
elseif (e != E_INVARG)
player:notify(tostr(e));
elseif ($object_utils:has_property(object, name))
"object already has property";
player:notify(tostr(object.name, " already has a ", nickname, " message."));
else
player:notify(tostr("Unable to add ", nickname, " message to ", object.name, ": ", e));
endif
.
#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
object = valid(iobj) ? iobj | player;
name = this:_messagify(dobjstr);
nickname = "@" + name[1..$ - 4];
try
delete_property(object, name);
player:notify(tostr(nickname, " message removed from ", object.name, "."));
except (E_PROPNF)
player:notify(tostr("No ", nickname, " message found on ", object.name, "."));
except error (ANY)
player:notify(error[2]);
endtry
.
#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..$];
endif
if ((length(name) < 4) || (name[$ - 3..$] != "_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:title(), "(", thing, ") has ", length(kids), " kid", (length(kids) == 1) ? "" | "s", "."));
player:notify(tostr($string_utils:names_of(kids)));
else
player:notify(tostr(thing:title(), "(", thing, ") has no kids."));
endif
endif
.
#4:17
"'@contents <obj> - list the contents of an object, with object numbers. This verb is by yduJ.";
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;
if (contents)
player:notify(tostr(dobj:title(), "(", dobj, ") contains:"));
player:notify(tostr($string_utils:names_of(contents)));
else
player:notify(tostr(dobj:title(), "(", dobj, ") contains nothing."));
endif
endif
.
#4:18
"'@parents <thing>' - List <thing> and its ancestors, all the way back to the Root Class (#1).";
if (player != this)
return player:notify("Permission denied: not a builder.");
elseif (!dobjstr)
player:notify(tostr("Usage:  ", verb, " <object>"));
return;
endif
set_task_perms(player);
o = player:my_match_object(dobjstr);
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 = $string_utils:match_player(dobjstr))))
$command_utils:object_match_failed(dobj, dobjstr);
return;
endif
player:notify($string_utils:names_of({what, @$object_utils:locations(what)}));
.
#4:20
"$class_registry is in the following format:";
"        { {name, description, members}, ... }";
"where `name' is the name of a particular class of objects, `description' is a one-sentence description of the membership of the class, and `members' is a list of object numbers, the members of the class.";
"";
if (!$command_utils:yes_or_no("This command can be very spammy.  Are you certain you need this information?"))
return player:tell("OK, aborting.  The lag thanks you.");
endif
if (args)
members = {};
for name in (args)
class = $list_utils:assoc_prefix(name, $class_registry);
if (class)
for o in (class[3])
members = setadd(members, o);
endfor
else
player:tell("There is no defined class of objects named `", name, "'; type `@classes' to see a complete list of defined classes.");
return;
endif
endfor
printed = {};
for o in (members)
what = o;
while (valid(what))
printed = setadd(printed, what);
what = parent(what);
endwhile
endfor
player:tell("Members of the class", (length(args) > 1) ? "es" | "", " named ", $string_utils:english_list(args), ":");
player:tell();
set_task_perms(player);
this:classes_2($root_class, "", members, printed);
player:tell();
else
"List all class names and descriptions";
player:tell("The following classes of objects have been defined:");
for class in ($class_registry)
name = class[1];
description = class[2];
player:tell();
player:tell("-- ", name, ": ", description);
endfor
player:tell();
player:tell("Type `@classes <name>' to see the members of the class with the given <name>.");
endif
.
#4:21
{root, indent, members, printed} = args;
if (root in members)
player:tell(indent, root.name, " (", root, ")");
else
player:tell(indent, "<", root.name, " (", root, ")>");
endif
printed = setremove(printed, root);
indent = indent + "  ";
set_task_perms(caller_perms());
for c in ($list_utils:sort_suspended(2, $set_utils:intersection(children(root), printed)))
$command_utils:suspend_if_needed(10);
this:classes_2(c, indent, members, printed);
endfor
.
#4:22
set_task_perms(caller_perms());
if (this:build_option("bi_create"))
return $quota_utils:bi_create(@args);
else
return $recycler:(verb)(@args);
endif
.
#4:23
set_task_perms(caller_perms());
if (this:build_option("bi_create"))
return recycle(@args);
else
return $recycler:(verb)(@args);
endif
.
#4:24
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 ((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.");
else
try
result = player:_chparent(object, parent);
player:notify("Parent changed.");
except (E_INVARG)
if (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("Either that is not a valid object or not a valid parent");
endif
except (E_PERM)
player:notify("Either you don't own the object, don't own the parent, or the parent is not fertile.");
except (E_RECMOVE)
player:notify("That parent object is a descendant of the object!");
endtry
endif
.
#4:25
"Copied from generic programmer (#217):@check-chparent by ur-Rog (#6349) Sun Nov  8 22:13:53 1992 PST";
"@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
.
#4:26
"Syntax:  @set <object>.<prop-name> to <value>";
"";
"Changes the value of the specified object's property to the given value.";
"You must have permission to modify the property, either because you own the property or if it is writable.";
set_task_perms(player);
if (this != player)
return player:tell(E_PERM);
endif
l = $code_utils:parse_propref(dobjstr);
if (l)
dobj = player:my_match_object(l[1], player.location);
if ($command_utils:object_match_failed(dobj, l[1]))
return;
endif
prop = l[2];
to_i = "to" in args;
at_i = "at" in args;
i = (to_i && at_i) ? min(to_i, at_i) | (to_i || at_i);
iobjstr = argstr[$string_utils:word_start(argstr)[i][2] + 1..$];
iobjstr = $string_utils:trim(iobjstr);
if (!iobjstr)
val = dobj.(prop) = "";
iobjstr = "\"\"";
"elseif (iobjstr[1] == \"\\\"\")";
"val = dobj.(prop) = iobjstr;";
"iobjstr = \"\\\"\" + iobjstr + \"\\\"\";";
else
val = $string_utils:to_value(iobjstr);
if (!val[1])
player:tell("Could not parse: ", iobjstr);
return;
elseif (!$object_utils:has_property(dobj, prop))
player:tell("That object does not define that property.");
return;
endif
val = dobj.(prop) = val[2];
endif
player:tell("Property ", dobj, ".", prop, " set to ", $string_utils:print(val), ".");
else
player:tell("Property ", dobjstr, " not found.");
endif
.
#4:27
":build_option(name)";
"Returns the value of the specified builder option";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return $build_options:get(this.build_options, args[1]);
else
return E_PERM;
endif
.
#4:28
":set_build_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 n 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:29
"@<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 = "build";
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
.
#4:30
"Syntax:";
"  @measure object <object name>";
"  @measure summary [player]";
"  @measure new [player]";
"  @measure breakdown <object name>";
"  @measure recent [number of days] [player]";
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..$], " "));
if (!valid(what))
player:tell("Sorry, I didn't understand `", name, "'");
elseif (((($object_utils:has_property(what, "object_size") && (what.object_size[1] > $byte_quota_utils.too_large)) && (!player.wizard)) && (player != $byte_quota_utils.owner)) && (player != $hacker))
player:tell($string_utils:nn(what), " when last measured was ", $string_utils:group_number(what.object_size[1]), " bytes.  To reduce lag induced by multiple players re-measuring large objects multiple times, you may not measure that object.");
else
player:tell("Checking size of ", what.name, " (", what, ")...");
player:tell("Size of ", what.name, " (", what, ") is ", $byte_quota_utils:object_bytes(what), " bytes.");
endif
elseif (index("summary", args[1]) == 1)
"Summarize player.";
if (length(args) == 1)
what = player;
else
what = $string_utils:match_player(name = $string_utils:from_list(args[2..$], " "));
endif
if (!valid(what))
player:tell("Sorry, I don't know who you mean by `", name, "'");
else
$byte_quota_utils:do_summary(what);
endif
elseif (index("new", args[1]) == 1)
if (length(args) == 1)
what = player;
elseif (!valid(what = $string_utils:match_player(name = $string_utils:from_list(args[2..$], " "))))
return $command_utils:player_match_failed(what, name);
endif
player:tell("Measuring the sizes of ", what.name, "'s recently created objects...");
total = 0;
unmeasured_index = 4;
unmeasured_multiplier = 100;
nunmeasured = 0;
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 = $byte_quota_utils: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.") | "");
elseif (index("recent", args[1]) == 1)
"@measure recent days player";
if (length(args) > 1)
days = $code_utils:toint(args[2]);
else
days = $byte_quota_utils.cycle_days;
endif
if (!days)
return player:tell("Couldn't understand `", args[2], "' as a positive integer.");
endif
if (length(args) > 2)
if (!valid(who = $string_utils:match_player(name = $string_utils:from_list(args[3..$], " "))))
return $command_utils:player_match_failed(who, name);
endif
else
who = player;
endif
player:tell("Re-measuring objects of ", $string_utils:nn(who), " which have not been measured in the past ", days, " days.");
when = time() - (days * 86400);
which = {};
for x in (who.owned_objects)
if (x.object_size[2] < when)
$byte_quota_utils:object_size(x);
which = setadd(which, x);
endif
endfor
player:tell("Done, re-measured ", length(which), " objects.", (length(which) > 0) ? "  Recommend you use @measure summary to update the display of @quota." | "");
elseif (index("breakdown", args[1]) == 1)
what = player.location:match_object(name = $string_utils:from_list(args[2..$], " "));
if (!valid(what))
player:tell("Sorry, I didn't understand `", name, "'");
elseif (!$byte_quota_utils:can_peek(player, what.owner))
return player:tell("Sorry, you don't control ", what.name, " (", 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 = $byte_quota_utils:do_breakdown(what);
if (typeof(info) == ERR)
player:tell(info);
endif
if (mail)
$mail_agent:send_message($byte_quota_utils.owner, {player}, tostr("Object breakdown of ", what.name, " (", 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
.
#4:31
if (caller_perms().wizard)
if (this == $builder)
this.build_options = {};
else
clear_property(this, "build_options");
endif
return pass(@args);
endif
.
#4:32
"Usage : @histogram [NUM steps] [NUM max_bars] [mail]";
"";
"This verb displays the quota usage of the moo in the ";
"form of a histogram with a max of [max_bars] bars ";
"and with a display step of [steps]. Empty bars will ";
"be removed. ";
"[mail] will moomail you the resulting histogram.";
"Defaults : steps = 1000   max_bars = 100   mail = no mail";
set_task_perms(player);
step = 1000;
ast = 60;
ben = "";
max_bars = 100;
biggies = {#2, #100, #37};
if ((args && ((rr = tonum(args[1])) > 0)) && (rr < 10000000))
step = rr;
endif
if (((length(args) >= 2) && ((rr = tonum(args[2])) > 2)) && (rr < 500))
max_bars = tonum(args[2]);
endif
l = length(k = tostr(step));
if ((l > 3) && (k[l - 2..l] == "000"))
ben = "K";
endif
stepc = (ben == "K") ? step / 1000 | step;
r = $list_utils:make(max_bars - 2);
extra = {0, 0};
tot = 0;
for p in (players())
$command_utils:suspend_if_needed(0);
q = p.size_quota;
if (((typeof(q) == LIST) && (length(q) > 1)) && (typeof(qq = q[2]) == NUM))
use = qq / step;
if (!(p in biggies))
tot = tot + qq;
endif
if (!use)
extra[1] = extra[1] + 1;
elseif (use > (max_bars - 2))
extra[2] = extra[2] + 1;
else
r[use] = r[use] + 1;
endif
endif
endfor
STR = "";
i = 0;
r = {extra[1], @r, extra[2]};
tot = tot / (length(players()) - length(biggies));
c = length(r);
for x in (r)
$command_utils:suspend_if_needed(0);
i = i + 1;
if (x)
STR = ($string_utils:left((((tostr((i - 1) * stepc) + ben) + "-") + ((i == c) ? ".." | tostr(i * stepc))) + ben, 13) + $string_utils:right(("[" + tostr(x)) + "]", 6)) + $string_utils:space((x > ast) ? ast | x, "*");
r[i] = STR;
endif
endfor
w = {(("STEP : " + tostr(step)) + "   MAX_BARS : ") + tostr(max_bars), ""};
i = 0;
for x in (r)
$command_utils:suspend_if_needed(0);
i = i + 1;
if (x)
w = {@w, x};
elseif ((i >= 2) && r[i - 1])
w = {@w, ".."};
endif
endfor
w = {@w, "", ("Average : " + tostr(tot)) + " bytes."};
if ((length(args) >= 3) && (args[3] == "mail"))
$mail_agent:send_message(player, player, "Histogram", {"", "", @w});
player:tell("Histogram mailed.");
else
player:tell_lines(w);
endif
"Last modified Fri Feb  6 08:52:49 1998 CST by Wizard (#2).";
.
#4:33
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Generates a dynamic web menu line based on which player class the user belongs to";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{menu, functions, preload, base_url} = pass(@args);
external_baseurl = (($xpress_client.external_baseurl + $xpress_client.themes_folder) + this.xpress_theme) + "/";
objects = {tostr("<A HREF=\"", base_url, "Xpress_object_editor/main.html\" TARGET=\"", $xpress_object_editor:get_frame_name("openObjectEditor"), "\" onClick=\"openObjectEditor(); swapImage('objects','", external_baseurl, "objects1.jpg')\" onMouseOver=\"swapImage('objects','", external_baseurl, "objects2.jpg')\" onMouseOut=\"swapImage('objects','", external_baseurl, "objects1.jpg')\"><IMG SRC=\"", external_baseurl, "objects1.jpg\" BORDER=0 ALIGN=bottom name=objects ALT=\"Create and Edit Objects\" TITLE=\"Create and Edit Objects\"></A>")};
preload = {@preload, tostr("   objects2 = new Image(); objects2.src = \"", external_baseurl, "edit2.jpg\";")};
object_editor = $encore_web_utils:javascript_window_open($Xpress_object_editor, "openObjectEditor", "");
menu = $list_utils:append(menu, objects);
functions = $list_utils:append(functions, object_editor);
return {menu, functions, preload, base_url};
"Last modified Fri Apr 13 19:05:56 2001 CDT by Wizard (#2).";
.
#5:0
set_task_perms(callers() ? caller_perms() | player);
if (this.location == player)
player:tell("You already have that!");
elseif (this.location != player.location)
player:tell("I don't see that here.");
else
this:moveto(player);
if (this.location == player)
player:tell(this:take_succeeded_msg() || "Taken.");
if (msg = this:otake_succeeded_msg())
player.location:announce(player.name, " ", msg);
endif
else
player:tell(this:take_failed_msg() || "You can't pick that up.");
if (msg = this:otake_failed_msg())
player.location:announce(player.name, " ", msg);
endif
endif
endif
.
#5:1
set_task_perms(callers() ? caller_perms() | player);
if (this.location != player)
player:tell("You don't have that.");
elseif (!player.location:acceptable(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.name, " ", 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.name, " ", msg);
endif
endif
endif
.
#5:2
where = args[1];
"if (!valid(where) || this:is_unlocked_for(where))";
if (this:is_unlocked_for(where))
pass(where);
endif
.
#5:3
set_task_perms(caller_perms());
return $string_utils:pronoun_sub(this.(verb));
.
#5:4
set_task_perms(callers() ? caller_perms() | player);
if (this.location != player)
player:tell("You don't have that!");
elseif (!valid(player.location))
player:tell("I see no \"", iobjstr, "\" here.");
elseif ($command_utils:object_match_failed(who = player.location:match_object(iobjstr), iobjstr))
elseif (who.location != player.location)
player:tell("I see no \"", iobjstr, "\" here.");
elseif (who == player)
player:tell("Give it to yourself?");
else
this:moveto(who);
if (this.location == who)
player:tell("You hand ", this:title(), " to ", who:title(), ".");
who:tell(player:titlec(), " ", $gender_utils:get_conj("hands/hand", player), " you ", this:title(), ".");
else
player:tell(who:titlec(), " ", $gender_utils:get_conj("does/do", who), " not want that item.");
endif
endif
.
#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:title(), " can only be moved to locations matching this key:"), tostr("  ", $lock_utils:unparse_key(this.key))};
endif
.
#5:6
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Take or drop a thing via Xpress.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
"-------------------------------------------";
"Determine where to send result";
"-------------------------------------------";
if (data != {{}, {}})
app = toobj(data[2][1]);
vrb = tostr(data[2][2]);
else
app = user.location;
vrb = "_html";
endif
alert = "";
if (verb == "take_html")
this:moveto(user);
if (this.location == user)
alert = $string_utils:print(this:take_succeeded_msg()) || "Taken.";
if (msg = this:otake_succeeded_msg())
notify(user, tostr("  <http://", $network.site, ":", $network.webport, "/", toint(user.location), "/>."));
user.location:announce(user.name, " ", msg);
endif
else
alert = $string_utils:print(this:take_failed_msg()) || "You can't pick that up.";
if (msg = this:otake_failed_msg())
user.location:announce(user.name, " ", msg);
endif
endif
else
if (!user.location:acceptable(this))
alert = "You can't drop that here.";
else
this:moveto(user.location);
if (this.location == user.location)
alert = $string_utils:print(this:drop_succeeded_msg()) || "Dropped.";
if (msg = this:odrop_succeeded_msg())
notify(user, tostr("  <http://", $network.site, ":", $network.webport, "/", toint(user.location), "/>."));
user.location:announce(user.name, " ", msg);
endif
else
alert = $string_utils:print(this:drop_failed_msg()) || "You can't seem to drop that here.";
if (msg = this:odrop_failed_msg())
user.location:announce(user.name, " ", msg);
endif
endif
endif
endif
body = app:(vrb)(user);
return body = this:build(user, body, app.name, $encore_web_utils:make_alert(alert));
"Last modified Fri Apr 13 19:03:56 2001 CDT by Wizard (#2).";
.
#6:0
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Reset object to default values.";
"===========================================================";
if (caller_perms().wizard)
pass();
if ($code_utils:verb_location() != this)
if (this in {$no_one, $hacker})
this.home = $nothing;
else
clear_property(this, "home");
endif
if (a = $list_utils:assoc(this, {{$builder, $builder_help}, {$prog, {$prog_help, $builtin_function_help, $verb_help, $core_help}}, {$wiz, $wiz_help}}))
this.help = a[2];
else
this.help = 0;
endif
for p in ({"last_connect_place", "all_connect_places", "features", "last_disconnect_time"})
clear_property(this, p);
endfor
this.first_connect_time = this.last_connect_time = $maxint;
else
"Xpress player defaults";
this.home = $player_start;
this.help = {$help, $encore_help};
this.features = {$stage_talk, $pasting_feature, $login_watcher, $soc_verbs};
this.doing = "";
this.web_access_code = "";
this.java_font = "Courier";
this.java_font_size = 12;
this.xpress_on = 0;
this.sound_volume = 10;
this.mail_notify_sound = "moomail.wav";
this.java_client_localecho = "False";
this.web_mail_signature = {};
this.update_web_display = 1;
this.recording = 0;
this.Xpress_Confunc_Msg = {};
this.Xpress_Log = {};
this.Xpress_Notebook = {};
this.invis = 0;
this.xpress_bookmarks = {};
this.xpress_screen_size = "screen.availWidth,screen.availHeight";
this.Xpress_layout = "vertical_layout";
this.xpress_screen_division = "50%,50%";
this.override_object_styles = 0;
this.xpress_bookmarks = {};
this.show_contextual_menu = 1;
this.designer = 0;
this.xpress_theme = "azure";
endif
endif
"Last modified Mon Sep 17 10:04:26 2001 CDT by Wizard (#2).";
.
#6:1
if (((valid(cp = caller_perms()) && (caller != this)) && (!$perm_utils:controls(cp, this))) && (caller != #0))
return E_PERM;
endif
try
this:("@last-connection")();
$news:check();
if (msg = $xpress_client:check_motd(player))
notify(player, "-------------------------------------------");
notify(player, msg);
notify(player, "-------------------------------------------");
endif
except error (ANY)
"An error occurred. Ignore in order not to break login";
endtry
"Last modified Mon Sep 17 10:09:08 2001 CDT by Wizard (#2).";
.
#6:2
if (((valid(cp = caller_perms()) && (caller != this)) && (!$perm_utils:controls(cp, this))) && (caller != #0))
return E_PERM;
endif
this:reset_Xpress_session();
this:expunge_rmm();
this:erase_paranoid_data();
this:gc_gaglist();
return;
"Last modified Fri Oct  1 06:49:39 1999 CDT by Wizard (#2).";
.
#6:3
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
this.help = 0;
return pass(@args);
else
return E_PERM;
endif
.
#6:4
return !is_player(args[1]);
.
#6:5
"Extra parsing of player commands.  Called by $command_utils:do_huh.";
"This version of my_huh just handles features.";
permissions = (((caller == this) || $perm_utils:controls(caller_perms(), this)) && $command_utils:validate_feature(@args)) ? this | $no_one;
"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 ($code_utils:toint(verb))
return;
endif
pass = args[2];
plist = {"any", prepstr ? $code_utils:full_prep(prepstr) | "none"};
dlist = dobjstr ? {"any"} | {"none", "any"};
ilist = iobjstr ? {"any"} | {"none", "any"};
for fobj in (this.features)
if (!$recycler:valid(fobj))
this:remove_feature(fobj);
elseif (`valid(loc = $object_utils:has_callable_verb(fobj, verb)[1]) ! ANY => 0')
vargs = verb_args(loc, verb);
if ((vargs[2] in plist) && ((vargs[1] in dlist) && (vargs[3] in ilist)))
"(got rid of notify_huh - should write a @which command)";
"if (this.notify_huh)";
"player:notify(tostr(\"Using \", what.name, \" (\", what, \")\"));";
"endif";
set_task_perms(permissions);
fobj:(verb)(@pass);
"Problem with verbs of the same name. If we use which=vrb in the loop instead, we have a problem with verbs that use the variable verb.";
return 1;
endif
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
.
#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);
return 1;
elseif (verb in {"give", "hand", "get", "take", "drop", "throw"})
$last_huh:(verb)(@args);
return 1;
else
return 0;
endif
.
#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:title());
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
name = this.name;
name = strsub(name, "_", " ");
if (this in players())
if (!(this in connected_players()))
name = name + " (asleep)";
elseif (idle_seconds(this) > 300)
name = tostr(name, " (idle ", $time_utils:short_format(idle_seconds(this)), ")");
endif
endif
return name;
"Last modified Fri Feb  6 08:42:23 1998 CST by Wizard (#2).";
.
#6:10
line = args[1];
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
elseif (!$object_utils:connected(this))
clear_property(this, "memory");
return;
elseif (!is_clear_property(this, "memory"))
if (idle_seconds(this) > 120)
task = $code_utils:queued_tasks_matching(E_NONE, E_NONE, "say emote", E_NONE, this)[1][1];
task && kill_task(task);
totell = this.memory;
clear_property(this, "memory");
this:tell_lines(totell);
this:tell();
this:tell("You took more than 2 minutes to type your statement/command, please try again.");
this:tell("Type: 'help output' for further information on output control.");
else
this.memory = {@this.memory, line};
return;
endif
elseif (this.pagelen)
"...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
"... 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)
"...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
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;
elseif (this.linelen > 0)
for l in (this:linesplit(line, this.linelen))
notify(this, l);
endfor
else
notify(this, line);
endif
"Last modified Sat Oct 18 16:56:12 1997 CDT by Wizard (#2).";
.
#6:11
if (($perm_utils:controls(caller_perms(), this) || (caller == this)) || (caller_perms() == this))
set_task_perms(caller_perms());
for line in ((typeof(lines = args[1]) != LIST) ? {lines} | lines)
this:notify(tostr(line));
endfor
else
return E_PERM;
endif
.
#6:12
":linesplit(line,len) => list of substrings of line";
"used by :notify to split up long lines if .linelen>0";
{line, len} = args;
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 = (nospace ? " " | "") + line[cutoff..$];
endwhile
return {@cline, line};
.
#6:13
return abs(this.linelen);
.
#6:14
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, strsub(this.more_msg, "%n", tostr(llen - howmany)));
this.linetask[1] = task_id();
endif
endif
this.linetask[2] = task_id();
.
#6:15
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:16
if (callers() ? (caller != this) && (!$perm_utils:controls(caller_perms(), this)) | (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 = toint(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
this:set_linelength(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:17
"@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 = toint(dobjstr)))
notify(player, "Page length can't be a negative number.");
return;
elseif (newlen == 0)
player.pagelen = 0;
notify(player, "Page buffering off.");
if (lb = this.linebuffer)
"queued text remains";
this:notify_lines(lb);
clear_property(this, "linebuffer");
endif
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, strsub(this.more_msg, "%n", tostr(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:18
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 (this:gag_p())
return;
endif
if (this.paranoid == 1)
$paranoid_db:add_data(this, {{@callers(), {player, "<cmd-line>", player}}, args});
elseif (this.paranoid == 2)
z = this:whodunnit({@callers(), {player, "", player}}, {this, $no_one}, {})[3];
args = {"(", z.name, " ", z, ") ", @args};
endif
endif
"Modified by Jan 5/26/99 to accommodate Xpress Logging Feature";
try
if (this.recording)
this.Xpress_log = {@this.Xpress_log, tostr(@args)};
endif
except error (ANY)
"Ignore errors, just make sure tell isn't broken";
endtry
pass(@args);
"Last modified Mon May 31 14:39:54 1999 CDT by Wizard (#2).";
.
#6:19
if (player in this.gaglist)
return 1;
elseif (gag = this.gaglist)
for x in (callers())
if (((x[1] == #-1) && (x[3] == #-1)) && (x[2] != ""))
elseif ((x[1] in gag) || (x[4] in gag))
return 1;
endif
endfor
endif
return 0;
"--- old definition --";
if (player in this.gaglist)
return 1;
elseif (this.gaglist)
for x in (callers())
if (valid(x[1]))
if (x[1] in this.gaglist)
return 1;
endif
endif
endfor
endif
return 0;
.
#6:20
":set_gaglist(@newlist) => this.gaglist = newlist";
if (!((caller == this) || $perm_utils:controls(caller_perms(), this)))
return E_PERM;
else
return this.gaglist = args;
endif
.
#6:21
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.");
elseif (children(p) && (verb != "@gag!"))
player:tell("If you really want to gag all descendents of ", $string_utils:nn(p), ", use `@gag! ", p, "' instead.");
else
changed = 1;
player:set_gaglist(@setadd(this.gaglist, p));
endif
endfor
if (changed)
this:("@listgag")();
endif
.
#6:22
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:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%n (%#)", this.gaglist)), "."));
endif
gl = {};
if (args)
player:notify("Searching for players who may be gagging you...");
for p in (players())
if ((typeof(`p.gaglist ! E_PERM') == LIST) && (this in p.gaglist))
gl = {@gl, p};
endif
$command_utils:suspend_if_needed(10, "...searching gaglist...");
endfor
if (gl || (!callers()))
player:notify(tostr($string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%n (%#)", gl), "No one"), " appear", (length(gl) <= 1) ? "s" | "", " to be gagging you."));
endif
endif
.
#6:23
if ((player != this) || ((caller != this) && (!$perm_utils:controls(caller_perms(), this))))
player:notify("Permission denied.");
elseif (dobjstr == "")
player:notify(tostr("Usage:  ", verb, " <player>  or  ", verb, " everyone"));
elseif (dobjstr == "everyone")
this.gaglist = {};
player:notify("You are no longer gagging anyone or anything.");
else
if (valid(dobj))
match = dobj;
elseif ((match = toobj(dobjstr)) > #0)
else
match = $string_utils:match(dobjstr, this.gaglist, "name", this.gaglist, "aliases");
endif
if (match == $failed_match)
player:notify(tostr("You don't seem to be gagging anything named ", dobjstr, "."));
elseif (match == $ambiguous_match)
player:notify(tostr("I don't know which \"", dobjstr, "\" you mean."));
else
this.gaglist = setremove(this.gaglist, match);
player:notify(tostr(valid(match) ? match.name | match, " removed from gag list."));
endif
this:("@listgag")();
endif
.
#6:24
{record, trust, mistrust} = args;
s = {this, "???", this};
for w in (record)
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:25
responsible = $paranoid_db:get_data(this);
if (length(verb) <= 6)
"@check, not @check-full";
n = 5;
trust = {this, $no_one};
"... trust no one, my friend.... no one....  --Herod";
mistrust = {};
for k in (args)
if (z = $code_utils:toint(k))
n = z;
elseif (k[1] == "!")
mistrust = listappend(mistrust, $string_utils:match_player(k[2..$]));
else
trust = listappend(trust, $string_utils:match_player(k));
endif
endfor
msg_width = player:linelen() - 60;
for q in ((n > (y = length(responsible))) ? responsible | responsible[(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 ***");
else
"@check-full, from @traceback by APHiD";
"s_i_n's by Ho_Yan 10/18/94";
matches = {};
if (length(match = argstr) == 0)
player:notify(tostr("Usage: ", verb, " <string> --or-- ", verb, " <number>"));
return;
endif
if (!responsible)
player:notify("No text has been saved by the monitor.  (See `help @paranoid').");
else
if (typeof(x = $code_utils:toint(argstr)) == ERR)
for line in (responsible)
if (index(tostr(@line[$]), argstr))
matches = {@matches, line};
endif
endfor
else
matches = responsible[($ - x) + 1..$];
endif
if (matches)
for match in (matches)
$command_utils:suspend_if_needed(3);
text = tostr(@match[$]);
player:notify("Traceback for:");
player:notify(text);
"Moved cool display code to $code_utils, 3/29/95, Ho_Yan";
$code_utils:display_callers(listdelete(mm = match[1], length(mm)));
endfor
player:notify("**** finished ****");
else
player:notify(tostr("No matches for \"", argstr, "\" found."));
endif
endif
endif
.
#6:26
if ((args == {}) || ((typ = args[1]) == ""))
$paranoid_db:set_kept_lines(this, 10);
this.paranoid = 1;
this:notify("Anti-spoofer on and keeping 10 lines.");
elseif (index("immediate", typ))
$paranoid_db:set_kept_lines(this, 0);
this.paranoid = 2;
this:notify("Anti-spoofer now in immediate mode.");
elseif (index("off", typ) || (typ == "0"))
this.paranoid = 0;
$paranoid_db:set_kept_lines(this, 0);
this:notify("Anti-spoofer off.");
elseif ((tostr(y = toint(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;
$paranoid_db:set_kept_lines(this, y = min(y, 30));
this:notify(tostr("Anti-spoofer on and keeping ", y, " lines."));
endif
.
#6:27
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", "here_explain_syntax"})
vwhere = $object_utils:has_verb(here, v);
if (vwhere && (((owner = verb_info(vwhere[1], v)[1]) != this) && (!owner.wizard)))
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:28
this:tell(player.name, " whispers, \"", dobjstr, "\"");
player:tell("You whisper, \"", dobjstr, "\" to ", this.name, ".");
.
#6:29
nargs = length(args);
short = 0;
if (((verb == "page") && (nargs < 2)) || (nargs < 1))
player:tell("Usage: page <player> <message> Standard page");
player:tell("           -<player> <message> Quick page");
player:tell("           - <message> Reply to last page");
player:tell("           +<player> <message> Remote emote");
player:tell("           + <message> Reply to last emote");
return;
endif
if (length(verb) <= 1)
who = $string_utils:match_player(player.last_contacted.name);
short = 1;
elseif (((verb[1] == "-") || (verb[1] == "'")) || (verb[1] == "+"))
who = $string_utils:match_player(verb[2..length(verb)]);
short = 1;
else
who = $string_utils:match_player(args[1]);
endif
if (who == $failed_match)
return player:tell("Sorry, cannot find any players by that name.");
elseif (who in this.gaglist)
player:tell("You have ", who:title(), " @gagged.  If you paged ", $gender_utils:get_pronoun("o", who), ", ", $gender_utils:get_pronoun("s", who), " wouldn't be able to answer you.");
return;
endif
"for pronoun_sub's benefit...";
dobj = who;
iobj = player;
header = player:page_origin_msg();
text = "";
if (nargs >= 1)
if (short)
msg_start = 1;
elseif ((args[2] == "with") && (nargs > 2))
msg_start = 3;
else
msg_start = 2;
endif
msg = $string_utils:from_list(args[msg_start..nargs], " ");
if (verb[1] == "+")
text = tostr($string_utils:pronoun_sub("(from %l) %N "), msg);
result = text ? who:receive_page(text) | who:receive_page(header);
else
text = tostr($string_utils:pronoun_sub(($string_utils:index_delimited(header, player.name) ? "%S" | "%N") + " %<pages>, \""), msg, "\"");
result = text ? who:receive_page(header, text) | who:receive_page(header);
endif
if (result == 2)
"not connected";
player:tell((typeof(msg = who:page_absent_msg()) == STR) ? msg | $string_utils:pronoun_sub("%n is not currently logged in.", who));
else
if (verb[1] == "+")
player:tell(who:titlec(), " received your emote");
else
player:tell(who:page_echo_msg());
endif
who.last_contacted = player;
endif
endif
"Last modified Wed Apr 15 12:17:42 1998 CDT by Wizard (#2).";
.
#6:30
"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 codes:";
"  1:  page was received";
"  2:  player is not connected";
"  0:  page refused";
"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.";
if (this:is_listening())
this:tell_lines(args);
return 1;
else
return 2;
endif
.
#6:31
"set_task_perms(this.owner)";
return (msg = `this.(verb) ! ANY') ? $string_utils:pronoun_sub(this.(verb), this) | "";
.
#6:32
if (c = player:contents())
this:tell_contents(c);
else
player:tell("You are empty-handed.");
endif
.
#6:33
if (!player.ts_client)
player:tell(this:titlec());
endif
pass();
if (!(this in connected_players()))
player:tell($gender_utils:pronoun_sub("%{:He} %{!is} sleeping.", this));
elseif ((idle = idle_seconds(this)) < 60)
player:tell($gender_utils:pronoun_sub("%{:He} %{!is} awake and %{!looks} alert.", this));
else
time = $string_utils:from_seconds(idle);
player:tell($gender_utils:pronoun_sub("%{:He} %{!is} awake, but %{!has} been staring off into space for ", this), time, ".");
endif
if (c = this:contents())
this:tell_contents(c);
endif
"Last modified Thu Apr  8 12:28:43 1999 CDT by Wizard (#2).";
.
#6:34
start = this.location;
if (start == this.home)
player:tell("You're already home!");
return;
elseif (typeof(this.home) != OBJ)
player:tell("You've got a weird home, pal.  I've reset it to the default one.");
this.home = $player_start;
elseif (!valid(this.home))
player:tell("Oh no!  Your home's been recycled.  Time to look around for a new one.");
this.home = $player_start;
else
player:tell("You click your heels three times.");
endif
this:moveto(this.home);
if (!valid(start))
elseif (start == this.location)
start:announce(player.name, " ", $gender_utils:get_conj("learns", player), " that you can never go home...");
else
start:announce(player.name, " ", $gender_utils:get_conj("goes", player), " home.");
endif
if (this.location == this.home)
this.location:announce(player.name, " ", $gender_utils:get_conj("comes", player), " 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, " ", $gender_utils:get_conj("arrives", player), ", looking quite bewildered.");
endif
endif
.
#6:35
set_task_perms(this);
here = this.location;
if (!$object_utils:has_callable_verb(here, "accept_for_abode"))
player:notify("This is a pretty odd place.  You should make your home in an actual room.");
elseif (here:accept_for_abode(this))
this.home = here;
player:notify(tostr(here.name, " is your new home."));
else
player:notify(tostr("This place doesn't want to be your home.  Contact ", here.owner.name, " to be added to the residents list of this place, or choose another place as your home."));
endif
.
#6:36
player:tell("This is not a pick-up joint!");
this:tell(player.name, " tried to pick you up.");
.
#6:37
"'@move <object> to <place>' - Teleport an object. Example: '@move trash to #11' to move trash to the closet.";
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.name, " is already ", valid(iobj) ? "in " + iobj.name | "nowhere", ".");
return;
endif
dobj:moveto(iobj);
if (dobj.location == iobj)
player:tell("Moved.");
if (is_player(dobj))
if (valid(old_loc))
old_loc:announce_all(dobj.name, " disappears suddenly for parts unknown.");
if (dobj != player)
dobj:tell("You have been moved by ", player.name, ".");
endif
endif
if (valid(dobj.location))
dobj.location:announce(dobj.name, " materializes out of thin air.");
endif
endif
elseif (dobj.location == old_loc)
if ($object_utils:contains(dobj, iobj))
player:tell(iobj.name, " is inside of ", dobj.name, "!");
else
player:tell($string_utils:pronoun_sub("Either %d doesn't want to go, or %i doesn't want to accept %[dpo]."));
endif
elseif (dobj == player)
player:tell("You have been deflected from your original destination.");
else
player:tell($string_utils:pronoun_sub("%D has been deflected from %[dpp] original destination."));
endif
.
#6:38
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:((verb == "@eject") ? "eject" | "eject_basic")(dobj);
player:notify($object_utils:has_callable_verb(iobj, "ejection_msg") ? iobj:ejection_msg() | $room:ejection_msg());
if (verb != "@eject!!")
dobj:tell($object_utils:has_callable_verb(iobj, "victim_ejection_msg") ? iobj:victim_ejection_msg() | $room:victim_ejection_msg());
endif
iobj:announce_all_but({player, dobj}, $object_utils:has_callable_verb(iobj, "oejection_msg") ? iobj:oejection_msg() | $room:oejection_msg());
.
#6:39
if (!args)
them = connected_players();
else
who = $command_utils:player_match_result($string_utils: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)
player:notify(tostr($string_utils:left($string_utils:nn(p), 25), " ", $string_utils:nn(p.location)));
endfor
.
#6:40
if (caller != player)
return E_PERM;
endif
plyrs = args ? listdelete($command_utils:player_match_result($string_utils:match_player(args), args), 1) | connected_players();
"Remove invisible players from the who-listing";
visible_players = {};
for person in (plyrs)
if (!person.invis)
visible_players = {@visible_players, person};
endif
endfor
if (!plyrs)
return;
elseif (length(plyrs) > 100)
player:tell("You have requested a listing of ", length(plyrs), " players.  Please either specify individual players you are interested in, to reduce the number of players in any single request, or else use the `@users' command instead.  The lag thanks you.");
return;
endif
$code_utils:show_who_listing(visible_players);
"Last modified Fri Jun  9 15:19:11 2000 CDT by Wizard (#2).";
.
#6:41
"@wizards [all]";
if (caller != player)
return E_PERM;
endif
if (args)
$code_utils:show_who_listing($wiz_utils:all_wizards());
else
$code_utils:show_who_listing($wiz_utils:connected_wizards()) || player:notify("No wizards currently logged in.");
endif
.
#6:42
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..$];
endif
"...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)
"... note: all of the last-resort stuff...";
"... is now located on $help:find_topics/get_topic...";
$wiz_utils:missed_help(what, result);
player:notify(tostr("Sorry, but no help is available on `", what, "'."));
elseif (result[1] == $ambiguous_match)
$wiz_utils:missed_help(what, result);
player:notify_lines(tostr("Sorry, but the topic-name `", what, "' is ambiguous.  I don't know which of the following topics you mean:"));
for x in ($help:columnize(@$help:sort_topics(result[2])))
player:notify(tostr("   ", x));
endfor
else
{help, topic} = result;
if (topic != what)
player:notify(tostr("Showing help on `", topic, "':"));
player:notify("----");
endif
dblist = dblist[1 + (help in dblist)..$];
if (1 == (text = help:get_topic(topic, dblist)))
"...get_topic took matters into its own hands...";
elseif (text)
"...these can get long...";
for line in ((typeof(text) == LIST) ? text | {text})
if (typeof(line) != STR)
player:notify("Odd results from help -- complain to a wizard.");
else
player:notify(line);
endif
$command_utils:suspend_if_needed(0);
endfor
else
player:notify(tostr("Help DB ", help, " thinks it knows about `", what, "' but something's messed up."));
player:notify(tostr("Tell ", help.owner.wizard ? "" | tostr(help.owner.name, " (", help.owner, ") or "), "a wizard."));
endif
endif
.
#6:43
":display_option(name) => returns the value of the specified @display option";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return $display_options:get(this.display_options, args[1]);
else
return E_PERM;
endif
.
#6:44
":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:45
":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
.
#6:46
"@<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 = {"mail", "edit", "display"}[index("med", 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:47
"set_name(newname) attempts to change this.name to newname";
"  => 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.";
"  => E_ARGS   if the name is too long (controlled by $login.max_player_name)";
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.";
set_task_perms(caller_perms());
return pass(@args);
elseif ($player_db.frozen)
return E_NACC;
elseif (length(name = args[1]) > $login.max_player_name)
return E_ARGS;
elseif (!($player_db:available(name, this) in {this, 1}))
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
.
#6:48
"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.";
"  => 1        if successful, and aliases changed from previous setting.";
"  => 0        if resulting work didn't change aliases from previous.";
"  => 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";
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 = args[1]) != LIST)
return E_TYPE;
elseif ((length(aliases = setadd(aliases, this.name)) > ($object_utils:has_property($local, "max_player_aliases") ? $local.max_player_aliases | $maxint)) && (length(aliases) >= length(this.aliases)))
return E_INVARG;
else
for a in (aliases)
if (typeof(a) != STR)
return E_INVARG;
endif
if ((!(index(a, " ") || index(a, "	"))) && (!($player_db:available(a, this) in {this, 1})))
aliases = setremove(aliases, a);
endif
endfor
changed = 0;
old = this.aliases;
this.aliases = aliases;
for a in (old)
if (!(a in aliases))
$player_db:delete2(a, this);
changed = 1;
endif
endfor
for a in (aliases)
if (!(index(a, " ") || index(a, "	")))
$player_db:insert(a, this);
if (!(a in old))
changed = 1;
endif
endif
endfor
return changed;
endif
.
#6:49
if ((player != caller) || (player != this))
return;
endif
set_task_perms(player);
bynumber = verb == "@rename#";
spec = $code_utils:parse_verbref(dobjstr);
if (spec)
if (!player.programmer)
return player:notify(tostr(E_PERM));
endif
object = this:my_match_object(spec[1]);
if (!$command_utils:object_match_failed(object, spec[1]))
vname = spec[2];
if (bynumber)
vname = $code_utils:toint(vname);
if (vname == E_TYPE)
return player:notify("Verb number expected.");
elseif ((vname < 1) || `vname > length(verbs(object)) ! E_PERM => 0')
return player:notify("Verb number out of range.");
endif
endif
try
info = verb_info(object, vname);
try
result = set_verb_info(object, vname, listset(info, iobjstr, 3));
player:notify("Verb name changed.");
except e (ANY)
player:notify(e[2]);
endtry
except (E_VERBNF)
player:notify("That object does not define that verb.");
except e (ANY)
player:notify(e[2]);
endtry
endif
elseif (bynumber)
player:notify("@rename# can only be used with verbs.");
elseif (pspec = $code_utils:parse_propref(dobjstr))
if (!player.programmer)
return player:notify(tostr(E_PERM));
endif
object = this:my_match_object(pspec[1]);
if (!$command_utils:object_match_failed(object, pspec[1]))
pname = pspec[2];
try
info = property_info(object, pname);
try
result = set_property_info(object, pname, {@info, iobjstr});
player:notify("Property name changed.");
except e (ANY)
player:notify(e[2]);
endtry
except (E_PROPNF)
player:notify("That object does not define that property.");
except e (ANY)
player:notify(e[2]);
endtry
endif
else
object = this:my_match_object(dobjstr);
if (!$command_utils:object_match_failed(object, dobjstr))
old_name = object.name;
old_aliases = object.aliases;
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, "\"");
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 not allowed (see help @rename).");
if (object == player)
player:notify($player_db:why_bad_name(player, iobjstr));
endif
elseif (e == E_NACC)
player:notify("Oops.  You can't update that name right now; try again in a few minutes.");
elseif (e == E_ARGS)
player:notify(tostr("Sorry, name too long.  Maximum number of characters in a name:  ", $login.max_player_name));
elseif (e == 0)
player:notify("Name and aliases remain unchanged.");
else
player:notify(tostr(e));
endif
endif
endif
.
#6:50
"Syntax: @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;
endif
set_task_perms(player);
bynumber = verb[$] == "#";
spec = $code_utils:parse_verbref(iobjstr);
if (spec)
if (!player.programmer)
return player:notify(tostr(E_PERM));
endif
object = player:my_match_object(spec[1]);
if (!$command_utils:object_match_failed(object, spec[1]))
vname = spec[2];
if (bynumber)
if ((vname = $code_utils:toint(vname)) == E_TYPE)
return player:notify("Verb number expected.");
elseif ((vname < 1) || `vname > length(verbs(object)) ! E_PERM => 0')
return player:notify("Verb number out of range.");
endif
endif
try
info = verb_info(object, vname);
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);
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, "\""));
except e (ANY)
player:notify(e[2]);
endtry
endif
except (E_VERBNF)
player:notify("That object does not define that verb.");
except e (ANY)
player:notify(e[2]);
endtry
endif
elseif (bynumber)
player:notify(tostr(verb, " can only be used with 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(is_player(object) ? strsub(dobjstr, " ", ",") | 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})) && (object.aliases == aliases))
player:notify(tostr("Alias", (length(new_aliases) > 1) ? "es" | "", " ", $string_utils:english_list(new_aliases), " added to ", object.name, "(", object, ")."));
player:notify(tostr("Aliases for ", $string_utils:nn(object), " are now ", $string_utils:from_value(aliases, 1)));
elseif (e)
player:notify("That particular name change not allowed (see help @rename or help @addalias).");
elseif (e == E_INVARG)
if ($object_utils:has_property(#0, "local"))
if ($object_utils:has_property($local, "max_player_aliases"))
max = $local.max_player_aliases;
player:notify(("You are not allowed more than " + tostr(max)) + " aliases.");
endif
else
player:notify("You are not allowed any more aliases.");
endif
elseif (e == E_NACC)
player:notify("Oops.  You can't update that object's aliases right now; try again in a few minutes.");
elseif (e == 0)
player:notify("Aliases not changed as expected!");
player:notify(tostr("Aliases for ", $string_utils:nn(object), " are now ", $string_utils:from_value(object.aliases, 1)));
else
player:notify(tostr(e));
endif
endif
endif
endif
.
#6:51
"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.";
"[5/10/93 Nosredna: flushed above is_player feature";
"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);
bynumber = verb[$] == "#";
spec = $code_utils:parse_verbref(iobjstr);
if (spec)
if (!player.programmer)
player:notify(tostr(E_PERM));
endif
object = player:my_match_object(spec[1]);
if (!$command_utils:object_match_failed(object, spec[1]))
vname = spec[2];
if (bynumber)
if ((vname = $code_utils:toint(vname)) == E_TYPE)
return player:notify("Verb number expected.");
elseif ((vname < 1) || `vname > length(verbs(object)) ! E_PERM => 0')
return player:notify("Verb number out of range.");
endif
endif
try
info = verb_info(object, vname);
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
result = set_verb_info(object, vname, info);
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, "):\"", aliases, "\""));
except e (ANY)
player:notify(e[2]);
endtry
elseif (!old_aliases)
player:notify("You have to leave a verb with at least one alias.");
endif
except (E_VERBNF)
player:notify("That object does not define that verb.");
except e (ANY)
player:notify(e[2]);
endtry
endif
elseif (bynumber)
player:notify(tostr(verb, " can only be used with verbs."));
else
object = player:my_match_object(iobjstr);
if (!$command_utils:object_match_failed(object, iobjstr))
old_aliases = object.aliases;
not_used = {};
for alias in (bad_aliases = $list_utils:remove_duplicates($list_utils:map_arg($string_utils, "trim", $string_utils:explode(dobjstr, ","))))
"removed is_player(object) ? strsub(dobjstr, \" \", \",\") | --Nosredna";
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(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.");
elseif (e == 0)
player:notify("Aliases not changed as expected!");
player:notify(tostr("Aliases for ", $string_utils:nn(object), " are ", $string_utils:from_value(object.aliases, 1)));
else
player:notify(tostr(e));
endif
endif
endif
endif
.
#6:52
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:53
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
found_one = 0;
props = $object_utils:all_properties(dobj);
if (typeof(props) == ERR)
player:notify("You can't read the messages on that.");
return;
endif
for pname in (props)
len = length(pname);
if ((len > 4) && (pname[len - 3..len] == "_msg"))
found_one = 1;
msg = `dobj.(pname) ! ANY';
if (msg == E_PERM)
value = "isn't readable by you.";
elseif (!msg)
value = "isn't set.";
elseif (typeof(msg) == LIST)
value = "is a list.";
elseif (typeof(msg) != STR)
value = "is corrupted! **";
else
value = "is " + $string_utils:print(msg);
endif
player:notify(tostr("@", pname[1..len - 4], " ", dobjstr, " ", value));
endif
endfor
if (!found_one)
player:notify("That object doesn't have any messages to set.");
endif
.
#6:54
$note_editor:invoke(dobjstr, verb);
.
#6:55
"@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 ", this: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:56
"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:set.";
if (!($perm_utils:controls(caller_perms(), this) || (this == caller)))
return E_PERM;
else
result = $gender_utils:set(this, args[1]);
this.gender = (typeof(result) == STR) ? result | args[1];
return result;
endif
.
#6:57
set_task_perms(valid(caller_perms()) ? caller_perms() | player);
if (!args)
player:notify(tostr("Your gender is currently ", this.gender, "."));
player:notify($string_utils:pronoun_sub("Your pronouns:  %s,%o,%p,%q,%r,%S,%O,%P,%Q,%R"));
player:notify(tostr("Available genders:  ", $string_utils:english_list($gender_utils.genders, "", " or ")));
else
result = this:set_gender(args[1]);
quote = (result == E_NONE) ? "\"" | "";
player:notify(tostr("Gender set to ", quote, this.gender, quote, "."));
if (typeof(result) != ERR)
player:notify($string_utils:pronoun_sub("Your pronouns:  %s,%o,%p,%q,%r,%S,%O,%P,%Q,%R"));
elseif (result != E_NONE)
player:notify(tostr("Couldn't set pronouns:  ", result));
else
player:notify("Pronouns unchanged.");
endif
endif
.
#6:58
"set_brief(value)";
"set_brief(value, anything)";
"If <anything> is given, add value to the current value; otherwise, just set the value.";
if (!($perm_utils:controls(caller_perms(), this) || (this == caller)))
return E_PERM;
else
if (length(args) == 1)
this.brief = args[1];
else
this.brief = this.brief + args[1];
endif
endif
.
#6:59
"@mode <mode>";
"Current modes are brief and verbose.";
"General verb for setting player `modes'.";
"Modes are coded right here in the verb.";
if (caller != this)
player:tell("You can't set someone else's modes.");
return E_PERM;
endif
modes = {"brief", "verbose"};
mode = modes[$string_utils:find_prefix(dobjstr, modes)];
if (!mode)
player:tell("Unknown mode \"", dobjstr, "\".  Known modes:");
for mode in (modes)
player:tell("  ", mode);
endfor
return 0;
elseif (mode == "brief")
this:set_brief(1);
elseif (mode == "verbose")
this:set_brief(0);
endif
player:tell($string_utils:capitalize(mode), " mode set.");
return 1;
.
#6:60
"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, $prog};
what = what;
printed_working_msg = 0;
while (what != $nothing)
if ($command_utils:running_out_of_time())
if (!printed_working_msg)
player:notify("Working on list of obvious verbs...");
printed_working_msg = 1;
endif
suspend(0);
endif
if (!(what in dull_classes))
for i in [1..length(verbs(what))]
if ($command_utils:running_out_of_time())
if (!printed_working_msg)
player:notify("Working on list of obvious verbs...");
printed_working_msg = 1;
endif
suspend(0);
endif
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, prep, iobj} = syntax;
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 (j = index(vname, "* "))
vname = tostr(vname[1..j - 1], "<anything>", vname[j + 1..$]);
endwhile
if (vname[$] == "*")
vname = vname[1..$ - 1] + "<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);
printed_working_msg && player:notify("(End of list.)");
elseif (printed_working_msg)
player:notify("No obvious verbs found.");
endif
.
#6:61
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:62
"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.";
try
feature:feature_add(this);
except (ANY)
"just ignore errors.";
endtry
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
.
#6:63
"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.";
try
feature:feature_remove(this);
except (ANY)
"just ignore errors.";
endtry
return 1;
"We're done.";
else
return E_PERM;
"Caller didn't have permission.";
endif
.
#6:64
"Usage:";
"  @add-feature";
"  @add-feature <feature object>";
"Modified 10 Oct 94, by Michele, to check the warehouse and match.";
"Lists all features or adds an object to your features list.";
set_task_perms(player);
if (dobjstr)
if (dobj == $failed_match)
dobj = $feature.warehouse:match_object(dobjstr);
endif
if (!$command_utils:object_match_failed(dobj, dobjstr))
if (dobj in player.features)
player:tell(dobjstr, " is already one of your features.");
elseif (player:add_feature(dobj))
player:tell(dobj, " (", dobj.name, ") added as a feature.");
else
player:tell("You can't seem to add ", dobj, " (", dobj.name, ") to your features list.");
endif
endif
else
player:tell("Usage:  @add-feature <object>");
if (length($feature.warehouse.contents) < 20)
player:tell("Available features include:");
player:tell("--------------------------");
fe = {};
for c in ($feature.warehouse.contents)
fe = {(c in player.features) ? c:title() + " (*)" | c:title()};
player:tell("  " + $string_utils:english_list(fe));
endfor
player:tell("--------------------------");
player:tell("A * after the feature name means that you already have that feature.");
endif
endif
.
#6:65
"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:66
"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 = $string_utils:match_player(iobjstr), iobjstr))
return;
endif
features = {};
for feature in (whose.features)
if (!valid(feature))
whose:remove_feature(feature);
elseif ((!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), "----");
cstr = ((tostr(length(features)) + " feature") + ((length(features) > 1) ? "s" | "")) + " found";
if (whose != this)
cstr = ((((cstr + " on ") + whose.name) + " (") + tostr(whose)) + ")";
endif
if (dobjstr)
cstr = ((cstr + " matching \"") + dobjstr) + "\"";
endif
cstr = cstr + ".";
player:tell(cstr);
elseif (dobjstr)
player:tell("No features found on ", whose.name, " (", whose, ") matching \"", dobjstr, "\".");
else
player:tell("No features found on ", whose.name, " (", whose, ").");
endif
.
#6:67
"Usage:  @features [<name>]";
"List the feature objects matching <name> used by player.";
iobjstr = player.name;
iobj = player;
this:("@features")();
.
#6:68
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:69
if ($object_utils:has_property($local, "server_hardware"))
hw = (" on " + $local.server_hardware) + ".";
else
hw = ".";
endif
player:notify(tostr("The MOO is currently running version ", server_version(), " of the LambdaMOO server code", hw));
try
{MOOname, sversion, coretime} = $core_history[1];
player:notify(tostr("The database is based on ", MOOname, " which was created on ", $time_utils:time_sub("$n $t, $Y", coretime), " for version ", sversion, " of the LambdaMOO server."));
except (E_RANGE)
player:notify("The database was created from scratch.");
except (ANY)
player:notify("No information is available on the database version.");
endtry
"Last modified Tue Oct 14 14:11:34 1997 CDT by Wizard (#2).";
.
#6:70
player:notify(tostr($network.MOO_name, " has been up for ", $time_utils:english_time(time() - $last_restart_time), "."));
player:notify(tostr("The database was last saved to disk ", $time_utils:english_time(time() - $last_dump_time), " ago."));
"Last modified Fri Feb  6 08:41:12 1998 CST by Wizard (#2).";
.
#6:71
boot_player(player);
"-- argh, let the player decide; #3:disfunc() takes care of this --Rog";
"player:moveto(player.home)";
.
#6:72
return this == args[1];
.
#6:73
"return true if player is active.";
return typeof(`idle_seconds(this) ! ANY') != ERR;
.
#6:74
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:75
return this.location:(verb)(@args);
"temporarily let player:announce be noisy to player";
if (verb == "announce_all_but")
if (this in args[1])
return;
endif
args = args[2..$];
endif
this:tell("(from within you) ", @args);
.
#6:76
"Return a true value if this needs linewrapping.";
"default is true if .linelen > 0";
return this.linelen > 0;
.
#6:77
"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();
if ((verb == "@set-note-string") && (length(text) <= 1))
text = text ? text[1] | "";
endif
if (spec = $code_utils:parse_propref(argstr))
o = player:my_match_object(spec[1]);
p = spec[2];
if ($object_utils:has_verb(o, vb = "set_" + p) && (typeof(e = o:(vb)(text)) != ERR))
player:tell("Set ", p, " property of ", o.name, " (", o, ") via :", vb, ".");
elseif (text != (e = `o.(p) = text ! ANY'))
player:tell("Error:  ", e);
else
player:tell("Set ", p, " property of ", o.name, " (", o, ").");
endif
elseif (typeof(note = $code_utils:toobj(argstr)) == OBJ)
e = note:set_text(text);
if (typeof(e) == ERR)
player:tell("Error:  ", e);
else
player:tell("Set text of ", note.name, " (", note, ").");
endif
else
player:tell("Error:  Malformed argument to ", verb, ": ", argstr);
endif
"Last modified Sat Oct 18 15:10:32 1997 CDT by Wizard (#2).";
.
#6:78
text = args[1];
if (a = `$list_utils:assoc(text, this.verb_subs) ! ANY')
return a[2];
else
return $gender_utils:get_conj(text, this);
endif
.
#6:79
if ($perm_utils:controls(caller_perms(), this))
return this.(verb);
else
return E_PERM;
endif
.
#6:80
lines = args[1];
if (typeof(lines) != LIST)
lines = {lines};
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 (this:gag_p())
return;
endif
if (this.paranoid == 2)
z = this:whodunnit({@callers(), {player, "", player}}, {this, $no_one}, {})[3];
lines = {((("[start text by " + z.name) + " (") + tostr(z)) + ")]", @lines, ((("[end text by " + z.name) + " (") + tostr(z)) + ")]"};
elseif (this.paranoid == 1)
$paranoid_db:add_data(this, {{@callers(), {player, "<cmd-line>", player}}, lines});
endif
endif
"don't gather stats for now: $list_utils:check_nonstring_tell_lines(lines)";
"Modified by Jan 5/26/99 to accommodate Xpress Logging Feature";
try
if (this.recording)
this.Xpress_log = {@this.Xpress_log, @lines};
endif
except error (ANY)
"Ignore errors, just make sure tell isn't broken";
endtry
this:notify_lines(lines);
"Last modified Mon May 31 14:39:54 1999 CDT by Wizard (#2).";
.
#6:81
"Copied from generic room (#3):@lastlog by Haakon (#2) Wed Dec 30 13:30:02 1992 PST";
if (dobjstr != "")
dobj = $string_utils: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)
when = x.last_connect_time;
how_long = now - when;
if ((when == 0) || (when > now))
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
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
.
#6:82
"Set linelength.  Linelength must be an integer >= 10.";
"If wrap is currently off (i.e. linelength is less than 0), maintains sign.  That is, this function *takes* an absolute value, and coerces the sign to be appropriate.";
"If you want to override the dwimming of wrap, pass in a second argument.";
"returns E_PERM if not allowed, E_INVARG if linelength is too low, otherwise the linelength.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
elseif (abs(len = args[1]) < 10)
return E_INVARG;
elseif (length(args) > 1)
this.linelen = len;
else
"DWIM here.";
this.linelen = (this.linelen > 0) ? len | (-len);
return len;
endif
.
#6:83
"Set pagelength. Must be an integer >= 5, or 0 to turn pagelength off.";
"Returns E_PERM if you shouldn't be doing this, E_INVARG if it's too low, otherwise, what it got set to.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
elseif (((len = args[1]) < 5) && (len != 0))
return E_INVARG;
else
if ((this.pagelen = len) == 0)
if (lb = this.linebuffer)
"queued text remains";
this:notify_lines(lb);
clear_property(this, "linebuffer");
endif
endif
return len;
endif
.
#6:84
"set_home(newhome) attempts to change this.home to newhome";
"E_TYPE   if newhome doesn't have a callable :accept_for_abode verb.";
"E_INVARG if newhome won't accept you as a resident.";
"E_PERM   if you don't own this and aren't its parent.";
"1        if it works.";
newhome = args[1];
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
if ($object_utils:has_callable_verb(newhome, "accept_for_abode"))
if (newhome:accept_for_abode(this))
return (typeof(e = `this.home = args[1] ! ANY') != ERR) || e;
else
return E_INVARG;
endif
else
return E_TYPE;
endif
else
return E_PERM;
endif
.
#6:85
"@registerme as <email-address> -- enter a new email address for player";
"   will change the database entry, assign a new password, and mail the new password to the player at the given email address.";
if (player != this)
return player:notify(tostr(E_PERM));
endif
who = this;
if ($object_utils:isa(this, $guest))
who:notify("Sorry, guests should use the '@request' command to request a character.");
return;
endif
connection = $string_utils:connection_hostname(connection_name(who));
if (!argstr)
if (who.email_address)
player:tell("You are currently registered as:  ", who.email_address);
else
player:tell("You are not currently registered.");
endif
player:tell("Use @registerme as <address> to change this.");
return;
elseif (((prepstr != "as") || (!iobjstr)) || dobjstr)
player:tell("Usage: @registerme as <address>");
return;
endif
email = iobjstr;
if (email == this.email_address)
who:notify("That is your current address.  Not changed.");
return;
elseif (reason = $wiz_utils:check_reregistration(this, email, connection))
if (reason[1] == "-")
if (!$command_utils:yes_or_no(reason[2..$] + ". Automatic registration not allowed. Ask to be registered at this address anyway?"))
who:notify("Okay.");
return;
endif
else
return who:notify(tostr(reason, " Please try again."));
endif
endif
if ($network.active && (!reason))
if (!$command_utils:yes_or_no(tostr("If you continue, your password will be changed, the new password mailed to `", email, "'. Do you want to continue?")))
return who:notify("Registration terminated.");
endif
password = $wiz_utils:random_password(5);
old = who.email_address || "[ unregistered ]";
who:notify(tostr("Registering you, and changing your password and mailing new one 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)
who:notify(tostr("Mail sending did not work: ", reason, ". Reregistration terminated."));
return;
endif
who:notify(tostr("Mail with your new password forwarded. If you do not get it, send regular email to ", $login.registration_address, " with your character name."));
$mail_agent:send_message($new_player_log, $new_player_log, "reg " + $string_utils:nn(this), {email, tostr("formerly ", old)});
$registration_db:add(this, email, "Reregistered at " + ctime());
this.email_address = email;
who.password = crypt(password);
who.last_password_time = time();
else
who:notify("No automatic reregistration: your request will be forwarded.");
if (typeof(curreg = $registration_db:find(email)) == LIST)
additional_info = {"Current registration information for this email address:", @$registration_db:describe_registration(curreg)};
else
additional_info = {};
endif
$mail_agent:send_message(this, $registration_db.registrar, "Registration request", {((("Reregistration request from " + $string_utils:nn(who)) + " connected via ") + connection) + ":", "", (("@register " + who.name) + " ") + email, ("@new-password " + who.name) + " is ", "", "Reason this request was forwarded:", reason, @additional_info});
endif
.
#6:86
":ctime([INT time]) => STR as the function.";
"May be hacked by players and player-classes to reflect differences in time-zone.";
return ctime(@args);
.
#6:87
if ((dobjstr == "") || (dobj == player))
dobj = player;
else
dobj = $string_utils: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
.
#6:88
"Calls the verb editor on verbs, the note editor on properties, and on anything else assumes it's an object for which you want to edit the .description.";
if (!args)
((player in $note_editor.active) ? $note_editor | $verb_editor):invoke(dobjstr, verb);
elseif ($code_utils:parse_verbref(args[1]))
if (player.programmer)
$verb_editor:invoke(argstr, verb);
else
player:notify("You need to be a programmer to do this.");
player:notify("If you want to become a programmer, talk to a wizard.");
return;
endif
else
$note_editor:invoke(dobjstr, verb);
endif
.
#6:89
if (!($perm_utils:controls(caller_perms(), this) || (this == caller)))
return E_PERM;
else
$paranoid_db:erase_data(this);
endif
.
#6:90
"'@move <object> to <place>' - Teleport an object. Example: '@move trash to #11' to move trash to the closet.";
set_task_perms((caller == this) ? this | $no_one);
if ((prepstr != "to") || (!iobjstr))
player:tell("Usage: @move <object> to <location>");
return;
endif
if ((!dobjstr) || (dobjstr == "me"))
dobj = this;
else
dobj = here:match_object(dobjstr);
if (!valid(dobj))
dobj = player:my_match_object(dobjstr);
endif
endif
if ($command_utils:object_match_failed(dobj, dobjstr))
return;
endif
iobj = this:lookup_room(iobjstr);
if ((iobj != $nothing) && $command_utils:object_match_failed(iobj, iobjstr))
return;
endif
if (((!player.programmer) && (!$perm_utils:controls(this, dobj))) && (this != dobj))
player:tell("You may only @move your own things.");
return;
endif
this:teleport(dobj, iobj);
.
#6:91
if (($perm_utils:controls(caller_perms(), this) || (caller == this)) || (caller_perms() == this))
set_task_perms(caller_perms());
for line in ((typeof(lines = args[1]) != LIST) ? {lines} | lines)
$command_utils:suspend_if_needed(0);
this:notify(tostr(line));
endfor
else
return E_PERM;
endif
.
#6:92
set_task_perms(caller_perms());
return chparent(@args);
.
#6:93
"Prints a count and compact list of the currently-connected players.";
cp = connected_players();
linelen = abs(player.linelen) || 79;
player:tell_lines({tostr("There are ", length(cp), " players connected:"), @$generic_editor:fill_string($string_utils:english_list($list_utils:sort_suspended(0, $list_utils:map_prop(cp, "name"))), linelen)});
.
#6:94
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;
elseif (is_clear_property(player, "password"))
player:notify("Your password has a `clear' property.  Please refer to a wizard for assistance in changing it.");
return;
else
new_password = args[2];
endif
if (r = $password_verifier:reject_password(new_password, player))
player:notify(r);
return;
endif
player.password = crypt(tostr(new_password));
player.last_password_time = time();
player:notify("New password set.");
.
#6:95
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
pass(@args);
features = this.features;
"Have to do this, or :feature_remove thinks you're a liar and doesn't believe.";
this.features = {};
for x in (features)
try
x:feature_remove(this);
except (ANY)
"Ignore errors";
endtry
endfor
endif
.
#6:96
((caller == this) || $perm_utils:controls(caller_perms(), this)) || raise(E_PERM);
if (g = this.gaglist)
recycler = $recycler;
for o in (g)
if (!recycler:valid(o))
g = setremove(g, o);
endif
endfor
this.gaglist = g;
endif
.
#6:97
if (!argstr)
player:tell_lines($news:index());
player:tell("");
player:tell("Type \"news <item>\" to display an article.");
elseif (argstr == "all")
player:tell("Reading all news articles...");
for x in ($news:contents())
x:read();
x:mark(player);
endfor
player:tell("Done.");
elseif (argstr == "new")
player:tell("Reading all new news...");
for x in ($news:contents())
if (!(player in x.read_by))
x:read();
x:mark(player);
endif
endfor
player:tell("Done.");
else
if (typeof(item = $news:item_no(argstr)) != OBJ)
return;
endif
player:tell_lines(item:display_item());
endif
"Last modified Mon Jun 17 13:35:51 1996 CDT by Jan (#2).";
.
#6:98
"Program for toggling HTML source viewing on and off.";
if (caller != this)
return E_PERM;
endif
if ((length(args) == 0) || ((argstr != "on") && (argstr != "off")))
return player:notify("Usage: @HTML on|off");
else
player.html_view = (argstr == "on") ? 1 | 0;
return player:notify((argstr == "on") ? "You will now be shown HTML source codes." | "You will no longer be shown HTML source codes.");
endif
"Last modified Wed Oct 22 23:40:13 1997 MET DST by Wizard (#2).";
.
#6:99
"===========================================================";
"Copyright (C) 1995-2001, Jan Rune Holmevik and Mark Blanchard";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
out = pass(user);
if (!$anonymous_users)
if ($object_utils:isa(this, $guest))
"Do not display extra info on guests";
else
extra = {};
extra = {@extra, tostr("<P>Real Name: ", this.real_name)};
extra = {@extra, tostr("<BR>E-mail Address: ")};
email = ((("<A HREF=\"mailto:" + this.email_address) + "\">") + this.email_address) + "</A>";
extra = {@extra, email};
if (this.homepage)
extra = {@extra, tostr("<BR>Homepage: ")};
email = ((("<a href=\"" + this.homepage) + "\" TARGET=_BLANK>") + this.homepage) + "</a>";
extra = {@extra, email};
endif
if (this.wizard)
extra = {@extra, tostr("<BR>", this.psc, " is one of the administrators of ", $network.MOO_name, ".")};
elseif (this.programmer)
extra = {@extra, tostr("<BR>", this.psc, " is one of the programmers at ", $network.MOO_name, ".")};
endif
if (this.interests_msg != "")
extra = {@extra, tostr("<BR>Interests: ")};
if (typeof(this.interests_msg) == LIST)
for n in (this.interests_msg)
extra = {@extra, n};
endfor
else
extra = {@extra, tostr(this.interests_msg)};
endif
endif
out = $list_utils:append(out, extra);
endif
endif
return out;
"Last modified Mon Sep 17 10:11:26 2001 CDT by Wizard (#2).";
.
#6:100
set_task_perms((caller == this) ? this | $no_one);
dobj = $string_utils:match_player(dobjstr);
if (dobj == $failed_match)
player:tell("There is no person named ", dobjstr, ".");
elseif (dobj == $nothing)
player:tell("You must @join a person.");
elseif (dobj == $ambiguous_match)
player:tell("I'm not sure which ", dobjstr, " you mean.");
else
destination = dobj.location;
if ($object_utils:isa(destination, $generic_editor))
player:tell("Sorry, ", dobjstr, " is editing right now.");
player:tell("You can't join ", dobj.po, " in the editor.");
return;
endif
origin = player.location;
if ($object_utils:isa(origin, $generic_editor))
player:tell("You should finish or abort your editing session first.");
return;
endif
player:moveto(destination);
if (player.location == destination)
if (valid(origin))
"Added 9/12/99 by Jan to prevent missing arrive and depart messages";
{oarrive_msg, odepart_msg} = $player:check_arrive_and_depart_msgs();
player:tell("You teleport into ", destination.name, "...");
origin:announce($string_utils:pronoun_sub(odepart_msg, player));
endif
destination:announce($string_utils:pronoun_sub(oarrive_msg, player));
else
player:tell("Sorry, you can't go there.");
endif
endif
"Last modified Fri Oct  1 06:54:44 1999 CDT by Wizard (#2).";
.
#6:101
if (caller != player)
return E_PERM;
endif
if (!args)
player:tell("The Surf and Turf Sketch Pad is ", player.sketch ? "active." | "inactive.");
else
if (args[1] == "on")
this:set_sketch(1);
player:tell("Sketchpad activated...");
elseif (args[1] == "off")
this:set_sketch(0);
player:tell("Sketch Pad deactivated...");
else
player:tell("Try '@sketch on' or '@sketch off'.");
endif
endif
.
#6:102
if (caller == this)
this.sketch = tonum(args[1]);
else
return E_PERM;
endif
.
#6:103
"Copyright (C) 1998, Jan Rune Holmevik";
player:tell("----------------");
player:tell($time_utils:time_sub("Time: $O:$M:$S $P, $Z", time()));
player:tell($time_utils:time_sub("Date: $D, $N $T, $Y", time()));
player:tell("----------------");
"Last modified Fri Feb  6 08:39:56 1998 CST by Wizard (#2).";
.
#6:104
if (caller == this)
this.ts_client = tonum(args[1]);
else
return E_PERM;
endif
"Last modified Fri Feb  7 17:36:24 1997 CST by Jan (#2).";
.
#6:105
set_task_perms(player);
if ($anonymous_users)
return player:tell("This verb is for non-anonymous MOOs only.");
endif
if (args)
player:tell("Please type ", verb, " again and press enter. You will be prompted for information.");
else
interests = $command_utils:read_lines();
player.interests_msg = interests;
player:tell("Your interests/research information has been updated.");
endif
.
#6:106
if ($anonymous_users)
return player:tell("Sorry, all users of ", $network.MOO_name, " are anonymous.");
endif
if (!args)
folks = {};
su = $string_utils;
for n in (connected_players())
if (!n.invis)
folks = {@folks, n};
endif
endfor
player:tell("Name -------------- Real Name ----------------------------------- Email address");
for plyr in (folks)
$command_utils:suspend_if_needed(0);
if ($object_utils:isa(plyr, $guest))
real_name = plyr.default_name;
addr = ("<" + plyr.address) + ">";
else
real_name = plyr.real_name;
addr = plyr.email_address;
endif
if (length(plyr.name) > 20)
name = plyr.name[1..19];
else
name = plyr.name;
endif
if (length(real_name) > 23)
real_name = real_name[1..22];
endif
if (length(addr) > 36)
addr = addr[1..36];
endif
player:tell(su:left(name, 20), su:left(real_name, 23), su:right(addr, 36));
endfor
player:tell("------------------ type '", verb, " name' for more information --------------------");
player:tell("Total: ", length(folks), " people.");
return;
endif
if (!valid(dobj))
dobj = $player_db:find(dobjstr);
endif
if ((!valid(dobj)) || (!is_player(dobj)))
player:tell("Sorry, could not find a player named \"", dobjstr, "\".");
return;
endif
player:tell("----------------- ", verb, " ", $string_utils:nn(dobj));
if ($object_utils:isa(dobj, $guest))
player:tell(dobj.name, " is a guest at ", $network.moo_name, ".");
player:tell(dobj.psc, " is connected on port ", dobj.last_connect_place);
else
if ((!$object_utils:has_property(dobj, "real_name")) || (typeof(dobj.real_name) != STR))
player:tell("Real name       : Not registered.");
return;
endif
if (dobj.real_name)
player:tell("Real name       : ", dobj.real_name);
else
player:tell("Real name       : Not available.");
endif
if (dobj.email_address != "")
if (dobj.email_address)
player:tell("E-mail address  : ", dobj.email_address);
else
player:tell("E-mail address  : Not available");
endif
else
player:tell("E-mail address  : Not registered");
endif
if (dobj.homepage)
player:tell("Homepage        : ", dobj.homepage);
endif
if (dobj.wizard)
player:tell("Status          : Wizard");
elseif (dobj.programmer)
player:tell("Status          : Programmer");
endif
duration = time() - dobj.first_connect_time;
if (duration < 0)
player:tell("First connected : Never ");
player:tell("----------------- End ", verb);
return;
else
age = $time_utils:english_time((duration / 86400) * 86400);
player:tell("First connected : ", age, " ago");
endif
if (dobj in connected_players())
player:tell("Location        : ", dobj.location.name, ", ", $time_utils:english_time(idle_seconds(dobj)), " idle time.");
else
time = dobj.first_connect_time;
player:tell("Last seen       : ", ctime(dobj.last_disconnect_time));
endif
if (dobj.interests_msg != "")
player:tell("Interests/ Research:");
player:tell_lines(dobj.interests_msg);
endif
endif
player:tell("----------------- End ", verb);
.
#6:107
"Program for toggling ASCII drawings on and off.";
if (caller != this)
return E_PERM;
endif
if ((length(args) == 0) || ((argstr != "on") && (argstr != "off")))
return player:notify("Usage: @drawings on|off");
else
this.display_drawings = (argstr == "on") ? 1 | 0;
return player:notify((argstr == "on") ? "You will now be shown drawings and maps." | "You will no longer be shown drawings and maps.");
endif
"Last modified Sun Jul 20 07:55:54 1997 CDT by Wizard (#2).";
.
#6:108
set_task_perms(player);
if (!args)
return player:tell("Syntax: @search text. Will search the database for all instances of text.");
endif
pattern = args[1];
hits = {};
su = $string_utils;
player:tell("Searching for ", pattern, "...");
for i in [0..tonum(max_object())]
$command_utils:suspend_if_needed(0);
if (match(toobj(i).name, pattern))
hits = {@hits, i};
endif
endfor
if (hits)
this:do_listing(hits);
endif
player:tell("Found ", length(hits), " instances of ", pattern, ".");
"Last modified Wed Jul 23 04:58:48 1997 CDT by Wizard (#2).";
.
#6:109
hits = args[1];
counter = length(hits);
line = 0;
su = $string_utils;
for i in (hits)
$command_utils:suspend_if_needed(0);
if (line == 0)
player:tell("Obj# - Name ------------------------ Location --------------------------- Owner");
endif
OBJ = toobj(i);
if (length(OBJ.name) > 30)
name = OBJ.name[1..29];
else
name = OBJ.name;
endif
if (OBJ.location == #-1)
location = "** Nowhere **";
else
if (length(OBJ.location.name) > 24)
location = OBJ.location.name[1..23];
else
location = OBJ.location.name;
endif
endif
if (length(OBJ.owner.name) > 18)
owner = OBJ.owner.name[1..17];
else
owner = OBJ.owner.name;
endif
player:tell(su:left(OBJ, 7), su:left(name, 30), su:left(location, 24), su:right(owner, 18));
counter = counter - 1;
line = line + 1;
if (line == this.page_length)
if (!$command_utils:yes_or_no(tostr("Do you wish to see remaining entries (", counter, ")?")))
return player:tell("Okay, listing aborted. ", counter, " entries flushed.");
else
line = 0;
endif
endif
endfor
player:tell("-------------------------------------------------------------------------------");
"Last modified Wed Jul 23 04:58:49 1997 CDT by Wizard (#2).";
.
#6:110
if ($anonymous_users)
return player:tell("Sorry, but I am not allowed to reveal any real names in this MOO.");
endif
pattern = args[1];
hits = {};
su = $string_utils;
player:tell("Searching for people with real names matching ", pattern, "...");
for i in (players())
$command_utils:suspend_if_needed(0);
if (match(i.real_name, pattern))
hits = {@hits, i};
endif
endfor
if (hits)
player:tell("Name -------------- Real Name ----------------------------------- Email address");
for plyr in (hits)
$command_utils:suspend_if_needed(0);
real_name = plyr.real_name;
addr = plyr.email_address;
name = ((plyr.name + " (") + tostr(plyr)) + ")";
if (length(plyr.name) > 20)
name = name[1..19];
endif
if (length(real_name) > 23)
real_name = real_name[1..22];
endif
if (length(addr) > 36)
addr = addr[1..36];
endif
player:tell(su:left(name, 20), su:left(real_name, 23), su:right(addr, 36));
endfor
player:tell("------------------ type '@whois name' for more information --------------------");
endif
if (!hits)
player:tell("Sorry, but there are nobody by that name in ", $network.MOO_name, ".");
elseif (length(hits) == 1)
player:tell("Found ", length(hits), " person whose real name matches ", pattern, ".");
else
player:tell("Found ", length(hits), " people whose real names match ", pattern, ".");
endif
"Last modified Sun Aug 17 14:53:42 1997 CDT by Wizard (#2).";
.
#6:111
player:tell("Suspending output until next command.");
player.memory = {};
"Last modified Sat Oct 18 16:56:12 1997 CDT by Wizard (#2).";
.
#6:112
if ($object_utils:isa(player.location, $generic_editor))
return player.location:(verb)();
endif
notify(player, ("Please enter your " + ((verb == "say") ? "statement" | "emote")) + " and hit return.");
player.memory = {};
argstr = read(player);
totell = this.memory;
clear_property(this, "memory");
player:tell_lines(totell);
if (argstr)
player.location:(verb)(argstr);
else
player:tell("You stay silent.");
endif
"Last modified Sat Oct 18 16:56:35 1997 CDT by Wizard (#2).";
.
#6:113
"Program for toggling output delimiter on and off.";
if (caller != this)
return E_PERM;
endif
if ((!args) || (!(argstr in {"on", "off"})))
return player:notify("Usage: @delimiter on|off");
endif
this.delimiter_msg = (argstr == "on") ? "----------------------------------------------------------------------" | {};
player:tell("Output delimiter ", argstr);
"Last modified Sat Oct 18 16:57:36 1997 CDT by Wizard (#2).";
.
#6:114
"Copyright (C) 1998, Jan Rune Holmevik";
"Verb that makes it easier for players to set all their personal preference properties.";
if ($object_utils:isa(player, $guest))
player:tell("Sorry, only registered users may use this command.");
return;
endif
finished = 0;
title = $encore_utils:make_title($network.MOO_name + " User Preference Setup");
while (!finished)
player:tell_lines(title);
player:tell("Please select one of the preferences below to change it.");
player:tell("");
player:tell("Personal          Messages            Display");
player:tell("--------          --------            -------");
player:tell("1) Name           7) Page Messages     9) Word Wrap");
player:tell("2) Gender         8) Move Messages    10) Page Length");
player:tell("3) Description");
player:tell("4) Password");
player:tell("5) Research");
player:tell("6) Home Page");
player:tell("");
player:tell("Please enter your choice (1-10), or type Q to quit");
ans = read(player);
if (ans == "1")
"------------------------- Name  -------------";
player:tell("Changing Your Name");
player:tell("------------------");
player:tell("Please type a new name for yourself or press RETURN or ENTER to return to the main menu..");
name = read(player);
if (name)
e = $building_utils:set_names(player, name);
if (e == E_INVARG)
player:notify($player_db:why_bad_name(player, name));
player:tell("Your name remain unchanged");
else
player:tell("Your name has been changed to ", name);
endif
endif
elseif (ans == "2")
"------------------------- Gender  ------------------------";
player:tell("Setting Your Gender");
player:tell("-------------------");
player:tell("Your gender is currently set to ", player.gender);
player:tell("Available genders: ", $string_utils:english_list($gender_utils.genders, "", " or "));
if (gender = $command_utils:read("your choice (Example: female) Press RETURN or ENTER to get back to the main menu"))
if (gender in $gender_utils.genders)
$gender_utils:set(player, gender);
player.gender = gender;
player:tell("Your gender has been set to ", gender);
else
player:tell("Sorry, ", gender, " is not an available gender");
endif
endif
elseif (ans == "3")
"------------------------- Description  -------------";
player:tell("Describing Yourself");
player:tell("-------------------");
if (player.description)
player:tell("Your description is currently set to:");
player:tell_lines(player.description);
player:tell("");
endif
player:tell("Please type a description of yourself. Press RETURN or ENTER to get back to the main menu");
if (descr = $command_utils:read())
player:tell("");
player:tell("You typed:");
player:tell(descr);
if ($command_utils:yes_or_no("Are you sure you want to change your description to this?"))
player.description = descr;
player:tell("Description set");
else
player:tell("No changes");
endif
endif
elseif (ans == "4")
"------------------------- Password  -------------";
player:tell("Password");
player:tell("--------");
player:tell("Your password is your key to ", $network.MOO_name, " so it is very important that you keep it secure at all times. When choosing a new password, we recommend that you pick one that is composed of at least six characters which is a combination of upper and lower case letters and numerals.");
passw = $command_utils:read("your old password. or press RETURN or ENTER to return to the main menu. (If you have forgotten it, please contact a wizard)");
if (passw)
if (player.password != crypt(passw, player.password))
player:tell("That's not your old password. Please try again and remember that passwords are case sensitive which means that 'PASSWORD' and 'password' are two completely different passwords.");
else
newpassw = $command_utils:read("a new password. ");
player.password = crypt(newpassw);
player.last_password_time = time();
player:tell("New password set.");
endif
endif
elseif (ans == "5")
"------------------------- Research/Interests  -------------";
player:tell("Research and Interests");
player:tell("----------------------");
if (player.interests_msg)
player:tell("Your research and interests message is currently set to:");
player:tell_lines(player.interests_msg);
player:tell("");
endif
player:tell("Please describe your research and other interests for other users to read. Press RETURN or ENTER to get back to the main menu");
if (msg = $command_utils:read())
player:tell("");
player:tell("You typed:");
player:tell(msg);
if ($command_utils:yes_or_no("Are you sure you want to change your research and interests message to this?"))
player.interests_msg = msg;
player:tell("Research and Interest Message Set");
else
player:tell("No changes");
endif
endif
elseif (ans == "6")
"------------------------- Home Page  ----------------------";
player:tell("Home Page");
player:tell("---------");
if (player.homepage)
player:tell("Your home page is currently: ", player.homepage);
endif
if (homepage = $command_utils:read("the URL for your World Wide Web home page. Press RETURN or ENTER to get back to the main menu"))
player.homepage = homepage;
player:tell("Homepage set");
endif
elseif (ans == "7")
"------------------------- Page messages ----------------------";
player:tell("Page Messages");
player:tell("-------------");
player:tell("The page messages are messages that you and other people see when you page them. You can customize them to your own liking if you wish. For convenience, you can use variables instead of names of people and places in your page messages.");
player:tell("");
player:tell("Variables:");
player:tell("Your name: %n");
player:tell("Name of the player you are paging: %d");
player:tell("Name of your room: %l");
player:tell("");
player:tell("Your current page messages are:");
player:tell("1) The message that people see when you page them is:");
player:tell("   ", player.page_origin_msg);
player:tell("2) The message that tell people you received their page is:");
player:tell("   ", player.page_echo_msg);
player:tell("3) The message that tell people you are not logged in when they page you is:");
player:tell("   ", player.page_absent_msg);
n = $command_utils:read("the number of the message you wish to change. Press RETURN or ENTER to get back to the main menu");
if (n == "1")
player.page_origin_msg = $command_utils:read("the new message you want people to see when you page them");
player:tell("Message Set");
elseif (n == "2")
player.page_echo_msg = $command_utils:read("the new message that tell people you received their page");
player:tell("Message Set");
elseif (n == "3")
player.page_absent_msg = $command_utils:read("the new message that tell people you are not logged in when they page");
player:tell("Message Set");
endif
"------------------------- Move messages ----------------------";
elseif (ans == "8")
player:tell("Move Messages");
player:tell("-------------");
player:tell("These are messages that people see when you move in and out of rooms with the @go and @join commands.");
player:tell("");
player:tell("Use the variable %n instead of typing your name. This way your message will still display your correct name if you should decide to change your name later.");
player:tell("");
player:tell("Your current move messages are:");
player:tell("1) The message that people see when you enter a room:");
player:tell("   ", player.oarrive_msg);
player:tell("2) The message that tell people you leave a room:");
player:tell("   ", player.odepart_msg);
n = $command_utils:read("the number of the message you wish to change. Press RETURN or ENTER to get back to the main menu");
if (n == "1")
player.oarrive_msg = $command_utils:read("the new message you want people to see when you enter a room");
player:tell("Message Set");
elseif (n == "2")
player.odepart_msg = $command_utils:read("the new message that you want people to see when you leave a room");
player:tell("Message Set");
endif
"------------------------- Word Wrap ----------------------";
elseif (ans == "9")
player:tell("Word Wrap");
player:tell("---------");
player:tell("If you have a problem with long lines running off the right edge of your screen you should check the preferences in your MOO client program to see if you can set it to wrap long lines. If your client program does not have this option, ", $network.MOO_name, " can wrap long lines for you. PS! If you are using the MACMOOSE client program, word wrap must be off!");
player:tell("");
player:notify(tostr("Word wrap is currently ", (player.linelen > 0) ? "on" | "off", "."));
player:tell("");
if (player.linelen > 0)
player:tell("Type 'off' to turn it off, or press RETURN or ENTER to return to the main menu.");
else
player:tell("Type 'on' to turn it on, or press RETURN or ENTER to return to the main menu.");
endif
wrap = read(player);
if (wrap)
player.linelen = abs(player.linelen) * ((wrap == "on") ? 1 | -1);
player:notify(tostr("Word wrap is now ", wrap, "."));
endif
"------------------------- Page Length ----------------------";
elseif (ans == "10")
player:tell("Page Length");
player:tell("-----------");
player:tell("If you are using telnet or some other program that does not have scroll back capabilities, you may have a problem reading long descriptions and notes because the text scrolls off the top of your screen too quickly. To fix this problem, you can get ", $network.MOO_name, " to buffer output for you so you can read one screen of text at a time.");
player:tell("");
player:notify(tostr("Page buffering is currently ", (player.pagelen != 0) ? "on" | "off", "."));
player:tell("");
if (player.pagelen != 0)
player:tell("Type 'off' to turn it off, or press RETURN or ENTER to return to the main menu.");
else
player:tell("Type 'on' to turn it on, or press RETURN or ENTER to return to the main menu.");
endif
buffering = read(player);
if (buffering == "on")
player.pagelen = 24;
player:tell("Page buffering turned on");
elseif (buffering == "off")
player.pagelen = 0;
player:tell("Page buffering turned off");
endif
else
player:tell("Exiting...");
finished = 1;
endif
endwhile
"Last modified Fri Feb  6 08:38:35 1998 CST by Wizard (#2).";
.
#6:115
"Copyright (C) 1998, Jan Rune Holmevik";
su = $string_utils;
width = 69;
heading = tostr("*  Information about ", $network.MOO_name, "   *");
founded = tostr("Founded : ", $time_utils:time_sub("$N $T, $Y.", #2.first_connect_time));
pop = tostr("Population : ", length(players()), " people");
ver = tostr("Server: LambdaMOO version ", server_version());
mcp = "";
if ($object_utils:has_property(#0, "mcp"))
mcp = tostr("MOO Client Protocol : version MCP/", $mcp.version[1], ".", $mcp.version[2]);
endif
core = tostr("Core: ", $core_name, ", version ", $core_version);
db = tostr("Database: ", tonum(max_object()), " objects, ", db_disk_size(), " bytes on disk");
delimiter = "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~";
player:tell(su:left("", 5), su:center(delimiter, width));
player:tell(su:right("|", 5), su:center(heading, width), su:left("|", 5));
player:tell(su:right("|", 5), su:right("|", 70));
player:tell(su:right("|", 5), su:center(founded, width), su:left("|", 5));
player:tell(su:right("|", 5), su:center(pop, width), su:left("|", 5));
player:tell(su:right("|", 5), su:center(ver, width), su:left("|", 5));
player:tell(su:right("|", 5), su:center(core, width), su:left("|", 5));
if (mcp)
player:tell(su:right("|", 5), su:center(mcp, width), su:left("|", 5));
endif
player:tell(su:right("|", 5), su:center(db, width), su:left("|", 5));
player:tell(su:left("", 5), su:center(delimiter, width));
"Last modified Mon Oct 19 11:46:09 1998 CDT by Wizard (#2).";
.
#6:116
"Copyright (C) 1998 Jan Rune Holmevik";
"Jan's version of the popular @knock, @invite and @busy verbs.";
if (!args)
return player:tell("Usage: ", verb, " person");
endif
who = $string_utils:match_player(args[1]);
if (who == $failed_match)
player:tell("Sorry, there is no person by that name in ", $network.MOO_name, ".");
elseif (!(who in connected_players()))
player:tell(who:titlec(), " is not currently logged in.");
else
loc = tostr("(From ", player.location.name, ") ");
if ((verb == "@knock") || (verb == "@kn"))
player:tell("You knock politely and ask ", who:titlec(), " if you may join ", who.po, ".");
who:tell(loc, player.name, " knocks politely and wonders ", player.ps, " may join you. Type '@invite ", player.name, "' to invite ", player.po, " over.");
elseif ((verb == "@invite") || (verb == "@inv"))
player:tell("You invite ", who:titlec(), " to join you here");
who:tell(loc, player.name, " invites you over. Type '@join ", player.name, "' to join ", player.po, ".");
else
player:tell("You tell ", who:titlec(), " that you are busy right now and can't talk.");
who:tell(loc, player.name, " tells you that ", player.ps, " is busy right now and can't talk, please try again later.");
endif
endif
"Last modified Sat Feb  7 04:31:04 1998 CST by Wizard (#2).";
.
#6:117
"===========================================================";
"Copyright (C) 1997-2001, Jan Rune Holmevik";
"Toggles a player's activity message on or off.";
"===========================================================";
if (!$perm_utils:controls(caller, this))
player:tell(E_PERM);
return E_PERM;
endif
if (!argstr)
if (this.doing)
clear_property(this, "doing");
this:tell("You reset your activity message.");
else
player:tell("Your activity has not been set. Type '@activity message' to set it.");
endif
else
this.doing = $string_utils:capitalize(argstr);
player:tell("You set your activity message set to: ", argstr, ". Please type '@activity' again to remove this message.");
endif
"Last modified Fri Apr 13 18:57:39 2001 CDT by Wizard (#2).";
.
#6:118
if (this.doing)
return this.name + this.doing;
else
return this.name;
endif
"Last modified Mon Oct 19 10:48:20 1998 CDT by Wizard (#2).";
.
#6:119
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Generates a dynamic web menu line based on which player class the user belongs to";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, preload} = args;
base_url = tostr("http://", $network.site, ":", $network.webport, "/");
external_baseurl = (($xpress_client.external_baseurl + $xpress_client.themes_folder) + user.xpress_theme) + "/";
"Assemble menu line";
about = {tostr("<A HREF=\"", base_url, "MOO_info/about.html\" TARGET=\"", $MOO_info:get_frame_name("openAboutWindow"), "\" onClick=\"openAboutWindow(); swapImage('about','", external_baseurl, "about1.jpg')\" onMouseOver=\"swapImage('about','", external_baseurl, "about2.jpg')\" onMouseOut=\"swapImage('about','", external_baseurl, "about1.jpg')\"><IMG SRC=\"", external_baseurl, "about1.jpg\" BORDER=0 ALIGN=bottom name=about ALT=\"Information about the MOO\" TITLE=\"Information about the MOO\"></A>")};
preload = {@preload, tostr("   about2 = new Image(); about2.src = \"", external_baseurl, "about2.jpg\";")};
help = {tostr("<A HREF=\"", base_url, "help_browser/main.html\" TARGET=\"", $help_browser:get_frame_name("openHelpBrowser"), "\" onClick=\"openHelpBrowser(); ; swapImage('help','", external_baseurl, "help1.jpg')\" onMouseOver=\"swapImage('help','", external_baseurl, "help2.jpg')\" onMouseOut=\"swapImage('help','", external_baseurl, "help1.jpg')\"><IMG SRC=\"", external_baseurl, "help1.jpg\" BORDER=0 ALIGN=bottom name=help ALT=\"Browse the MOO's Help System\" TITLE=\"Browse the MOO's Help System\"></A>")};
preload = {@preload, tostr("   help2 = new Image(); help2.src = \"", external_baseurl, "help2.jpg\";")};
look = {tostr("<A HREF=\"", base_url, "Xpress_client/display_user_location\" TARGET=\"web_frame\" onClick=\"swapImage('look','", external_baseurl, "look1.jpg')\" onMouseOver=\"swapImage('look','", external_baseurl, "look2.jpg')\" onMouseOut=\"swapImage('look','", external_baseurl, "look1.jpg')\"><IMG SRC=\"", external_baseurl, "look1.jpg\" BORDER=0 ALIGN=bottom name=look ALT=\"Focus On My Location\" TITLE=\"Focus On My Location\"></A>")};
preload = {@preload, tostr("   look2 = new Image(); look2.src = \"", external_baseurl, "look2.jpg\";")};
who = {tostr("<A HREF=\"", base_url, "who_browser/main.html\" TARGET=\"", $who_browser:get_frame_name("openWhoBrowser"), "\" onClick=\"openWhoBrowser(); swapImage('who','", external_baseurl, "who1.jpg')\" onMouseOver=\"swapImage('who','", external_baseurl, "who2.jpg')\" onMouseOut=\"swapImage('who','", external_baseurl, "who1.jpg')\"><IMG SRC=\"", external_baseurl, "who1.jpg\" BORDER=0 ALIGN=bottom name=who ALT=\"See Who Is Online\" TITLE=\"See Who Is Online\"></A>")};
preload = {@preload, tostr("   who2 = new Image(); who2.src = \"", external_baseurl, "who2.jpg\";")};
navigator = {tostr("<A HREF=\"", base_url, "Xpress_navigator/main_html\" TARGET=\"", $Xpress_navigator:get_frame_name("openNavigator"), "\" onClick=\"openNavigator(); swapImage('navigator','", external_baseurl, "xpress1.jpg')\" onMouseOver=\"swapImage('navigator','", external_baseurl, "xpress2.jpg')\" onMouseOut=\"swapImage('navigator','", external_baseurl, "xpress1.jpg')\"><IMG SRC=\"", external_baseurl, "xpress1.jpg\" BORDER=0 ALIGN=bottom name=navigator ALT=\"Xpress Links to MOO Resources\" TITLE=\"Xpress Links to MOO Resources\"></A>")};
preload = {@preload, tostr("   xpress2 = new Image(); xpress2.src = \"", external_baseurl, "xpress2.jpg\";")};
inventory = {tostr("<A HREF=\"", base_url, "Inventory_Manager/view_inventory?", toint(user), "\" TARGET=\"", $inventory_manager:get_frame_name("openInventory"), "\" onClick=\"openInventory();swapImage('inventory','", external_baseurl, "inventory1.jpg')\" onMouseOver=\"swapImage('inventory','", external_baseurl, "inventory2.jpg')\" onMouseOut=\"swapImage('inventory','", external_baseurl, "inventory1.jpg')\"><IMG SRC=\"", external_baseurl, "inventory1.jpg\" BORDER=0 ALIGN=bottom name=inventory ALT=\"View My Inventory\" TITLE=\"View My Inventory\"></A>")};
preload = {@preload, tostr("   inventory2 = new Image(); inventory2.src = \"", external_baseurl, "inventory2.jpg\";")};
options = {tostr("<A HREF=\"", base_url, "Xpress_Object_editor/view.html?", toint(user), "\" TARGET=\"", $Xpress_Object_Editor:get_frame_name("openOptions"), "\"onClick=\"openOptions(); swapImage('options','", external_baseurl, "options1.jpg')\" onMouseOver=\"swapImage('options','", external_baseurl, "options2.jpg')\" onMouseOut=\"swapImage('options','", external_baseurl, "options1.jpg')\"><IMG SRC=\"", external_baseurl, "options1.jpg\" BORDER=0 ALIGN=bottom name=options ALT=\"Change My Preferences\" TITLE=\"Change My Preferences\"></A>")};
preload = {@preload, tostr("   options2 = new Image(); options2.src = \"", external_baseurl, "options2.jpg\";")};
search = {tostr("<A HREF=\"", base_url, "search_engine/search.html\" TARGET=\"", $search_engine:get_frame_name("openSearchEngine"), "\" onClick=\"openSearchEngine(); swapImage('search','", external_baseurl, "search1.jpg')\" onMouseOver=\"swapImage('search','", external_baseurl, "search2.jpg')\" onMouseOut=\"swapImage('search','", external_baseurl, "search1.jpg')\"><IMG SRC=\"", external_baseurl, "search1.jpg\" BORDER=0 ALIGN=bottom name=search ALT=\"Search the MOO Database\" TITLE=\"Search the MOO Database\"></A>")};
preload = {@preload, tostr("   search2 = new Image(); search2.src = \"", external_baseurl, "search2.jpg\";")};
mail = {tostr("<A HREF=\"", base_url, "Xpress_MOO_mailer/main.html\" TARGET=\"", $Xpress_MOO_mailer:get_frame_name("openMailBrowser"), "\" onClick=\"openMailBrowser(); swapImage('mail','", external_baseurl, "mail1.jpg')\" onMouseOver=\"swapImage('mail','", external_baseurl, "mail2.jpg')\" onMouseOut=\"swapImage('mail','", external_baseurl, "mail1.jpg')\"><IMG SRC=\"", external_baseurl, "mail1.jpg\" BORDER=0 ALIGN=bottom name=mail ALT=\"Send and Receive MOO Mail\" TITLE=\"Send and Receive MOO Mail\"></A>")};
preload = {@preload, tostr("   mail2 = new Image(); mail2.src = \"", external_baseurl, "mail2.jpg\";")};
menu = $list_utils:append(about, help, navigator, look, who, search, inventory, options, mail);
"Assemble JavaScript functions";
about_window = $encore_web_utils:javascript_window_open($MOO_info, "openAboutWindow", "");
help_browser = $encore_web_utils:javascript_window_open($help_browser, "openHelpBrowser", "");
who_browser = $encore_web_utils:javascript_window_open($who_browser, "openWhoBrowser", "");
xpress_navigator = $encore_web_utils:javascript_window_open($xpress_navigator, "openNavigator", "");
inventory_window = $encore_web_utils:javascript_window_open($inventory_manager, "openInventory", "");
options_window = $encore_web_utils:javascript_window_open($xpress_object_editor, "openOptions", "");
search_window = $encore_web_utils:javascript_window_open($search_engine, "openSearchEngine", "");
mail_browser = $encore_web_utils:javascript_window_open($Xpress_MOO_mailer, "openMailBrowser", "");
functions = $list_utils:append(about_window, help_browser, who_browser, xpress_navigator, inventory_window, options_window, search_window, mail_browser);
return {menu, functions, preload, base_url};
"Last modified Fri Apr 13 19:05:33 2001 CDT by Wizard (#2).";
.
#6:120
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Display a URL to someone";
"Modified this command to send output to new window instead of Xpress screen to prevent javascript on target page to take over the client and log out user.";
"===========================================================";
if (!args)
return player:tell("Usage: <@URL http://someurl>. Displays a URL to everyone in your room. <@URL http://someurl name>, to show URL to only one person. ");
endif
{url, ?to = 0} = args;
if (!to)
if (!match(url, "http://"))
url = "http://" + url;
endif
player:tell(tostr("You share a URL. (", url, ")"));
player.location:announce(player.name, " shares a URL. (", url, ")");
player.location:announce_all("  <", url, " _blank>.");
else
who = $string_utils:match_player(to);
if (who == $failed_match)
return player:tell("Sorry, cannot find any players by that name.");
else
player:tell("You share a URL with ", who.name);
who:tell(player.name, " shares a URL with you...");
who:tell("  <", url, " _blank>.");
endif
endif
"Last modified Fri Apr 13 18:49:50 2001 CDT by Wizard (#2).";
.
#6:121
"Copyright (C) 1999-2000, Jan Rune Holmevik";
"Start or stop a user's personal MOO Log";
if (!caller_perms().wizard)
return E_PERM;
endif
{?user = player} = args;
if (verb == "start_log")
message = "Log Started";
user.recording = 1;
else
message = "Log Stopped";
user.recording = 0;
endif
user.Xpress_log = $list_utils:append(user.Xpress_log, {tostr("-- ", message, (": " + $time_utils:time_sub("$D, $N $t, $Y $o:$M:$S $p $Z")) + " --")}, {""});
return message;
"Last modified Fri Mar 24 21:03:14 2000 CST by Wizard (#2).";
.
#6:122
"Copyright (C) 1999-2000, Jan Rune Holmevik";
"Reset Xpress session data";
if (!caller_perms().wizard)
return E_PERM;
else
player.web_access_code = "";
player.xpress_on = 0;
player.ts_client = 0;
if (player.recording)
"Stop logging and notify user next time she logs in.";
player:stop_log();
player.Xpress_Confunc_Msg = {@player.Xpress_Confunc_Msg, tostr("Your Xpress log dated ", $time_utils:time_sub("$D, $N $t, $Y $o:$M:$S $p $Z"), " was automatically aborted. This happens if you log out without stopping the log, or, if you get disconnected from the MOO. The content of your log prior to being aborted has been preserved.")};
endif
endif
"Last modified Fri Mar 24 21:03:58 2000 CST by Wizard (#2).";
.
#6:123
"Copyright (C) 1999, Jan Rune Holmevik";
"If player's oarrive and/or odepart messages are empty, use generic $player messages instead";
oarrive_msg = player.oarrive_msg;
odepart_msg = player.odepart_msg;
if (oarrive_msg == "")
oarrive_msg = $player.oarrive_msg;
endif
if (odepart_msg == "")
odepart_msg = $player.odepart_msg;
endif
return {oarrive_msg, odepart_msg};
"Last modified Sun Nov 28 04:36:32 1999 CST by Wizard (#2).";
.
#6:124
if (dobjstr)
destname = dobjstr;
else
if (length(args) < 1)
return player:tell(tostr("Try this instead: ", verb, " <room>"));
else
destname = args[1];
endif
endif
numeric = toobj(destname);
if (valid(numeric) && (numeric != #0))
dest = numeric;
else
if ($string_utils:find_prefix("The", {destname}))
dest1 = destname;
dest2 = destname[5..length(destname)];
else
dest1 = "The " + destname;
dest2 = destname;
endif
match = $room_db:find_exact(destname);
if (valid(match))
match = {match};
endif
if (!match)
match = {@$room_db:find_all(dest1), @$room_db:find_all(dest2)};
endif
if (match)
if (length(match) == 1)
"Mickey wanted @go home to work, thus the next 5 lines. al 3/7/94";
if (destname == "home")
dest = this.home;
else
dest = match[1];
endif
else
player:tell("\"", dobjstr, "\" is ambiguous.");
player:tell("Matches: ", $string_utils:english_list($list_utils:map_prop(match, "name")));
return match;
endif
else
if (destname == "home")
dest = this.home;
else
player:tell("\"", dobjstr, "\" not found in room database.");
if ($room_db.updating)
player:tell("Note that the room database is presently being updated, so the room may exist.  Please try again later.");
endif
if ((time() - $room_db.last_update) > ((60 * 60) * 24))
fork (0)
$room_db.last_update = time();
$room_db:update();
endfork
endif
return 0;
endif
endif
endif
if (player != this)
return;
endif
set_task_perms(player);
if (dest == this.location)
player:tell("You're already here!");
else
oldroom = player.location;
player:moveto(dest);
if (player.location == oldroom)
player:tell(dest, " (", dest.name, ") won't allow you in.");
else
newroom = player.location;
"Added 9/12/99 by Jan to prevent missing arrive and depart messages";
{oarrive_msg, odepart_msg} = $player:check_arrive_and_depart_msgs();
oldroom:announce($string_utils:pronoun_sub(odepart_msg, player));
newroom:announce($string_utils:pronoun_sub(oarrive_msg, player));
endif
endif
"Last modified Fri Oct  1 06:56:43 1999 CDT by Wizard (#2).";
.
#6:125
"Copyright (C) 1999, Noel Davis";
"Send a beep to a player";
if (argstr == "")
player:tell("Usage: @beep player");
return;
endif
beeped = $string_utils:match_player(args[1]);
if (beeped == #-3)
player:tell("No such player exists.");
return;
endif
if (!is_player(beeped))
player:tell("You may only send beeps to players.");
return;
endif
if (!$object_utils:connected(beeped))
player:tell(beeped.name, " is not connected.");
return;
endif
if (beeped.gaglist)
if (beeped:gag_p())
return;
endif
endif
if (ticks_left() < 500)
raise(E_QUOTA, "Not enough ticks left to beep.");
else
try
set_connection_option(beeped, "binary", 1);
beeped:notify("~07~07~07~07");
finally
set_connection_option(beeped, "binary", 0);
player:tell("Sent beep to ", beeped.name, ".");
beeped:tell(player.name, " has beeped you to get your attention.");
endtry
endif
"Last modified Sun Nov 28 04:25:49 1999 CST by Wizard (#2).";
.
#6:126
"Not revealing your name and location to other players who use the @who ";
"command.  To change your visibility status type '@hide on/off'.  To see";
"your current status type '@hide' with no argument.";
if (caller != player)
return E_PERM;
endif
if ($object_utils:isa(player, $guest))
player:tell("Sorry, guests are not permitted to @hide. . .");
return;
endif
if (!args)
player:tell("You are currently ", player.invis ? "not visible." | "visible to all.");
else
if (args[1] == "on")
this:set_invis(1);
player:tell("You are now invisible to @who commands.");
elseif (args[1] == "off")
this:set_invis(0);
player:tell("You are now visible to @who commands.");
else
player:tell("Try '@hide on' or '@hide off'.");
endif
endif
"Last modified Fri Jun  9 15:18:59 2000 CDT by Wizard (#2).";
.
#6:127
if (caller == this)
this.invis = tonum(args[1]);
else
return E_PERM;
endif
"Last modified Fri Jun  9 15:19:11 2000 CDT by Wizard (#2).";
.
#6:128
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Assemble the most important properties for quick and easy editing.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
options = pass(@args);
options = {@options, "<OPTION VALUE=\"edit_password\">Change Password"};
options = {@options, "<OPTION VALUE=\"edit_gender\">Change Gender"};
options = {@options, "<OPTION VALUE=\"edit_player_info\">Edit User Information"};
options = {@options, "<OPTION VALUE=\"edit_player_messages\">Edit Player Messages"};
options = {@options, "<OPTION VALUE=\"edit_theme\">Edit Xpress Theme and Appearance"};
options = {@options, "<OPTION VALUE=\"edit_screen_layout\">Edit Xpress Screen Size and Layout"};
options = {@options, "<OPTION VALUE=\"edit_java_settings\">Edit Talk Area Settings"};
options = {@options, "<OPTION VALUE=\"edit_sound_settings\">Edit Sound Settings"};
return options;
"Last modified Fri Apr 13 19:02:19 2001 CDT by Wizard (#2).";
.
#7:0
set_task_perms(caller_perms());
this:move(player);
.
#7:1
set_task_perms(caller_perms());
what = args[1];
"if ((what.location != this.source) || (!(this in this.source.exits)))";
"  player:tell(\"You can't go that way.\");";
"  return;";
"endif";
unlocked = this:is_unlocked_for(what);
if (unlocked)
this.dest:bless_for_entry(what);
endif
if (unlocked && this.dest:acceptable(what))
start = what.location;
if (msg = this:leave_msg(what))
what:tell_lines(msg);
endif
what:moveto(this.dest);
if (what.location != start)
"Don't print oleave messages if WHAT didn't actually go anywhere...";
this:announce_msg(start, what, (this:oleave_msg(what) || this:defaulting_oleave_msg(what)) || "has left.");
endif
if (what.location == this.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
this:announce_msg(what.location, what, this:oarrive_msg(what) || "has arrived.");
endif
else
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))
this:announce_msg(what.location, what, msg);
endif
endif
"Last modified Sun Nov 28 06:02:41 1999 CST by Wizard (#2).";
.
#7:2
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
try
this.source:remove_exit(this);
this.dest:remove_entrance(this);
except id (ANY)
endtry
return pass(@args);
else
return E_PERM;
endif
.
#7:3
msg = this.(verb);
return msg ? $string_utils:pronoun_sub(msg, @args) | "";
.
#7:4
if ($perm_utils:controls(cp = caller_perms(), this) || (valid(this.source) && (this.source.owner == cp)))
return (typeof(e = `this.name = args[1] ! ANY') != ERR) || e;
else
return E_PERM;
endif
.
#7:5
if ($perm_utils:controls(cp = caller_perms(), this) || (valid(this.source) && (this.source.owner == cp)))
if (typeof(e = `this.aliases = args[1] ! ANY') == ERR)
return e;
else
return 1;
endif
else
return E_PERM;
endif
.
#7:6
"This is intended to be called only by exits, for announcing various oxxx messages.  First argument is room to announce in.  Second argument is as in $room:announce_all_but's first arg, who not to announce to.  Rest args are what to say.  If the final arg is a list, prepends all the other rest args to the first line and emits the lines separately.";
where = args[1];
whobut = args[2];
last = args[$];
if (typeof(last) == LIST)
where:announce_all_but(whobut, @args[3..$ - 1], last[1]);
for line in (last[2..$])
where:announce_all_but(whobut, line);
endfor
else
where:announce_all_but(@args[3..$]);
endif
.
#7:7
for k in ({this.name, @this.aliases})
if (k in {"east", "west", "south", "north", "northeast", "southeast", "southwest", "northwest", "out", "up", "down", "nw", "sw", "ne", "se", "in"})
return ("goes " + k) + ".";
elseif (k in {"leave", "out", "exit"})
return "leaves";
endif
endfor
if ((index(this.name, "an ") == 1) || (index(this.name, "a ") == 1))
return ("leaves for " + this.name) + ".";
else
return ("leaves for the " + this.name) + ".";
endif
.
#7:8
if ((caller in {this, this.owner}) || $perm_utils:controls(caller_perms(), this))
return pass(@args);
else
return E_PERM;
endif
.
#7:9
"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:title(), " will only transport objects matching this key:"), tostr("  ", $lock_utils:unparse_key(this.key))};
endif
.
#7:10
":announce_msg(place, what, msg)";
"  announce msg in place (except to what). Prepend with what:title if it isn't part of the string";
msg = args[3];
what = args[2];
title = what:titlec();
if (!$string_utils:index_delimited(msg, title))
msg = tostr(title, " ", msg);
endif
args[1]:announce_all_but({what}, msg);
.
#7:11
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Assemble the most important properties for quick and easy editing.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
options = {};
options = {@options, "<OPTION VALUE=\"edit_name\">Edit Name"};
options = {@options, "<OPTION VALUE=\"edit_icon\">Edit Icon"};
options = {@options, "<OPTION VALUE=\"edit_exit_options\">Exit Options"};
return options;
"Last modified Fri Apr 13 19:02:19 2001 CDT by Wizard (#2).";
.
#8:0
if ((this.location != player) && (this.location != player.location))
player:tell("You can't get at ", this.name, ".");
elseif (dobj == $nothing)
player:tell("What do you want to put ", prepstr, " ", this.name, "?");
elseif ($command_utils:object_match_failed(dobj, dobjstr))
elseif ((dobj.location != player) && (dobj.location != player.location))
player:tell("You don't have ", dobj.name, ".");
elseif (!this.opened)
player:tell(this.name, " is closed.");
else
set_task_perms(callers() ? caller_perms() | player);
dobj:moveto(this);
if (dobj.location == this)
player:tell(this:put_msg());
if (msg = this:oput_msg())
player.location:announce(player.name, " ", msg);
endif
else
player:tell(this:put_fail_msg());
if (msg = this:oput_fail_msg())
player.location:announce(player.name, " ", msg);
endif
endif
endif
.
#8:1
if (!this.opened)
player:tell(this.name, " is not open.");
elseif (this.dark)
player:tell("You can't see into ", this.name, " to remove anything.");
elseif ((dobj = this:match_object(dobjstr)) == $nothing)
player:tell("What do you want to take from ", this.name, "?");
elseif ($command_utils:object_match_failed(dobj, dobjstr))
elseif (!(dobj in this:contents()))
player:tell(dobj.name, " isn't in ", this.name, ".");
else
dobj:moveto(player);
if (dobj.location == player)
player:tell(this:remove_msg());
if (msg = this:oremove_msg())
player.location:announce(player.name, " ", msg);
endif
else
dobj:moveto(this.location);
if (dobj.location == this.location)
player:tell(this:remove_msg());
if (msg = this:oremove_msg())
player.location:announce(player.name, " ", msg);
endif
player:tell("You can't pick up ", dobj.name, ", so it tumbles onto the floor.");
else
player:tell(this:remove_fail_msg());
if (msg = this:oremove_fail_msg())
player.location:announce(player.name, " ", msg);
endif
endif
endif
endif
.
#8:2
pass();
if ((!this.dark) && (!player.ts_client))
this:tell_contents();
endif
"Last modified Thu Apr  8 12:28:43 1999 CDT by Wizard (#2).";
.
#8:3
return !is_player(args[1]);
.
#8:4
perms = (callers() && (caller != this)) ? caller_perms() | player;
if (this.opened)
player:tell("It's already open.");
"elseif (this:is_openable_by(player))";
elseif (this:is_openable_by(perms))
this:set_opened(1);
player:tell(this:open_msg());
if (msg = this:oopen_msg())
player.location:announce(player.name, " ", msg);
endif
else
player:tell(this:open_fail_msg());
if (msg = this:oopen_fail_msg())
player.location:announce(player.name, " ", msg);
endif
endif
.
#8:5
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.open_key = key;
player:tell("Locked opening of ", this.name, " with this key:");
player:tell("  ", $lock_utils:unparse_key(key));
except error (ANY)
player:tell(error[2], ".");
endtry
endif
.
#8:6
return (this.open_key == 0) || $lock_utils:eval_key(this.open_key, args[1]);
"Last modified Fri Apr 13 19:03:56 2001 CDT by Wizard (#2).";
.
#8:7
if (!this.opened)
player:tell("It's already closed.");
else
this:set_opened(0);
player:tell(this:close_msg());
if (msg = this:oclose_msg())
player.location:announce(player.name, " ", msg);
endif
endif
.
#8:8
set_task_perms(player);
try
dobj.open_key = 0;
player:tell("Unlocked ", dobj.name, " for opening.");
except error (ANY)
player:tell(error[2], ".");
endtry
.
#8:9
if (this.contents)
player:tell("Contents:");
for thing in (this:contents())
player:tell("  ", thing:title());
endfor
elseif (msg = this:empty_msg())
player:tell(msg);
endif
.
#8:10
if (!$perm_utils:controls(caller.owner, this))
return E_PERM;
else
this.opened = opened = !(!args[1]);
this.dark = this.opaque > opened;
return opened;
endif
.
#8:11
if (!$perm_utils:controls(player, this))
player:tell("Can't set opacity of something you don't own.");
elseif ((iobjstr != "0") && (!toint(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(toint(iobjstr))]);
endif
.
#8:12
if (!$perm_utils:controls(caller.owner, this))
return E_PERM;
elseif (typeof(number = args[1]) != INT)
return E_INVARG;
else
number = (number < 0) ? 0 | ((number > 2) ? 2 | number);
this.dark = number > this.opened;
return this.opaque = number;
endif
.
#8:13
return (msg = `this.(verb) ! ANY') ? $string_utils:pronoun_sub(msg) | "";
.
#8:14
return this.(verb);
.
#8:15
"===========================================================";
"Copyright (C) 1995-2001, Jan Rune Holmevik";
"===========================================================";
user = args[1];
contents = {};
if (!$encore_web_utils:page_assembled(html = pass(user)))
if (!this.opened)
html = {@html, tostr(this.name, " is closed. Type 'open ", this.name, "' to open it.")};
elseif (this.contents == {})
html = {@html, tostr(this.name, " is empty.")};
elseif (this.dark)
html = {@html, tostr("The inside of ", this.name, " is too dark to make out its contents.")};
elseif (!$object_utils:has_verb(caller, "_html"))
contents = $encore_web_utils:generate_links(user, this.contents);
contents = $encore_web_utils:insert_line_breaks({"<P><B>Contents:</B>", @contents});
html = $list_utils:append(html, contents);
endif
endif
return html;
"Last modified Fri Apr 13 19:05:15 2001 CDT by Wizard (#2).";
.
#9:0
if (!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:look_self();
player:tell();
player:tell_lines_suspended(this:text());
player:tell();
player:tell("(You finish reading.)");
endif
.
#9:1
if (this:is_writable_by(valid(caller_perms()) ? caller_perms() | player))
this:set_text({});
player:tell("Note erased.");
else
player:tell("You can't erase this note.");
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 = toint(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 error (ANY)
player:tell(error[2], ".");
endtry
endif
.
#9:5
set_task_perms(player);
try
dobj.encryption_key = 0;
player:tell("Decrypted ", dobj.name, ".");
except error (ANY)
player:tell(error[2], ".");
endtry
.
#9:6
cp = caller_perms();
if ($perm_utils:controls(cp, this) || this:is_readable_by(cp))
return this.text;
else
return E_PERM;
endif
.
#9:7
"Modified, 3/23/01 by Jan. Allow shared owners to read an encrypted note.";
if ((typeof(this.shared_owners) == LIST) && (args[1] in this.shared_owners))
return 1;
else
key = this.encryption_key;
return (key == 0) || $lock_utils:eval_key(key, args[1]);
endif
"Last modified Fri Apr 13 19:03:56 2001 CDT by Wizard (#2).";
.
#9:8
cp = caller_perms();
newtext = args[1];
if ($perm_utils:controls(cp, this) || this:is_writable_by(cp))
if (typeof(newtext) == LIST)
this.text = newtext;
this.date = time();
this.author_name = player.name;
else
return E_TYPE;
endif
else
return E_PERM;
endif
"Last modified Wed Apr 15 12:14:21 1998 CDT by Wizard (#2).";
.
#9:9
who = args[1];
wr = this.writers;
if ($perm_utils:controls(who, this))
return 1;
elseif (typeof(wr) == LIST)
return who in wr;
else
return wr;
endif
.
#9:10
"Usage:  mailme <note>";
"  uses $network to sends the text of this note to your REAL internet email address.";
if (!this:is_readable_by(player))
return player:tell("Sorry, but it seems to be written in some code that you can't read.");
elseif (!player.email_address)
return player:tell("Sorry, you don't have a registered email address.");
elseif (!$network.active)
return player:tell("Sorry, internet mail is disabled.");
elseif (!(text = this:text()))
return player:tell($string_utils:pronoun_sub("%T is empty--there wouldn't be any point to mailing it."));
endif
player:tell("Mailing ", this:title(), " to ", player.email_address, ".");
player:tell("... ", length(text), " lines ...");
suspend(0);
$network:sendmail(player.email_address, this:titlec(), "", @text);
.
#9:11
"===========================================================";
"Copyright (C) 1995-2001, Jan Rune Holmevik and Mark Blanchard";
"===========================================================";
user = args[1];
if (!$encore_web_utils:page_assembled(html = pass(user)))
text = $encore_web_utils:detect_urls(user, this.text);
text = $encore_web_utils:insert_line_breaks(text);
text = $encore_web_utils:align(text, this.note_text_alignment);
html = $list_utils:append(html, {"<!-- Note text>", "<P>"}, text, {"</P>", "<!-- end>"});
endif
return html;
"Last modified Sun Sep 30 13:16:26 2001 CDT by Wizard (#2).";
.
#9:12
"Copyright (C) 1998, Jan Rune Holmevik";
"Verb that makes editing of notes easier.";
set_task_perms(player);
if (!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
if (verb == "copy")
if (this.text)
player:tell();
player:tell("------> BEGIN COPY <------");
player:tell();
player:tell_lines_suspended(this.text);
player:tell();
player:tell("------> END COPY <------");
player:tell();
player:tell("To edit the contents of this note, copy the text above into a text editor or word processor and edit it there, when you are done type 'paste note-name', and paste the text back into your MOO window.");
else
player:tell("This note is devoid of text.");
endif
else
player:tell("Please type the text you want, or paste it in from your text editor or word processor");
text = $command_utils:read_lines();
if ($command_utils:yes_or_no("Do you really want to replace the text on this note?"))
this:set_text(text);
player:tell("Note updated.");
else
player:tell("Okay, leaving your note unchanged.");
endif
endif
endif
"Last modified Wed Apr 15 13:01:10 1998 CDT by Wizard (#2).";
.
#9:13
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Assemble the most important properties for quick and easy editing.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
options = pass(@args);
options = {@options, "<OPTION VALUE=\"edit_note_text\">Edit Note Text"};
return options;
"Last modified Fri Apr 13 19:02:02 2001 CDT by Wizard (#2).";
.
#9:14
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Email note text via Xpress.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
if (data != {{}, {}})
app = toobj(data[2][1]);
vrb = tostr(data[2][2]);
else
app = user.location;
vrb = "_html";
endif
alert = "";
result = 0;
subject = tostr(this.name, " (", $network.moo_name, ")");
text = this.text;
if (typeof(text) != LIST)
text = {text};
endif
if (result = $Xpress_MOO_Mailer:sendmail(user, user.email_address, subject, @text))
alert = tostr("An error was encountered while trying to email note: ", result);
else
alert = tostr(this.name, " emailed to your address: ", user.email_address);
endif
html = this:build(user, app:(vrb)(user), app.name, $encore_web_utils:make_alert(alert));
return html;
"Last modified Fri Apr 13 19:03:56 2001 CDT by Wizard (#2).";
.
#10:0
if ((caller != #0) && (caller != this))
return E_PERM;
else
clist = {};
for j in ({this, @$object_utils:ancestors(this)})
for i in [1..length(verbs(j))]
if ((verb_args(j, i) == {"any", "none", "any"}) && index((info = verb_info(j, 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..$], "*", "")};
endif
endfor
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;
endif
.
#10:1
if ((caller != #0) && (caller != this))
return E_PERM;
else
msg = this.welcome_message;
version = server_version();
for line in ((typeof(msg) == LIST) ? msg | {msg})
if (typeof(line) == STR)
notify(player, strsub(line, "%v", version));
endif
endfor
this:check_player_db();
this:check_for_shutdown();
this:maybe_print_lag();
return 0;
endif
"Last modified Thu Apr  8 10:36:15 1999 CDT by Wizard (#2).";
.
#10:2
masked = $login.who_masks_wizards ? $wiz_utils:connected_wizards() | {};
if ((caller != #0) && (caller != this))
return E_PERM;
elseif (!args)
plyrs = connected_players();
if (length(plyrs) > 100)
this:notify(tostr("You have requested a listing of ", length(plyrs), " players.  Please restrict the number of players in any single request to a smaller number.  The lag thanks you."));
return 0;
else
$code_utils:show_who_listing($set_utils:difference(plyrs, masked)) || this:notify("No one logged in.");
endif
else
plyrs = listdelete($command_utils:player_match_result($string_utils:match_player(args), args), 1);
if (length(plyrs) > 100)
this:notify(tostr("You have requested a listing of ", length(plyrs), " players.  Please restrict the number of players in any single request to a smaller number.  The lag thanks you."));
return 0;
endif
$code_utils:show_who_listing(plyrs, $set_utils:intersection(plyrs, masked));
endif
return 0;
.
#10:3
"$login:connect(player-name [, password])";
" => 0 (for failed connections)";
" => objnum (for successful connections)";
((caller == #0) || (caller == this)) || raise(E_PERM);
"=================================================================";
"=== Check arguments, print usage notice if necessary";
try
{name, ?password = 0} = args;
name = strsub(name, " ", "_");
except (E_ARGS)
notify(player, tostr("Usage:  ", verb, " <existing-player-name> <password>"));
return 0;
endtry
try
"=================================================================";
"=== Is our candidate name invalid?";
if (!valid(candidate = orig_candidate = this:_match_player(name)))
raise(E_INVARG, tostr("`", name, "' matches no player name."));
endif
"=================================================================";
"=== Is our candidate unable to connect for generic security";
"=== reasons (ie clear password, non-player object)?";
if (`is_clear_property(candidate, "password") ! E_PROPNF' || (!$object_utils:isa(candidate, $player)))
server_log(tostr("FAILED CONNECT: ", name, " (", candidate, ") on ", connection_name(player), ($string_utils:connection_hostname(connection_name(player)) in candidate.all_connect_places) ? "" | "******"));
raise(E_INVARG);
endif
"=================================================================";
"=== Check password";
if (typeof(cp = candidate.password) == STR)
"=== Candidate requires a password";
if (password)
"=== Candidate requires a password, and one was provided";
if (strcmp(crypt(password, cp), cp))
"=== Candidate requires a password, and one was provided, which was wrong";
server_log(tostr("FAILED CONNECT: ", name, " (", candidate, ") on ", connection_name(player), ($string_utils:connection_hostname(connection_name(player)) in candidate.all_connect_places) ? "" | "******"));
raise(E_INVARG, "Invalid password.");
else
"=== Candidate requires a password, and one was provided, which was right";
endif
else
"=== Candidate requires a password, and none was provided";
set_connection_option(player, "binary", 1);
notify(player, "Password: ");
set_connection_option(player, "binary", 0);
set_connection_option(player, "client-echo", 0);
this:add_interception(player, "intercepted_password", candidate);
return 0;
endif
elseif (cp == 0)
"=== Candidate does not require a password";
else
"=== Candidate has a nonstandard password; something's wrong";
raise(E_INVARG);
endif
"=================================================================";
"=== Is the player locked out?";
if ($no_connect_message && (!candidate.wizard))
notify(player, $no_connect_message);
server_log(tostr("REJECTED CONNECT: ", name, " (", candidate, ") on ", connection_name(player)));
return 0;
endif
"=================================================================";
"=== Check guest connections";
if ($object_utils:isa(candidate, $guest) && (!valid(candidate = candidate:defer())))
if (candidate == #-2)
server_log(tostr("GUEST DENIED: ", connection_name(player)));
notify(player, "Sorry, guest characters are not allowed from your site at the current time.");
else
notify(player, "Sorry, all of our guest characters are in use right now.");
endif
return 0;
endif
"=================================================================";
"=== Check newts";
if (candidate in this.newted)
if (entry = $list_utils:assoc(candidate, this.temporary_newts))
if ((uptime = this:uptime_since(entry[2])) > entry[3])
"Temporary newting period is over.  Remove entry.  Oh, send mail, too.";
this.temporary_newts = setremove(this.temporary_newts, entry);
this.newted = setremove(this.newted, candidate);
fork (0)
player = this.owner;
$mail_agent:send_message(player, $newt_log, tostr("automatic @unnewt ", candidate.name, " (", candidate, ")"), {"message sent from $login:connect"});
endfork
else
notify(player, "");
notify(player, this:temp_newt_registration_string(entry[3] - uptime));
boot_player(player);
return 0;
endif
else
notify(player, "");
notify(player, this:newt_registration_string());
boot_player(player);
return 0;
endif
endif
"=================================================================";
"=== Connection limits based on lag";
if ((((!candidate.wizard) && (!(candidate in this.lag_exemptions))) && ((howmany = length(connected_players())) >= (max = this:max_connections()))) && (!$object_utils:connected(candidate)))
notify(player, $string_utils:subst(this.connection_limit_msg, {{"%n", tostr(howmany)}, {"%m", tostr(max)}, {"%l", tostr(this:current_lag())}, {"%t", candidate.last_connect_attempt ? ctime(candidate.last_connect_attempt) | "not recorded"}}));
if ($object_utils:has_property($local, "mudlist"))
notify(player, "You may wish to try another MUD while waiting for the MOO to unlag.  Here are a few that we know of:");
for l in ($local.mudlist:choose(3))
notify(player, l);
endfor
endif
candidate.last_connect_attempt = time();
server_log(tostr("CONNECTION LIMIT EXCEEDED: ", name, " (", candidate, ") on ", connection_name(player)));
boot_player(player);
return 0;
endif
"=================================================================";
"=== Log the player on!";
if (candidate != orig_candidate)
notify(player, tostr("Okay,... ", name, " is in use.  Logging you in as `", candidate.name, "'"));
endif
this:record_connection(candidate);
return candidate;
except (E_INVARG)
notify(player, "Either that player does not exist, or has a different password.");
return 0;
endtry
.
#10:4
if ((caller != #0) && (caller != this))
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 ($player_db.frozen)
notify(player, "Sorry, can't create any new players right now.  Try again in a few minutes.");
elseif ((!(name = args[1])) || (name == "<>"))
notify(player, "You can't have a blank name!");
if (name)
notify(player, "Also, don't use angle brackets (<>).");
endif
elseif ((name[1] == "<") && (name[$] == ">"))
notify(player, "Try that again but without the angle brackets, e.g.,");
notify(player, tostr(" ", verb, " ", name[2..$ - 1], " ", strsub(strsub(args[2], "<", ""), ">", "")));
notify(player, "This goes for other commands as well.");
elseif (index(name, " "))
notify(player, "Sorry, no spaces are allowed in player names.  Use dashes or underscores.");
"... lots of routines depend on there not being spaces in player names...";
elseif ((!$player_db:available(name)) || (this:_match_player(name) != $failed_match))
notify(player, "Sorry, that name is not available.  Please choose another.");
"... note the :_match_player call is not strictly necessary...";
"... it is merely there to handle the case that $player_db gets corrupted.";
elseif (!(password = args[2]))
notify(player, "You must set a password for your player.");
else
new = $quota_utils:bi_create($player_class, $nothing);
set_player_flag(new, 1);
new.name = name;
new.aliases = {name};
new.programmer = $player_class.programmer;
new.password = crypt(password);
new.last_password_time = time();
new.last_connect_time = $maxint;
"Last disconnect time is creation time, until they login.";
new.last_disconnect_time = time();
"make sure the owership quota isn't clear!";
$quota_utils:initialize_quota(new);
this:record_connection(new);
move(new, $player_start);
$player_db:insert(name, new);
return new;
endif
return 0;
.
#10:5
if ((caller != #0) && (caller != this))
return E_PERM;
else
boot_player(player);
return 0;
endif
.
#10:6
if ((caller != #0) && (caller != this))
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) && (caller != this))
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) && (caller != this))
return E_PERM;
endif
if (li = this:interception(player))
return {@li, @args};
endif
if (!args)
return {this.blank_command, @args};
elseif ((verb = args[1]) && (!$string_utils:is_numeric(verb)))
for i in ({this, @$object_utils:ancestors(this)})
try
if ((verb_args(i, verb) == {"any", "none", "any"}) && index(verb_info(i, verb)[2], "x"))
return args;
endif
except (ANY)
continue i;
endtry
endfor
endif
return {this.bogus_command, @args};
.
#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 shut down in " + $time_utils:english_time(when - (when % 60))) + ".");
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
notify(player, "***");
for piece in ($generic_editor:fill_string("Repeat:  Do not panic.  In particular, please do not send mail to any wizards or the registrar asking about this.  It will finish in time.  Thank you for your patience.", 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]) ! ANY';
.
#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 (caller_perms().wizard)
pass();
this.lag_exemptions = {};
this.max_connections = 99999;
this.lag_samples = {0, 0, 0, 0, 0};
this.print_lag = 0;
this.last_lag_sample = 0;
this.bogus_command = "?";
this.blank_command = "welcome";
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 = this.core_welcome;
this.help_message = {"Sorry, but there's no help here yet.  Type `?' for a list of commands."};
this.redlist = this.blacklist = this.graylist = this.spooflist = {{}, {}};
this.temporary_redlist = this.temporary_blacklist = this.temporary_graylist = this.temporary_spooflist = {{}, {}};
this.who_masks_wizards = 0;
this.newted = this.temporary_newts = {};
this.downtimes = {};
if ("monitor" in properties(this))
delete_property(this, "monitor");
endif
if ("monitor" in verbs(this))
delete_verb(this, "monitor");
endif
if ("record_connection(real)" in verbs(this))
delete_verb(this, "record_connection");
info = verb_info(this, "record_connection(real)");
set_verb_info(this, "record_connection(real)", {info[1], "rxd", "record_connection"});
endif
if ("special_action" in verbs(this))
set_verb_code(this, "special_action", {});
endif
endif
"Last modified Sun Aug 17 11:13:21 1997 CDT by Wizard (#2).";
.
#10:17
.
#10:18
":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 this:(verb + "_temp")(hostname);
.
#10:19
"To add a temporary entry, only call the `temp' version.";
"blacklist_add_temp(Site, start time, duration)";
if (!caller_perms().wizard)
return E_PERM;
endif
{where, ?start, ?duration} = args;
lname = this:listname(verb);
which = 1 + (!$site_db:domain_literal(where));
if (index(verb, "temp"))
lname = "temporary_" + lname;
this.(lname)[which] = setadd(this.(lname)[which], {where, start, duration});
else
this.(lname)[which] = setadd(this.(lname)[which], where);
endif
return 1;
.
#10:20
"The temp version removes from the temporary property if it exists.";
if (!caller_perms().wizard)
return E_PERM;
endif
where = args[1];
lname = this:listname(verb);
which = 1 + (!$site_db:domain_literal(where));
if (index(verb, "temp"))
lname = "temporary_" + lname;
if (entry = $list_utils:assoc(where, this.(lname)[which]))
this.(lname)[which] = setremove(this.(lname)[which], entry);
return 1;
else
return E_INVARG;
endif
elseif (where in this.(lname)[which])
this.(lname)[which] = setremove(this.(lname)[which], where);
return 1;
else
return E_INVARG;
endif
.
#10:21
return {"???", "blacklist", "graylist", "redlist", "spooflist"}[1 + index("bgrs", (args[1] || "?")[1])];
.
#10:22
":record_connection(plyr) update plyr's connection information";
"to reflect impending login.";
if (!caller_perms().wizard)
return E_PERM;
else
plyr = args[1];
plyr.first_connect_time = min(time(), plyr.first_connect_time);
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);
chost = $string_utils:connection_hostname(cn);
acp = setremove(plyr.all_connect_places, chost);
plyr.all_connect_places = {chost, @acp[1..min($, 15)]};
if (!$object_utils:isa(plyr, $guest))
$site_db:add(plyr, chost);
endif
endif
.
#10:23
if (!caller_perms().wizard)
return E_PERM;
endif
lag = (time() - this.last_lag_sample) - 15;
this.lag_samples = {lag, @this.lag_samples[1..3]};
"Now compute the current lag and store it in a property, instead of computing it in :current_lag, which is called a hundred times a second.";
thislag = max(0, (time() - this.last_lag_sample) - this.lag_sample_interval);
if (thislag > (60 * 60))
"more than an hour, probably the lag sampler stopped";
this.current_lag = 0;
else
samples = this.lag_samples;
sum = 0;
cnt = 0;
for x in (listdelete(samples, 1))
sum = sum + x;
cnt = cnt + 1;
endfor
this.current_lag = max(thislag, samples[1], samples[2], sum / cnt);
endif
fork (15)
this:sample_lag();
endfork
this.last_lag_sample = time();
.
#10:24
return this:current_lag() > this.lag_cutoff;
.
#10:25
max = this.max_connections;
if (typeof(max) == LIST)
if (this:is_lagging())
max = max[1];
else
max = max[2];
endif
endif
return max;
.
#10:26
"request_character(player, name, address)";
"return true if succeeded";
if (!caller_perms().wizard)
return E_PERM;
endif
{who, name, address} = args;
connection = $string_utils:connection_hostname(connection_name(who));
if (reason = $wiz_utils:check_player_request(name, address, connection))
prefix = "";
if (reason[1] == "-")
reason = reason[2..$];
prefix = "Please";
else
prefix = "Please try again, or, to register another way,";
endif
notify(who, reason);
msg = tostr(prefix, " send mail to ", $login.registration_address, ", with the character name you want.");
for l in ($generic_editor:fill_string(msg, 70))
notify(who, l);
endfor
return 0;
endif
if (lines = $no_one:eval_d("$local.help.(\"multiple-characters\")")[2])
notify(who, "Remember, in general, only one character per person is allowed.");
notify(who, tostr("Do you already have a ", $network.moo_name, " character? [enter `yes' or `no']"));
answer = read(who);
if (answer == "yes")
notify(who, "Process terminated *without* creating a character.");
return 0;
elseif (answer != "no")
return notify(who, tostr("Please try again; when you get this question, answer `yes' or `no'. You answered `", answer, "'"));
endif
notify(who, "For future reference, do you want to see the full policy (from `help multiple-characters'?");
notify(who, "[enter `yes' or `no']");
if (read(who) == "yes")
for x in (lines)
for y in ($generic_editor:fill_string(x, 70))
notify(who, y);
endfor
endfor
endif
endif
notify(who, tostr("A character named `", name, "' will be created."));
notify(who, tostr("A random password will be generated, and e-mailed along with"));
notify(who, tostr(" an explanatory message to: ", address));
notify(who, tostr(" [Please double-check your email address and answer `no' if it is incorrect.]"));
notify(who, "Is this OK? [enter `yes' or `no']");
if (read(who) != "yes")
notify(who, "Process terminated *without* creating a character.");
return 0;
endif
if (!$network.active)
$mail_agent:send_message(this.owner, $registration_db.registrar, "Player request", {"Player request from " + connection, ":", "", (("@make-player " + name) + " ") + address});
notify(who, tostr("Request for new character ", name, " email address '", address, "' accepted."));
notify(who, tostr("Please be patient until the registrar gets around to it."));
notify(who, tostr("If you don't get email within a week, please send regular"));
notify(who, tostr("  email to: ", $login.registration_address, "."));
elseif ($player_db.frozen)
notify(who, "Sorry, can't create any new players right now.  Try again in a few minutes.");
else
new = $wiz_utils:make_player(name, address);
password = new[2];
new = new[1];
notify(who, tostr("Character ", name, " (", new, ") created."));
notify(who, tostr("Mailing password to ", address, "; you should get the mail very soon."));
notify(who, tostr("If you do not get it, please do NOT request another character."));
notify(who, tostr("Instead, send regular email to ", $login.registration_address, ","));
notify(who, tostr("with the name of the character you requested."));
$mail_agent:send_message(this.owner, $new_player_log, tostr(name, " (", new, ")"), {address, tostr(" Automatically created at request of ", valid(player) ? player.name | "unconnected player", " from ", connection, ".")});
$wiz_utils:send_new_player_mail(tostr("Someone connected from ", connection, " at ", ctime(), " requested a character on ", $network.moo_name, " for email address ", address, "."), name, address, new, password);
return 1;
endif
.
#10:27
if ((caller != #0) && (caller != this))
return E_PERM;
endif
"must be #0:do_login_command";
if (!this.request_enabled)
for line in ($generic_editor:fill_string(this:registration_string(), 70))
notify(player, line);
endfor
elseif ((length(args) != 3) || (args[2] != "for"))
notify(player, tostr("Usage:  ", verb, " <new-player-name> for <email-address>"));
elseif ($login:request_character(player, args[1], args[3]))
boot_player(player);
endif
.
#10:28
if ((caller != #0) && (caller != this))
return E_PERM;
else
msg = this.help_message;
for line in ((typeof(msg) == LIST) ? msg | {msg})
if (typeof(line) == STR)
notify(player, line);
endif
endfor
return 0;
endif
.
#10:29
if ((caller == this) || (caller_perms() == player))
if (this.print_lag)
lag = this:current_lag();
if (lag > 1)
lagstr = tostr("approximately ", lag, " seconds");
elseif (lag == 1)
lagstr = "approximately 1 second";
else
lagstr = "low";
endif
notify(player, tostr("The lag is ", lagstr, "; there ", ((l = length(connected_players())) == 1) ? "is " | "are ", l, " connected."));
endif
endif
.
#10:30
return this.current_lag;
.
#10:31
"This limits the number of commands that can be issued from the login prompt to prevent haywire login programs from lagging the MOO.";
"$login.current_connections has the current player id's of people at the login prompt.";
"$login.current_numcommands has the number of commands they have issued at the prompt so far.";
"$login.max_numcommands has the maximum number of commands they may try before being booted.";
if (!caller_perms().wizard)
return E_PERM;
else
if (iconn = player in this.current_connections)
knocks = this.current_numcommands[iconn] = this.current_numcommands[iconn] + 1;
else
this.current_connections = {@this.current_connections, player};
this.current_numcommands = {@this.current_numcommands, 1};
knocks = 1;
"...sweep idle connections...";
for p in (this.current_connections)
if (typeof(`idle_seconds(p) ! ANY') == ERR)
n = p in this.current_connections;
this.current_connections = listdelete(this.current_connections, n);
this.current_numcommands = listdelete(this.current_numcommands, n);
endif
endfor
endif
if (knocks > this.max_numcommands)
notify(player, "Sorry, too many commands issued without connecting.");
boot_player(player);
return 1;
else
return 0;
endif
endif
.
#10:32
"Called by #0:server_started when the server restarts.";
if (caller_perms().wizard)
this.downtimes = {{time(), this.last_lag_sample}, @this.downtimes[1..min($, 100)]};
endif
.
#10:33
"uptime_since(time): How much time has LambdaMOO been up since `time'";
since = args[1];
up = time() - since;
for x in (this.downtimes)
if (x[1] < since)
"downtime predates when we're asking about";
return up;
endif
"since the server was down between x[2] and x[1], don't count it as uptime";
up = up - (x[1] - max(x[2], since));
endfor
return up;
.
#10:34
caller_perms().wizard || $error:raise(E_PERM);
now = time();
tasks = queued_tasks();
sum = 0;
for t in (tasks)
delay = t[2] - now;
interval = (delay <= 0) ? 1 | (delay * 2);
"SUM is measured in hundredths of a player for the moment...";
(delay <= 300) && (sum = sum + (2000 / interval));
endfor
count = sum / 100;
return count;
.
#10:35
":blacklisted_temp(hostname) => is hostname on the .blacklist...";
":graylisted_temp(hostname)  => is hostname on the .graylist...";
":redlisted_temp(hostname)   => is hostname on the .redlist...";
":spooflisted_temp(hostname) => is hostname on the .spooflist...";
"";
"... and the time limit hasn't run out.";
lname = this:listname(verb);
sitelist = this.("temporary_" + lname);
if (!caller_perms().wizard)
return E_PERM;
elseif (entry = $list_utils:assoc(hostname = args[1], sitelist[1]))
return this:templist_expired(lname, @entry);
elseif (entry = $list_utils:assoc(hostname, sitelist[2]))
return this:templist_expired(lname, @entry);
elseif ($site_db:domain_literal(hostname))
for lit in (sitelist[1])
if ((index(hostname, lit[1]) == 1) && ((hostname + ".")[length(lit[1]) + 1] == "."))
return this:templist_expired(lname, @lit);
endif
endfor
else
for dom in (sitelist[2])
if (index(dom[1], "*"))
"...we have a wildcard; let :match_string deal with it...";
if ($string_utils:match_string(hostname, dom[1]))
return this:templist_expired(lname, @dom);
endif
else
"...tail of hostname ...";
if ((r = rindex(hostname, dom[1])) && ((("." + hostname)[r] == ".") && (((r - 1) + length(dom[1])) == length(hostname))))
return this:templist_expired(lname, @dom);
endif
endif
endfor
endif
return 0;
.
#10:36
"check to see if duration has expired on temporary_<colorlist>. Removes entry if so, returns true if still <colorlisted>";
":(listname, hostname, start time, duration)";
{lname, hname, start, duration} = args;
if (!caller_perms().wizard)
return E_PERM;
endif
if (this:uptime_since(start) > duration)
this:(lname + "_remove_temp")(hname);
return 0;
else
return 1;
endif
.
#10:37
return ("Your character is unavailable for another " + $time_utils:english_time(args[1])) + ".";
.
#10:38
(caller == this) || raise(E_PERM);
{who, verbname, @arguments} = args;
(who in this.intercepted_players) && raise(E_INVARG, "Player already has an interception set.");
this.intercepted_players = {@this.intercepted_players, who};
this.intercepted_actions = {@this.intercepted_actions, {verbname, @arguments}};
return 1;
.
#10:39
(caller == this) || raise(E_PERM);
{who} = args;
if (loc = who in this.intercepted_players)
this.intercepted_players = listdelete(this.intercepted_players, loc);
this.intercepted_actions = listdelete(this.intercepted_actions, loc);
return 1;
else
"raise an error?  nah.";
return 0;
endif
.
#10:40
(caller == this) || raise(E_PERM);
{who} = args;
return (loc = who in this.intercepted_players) ? this.intercepted_actions[loc] | 0;
.
#10:41
(caller == #0) || raise(E_PERM);
this:delete_interception(player);
set_connection_option(player, "client-echo", 1);
notify(player, "");
{candidate, ?password = ""} = args;
return this:connect(tostr(candidate), password);
.
#10:42
"This is where oob handlers need to be put to handle oob commands issued prior to assigning a connection to a player object.  Right now it simply returns.";
return;
.
#10:43
"This is an auto connect version of $login:connect. User's temporary web access code is used for authentication. Jan, 11/18/98";
" => 0 (for failed connections)";
" => objnum (for successful connections)";
((caller == #0) || (caller == this)) || raise(E_PERM);
"=================================================================";
"=== Check arguments, print usage notice if necessary";
try
{name, ?web_access_code = 0} = args;
name = strsub(name, " ", "_");
except (E_ARGS)
return 0;
endtry
try
"=================================================================";
"=== Is our candidate name invalid?";
if (!valid(candidate = orig_candidate = this:_match_player(name)))
raise(E_INVARG, tostr("`", name, "' matches no player name."));
endif
"=================================================================";
"=== Is our candidate unable to connect for generic security";
"=== reasons (ie clear password, non-player object)?";
if (`is_clear_property(candidate, "web_access_code") ! E_PROPNF' || (!$object_utils:isa(candidate, $player)))
server_log(tostr("FAILED CONNECT: ", name, " (", candidate, ") on ", connection_name(player), ($string_utils:connection_hostname(connection_name(player)) in candidate.all_connect_places) ? "" | "******"));
raise(E_INVARG);
endif
"=================================================================";
"=== Check web access code";
if (typeof(cp = candidate.web_access_code) == STR)
"=== Candidate requires a password";
if (web_access_code)
"=== Candidate requires a password, and one was provided";
if (strcmp(crypt(cp, web_access_code), web_access_code))
"=== Candidate requires a password, and one was provided, which was wrong";
server_log(tostr("FAILED CONNECT: ", name, " (", candidate, ") on ", connection_name(player), ($string_utils:connection_hostname(connection_name(player)) in candidate.all_connect_places) ? "" | "******"));
raise(E_INVARG, "Invalid web access code.");
else
"=== Candidate requires a web access code, and one was provided, which was right";
endif
else
"=== Candidate requires a password, and none was provided";
set_connection_option(player, "binary", 1);
notify(player, "Web Access Code: ");
set_connection_option(player, "binary", 0);
set_connection_option(player, "client-echo", 0);
this:add_interception(player, "intercepted_password", candidate);
return 0;
endif
elseif (cp == 0)
"=== Candidate does not require a password";
else
"=== Candidate has a nonstandard password; something's wrong";
raise(E_INVARG);
endif
"=================================================================";
"=== Is the player locked out?";
if ($no_connect_message && (!candidate.wizard))
notify(player, $no_connect_message);
server_log(tostr("REJECTED CONNECT: ", name, " (", candidate, ") on ", connection_name(player)));
return 0;
endif
"=================================================================";
"Guest login check removed as logins with Xpress are always pre-approved";
"=================================================================";
"=== Check newts";
if (candidate in this.newted)
if (entry = $list_utils:assoc(candidate, this.temporary_newts))
if ((uptime = this:uptime_since(entry[2])) > entry[3])
"Temporary newting period is over.  Remove entry.  Oh, send mail, too.";
this.temporary_newts = setremove(this.temporary_newts, entry);
this.newted = setremove(this.newted, candidate);
fork (0)
player = this.owner;
$mail_agent:send_message(player, $newt_log, tostr("automatic @unnewt ", candidate.name, " (", candidate, ")"), {"message sent from $login:connect"});
endfork
else
notify(player, "");
notify(player, this:temp_newt_registration_string(entry[3] - uptime));
boot_player(player);
return 0;
endif
else
notify(player, "");
notify(player, this:newt_registration_string());
boot_player(player);
return 0;
endif
endif
"=================================================================";
"=== Connection limits based on lag";
if ((((!candidate.wizard) && (!(candidate in this.lag_exemptions))) && ((howmany = length(connected_players())) >= (max = this:max_connections()))) && (!$object_utils:connected(candidate)))
notify(player, $string_utils:subst(this.connection_limit_msg, {{"%n", tostr(howmany)}, {"%m", tostr(max)}, {"%l", tostr(this:current_lag())}, {"%t", candidate.last_connect_attempt ? ctime(candidate.last_connect_attempt) | "not recorded"}}));
if ($object_utils:has_property($local, "mudlist"))
notify(player, "You may wish to try another MUD while waiting for the MOO to unlag.  Here are a few that we know of:");
for l in ($local.mudlist:choose(3))
notify(player, l);
endfor
endif
candidate.last_connect_attempt = time();
server_log(tostr("CONNECTION LIMIT EXCEEDED: ", name, " (", candidate, ") on ", connection_name(player)));
boot_player(player);
return 0;
endif
"=================================================================";
"=== Log the player on!";
if (candidate != orig_candidate)
notify(player, tostr("Okay,... ", name, " is in use.  Logging you in as `", candidate.name, "'"));
endif
this:record_connection(candidate);
return candidate;
except (E_INVARG)
notify(player, "Either that player does not exist, or has a different password.");
return 0;
endtry
"Last modified Thu Apr  8 11:33:02 1999 CDT by Wizard (#2).";
.
#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(tostr("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..$];
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
"... oh well ...";
elseif (pos == nargs)
if (E_PROPNF == (get = `dobj.(msg_name + "_msg") ! ANY'))
player:notify(tostr(dobj.name, " (", dobj, ") has no \"", msg_name, "\" message."));
elseif (typeof(get) == ERR)
player:notify(tostr(get));
elseif (!get)
player:notify("Message is not set.");
else
player:notify(tostr("The \"", msg_name, "\" message of ", dobj.name, " (", dobj, "):"));
player:notify(tostr(get));
endif
else
set = dobj:set_message(msg_name, message);
if (set)
if (typeof(set) == STR)
player:notify(set);
else
player:notify(tostr("You set the \"", msg_name, "\" message of ", dobj.name, " (", dobj, ")."));
endif
elseif (set == E_PROPNF)
player:notify(tostr(dobj.name, " (", dobj, ") has no \"", msg_name, "\" message to set."));
elseif (typeof(set) == ERR)
player:notify(tostr(set));
else
player:notify(tostr("You clear the \"", msg_name, "\" message of ", dobj.name, " (", dobj, ")."));
endif
endif
.
#11:1
"{last_huh}  give any to any";
"a give \"verb\" that works for non-$things.";
set_task_perms(caller_perms());
if (((verb == "give") && (dobjstr == "up")) && (!prepstr))
player:tell("Try this instead: @quit");
elseif (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("I don't see ", iobj.name, " here.");
else
dobj:moveto(iobj);
if (dobj.location == iobj)
player:tell("You give ", dobj:title(), " to ", iobj.name, ".");
iobj:tell(player.name, " gives you ", dobj:title(), ".");
else
player:tell("Either that doesn't want to be given away or ", iobj.name, " 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 (dobj == $nothing)
player:tell(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("I don't see that here.");
else
dobj:moveto(player);
if (dobj.location == player)
player:tell("Taken.");
player.location:announce(player.name, " takes ", dobj.name, ".");
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 (dobj == $nothing)
player:tell(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:acceptable(dobj))
player:tell("You can't drop that here.");
else
dobj:moveto(player.location);
if (dobj.location == player.location)
player:tell_lines((verb[1] == "d") ? "Dropped." | "Thrown.");
player.location:announce(player.name, (verb[1] == "d") ? " dropped " | " threw away ", dobj.name, ".");
else
player:tell_lines("You can't seem to drop that here.");
endif
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($guest_log.max_entries, $)]};
else
return E_PERM;
endif
.
#12:1
":last([n,[guest_list]])";
"print list of the last n entries in the guest log";
" (use n=0 if you want all entries)";
" optional second arg limits listing to the specified guest(s)";
set_task_perms(caller_perms());
{?howmany = 0, ?which = 0} = args;
howmany = min(howmany || $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 (which && (!(c[1] in which)))
elseif (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], $object_utils:connected(c[1]) ? -idle_seconds(c[1]) | 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
$command_utils:suspend_if_needed(2);
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]);
$command_utils:suspend_if_needed(2);
endfor
endif
.
#12:2
if (caller_perms().wizard)
pass(@args);
this.connections = {};
endif
.
#12:3
":find(guest_id,time)";
" => site name of guest logged in at that time";
" => 0 if not logged in";
" => E_NACC if this is earlier than the earliest guest recorded";
set_task_perms(caller_perms());
{who, when} = args;
if (!caller_perms().wizard)
raise(E_PERM);
else
found = (who in connected_players()) ? $string_utils:connection_hostname(who.last_connect_place) | 0;
for c in ($guest_log.connections)
if (c[3] < when)
return found;
elseif (c[1] != who)
"... different guest...";
elseif (c[2])
"...login...";
if (c[3] == when)
return found;
endif
found = 0;
else
"...logout...";
found = c[4];
endif
endfor
return E_NACC;
endif
.
#13:0
":length(tree) => number of leaves in tree.";
return args[1] ? args[1][2] | 0;
.
#13:1
":find_nth(tree,n) => nth leaf of tree.  Assumes n in [1..tree[2]]";
return this:_find_nth(caller, @args);
.
#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;
"Last modified Wed Dec 17 18:11:11 1997 MET by Wizard (#2).";
.
#13:3
":set_nth(tree,n,value) => tree";
"modifies tree so that nth leaf == value";
if (((n = args[2]) < 1) || ((!(tree = args[1])) || (tree[2] < n)))
return E_RANGE;
else
this:_set_nth(caller, @args);
return (n != 1) ? tree | listset(tree, caller:_ord(args[3]), 3);
endif
.
#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
":_find_nth(home,tree,n) => nth leaf of tree.";
"...Assumes n in [1..tree[2]]";
if (caller != this)
return E_PERM;
endif
{home, tree, n} = args;
if ((p = home:_get(tree[1]))[1])
for k in (p[2])
if (n > k[2])
n = n - k[2];
else
return this:_find_nth(home, k, n);
endif
endfor
return E_RANGE;
else
return p[2][n];
endif
.
#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
"Last modified Wed Dec 17 18:11:27 1997 MET by Wizard (#2).";
.
#13:14
":_set_nth(home,tree,n,value) => tree[n] = value";
"Assumes n in [1..tree[2]]";
if (caller != this)
return E_PERM;
endif
{home, tree, n, value} = args;
if ((p = home:_get(tree[1]))[1])
ik = this:_listfind_nth(p[2], n - 1);
this:_set_nth(home, p[2][ik[1]], ik[2] + 1, value);
if (!ik[2])
p[2][ik[1]][3] = home:_ord(value);
home:_put(tree[1], @p);
endif
else
p[2][n] = value;
home:_put(tree[1], @p);
endif
.
#13:15
":_skill(home,node,kill_leaf)";
"home:_kill's node and all descendants, home:(kill_leaf)'s all leaves";
if (caller != this)
raise(E_PERM);
endif
{home, node, kill_leaf} = args;
try
{height, subtrees} = home:_get(node) || {0, {}};
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);
"Last modified Wed Dec 17 18:11:47 1997 MET by Wizard (#2).";
.
#13:16
":_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)};
.
#13:17
"_merge(home,ltree,rtree) => newtree";
"assumes ltree and rtree to be nonempty.";
if (caller != this)
return E_PERM;
endif
{home, lnode, rnode} = args;
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]};
.
#13:18
"_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, height, ltree, rtree} = args;
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..$]);
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
.
#13:19
"_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, height, lmax, ltree, @rtrees} = 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
.
#13:20
":_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, tree, insert} = args;
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]};
.
#13:21
":_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;
.
#13:22
"_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, key} = args;
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};
.
#13:23
if (caller != this)
return E_PERM;
endif
.
#13:24
return $perm_utils:controls(caller_perms(), this) ? this:(args[1])(@listdelete(args, 1)) | E_PERM;
.
#13:25
":_call(home,verb,@vargs) calls home:verb(@vargs) as $no_one";
set_task_perms($no_one);
if (caller != this)
raise(E_PERM);
endif
{home, vb, @vargs} = args;
return home:(vb)(@vargs);
"Last modified Wed Dec 17 18:10:47 1997 MET by Wizard (#2).";
.
#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, ""}) ! ANY';
return prop;
.
#14:2
":_kill(node) destroys the given node.";
if (!(caller in {this, this._mgr}))
return E_PERM;
endif
`delete_property(this, args[1]) ! ANY';
.
#14:3
return (caller == this._mgr) ? `this.(args[1]) ! ANY' | 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..$]), 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..$];
.
#14:11
return {@args[3..$], @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..$];
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, ?cur = 0, ?last_old = $maxint} = args;
keep_seq = {@$seq_utils:contract(this:kept_msg_seq(), $seq_utils:complement(seq, 1, this:length_all_msgs())), $maxint};
k = 1;
mcount = 0;
width = 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);
if (keep_seq[k] <= (mcount = mcount + 1))
k = k + 1;
endif
annot = (x[3] > last_old) ? "+" | ((k % 2) ? " " | "=");
line = tostr($string_utils:right(x[2], 5, (cur == x[2]) ? ">" | " "), ":", annot, " ", this:msg_summary_line(@this:(getmsg)(@x)));
player:tell(line[1..min(width, $)]);
endfor
handle = this._mgr:next(@listdelete(handle, 1));
endwhile
seq = seq[3..$];
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, ?preamble = ""} = args;
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)) | {}, this:msg_full_text(@this:_message_text(@x)));
endfor
handle = this._mgr:next(@listdelete(handle, 1));
$command_utils:suspend_if_needed(0);
endwhile
seq = seq[3..$];
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";
going = this.messages_going;
if (going && ((!going[1]) || (typeof(going[1][2]) == INT)))
kept = {@going[1], $maxint};
going = going[2];
else
kept = {$maxint};
endif
k = 1;
mcount = 0;
for s in (going)
if (kept[k] <= (mcount = mcount + s[1]))
k = k + 1;
endif
len = len + s[2][2];
handle = this._mgr:start(s[2], 1, s[2][2]);
while (handle)
for x in (handle[1])
if (kept[k] <= (mcount = mcount + 1))
k = k + 1;
endif
player:tell($string_utils:right(this:_message_num(@x), 4), (k % 2) ? ":  " | ":= ", 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;
"there are two possible formats here:";
"OLD: {{n,msgs},{n,msgs},...}";
"NEW: {kept_seq, {{n,msgs},{n,msgs},...}}";
going = this.messages_going;
if (going && ((!going[1]) || (typeof(going[1][2]) == INT)))
kept = going[1];
going = going[2];
else
kept = {};
endif
for s in (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.messages_kept = $seq_utils:union(kept, $seq_utils:expand(this.messages_kept, seq));
this:_fix_last_msg_date();
return seq;
.
#14:21
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
len = 0;
going = this.messages_going;
if (going && ((!going[1]) || (typeof(going[1][2]) == INT)))
going = going[2];
endif
for s in (going)
len = len + s[2][2];
this._mgr:kill(s[2], "_killmsg");
endfor
this.messages_going = {};
return len;
.
#14:22
seq = args[1];
if (!(this:ok_write(caller, caller_perms()) || (this:ok(caller, caller_perms()) && (seq = this:own_messages_filter(caller_perms(), @args)))))
return E_PERM;
endif
msgtree = this.messages;
save = nums = {};
onext = 1;
rmmed = 0;
for i in [1..length(seq) / 2]
if ($command_utils:suspend_if_needed(0))
player:tell("... rmm ", onext);
suspend(0);
endif
start = seq[(2 * i) - 1];
next = seq[2 * i];
{msgtree, zmsgs} = this._mgr:extract_range(msgtree, start - rmmed, (next - 1) - rmmed);
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;
save_kept = $seq_utils:intersection(this.messages_kept, seq);
this.messages_kept = $seq_utils:contract(this.messages_kept, seq);
this.messages_going = save_kept ? {save_kept, save} | 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}.";
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
{?cur = 0} = args;
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
if (this:ok(caller, caller_perms()))
new = (msgtree = this.messages) ? this:_message_num(@this._mgr:find_nth(msgtree, msgtree[2])) + 1 | 1;
if (rmsgs = this.messages_going)
lbrm = rmsgs[$][2];
return max(new, this:_message_num(@this._mgr:find_nth(lbrm, lbrm[2])) + 1);
else
return new;
endif
else
return E_PERM;
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
{plist, ?mask = {1, this.messages[2] + 1}} = args;
if (typeof(plist) != LIST)
plist = {plist};
endif
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
{nlist, ?mask = {1, this.messages[2] + 1}} = args;
if (typeof(nlist) != LIST)
nlist = {nlist};
endif
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];
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
{plist, ?mask = {1, this.messages[2] + 1}} = args;
if (typeof(plist) != LIST)
plist = {plist};
endif
seq = {};
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
{nlist, ?mask = {1, this.messages[2] + 1}} = args;
if (typeof(nlist) != LIST)
nlist = {nlist};
endif
seq = {};
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, ?mask = {1, this.messages[2] + 1}} = args;
seq = {};
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, ?mask = {1, this.messages[2] + 1}} = args;
seq = {};
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]))) && index(tostr(@body), target))
seq = $seq_utils:add(seq, i, i);
"Above saves ticks. Munges the whole message into one string and indexes it. Old code follows.";
"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]) || 0;
.
#14:37
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
{?doit = 0} = args;
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..$])};
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 (caller_perms().wizard)
pass();
this._mgr = $biglist;
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
c = callers();
if ((caller != this) && (!((((length(c) > 1) && (c[1][1] == $list_utils)) && (c[1][2] == "map_arg")) && (c[2][1] == this))))
raise(E_PERM);
endif
$command_utils:suspend_if_needed(0);
biglist = this;
propname = args[1];
if (!propname)
bestlevel = -1;
best = {};
for prop in (properties(biglist))
$command_utils:suspend_if_needed(0);
if (index(prop, " ") == 1)
val = biglist.(prop);
if (typeof(val[1]) == INT)
if (bestlevel < val[1])
bestlevel = val[1];
best = {prop};
elseif (bestlevel == val[1])
best = {@best, prop};
endif
endif
endif
endfor
if (!best)
player:notify("Can't find a root.");
raise(E_INVARG);
elseif (length(best) == 1)
propname = best[1];
else
propname = best[1];
val = biglist.(propname);
for prop in (best[2..$])
$command_utils:suspend_if_needed(0);
val[2] = {@val[2], @biglist.(prop)[2]};
endfor
biglist.(propname) = val;
"Now that the new value is safely stored, delete old values.";
for prop in (best[2..$])
$command_utils:suspend_if_needed(0);
player:notify(tostr("Removing property ", toliteral(prop), ".  Its value, ", toliteral(biglist.(prop)), ", has been merged with property ", toliteral(propname), "."));
delete_property(biglist, prop);
endfor
endif
maxlevel = biglist.(propname)[1];
player:notify(tostr("Maximum level is ", maxlevel, "."));
items = $list_utils:make(maxlevel, {});
"Arrgh.  Even after finding the root, some nodes might be detached!";
player:notify("Checking for orphans...");
for prop in (properties(biglist))
$command_utils:suspend_if_needed(0);
if (prop && (prop[1] == " "))
val = biglist.(prop);
if (((typeof(val) == LIST) && (typeof(level = val[1]) == INT)) && (level < maxlevel))
items[level + 1] = {@items[level + 1], prop};
endif
endif
endfor
for prop in (properties(biglist))
$command_utils:suspend_if_needed(0);
if (prop && (prop[1] == " "))
val = biglist.(prop);
if (((typeof(val) == LIST) && (typeof(level = val[1]) == INT)) && (level > 0))
for item in (val[2])
items[level] = setremove(items[level], item[1]);
endfor
endif
endif
endfor
player:notify(tostr("Orphans: ", toliteral(items)));
backbone_prop = propname;
level = maxlevel;
while (level)
backbone = biglist.(backbone_prop);
lastkid = backbone_prop;
for prop in (props = items[level])
backbone[2] = {@backbone[2], {lastkid = prop, 0, {0, 0}}};
endfor
player:notify(tostr("Attaching ", nn = length(props), " propert", (nn == 1) ? "y" | "ies", " to property ", toliteral(backbone_prop), "..."));
biglist.(backbone_prop) = backbone;
backbone_prop = lastkid;
level = level - 1;
endwhile
player:notify(tostr("Orphans repatriated."));
endif
toplevel = "(top level)";
context = args[2] || toplevel;
"This stuff is just paranoia in case something unexpected is in the data structure.  Normally there should be no blowouts here. --Minnie";
if (typeof(propname) != STR)
player:notify(tostr("Context=", context, " Prop Name=", toliteral(propname), " -- bad property name."));
raise(E_INVARG);
endif
val = biglist.(propname);
if (typeof(val) != LIST)
player:notify(tostr("Context=", context, " Prop Name=", toliteral(propname), " -- contents invalid."));
raise(E_INVARG);
endif
if (typeof(level = val[1]) != INT)
player:notify(tostr("Context=", context, " Prop Name=", toliteral(propname), " -- contents invalid (bad first argument)."));
raise(E_INVARG);
endif
"This is where the real work starts. --Minnie";
"First check that the properties referred to really exist.  This must be done for all levels.";
for item in (val[2])
try
biglist.(item[1]);
except (E_PROPNF)
player:notify(tostr("Item ", toliteral(item), " is invalid in property ", toliteral(propname), ".  It is being removed."));
val[2] = setremove(val[2], item);
continue item;
endtry
endfor
"Next, only for upper levels, check that the message count for inferior levels is correct, but only after recursing into those levels and making repairs.";
if (level > 0)
new = $list_utils:map_arg(this, verb, $list_utils:slice(val[2]), propname);
if (val[2] != new)
player:notify(tostr("Changing ", toliteral(val[2]), " to ", toliteral(new), "."));
val[2] = new;
endif
"Now that everything is correct, count size of inferiors.";
endif
"Bravely stuff the result back into place.";
biglist.(propname) = val;
"The result will be of the form:                               ";
"  {propname, inferior_msgcount, {first_msgnum, first_time}}  ";
if (level == 0)
"Count the messages for message count.";
"Use first message number and time for first_msgnum and first_time.";
result = {propname, length(val[2]), val[2][1][2..3]};
else
"Use message count that is sum of inferior counts.";
"Just propagate first node's first_msgnum and first_time upward literally.";
n = 0;
for subnode in (val[2])
n = n + subnode[2];
endfor
result = {propname, n, val[2][1][3]};
endif
if (context == toplevel)
if (result != biglist.messages)
biglist.messages = result;
player:notify(tostr("Property ", biglist, ".messages updated."));
endif
player:tell(biglist.messages[2], " messages repaired in ", $mail_agent:name(biglist), ".");
endif
return result;
"Last modified Thu Feb 15 23:13:44 1996 MST by Minnie (#123).";
.
#14:41
"Syntax: repair <biglist>";
"";
"This tool makes a last-resort attempt to repair broken biglists (ones whose data structures are out of alignment due to an error such as \"out of ticks\" during some update operation leaving the b-tree in an inconsistent state).  This tool comes with no warranty of any kind.  You should only use it when you have no other choice, and you should make an attempt to @dump or fully copy or otherwise checkpoint your object before attempting to repair it so that you can recover from any failures this might produce.  This operation is NOT undoable.";
if (!$perm_utils:controls(player, this))
player:tell("You do not control that.");
elseif (!$command_utils:yes_or_no("This tool can be used to repair some (but maybe not all) situations involving generic biglists that have had an error (usually \"out of ticks\") during an update operation and were left inconsistent.  Is this list really and truly broken in such a way?"))
player:tell("No action taken.  PLEASE don't use this except in extreme cases.");
elseif (!$command_utils:yes_or_no("Have you made a best effort to @dump or otherwise save the contents in case this make things worse?"))
player:tell("No action taken.  PLEASE do any saving you can before proceeding.");
elseif (!$command_utils:yes_or_no("This tool comes with no warranty of any kind.  Is this really your last resort and are you prepared to accept the consequences of utter failure?  There is no undoing the actions this takes.  Do you understand and accept the risks?"))
player:tell("No action taken.  I'm not taking any responsibility for this failing.  It's gotta be your choice.");
else
player:tell("OK!  Going ahead with repair attempts...");
this:_repair();
player:tell("All done.  If this worked, you can thank Mickey.  If not, remember the promises you made above about accepting responsibility for failure.");
endif
"Last modified Fri Feb 16 08:36:27 1996 MST by Minnie (#123).";
.
#14:42
":restore_from(OLD_MAIL_RECIPIENT, LOST_STRING)";
"This clears all biglist properties from this object, then";
"scans the properties of OLD_MAIL_RECIPIENT, which must be a descendant";
"of $big_mail_recipient, looking for those corresponding to mail messages,";
"and then rebuilds the message tree entirely from scratch.";
"";
"No attempt is made to preserve the original tree structure.";
"The live/deleted state of any given message is lost;";
"all messages, including formerly rmm-ed ones, are restored to .messages";
"";
"In the (unlikely) event that message-body properties have been lost, the";
"affected messages are given a one-line body consisting of LOST_STRING";
"";
{old, ?lost_body = "###BODY-LOST###"} = args;
if (!($perm_utils:controls(caller_perms(), this) && $perm_utils:controls(caller_perms(), old)))
raise(E_PERM);
elseif (!$object_utils:isa(old, $big_mail_recipient))
raise(E_TYPE, "First argument must be a $big_mail_recipient.");
elseif (typeof(lost_body) != STR)
raise(E_TYPE, "Second argument, if given, must be a string.");
endif
mgr = this._mgr;
"...";
"... destroy everything...";
for p in (properties(this))
delete_property(this, p);
endfor
this.messages = this.messages_going = {};
"...";
"... look at all properties...";
msgcount = lostcount = 0;
for p in (properties(old))
if (index(p, " ") == 1)
pvalue = old.(p);
"... ignore everything except level-0 nodes...";
if (pvalue[1..min(1, $)] == {0})
for msg in (pvalue[2])
if ((ticks_left() < 6000) || (seconds_left() < 2))
player:tell("...", msgcount, " copied.");
suspend(0);
endif
try
body = old.(msg[1]);
except e (E_PROPNF)
body = {lost_body};
lostcount = lostcount + 1;
endtry
msg[1] = this:_make(@body);
msgtree = mgr:insert_last(this.messages, msg);
msgcount = msgcount + 1;
n = mgr:find_ord(msgtree, this:_message_num(@msg), "_lt_msgnum");
if (n < msgcount)
{msgtree, singleton} = mgr:extract_range(msgtree, msgcount, msgcount);
msgtree = mgr:insert_after(msgtree, singleton, n);
endif
this.messages = msgtree;
endfor
endif
endif
endfor
player:tell(msgcount, " messages installed on ", this.name, "(", this, ")");
if (lostcount)
player:tell(lostcount, " messages have missing bodies (indicated by ", toliteral(lost_body), ").");
else
player:tell("No message bodies were missing.");
endif
.
#15:0
what = args[1];
return is_player(what) && (!(what in connected_players()));
.
#15:1
if (caller != #0)
return E_PERM;
endif
who = args[1];
this:eject(who);
"if (((!$recycler:valid(who.home)) || (!who.home:acceptable(who))) || (typeof(move(who, who.home)) == ERR))";
"  move(who, $player_start);";
"endif";
who.home:announce_all_but({who}, who.name, " has connected.");
.
#15:2
return $player_start:who_location_msg(@args);
.
#15:3
"Don't go anywhere.";
.
#15:4
if ($perm_utils:controls(caller_perms(), this))
if ((what = args[1]).wizard && (what.location == this))
move(what, what.home);
else
return pass(@args);
endif
endif
.
#16:0
return ((caller == this) || caller_perms().wizard) ? pass(@args) | E_PERM;
.
#16:1
":add(player,email[,comment])";
if (!caller_perms().wizard)
return E_PERM;
endif
{who, email, @comment} = 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 (caller_perms().wizard)
pass();
this:clearall();
this.registrar = #2;
endif
.
#16:3
"suspicious(address [,who])";
"Determine whether an address appears to be another player in disguise.";
"returns a list of similar addresses.";
"If second argument given, then if all similar addresses are held by that";
"person, let it pass---they're just switching departments at the same school";
"or something.";
"";
"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
{address, ?allowed = #-1} = args;
{userid, site} = $network:parse_address(address);
exact = (!site) && this:find_exact(address);
if (!site)
site = $network.site;
endif
site = $network:local_domain(site);
sitelen = length(site);
others = this:find_all_keys(userid + "@");
for other in (others)
if (other[max(1, ($ - sitelen) + 1)..$] != site)
others = setremove(others, other);
endif
endfor
if (exact)
others = listinsert(others, address);
endif
for x in (others)
allzapped = 1;
for y in (this:find_exact(x))
if ((((length(y) == 2) && ((y[2] == "zapped due to inactivity") || (y[2] == "toaded due to inactivity"))) || (y[1] == allowed)) || (($object_utils:has_property($local, "second_char_registry") && (typeof(them = $local.second_char_registry:other_chars(y[1])) == LIST)) && (allowed in them)))
"let them change to the address if it is them, or if it is a registered char of theirs.";
"Hrm. Need typeof==LIST check because returns E_INVARG for shared characters. bleah Ho_Yan 5/8/95";
else
allzapped = 0;
endif
endfor
if (allzapped)
others = setremove(others, x);
endif
endfor
return others;
.
#16:4
"suspicious_userid(userid)";
"Return yes if userid is root or postmaster or something like that.";
if ($object_utils:has_property(#0, "local") && $object_utils:has_property($local, "suspicious_userids"))
extra = $local.suspicious_userids;
else
extra = {};
endif
return ((((args[1] in {@$network.suspicious_userids, @extra}) || match(args[1], "^guest")) || match(args[1], "^help")) || 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
"Returns a list of strings describing the registration data for an email address.  Args[1] should be the result of this:find.";
set_task_perms(caller_perms());
result = {};
for x in (args[1])
name = (valid(x[1]) && is_player(x[1])) ? x[1].name | "<recycled>";
email = (valid(x[1]) && is_player(x[1])) ? x[1].email_address | "<???>";
result = {@result, tostr("  ", name, " (", x[1], ") current email: ", email, (length(x) > 1) ? (" [" + x[2]) + "]" | "")};
endfor
return result;
.
#16:6
"Carefully loop through the db and delete items associated with reaped objects.  If that results in no objects remaining for a username, delete that username.";
"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.";
if (!caller_perms().wizard)
raise(E_PERM);
endif
this.prune_task = task_id();
probe = this.prune_progress;
while (probe < this.prune_stop)
for username in (this:find_all_keys(probe))
items = this:find_exact(username);
orig = items;
for y in (items)
{who, @whys} = y;
if ((!valid(who)) || (!is_player(who)))
nuke = 1;
for why in (whys)
if ((why && (why != "zapped due to inactivity")) && (why != "toaded due to inactivity"))
nuke = 0;
endif
endfor
if (nuke)
items = setremove(items, y);
endif
endif
$command_utils:suspend_if_needed(0);
endfor
if (!items)
this:delete(username);
this.total_pruned_people = this.total_pruned_people + 1;
elseif (items != orig)
this:insert(username, items);
this.total_pruned_characters = (this.total_pruned_characters + length(orig)) - length(items);
endif
$command_utils:suspend_if_needed(0);
endfor
probe = $string_utils:incr_alpha(probe);
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));
.
#16:7
player:tell("Prune is up to ", toliteral(this.prune_progress), ".");
mine = 0;
if (typeof(this.prune_progress) == STR)
total = (26 * 26) * 26;
for x in [1..3]
mine = ((mine * 26) + index($string_utils.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_characters, " individual list entries removed, and ", this.total_pruned_people, " whole email addresses 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
.
#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, $)] + subject[open..close];
endif
who = $string_utils:left(who, 18);
line = args[("" in args) + 1];
email = line[1..index(line + " ", " ") - 1];
if (!index(email, "@"))
email = "??";
endif
return tostr(when, "  ", by, " ", who, "  ", email);
.
#17:2
if (caller_perms().wizard)
pass();
"this:rm_message_seq({1, 1 + this:length_all_msgs()})";
"this:expunge_rmm()";
for p in (properties(this))
$command_utils:suspend_if_needed(0);
if (p && (p[1] == " "))
delete_property(this, p);
endif
endfor
this.messages = this.messages_going = {};
this:_fix_last_msg_date();
this._genprop = "";
this.mail_forward = {};
this.mail_notify = {player};
player.current_message = {@player.current_message, {this, 0, 0}};
for p in ({"moderator_forward", "writers", "readers", "expire_period", "last_used_time"})
clear_property(this, p);
endfor
this.moderated = {this};
else
return E_PERM;
endif
.
#17:3
"Copied from Generic Mail Recipient (#6419):is_usable_by by Rog (#4292) Tue Mar  2 10:02:32 1993 PST";
return (!this.moderated) || ((this:is_writable_by(who = args[1]) || (who in this.moderated)) || who.wizard);
.
#17:4
"Stop breaking the expire task completely with out of seconds/ticks.";
if (this:ok_write(caller, caller_perms()))
fork (0)
pass(@args);
endfork
else
return E_PERM;
endif
.
#18:0
if ($code_utils:parse_verbref(what = args[1]))
"... hey wow, I found it!...";
return {what};
else
return {};
endif
.
#18: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)
return {tostr("Information about ", objverb), "----", @verbdoc};
else
return tostr("No information about ", objverb);
endif
endif
.
#18: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;
elseif (typeof(vd = $code_utils:verb_documentation(hv[1], spec[2])) != LIST)
return vd;
else
return {tostr(";$code_utils:set_verb_documentation(", $code_utils:corify_object(hv[1]), ",", $string_utils:print(spec[2]), ",$command_utils:read_lines())"), @$command_utils:dump_lines(vd)};
endif
.
#19:0
if (!args)
l = {};
for p in (properties(#0))
if ((p[max(1, $ - 5)..$] == "_utils") && `#0.(p):help_msg() ! ANY')
l = {@l, "$" + p};
endif
endfor
return {@pass(@args), @l};
elseif (ts = pass(@args))
return ts;
elseif ((what = args[1])[1] != "$")
return {};
elseif (ts = pass("$generic_" + what[2..$]))
return ts;
elseif ((r = rindex(w = strsub(what[2..$], "-", "_"), "_utils")) && ((r == (length(w) - 5)) && (`valid(#0.(w)) ! ANY' && `#0.(w):help_msg() ! ANY')))
return {what};
else
return {};
endif
.
#19:1
topic = args[1];
if ((topic == (("$" + topic[2..$ - 5]) + "utils")) && (valid(#0.(w = strsub(topic[2..$], "-", "_"))) && (uhelp = #0.(w):description())))
return {tostr("General information on $", w, ":"), "----", @(typeof(uhelp) == STR) ? {uhelp} | uhelp};
else
return pass(@args);
endif
.
#19:2
if ((E_PROPNF != (text = pass(@args))) || ((args[1][1] != "$") || ((!((uprop = args[1][2..$]) in properties(#0))) || (typeof(uobj = #0.(uprop)) != OBJ))))
return text;
else
udesc = uobj.description;
return {tostr(";;$", uprop, ".description = $command_utils:read_lines()"), @$command_utils:dump_lines((typeof(udesc) == LIST) ? udesc | {udesc})};
endif
.
#20: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.";
{n, ?fill = " "} = args;
if (typeof(n) == STR)
n = length(n);
endif
if (n > 1000)
"Prevent someone from crashing the moo with $string_utils:space($maxint)";
return 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[($ + 1) + n..$];
.
#20:1
"$string_utils:left(string,width[,filler])";
"";
"Assures that <string> is at least <width> characters wide.  Returns <string> if it is at least that long, or else <string> followed by enough filler to make it that wide. If <width> is negative and the length of <string> is greater than the absolute value of <width>, then the <string> is cut off at <width>.";
"";
"The <filler> is optional and defaults to \" \"; it controls what is used to fill the resulting string when it is too short.  The <filler> is replicated as many times as is necessary to fill the space in question.";
{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
.
#20:2
"$string_utils:right(string,width[,filler])";
"";
"Assures that <string> is at least <width> characters wide.  Returns <string> if it is at least that long, or else <string> preceded by enough filler to make it that wide. If <width> is negative and the length of <string> is greater than the absolute value of <width>, then <string> is cut off at <width> from the right.";
"";
"The <filler> is optional and defaults to \" \"; it controls what is used to fill the resulting string when it is too short.  The <filler> is replicated as many times as is necessary to fill the space in question.";
{text, len, ?fill = " "} = args;
abslen = abs(len);
out = tostr(text);
if ((lenout = length(out)) < abslen)
return this:space(abslen - lenout, fill) + out;
else
return (len > 0) ? out | out[($ - abslen) + 1..$];
endif
.
#20:3
"$string_utils:center(string,width[,lfiller[,rfiller]])";
"";
"Assures that <string> is at least <width> characters wide.  Returns <string> if it is at least that long, or else <string> preceded and followed by enough filler to make it that wide.  If <width> is negative and the length of <string> is greater than the absolute value of <width>, then the <string> is cut off at <width>.";
"";
"The <lfiller> is optional and defaults to \" \"; it controls what is used to fill the left part of the resulting string when it is too short.  The <rfiller> is optional and defaults to the value of <lfiller>; it controls what is used to fill the right part of the resulting string when it is too short.  In both cases, the filler is replicated as many times as is necessary to fill the space in question.";
{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
.
#20: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($, width)]);
endfor
return result;
.
#20:5
"$string_utils:from_list(list [, separator])";
"Return a string being the concatenation of the string representations of the elements of LIST, each pair separated by the string SEPARATOR, which defaults to the empty string.";
{thelist, ?separator = ""} = args;
if (separator == "")
return tostr(@thelist);
elseif (thelist)
result = tostr(thelist[1]);
for elt in (listdelete(thelist, 1))
result = tostr(result, separator, elt);
endfor
return result;
else
return "";
endif
.
#20: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
.
#20:7
"Return a string of the names and object numbers of the objects in a list.";
line = "";
for item in (args[1])
if ((typeof(item) == OBJ) && valid(item))
line = (((line + item.name) + "(") + tostr(item)) + ")   ";
endif
endfor
return $string_utils:trimr(line);
.
#20:8
":from_seconds(number of seconds) => returns a string containing the rough increment of days, or hours if less than a day, or minutes if less than an hour, or lastly in seconds.";
":from_seconds(86400) => \"a day\"";
":from_seconds(7200)  => \"two hours\"";
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;
.
#20: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]];
.
#20: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, ?what = " "} = args;
m = match(string, tostr("[^", what, "]%|$"));
return string[m[1]..$];
.
#20: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, ?what = " "} = args;
return string[1..rmatch(string, tostr("[^", what, "]%|^"))[2]];
.
#20:12
":strip_chars(string,chars) => string with chars removed";
{subject, stripped} = args;
for i in [1..length(stripped)]
subject = strsub(subject, stripped[i], "");
endfor
return subject;
.
#20: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];
wanted = ("[" + args[2]) + "]+";
output = "";
while (m = match(string, wanted))
output = output + string[m[1]..m[2]];
string = string[m[2] + 1..$];
endwhile
return output;
.
#20: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;
.
#20:15
"capitalizes its argument.";
if ((string = args[1]) && (i = index("abcdefghijklmnopqrstuvwxyz", string[1], 1)))
string[1] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i];
endif
return string;
.
#20:16
"Matches args[1] against literal objects: #xxxxx, $variables, *mailing-lists, and username.  Returns the object if successful, $failed_match else.";
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] == "*") && (length(string) > 1))
return $mail_agent:match_recipient(string);
elseif (string[1] == "$")
string[1..1] = "";
object = #0;
while (pn = string[1..(dot = index(string, ".")) ? dot - 1 | $])
if ((!$object_utils:has_property(object, pn)) || (typeof(object = object.(pn)) != OBJ))
return $failed_match;
endif
string = string[length(pn) + 2..$];
endwhile
if ((object == #0) || (typeof(object) == ERR))
return $failed_match;
else
return object;
endif
else
return $failed_match;
endif
.
#20: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];
if (subject == "")
return $nothing;
endif
no_exact_match = no_partial_match = 1;
for i in [1..length(args) / 2]
prop_name = args[(2 * i) + 1];
for object in ((typeof(olist = args[2 * i]) == LIST) ? olist | {olist})
if (valid(object))
if (typeof(str_list = `object.(prop_name) ! E_PERM, E_PROPNF => {}') != 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);
.
#20: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.";
{what, targ, @rest} = args;
wild = "*";
case = ret = {};
what = what + "&^%$";
targ = targ + "&^%$";
for y in (rest)
if (typeof(y) == STR)
wild = y;
elseif (typeof(y) == INT)
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..$];
targ = targ[z + 1..$];
else
return 0;
endif
endwhile
if (ret == {})
return what == "";
elseif (ret == {""})
return 1;
elseif (ret[1] == "")
return ret[2..$];
else
return 0;
endif
.
#20: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 == "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})) || (pobject == $failed_match))
"...exact match in room or match in player failed completely...";
return hobject;
else
return pobject;
endif
.
#20: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;
.
#20: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;
.
#20:22
"find_prefix(prefix, string-list) => list index of something starting with prefix, or 0 or $ambiguous_match.";
{subject, choices} = args;
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;
.
#20: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;
.
#20:24
"Usage:  is_numeric(string)";
"        is_integer(string)";
"Is string numeric (composed of one or more digits possibly preceded by a minus sign)? This won't catch floating points.";
"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;
.
#20: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
.
#20: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, ?comma = ","} = args;
result = "";
sign = (n < 0) ? "-" | "";
n = tostr(abs(n));
while (length(n) > 3)
result = (comma + n[$ - 2..$]) + result;
n = n[1..$ - 3];
endwhile
return (sign + n) + result;
.
#20:27
"$string_utils:english_number(n) -- convert the integer N into English";
"";
"Produces a string containing the English phrase naming the given integer.  For example, $string_utils:english_number(-1234) returns the string `negative one thousand two hundred thirty-four'.";
numb = toint(args[1]);
if (numb == 0)
return "zero";
endif
labels = {"", " thousand", " million", " billion"};
numstr = "";
mod = abs(numb);
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 ((numb < 0) ? "negative " | "") + numstr;
.
#20:28
"$string_utils:english_ordinal(n) -- convert the integer N into an english ordinal (1 => \"first\", etc...)";
numb = toint(args[1]);
if (numb == 0)
return "zeroth";
elseif (numb % 100)
hundreds = (abs(numb) > 100) ? this:english_number((numb / 100) * 100) + " " | ((numb < 0) ? "negative " | "");
numb = abs(numb) % 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 = numb in specials)
return hundreds + ordinals[i];
elseif ((numb > 20) && (i = (numb % 10) in specials))
return ((hundreds + this:english_tens((numb / 10) * 10)) + "-") + ordinals[i];
else
return (hundreds + this:english_number(numb)) + "th";
endif
else
return this:english_number(numb) + "th";
endif
.
#20:29
numb = args[1];
ones = {"", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};
return ones[numb + 1];
.
#20:30
numb = args[1];
teens = {"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"};
others = {"twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"};
if (numb < 10)
return this:english_ones(numb);
elseif (numb < 20)
return teens[numb - 9];
else
return (others[(numb / 10) - 1] + ((numb % 10) ? "-" | "")) + this:english_ones(numb % 10);
endif
.
#20: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\"";
{ostr, subs, ?case = 0} = args;
if (typeof(ostr) != STR)
return ostr;
endif
len = length(ostr);
" - - - find the first instance of each substitution - -";
indices = {};
substs = {};
for s in (subs)
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;
.
#20:32
"subst(string,{{redex1,repl1},{redex2,repl2},{redex3,repl3}...}[,case])";
"Just like :substitute() but it uses index_delimited() instead of index()";
{ostr, subs, ?case = 0} = args;
if (typeof(ostr) != STR)
return ostr;
endif
len = length(ostr);
" - - - find the first instance of each substitution - -";
indices = {};
substs = {};
for s in (subs)
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;
.
#20: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:title().";
"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.";
"If args[1] is a list, calls itself on each element of the list and returns $string_utils:english_list(those results).";
{what, prop, ?ucase = 0} = args;
set_task_perms(caller_perms());
if (typeof(what) == LIST)
result = {};
for who in (what)
result = {@result, this:_cap_property(who, prop, ucase)};
endfor
return $string_utils:english_list(result);
endif
ucase = (prop && (strcmp(prop, "a") < 0)) || ucase;
if (!prop)
return valid(what) ? ucase ? what:titlec() | what:title() | ((ucase ? "N" | "n") + "othing");
elseif ((!ucase) || (typeof(s = `what.(prop + "c") ! ANY') == ERR))
if (prop == "name")
s = valid(what) ? what.name | "nothing";
ucase = ucase && (!is_player(what));
else
s = `$object_utils:has_property(what, prop) ? what.(prop) | $player.(prop) ! ANY';
endif
if (ucase && (s && ((typeof(s) == STR) && (((z = index(this.alphabet, s[1], 1)) < 27) && (z > 0)))))
s[1] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[z];
endif
endif
return (typeof(s) == ERR) ? s | tostr(s);
.
#20:34
"Pronoun (and other things) substitution. See 'help pronouns' for details.";
"syntax:  $string_utils:pronoun_sub(text[,who[,thing[,location[,dobj[,iobj]]]]])";
"%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.";
{string, ?who = player, ?thing = caller, ?where = $nothing, ?dobject = dobj, ?iobject = iobj} = args;
where = valid(where) ? where | (valid(who) ? who.location | where);
set_task_perms($no_one);
if (typeof(string) == LIST)
plines = {};
for line in (string)
plines = {@plines, this:(verb)(line, who, thing, where)};
endfor
return plines;
endif
old = tostr(string);
new = "";
objspec = "nditl";
objects = {who, dobject, iobject, 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..$], ">")))
"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..$];
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 = {};
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};
elseif (w = index(prnspec, s, 1))
cp_args = {who, prprops[w]};
elseif (s == "#")
s = tostr(who);
elseif (s != "%")
s = "%" + s;
endif
new = (new + old[1..prcnt - 1]) + ((!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;
.
#20: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).";
who = (length(args) > 2) ? args[2] | player;
default = args[$];
result = this:pronoun_sub(@args[1..$ - 1]);
return this:index_delimited(result, who.name) ? result | this:pronoun_sub(@{default, @args[2..$ - 1]});
.
#20: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
.
#20:37
"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, this, <who>.location and %";
"%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.";
set_task_perms($no_one);
{string, ?who = player, ?thing = caller, ?where = $nothing} = args;
where = valid(who) ? who.location | where;
if (typeof(string) == LIST)
plines = {};
for line in (string)
plines = {@plines, this:(verb)(line, who, thing, where)};
endfor
return plines;
endif
old = tostr(string);
new = "";
objspec = "nditl";
objects = {who, dobj, 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..$], ">")))
"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..$];
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 = {};
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};
elseif (w = index(prnspec, s, 1))
cp_args = {who, prprops[w]};
elseif (s == "#")
s = tostr(who);
elseif (s != "%")
s = "%" + s;
endif
new = (new + old[1..prcnt - 1]) + ((!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;
.
#20:38
"$string_utils:explode(subject [, break])";
"Return a list of those substrings of subject separated by runs of break[1].";
"break defaults to space.";
{subject, ?breakit = {" "}} = args;
breakit = breakit[1];
subject = subject + breakit;
parts = {};
while (subject)
if ((i = index(subject, breakit)) > 1)
parts = {@parts, subject[1..i - 1]};
endif
subject = subject[i + 1..$];
endwhile
return parts;
.
#20:39
"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...";
if (0)
rest[1..match(rest, "^ *")[2]] = "";
endif
rest = $string_utils:triml(rest);
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...";
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;
.
#20:40
"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;
.
#20:41
":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]) == INT)
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
.
#20:42
":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]) != INT)
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
.
#20:43
"_tolist(string) --- auxiliary for :to_value()";
rest = this:triml(args[1]);
vlist = {};
if (!rest)
return {0, {}};
elseif (rest[1] == "}")
return {rest[2..$], {}};
endif
while (1)
rlen = length(rest);
if (w = index("{\"", rest[1]))
result = this:({"_tolist", "_unquote"}[w])(rest[2..rlen]);
if (typeof(result[1]) == INT)
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..$], vlist};
elseif (rest[1] == ",")
rest = this:triml(rest[2..$]);
else
return {length(rest), ", or } expected"};
endif
endwhile
.
#20:44
"_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..$], result + rest[1..pos - 1]};
endif
result = (result + rest[1..pos - 1]) + rest[pos + 1..m[2]];
rest = rest[m[2] + 1..$];
endwhile
return {0, result + rest};
.
#20:45
":_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) | toint(thing);
elseif (match(thing, "^[-+]?%([0-9]+%.[0-9]*%|[0-9]*%.[0-9]+%)%(e[-+]?[0-9]+%)? *$"))
"matches 2. .2 3.2 3.2e3 .2e-3 3.e3";
return `tofloat(thing) ! E_INVARG => tostr("Bad floating point value: ", thing)';
elseif (match(thing, "^[-+]?[0-9]+e[-+]?[0-9]+ *$"))
"matches 345e4. No decimal, but has an e so still a float";
return `tofloat(thing) ! E_INVARG => tostr("Bad floating point value: ", thing)';
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
.
#20:46
":parse_command(cmd_line[,player])";
" => {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 `player' 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'.";
{c, ?who = player} = args;
y = $string_utils:words(c);
if (y == {})
return {};
endif
vrb = y[1];
y = y[2..$];
as = (y == {}) ? "" | c[length(vrb) + 2..$];
n = 1;
while ((!(gp = $code_utils:get_prep(@y[n..$]))[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}};
.
#20:47
"$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..$];
endwhile
return (result + value) + "\"";
elseif (typeof(value) == ERR)
return $code_utils:error_name(value);
else
return tostr(value);
endif
else
return tostr(value);
endif
.
#20:48
"$string_utils:print(value)";
"Print the given value into a string. == from_value(value,1,-1)";
return toliteral(args[1]);
value = args[1];
if (typeof(value) == LIST)
if (value)
result = "{" + this:print(value[1]);
for val in (listdelete(value, 1))
result = tostr(result, ", ", this:print(val));
endfor
return result + "}";
else
return "{}";
endif
elseif (typeof(value) == STR)
return tostr("\"", strsub(strsub(value, "\\", "\\\\"), "\"", "\\\""), "\"");
elseif (typeof(value) == ERR)
return $code_utils:error_name(value);
else
return tostr(value);
endif
.
#20:49
":reverse(string) => \"gnirts\"";
"An example: :reverse(\"This is a test.\") => \".tset a si sihT\"";
string = args[1];
if ((len = length(string)) > 50)
return this:reverse(string[($ / 2) + 1..$]) + this:reverse(string[1..$ / 2]);
endif
index = len;
result = "";
while (index > 0)
result = result + string[index];
index = index - 1;
endwhile
return result;
.
#20: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..$ / 2]), @this:char_list(string[($ / 2) + 1..$])};
else
l = {};
for c in [1..len]
l = {@l, string[c]};
endfor
return l;
endif
.
#20: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;
.
#20: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];
return (m = `match(args[1], "^.* %(from%|to%) %([^, ]+%)") ! ANY') ? substitute("%2", m) | "";
.
#20: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);
.
#20:54
"$string_utils:from_value(value [, quote_strings = 0 [, list_depth = 1]])";
"Print the given value into a string.";
"This verb suspends as necessary for large values.";
set_task_perms(caller_perms());
{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..$];
endwhile
return (result + value) + "\"";
elseif (typeof(value) == ERR)
return $code_utils:error_name(value);
else
return tostr(value);
endif
else
return tostr(value);
endif
.
#20:55
":end_expression(string[,stop_at])";
"  assumes string starts with an expression; returns the index of the last char in expression or 0 if string appears not to be an expression.  Expression ends at any character from stop_at which occurs at top level.";
{string, ?stop_at = " "} = args;
gone = 0;
paren_stack = "";
inquote = 0;
search = top_level_search = ("[][{}()\"" + strsub(stop_at, "]", "")) + "]";
paren_search = "[][{}()\"]";
while (m = match(string, search))
char = string[m[1]];
string[1..m[2]] = "";
gone = gone + m[2];
if (char == "\"")
"...skip over quoted string...";
char = "\\";
while (char == "\\")
if (!(m = match(string, "%(\\.?%|\"%)")))
return 0;
endif
char = string[m[1]];
string[1..m[2]] = "";
gone = gone + m[2];
endwhile
elseif (index("([{", char))
"... push parenthesis...";
paren_stack[1..0] = char;
search = paren_search;
elseif (i = index(")]}", char))
if (paren_stack && ("([{"[i] == paren_stack[1]))
"... pop parenthesis...";
paren_stack[1..1] = "";
search = paren_stack ? paren_search | top_level_search;
else
"...parenthesis mismatch...";
return 0;
endif
else
"... stop character ...";
return gone - 1;
endif
endwhile
return (!paren_stack) && (gone + length(string));
.
#20:56
":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, ""};
.
#20:57
":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 (first[l..h] == second[l..h])
l = h + 1;
else
r = h - 1;
endif
endwhile
return r;
.
#20:58
"wr_utils:title_list/title_listc(<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.";
"title_listc uses :titlec() for the first item.";
titles = $list_utils:map_verb(args[1], "title");
if (verb[length(verb)] == "c")
if (titles)
titles[1] = args[1][1]:titlec();
elseif (length(args) > 1)
args[2] = $string_utils:capitalize(args[2]);
else
args = listappend(args, "Nothing");
endif
endif
return $string_utils:english_list(titles, @args[2..$]);
.
#20:59
"name_and_number(object [,sepr]) => \"ObjectName (#object)\"";
"Return name and number for OBJECT.  Second argument is optional separator (for those who want no space, use \"\").  If OBJECT is a list of objects, this maps the above function over the list and then passes it to $string_utils:english_list.";
{objs, ?sepr = " "} = args;
if (typeof(objs) != LIST)
objs = {objs};
endif
name_list = {};
for what in (objs)
name = valid(what) ? what.name | {"<invalid>", "$nothing", "$ambiguous_match", "$failed_match"}[1 + (what in {#-1, #-2, #-3})];
name = tostr(name, sepr, "(", what, ")");
name_list = {@name_list, name};
endfor
return $string_utils:english_list(name_list);
.
#20:60
"columnize_suspended (interval, items, n [, width]) - Turn a one-column list of items into an n-column list, suspending for `interval' seconds as necessary. '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_suspended(0, {1, 2, 3, 4, 5, 6, 7}, 3));'.";
{interval, 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]
$command_utils:suspend_if_needed(interval);
line = tostr(this:left(line, colwidths[col]), " ", items[row + (col * height)]);
endfor
result = listappend(result, line[1..min($, width)]);
endfor
return result;
.
#20:61
":a_or_an(<noun>) => \"a\" or \"an\"";
"To accomodate personal variation (e.g., \"an historical book\"), a player can override this by having a personal a_or_an verb.  If that verb returns 0 instead of a string, the standard algorithm is used.";
noun = args[1];
if ($object_utils:has_verb(player, "a_or_an") && ((custom_result = player:a_or_an(noun)) != 0))
return custom_result;
endif
if (noun in this.use_article_a)
return "a";
endif
if (noun in this.use_article_an)
return "an";
endif
a_or_an = "a";
if (noun != "")
if (index("aeiou", noun[1]))
a_or_an = "an";
"unicycle, unimplemented, union, united, unimpressed, unique";
if ((((noun[1] == "u") && (length(noun) > 2)) && (noun[2] == "n")) && ((index("aeiou", noun[3]) == 0) || (((noun[3] == "i") && (length(noun) > 3)) && (index("aeioubcghqwyz", noun[4]) || ((length(noun) > 4) && index("eiy", noun[5]))))))
a_or_an = "a";
endif
endif
endif
return a_or_an;
"Ported by Mickey with minor tweaks from a Moo far far away.";
"Last modified Sun Aug  1 22:53:07 1993 EDT by BabyBriar (#2).";
.
#20:62
"index_all(string,target) -- returns list of positions of target in string.";
"Usage: $string_utils:index_all(<string,pattern>)";
"       $string_utils:index_all(\"aaabacadae\",\"a\")";
{line, pattern} = args;
if ((typeof(line) != STR) || (typeof(pattern) != STR))
return E_TYPE;
else
where = {};
place = -1;
next = 0;
while ((place = index(line[next + 1..$], pattern)) != 0)
where = {@where, place + next};
next = ((place + next) + length(pattern)) - 1;
endwhile
return where;
endif
.
#20:63
"Copied from Puff (#1449):match_stringlist Tue Oct 19 08:18:13 1993 PDT";
"$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 == "") || (length(stringlist) < 1))
return $nothing;
endif
matches = {};
"First check for exact matches.";
for i in [1..length(stringlist)]
if (subject == stringlist[i])
matches = {@matches, i};
endif
endfor
"Now return a match, or $ambiguous, or check for partial matches.";
if (length(matches) == 1)
return matches[1];
elseif (length(matches) > 1)
return $ambiguous_match;
elseif (length(matches) == 0)
"Checking for partial matches is almost identical to checking for exact matches, but we use index(list[i], target) instead of list[i] == target to see if they match.";
for i in [1..length(stringlist)]
if (index(stringlist[i], subject) == 1)
matches = {@matches, i};
endif
endfor
if (length(matches) == 1)
return matches[1];
elseif (length(matches) > 1)
return $ambiguous_match;
elseif (length(matches) == 0)
return $failed_match;
endif
endif
.
#20:64
"This converts a ASCII character code in the range [32..126] into the ASCII character with that code, represented as a one-character string.";
"";
"Example:   $string_utils:from_ASCII(65) => \"A\"";
code = args[1];
return this.ascii[code - 31];
.
#20:65
"Convert a one-character string into the ASCII character code for that character.";
"";
"Example:  $string_utils:to_ASCII(\"A\") => 65";
return (index(this.ascii, args[1], 1) || raise(E_INVARG)) + 31;
.
#20:66
"Copied from Mickey (#52413):abbreviated_value Fri Sep  9 08:52:41 1994 PDT";
":abbreviated_value(value,max_reslen,max_lstlev,max_lstlen,max_strlen,max_toklen)";
"";
"Gets the printed representation of value, subject to these parameters:";
" max_reslen = Maximum desired result string length.";
" max_lstlev = Maximum list level to show.";
" max_lstlen = Maximum list length to show.";
" max_lstlen = Maximum string length to show.";
" max_lstlen = Maximum token length (e.g., numbers and errors) to show.";
"";
"A best attempt is made to get the exact target size, but in some cases the result is not exact.";
{value, ?max_reslen = $maxint, ?max_lstlev = $maxint, ?max_lstlen = $maxint, ?max_strlen = $maxint, ?max_toklen = $maxint} = args;
return this:_abbreviated_value(value, max_reslen, max_lstlev, max_lstlen, max_strlen, max_toklen);
"Originally written by Mickey.";
.
#20:67
"Copied from Mickey (#52413):_abbreviated_value Fri Sep  9 08:52:44 1994 PDT";
"Internal to :abbreviated_value.  Do not call this directly.";
{value, max_reslen, max_lstlev, max_lstlen, max_strlen, max_toklen} = args;
if ((type = typeof(value)) == LIST)
if (!value)
return "{}";
elseif (max_lstlev == 0)
return "{...}";
else
n = length(value);
result = "{";
r = max_reslen - 2;
i = 1;
eltstr = "";
while (((i <= n) && (i <= max_lstlen)) && (r > (x = (i == 1) ? 0 | 2)))
eltlen = length(eltstr = this:(verb)(value[i], r, max_lstlev - 1, max_lstlen, max_strlen, max_toklen));
lastpos = 1;
if (r >= (eltlen + x))
comma = (i == 1) ? "" | ", ";
result = tostr(result, comma);
if (r > 4)
lastpos = length(result);
endif
result = tostr(result, eltstr);
r = (r - eltlen) - x;
elseif (i == 1)
return "{...}";
elseif (r > 4)
return tostr(result, ", ...}");
else
return tostr(result[1..lastpos], "...}");
endif
i = i + 1;
endwhile
if (i <= n)
if (i == 1)
return "{...}";
elseif (r > 4)
return tostr(result, ", ...}");
else
return tostr(result[1..lastpos], "...}");
endif
else
return tostr(result, "}");
endif
endif
elseif (type == STR)
result = "\"";
while ((q = index(value, "\"")) ? q = min(q, index(value, "\\")) | (q = index(value, "\\")))
result = ((result + value[1..q - 1]) + "\\") + value[q];
value = value[q + 1..$];
endwhile
result = result + value;
if ((length(result) + 1) > (z = max(min(max_reslen, max(max_strlen, max_strlen + 2)), 6)))
z = z - 5;
k = 0;
while ((k < z) && (result[z - k] == "\\"))
k = k + 1;
endwhile
return tostr(result[1..z - (k % 2)], "\"+...");
else
return tostr(result, "\"");
endif
else
v = (type == ERR) ? $code_utils:error_name(value) | tostr(value);
len = max(4, min(max_reslen, max_toklen));
return (length(v) > len) ? v[1..len - 3] + "..." | v;
endif
"Originally written by Mickey.";
.
#20:68
"$string_utils:match_suspended(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.";
"This verb will suspend as needed, and should be used if obj-list is very large.";
subject = args[1];
if (subject == "")
return $nothing;
endif
no_exact_match = no_partial_match = 1;
for i in [1..length(args) / 2]
prop_name = args[(2 * i) + 1];
for object in ((typeof(olist = args[2 * i]) == LIST) ? olist | {olist})
if (valid(object))
if (typeof(str_list = `object.(prop_name) ! E_PERM, E_PROPNF => {}') != 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
$command_utils:suspend_if_needed(5);
endfor
endfor
return no_exact_match && (no_partial_match && $failed_match);
.
#20:69
"args[1] is a string.  'increments' the string by one. E.g., aaa => aab, aaz => aba.  empty string => a, zzz => aaaa.";
s = args[1];
index = length(s);
if (!s)
return "a";
elseif (s[$] == "z")
return this:incr_alpha(s[1..index - 1]) + "a";
else
t = index(this.alphabet, s[index]);
return s[1..index - 1] + this.alphabet[t + 1];
endif
.
#20:70
"Usage:  is_float(string)";
"Is string composed of one or more digits possibly preceded by a minus sign either followed by a decimal or by an exponent?";
"Return true or false";
return match(args[1], "^ *[-+]?%(%([0-9]+%.[0-9]*%|[0-9]*%.[0-9]+%)%(e[-+]?[0-9]+%)?%)%|%([0-9]+e[-+]?[0-9]+%) *$");
.
#21:0
"make_exit(spec, source, dest[, use-$recycler-pool [, kind]])";
"";
"Uses $recycler by default; supplying fourth arg as 0 suppresses this.";
"Optional 5th arg gives a parent for the object to be created";
"(i.e., distinct from $exit)";
"Returns the object number as a list if successful, 0 if not.";
set_task_perms(caller_perms());
{spec, source, dest, ?use_recycler, ?exit_kind = $exit} = args;
exit = player:_create(exit_kind);
if (typeof(exit) == ERR)
player:notify(tostr("Cannot create new exit as a child of ", $string_utils:nn(exit_kind), ": ", exit, ".  See `help @build-options' for information on how to specify the kind of exit this command tries to create."));
return;
endif
for f in ($string_utils:char_list(player:build_option("create_flags") || ""))
exit.(f) = 1;
endfor
$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};
else
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
.
#21: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;
return object:set_name(name) && object:set_aliases(names[2]);
.
#21:2
":recreate(object,newparent) -- effectively recycle and recreate the specified object as a child of parent.  Returns true if successful.";
{object, parent} = args;
who = caller_perms();
if (!(valid(object) && valid(parent)))
return E_INVARG;
elseif (who.wizard)
"no problemo";
elseif ((who != object.owner) || ((who != parent.owner) && (!parent.f)))
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.";
"Because this is done before set_task_perms() -- thus with wizard perms -- we save ticks and use chparent() instead of #0:chparent().  This will save many more ticks, if this is an object with many children.";
grandpa = parent(object);
for c in (children(object))
chparent(c, grandpa);
endfor
for item in (object.contents)
if (!is_player(item))
move(item, #-1);
else
move(item, $player_start);
endif
endfor
set_task_perms(who);
if ($object_utils:has_callable_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
chparent(object, parent);
object.name = "";
object.r = 0;
object.f = 0;
object.w = 0;
if ($object_utils:has_callable_verb(parent, "initialize"))
object:initialize();
endif
return 1;
.
#21: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[1];
else
aliases = $string_utils:explode(spec[colon + 1..$], ",");
name = spec[1..colon - 1];
endif
return {name, $list_utils:map_arg($string_utils, "trim", aliases)};
.
#21:4
if (is_player(what = args[1]))
return "P";
endif
while (valid(what))
if (i = what in this.classes)
return this.class_string[i];
endif
what = parent(what);
endwhile
return " ";
.
#21:5
":object_audit_string(object [,prospectus-style])";
{o, ?prospectus = 0} = args;
olen = length(tostr(max_object()));
if (!$recycler:valid(o))
return tostr(prospectus ? "          " | "", $quota_utils.byte_based ? "    " | "", $string_utils:right(o, olen), " Invalid Object!");
endif
if (prospectus)
kids = 0;
for k in (children(o))
$command_utils:suspend_if_needed(0);
if (k.owner != o.owner)
kids = 2;
break k;
elseif (kids == 0)
kids = 1;
endif
endfor
"The verbs() call below might fail, but that's OK";
"Well, actually it won't cuz we seem to be a wizard.  Since you can get the number of verbs information from @verbs anyway, it seems kind of pointless to hide it here.";
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 ($quota_utils.byte_based)
vstr = tostr(this:size_string(o.object_size[1]), " ", vstr);
name_field_len = 26;
else
name_field_len = 30;
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 = min(length(source), 19);
destlen = min(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
namelen = min(length(o.name), name_field_len - 1);
return tostr(vstr, $string_utils:right(o, olen), " ", $string_utils:left(o.name[1..namelen], name_field_len), loc);
.
#21:6
":do_audit(who, start, end, match)";
"audit who, with objects from start to end that match 'match'";
":do_prospectus(...)";
"same, but with verb counts";
{who, start, end, match} = args;
pros = verb == "do_prospectus";
"the set_task_perms is to make the task owned by the player. There are no other security aspects";
set_task_perms(caller_perms());
if ((((((start == 0) && (end == toint(max_object()))) && (!match)) && (typeof(who.owned_objects) == LIST)) && (length(who.owned_objects) > 100)) && (!$command_utils:yes_or_no(tostr(who.name, " has ", length(who.owned_objects), " objects.  This will be a very long list.  Do you wish to proceed?"))))
v = pros ? "@prospectus" | "@audit";
return player:tell(v, " aborted.  Usage:  ", v, " [player] [from <start>] [to <end>] [for <match>]");
endif
player:tell(tostr("Objects owned by ", who.name, " (from #", start, " to #", end, match ? " matching " + match | "", ")", ":"));
count = bytes = 0;
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 ((toint(o) >= start) && (toint(o) <= end))
didit = this:do_audit_item(o, match, pros);
count = count + didit;
if ((didit && $quota_utils.byte_based) && $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 ($recycler:valid(o) && (o.owner == who))
didit = this:do_audit_item(o, match, pros);
count = count + didit;
if ((didit && $quota_utils.byte_based) && $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.byte_based ? tostr("  Total bytes: ", $string_utils:group_number(bytes), ".") | ""), player:linelen() - 1, "-"));
.
#21:7
":do_audit_item(object, match-name-string, prospectus-flag)";
{o, match, pros} = args;
found = match ? 0 | 1;
names = `{o.name, @o.aliases} ! ANY => {o.name}';
"Above to get rid of screwed up 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.";
line = $building_utils:object_audit_string(o, pros);
player:tell(line[1..min($, player:linelen())]);
return 1;
endif
return 0;
.
#21:8
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
.
#21:9
if (caller_perms().wizard)
pass();
this.classes = $object_utils:fertile_objects_suspended();
endif
"Last modified Sun Aug 17 11:13:43 1997 CDT by Wizard (#2).";
.
#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;
.
#24:0
":set_programmer(victim[,mail from])  => 1 or error.";
"Sets victim.programmer, chparents victim to $prog if necessary, and sends mail to $new_prog_log, mail is from optional second arg or caller_perms().";
whodunnit = caller_perms();
{victim, ?mailfrom = whodunnit} = args;
if (!whodunnit.wizard)
return E_PERM;
elseif (!(valid(victim) && (is_player(victim) && $object_utils:isa(victim, $player))))
return E_INVARG;
elseif (victim.programmer)
return E_NONE;
elseif (this:check_prog_restricted(victim))
return E_INVARG;
elseif (typeof(e = `victim.programmer = 1 ! ANY') == ERR)
return e;
else
$quota_utils:adjust_quota_for_programmer(victim);
if (!$object_utils:isa(victim, $prog))
if (typeof(e = `chparent(victim, $prog) ! ANY') == ERR)
"...this isn't really supposed to happen but it could...";
player:notify(tostr("chparent(", victim, ",", $prog, ") failed:  ", e));
player:notify("Check for common properties.");
endif
else
player:notify(tostr(victim.name, " was already a child of ", parent(victim).name, " (", parent(victim), ")"));
endif
if (!$mail_agent:send_message(mailfrom, {$new_prog_log, victim}, tostr("@programmer ", victim.name, " (", victim, ")"), tostr("I just gave ", victim.name, " a programmer bit."))[1])
$mail_agent:send_message(mailfrom, {$new_prog_log}, tostr("@programmer ", victim.name, " (", victim, ")"), tostr("I just gave ", victim.name, " a programmer bit."));
endif
return 1;
endif
.
#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";
{victim, ?nochown = 0} = args;
if (!caller_perms().wizard)
return E_PERM;
elseif (!(valid(victim) && $object_utils:isa(victim, $player)))
return E_INVARG;
elseif (is_player(victim))
return E_NONE;
elseif ($player_db.frozen)
return E_NACC;
elseif (!$player_db:available(name = victim.name))
return E_RECMOVE;
else
set_player_flag(victim, 1);
if (0 && $object_utils:isa(victim, $prog))
victim.programmer = 1;
else
victim.programmer = $player.programmer;
endif
if (!nochown)
$wiz_utils:set_owner(victim, victim);
endif
$player_db:insert(name, victim);
for a in (setremove(aliases = victim.aliases, name))
if (index(a, " "))
"..ignore ..";
elseif ($player_db:available(a) in {this, 1})
$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).";
{object, newowner, ?suspendok = 0} = args;
if (!valid(object))
return E_INVIND;
elseif (!caller_perms().wizard)
return E_PERM;
elseif (!(valid(newowner) && is_player(newowner)))
return E_INVARG;
endif
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...).";
{object, pname, newowner, ?suspendok = 0} = args;
if (!caller_perms().wizard)
return E_PERM;
elseif (!(info = `property_info(object, pname) ! ANY'))
"... handles E_PROPNF and invalid object errors...";
return info;
elseif (!is_player(newowner))
return E_INVARG;
elseif (index(info[2], "c"))
if (suspendok / 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 % 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.";
{victim, ?newowner = 0} = args;
if (!caller_perms().wizard)
return E_PERM;
elseif (!valid(victim))
return E_INVARG;
elseif (!is_player(victim))
return E_NONE;
endif
if (typeof(newowner) == OBJ)
$wiz_utils:set_owner(victim, newowner);
endif
victim.programmer = 0;
victim.wizard = 0;
set_player_flag(victim, 0);
if ($object_utils:has_property($local, "second_char_registry"))
$local.second_char_registry:delete_player(victim);
"We're providing this variable for faster access than computing players() all the time.";
$local.players = setremove($local.players, victim);
endif
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);
"I don't *think* this is bad---we've already toaded the guy.  And folks with lots of aliases screw us. --Nosredna";
$command_utils:suspend_if_needed(0);
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) ! ANY'))
"... 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) ! ANY') && (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].  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.";
"Capital I and lowercase L are excluded on the basis of looking like each other.";
vowels = "aeiouyAEUY";
consonants = "bdghjmnpqrstvwxzBDGHJLMNPQRSTVWXZ";
len = toint(args[1]);
if (len)
alt = random(2) - 1;
s = "";
for i in [1..len]
newchar = alt ? vowels[random($)] | consonants[random($)];
s = s + newchar;
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]) ! ANY') == 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:10
"Return 1 if args[1] is a newted player.";
if (!caller_perms().wizard)
return E_PERM;
else
return args[1] in $login.newted;
endif
.
#24:11
if (!caller_perms().wizard)
return E_PERM;
else
set_task_perms(caller_perms());
player:tell("Beginning initialize_owned:  ", ctime());
for o in [#0..max_object()]
if (valid(o))
if ($object_utils:isa(owner = o.owner, $player) && (typeof(owner.owned_objects) == LIST))
owner.owned_objects = setadd(owner.owned_objects, o);
endif
endif
$command_utils:suspend_if_needed(0);
endfor
player:tell("Done adding, beginning verification pass.");
this:verify_owned_objects();
player:tell("Finished:  ", ctime());
endif
.
#24:12
if (!caller_perms().wizard)
return E_PERM;
else
for p in (players())
if (typeof(p.owned_objects) == LIST)
for o in (p.owned_objects)
if (((typeof(o) != OBJ) || (!valid(o))) || (o.owner != p))
p.owned_objects = setremove(p.owned_objects, o);
player:tell("Removed ", $string_utils:nn(o), " from ", $string_utils:nn(p), "'s .owned_objects list.");
if (((typeof(o) == OBJ) && valid(o)) && (typeof(o.owner.owned_objects) == LIST))
o.owner.owned_objects = setadd(o.owner.owned_objects, o);
endif
endif
$command_utils:suspend_if_needed(0, p);
endfor
endif
endfor
endif
.
#24:13
":connected_wizards() => list of currently connected wizards and players mentioned in .public_identity properties as being wizard counterparts.";
wizzes = $object_utils:leaves($wiz);
wlist = {};
everyone = verb == "connected_wizards_unadvertised";
for w in (wizzes)
if (w.wizard && (w.advertised || everyone))
if (`connected_seconds(w) ! ANY => 0')
wlist = setadd(wlist, w);
endif
if (`connected_seconds(w.public_identity) ! ANY => 0')
wlist = setadd(wlist, w.public_identity);
endif
endif
endfor
return wlist;
.
#24:14
":all_wizards_advertised() => list of all wizards who have set .advertised true and players mentioned their .public_identity properties as being wizard counterparts";
wizzes = $object_utils:leaves($wiz);
wlist = {};
everyone = verb == "all_wizards_unadvertised";
for w in (wizzes)
if (w.wizard && (w.advertised || everyone))
if (is_player(w))
wlist = setadd(wlist, w);
endif
if (`is_player(w.public_identity) ! ANY')
wlist = setadd(wlist, w.public_identity);
endif
endif
endfor
return wlist;
.
#24:15
":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";
if (caller_perms().wizard)
found = 0;
{object, oldname, newname} = args;
while (info = `verb_info(object, oldname) ! ANY')
`set_verb_info(object, oldname, listset(info, newname, 3)) ! ANY';
found = 1;
endwhile
return found;
else
return E_PERM;
endif
.
#24:16
if (this.record_missed_help && (callers()[1][4] == $player))
miss = args[1];
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 = args[2] ? 2 | 1;
this.missed_help_counters[index][which] = this.missed_help_counters[index][which] + 1;
endif
.
#24:17
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 = {};
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);
player:tell(" miss ambig word");
for x in [1..len]
$command_utils:suspend_if_needed(0);
player:tell(sorted[(len - x) + 1]);
endfor
player:tell(" - - - - - - - - -");
.
#24:18
if (caller_perms().wizard)
pass();
`delete_property(this, "guest_feature_restricted") ! ANY';
this.boot_exceptions = {};
this.programmer_restricted = {};
this.programmer_restricted_temp = {};
this.record_missed_help = 0;
this.missed_help_counters = this.missed_help_strings = {};
this.suicide_string = "You don't *really* want to commit suicide, do you?";
this.wizards = {#2};
this.next_perm_index = 1;
this.system_chars = {$hacker, $no_one, $housekeeper};
this.expiration_progress = $nothing;
endif
.
#24:19
":show_netwho_listing(tell,player_list)";
" prints a listing of the indicated players showing connect sites.";
{who, unsorted} = args;
if (!caller_perms().wizard)
return E_PERM;
endif
if (!unsorted)
return;
endif
su = $string_utils;
alist = {};
footnotes = {};
nwidth = length("Name");
for u in (unsorted)
$command_utils:suspend_if_needed(0);
if (u.programmer)
pref = "% ";
footnotes = setadd(footnotes, "prog");
else
pref = "  ";
endif
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
name = u.name;
if (length(name) > 15)
name = name[1..13] + "..";
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 = {"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
who:notify(tell1);
who:notify(tell2);
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
who:notify(tell1[1..min($, 79)]);
endfor
if (footnotes)
who:notify("");
if ("prog" in footnotes)
who:notify(" %  == programmer.");
endif
if ("black" in footnotes)
who:notify("(*) == blacklisted site.");
endif
if ("gray" in footnotes)
who:notify("(+) == graylisted site.");
endif
endif
"Last modified Wed Jul 23 05:41:22 1997 CDT by Wizard (#2).";
.
#24:20
":show_netwho_from_listing(tell,site)";
"@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 (!caller_perms().wizard)
return E_PERM;
endif
{tellwho, where} = args;
su = $string_utils;
if (!index(where, "*"))
"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)
"Temporary kluge until $site_db is repaired. --Nosredna";
for b in ($site_db:find_exact(domain) || {})
$command_utils:suspend_if_needed(0, "..netwho..");
if (typeof(b) == STR)
sites = setadd(sites, (b + ".") + domain);
else
bozos = setadd(bozos, b);
nl = max(length(tostr(b, (valid(b) && is_player(b)) ? b.name | "*** recreated ***")), nl);
endif
endfor
endfor
endwhile
if (bozos)
tellwho:notify(tostr(su:left("  Player", nl + 7), "From"));
tellwho:notify(tostr(su:left("  ------", nl + 7), "----"));
for who in (bozos)
st = su:left(tostr((valid(who) && is_player(who)) ? (who.programmer ? "% " | "  ") + who.name | "", " (", who, ")"), nl + 7);
comma = 0;
if ($object_utils:isa(who, $player) && is_player(who))
for p in ({who.email_address || "*Unregistered*", @who.all_connect_places})
if (comma && (length(p) >= (78 - length(st))))
tellwho: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
tellwho:notify(st);
endfor
tellwho:notify("");
tellwho:notify(tostr(length(bozos), " player", (length(bozos) == 1) ? "" | "s", " found."));
else
tellwho: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;
tellwho:notify(tostr(who.name, " (", who, "): ", su:english_list(matches)));
endif
endfor
tellwho:notify(tostr(howmany || "No", " matches found."));
endif
.
#24:21
":check_player_request(name [,email [,connection]])";
" check if the request for player and email address is valid;";
" return empty string if it valid, or else a string saying why not.";
" The result starts with - if this is a 'send email, don't try again' situation.";
":check_reregistration(who, email, connection)";
"  Since name is ignored, only check the 'email' parts and use the first arg";
"  for the re-registering player.";
if (!caller_perms().wizard)
return E_PERM;
"accesses registration information -- wiz only";
endif
name = args[1];
if (verb == "check_reregistration")
"don't check player name";
elseif (!name)
return "A blank name isn't allowed.";
elseif (name == "<>")
return "Names with angle brackets aren't allowed.";
elseif (index(name, " "))
return "Names with spaces are not allowed. Use dashes or underscores.";
elseif (match(name, "^<.*>$"))
return tostr("Try using ", name[2..$ - 1], " instead of ", name, ".");
elseif ($player_db.frozen)
return "New players cannot be created at the moment, try again later.";
elseif (!$player_db:available(name))
return ("The name '" + name) + "' is not available.";
elseif ($login:_match_player(name) != $failed_match)
return ("The name '" + name) + "' doesn't seem to be available.";
endif
if (length(args) == 1)
"no email address supplied.";
return "";
endif
address = args[2];
addrargs = (verb == "check_reregistration") ? {name} | {};
if ($registration_db:suspicious_address(address, @addrargs))
return "-There has already been a character with that or a similar email address.";
endif
if (reason = $network:invalid_email_address(address))
return reason + ".";
endif
parsed = $network:parse_address(address);
if ($registration_db:suspicious_userid(parsed[1]))
return tostr("-Automatic registration from an account named ", parsed[1], " is not allowed.");
endif
connection = (length(args) > 2) ? args[3] | parsed[2];
if ((connection[max($ - 2, 1)..$] == ".uk") && (parsed[2][1..3] == "uk."))
return 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 "-The system cannot resolve the name of the system you're connected from.";
elseif ((a = $network:local_domain(connection)) != (b = $network:local_domain(parsed[2])))
return tostr("-The connection is from '", a, "' but the mail address is '", b, "'; these don't seem to be the same place.");
elseif ($login:spooflisted(parsed[2]))
return tostr("-Automatic registration is not allowed from ", parsed[2], ".");
endif
return "";
.
#24:22
"create a player named NAME with email address ADDRESS; return {object, password}.  Optional third arg is comment to be put in registration db.";
"assumes $wiz_utils:check_player_request() has been called and it passes.";
if (!caller_perms().wizard)
return E_PERM;
endif
{name, address, realname} = args;
new = $quota_utils:bi_create($player_class, $nothing);
new.name = name;
new.aliases = {name};
new.real_name = realname;
new.password = crypt(password = $wiz_utils:random_password(5));
new.last_password_time = time();
new.last_connect_time = $maxint;
"Last disconnect time is creation time, until they login.";
new.last_disconnect_time = time();
$quota_utils:initialize_quota(new);
if (!(error = $wiz_utils:set_player(new)))
return player:tell("An error, ", error, " occurred while trying to make ", new, " a player. The database is probably inconsistent.");
endif
new.email_address = address;
$registration_db:add(new, address, realname);
move(new, $player_start);
new.programmer = $player_class.programmer;
return {new, password};
.
#24:23
":send_new_player_mail(preface, name, address, character#, password)";
"  used by $wiz:@make-player and $guest:@request";
if (!caller_perms().wizard)
return E_PERM;
endif
if (!$network.active)
return player:tell("Sorry, the network is not active. Cannot send email.");
endif
{preface, name, address, new, password, ?Xpress_user = 0} = args;
msg = {preface};
msg = {@msg, ""};
msg = {@msg, tostr("Welcome to ", $network.moo_name, "!")};
msg = {@msg, ""};
msg = {@msg, tostr("Your user ID is   : ", name)};
msg = {@msg, tostr("Your password is  : ", password)};
msg = {@msg, ""};
msg = {@msg, tostr("To enter ", $network.MOO_name, " point your web browser to: http://", $network.site, ":", $network.webport)};
msg = {@msg, ""};
msg = {@msg, "Your browser must be either Netscape Communicator version 4.08 or newer, or Microsoft Internet Explorer version 4.0 or newer. Java, Javascript, and Cookies must be enabled for the system to work."};
msg = {@msg, ""};
msg = {@msg, tostr("To connect to ", $network.MOO_name, " with a standard telnet client, go to ", $network.site, " ", $network.port, " and type:")};
msg = {@msg, ""};
msg = {@msg, tostr("connect ", name, " ", password)};
msg = {@msg, ""};
msg = {@msg, "Please note that passwords are case sensitive, which means you have to type it exactly as it appears above, including capital and lowercase letters. If you are new to MOO, please take a look at the Beginner's Guide below before you connect."};
msg = {@msg, ""};
msg = {@msg, @$guide.text};
if (Xpress_user != 0)
return $xpress_MOO_mailer:sendmail(Xpress_user, new.email_address, (("Your " + $network.moo_name) + " character, ") + name, "Reply-to: " + $network.reply_address, @msg);
else
return $network:sendmail(address, (("Your " + $network.moo_name) + " character, ") + name, "Reply-to: " + $network.reply_address, @msg);
endif
"Last modified Sun May 16 17:35:46 1999 CDT by Wizard (#2).";
.
#24:24
"do_maker_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;
endif
{name, email, @comments} = args;
comments = $string_utils:from_list(comments, " ");
reason = $wiz_utils:check_player_request(name, email);
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>", (valid(x[1]) && (!is_player(x[1]))) ? " {nonplayer}" | "", " (", x[1], ") ", (length(x) > 1) ? ("[" + tostr(@x[2..$])) + "]" | ""));
endfor
if (!reason)
reason = "Already registered.";
endif
endif
if (reason)
player:notify(reason);
if (!$command_utils:yes_or_no("Create character anyway? "))
player:notify("Character not created.");
return;
endif
endif
new = $wiz_utils:make_player(name, email, comments);
player:notify(tostr(name, " (", new[1], ") created with password `", new[2], "' for ", email, comments ? (" [" + comments) + "]" | ""));
$mail_agent:send_message(player, $new_player_log, tostr(name, " (", new[1], ")"), tostr(email, comments ? " " + comments | ""));
if ($network.active)
if ($command_utils:yes_or_no(("Send email to " + email) + " with password? "))
player:notify(tostr("Sending the password to ", email, "."));
if ((result = $wiz_utils:send_new_player_mail(tostr("From ", player.name, "@", $network.moo_name, ":"), name, email, new[1], new[2])) == 0)
player:notify(tostr("Mail sent successfully to ", email, "."));
else
player:tell("Cannot send mail: ", result);
endif
else
player:notify("No mail sent.");
endif
else
player:notify("Sorry, the network isn't active.");
endif
.
#24:25
"do_register(name, email_address [,comments])";
"change player's email address.";
if (!caller_perms().wizard)
return E_PERM;
endif
whostr = args[1];
email = args[2];
comments = $string_utils:from_list(args[3..length(args)]);
whostr = args[1];
who = $string_utils:match_player(whostr);
if ($command_utils:player_match_failed(who, whostr))
return;
endif
if (((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;
endif
if (reason = $network:invalid_email_address(email))
player:notify(reason);
if (!$command_utils:yes_or_no("Register anyway?"))
return player:notify("re-registration aborted.");
endif
endif
if (comments)
$registration_db:add(who, email, comments);
else
$registration_db:add(who, email);
endif
old = who.email_address;
who.email_address = email;
who.real_name = comments;
player:notify(tostr(who.name, " (", who, ") formerly ", old ? old | "unregistered", ", registered at ", email, ".", comments ? (" [" + comments) + "]" | ""));
"Last modified Sun Aug 17 11:02:27 1997 CDT by Wizard (#2).";
.
#24:26
"do_new_password(who, [password])";
if (!caller_perms().wizard)
return E_PERM;
endif
{who, ?password = $wiz_utils:random_password(6)} = args;
if (!password)
password = $wiz_utils:random_password(6);
endif
whostr = $string_utils:nn(who);
player:notify(tostr("About to change password for ", whostr, ". Old encrypted password is \"", who.password, "\""));
who.password = crypt(password);
who.last_password_time = time();
$mail_agent:send_message(player, $new_password_log, tostr("@newpassword ", who.name, " (", who, ")"), {tostr("I just gave ", who.name, " a new password.")});
player:notify(tostr(whostr, " new password is `", password, "'."));
if (!who.email_address)
player:notify(tostr(whostr, " doesn't have a registered email_address, cannot mail password; tell them some some other way."));
elseif ((who.last_connect_time == $maxint) && $command_utils:yes_or_no(tostr(who.name, " has never logged in.  Send mail with the password as though this were a new player request?")))
if ((result = $wiz_utils:send_new_player_mail(tostr("From ", player.name, "@", $network.moo_name, ":"), who.name, who.email_address, who, password)) == 0)
player:tell("Mail sent.");
else
player:tell("Trouble sending mail: ", result);
endif
elseif ($command_utils:yes_or_no(tostr("Email new password to ", whostr, "?")))
player:notify("Sending the password via email.");
$network:adjust_postmaster_for_password("enter");
if ((result = $network:sendmail(who.email_address, ("Your " + $network.moo_name) + " password", ("The password for your " + $network.moo_name) + " character:", " " + whostr, "has been changed. The new password is:", " " + password, "", "Please note that passwords are case sensitive.")) == 0)
player:tell("Mail sent.");
else
player:tell("Trouble sending mail: ", result);
endif
$network:adjust_postmaster_for_password("exit");
else
player:tell("No mail sent.");
endif
.
#24:27
":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).";
{object, newowner, ?suspendok = 0} = args;
if (!valid(object))
return E_INVIND;
elseif (!caller_perms().wizard)
return E_PERM;
elseif (!(valid(newowner) && is_player(newowner)))
return E_INVARG;
endif
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:28
if (!caller_perms().wizard)
return E_PERM;
endif
"------- constants ---- ";
"20 minutes idle for regular players";
mintime = 60 * 20;
"10 minutes for guests";
minguest = 60 * 10;
"wait 3 minutes before actually booting";
bootdelay = 3;
"start booting when there are 20 less than max players";
threshold = 20;
" ----------------------";
if ($code_utils:task_valid(this.boot_task) && (task_id() != this.boot_task))
"starting a new one: kill the old one";
kill_task(this.boot_task);
this.boot_task = 0;
endif
fork taskn ((bootdelay * 60) * 3)
maxplayers = $login:max_connections() - threshold;
if (length(pl = connected_players()) > maxplayers)
pll = {};
plt = {};
for x in (pl)
suspend(0);
min = $object_utils:isa(x, $guest) ? minguest | mintime;
if ((((idle = `idle_seconds(x) ! ANY => 0') > min) && (!x.wizard)) && (!(x in this.boot_exceptions)))
pll = {x, @pll};
plt = {idle, @plt};
endif
endfor
if (pll)
"Sort by idle time, and choose person who has been idle longest.";
pll = $list_utils:sort(pll, plt);
booted = pll[$];
guest = $object_utils:isa(booted, $guest);
min = guest ? minguest | mintime;
if (`idle_seconds(booted) ! ANY => 0' > min)
notify(booted, tostr("*** You've been idle more than ", min / 60, " minutes, and there are more than ", maxplayers, " players connected. If you're still idle and LambdaMOO is still busy in ", bootdelay, " minute", (bootdelay == 1) ? "" | "s", ", you will be booted. ***"));
fork (60 * bootdelay)
idle = `idle_seconds(booted) ! ANY => 0';
if ((idle > min) && (length(connected_players()) > ($login:max_connections() - threshold)))
notify(booted, "*** You've been idle too long and LambdaMOO is still too busy ***");
server_log(tostr("IDLE: ", booted.name, " (", booted, ") idle ", idle / 60));
boot_player(booted);
endif
endfork
endif
endif
endif
this:(verb)(@args);
endfork
this.boot_task = taskn;
"This is set up so that it forks the task first, and this.boot_task is the task_id of whatever is running the idle booter";
.
#24:29
":grant_object(what, towhom);";
"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 (!caller_perms().wizard)
return E_PERM;
endif
{object, newowner} = args;
if (!is_player(newowner))
return E_INVARG;
endif
same = object.owner == newowner;
for vnum in [1..length(verbs(object))]
info = verb_info(object, vnum);
if (!((info[1] != object.owner) && (valid(info[1]) && is_player(info[1]))))
same = same && (info[1] == newowner);
set_verb_info(object, vnum, listset(info, newowner, 1));
endif
endfor
for prop in (properties(object))
$command_utils:suspend_if_needed(0);
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, 1);
endif
endfor
suspend(0);
$wiz_utils:set_owner(object, newowner, 1);
return same ? "nothing changed" | "grant changed";
.
#24:30
"connection_hash(forwhom, host [,seed])";
"Compute an encrypted hash of the host for 'forwhom', using 'crypt'.";
{forwhom, host, @seed} = args;
hash = toint(forwhom);
for i in [1..length(host)]
hash = (hash * 14) + index($string_utils.ascii, host[i]);
endfor
return crypt(tostr(hash), @seed);
.
#24:31
":newt_player(who [ , commentary] [, temporary])";
{who, ?comment = "", ?temporary = 0} = args;
if (!caller_perms().wizard)
$error:raise(E_PERM);
elseif (length(args) < 1)
$error:raise(E_ARGS);
elseif ((typeof(who = args[1]) != OBJ) || (!is_player(who)))
$error:raise(E_INVARG);
else
if (!comment)
player:notify("So why has this player been newted?");
comment = $command_utils:read();
endif
if (temporary)
comment = temporary + comment;
endif
$login.newted = setadd($login.newted, 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} | {}});
if ($object_utils:isa(who.location, $room) && (msg = player:newt_msg()))
who.location:announce_all_but({who}, msg);
endif
player:notify(tostr("Mail sent to ", $mail_agent:name($newt_log), "."));
endif
.
#24:32
":unset_programmer(victim[,reason[,start time,duration]]) => 1 or error.";
"Resets victim.programmer, adds victim to .programmer_restricted.";
"Put into temporary list if 3rd and 4th arguments are given. Which restricts the victim for uptime duration since start time. Must give a reason, though it can be blank, in this case.";
{victim, ?reason = "", ?start = 0, ?duration = 0} = args;
if (!caller_perms().wizard)
return E_PERM;
elseif (!valid(victim))
return E_INVARG;
elseif ((!victim.programmer) && this:check_prog_restricted(victim))
return E_NONE;
else
victim.programmer = 0;
if (is_player(victim) && $object_utils:isa(victim, $player))
this.programmer_restricted = setadd(this.programmer_restricted, victim);
if (start)
this.programmer_restricted_temp = setadd(this.programmer_restricted_temp, {victim, start, duration});
endif
endif
$mail_agent:send_message(caller_perms(), {$newt_log}, tostr("@deprogrammer ", victim.name, " (", victim, ")"), reason ? (typeof(reason) == STR) ? {reason} | reason | {});
return 1;
endif
.
#24:33
":is_wizard(who) => whether `who' is a wizard or is the .public_identity of some wizard.";
"This verb is used for permission checks on commands that should only be accessible to wizards or their ordinary-player counterparts.  It will return true for unadvertised wizards.";
who = args[1];
if (who.wizard)
return 1;
else
for w in ($object_utils:leaves($wiz))
if ((w.wizard && is_player(w)) && (who == `w.public_identity ! ANY'))
return 1;
endif
endfor
endif
return 0;
.
#24:34
if (!caller_perms().wizard)
return E_PERM;
endif
this:expire_mail_lists();
this:expire_mail_players();
.
#24:35
if (!caller_perms().wizard)
return E_PERM;
endif
fork (((7 * 24) * 60) * 60)
this:(verb)();
endfork
this:expire_mail();
.
#24:36
"Checks to see if args[1] is restricted from programmer either permanently or temporarily. Removes from temporary list if time is up";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
if ((who = args[1]) in this.programmer_restricted)
"okay, who is restricted. Now check to see if it is temporary";
if (entry = $list_utils:assoc(who, this.programmer_restricted_temp))
if ($login:uptime_since(entry[2]) > entry[3])
"It's temporary and the time is up, remove and return false";
this.programmer_restricted_temp = setremove(this.programmer_restricted_temp, entry);
this.programmer_restricted = setremove(this.programmer_restricted, who);
return 0;
else
"time is not up";
return 1;
endif
else
return 1;
endif
else
return 0;
endif
.
#24:37
if (!caller_perms().wizard)
return E_PERM;
endif
s = 0;
for p in (players())
this.expiration_progress = p;
if ((p.owner == p) && is_player(p))
s = s + (p:expire_old_messages() || 0);
endif
if (ticks_left() < 10000)
set_task_perms($wiz_utils:random_wizard());
suspend(0);
endif
endfor
$mail_agent:send_message(player, player, verb, tostr(s, " messages have been expired from players."));
return s;
.
#24:38
if (!caller_perms().wizard)
return E_PERM;
endif
sum = 0;
for x in ($object_utils:leaves_suspended($mail_recipient))
this.expiration_progress = x;
temp = x:expire_old_messages();
if (typeof(temp) == INT)
sum = sum + temp;
endif
if (ticks_left() < 10000)
set_task_perms($wiz_utils:random_wizard());
suspend(0);
endif
endfor
$mail_agent:send_message(player, player, verb, tostr(sum, " messages have been expired from mailing lists."));
return sum;
.
#24:39
if (!caller_perms().wizard)
return E_PERM;
else
fork (86400 * 7)
this:(verb)();
endfork
player:tell("Flushing ancient editor sessions.");
for x in ({$verb_editor, $note_editor, $mail_editor})
x:do_flush(time() - (30 * 86400), 0);
$command_utils:suspend_if_needed(0);
endfor
endif
.
#24:40
"Put all your wizards in $wiz_utils.wizards.  Then various long-running tasks will cycle among the permissions, spreading out the scheduler-induced personal lag.";
w = this.wizards;
i = this.next_perm_index;
if (i == length(w))
i = 1;
else
i = i + 1;
endif
this.next_perm_index = i;
return w[i];
.
#25:0
return ((caller == this) || caller_perms().wizard) ? pass(@args) | E_PERM;
.
#25:1
":add(player,site)";
if (!caller_perms().wizard)
return E_PERM;
endif
{who, domain} = args;
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..$];
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, $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 toint(hnum[1..9]) && toint(hnum[6..len]);
else
return toint(hnum);
endif
"SLEAZY CODE ALERT";
"... what I wanted to do was return toint(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
if (caller_perms().wizard)
pass();
this:clearall();
this.domain = "localdomain";
endif
.
#25:5
"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.";
"Rewriting this to do numerics now.";
if (!caller_perms().wizard)
raise(E_PERM);
endif
this.prune_task = task_id();
probe = this.prune_progress;
while (probe[1] <= this.prune_stop)
probestring = tostr(probe[1], ".", probe[2], ".");
for sitename in (z = this:find_all_keys(probestring))
items = this:find_exact(sitename);
orig = items;
for y in (items)
if ((typeof(y) == OBJ) && ((!valid(y)) || (!is_player(y))))
items = setremove(items, y);
endif
$command_utils:suspend_if_needed(0);
endfor
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);
endfor
if (probe[2] == 255)
probe[1] = probe[1] + 1;
probe[2] = 0;
else
probe[2] = probe[2] + 1;
endif
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));
.
#25:6
player:tell("Prune is up to ", toliteral(this.prune_progress), ".");
mine = 0;
if (typeof(this.prune_progress) == STR)
total = (26 * 26) * 26;
for x in [1..3]
mine = ((mine * 26) + index($string_utils.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
.
#25:7
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;
for item in (items)
if (typeof(item) == STR)
if (this:prune_fixup((item + ".") + root))
items = setremove(items, item);
endif
endif
$command_utils:suspend_if_needed(0);
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
.
#26:0
"xsin(INT x) -- calculates the taylor approximation for the sine function";
if (typeof(x = args[1]) != INT)
return E_TYPE;
endif
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:1
"xcos(INT x) -- calculates the taylor approximation for the cosine function";
if (typeof(x = args[1]) != INT)
return E_TYPE;
endif
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:2
"factorial(INT n) -- returns n factorial for 0 <= n (<= 12).";
if ((number = args[1]) < 0)
return E_INVARG;
elseif (typeof(number) != INT)
return E_TYPE;
endif
fact = 1;
for i in [2..number]
fact = fact * i;
endfor
return fact;
.
#26:3
"pow(INT|FLOAT x,(INT)|(INT|FLOAT) n) -- returns x raised to the nth power. n must be >= 0. If x is an integer, n must be an integer. If x is a floating point number, n can be either.";
{x, n} = args;
if (n < 0)
return E_INVARG;
elseif ((typeof(x) == INT) && (typeof(n) == FLOAT))
return E_TYPE;
endif
return x ^ n;
"old code below";
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:4
"fibonacci(INT n) -- calculates the fibonacci numbers to the nth term";
"and returns them in a list. n must be >= 0.";
if (typeof(n) != INT)
return E_TYPE;
elseif ((n = args[1]) < 0)
return E_INVARG;
elseif (n == 0)
return {0};
else
x = {0, 1};
for i in [2..n]
x = {@x, x[$ - 1] + x[$]};
endfor
return x;
endif
.
#26:5
"geometric(INT|FLOAT x [,INT 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;
if ((!(typeof(n) in {INT, FLOAT})) || (typeof(order) != INT))
return E_TYPE;
elseif (order <= 0)
return E_INVARG;
endif
x = 1;
for i in [1..order]
x = x + (n ^ i);
endfor
return x;
.
#26:6
"divmod(INT n, INT 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, d} = args;
if ((typeof(n) != INT) && (typeof(d) != INT))
return E_TYPE;
endif
r = ((n % d) + d) % d;
q = (n - r) / d;
return {q, r};
.
#26:7
"combinations(INT n, INT 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, r} = args;
if ((typeof(n) != INT) && (typeof(r) != INT))
return E_TYPE;
endif
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:8
"permutations(INT n, INT r) -- returns the number of ways possible for one to";
"order r distinct objects given n locations.";
"P(n,r) = n!/(n-r)!";
{n, r} = args;
if ((typeof(n) != INT) && (typeof(r) != INT))
return E_TYPE;
endif
if ((r < 1) || ((diff = n - r) < 0))
return 0;
else
p = n;
for i in [diff + 1..n - 1]
p = p * i;
endfor
return p;
endif
.
#26:9
"simpson({a,b},{f(a),f((a+b)/2),f(b)} [,INT ret-float])";
" -- 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.";
"Entries can either be all INT or all FLOAT. Don't mix!";
"If the optional 3rd argument is provided and true, the answer is returned as a floating point regardless of what the input was. Otherwise, if the input was all INT, the answer is returned as {integer,fraction}";
{point, fcn, ?retfloat = 0} = args;
if ((!retfloat) && (typeof(point[1]) == INT))
numer = (point[2] - point[1]) * ((fcn[1] + (4 * fcn[2])) + fcn[3]);
return this:parts(numer, 6);
else
numer = tofloat(point[2] - point[1]) * ((tofloat(fcn[1]) + (4.0 * tofloat(fcn[2]))) + tofloat(fcn[3]));
return numer / 6.0;
endif
.
#26:10
"parts(INT n, INT q [,INT 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";
{n, q, ?i = 5} = args;
if (((typeof(n) != INT) && (typeof(q) != INT)) && (typeof(i) != INT))
return E_TYPE;
endif
parts = {n / q, n % q};
return {parts[1], (parts[2] * (10 ^ i)) / q};
.
#26:11
"sqrt(INT|FLOAT n) => largest integer <= square root of n. Returns the same type as the input. (Backwards compatibility)";
n = args[1];
return (typeof(n) == INT) ? toint(sqrt(tofloat(n))) | sqrt(n);
"Old code. Newton's method";
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:12
"div(INT n, INT 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:13
"A correct mod function.";
"mod(INT n, INT d) => r such that n = dq + r and (0<=r<d if d>0 or -d<r<=0 if d<0).";
{n, d} = args;
if ((typeof(n) != INT) && (typeof(d) != INT))
return E_TYPE;
endif
return ((n % d) + d) % d;
.
#26:14
"exp(INT|FLOAT x[,INT 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} if the input x was an integer. If it is floating point, so will the answer (and this uses the builtin function.)";
{x, ?n = 5} = args;
if (typeof(x) == FLOAT)
return exp(x);
elseif ((typeof(x) != INT) && (typeof(n) != INT))
return E_TYPE;
endif
ex = nfact = 1;
for i in [0..n - 1]
j = n - i;
ex = (ex * x) + (nfact = nfact * j);
endfor
return this:parts(ex, nfact);
.
#26:15
"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:16
"random(INT n): returns a random integer in the following manner:";
"random(n > 0) will return a integer in the range 0 to n";
"random(n < 0) will return a integer in the range n to 0";
if (typeof(prob = args[1]) != INT)
return E_TYPE;
endif
mod = (prob < 0) ? -1 | 1;
return (mod * random(abs(prob + mod))) - mod;
.
#26:17
"random_range(INT range [,INT mean]): returns a random integer within the given range from the mean. if the mean isn't given, it defaults to 0";
"e.g., random_range(10) => -10..10";
"      random_range(10,4) => -6..14";
{range, ?mean = 0} = args;
if ((typeof(range) != INT) && (typeof(mean) != INT))
return E_TYPE;
endif
return mean + (((random(2) == 1) ? -1 | 1) * this:random(range));
.
#26:18
"is_prime(INT number) returns 1 if the number is prime or 0 if it isn't.";
"of course, only positive numbers are candidates for primality.";
if (typeof(number = args[1]) != INT)
return E_TYPE;
endif
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:19
"Only useful for integer input.";
{x, y} = args;
if ((typeof(x) != INT) && (typeof(y) != INT))
return E_TYPE;
endif
table = this.(verb);
if (xsgn = x < 0)
x = x + $minint;
endif
if (ysgn = y < 0)
y = y + $minint;
endif
power = 1;
z = 0;
while (x || y)
z = z + (power * table[1 + (x % 16)][1 + (y % 16)]);
x = x / 16;
y = y / 16;
power = power * 16;
endwhile
if (table[1 + xsgn][1 + ysgn])
return z + $minint;
else
return z;
endif
.
#26:20
return this:NOT(this:AND(this:NOT(args[1]), this:NOT(args[2])));
.
#26:21
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:22
"BlFromInt(INT x) => converts the number provided into a 32 bit binary number, which is returned via a 32 element LIST of 1's and 0's. Note that this verb was originally written to be used with the $math_utils verbs: AND, NOT, OR, XOR, but has since been taken out of them.";
if (typeof(x = args[1]) != INT)
return E_TYPE;
endif
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:23
"IntFromBl(LIST of 1's and 0's) => converts the 32 bit binary representation given by the list of 1's and 0's and converts it to a normal decimal number. Note that this verb was originally written to be used with the $math_utils verbs: AND, NOT, OR, XOR, but has since been taken out of them.";
bl = args[1];
x = 0;
for l in (bl)
x = x * 2;
x = x + l;
endfor
return x;
.
#26:24
"gcd(INT num1,INT 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:25
"lcm(INT num1,INT 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:26
"are_rel_prime(INT num1,INT 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:27
"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 = toint(args[4]);
endif
result = 0;
thenum = tostr(args[1]);
origbase = toint(args[2]);
newbase = toint(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;
.
#26:28
":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 toint(sqrt(tofloat(s)));
else
factor = toint("1" + "0000000"[1..logm - 4]);
s = 0;
for a in (args)
a = a / factor;
s = s + (a * a);
endfor
return toint(sqrt(tofloat(s))) * factor;
endif
.
#26:29
":sum(INT|FLOAT num, num, num ...) => Total of all arguments added together.";
":sum({num, num, num, ...}) will also work.";
total = (typeof((typeof(x = args[1]) == LIST) ? x[1] | x) == INT) ? 0 | 0.0;
for number in ((typeof(x) == LIST) ? x | args)
total = total + number;
endfor
return total;
.
#26:30
"Copied from Trig_Utils (#25800):sin by Obvious (#54879) Fri Nov 17 06:07:39 1995 PST";
theta = args[1];
if (typeof(theta) == FLOAT)
return sin(theta);
elseif (typeof(theta) == INT)
degtheta = theta % 360;
mintheta = 0;
elseif (typeof(theta) == LIST)
degtheta = theta[1] % 360;
mintheta = theta[2] % 60;
else
return E_INVARG;
endif
if (mintheta < 0)
mintheta = mintheta + 60;
degtheta = degtheta - 1;
endif
while (degtheta < 1)
degtheta = degtheta + 360;
endwhile
if (mintheta == 0)
return this.sines[degtheta];
endif
lim1 = this.sines[degtheta];
lim2 = this.sines[degtheta + 1];
delta = lim2 - lim1;
result = (((delta * mintheta) + 30) / 60) + lim1;
return result;
.
#26:31
"Copied from Trig_Utils (#25800):cos by Obvious (#54879) Fri Nov 17 06:07:50 1995 PST";
theta = args[1];
if (typeof(theta) == FLOAT)
return cos(theta);
elseif (typeof(theta) == INT)
degtheta = 90 - theta;
mintheta = 0;
elseif (typeof(theta) == LIST)
degtheta = 89 - theta[1];
mintheta = 60 - theta[2];
else
return;
endif
return this:sin({degtheta, mintheta});
.
#26:32
"Copied from Trig_Utils (#25800):tan by Obvious (#54879) Fri Nov 17 06:07:53 1995 PST";
{theta} = args;
if (typeof(theta) == FLOAT)
return tan(theta);
endif
sine = this:sin(theta);
cosine = this:cos(theta);
return ((sine * 10000) + ((cosine + 1) / 2)) / cosine;
.
#26:33
"Copied from Trig_Utils (#25800):arcsin by Obvious (#54879) Fri Nov 17 06:08:01 1995 PST";
{given} = args;
if (typeof(given) == FLOAT)
return asin(given);
endif
given = abs(given);
if (given > 10000)
return E_RANGE;
endif
i = 1;
while (given > this.sines[i])
i = i + 1;
endwhile
if (given == this.sines[i])
if (args[1] < 0)
return {-i, 0};
else
return {i, 0};
endif
endif
degrees = i - 1;
if (i == 1)
lower = 0;
else
lower = this.sines[i - 1];
endif
upper = this.sines[i];
delta1 = given - lower;
delta2 = upper - lower;
minutes = ((delta1 * 60) + ((delta2 + 1) / 2)) / delta2;
if (args[1] < 0)
degrees = -degrees;
minutes = -minutes;
endif
return {degrees, minutes};
.
#26:34
"Copied from Trig_Utils (#25800):arccos by Obvious (#54879) Fri Nov 17 06:08:08 1995 PST";
given = args[1];
if (typeof(given) == FLOAT)
return acos(given);
endif
arcsin = this:arcsin(given);
degrees = 89 - arcsin[1];
minutes = 60 - arcsin[2];
if (minutes > 60)
minutes = minutes - 60;
degrees = degrees + 1;
endif
return {degrees, minutes};
.
#26:35
"Copied from Trig_Utils (#25800):arctan by Obvious (#54879) Fri Nov 17 06:08:18 1995 PST";
given = args[1];
if (typeof(given) == FLOAT)
return atan(given);
endif
reciprocal = ((given * given) / 10000) + 10000;
reciprocal = sqrt(reciprocal * 10000);
cosine = 100000000 / reciprocal;
return this:arccos(cosine);
.
#27:0
"Returns the set union of all of the lists provided as arguments.";
if (!args)
return {};
endif
{set, @rest} = args;
for l in (rest)
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, @rest} = args;
for set in (rest)
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";
{set, @rest} = args;
for l in (rest)
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 = {}, @rest} = args;
for l in (rest)
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, @rest} = args;
so_far = set;
for l in (rest)
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_suspended(set 1, set 2, ..., set n)";
"Returns all elements of set 1 that are not in sets 2..n";
"Suspends as needed if the lists are large.";
{set, @rest} = 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
"True if the two lists given contain the same elements.";
"False otherwise.";
{set1, set2} = args;
while (set1)
{elt, @set1} = set1;
if (elt in set2)
set2 = setremove(set2, elt);
while (elt in set2)
set2 = setremove(set2, elt);
endwhile
while (elt in set1)
set1 = setremove(set1, elt);
endwhile
else
return 0;
endif
endwhile
if (set2)
return 0;
else
return 1;
endif
.
#27:7
"Copied from Fox (#54902):intersection Mon Dec 27 17:02:57 1993 PST";
"a version of $set_utils:intersection that maintains the property that everything in the return value is in the first argument, even considering case";
if (!args)
return {};
endif
{result, @rest} = args;
for s in (rest)
for x in (result)
if (!(x in s))
result = setremove(result, x);
endif
endfor
endfor
return result;
.
#29:0
if (caller_perms().wizard)
pass();
if (l = this:length_all_msgs())
this:rm_message_seq({1, l});
endif
this:expunge_rmm();
this.mail_forward = {};
this.mail_notify = {player};
player.current_message = {@player.current_message, {this, 0, 0}};
for p in ({"moderator_forward", "writers", "readers", "expire_period", "last_used_time"})
clear_property(this, p);
endfor
this.moderated = 1;
else
return E_PERM;
endif
"Last modified Sun Aug 17 11:13:43 1997 CDT by Wizard (#2).";
.
#29:1
if (!this:is_writable_by(caller_perms()))
return E_PERM;
else
if (msgs = this.messages)
new = msgs[$][1] + 1;
else
new = 1;
endif
if (rmsgs = this.messages_going)
lbrm = rmsgs[$][2];
new = max(new, 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
{msg_seq, ?cur = 0, ?read_date = $maxint} = args;
last = ldate = 0;
player:tell("       WHEN           ", $string_utils:left(this.keyword, -30), "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
if (ldate < (time() - 31536000))
c = player:ctime(ldate);
date = c[5..11] + c[21..25];
else
date = player:ctime(ldate)[5..16];
endif
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
{plist, ?mask = {1}} = args;
if (typeof(plist) != LIST)
plist = {plist};
endif
i = 1;
fseq = {};
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..$];
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
{plist, ?mask = {1}} = args;
if (typeof(plist) != LIST)
plist = {plist};
endif
i = 1;
fseq = {};
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..$];
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
{nlist, ?mask = {1}} = args;
if (typeof(nlist) != LIST)
nlist = {nlist};
endif
i = 1;
fseq = {};
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..$];
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 (`$object_utils:has_property(parent(this), search) ! ANY')
if ($object_utils:has_property(this, " " + search))
return {search};
endif
elseif ($object_utils:has_property(this, search))
return {search};
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);
topics = {};
if (search[1] == "@")
search = search[2..$];
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])))
topics = {@topics, (prop[1] == " ") ? prop[2..$] | prop};
endif
endfor
return topics;
else
"...return list of all topics...";
props = setremove(properties(this), "");
for p in (`$object_utils:all_properties(parent(this)) ! ANY => {}')
if (i = (" " + p) in props)
props = {p, @listdelete(props, i)};
endif
endfor
return props;
endif
.
#30:1
"WIZARDLY";
{topic, ?dblist = {}} = args;
if (`$object_utils:has_property(parent(this), topic) ! ANY')
text = `this.(" " + topic) ! ANY';
else
text = `this.(topic) || this.(" " + topic) ! ANY';
endif
if (typeof(text) == LIST)
if (text && (text[1] == (("*" + (vb = strsub(text[1], "*", ""))) + "*")))
text = `this:(vb)(listdelete(text, 1), dblist) ! ANY';
endif
endif
return text;
.
#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..$] + " " | 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(@names);
.
#30:3
":columnize(@list_of_strings) -- prints the given list in a number of columns wide enough to accomodate longest entry. But no more than 4 columns.";
longest = $list_utils:longest(args);
for d in ({4, 3, 2, 1})
if ((79 / d) >= length(longest))
return $string_utils:columnize_suspended(0, args, d);
endif
endfor
.
#30:4
"{\"*forward*\", topic, @rest}  => text for topic from this help db.";
"{\"*pass*\",    topic, @rest}  => text for topic from next 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 ((result = $code_utils:help_db_search(text[1], dblist)) && ((db = result[1]) != $ambiguous_match))
first = db:get_topic(result[2], dblist[(db in dblist) + 1..$]);
else
first = {};
endif
if (2 <= length(text))
if (text[2] == (("*" + (vb = strsub(text[2], "*", ""))) + "*"))
return {@first, @`this:(vb)(text[3..$], dblist) ! ANY => {}'};
else
return {@first, @text[2..$]};
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..$];
if (code == "[")
prog = "";
while ((b = index(old + "]", "]")) > (p = index(old + "%", "%")))
prog = (prog + old[1..p - 1]) + old[p + 1];
old = old[p + 2..$];
endwhile
prog = prog + old[1..b - 1];
old = old[b + 1..$];
value = $no_one:eval_d(prog);
if (value[1])
new = tostr(new, value[2]);
else
new = tostr(new, toliteral(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..$ - 1]};
new = tostr(r[$]);
else
new = tostr(new, toliteral(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 {"", 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 ($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
try
text = this.(fulltopic = args[1]);
return {tostr(";;", $code_utils:corify_object(this), ".(", toliteral(fulltopic), ") = $command_utils:read_lines()"), @$command_utils:dump_lines(text)};
except error (ANY)
return error[1];
endtry
.
#30:10
"{\"*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_msg"))
return E_VERBNF;
else
return object:help_msg();
endif
.
#30:11
":find_index_topic([search])";
"Return the list of index topics of this help DB";
"(i.e., those which contain an index (list of topics)";
"this DB, return it, otherwise return false.";
"If search argument is given and true,";
"we first remove any cached information concerning index topics.";
{?search = 0} = args;
if (this.index_cache && (!search))
"...make sure every topic listed in .index_cache really is an index topic";
for p in (this.index_cache)
if (!("*index*" in `this.(p) ! ANY => {}'))
search = 1;
endif
endfor
if (!search)
return this.index_cache;
endif
elseif ($generic_help == this)
return {};
endif
itopics = {};
for p in (properties(this))
if ((h = `this.(p) ! ANY') && ("*index*" in h))
itopics = {@itopics, p};
endif
endfor
this.index_cache = itopics;
return itopics;
.
#31:0
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..."} | {" or type `create' to learn more about how to get a character of your own."}));
"boot_player(this)";
return;
"See #0:user_reconnected.";
.
#31:1
if ((((valid(cp = caller_perms()) && (caller != this)) && (!$perm_utils:controls(cp, this))) && (cp != this)) && (caller != #0))
return E_PERM;
endif
"Don't let another guest use this one until all this is done. See :defer, Ho_Yan 1/19/94";
this.free_to_use = 0;
this:log_disconnect();
this:erase_paranoid_data();
try
if (this.location != this.home)
this:room_announce(player.name, " has disconnected.");
this:room_announce("The housekeeper arrives to remove ", this.name, ".");
move(this, this.home);
this:room_announce("The housekeeper arrives to drop off ", this.name, ".");
endif
finally
this:do_reset();
endtry
"Last modified Wed Apr 14 13:13:12 1999 CDT by Wizard (#2).";
.
#31:2
"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($string_utils:connection_hostname(connection_name(player))))
return #-2;
elseif ((!(this in connected_players())) && this.free_to_use)
"...not logged in, no problemo...";
this.free_to_use = 0;
this:check_login(this);
return this;
endif
longest = 900;
"...guests get 15 minutes before they can be dislodged...";
candidate = #-1;
free = {};
for g in ($object_utils:leaves($guest))
if (!is_player(g))
"...a toaded guest?...";
elseif ((!(con = g in connected_players())) && g.free_to_use)
"...yay; found an unused guest...and their last :disfunc is complete";
free = {@free, g};
elseif (con && ((t = connected_seconds(g)) > longest))
longest = t;
candidate = g;
endif
endfor
if (free)
candidate = free[random($)];
candidate.free_to_use = 0;
this:check_login(candidate);
elseif (valid(candidate))
"...someone's getting bumped...";
candidate:boot();
endif
return candidate;
"Last modified Wed Apr 14 13:13:30 1999 CDT by Wizard (#2).";
.
#31:3
return;
.
#31:4
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:5
return pass(@args);
.
#31:6
":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:7
if ((((valid(cp = caller_perms()) && (caller != this)) && (!$perm_utils:controls(cp, this))) && (cp != this)) && (caller != #0))
return E_PERM;
else
$guest_log:enter(1, time(), $string_utils:connection_hostname(connection_name(this)));
ret = pass(@args);
return ret;
endif
"Last modified Mon Sep 17 10:08:53 2001 CDT by Wizard (#2).";
.
#31:8
if (caller != this)
return E_PERM;
else
cname = `connection_name(this) ! ANY' || this.last_connect_place;
$guest_log:enter(0, time(), $string_utils:connection_hostname(cname));
endif
.
#31:9
if (!valid(caller_perms()))
player:tell("Sorry, that information is not available.");
endif
.
#31:10
if (caller_perms() != this)
return E_PERM;
else
return pass(@args);
endif
.
#31:11
return pass(@args);
.
#31:12
return pass(@args);
"only for setting permission";
.
#31:13
if (caller_perms().wizard)
this.extra_confunc_msg = "";
clear_property(this, "rooms");
this.features = {$stage_talk, $pasting_feature, $soc_verbs};
endif
"Last modified Sun Aug 17 11:03:05 1997 CDT by Wizard (#2).";
.
#31:14
"disallow guests from setting aliases on themselves";
if ($perm_utils:controls(caller_perms(), this))
return pass(@args);
else
return E_PERM;
endif
.
#31:15
return $string_utils:pronoun_sub(this.(verb));
.
#31:16
"Updated by Jan Apr. 21 1999";
if (!caller_perms().wizard)
return E_PERM;
else
"Added for Xpress cleanup, jan";
this.web_access_code = "";
this.xpress_on = 0;
this.ts_client = 0;
this.linelen = -79;
this.display_drawings = 1;
this.java_client_localecho = "False";
if (this.default_name || $real_guest_names)
this:set_name(this.default_name);
endif
if (this.default_aliases || $real_guest_names)
this:set_aliases(this.default_aliases);
endif
this:set_description(this.default_description);
this:set_gender(this.default_gender);
flush_input(this, 0);
for x in ({"paranoid", "lines", "responsible", "linelen", "linebuffer", "brief", "gaglist", "rooms", "pagelen", "current_message", "current_folder", "messages", "messages_going", "request", "mail_options", "edit_options", "home"})
if ($object_utils:has_property(parent(this), x))
clear_property(this, x);
endif
endfor
for x in (this.contents)
this:eject(x);
endfor
for x in (this.features)
if (!(x in $guest.features))
this:remove_feature(x);
endif
endfor
for x in ($guest.features)
if (!(x in this.features))
this:add_feature(x);
endif
endfor
for x in ($object_utils:descendants($generic_editor))
if (loc = this in x.active)
x:kill_session(loc);
endif
endfor
this.free_to_use = 1;
endif
"Last modified Mon Sep 17 10:04:26 2001 CDT by Wizard (#2).";
.
#31:17
"New Character Request. By Jan@LinguaMOO. Usage @request name for email-address";
if (!args)
return player:tell("Usage: @request <name> for <email-address>");
endif
if ($login.create_enabled)
if (player != this)
return player:tell(E_PERM);
endif
if (this.request)
return player:tell("Sorry, you appear to have already requested a character.");
endif
name = dobjstr;
if ((prepstr != "for") || ((!dobjstr) || index(address = iobjstr, " ")))
return player:notify_lines($code_utils:verb_usage());
endif
if ($login:request_character(player, name, address))
this.request = 1;
endif
else
name = dobjstr;
address = iobjstr;
if ((!$player_db:available(name)) && (name != this.name))
return player:notify(("Sorry, " + name) + " is not an available name.");
elseif ($registration_db:find_exact(address))
return player:notify("Hmm...you seem to have a registered character already.");
endif
player.old_location = player.location;
player.location:announce(player.name, " goes to request a character.");
move(player, $character_request_room);
player:notify("Please enter your full name:");
real_name = $command_utils:read();
player:notify(("Please list your reason(s) for wanting a character here at " + $network.moo_name) + ":");
reason = $command_utils:read();
player:notify("");
player:notify("You have entered the following information:");
player:notify("Character name   : " + name);
player:notify("Email address    : " + address);
player:notify("Real name        : " + real_name);
player:notify("Reason           : " + reason);
player:notify(" ");
player:notify("Is this correct? (please double check your email address. If the address is not correct we will not be able to send you the password via email.)");
if (!$command_utils:yes_or_no())
move(player, player.old_location);
player:announce(player.name, " returns from character request.");
player:notify("*** Character request terminated ***");
return 0;
endif
$mail_agent:send_message(this, $character_request_list, "Character request", {"Player name: " + name, "Email address: " + address, "Real name: " + real_name, "Reason for request: " + reason});
move(player, player.old_location);
player:notify("");
player:tell("Your character request has been registered. Please allow up to a week for processing. If you haven't heard back from us by then please send regular email to ", $network.reply_address, ". Thank you and have a good day.");
player:announce(player.name, " returns from character request.");
endif
"Last modified Tue Oct 14 14:21:46 1997 CDT by Wizard (#2).";
.
#31:18
"Compute an encrypted hash of the guest's (last) connection, using 'crypt'. Basically, you can't tell where the guest came from, but it is unlikely that two guests will have the same hash";
"You can use guest:connection_name_hash(seed) as a string to identify whether two guests are from the same place.";
xx = $wiz_utils:connection_hash(caller_perms(), $string_utils:connection_hostname(this.last_connect_place), @args);
hash = toint(caller_perms());
host = $string_utils:connection_hostname(this.last_connect_place);
for i in [1..length(host)]
hash = (hash * 14) + index($string_utils.ascii, host[i]);
endfor
return crypt(tostr(hash), @args);
.
#31:19
pass();
domain = $string_utils:connection_hostname(this.last_connect_place);
player:tell(this.name, " is connected from ", domain);
"Last modified Sun Aug 17 11:00:07 1997 CDT by Wizard (#2).";
.
#31:20
if (valid(caller_perms()) && (caller != #0))
return E_PERM;
endif
finished = 0;
while (!finished)
player:tell(">> What name do you wish to use in ", $network.MOO_name, "? <<");
name = read(this);
name = $string_utils:trim(name);
if (name)
if ($player_db:available(name))
if ($guest_name_suffix)
aliases = {@this.aliases, name, tostr(name, " ", $guest_name_suffix)};
name = tostr(name, " ", $guest_name_suffix);
else
aliases = {@this.aliases, name};
endif
this.name = name;
this:set_aliases(aliases);
finished = 1;
else
player:tell(name + " is being used by someone else. Please select a different name");
endif
else
player:tell("You must select a name for yourself, please try again.");
endif
endwhile
"Last modified Thu Apr  8 10:51:28 1999 CDT by Wizard (#2).";
.
#31:21
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Generates a dynamic web menu line based on which player class the user belongs to";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, preload} = args;
options = options_window = {};
base_url = tostr("http://", $network.site, ":", $network.webport, "/");
external_baseurl = (($xpress_client.external_baseurl + $xpress_client.themes_folder) + this.xpress_theme) + "/";
"Assemble menu line";
about = {tostr("<A HREF=\"", base_url, "MOO_info/about.html\" TARGET=\"", $MOO_info:get_frame_name("openAboutWindow"), "\" onClick=\"openAboutWindow(); swapImage('about','", external_baseurl, "about1.jpg')\" onMouseOver=\"swapImage('about','", external_baseurl, "about2.jpg')\" onMouseOut=\"swapImage('about','", external_baseurl, "about1.jpg')\"><IMG SRC=\"", external_baseurl, "about1.jpg\" BORDER=0 ALIGN=bottom name=about ALT=\"Information About the MOO\" TITLE=\"Information About the MOO\"></A>")};
preload = {@preload, tostr("   about2 = new Image(); about2.src = \"", external_baseurl, "about2.jpg\";")};
help = {tostr("<A HREF=\"", base_url, "help_browser/main.html\" TARGET=\"", $help_browser:get_frame_name("openHelpBrowser"), "\" onClick=\"openHelpBrowser(); ; swapImage('help','", external_baseurl, "help1.jpg')\" onMouseOver=\"swapImage('help','", external_baseurl, "help2.jpg')\" onMouseOut=\"swapImage('help','", external_baseurl, "help1.jpg')\"><IMG SRC=\"", external_baseurl, "help1.jpg\" BORDER=0 ALIGN=bottom name=help ALT=\"Browse the MOO's Help System\" TITLE=\"Browse the MOO's Help System\"></A>")};
preload = {@preload, tostr("   help2 = new Image(); help2.src = \"", external_baseurl, "help2.jpg\";")};
look = {tostr("<A HREF=\"", base_url, "Xpress_client/display_user_location\" TARGET=\"web_frame\" onClick=\"swapImage('look','", external_baseurl, "look1.jpg')\" onMouseOver=\"swapImage('look','", external_baseurl, "look2.jpg')\" onMouseOut=\"swapImage('look','", external_baseurl, "look1.jpg')\"><IMG SRC=\"", external_baseurl, "look1.jpg\" BORDER=0 ALIGN=bottom name=look ALT=\"Focus On My Location\" TITLE=\"Focus On My Location\"></A>")};
preload = {@preload, tostr("   look2 = new Image(); look2.src = \"", external_baseurl, "look2.jpg\";")};
who = {tostr("<A HREF=\"", base_url, "who_browser/main.html\" TARGET=\"", $who_browser:get_frame_name("openWhoBrowser"), "\" onClick=\"openWhoBrowser(); swapImage('who','", external_baseurl, "who1.jpg')\" onMouseOver=\"swapImage('who','", external_baseurl, "who2.jpg')\" onMouseOut=\"swapImage('who','", external_baseurl, "who1.jpg')\"><IMG SRC=\"", external_baseurl, "who1.jpg\" BORDER=0 ALIGN=bottom name=who ALT=\"See Who Is Online\" TITLE=\"See Who Is Online\"></A>")};
preload = {@preload, tostr("   who2 = new Image(); who2.src = \"", external_baseurl, "who2.jpg\";")};
if ($real_guest_names)
options = {tostr("<A HREF=\"", base_url, "xpress_client/guest_preferences.html?", toint(user), "\" TARGET=\"", $xpress_client:get_frame_name("guestPreferenceWindow"), "\" onClick=\"guestPreferenceWindow(); swapImage('options','", external_baseurl, "options1.jpg')\" onMouseOver=\"swapImage('options','", external_baseurl, "options2.jpg')\" onMouseOut=\"swapImage('options','", external_baseurl, "options1.jpg')\"><IMG SRC=\"", external_baseurl, "options1.jpg\" BORDER=0 ALIGN=bottom name=options ALT=\"Change My Preferences\" TITLE=\"Change My Preferences\"></A>")};
preload = {@preload, tostr("   options2 = new Image(); options2.src = \"", external_baseurl, "options2.jpg\";")};
options_window = $encore_web_utils:javascript_window_open($MOO_info, "openOptions", "");
endif
guide = {tostr("<A HREF=\"", base_url, "xpress_client/guide.html\" TARGET=\"", $xpress_client:get_frame_name("openGuide"), "\" onClick=\"openGuide(); swapImage('guide','", external_baseurl, "guide1.jpg')\" onMouseOver=\"swapImage('guide','", external_baseurl, "guide2.jpg')\" onMouseOut=\"swapImage('guide','", external_baseurl, "guide1.jpg')\"><IMG SRC=\"", external_baseurl, "guide1.jpg\" BORDER=0 ALIGN=bottom name=guide ALT=\"Read an Introduction to MOOing\" TITLE=\"Read an Introduction to MOOing\"></A>")};
preload = {@preload, tostr("   guide2 = new Image(); guide2.src = \"", external_baseurl, "guide2.jpg\";")};
if ($login.create_enabled)
target_app = "Xpress_login";
else
target_app = "Xpress_client";
endif
request = {tostr("<A HREF=\"", base_url, target_app, "/character_application.html\" TARGET=\"", $xpress_client:get_frame_name("openCharacterApplication"), "\" onClick=\"openCharacterApplication(); swapImage('request','", external_baseurl, "request1.jpg')\" onMouseOver=\"swapImage('request','", external_baseurl, "request2.jpg')\" onMouseOut=\"swapImage('request','", external_baseurl, "request1.jpg')\"><IMG SRC=\"", external_baseurl, "request1.jpg\" BORDER=0 ALIGN=bottom name=request ALT=\"Apply for a Character\" TITLE=\"Apply for a Character\"></A>")};
preload = {@preload, tostr("   request2 = new Image(); request2.src = \"", external_baseurl, "request2.jpg\";")};
menu = $list_utils:append(about, help, look, who, options, guide, request);
"Assemble JavaScript functions";
about_window = $encore_web_utils:javascript_window_open($MOO_info, "openAboutWindow", "");
help_browser = $encore_web_utils:javascript_window_open($help_browser, "openHelpBrowser", "");
who_browser = $encore_web_utils:javascript_window_open($who_browser, "openWhoBrowser", "");
guide_window = $encore_web_utils:javascript_window_open($MOO_info, "openGuide", "");
character_application = $encore_web_utils:javascript_window_open($MOO_info, "openCharacterApplication", "");
functions = $list_utils:append(about_window, help_browser, who_browser, guide_window, character_application, options_window);
return {menu, functions, preload, base_url};
"Last modified Mon Sep 17 10:08:22 2001 CDT by Wizard (#2).";
.
#31:22
for guest in (children($guest))
if ((!(guest in connected_players())) && (!guest.free_to_use))
guest:do_reset();
endif
endfor
fork (1800)
$guest:check();
endfork
"Last modified Tue Apr 13 23:37:42 1999 CDT by Wizard (#2).";
.
#31:23
"Copyright (C) 1999, Jan Rune Holmevik";
"Checks to see if a reserved guest character has been logged in after a certain period of time. If not, send the assigned character back to the guest pool";
if (!caller_perms().wizard)
return E_PERM;
endif
guest = args[1];
fork (this.xpress_login_interval)
if ((!(guest in connected_players())) && (guest.free_to_use == 0))
guest:do_reset();
endif
endfork
"Last modified Wed Apr 14 13:24:03 1999 CDT by Wizard (#2).";
.
#32:0
"===========================================================";
"The High Wired enCore enCore, and enCore Xpress web interface is Copyright (C) 1995-2001 of Jan Rune Holmevik and Cynthia Haynes. All rights reserved. This system is based on a design by Mark Blanchard. The MOOtcan telnet applet was written by Sindre Srensen. This verb accepts and parses the initial web connection";
"===========================================================";
if (callers())
return E_PERM;
endif
if (this.watchers)
this:log(argstr, 8);
endif
if (comm = match(argstr, "^%(GET%|POST%|HEAD%) /%([^ ]*%)%( HTTP.*%)?"))
result = this:get(substitute("%1", comm), substitute("%2", comm), substitute("%3", comm));
boot_player(player);
kill_task(task_id());
else
result = 0;
endif
return result;
"Last modified Fri Apr 13 18:50:06 2001 CDT by Wizard (#2).";
.
#32:1
"===========================================================";
"Copyright (C) 1998-2001, Jan Rune Holmevik and sindre.sorensen@rasmus.uib.no";
"This verb handles and directs all http requests";
"===========================================================";
if (caller != this)
return E_PERM;
endif
try
"==========================";
"== Initialize variables ==";
"==========================";
{method, uri, httpver} = args;
body = result = html = head = page_props = footer = {};
data = message = cookie = content_type = content_type_incoming = boundary = "";
authentication_ok = 0;
status = "200_OK";
conn = player;
if (method in {"GET", "POST"})
"=================================================================";
"== Find and return the object and verb that the user requested ==";
"=================================================================";
if (valid_uri = $encore_web_utils:parse_url(uri))
object = valid_uri[1];
program = valid_uri[2];
client_headers = this:collect_user_agent_headers(conn);
if ("GET" == method)
"Make sure form data is preserved in POST requests";
data = valid_uri[3];
form_fields = $encore_web_utils:parse_form(data);
elseif ("POST" == method)
{data, content_type_incoming, boundary} = this:collect_user_agent_POST(conn, client_headers);
form_fields = $encore_web_utils:parse_form(data, content_type_incoming, boundary);
endif
"========================================";
"== Perform HTTP-cookie authentication ==";
"========================================";
{authentication_ok, user, login, cookie, message} = this:authenticate(uri, client_headers, form_fields, object, program, method);
if (authentication_ok)
"==============================================================";
"Generate HTML code for the requested object or web application";
"==============================================================";
if (login)
body = $xpress_login:login(user, message);
else
body = object:(program)(user, form_fields);
endif
"==============================================================";
"Perform misc error checks";
"==============================================================";
"Check to see if the verb returns anything but a list of strings:";
if ((!$list_utils:flat_list_of_STR(body)) && (typeof(body) != STR))
status = "500_Internal_Server_Error";
body = {"The verb did not return a flat list of strings:<P><PRE>", toliteral(body), "</PRE>"};
endif
"Check to see if a type 400 error code has been returned";
if (body in {"400_Bad_Request", "403_Forbidden"})
status = body;
else
(!body) ? body = {"HTTP-EQUIV=\"content-type\""} | 0;
if (match(body[1], "HTTP-EQUIV=\"content-type\""))
"A plain text is to be returned.";
content_type_source = body[1];
html = listdelete(body, 1);
else
if ($encore_web_utils:page_assembled(body))
"=========================================================";
"The page has already been assembled either by";
"a web application or by the owner of the owner";
"=========================================================";
html = body;
else
"=========================================================";
"Assemble the elements of the web page";
"=========================================================";
head = $encore_web_utils:make_head(user, object);
body = $encore_web_utils:make_body(user, object, body);
html = $encore_web_utils:make_page(user, object, $list_utils:append(head, body));
endif
content_type_source = html[3];
endif
"Determine correct content type for outgoing page";
content_type = this:determine_content_type(content_type_source);
endif
this.total_pages_served = this.total_pages_served + 1;
else
"==============================================================";
"Authentication failed. Determine where to direct user";
"==============================================================";
"Authentication failed for logged in user return login page";
if (login)
html = $xpress_login:login(user, message);
else
"First point of access. Return usert to main frameset.";
html = $Xpress_login:login_page(user);
endif
endif
else
"Object not found";
status = "404_Not_Found";
endif
else
"HTTP Method not Implemented";
status = "501_Not_Implemented";
endif
"===========================================================";
"== Add server response header and send data back to user ==";
"===========================================================";
result = $httpd:generate_server_response_header(status, cookie, html, content_type, argstr);
if (this.watchers)
this:log(tostr(status, " Sent ", length(result), " lines."), 9);
if ({{}, {}} != form_fields)
this:log("User agent posted data: " + toliteral(form_fields), 7);
endif
endif
except error (ANY)
body = {"<PRE>", @this:generate_traceback(error), "</PRE>"};
status = "500_Internal_Server_Error";
result = $httpd:generate_server_response_header(status, "", body, "", argstr);
endtry
for line in (result)
while ((!notify(conn, line, 1)) && (conn in connected_players(1)))
suspend(0);
endwhile
endfor
if (buffered_output_length(conn))
"========================================";
"== Make sure the user gets everything ==";
"========================================";
while (buffered_output_length(conn))
suspend(0);
endwhile
endif
return;
"Last modified Fri Apr 13 18:50:06 2001 CDT by Wizard (#2).";
.
#32:2
if (caller_perms().wizard || (caller_perms() == #0))
return listen($httpd, $network.webport);
else
return E_PERM;
endif
"Last modified Sun Aug 17 11:03:05 1997 CDT by Wizard (#2).";
.
#32:3
if (caller_perms().wizard)
pass();
this.protocol = "HTTP/1.0 ";
this.allow = "GET, POST, HEAD";
this.content_type = "text/html";
this.pragma = "No-Cache";
this.expires = "Expires: Thu, 15 Apr 1999 12:00:00 GMT";
this.total_homepage_hits = 0;
this.total_logins = 0;
this.total_pages_served = 0;
endif
"Last modified Fri Apr 23 15:43:50 1999 CDT by Wizard (#2).";
.
#32:4
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Performs authentication of HTTP requests.";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
{URI, client_headers, form_fields, requested_object, requested_verb, method} = args;
access_granted = display_login_page = 0;
message = cookie = "";
user = $no_one;
if (URI == "")
if (form_fields != {{}, {}})
"=========================================";
"===== New user login. Process login =====";
"=========================================";
display_login_page = 1;
user_info = form_fields;
user_name = $string_utils:trim(user_info[2][1]) || "";
password = $string_utils:trim(user_info[2][2]) || "";
OS = user_info[2][3];
if (user_name == "guest")
if (!children($guest))
"No guest characters has been made yet";
message = "alert('Sorry, no guest accounts are available in this MOO. Please enter a valid user name and password to log in.');";
else
"===================";
"Process guest login";
"===================";
user = $string_utils:match_player("guest");
if (!valid(user = `user:defer_login(user_name) ! E_VERBNF => user:defer(user_name)'))
"Guest login failed";
user = $no_one;
message = "alert('Sorry, all guest accounts are currently in use. Please try connecting again later.');";
else
"Guest login approved";
{user, cookie, message} = $Xpress_client:initialize_session(user, OS);
this.total_logins = this.total_logins + 1;
endif
endif
else
"=========================================";
"===== Process registered user login =====";
"=========================================";
if (valid(user = $string_utils:match_player(user_name)))
"Valid user name, now check password";
if (strcmp(crypt(password, user.password), user.password))
"Password not valid. Alert, and return user to login page";
user = $no_one;
message = tostr("alert('The password you entered is invalid. Remember that passwords are case sensitive. If you have forgot your password, please send an email to ", $network.postmaster, " to receive a new one.');");
else
"Valid user and password, create a temporary web password and return the user's home URI";
{user, cookie, message} = $Xpress_client:initialize_session(user, OS);
this.total_logins = this.total_logins + 1;
endif
else
"User not found. Alert, and return user to login page";
user = $no_one;
message = "alert('The user name you entered is invalid. Please try again.');";
endif
endif
endif
else
"=====================================================================";
"== Logged in user is requesting a page.                            ==";
"== Process client header for cookie information and access code    ==";
"=====================================================================";
access_code_found = 0;
client_headers = $list_utils:reverse(client_headers);
for line_nr in [1..length(client_headers)]
line = client_headers[line_nr];
if (position = index(line, "Access_Code="))
cookie_data = line[position + 12..$];
cookie_data = $string_utils:explode(cookie_data);
user = toobj(cookie_data[1]);
access_code = cookie_data[2];
access_code_found = 1;
break;
endif
endfor
if (!$object_utils:isa(user, $player))
"======================================================";
"==    Make sure user is actually a player object    ==";
"======================================================";
user = $no_one;
endif
if (access_code_found)
"Check to see if access code is valid";
if (strcmp(crypt(user.web_access_code, access_code), access_code))
"============================================================";
"== Web access code not valid. Check if special conditions ==";
"== apply so the user can have the page anyway             ==";
"============================================================";
if ($encore_web_utils:anonymous_access(requested_object, requested_verb, method))
access_granted = 1;
else
"=====================================================";
"== Authentication error, access code expired       ==";
"=====================================================";
display_login_page = 1;
message = "alert('Permission denied. Your Xpress access code has expired. Please return to the login page and reconnect.');window.close();";
endif
else
"===========================================";
"== Valid user and password, grant access ==";
"===========================================";
access_granted = 1;
endif
else
if ($encore_web_utils:anonymous_access(requested_object, requested_verb, method))
"==========================================================";
"== No web access code was found, but anonymous requests ==";
"== are enabled so let it pass anyway                    ==";
"==========================================================";
access_granted = 1;
else
display_login_page = 1;
"return message that cookie has expired and return user to login";
message = "alert('Permission denied. You must be logged in to access this object. If you are trying to log in, please enable cookies in your browser.');window.close();";
boot_player(user);
endif
endif
endif
return {access_granted, user, display_login_page, cookie, message};
"Last modified Fri Apr 13 18:50:23 2001 CDT by Wizard (#2).";
.
#32:5
"Copyright (C) 1999, Jan Rune Holmevik";
{conn} = args;
"Read and compile client header information";
client_headers = {};
while (line = read(conn))
field_name = line[1..index(line, ": ") - 1];
field_value = line[index(line, ": ") + 2..$];
client_headers = {@client_headers, field_name, field_value};
endwhile
return client_headers;
"Last modified Thu Apr  8 12:19:58 1999 CDT by Wizard (#2).";
.
#32:6
"Copyright (C) 1999, sindre.sorensen@rasmus.uib.no and Matthew Beerman";
{conn, client_headers} = args;
boundary = "";
content_type = client_headers[1 + ("Content-type" in client_headers)];
if ("" == content_type)
content_type = "application/x-www-form-urlencoded";
endif
if (index(content_type, "multipart/form-data"))
content_length = tonum(client_headers[1 + ("Content-Length" in client_headers)]);
boundary = content_type[9 + index(content_type, "boundary=")..$];
data = {};
line = "";
end_boundary = ("--" + boundary) + "--";
while (end_boundary != line)
if (`line = read(conn) ! ANY => 0' != 0)
data = {@data, line};
endif
endwhile
else
if ("application/x-www-form-urlencoded" == content_type)
if (data = read(conn))
endif
endif
endif
if (this.watchers)
this:log({"Received POST: ", data}, 7);
endif
return {data, content_type, boundary};
"Last modified Fri Oct  1 06:42:49 1999 CDT by Wizard (#2).";
.
#32:7
"Copyright (C) 1999, sindre.sorensen@rasmus.uib.no";
if (!player.wizard)
player:tell("Only wizards are allowed to watch the web-server log.");
return;
endif
if (!iobjstr)
player:tell("Usage: 'watch ", this.name, " <verboseness>");
player:tell("  verboseness  0 = off");
player:tell("  verboseness  1 = show errors only");
player:tell("  verboseness  7 = show all POSTs");
player:tell("  verboseness  8 = show all requests");
player:tell("  verboseness  9 = show all requests and what was returned");
player:tell("  verboseness 10 = show all data transmitted (not implemented)");
return;
else
verboseness = toint(iobjstr);
endif
for watcher_index in [1..length(this.watchers)]
watcher = this.watchers[watcher_index][1];
if (watcher == player)
if (0 == verboseness)
this.watchers = listdelete(this.watchers, watcher_index);
player:tell(("You are no longer watching the " + this.name) + " log.");
return;
else
this.watchers[watcher_index][2] = verboseness;
endif
player:tell(((this.name + "log verboseness level updated (now set to ") + tostr(verboseness)) + ")");
return;
endif
endfor
if (verboseness > 0)
this.watchers = {@this.watchers, {player, verboseness}};
player:tell((("You are now watching the " + this.name) + ". Verboseness level is ") + tostr(verboseness));
return;
endif
"Last modified Fri Oct  1 06:27:59 1999 CDT by Wizard (#2).";
.
#32:8
"Copyright (C) 1999, sindre.sorensen@rasmus.uib.no";
{log_entry, verboseness_level} = args;
for entry in (this.watchers)
{watcher, watcher_verboseness} = entry;
if (watcher_verboseness < verboseness_level)
break;
endif
for arg in (args)
for line in ($list_utils:flatten({arg}))
$command_utils:suspend_if_needed(0);
watcher:tell(tostr(this.name, ": ", connection_name(player), " ", ctime(), ": ", toliteral(line)));
endfor
endfor
endfor
"Last modified Fri Oct  1 06:28:24 1999 CDT by Wizard (#2).";
.
#32:9
"Copyright (C) 1999-2001, Jan Rune Holmevik and sindre.sorensen@uib.no";
"Adds server responses to HTTP transactions";
if (!caller_perms().wizard)
return E_PERM;
endif
{status, cookie, html, content_type, ?user_agent_request = ""} = args;
error_mess = header_fields = {};
status_response = this.(status)[1];
"Added to make sure content type is always included. Jan 9/21/01";
if (!content_type)
content_type = this.content_type;
endif
"==============================================================";
"== Assemble server status response and entity header fields ==";
"==============================================================";
header_fields = {@header_fields, tostr("Content-Type: ", content_type)};
header_fields = {@header_fields, tostr("Pragma: ", this.pragma)};
header_fields = {@header_fields, tostr("Expires: ", this.expires)};
header_fields = {@header_fields, tostr("Server: ", this.server_name, $core_version)};
response_header = $list_utils:append(status_response, header_fields);
if (cookie != "")
"==============================================================";
"== Add instructions for setting a client-side access cookie ==";
"==============================================================";
set_cookie = tostr("Set-Cookie: Access_Code=", cookie, " path=/;", " domain=", $network.site);
response_header = {@response_header, set_cookie};
endif
response_header = {@response_header, ""};
if (status != "200_OK")
"=====================================================================";
"== Return a page with an explanation of the error that has occured ==";
"=====================================================================";
error_mess = {@error_mess, @$httpd.(status)[2]};
error_mess = {@error_mess, user_agent_request ? "<BR>Failed request was: " + user_agent_request | ""};
error_mess = {@error_mess, "<BR>", html ? "Details: " | "No details available.", @html};
error_mess = {@error_mess, tostr("<HR SIZE=\"1\"><ADDRESS>", $httpd.server_name, $core_version, " web server at ", $network.site, " port ", $network.webport, "</ADDRESS>", "</BODY>", "</HTML>")};
html = error_mess;
endif
result = $list_utils:append(response_header, html);
return result;
"Last modified Tue Aug 21 05:52:40 2001 CDT by Wizard (#2).";
.
#32:10
"Copyright (C) 1999, Jan Rune Holmevik";
"Parse data to determine content type of outgoing file";
if (!caller_perms().wizard)
return E_PERM;
endif
line = args[1];
try
content_type = match(line, "HTTP-EQUIV=\"content-type\" CONTENT=\"%(.*%)\"");
content_type = substitute("%1", content_type);
except error (ANY)
content_type = this.content_type;
endtry
return content_type;
"Last modified Mon May 31 14:40:36 1999 CDT by Wizard (#2).";
.
#32:11
"Copyright (C) 1999, sindre.sorensen@rasmus.uib.no";
"Adapted from 'LambdaMOO Programmer's Manual'";
{error} = args;
tb = error[4];
result = {"Traceback: ", ""};
top = tb[1];
tb[1..1] = {};
result = {@result, tostr(top[1], ":", top[2], ", line ", top[6], ":", error[2])};
for fr in (tb)
result = {@result, tostr("... called from ", fr[1], ":", fr[2], ", line ", fr[6])};
endfor
result = {@result, "(End of traceback)"};
return result;
"Last modified Fri Oct  1 06:28:54 1999 CDT by Wizard (#2).";
.
#33:0
"   add(seq,start[,end]) => seq with range added.";
"remove(seq,start[,end]) => seq with range removed.";
"  both assume start<=end.";
remove = verb == "remove";
seq = args[1];
start = args[2];
s = (start == $minint) ? 1 | $list_utils:find_insert(seq, start - 1);
if (length(args) < 3)
return {@seq[1..s - 1], @((s + remove) % 2) ? {start} | {}};
else
e = $list_utils:find_insert(seq, after = args[3] + 1);
return {@seq[1..s - 1], @((s + remove) % 2) ? {start} | {}, @((e + remove) % 2) ? {after} | {}, @seq[e..$]};
endif
.
#33:1
":contains(seq,elt) => true iff elt is in seq.";
return ($list_utils:find_insert(@args) + 1) % 2;
.
#33:2
":complement(seq[,lower[,upper]]) => the sequence containing all integers *not* in seq.";
"If lower/upper are given, the resulting sequence is restricted to the specified range.";
"Bad things happen if seq is not a subset of [lower..upper]";
{seq, ?lower = $minint, ?upper = $nothing} = args;
if (upper != $nothing)
if (seq[$] >= (upper = upper + 1))
seq[$..$] = {};
else
seq[$ + 1..$] = {upper};
endif
endif
if (seq && (seq[1] <= lower))
return listdelete(seq, 1);
else
return {lower, @seq};
endif
.
#33:3
":union(seq1,seq2,...)        => union of all sequences...";
if ({} in args)
args = $list_utils:setremove_all(args, {});
endif
if (length(args) <= 1)
return args ? args[1] | {};
endif
return this:_union(@args);
.
#33:4
"tostr(seq [,delimiter]) -- turns a sequence into a string, delimiting ranges with delimiter, defaulting to .. (e.g. 5..7)";
{seq, ?separator = ".."} = args;
if (!seq)
return "empty";
endif
e = tostr((seq[1] == $minint) ? "" | seq[1]);
len = length(seq);
for i in [2..len]
e = e + ((i % 2) ? tostr(", ", seq[i]) | ((seq[i] == (seq[i - 1] + 1)) ? "" | tostr(separator, seq[i] - 1)));
endfor
return e + ((len % 2) ? separator | "");
.
#33:5
":for([n,]seq,obj,verb,@args) => for s in (seq) obj:verb(s,@args); endfor";
if (typeof(n = args[1]) == INT)
args = listdelete(args, 1);
else
n = 1;
endif
{seq, object, vname, @args} = args;
if (seq[1] == $minint)
return E_RANGE;
endif
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[$];
while (1)
if (typeof(object:(vname)(@listinsert(args, i, n))) == ERR)
return;
endif
i = i + 1;
endwhile
endif
.
#33:6
"extract(seq,array) => list of elements of array with indices in seq.";
{seq, array} = args;
if (alen = length(array))
e = $list_utils:find_insert(seq, 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
.
#33: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
.
#33:8
":fromlist(list) => corresponding sequence.";
return this:from_sorted_list($list_utils:sort(args[1]));
.
#33: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
.
#33:10
return (seq = args[1]) ? seq[1] | E_NONE;
.
#33:11
return (seq = args[1]) ? (length(seq) % 2) ? $minint - 1 | (seq[$] - 1) | E_NONE;
.
#33: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;
.
#33: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 = {toint(word), toint(word) + 1};
elseif (to)
if (to == 1)
start = $minint;
elseif (su:is_numeric(start = word[1..to - 1]))
start = toint(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 = toint(end)) >= start)
part = {start, end + 1};
else
part = {};
endif
else
return E_INVARG;
endif
parts = {@parts, part};
endfor
return this:union(@parts);
.
#33: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;
.
#33: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
.
#33:16
":range(start,end) => sequence corresponding to [start..end] range";
return ((start = args[1]) <= (end = args[2])) ? {start, end + 1} | {};
.
#33:17
":expand(seq,eseq[,include=0])";
"eseq is assumed to be a finite sequence consisting of intervals ";
"[f1..a1-1],[f2..a2-1],...  We map each element i of seq to";
"  i               if               i < f1";
"  i+(a1-f1)       if         f1 <= i < f2-(a1-f1)";
"  i+(a1-f1+a2-f2) if f2-(a1-f1) <= i < f3-(a2-f2)-(a1-f1)";
"  ...";
"returning the resulting sequence if include=0,";
"returning the resulting sequence unioned with eseq if include=1;";
{old, insert, ?include = 0} = args;
exclude = !include;
if (!insert)
return old;
elseif ((length(insert) % 2) || (insert[1] == $minint))
return E_TYPE;
endif
olast = length(old);
ilast = length(insert);
"... find first o for which old[o] >= insert[1]...";
ifirst = insert[i = 1];
o = $list_utils:find_insert(old, ifirst - 1);
if (o > olast)
return ((olast % 2) == exclude) ? {@old, @insert} | old;
endif
new = old[1..o - 1];
oe = old[o];
diff = 0;
while (1)
"INVARIANT: oe == old[o]+diff";
"INVARIANT: oe >= ifirst == insert[i]";
"... at this point we need to dispose of the interval ifirst..insert[i+1]";
if (oe == ifirst)
new = {@new, insert[i + ((o % 2) == exclude)]};
if (o >= olast)
return ((olast % 2) == exclude) ? {@new, @insert[i + 2..ilast]} | new;
endif
o = o + 1;
else
if ((o % 2) != exclude)
new = {@new, @insert[i..i + 1]};
endif
endif
"... advance i...";
diff = (diff + insert[i + 1]) - ifirst;
if ((i = i + 2) > ilast)
for oe in (old[o..olast])
new = {@new, oe + diff};
endfor
return new;
endif
ifirst = insert[i];
"... find next o for which old[o]+diff >= ifirst )...";
while ((oe = old[o] + diff) < ifirst)
new = {@new, oe};
if (o >= olast)
return ((olast % 2) == exclude) ? {@new, @insert[i..ilast]} | new;
endif
o = o + 1;
endwhile
endwhile
.
#33:18
":contract(seq,cseq)";
"cseq is assumed to be a finite sequence consisting of intervals ";
"[f1..a1-1],[f2..a2-1],...  From seq, we remove any elements that ";
"are in those ranges and map each remaining element i to";
"  i               if       i < f1";
"  i-(a1-f1)       if a1 <= i < f2";
"  i-(a1-f1+a2-f2) if a2 <= i < f3 ...";
"returning the resulting sequence.";
"";
"For any finite sequence cseq, the following always holds:";
"  :contract(:expand(seq,cseq,include),cseq)==seq";
{old, removed} = args;
if (!removed)
return old;
elseif (((rlen = length(removed)) % 2) || (removed[1] == $minint))
return E_TYPE;
endif
rfirst = removed[1];
ofirst = $list_utils:find_insert(old, rfirst - 1);
new = old[1..ofirst - 1];
diff = 0;
rafter = removed[r = 2];
for o in [ofirst..olast = length(old)]
while (old[o] > rafter)
if ((o - ofirst) % 2)
new = {@new, rfirst - diff};
ofirst = o;
endif
diff = (diff + rafter) - rfirst;
if (r >= rlen)
for oe in (old[o..olast])
new = {@new, oe - diff};
endfor
return new;
endif
rfirst = removed[r + 1];
rafter = removed[r = r + 2];
endwhile
if (old[o] < rfirst)
new = {@new, old[o] - diff};
ofirst = o + 1;
endif
endfor
return ((olast - ofirst) % 2) ? new | {@new, rfirst - diff};
.
#33:19
":_union(seq,seq,...)";
"assumes all seqs are nonempty and that there are at least 2";
nargs = length(args);
"args  -- list of sequences.";
"nexts -- nexts[i] is the index in args[i] of the start of the first";
"         interval not yet incorporated in the return sequence.";
"heap  -- a binary tree of indices into args/nexts represented as a list where";
"         heap[1] is the root and the left and right children of heap[i]";
"         are heap[2*i] and heap[2*i+1] respectively.  ";
"         Parent index h is <= both children in the sense of args[h][nexts[h]].";
"         heap[i]==0 indicates a nonexistant child; we fill out the array with";
"         zeros so that length(heap)>2*length(args).";
"...initialize heap...";
heap = {0, 0, 0, 0, 0};
nexts = {1, 1};
hlen2 = 2;
while (hlen2 < nargs)
nexts = {@nexts, @nexts};
heap = {@heap, @heap};
hlen2 = hlen2 * 2;
endwhile
for n in [-nargs..-1]
s1 = args[i = -n][1];
while ((hleft = heap[2 * i]) && (s1 > (m = min(la = args[hleft][1], (hright = heap[(2 * i) + 1]) ? args[hright][1] | $maxint))))
if (m == la)
heap[i] = hleft;
i = 2 * i;
else
heap[i] = hright;
i = (2 * i) + 1;
endif
endwhile
heap[i] = -n;
endfor
"...";
"...find first interval...";
h = heap[1];
rseq = {args[h][1]};
if (length(args[h]) < 2)
return rseq;
endif
current_end = args[h][2];
nexts[h] = 3;
"...";
while (1)
if (length(args[h]) >= nexts[h])
"...this sequence has some more intervals in it...";
else
"...no more intevals left in this sequence, grab another...";
h = heap[1] = heap[nargs];
heap[nargs] = 0;
if ((nargs = nargs - 1) > 1)
elseif (args[h][nexts[h]] > current_end)
return {@rseq, current_end, @args[h][nexts[h]..$]};
elseif ((i = $list_utils:find_insert(args[h], current_end)) % 2)
return {@rseq, current_end, @args[h][i..$]};
else
return {@rseq, @args[h][i..$]};
endif
endif
"...";
"...sink the top sequence...";
i = 1;
first = args[h][nexts[h]];
while ((hleft = heap[2 * i]) && (first > (m = min(la = args[hleft][nexts[hleft]], (hright = heap[(2 * i) + 1]) ? args[hright][nexts[hright]] | $maxint))))
if (m == la)
heap[i] = hleft;
i = 2 * i;
else
heap[i] = hright;
i = (2 * i) + 1;
endif
endwhile
heap[i] = h;
"...";
"...check new top sequence ...";
if (args[h = heap[1]][nexts[h]] > current_end)
"...hey, a new interval! ...";
rseq = {@rseq, current_end, args[h][nexts[h]]};
if (length(args[h]) <= nexts[h])
return rseq;
endif
current_end = args[h][nexts[h] + 1];
nexts[h] = nexts[h] + 2;
else
"...first interval overlaps with current one ...";
i = $list_utils:find_insert(args[h], current_end);
if (i % 2)
nexts[h] = i;
elseif (i > length(args[h]))
return rseq;
else
current_end = args[h][i];
nexts[h] = i + 1;
endif
endif
endwhile
.
#33:20
":intersection(seq1,seq2,...) => intersection of all sequences...";
if ((U = {$minint}) in args)
args = $list_utils:setremove_all(args, U);
endif
if (length(args) <= 1)
return args ? args[1] | U;
endif
return this:complement(this:_union(@$list_utils:map_arg(this, "complement", args)));
.
#34:0
if (caller_perms().wizard)
pass();
try
delete_verb(this, "is_readable_by");
delete_verb(this, "is_usable_by");
delete_verb(this, "mail_notify");
except (ANY)
endtry
"...remove references to ARB...";
if (l = this:length_all_msgs())
this:rm_message_seq({1, l});
endif
this:expunge_rmm();
this.mail_forward = {};
this.mail_notify = {player};
player.current_message = {@player.current_message, {this, 0, 0}};
for p in ({"moderator_forward", "moderator_notify", "writers", "readers", "expire_period", "last_used_time"})
this.(p) = $mail_recipient.(p);
endfor
this.moderated = 1;
else
return E_PERM;
endif
"Last modified Sun Aug 17 11:13:59 1997 CDT by Wizard (#2).";
.
#35:0
"$you:verb_sub(STR verbspec) -> returns verbspec conjugated for singular use as if `you' were saying it.";
return $gender_utils:get_conj(args[1], this);
x = args[1];
len = length(x);
if ((len > 3) && (rindex(x, "n't") == (len - 3)))
return this:verb_sub(x[1..len - 3]) + "n't";
endif
for y in (this.conjugations)
if (x == y[1])
return y[2];
endif
endfor
for y in ({{"ches", "ch"}, {"ies", "y"}, {"sses", "ss"}, {"shes", "sh"}, {"s", ""}})
if ((len > length(y[1])) && (rindex(x, y[1]) == ((len - length(y[1])) + 1)))
return x[1..len - length(y[1])] + y[2];
endif
endfor
return x;
.
#35:1
"$you:say_action(message [,who [,thing, [,where]]]).";
"announce 'message' with pronoun substitution as if it were just ";
"  where:announce_all($string_utils:pronoun_sub(message, who, thing, where)); ";
"except that who (player), dobj, and iobj 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";
{msg, ?who = player, ?thing = caller, ?where = who.location} = args;
you = this;
if (typeof(msg) == LIST)
tell = "";
for x in (msg)
tell = tell + ((typeof(x) == STR) ? x | x[random(length(x))]);
endfor
else
tell = msg;
endif
who:tell($string_utils:pronoun_sub(this:fixpos(tell, "%n"), you, thing, where));
if ($object_utils:has_callable_verb(where, "announce_all_but"))
where:announce_all_but({dobj, who, iobj}, $string_utils:pronoun_sub(tell, who, thing, where));
endif
if (valid(dobj) && (dobj != who))
x = dobj;
dobj = you;
x:tell($string_utils:pronoun_sub(this:fixpos(tell, "%d"), who, thing, where));
dobj = x;
endif
if (valid(iobj) && (!(iobj in {who, dobj})))
x = iobj;
iobj = you;
x:tell($string_utils:pronoun_sub(this:fixpos(tell, "%i"), who, thing, where));
iobj = x;
endif
.
#35:2
"This is horribly dwimmy.  E.g. %x's gets turned into your, %X's gets turned into Your, and %X'S gets turned into YOUR. --Nosredna";
upper = $string_utils:uppercase(args[2]);
allupper = upper + "'S";
upper = upper + "'s";
lower = $string_utils:lowercase(args[2]) + "'s";
return strsub(strsub(strsub(args[1], lower, "your", 1), upper, "Your", 1), allupper, "YOUR", 1);
.
#36:0
if (caller_perms().wizard)
pass();
this.mail_forward = {#2};
endif
.
#37:0
"find(string[,n]) => datum corresponding to string with the search starting at node \" \"+string[1..n], n defaults to 0 (root node), $ambiguous_match or $failed_match";
"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 $ambiguous_match, but if there is a unique datum associated with all of these strings :find(prefix) will return it rather than $ambiguous_match.";
"Assumes n<=length(string)";
{search, ?sofar = 0} = args;
rest = search;
prefix = search[1..sofar];
rest[1..sofar] = "";
info = this.(" " + prefix);
data = (verb == "find") ? this.data | 3;
if (i = search in info[3])
"...exact match for one of the strings in this node...";
return info[data][i];
elseif (index(info[1], rest) == 1)
"...ambiguous iff there's more than one object represented in this node..";
return this:_only(prefix, data);
elseif (index(rest, info[1]) != 1)
"...search string doesn't agree with common portion...";
return $failed_match;
elseif (index(info[2], search[nsofar = (sofar + length(info[1])) + 1]))
"...search string follows one of continuations leading to other nodes...";
return this:(verb)(search, nsofar);
else
"...search string may partially match one of the strings in this node...";
for i in [1..length(exacts = info[3])]
if (index(exacts[i], search) == 1)
return info[data][i];
endif
endfor
return $failed_match;
endif
.
#37:1
{search, ?sofar = 0} = args;
rest = search;
prefix = search[1..sofar];
rest[1..sofar] = "";
info = this.(" " + prefix);
if (i = search in info[3])
return info[this.data][i];
elseif ((length(rest) <= (common = length(info[1]))) || (rest[1..common] != info[1]))
return $failed_match;
elseif (index(info[2], search[(sofar + common) + 1]))
return this:find_exact(search, (sofar + common) + 1);
else
return $failed_match;
endif
.
#37:2
":find_all(string [,n=0])";
"assumes n <= length(string)";
{search, ?sofar = 0} = args;
rest = search;
prefix = search[1..sofar];
rest[1..sofar] = "";
info = this.(" " + prefix);
data = (verb == "find_all") ? this.data | 3;
if (index(info[1], rest) == 1)
"...return entire subtree.";
return this:((data == 3) ? "_every_key" | "_every")(prefix);
elseif (index(rest, info[1]) != 1)
"...common portion doesn't agree.";
return {};
elseif (index(info[2], rest[1 + (common = length(info[1]))]))
"...matching strings are in a subnode.";
return this:(verb)(search, (sofar + common) + 1);
else
"...matching string is in info[3].  length(rest) > common,";
"...so there will be at most one matching string.";
for i in [1..length(info[3])]
if (index(info[3][i], search) == 1)
return {info[data][i]};
endif
endfor
return {};
endif
.
#37:3
":_only(prefix,data) => if all strings in this node have the same datum, return it, otherwise, return $ambiguous_match.";
if (caller != this)
raise(E_PERM);
endif
{prefix, data} = args;
info = this.(" " + prefix);
if (data == 3)
"... life is much simpler if there's no separate datum.";
"... if there's more than one string here, we barf.";
if (info[2] || (length(info[3]) > 1))
return $ambiguous_match;
elseif (info[3])
return info[3][1];
else
"..this can only happen with the root node of an empty db.";
return $failed_match;
endif
elseif (info[2])
what = this:_only(tostr(prefix, info[1], info[2][1]), data);
if (what == $ambiguous_match)
return what;
endif
elseif (info[data])
what = info[data][1];
info[data] = listdelete(info[data], 1);
else
"..this can only happen with the root node of an empty db.";
return $failed_match;
endif
for x in (info[data])
if (what != x)
return $ambiguous_match;
endif
endfor
for i in [2..length(info[2])]
if (what != this:_only(tostr(prefix, info[1], info[2][i]), data))
return $ambiguous_match;
endif
endfor
return what;
.
#37:4
if (caller != this)
raise(E_PERM);
endif
info = this.(" " + args[1]);
prefix = args[1] + info[1];
r = $list_utils:remove_duplicates(info[4]);
for i in [1..length(branches = info[2])]
for new in (this:_every(prefix + branches[i]))
r = setadd(r, new);
endfor
endfor
return r;
.
#37:5
if (caller != this)
raise(E_PERM);
endif
info = this.(" " + args[1]);
prefix = args[1] + info[1];
r = info[3];
for i in [1..length(branches = info[2])]
for new in (this:_every_key(prefix + branches[i]))
r = setadd(r, new);
$command_utils:suspend_if_needed(0);
endfor
$command_utils:suspend_if_needed(0);
endfor
return r;
.
#37:6
":insert([n,]string,datum) -- inserts <string,datum> correspondence into tree starting at node \" \"+string[1..n], n defaulting to 0 (root node).";
"Assumes length(string) >= n";
"Returns {old_datum} (or 1) if there was a <string,old_datum> correspondence there before, otherwise returns 0";
if (!($perm_utils:controls(caller_perms(), this) || (caller == this)))
return E_PERM;
endif
has_datum = this.data > 3;
if (typeof(sofar = args[1]) == INT)
search = args[2];
datum = has_datum ? args[3] | 0;
else
search = sofar;
sofar = 0;
datum = has_datum ? args[2] | 0;
endif
prefix = search[1..sofar];
info = this.(" " + prefix);
if (i = search in info[3])
"... exact match ...";
if (has_datum)
previous = {info[this.data][i]};
info[this.data][i] = datum;
this:set_node(prefix, @info);
return previous;
else
return 1;
endif
endif
rest = search;
rest[1..sofar] = "";
if (index(rest, info[1]) != 1)
"... find where new string disagrees with common portion...";
c = $string_utils:common(rest, info[1]) + 1;
"... make a new node with a shorter common portion....";
this:make_node(prefix + info[1][1..c], @listset(info, info[1][c + 1..$], 1));
this:set_node(prefix, info[1][1..c - 1], info[1][c], {search}, @has_datum ? {{datum}} | {});
return 0;
elseif (rest == info[1])
".. new string == common portion, insert...";
info[3] = {@info[3], search};
if (has_datum)
info[this.data] = {@info[this.data], datum};
endif
this:set_node(prefix, @info);
return 0;
elseif (index(info[2], search[nsofar = (sofar + length(info[1])) + 1]))
"... new string matches pre-existing continuation. insert in subnode....";
return this:insert(nsofar, search, datum);
else
"... 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....";
s = search[1..nsofar];
for m in (info[3])
if (index(m, s) == 1)
i = m in info[3];
"... we know m != search ...";
"... string m has been blown away.  create new node ...";
cbegin = cafter = length(s) + 1;
cend = $string_utils:common(search, m);
this:make_node(s, m[cbegin..cend], "", {search, m}, @has_datum ? {{datum, info[this.data][i]}} | {});
this:set_node(prefix, info[1], info[2] + s[nsofar], listdelete(info[3], i), @has_datum ? {listdelete(info[this.data], i)} | {});
return 0;
endif
endfor
"... new string hasn't blown away any of the exact matches, insert it as a new exact match...";
info[3] = {search, @info[3]};
if (has_datum)
info[this.data] = {datum, @info[this.data]};
endif
this:set_node(prefix, @info);
return 0;
endif
.
#37:7
":delete(string[,n]) deletes any <string,something> pair from the tree starting at node \" \"+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 (!($perm_utils:controls(caller_perms(), this) || (caller == this)))
return E_PERM;
endif
{search, ?sofar = 0} = args;
rest = search;
prefix = search[1..sofar];
rest[1..sofar] = "";
info = this.(" " + prefix);
if (i = search in info[3])
previous = {info[this.data][i]};
info[3] = listdelete(info[3], i);
if (this.data > 3)
info[this.data] = listdelete(info[this.data], i);
endif
elseif ((rest == info[1]) || ((index(rest, info[1]) != 1) || (!index(info[2], search[d = (sofar + length(info[1])) + 1]))))
"... hmm string isn't in here...";
return 0;
elseif ((previous = this:delete(search, d)) && (length(previous) > 1))
i = index(info[2], search[d]);
info[2][i..i] = "";
info[3] = {previous[2], @info[3]};
if (this.data > 3)
info[this.data] = {previous[3], @info[this.data]};
endif
previous = previous[1..1];
else
return previous;
endif
if ((!prefix) || ((length(info[3]) + length(info[2])) != 1))
this:set_node(prefix, @info);
return previous;
elseif (info[3])
this:kill_node(prefix);
return {@previous, info[3][1], info[this.data][1]};
else
sub = this.(" " + (p = tostr(prefix, info[1], info[2])));
this:kill_node(p);
this:set_node(prefix, @listset(sub, tostr(info[1], info[2], sub[1]), 1));
return previous;
endif
.
#37:8
":delete2(string,datum[,n]) deletes the pair <string,datum> from the tree starting at node \" \"+string[1..n], n defaulting to 0 (root node)";
"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 ";
" ";
"  if(this:find_exact(string)==datum) ";
"    this:delete(string); ";
"  endif";
if (!($perm_utils:controls(caller_perms(), this) || (caller == this)))
return E_PERM;
endif
{search, datum, ?sofar = 0} = args;
rest = search;
prefix = search[1..sofar];
rest[1..sofar] = "";
info = this.(" " + prefix);
if (i = search in info[3])
previous = {info[this.data][i]};
if (previous[1] != datum)
return previous;
endif
info[3] = listdelete(info[3], i);
if (this.data > 3)
info[this.data] = listdelete(info[this.data], i);
endif
elseif ((rest == info[1]) || ((index(rest, info[1]) != 1) || (!index(info[2], search[d = (sofar + length(info[1])) + 1]))))
"... hmm string isn't in here...";
return 0;
elseif ((previous = this:delete2(search, datum, d)) && (length(previous) > 1))
i = index(info[2], search[d]);
info[2][i..i] = "";
info[3] = {previous[2], @info[3]};
if (this.data > 3)
info[this.data] = {previous[3], @info[this.data]};
endif
previous = previous[1..1];
else
return previous;
endif
if ((!prefix) || ((length(info[3]) + length(info[2])) != 1))
this:set_node(prefix, @info);
return previous;
elseif (info[3])
this:kill_node(prefix);
return {@previous, info[3][1], info[this.data][1]};
else
sub = this.(" " + (p = tostr(prefix, info[1], info[2])));
this:kill_node(p);
this:set_node(prefix, @listset(sub, tostr(info[1], info[2], sub[1]), 1));
return previous;
endif
.
#37:9
return (caller != this) ? E_PERM | (this.(" " + args[1]) = listdelete(args, 1));
.
#37:10
"WIZARDLY";
return (caller != this) ? E_PERM | add_property(this, " " + args[1], listdelete(args, 1), {$generic_db.owner, this.node_perms});
.
#37:11
"WIZARDLY";
return (caller != this) ? E_PERM | delete_property(this, " " + args[1]);
.
#37:12
"WIZARDLY";
if (!($perm_utils:controls(caller_perms(), this) || (caller == this)))
return E_PERM;
endif
if (args && ((d = args[1]) in {3, 4}))
this.data = d;
endif
root = {"", "", {}, @(this.data > 3) ? {{}} | {}};
"...since the for loop contains a suspend, we want to keep people";
"...from getting at properties which are now garbage but which we";
"...haven't had a chance to wipe yet.  Somebody might yet succeed";
"...in adding something; thus we have the outer while loop.";
this:set_node("", 37);
while (this.(" ") != root)
this:set_node("", @root);
for p in (properties(this))
if ((p[1] == " ") && (p != " "))
delete_property(this, p);
endif
"...Bleah; db is inconsistent now....";
"...At worst someone will add something that references an";
"...existing property.  He will deserve to die...";
$command_utils:suspend_if_needed(0);
endfor
endwhile
.
#37:13
if (!($perm_utils:controls(caller_perms(), this) || (caller == this)))
return E_PERM;
endif
this:_kill_subtrees("", 0);
this:clearall(@args);
.
#37:14
":_kill_subtree(node,count)...wipes out all subtrees";
"...returns count + number of nodes removed...";
if (!($perm_utils:controls(caller_perms(), this) || (caller == this)))
return E_PERM;
endif
info = this.(" " + (prefix = args[1]));
count = args[2];
if ((ticks_left() < 500) || (seconds_left() < 2))
player:tell("...", count);
suspend(0);
endif
for i in [1..length(info[2])]
count = this:_kill_subtrees(n = tostr(prefix, info[1], info[2][i]), count) + 1;
this:kill_node(n);
endfor
return count;
.
#37:15
info = this.(" " + (prefix = (args || {""})[1]));
depth = 0;
string = prefix;
if ((ticks_left() < 500) || (seconds_left() < 2))
player:tell("...", prefix);
suspend(0);
endif
for i in [1..length(info[2])]
if ((r = this:depth(tostr(prefix, info[1], info[2][i])))[1] > depth)
depth = r[1];
string = r[2];
endif
endfor
return {depth + 1, string};
.
#37:16
info = this.(" " + (prefix = args[1]));
count = length(info[3]) + args[2];
if ((ticks_left() < 500) || (seconds_left() < 2))
player:tell("...", count);
suspend(0);
endif
for i in [1..length(info[2])]
count = this:count_entries(tostr(prefix, info[1], info[2][i]), count);
endfor
return count;
.
#37:17
info = this.(" " + (prefix = args[1]));
count = args[2];
for s in (info[3])
count = count + length(s);
endfor
if ((ticks_left() < 500) || (seconds_left() < 2))
player:tell("...", count);
suspend(0);
endif
for i in [1..length(info[2])]
count = this:count_chars(tostr(prefix, info[1], info[2][i]), count);
endfor
return count;
.
#37:18
"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
.
#38: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.";
exp = args[1];
if (this:bad_eval(exp))
return E_PERM;
endif
set_task_perms(this);
if (exp[1] != ";")
return eval(tostr("this=", caller, "; return ", exp, ";"));
else
return eval(tostr("this=", caller, ";", exp, ";"));
endif
.
#38:1
return 0;
.
#38: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.";
exp = args[1];
if (this:bad_eval(exp))
return E_PERM;
endif
set_task_perms(this);
if (exp[1] != ";")
return $code_utils:eval_d(tostr("this=", caller, "; return ", exp, ";"));
else
return $code_utils:eval_d(tostr("this=", caller, ";", exp, ";"));
endif
.
#38: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);
return args[1]:(args[2])(@args[3]);
.
#38:4
"Return 1 if this code will call fork, suspend, or eval.";
"At present it's overzealous---it should check for delimited uses of the above calls, in case someone has a variable called prevalent.";
exp = args[1];
if ((index(exp, "eval") || index(exp, "fork")) || index(exp, "suspend"))
"Well, they had one of the evil words in here.  See if it was in a quoted string or not -- we want to permit player:tell(\"Gentlemen use forks.\")";
for bad in ({"eval", "fork", "suspend"})
l = index(exp, bad);
if (l && (!$code_utils:inside_quotes(exp[1..l])))
return 1;
endif
endfor
endif
return 0;
.
#38:5
if (!caller_perms().wizard)
return E_PERM;
else
return pass(@args);
endif
.
#39: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
"...N.B. clearall suspends, therefore we put the .frozen mark on FIRST...";
this.frozen = 1;
this:clearall();
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, " ") || index(a, "	"))
"don't bother, space or tab";
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;
.
#39: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] == " ")
nlist = this.(p)[3];
olist = this.(p)[4];
for i in [1..length(nlist)]
if (valid(olist[i]) && (is_player(olist[i]) && (nlist[i] in olist[i].aliases)))
else
player:tell(".", p[2..$], " <- ", nlist[i], " ", olist[i]);
endif
endfor
endif
endfor
player:tell("done.");
.
#39:2
if (caller_perms().wizard)
pass();
this.reserved = {};
this:load();
endif
.
#39:3
":available(name,who) => 1 if a name is available for use, 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.";
{name, ?target = valid(caller) ? caller | player} = args;
if ((name in this.stupid_names) || (name in this.reserved))
return 0;
elseif (((((!name) || index(name, " ")) || index(name, "\\")) || index(name, "\"")) || index(name, "	"))
return 0;
elseif (index("*#()", name[1]))
return 0;
elseif (valid(who = this:find_exact(name)) && is_player(who))
return who;
elseif ($object_utils:has_callable_verb($local, "legal_name") && (!$local:legal_name(name, target)))
return 0;
else
return 1;
endif
.
#39: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]);
set_task_perms($byte_quota_utils:task_perms());
suspend(0);
if (this.frozen != 1)
player:tell("...argh... restarting $player_db:load...");
fork (0)
this:load();
endfork
kill_task(task_id());
endif
endif
.
#39:5
":why_bad_name(player, namespec) => Returns a message explaining why a player name change is invalid.  Stolen from APHiD's #15411:name_okay.";
"Modified by Jan 03/24/00. Removed quote marks in order to let Xpress access this verb.";
who = args[1];
name = $building_utils:parse_names(args[2])[1];
si = index(name, " ");
qi = index(name, "\"");
bi = index(name, "\\");
if ((si || qi) || bi)
return tostr("You may not use a name containing ", $string_utils:english_list({@si ? {"spaces"} | {}, @qi ? {"quotation marks"} | {}, @bi ? {"backslashes"} | {}}, "ERROR", " or "), ".  Try \"", strsub(strsub(strsub(name, " ", "_"), "\"", "'"), "\\", "/"), "\" instead.");
elseif (name == "")
return tostr("You may not use a blank name.");
elseif (i = index("*#()", name[1]))
return tostr("You may not begin a name with the ", "*#()"[i], " character.");
elseif (name in $player_db.stupid_names)
return tostr("The name ", name, " would probably cause problems in command parsing or similar usage.");
elseif (name in $player_db.reserved)
return tostr("The name ", name, " is reserved.");
elseif (length(name) > $login.max_player_name)
return tostr("The name ", name, " is too long.  Maximum name length is ", $login.max_player_name, " characters.");
elseif ((valid(match = $player_db:find_exact(name)) && is_player(match)) && (who != match))
return tostr("The name ", name, " is already being used by ", match.name, "(", match, ").");
elseif ($player_db.frozen)
return tostr("$player_db is not accepting new changes at the moment.");
endif
"Last modified Fri Mar 24 21:05:34 2000 CST by Wizard (#2).";
.
#40:0
if (typeof(mf = this.(verb)) == STR)
return $string_utils:pronoun_sub(mf, @args);
else
return mf;
endif
.
#40:1
":receive_message(msg,from)";
if ((!$perm_utils:controls(caller_perms(), this)) && (caller != this))
return E_PERM;
endif
if (this:mail_option("no_dupcc", args[1][1], args[1][2]))
"pass to :mail_option the TEXT versions of who the message is from and to";
recipients = setremove($mail_agent:parse_address_field(args[1][3]), this);
for x in (recipients)
if (this:get_current_message(x))
return 0;
endif
endfor
endif
if (this:mail_option("netmail"))
msg = args[1];
message = {"Forwarded: " + msg[4], "Original-date: " + ctime(msg[1]), "Original-From: " + msg[2], "Original-To: " + msg[3], ((("Reply-To: " + $string_utils:substitute(args[2].name, {{"@", "%"}})) + "@") + $network.moo_name) + ".moo.mud.org"};
for x in (msg[5..$])
message = {@message, @$generic_editor:fill_string(x, this:linelen())};
endfor
if (this:send_self_netmail(message, @listdelete(args, 1)) == 0)
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;
.
#40:2
":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], "--------------------------"});
.
#40:3
"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
.
#40:4
return $mail_agent:msg_summary_line(@args);
.
#40:5
":msg_text(@msg) => list of strings.";
"msg is a mail message (in the usual transmission format) being read BY this player.";
"The default version of recipient:msg_full_text calls this to obtain the actual list of strings to display.  (this is a badly named verb).";
"returns the actual list of strings to display.";
return $mail_agent:to_text(@args);
.
#40:6
":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 (!$object_utils:connected(this))
return;
elseif (!((caller in {this, $mail_agent}) || $perm_utils:controls(caller_perms(), this)))
return E_PERM;
else
{from, recipients, ?msgnums = {}} = args;
from_name = $mail_agent:name(from);
"... 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)), "");
this:notify(tostr("You have new mail (", msgnums[t], ") from ", from_name, namelist ? " which was also sent to " + namelist | "", "."));
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))}, "");
this:tell(tostr(namelist, (length(recipients) == 1) ? " has" | " have", " just been sent new mail by ", from_name, "."));
endif
endif
.
#40:7
":current_message([recipient])";
" => current message number for the given recipient (defaults to this).";
" => 0 if we have no record of that recipient";
"      or current message happens to be 0.";
"This verb is mostly obsolete; consider using :get_current_message()";
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
.
#40:8
":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)))
raise(E_PERM);
elseif ((!args) || (args[1] == this))
if (length(this.current_message) < 2)
"Whoops, this got trashed---fix it up!";
this.current_message = {0, time(), @this.current_message};
endif
return this.current_message[1..2];
elseif (a = $list_utils:assoc(args[1], this.current_message))
return a[2..3];
else
return 0;
endif
.
#40:9
":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)))
raise(E_PERM);
endif
{recip, ?number = E_NONE, ?date = 0} = args;
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
.
#40:10
":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 = args[1];
cm = this.current_message;
if (length(args) > 1)
i = max(2, min(args[2], length(cm)));
else
i = 0;
endif
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
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..$]};
elseif (j > (i + 1))
this.current_message = {@cm[1..i], cm[j], @cm[i + 1..j - 1], @cm[j + 1..$]};
endif
endif
else
this.current_message = listappend(cm, {recip, 0, 0}, @i ? {i} | {});
endif
.
#40:11
":kill_current_message(recipient)";
"entirely forgets current message for this recipient...";
"Returns true iff successful.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
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
.
#40:12
":current_folder() => default folder to use, always an object, usually `this'";
set_task_perms(caller_perms());
return ((!this:mail_option("sticky")) || this.current_folder) && this;
.
#40:13
set_task_perms(caller_perms());
return this.current_folder = args[1];
.
#40:14
":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, prep, ?extra = 0} = args;
p = prepstr in args;
if (prepstr != prep)
"...unexpected preposition...";
if (extra && (!index(prepstr, " ")))
return {folder, args[1..p - 1], args[p..$]};
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, this), fname))
"...bogus mail folder...";
else
return {folder, args[1..p - 1], args[p + 2..$]};
endif
return 0;
.
#40:15
":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, default, prep, ?extra = 0} = args;
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("I don't understand `", 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>", $gender_utils:get_conj("has", folder)}};
else
subst = {{"%f", $mail_agent:name(folder)}, {"%<has>", "has"}};
endif
player:tell($string_utils:substitute(pms, {@subst, {"%%", "%"}}));
return 0;
.
#40:16
"@mail <msg-sequence>                --- as in help @mail";
"@mail <msg-sequence> on <recipient> --- shows mail on mailing list or player.";
set_task_perms(valid(cp = caller_perms()) ? cp | player);
if (p = this:parse_mailread_cmd("@mail", args, this:mail_option("@mail") || $mail_agent.("player_default_@mail"), "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
.
#40:17
"@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(cp = caller_perms()) ? cp | player);
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._mail_task = task_id();
if (cur = folder:display_seq_full(msg_seq, tostr("Message %d", (folder == this) ? "" | (" on " + $mail_agent:name(folder)), ":")))
if (verb != "@peek")
this:set_current_message(folder, @cur);
endif
endif
endif
.
#40:18
set_task_perms(player.owner);
if (dobjstr && (!(n = toint(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
.
#40:19
"@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($mail_agent:name(folder), ":  ", 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
.
#40:20
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
.
#40:21
"@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], " ");
keep = 0;
if ((!dobjstr) || (keep = index("keep", dobjstr) == 1))
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)(@keep ? {keep} | {}))
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($mail_agent:name(folder), ":  ", msg_seq));
else
player:notify(tostr("No messages to ", (do == "expunge_rmm") ? "expunge from " | "restore to ", $mail_agent:name(folder)));
endif
.
#40:22
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);
.
#40:23
"@answer <msg> [on *<recipient>] [<flags>...]";
set_task_perms(who = valid(caller_perms()) ? caller_perms() | player);
if (p = this:parse_mailread_cmd(verb, args, "cur", "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
.
#40:24
"@forward <msg> [on *<recipient>] to <recipient> [<recipient>...]";
set_task_perms(valid(cp = caller_perms()) ? cp | 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)))
subject = tostr("[", from, ":  `", msgtxt[h + 1][1..min(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
.
#40:25
$mail_editor:invoke($gripe_recipients, "@gripe", "@gripe: " + argstr);
.
#40:26
subject = tostr($string_utils:capitalize(verb[2..$]), ":  ", (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
.
#40:27
"@skip [*<folder/mailing_list>...]";
"  sets your last-read time for the given lists to now, indicating your";
"  disinterest in any new messages that might have appeared recently.";
set_task_perms(player);
current_folder = this:current_folder();
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...  No, try anyway.";
if (this:kill_current_message(this:my_match_object(a)))
player:notify("Invalid folder, but found it subscribed anyway.  Removed.");
endif
else
lseq = folder:length_all_msgs();
unread = (n = this:get_current_message(folder)) ? folder:length_date_gt(n[2]) | lseq;
this:set_current_message(folder, lseq && folder:messages_in_seq({lseq, lseq + 1})[1][1], time());
player:notify(tostr(unread ? tostr("Ignoring ", unread) | "No", " unread message", (unread != 1) ? "s" | "", " on ", $mail_agent:name(folder)));
if (current_folder == folder)
this:set_current_folder(this);
endif
endif
endfor
.
#40:28
"@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.";
"@unsubscribed";
"  prints out available mailing lists you aren't already subscribed to.";
"@subscribe-quick and @unsubscribed-quick";
"  prints out same as above except without mail list descriptions, just names.";
set_task_perms(player);
quick = 0;
if (qi = index(verb, "-q"))
verb = verb[1..qi - 1];
quick = 1;
endif
fname = {@args, 0}[1];
if (!fname)
ml = $list_utils:slice(this.current_message[3..$]);
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)) && ((verb != "@unsubscribed") || (!(c in ml))))
c:look_self(quick);
endif
endfor
player:notify(tostr("-------- end of ", verb, " -------"));
return;
elseif (verb == "@unsubscribed")
player:notify("@unsubscribed does not take arguments.");
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;
i = 0;
beforeafter = 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 = 1;
else
player:notify("This mail recipient does not allow immediate notification.");
endif
else
folder:delete_notify(this);
notification = 0;
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
this:make_current_message(folder, @i ? {i} | {});
len = folder:length_all_msgs();
player:notify(tostr($mail_agent:name(folder), " has ", len, " message", (len == 1) ? "" | "s", ".", notification ? "  You will be notified immediately when new messages are posted." | "  Notification of new messages will be printed when you connect."));
this:set_current_folder(folder);
.
#40:29
set_task_perms((caller == this) ? this.owner | caller_perms());
this:set_current_folder(this);
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)};
.
#40:30
set_task_perms((caller == this) ? this.owner | caller_perms());
which = {};
cm = this.current_message;
cm[1..2] = (verb == "@rn") ? {{this, @cm[1..2]}} | {};
all = verb == "@subscribed";
for n in (cm)
rcpt = n[1];
if (n == $news)
"... $news is handled separately ...";
elseif ($mail_agent:is_recipient(rcpt))
if ((nmsgs = n[1]:length_date_gt(n[3])) || all)
which = {@which, {n[1], nmsgs}};
endif
else
player:notify(tostr("Bogus recipient ", rcpt, " removed from .current_message."));
this.current_message = setremove(this.current_message, n);
endif
$command_utils:suspend_if_needed(0);
endfor
if (which)
player:notify(tostr((verb == "@subscribed") ? "You are subscribed to the following" | "There is new activity on the following", (length(which) > 1) ? " lists:" | " list:"));
for w in (which)
name = (w[1] == this) ? " me" | $mail_agent:name(w[1]);
player:notify(tostr($string_utils:left("    " + name, 40), " ", w[2], " new message", (w[2] == 1) ? "" | "s"));
$command_utils:suspend_if_needed(0);
endfor
if (verb != "check_mail_lists")
player:notify("-- End of listing");
endif
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;
.
#40:31
":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
.
#40:32
"@unsubscribe [*<folder/mailing_list> ...]";
"entirely removes the record of your current message for the named folders,";
"indicating your disinterest in anything that might appear there in the future.";
set_task_perms(player);
unsubscribed = {};
current_folder = this:current_folder();
for a in (args || {0})
if (a != 0)
folder = $mail_agent:match_recipient(a);
if (folder == $failed_match)
folder = this:my_match_object(a);
endif
else
folder = current_folder;
endif
if (!valid(folder))
"...bogus folder name...  try removing it anyway.";
if (this:kill_current_message(folder))
player:notify("Invalid folder, but found it subscribed anyway.  Removed.");
else
$mail_agent:match_failed(folder, a);
endif
elseif (folder == this)
player:notify(tostr("You can't ", verb, " yourself."));
else
if (!this:kill_current_message(folder))
player:notify(tostr("You weren't subscribed to ", $mail_agent:name(folder)));
if ($object_utils:isa(folder, $mail_recipient))
result = folder:delete_notify(this);
if ((typeof(result) == LIST) && (result[1] == this))
player:notify("Removed you from the mail notifications list.");
endif
endif
else
unsubscribed = {@unsubscribed, folder};
if ($object_utils:isa(folder, $mail_recipient))
folder:delete_notify(this);
endif
endif
endif
endfor
if (unsubscribed)
player:notify(tostr("Forgetting about ", $string_utils:english_list($list_utils:map_arg($mail_agent, "name", unsubscribed))));
if (current_folder in unsubscribed)
this:set_current_folder(this);
endif
endif
.
#40:33
":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
.
#40:34
"@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(cp = caller_perms()) ? cp | 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 ((!this:mail_option("expert_netfwd")) && (!$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:notify("Attempting to send network mail...");
player._mail_task = task_id();
multiple_vals = this:format_for_netforward(folder:messages_in_seq(msg_seq), folderstr);
netmail = multiple_vals[1];
header = multiple_vals[2];
reason = player:send_self_netmail({header, @netmail});
player:notify((reason == 0) ? tostr("@netforward of ", header, " completed.") | tostr("@netforward failed: ", reason, "."));
endif
.
#40:35
"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 (#4292)";
"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("The ", verb, " command takes no arguments."));
$command_utils:read_lines();
return;
elseif (this != player)
player:notify(tostr("You can't use ", this.pp, " ", verb, " verb."));
$command_utils:read_lines();
return;
endif
msg = $command_utils:read_lines();
end_head = ("" in msg) || (length(msg) + 1);
from = this;
subject = "";
replyto = "";
rcpts = {};
body = msg[end_head + 1..$];
for i in [1..end_head - 1]
line = msg[i];
if (index(line, "Subject:") == 1)
subject = $string_utils:trim(line[9..$]);
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..$]))
player:notify("No address found in Reply-to: line");
return;
endif
elseif (index(line, "From:") == 1)
"... :send_message() bombs if designated sender != player ...";
if (!(from = $mail_agent:parse_address_field(line)))
player:notify("No sender found in From: line");
return;
elseif (length(from) > 1)
player:notify("Multiple senders?");
return;
endif
from = from[1];
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(from, 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
.
#40:36
"@keep-mail [<msg-sequence>|none] [on <recipient>]";
"marks the indicated messages as `kept'.";
set_task_perms(valid(cp = caller_perms()) ? cp | player);
if (!args)
return player:notify("Usage:  @keep-mail [<msg-sequence>|none] [on <recipient>]");
elseif (args[1] == "none")
args[1..1] = {};
if (!(pfs = this:parse_folder_spec(verb, args, "on", 0)))
return;
elseif (pfs[2])
player:notify(tostr(verb, " <message-sequence> or `none', but not both."));
return;
endif
this:set_current_folder(folder = pfs[1]);
if (e = folder:keep_message_seq({}))
player:notify(tostr("Messages on ", $mail_agent:name(folder), " are no longer marked as kept."));
else
player:notify(tostr(e));
endif
return;
elseif (p = this:parse_mailread_cmd(verb, args, "", "on"))
if ((folder = p[1]) != this)
"... maybe I'll take this clause out some day...";
player:notify(tostr(verb, " can only be used on your own mail collection."));
return;
endif
this:set_current_folder(folder);
if (e = folder:keep_message_seq(msg_seq = p[2]))
player:notify(tostr("Message", match(e, "[.,]") ? "s " | " ", e, " now marked as kept."));
elseif (typeof(e) == ERR)
player:notify(tostr(e));
else
player:notify(tostr(((seq_size = $seq_utils:size(msg_seq)) == 1) ? "That message is" | "Those messages are", " already marked as kept."));
endif
endif
.
#40:37
":my_match_recipient(string) => matches string against player's private mailing lists.";
if (!(string = args[1]))
return $nothing;
elseif (string[1] == "*")
string = string[2..$];
endif
return $string_utils:match(string, this.mail_lists, "aliases");
.
#40:38
set_task_perms(caller_perms());
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
else
seq = this:expirable_msg_seq();
if (seq)
this:rm_message_seq(seq);
return this:expunge_rmm();
else
return 0;
endif
endif
.
#40:39
":msg_full_text(@msg) => list of strings.";
"msg is a mail message (in the usual transmission format).";
"display_seq_full calls this to obtain the actual list of strings to display.";
return player:msg_text(@args);
"default is to leave it up to the player how s/he wants it to be displayed.";
.
#40:40
"@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...";
"...";
{msgnum, msgtxt} = folder:messages_in_seq(sequence)[1];
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
.
#40:41
":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 = args[1];
cm = this.current_message;
if (length(args) > 1)
i = max(2, min(args[2], length(cm)));
else
i = 0;
endif
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
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..$]};
elseif (j > (i + 1))
this.current_message = {@cm[1..i], cm[j], @cm[i + 1..j - 1], @cm[j + 1..$]};
endif
endif
else
this.current_message = listappend(cm, {recip, 0, 0}, @i ? {i} | {});
endif
.
#40:42
"Return a sequence indicating the expirable messages for this player.";
set_task_perms(caller_perms());
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
elseif (!(curmsg = this:get_current_message(this)))
"No messages!  Don't even try.";
return {};
elseif (0 >= (period = this:mail_option("expire") || $mail_agent.player_expire_time))
"...no expiration allowed here...";
return {};
else
return $seq_utils:remove(this:unkept_msg_seq(), 1 + this:length_date_le(min(time() - period, curmsg[2] - 86400)));
"... the 86400 is pure fudge...";
endif
.
#40:43
"Takes a message sequence (the actual messages, not just the sequence describing it) and grovels over it filling text etc.  Returns a two valued list: {formatted message, header for same}";
set_task_perms(caller_perms());
{message_seq, folderstr} = args;
netmail = {};
linelen = this:linelen();
maxmsg = minmsg = 0;
for msg in (message_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..$])
if (typeof(line) != STR)
"I don't know how this can happen, but apparently non-strings can end up in the mail message.  So, cope.";
line = tostr(line);
endif
lines = {@lines, @$generic_editor:fill_string(line, linelen)};
$command_utils:suspend_if_needed(0);
endfor
netmail = {@netmail, @lines, "", "--------------------------", "", ""};
endfor
header = tostr($network.MOO_name, " Message(s) ", minmsg, @(minmsg != maxmsg) ? {" - ", maxmsg} | {}, folderstr);
return {netmail, header};
.
#40:44
"Takes a message sequence (the actual messages, not just the sequence describing it) and grovels over it filling text etc.  Returns a two valued list: {formatted message, header for same}";
set_task_perms(caller_perms());
{message_seq, folderstr} = args;
netmail = {};
linelen = this:linelen();
maxmsg = minmsg = 0;
for msg in (message_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..$])
if (typeof(line) != STR)
"I don't know how this can happen, but apparently non-strings can end up in the mail message.  So, cope.";
line = tostr(line);
endif
lines = {@lines, @$generic_editor:fill_string(line, linelen)};
$command_utils:suspend_if_needed(0);
endfor
netmail = {@netmail, @lines, "", "--------------------------", "", ""};
endfor
header = tostr($network.MOO_name, " Message(s) ", minmsg, @(minmsg != maxmsg) ? {" - ", maxmsg} | {}, folderstr);
return {netmail, header};
.
#40:45
"@nn  -- reads the first new message on the first mail_recipient (in .current_message) where new mail exists.";
set_task_perms(valid(cp = caller_perms()) ? cp | player);
cm = this.current_message;
cm[1..2] = {{this, @cm[1..2]}};
for n in (cm)
if (new = n[1]:length_date_gt(n[3]))
next = (n[1]:length_all_msgs() - new) + 1;
this:set_current_folder(folder = n[1]);
this._mail_task = task_id();
cur = folder:display_seq_full({next, next + 1}, tostr("Message %d", " on ", $mail_agent:name(folder), ":"));
this:set_current_message(folder, @cur);
return;
endif
endfor
player:tell("No News (is good news)");
.
#40:46
"@unread <msg> [on *<recipient>]  -- resets last-read-date for recipient to just before the first of the indicated messages.";
set_task_perms(player);
if (p = this:parse_mailread_cmd("@unread", args, "cur", "on"))
this:set_current_folder(folder = p[1]);
msg_ord = $seq_utils:first(msg_seq = p[2]);
msgdate = folder:messages_in_seq(msg_ord)[2][1] - 1;
if ((!(cm = this:get_current_message(folder))) || (cm[2] < msgdate))
player:notify("Already unread.");
else
if (folder == this)
this.current_message[2] = msgdate - 1;
else
this:kill_current_message(folder);
this:set_current_message(folder, cm[1], min(cm[2], msgdate));
endif
folder:display_seq_headers({msg_ord, msg_ord + 1}, cm[1], msgdate);
endif
endif
.
#40:47
"@refile/@copym*ail <msg-sequence> [on <recipient>] to <recipient>";
"@refile will delete the messages from the source folder.  @copym does not.";
"I'm not happy with this one, yet...";
set_task_perms(player);
if (!(p = this:parse_mailread_cmd("@refile", args, "cur", "on", 1)))
"...lose...";
elseif ((length(p[4]) != 2) || (p[4][1] != "to"))
player:notify(tostr("Usage:  ", verb, " [<message numbers>] [on <folder>] to <folder>"));
elseif ($mail_agent:match_failed(dest = $mail_agent:match_recipient(p[4][2]), p[4][2]))
"...bogus destination folder...";
else
source = p[1];
msg_seq = p[2];
for m in (source:messages_in_seq(msg_seq))
if (!(e = dest:receive_message(m[2], source)))
player:notify(tostr("Copying msg. ", m[1], ":  ", e));
return;
endif
$command_utils:suspend_if_needed(0);
endfor
if (refile = verb == "@refile")
if (typeof(e = source:rm_message_seq(msg_seq)) == ERR)
player:notify(tostr("Deleting from ", source, ":  ", e));
endif
endif
count = tostr(n = $seq_utils:size(msg_seq), " message", (n == 1) ? "" | "s");
fname = (source == this) ? "" | tostr(is_player(source) ? " from " | " from *", source.name, "(", source, ")");
suffix = tostr(is_player(dest) ? " to " | " to *", dest.name, "(", dest, ").");
player:notify(tostr(refile ? "Refiled " | "Copied ", count, fname, suffix));
endif
.
#40:48
"@qreply <msg> [on *<recipient>] [<flags>...]";
"like @reply only, as in @qsend, we prompt for the message text using ";
"$command_utils:read_lines() rather than invoking the $mail_editor.";
set_task_perms(who = valid(cp = caller_perms()) ? cp | player);
if (!(p = this:parse_mailread_cmd(verb, args, "cur", "on", 1)))
"...garbled...";
elseif ($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("noinclude", @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", tostr("  include    include the original message in reply (can't do this for ", verb, ")"), "  noinclude  don't include the original in your reply"});
elseif ("include" in flags_replytos[1])
player:notify(tostr("Can't include message on a ", verb));
else
this:set_current_folder(p[1]);
if (to_subj = $mail_editor:parse_msg_headers(p[1]:messages_in_seq(p[2])[1][2], flags_replytos[1]))
player:notify(tostr("To:       ", $mail_agent:name_list(@to_subj[1])));
if (to_subj[2])
player:notify(tostr("Subject:  ", to_subj[2]));
endif
if (replytos = flags_replytos[2])
player:notify(tostr("Reply-to: ", $mail_agent:name_list(@replytos)));
endif
hdrs = {to_subj[2], replytos || {}};
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(@to_subj[1]), "."), @active ? {} | {"Type `@edit' to take this into the mail editor."}});
if (typeof(message) == ERR)
player:notify(tostr(message));
elseif (message[1] == "@edit")
$mail_editor:invoke(1, verb, to_subj[1], @hdrs, message[2]);
elseif (!message[2])
player:notify("Blank message not sent.");
else
result = $mail_agent:send_message(this, to_subj[1], hdrs, message[2]);
if (result && result[1])
player:notify(tostr("Message sent to ", $mail_agent:name_list(@listdelete(result, 1)), "."));
else
player:notify("Message not sent.");
endif
endif
endif
endif
.
#40:49
"@mail-all-new-mail";
" Prints headers for all new mail on every mail-recipient mentioned in .current_message.";
set_task_perms(valid(cp = caller_perms()) ? cp | player);
cm = this.current_message;
cm[1..2] = {{this, @cm[1..2]}};
this._mail_task = task_id();
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, "="));
folder:display_seq_headers(msg_seq, @f[2..3]);
player:notify("");
$command_utils:suspend_if_needed(2);
endif
endfor
if (nomail)
player:notify("You don't have any new mail anywhere.");
else
player:notify("===== " + $string_utils:left("End of new mail ", 40, "="));
endif
.
#40:50
"@read-all-new-mail [yes]";
" Prints all new mail on every mail-recipient mentioned in .current_message";
" Generally this will spam you into next Tuesday.";
" You will be queried for whether you want your last-read dates updated";
"   but you can specify \"yes\" on the command line to suppress this.";
"   If you do so, last-read dates will be updated after each folder read.";
set_task_perms(valid(cp = caller_perms()) ? cp | player);
noconfirm = args && ("yes" in args);
cm = this.current_message;
cm[1..2] = {{this, @cm[1..2]}};
this._mail_task = task_id();
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)), ":")))
if (noconfirm)
this:set_current_message(folder, @cur);
this:set_current_folder(folder);
else
new_cms = {@new_cms, {folder, @cur}};
endif
player:notify("");
endif
endif
$command_utils:suspend_if_needed(1);
this._mail_task = task_id();
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);
this:set_current_folder(n[1]);
endfor
player:notify("Last-read-dates updated");
else
player:notify("Last-read-dates not updated");
endif
.
#40:51
"Syntax: @quicksend <recipients(s)> [subj=<text>] [<message>]";
"Sends the recipients(s) a quick message, wit{out having to go to the mailroom. 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..$]);
ws = $string_utils:word_start(argstr);
argstr = argstr[1..ws[1][2]] + argstr[ws[2][2] + 1..$];
args = listdelete(args, 2);
else
subject = "";
endif
if (length(args) > 1)
unbroken = argstr[(argstr[1] == "\"") ? length(args[1]) + 4 | (length(args[1]) + 2)..$] + "^";
message = {};
while (unbroken)
if (i = index(unbroken, "^"))
message = {@message, unbroken[1..i - 1]};
endif
unbroken = unbroken[i + 1..$];
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
result = $mail_agent:send_message(this, recipients, subject, message);
if (result && result[1])
player:notify(tostr("Message sent to ", $mail_agent:name_list(@listdelete(result, 1)), "."));
else
player:notify("Message not sent.");
endif
endif
.
#40:52
if (caller_perms().wizard)
pass();
if ($code_utils:verb_location() == this)
this.mail_options = {};
this.help = $mail_help;
else
clear_property(this, "mail_options");
endif
endif
"Last modified Sun Aug 17 11:13:59 1997 CDT by Wizard (#2).";
.
#40:53
if (((valid(cp = caller_perms()) && (caller != this)) && (!$perm_utils:controls(cp, this))) && (caller != #0))
return E_PERM;
endif
nm = this:length_all_msgs() - this:length_date_le(this:get_current_message()[2]);
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();
pass(@args);
.
#40:54
"Ideally, in order for one person to be notified that another person has new mail, both the mail recipient and the notification recipient should agree that this is an OK transfer of information.";
"Usage:  @add-notify me to player";
"    Sends mail to player saying that I want to be added to their mail notification property.";
"Usage:  @add-notify player to me";
"    Makes sure that player wants to be notified, if so, adds them to my .mail_notify property.  (Deletes from temporary record.)";
if (this == dobj)
target = $string_utils:match_player(iobjstr);
if ($command_utils:player_match_failed(target, iobjstr))
return;
elseif (this in target.mail_notify[1])
player:tell("You already receive notifications when ", target.name, " receives mail.");
elseif (this in target.mail_notify[2])
player:tell("You already asked to be notified when ", target.name, " receives mail.");
else
$mail_agent:send_message(player, {target}, "mail notification request", {tostr($string_utils:nn(this), " would like to receive mail notifications when you get mail."), "Please type:", tostr("  @add-notify ", this.name, " to me"), "if you wish to allow this action."});
player:tell("Notifying ", $string_utils:nn(target), " that you would like to be notified when ", target.ps, " receives mail.");
target.mail_notify[2] = setadd(target.mail_notify[2], this);
endif
elseif (this == iobj)
target = $string_utils:match_player(dobjstr);
if ($command_utils:player_match_failed(target, dobjstr))
return;
elseif (target in this.mail_notify[2])
this.mail_notify[1] = setadd(this.mail_notify[1], target);
this.mail_notify[2] = setremove(this.mail_notify[2], target);
player:tell(target.name, " will be notified when you receive mail.");
else
player:tell("It doesn't look like ", target.name, " wants to be notified when you receive mail.");
endif
else
player:tell("Usage:  @add-notify me to player");
player:tell("        @add-notify player to me");
endif
.
#40:55
if ((length(this.mail_notify) > 0) && (typeof(this.mail_notify[1]) == LIST))
return this.mail_notify[1];
else
return this.mail_notify;
endif
.
#41:0
"$gender_utils:set(object,gender) --- sets the pronoun properties of object.";
"gender is a string: one of the strings in $gender_utils.genders, the list of rcognized genders.  If the gender change is successful, the (full) name of the gender (e.g., \"male\") is returned.  E_NONE is returned if gender does not match any recognized gender.  Any other error encountered (e.g., E_PERM, E_PROPNF) is likewise returned and the object's pronoun properties are left unaltered.";
set_task_perms(caller_perms());
{object, gender} = args;
if (this == object)
return E_DIV;
elseif (gnum = $string_utils:find_prefix(gender, this.genders))
gender = this.genders[gnum];
else
return E_NONE;
endif
save = {};
prons = this.pronouns;
for p in (prons)
save = {@save, e = `object.(p) ! ANY'};
if ((typeof(e) != STR) || (typeof(e = `object.(p) = this.(p)[gnum] ! ANY') == ERR))
for i in [1..length(save) - 1]
object.(prons[i]) = save[i];
endfor
return e;
endif
endfor
return gender;
.
#41:1
"$gender_utils:add(object[,perms[,owner]])";
"--- adds pronoun properties to object if they're not already there.";
"    perms default to \"rc\", owner defaults to the object owner.";
set_task_perms(caller_perms());
{object, ?perms = "rc", ?owner = object.owner} = args;
prons = this.pronouns;
e = 1;
for p in (prons)
if (!$object_utils:has_property(object, p))
e = `add_property(object, p, "", {owner, perms}) ! ANY';
if (typeof(e) == ERR)
player:tell("Couldn't add ", object, ".", p, ":  ", e);
return;
endif
elseif ((typeof(object.(p)) != STR) && (typeof(e = `object.(p) = "" ! ANY') == ERR))
player:tell("Couldn't reset ", object, ".", p, ":  ", e);
return;
elseif (!object.(p))
e = 0;
endif
endfor
if ((!e) && (ERR == typeof(e = this:set(object, "neuter"))))
player:tell("Couldn't initialize pronouns:  ", e);
endif
.
#41:2
"get_pronoun(key,object) => pronoun corresponding to object.";
"key can be one of s,o,p,q,r,S,O,P,Q,R to refer to the pronoun properties relatively directly or it can be something of the form \"he/she\" or \"He/She\".";
"Next the object is checked for the desired pronoun property.  If that doesn't exist, we look at object.gender and infer the pronoun from the corresponding $gender_utils property.  If .gender doesn't exist or the object itself is invalid, we use the corresponding property on $player.";
{key, ?object = player} = args;
if (key[1] == ":")
key = key[2..$];
endif
if ((length(key) == 1) && (i = index("sopqrSOPQR", key, 1)))
prop = this.pronouns[i];
else
search = "$1:he$s:she$1:he/she$2:him$2:him/her$3:his/her$4:hers$4:his/hers$5:himself$5:herself$5:himself/herself";
i = index(search, (":" + key) + "$");
if (!i)
return "";
endif
cap = strcmp("a", key) > 0;
prop = this.pronouns[toint(search[i - 1]) + (5 * cap)];
endif
if (!valid(object))
return $player.(prop);
elseif (STR == typeof(p = `object.(prop) ! ANY'))
return p;
elseif ((STR == typeof(g = `object.gender ! ANY')) && (i = g in this.genders))
return this.(prop)[i];
else
return $player.(prop);
endif
.
#41:3
"get_conj(verbspec,object) => verb conjugated according to object.";
"verbspec can be one of \"singular/plural\", \"singular\", \"singular/\", or \"/plural\", e.g., \"is/are\", \"is\", \"is/\", or \"/are\".";
"The object is checked to see whether it is singular or plural.  This is inferred from its .gender property.  If .gender doesn't exist or the object itself is invalid, we assume singular.";
{spec, ?object = player} = args;
i = index(spec + "/", "/");
sing = spec[1..i - 1];
if (i < length(spec))
plur = spec[i + 1..$];
else
plur = "";
endif
cap = strcmp("a", (i == 1) ? spec[2] | spec) > 0;
if (((valid(object) && (STR == typeof(g = `object.gender ! ANY'))) && (i = g in this.genders)) && this.is_plural[i])
vb = plur || this:_verb_plural(sing, i);
else
vb = sing || this:_verb_singular(plur, i);
endif
if (cap)
return $string_utils:capitalize(vb);
else
return vb;
endif
.
#41:4
{st, idx} = args;
if (typeof(st) != STR)
return E_INVARG;
endif
len = length(st);
if ((len >= 3) && (rindex(st, "n't") == (len - 2)))
return this:_verb_plural(st[1..len - 3], idx) + "n't";
elseif (i = st in {"has", "is"})
return this.({"have", "be"}[i])[idx];
elseif (st == "was")
return (idx > 6) ? "were" | st;
elseif ((len <= 3) || (st[len] != "s"))
return st;
elseif (st[len - 1] != "e")
return st[1..len - 1];
"elseif ((r = (rindex(st, \"sses\") || rindex(st, \"zzes\"))) && (r == (len - 3)))";
elseif ((r = rindex(st, "zzes")) && (r == (len - 3)))
return st[1..len - 3];
elseif ((((st[len - 2] == "h") && index("cs", st[len - 3])) || index("ox", st[len - 2])) || (st[len - 3..len - 2] == "ss"))
return st[1..len - 2];
"washes => wash, belches => belch, boxes => box";
"used to have || ((st[len - 2] == \"s\") && (!index(\"aeiouy\", st[len - 3])))";
"so that <consonant>ses => <consonant>s";
"known examples: none";
"counterexample: browses => browse";
"update of sorts--put in code to handle passes => pass";
elseif (st[len - 2] == "i")
return st[1..len - 3] + "y";
else
return st[1..len - 1];
endif
.
#41:5
{st, idx} = args;
if (typeof(st) != STR)
return E_INVARG;
endif
len = length(st);
if ((len >= 3) && (rindex(st, "n't") == (len - 2)))
return this:_verb_singular(st[1..len - 3], idx) + "n't";
elseif (i = st in {"have", "are"})
return this.({"have", "be"}[i])[idx];
elseif ((st[len] == "y") && (!index("aeiou", st[len - 1])))
return st[1..len - 1] + "ies";
elseif (index("sz", st[len]) && index("aeiou", st[len - 1]))
return (st + st[len]) + "es";
elseif (index("osx", st[len]) || ((len > 1) && (index("chsh", st[len - 1..len]) % 2)))
return st + "es";
else
return st + "s";
endif
.
#41:6
"_do(cap,object,modifiers...)";
{cap, object, modifiers} = args;
if (!modifiers)
if (typeof(object) != OBJ)
return tostr(object);
elseif (!valid(object))
return (cap ? "N" | "n") + "othing";
else
return cap ? object:titlec() | object:title();
endif
elseif (modifiers[1] == ".")
if (i = index(modifiers[2..$], "."))
i = i + 1;
elseif (!(i = (index(modifiers, ":") || index(modifiers, "#")) || index(modifiers, "!")))
i = length(modifiers) + 1;
endif
if (typeof(o = `object.(modifiers[2..i - 1]) ! ANY') == ERR)
return tostr("%(", o, ")");
else
return this:_do(cap || (strcmp("a", modifiers[2]) > 0), o, modifiers[i..$]);
endif
elseif (modifiers[1] == ":")
if (typeof(object) != OBJ)
return tostr("%(", E_TYPE, ")");
elseif (p = this:get_pronoun(modifiers, object))
return p;
else
return tostr("%(", modifiers, "??)");
endif
elseif (modifiers[1] == "#")
return tostr(object);
elseif (modifiers[1] == "!")
return this:get_conj(modifiers[2..$], object);
else
i = (((index(modifiers, ".") || index(modifiers, ":")) || index(modifiers, "#")) || index(modifiers, "!")) || (length(modifiers) + 1);
s = modifiers[1..i - 1];
if (j = s in {"dobj", "iobj", "this"})
return this:_do(cap, {dobj, iobj, callers()[2][1]}[j], modifiers[i..$]);
else
return tostr("%(", s, "??)");
endif
endif
.
#41:7
"Experimental pronoun substitution. The official version is on $string_utils.";
"syntax:  :pronoun_sub(text[,who])";
"experimental version that accomodates Aladdin's style...";
set_task_perms($no_one);
{old, ?who = player} = args;
if (typeof(old) == LIST)
plines = {};
for line in (old)
plines = {@plines, this:pronoun_sub(line, who)};
endfor
return plines;
endif
new = "";
here = valid(who) ? who.location | $nothing;
objspec = "nditl";
objects = {who, dobj, iobj, caller, here};
prnspec = "sopqrSOPQR";
prprops = {"ps", "po", "pp", "pq", "pr", "Ps", "Po", "Pp", "Pq", "Pr"};
oldlen = length(old);
while ((prcnt = index(old, "%")) && (prcnt < oldlen))
cp_args = {};
s = old[k = prcnt + 1];
if (brace = index("([{", s))
if (!(w = index(old[k + 1..oldlen], ")]}"[brace])))
return new + old;
elseif (brace == 3)
s = this:_do(0, who, old[prcnt + 2..(k = k + w) - 1]);
else
p = old[prcnt + 2..(k = k + w) - 1];
if (brace == 1)
cp_args = {who, p};
elseif (p[1] == "#")
s = (o = index(objspec, p[2])) ? tostr(objects[o]) | (("[" + p) + "]");
elseif (!(o = index(objspec, p[1])))
s = ("[" + p) + "]";
else
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};
elseif (w = index(prnspec, s, 1))
cp_args = {who, prprops[w]};
elseif (s == "#")
s = tostr(who);
elseif (s != "%")
s = "%" + s;
endif
new = (new + old[1..prcnt - 1]) + ((!cp_args) ? s | ((typeof(sub = $string_utils:_cap_property(@cp_args)) != ERR) ? sub | (("%(" + tostr(sub)) + ")")));
old = old[k + 1..oldlen];
oldlen = oldlen - k;
endwhile
return new + old;
.
#42:0
"$perm_utils:controls(who, what)";
"Is WHO allowed to hack on WHAT?";
return (args[1] == args[2].owner) || args[1].wizard;
.
#42:1
":apply(permstring,mods) => 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).";
{perms, mods} = args;
if ((!mods) || (!index("!-+", mods[1])))
return mods;
endif
i = 1;
while (i <= length(mods))
if (mods[i] == "+")
while (((i = i + 1) <= length(mods)) && (!index("!-+", mods[i])))
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])))
perms = strsub(perms, mods[i], "");
endwhile
endif
endwhile
return perms;
.
#42:2
stage = 1;
{?lineno = 0} = args;
c = callers(lineno);
while (((stage = stage + 1) < length(c)) && (c[stage][1] == c[1][1]))
endwhile
return c[stage];
.
#42:3
"Syntax:  controls_prop(OBJ who, OBJ what, STR propname)   => 0 | 1";
"         controls_verb(OBJ who, OBJ what, STR verbname)   => 0 | 1";
"";
"Is WHO allowed to hack on WHAT's PROPNAME? Or VERBNAME?";
{who, what, name} = args;
bi = (verb == "controls_verb") ? "verb_info" | "property_info";
return who.wizard || (who == call_function(bi, what, name)[1]);
.
#43:0
"Given a time() or ctime()-style date, this returns the full name of the day.";
if (typeof(args[1]) == INT)
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];
.
#43:1
"Given a time() or ctime()-style date, this returns the full name";
"of the month.";
if (typeof(args[1]) == INT)
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];
.
#43: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";
{time, ?precision = 2} = args;
if (typeof(time) == INT)
time = ctime(time);
elseif (typeof(time) != STR)
return E_TYPE;
endif
time = $string_utils:explode(time)[4];
hour = toint(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;
.
#43: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) * toint(args[1][1..2])) + (60 * toint(args[1][4..5]))) + toint(args[1][7..8]);
.
#43:4
{?time = time()} = args;
r = 10000;
h = (r * r) + (r / 2);
t = ((time + 120) % 86400) / 240;
s = (5 * ((time - 14957676) % 31556952)) / 438291;
phi = (s + t) + this.corr;
cs = $trig_utils:cos(s);
spss = ((($trig_utils:sin(phi) * $trig_utils:sin(s)) + h) / r) - r;
cpcs = ((($trig_utils:cos(phi) * cs) + h) / r) - r;
return (((((this.stsd * cs) - (this.ctcd * cpcs)) - (this.ct * spss)) + h) / r) - r;
.
#43: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 = toint(words[5]);
day = ({-1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}[month] + toint(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) + toint(hms[1])) + zone) * 60) + toint(hms[2])) * 60) + toint(hms[3]);
.
#43: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
.
#43:7
"english_time(time [,reference time]): returns the time as a string of";
"years, months, days, hours, 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().";
{_time, ?reftime = time()} = args;
if (_time < 1)
return "0 seconds";
endif
_ctime = (typeof(reftime) == INT) ? 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 = toint(_ctime[21..24]);
"attribution: the algorithm used is from the eminently eminent g7.";
while (_time >= (days = this.monthlens[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
$command_utils:suspend_if_needed(0);
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
return $string_utils:english_list(units);
.
#43: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.";
{day, ?dir = 0} = args;
if (!(toint(day) || (day = $string_utils:find_prefix(day, this.days))))
return E_DIV;
endif
delta = {288000, 374400, 460800, 547200, 28800, 115200, 201600}[toint(day)];
time = time() - delta;
if (dir)
time = (time / 604800) + ((dir > 0) ? dir | (dir + 1));
else
time = (time + 302400) / 604800;
endif
return (time * 604800) + delta;
.
#43: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.";
{month, ?dir = 0, ?dth = 1} = args;
if (!(toint(month) || (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] + dth) - 1;
day = (time() - 28800) / 86400;
day = (day - ((day + 672) / 1461)) - delta;
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;
.
#43: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 * (((toint(ctime(time)[12..13]) + 12) % 24) - 12));
.
#43:11
"Works like pronoun substitution, but substitutes time stuff.";
"Call with time_sub(string, time). 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";
"$$ -> $.";
"";
"This verb stolen from Ozymandias's #4835:time_subst.";
res = "";
{thestr, ?thetime = time()} = args;
if ((typeof(thestr) != STR) || (typeof(thetime) != INT))
player:tell("Bad arguments to time_subst.");
return;
endif
itslength = length(thestr);
if (!itslength)
return "";
endif
done = 0;
cctime = ctime(thetime);
while (dollar = index(thestr, "$"))
res = res + thestr[1..dollar - 1];
if (dollar == length(thestr))
return res;
endif
thechar = thestr[dollar + 1];
thestr[1..dollar + 1] = "";
if (thechar == "$")
res = res + "$";
elseif (!strcmp(thechar, "h"))
res = res + $string_utils:trim(tostr(toint(cctime[12..13])));
elseif (thechar == "H")
res = res + cctime[12..13];
elseif (!strcmp(thechar, "m"))
res = res + $string_utils:trim(tostr(toint(cctime[15..16])));
elseif (thechar == "M")
res = res + cctime[15..16];
elseif (!strcmp(thechar, "s"))
res = res + $string_utils:trim(tostr(toint(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(((toint(cctime[12..13]) + 11) % 12) + 1);
elseif (!strcmp(thechar, "p"))
res = res + ((toint(cctime[12..13]) >= 12) ? "pm" | "am");
elseif (thechar == "P")
res = res + ((toint(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
endwhile
return res + thestr;
.
#43:12
"Copied from Archer (#52775):mmddyy Tue Apr  6 17:04:26 1993 PDT";
"Given a time() or ctime()-style date and an optional separator, this returns the MM/DD/YY or DD/MM/YY form of the date (depending on the verb called.)  The default seperator is '/'";
{time, ?divstr = "/"} = args;
if (typeof(time) == INT)
time = ctime(time);
elseif (typeof(time) != STR)
return E_TYPE;
endif
date = $string_utils:explode(time);
day = toint(date[3]);
month = date[2] in $time_utils.monthabbrs;
year = date[5];
daystr = (day < 10) ? "0" + tostr(day) | tostr(day);
monthstr = (month < 10) ? "0" + tostr(month) | tostr(month);
yearstr = tostr(year)[3..4];
if (verb == "mmddyy")
return tostr(monthstr, divstr, daystr, divstr, yearstr);
else
return tostr(daystr, divstr, monthstr, divstr, yearstr);
endif
.
#43: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 = toint(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[$] == ",")
unit = unit[1..$ - 1];
endif
ok = 0;
for entry in ($time_utils.time_units)
if ((!ok) && (unit in entry[2..$]))
nsec = nsec + (entry[1] * n);
ok = 1;
endif
endfor
if (!ok)
return E_INVARG;
endif
endif
endfor
return nsec;
.
#43:14
"Copied from Ballroom Complex (#29992):from_date by Keelah! (#30246) Tue Jul 13 19:42:32 1993 PDT";
":seconds_until_date(month,day,time,which)";
"month is a string or the numeric representation of the month, day is a number, time is a string in the following format, hh:mm:ss.";
"which==-1 => use most recent such month.";
"which==+1 => use first upcoming such month.";
"which==0 => use closest such month.";
"This will return the number of seconds until the month, day and time given to it.";
"Written by Keelah, on July 5, 1993.";
{month, day, time, which} = args;
converted = 0;
converted = converted + $time_utils:from_month(month, which, day);
current = this:seconds_until_time("12:00:00");
get_seconds = this:seconds_until_time(time);
if (get_seconds < 0)
get_seconds = (get_seconds + 39600) - current;
else
get_seconds = (get_seconds + 39600) - current;
endif
converted = (converted + get_seconds) - time();
return converted;
.
#43:15
"Copied from Ballroom Complex (#29992):seconds_until by Keelah! (#30246) Tue Jul 13 19:42:37 1993 PDT";
":seconds_until_time(hh:mm:ss)";
"Given the string hh:mm:ss, this returns the number of seconds until that hh:mm:ss. If the hh:mm:ss is before the current time(), the number returned is a negative, else the number is a positive.";
"Written by Keelah, on July 4, 1993.";
current = $time_utils:to_seconds(ctime()[12..19]);
time = $time_utils:to_seconds(args[1]);
return toint(time) - toint(current);
.
#43:16
"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 s + x;
"Last modified Fri Feb  6 08:42:23 1998 CST by Wizard (#2).";
.
#45: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 ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
elseif (this.location != $mail_agent)
"... we don't care...";
return pass(@args);
else
for a in (aliases = args[1])
if (index(a, " "))
"... we don't care...";
elseif (rp = $mail_agent:reserved_pattern(a))
player:tell("Mailing list name \"", a, "\" uses a reserved pattern: ", rp[1]);
aliases = setremove(aliases, a);
elseif (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
if (aliases)
return pass(aliases);
else
return 1;
endif
endif
.
#45:1
"Returns full name and mail aliases for this list, read and write status by the player, and a short description. Calling :look_self(1) will omit the description.";
{?brief = 0} = args;
namelist = "*" + ((names = this:mail_names()) ? $string_utils:from_list(names, ", *") | tostr(this));
if (typeof(fwd = this:mail_forward()) != LIST)
fwd = {};
endif
if (this:is_writable_by(player))
if (player in fwd)
read = " [Writable/Subscribed]";
else
read = " [Writable]";
endif
elseif (this.readers == 1)
read = tostr(" [Public", (player in fwd) ? "/Subscribed]" | "]");
elseif (player in fwd)
read = " [Subscribed]";
elseif (this:is_readable_by(player))
read = " [Readable]";
else
read = "";
endif
if (this:is_usable_by($no_one))
mod = "";
elseif (this:is_usable_by(player))
mod = " [Approved]";
else
mod = " [Moderated]";
endif
player:tell(namelist, "  (", this, ")", read, mod);
if (!brief)
d = this:description();
if (typeof(d) == STR)
d = {d};
endif
for l in (d)
if (length(l) <= 75)
ls = {l};
else
ls = $generic_editor:fill_string(l, 76);
endif
for line in (ls)
player:tell("    ", line);
$command_utils:suspend_if_needed(0);
endfor
endfor
endif
.
#45:2
return $perm_utils:controls(who = args[1], this) || `who in this.writers ! E_TYPE';
.
#45:3
return (typeof(this.readers) != LIST) || (((who = args[1]) in this.readers) || (this:is_writable_by(who) || $mail_agent:sends_to(1, this, who)));
.
#45:4
who = args[1];
if (this.moderated)
return `who in this.moderated ! E_TYPE' || (this:is_writable_by(who) || who.wizard);
else
return this.guests_can_send_here || (!$object_utils:isa(who, $guest));
endif
.
#45:5
if ((args && (!this:is_usable_by(args[1]))) && (!args[1].wizard))
return this:moderator_notify(@args);
else
return this.(verb);
endif
.
#45: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
.
#45:7
if (typeof(mf = this.(verb)) == STR)
return $string_utils:pronoun_sub(mf, args ? args[1] | $player);
else
return mf;
endif
.
#45: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;
.
#45: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;
.
#45: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;
.
#45: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;
.
#45: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
.
#45: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]));
.
#45: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]));
.
#45:15
":parse_message_seq(strings,cur) => {msg_seq,@unused_strings} or string error";
"";
":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;
.
#45:16
":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
.
#45:17
":rm_message_seq(msg_seq) removes the given sequence of from folder";
"               => string giving msg numbers removed";
"See the corresponding routine on $mail_agent.";
if (this:ok_write(caller, caller_perms()))
return $mail_agent:(verb)(@args);
elseif (this:ok(caller, caller_perms()) && (seq = this:own_messages_filter(caller_perms(), @args)))
return $mail_agent:(verb)(@listset(args, seq, 1));
else
return E_PERM;
endif
.
#45:18
":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;
.
#45:19
":own_messages_filter(who,msg_seq) => subsequence of msg_seq consisting of those messages that <who> is actually allowed to remove (on the assumption that <who> is not one of the allowed writers of this folder.";
if (!this.rmm_own_msgs)
return E_PERM;
elseif ((typeof(seq = this:from_msg_seq({args[1]}, args[2])) != LIST) || (seq != args[2]))
return {};
else
return seq;
endif
.
#45:20
"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
.
#45:21
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[$][2][1];
endif
.
#45:22
mlen = this:length_all_msgs();
this.last_msg_date = mlen && this:messages_in_seq(mlen)[2][1];
.
#45:23
return this.(verb);
.
#45:24
return $mail_agent:msg_summary_line(@args);
.
#45:25
for m in (this.messages)
$mail_agent:__convert_new(@m[2]);
$command_utils:suspend_if_needed(0);
endfor
.
#45:26
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;
.
#45:27
if (caller_perms().wizard)
pass();
if (!(this in {$mail_recipient, $big_mail_recipient}))
"...generic mail recipients stay in #-1...";
move(this, $mail_agent);
this:_fix_last_msg_date();
"Since the mail_name_db name hierarchy stuff is mostly incomplete..";
this.names = {};
endif
endif
.
#45:28
if ($perm_utils:controls(caller_perms(), this))
this.mail_forward = {};
return pass(@args);
endif
.
#45:29
return "*" + this.aliases[1];
.
#45:30
names = {};
for a in (this.aliases)
if (!index(a, " "))
names = setadd(names, strsub(a, "_", "-"));
endif
endfor
return names;
.
#45:31
if (this:ok_write(caller, caller_perms()))
"Passed security check...";
if (this.expire_period && (rmseq = $seq_utils:remove(this:unkept_msg_seq(), 1 + this:length_date_le(time() - this.expire_period))))
"... i.e., everything not marked kept that is older than expire_period";
if (this.registered_email && this.email_validated)
format = this.owner:format_for_netforward(this:messages_in_seq(rmseq), " expired from " + $mail_agent:name(this));
$network:sendmail(this.registered_email, @{format[2], @format[1]});
"Do nothing if it bounces, etc.";
endif
this:rm_message_seq(rmseq);
return this:expunge_rmm();
else
return 0;
endif
else
return E_PERM;
endif
.
#45:32
if (this:is_writable_by(caller_perms()) || this:is_writable_by(caller))
pass(@args);
else
return E_PERM;
endif
.
#45:33
":accept_subname(object,name,perms)";
"is <perms> allowed to make <object> mail subname of this with the given <name>?";
object = args[1];
accept = this.accept_subname;
if (!valid(object))
return E_INVARG;
elseif ((accept && (typeof(accept) != LIST)) || (this:is_writable_by(object.owner) || this:is_writable_by(perms = args[3])))
"...writers can do anything...";
"... .accept_subname==1 => anyone can do anything...";
return 1;
elseif (accept && ((object in accept) || ({object, args[2]} in accept)))
"... object/name is explicitly sanctioned";
return 1;
elseif (object in $mail_name_db:find_all(tostr(this, ":")))
"...object is already a subname...";
"...allow name change unless .accept_subname specifies a name";
return !(accept && (a = $list_utils:assoc(object, accept)));
else
return 0;
endif
.
#45:34
return (!(names = this.names)) ? "" | (((name = names[1])[1] == #-1) ? "*" + name[2] | tostr(is_player(name[1]) ? name[1].name | ("*" + name[1]:(verb)()), ":", name[2]));
.
#45:35
"@mail_name <this> is whatever";
if (!this:is_writable_by(player))
player:tell(E_PERM);
return;
endif
if (h = rindex(iobjstr, ":"))
if ($mail_agent:match_failed(head = $mail_agent:match_new(iobjstr[1..h]), iobjstr[1..h]))
return;
elseif (!(name = iobjstr[h + 1..$]))
player:tell("Recipient cannot have a blank name.");
return;
endif
elseif (this.names && $object_utils:isa(head = this.names[1][1], $mail_recipient))
name = iobjstr;
else
player:tell("No names have yet been given to this recipient.");
player:tell("You need to specify a full path.");
return;
endif
if (e = $mail_agent:set_mail_name(this, head, name))
player:tell("Name set to ", this:mail_name_new(), ".");
elseif (e == E_NACC)
player:tell(head:mail_name_new(), " does not want ", this, " to be a subname.");
elseif (e == E_RECMOVE)
player:tell(head:mail_name_new(), " is a name-descendant of ", this, ".");
elseif (e == E_INVARG)
player:tell("Name must contain no parentheses, colons or stars.");
else
player:tell(e);
endif
.
#45:36
"@rm-mail-name whatever from <this>";
if (!this:is_writable_by(player))
player:tell(E_PERM);
return;
elseif (!this.names)
player:tell("This recipient has no names.");
return;
endif
if (h = rindex(dobjstr, ":"))
if ($mail_agent:match_failed(head = $mail_agent:match_new(dobjstr[1..h]), dobjstr[1..h]))
return;
endif
name = dobjstr[h + 1..$];
nlist = this.names;
while ((i = $list_utils:iassoc(head, nlist)) && (name && (name != nlist[i][2])))
nlist[1..i] = {};
endwhile
if (i)
"...no problem...";
elseif (name)
player:tell("Recipient does not have that name");
return;
else
player:tell("Recipient does not have any names under ", head:mail_name_new(), ": (", head, ").");
return;
endif
name = nlist[i][2];
elseif (a = $list_utils:assoc(name = dobjstr, this.names, 2))
head = a[1];
else
player:tell("Recipient does not have that name");
return;
endif
if (e = $mail_agent:remove_mail_name(this, head, name))
player:tell("Removed name ", head:mail_name_new(), ":", name, " from ", this:mail_name_new(), " (", this, ").");
else
player:tell(e);
endif
.
#45:37
"@add-mail-name whatever to <this>";
if (!this:is_writable_by(player))
player:tell(E_PERM);
return;
endif
if (h = rindex(dobjstr, ":"))
if ($mail_agent:match_failed(head = $mail_agent:match_new(dobjstr[1..h]), dobjstr[1..h]))
return;
elseif (!(name = dobjstr[h + 1..$]))
player:tell("Recipient cannot have a blank name.");
return;
endif
elseif (this.names && $object_utils:isa(head = this.names[1][1], $mail_recipient))
name = dobjstr;
else
player:tell("No names have yet been given to this recipient.");
player:tell("You need to specify a full path.");
return;
endif
if (e = $mail_agent:add_mail_name(this, head, name))
player:tell("Added name ", head:mail_name_new(), ":", name, " to ", $mail_agent:name_new(this), ".");
elseif (e == E_NACC)
player:tell(head:mail_name_new(), " does not want ", this, " to be a subname.");
elseif (e == E_RECMOVE)
player:tell(head:mail_name_new(), " is a name-descendant of ", this, ".");
elseif (e == E_INVARG)
player:tell("Name must contain no parentheses, colons or stars.");
else
player:tell(e);
endif
.
#45:38
":msg_full_text(@msg) => list of strings.";
"msg is a mail message (in the usual transmission format).";
"display_seq_full calls this to obtain the actual list of strings to display.";
return player:msg_text(@args);
"default is to leave it up to the player how s/he wants it to be displayed.";
.
#45:39
"Syntax:  @set_expire <recipient> to <time>";
"         @set_expire <recipient> to";
"";
"Allows the list owner to set the expiration period of this mail recipient. This is the time messages will remain before they are removed from the list. The <time> given can be in english terms (e.g., 2 months, 45 days, etc.).";
"Non-wizard mailing list owners are limited to a maximum expire period of 180 days. They are also prohibited from setting the list to non-expiring.";
"Wizards may set the expire period to 0 for no expiration.";
"The second form, leaving off the time specification, will tell you what the recipient's expire period is currently set to.";
if ((caller_perms() != #-1) && (caller_perms() != player))
return player:tell(E_PERM);
elseif (!this:is_writable_by(player))
return player:tell(E_PERM);
elseif (!iobjstr)
return player:tell(this.expire_period ? tostr("Messages will automatically expire from ", this:mail_name(), " after ", $time_utils:english_time(this.expire_period), ".") | tostr("Messages will not expire from ", this:mail_name()));
elseif (typeof(time = $time_utils:parse_english_time_interval(iobjstr)) == ERR)
return player:tell(time);
elseif ((time == 0) && (!player.wizard))
return player:tell("Only wizards may set a mailing list to not expire.");
elseif ((time > (180 * 86400)) && (!player.wizard))
return player:tell("Only a wizard may set the expiration period on a mailing list to greater than 180 days.");
endif
this.expire_period = time;
player:tell("Messages will ", (time != 0) ? tostr("automatically expire from ", this:mail_name(), " after ", $time_utils:english_time(time)) | tostr("not expire from ", this:mail_name()), ".");
.
#45:40
"Syntax:   @register <recipient> to <email-address>";
"alias     @netregister <recipient> to <email-address>";
"          @register <recipient> to";
"";
"The list owner may use this command to set a registered email address for the mail recipient. When set, mail messages that expire off of the mail recipient will be mailed to that address.";
"If you leave the email address off of the command, it will return the current registration and expiration information for that recipient if you own it.";
"The owner may register a mail recipient to any email address. However, if the address does not match his registered email address, then a password will be generated and sent to the address specified when this command is used. Then, the owner may retrieve that password and verify the address with the command:";
"";
"  @validate <recipient> with <password>";
"";
"See *B:MailingListReform #98087 for full details.";
if ((caller_perms() != #-1) && (caller_perms() != player))
return player:tell(E_PERM);
elseif (!$perm_utils:controls(player, this))
return player:tell(E_PERM);
elseif (!iobjstr)
if (this.registered_email)
player:tell(this:mail_name(), " is registered to ", this.registered_email, ". Messages will be sent there when they expire after ", (this.expire_period == 0) ? "never" | $time_utils:english_time(this.expire_period), ".");
else
player:tell(this:mail_name(), " is not registered to any address. Messages will be deleted when they expire after ", (this.expire_period == 0) ? "never" | $time_utils:english_time(this.expire_period), ".");
player:tell("Usage:  @register <recipient> to <email-address>");
endif
return;
elseif (iobjstr == player.email_address)
this.registered_email = player.email_address;
this.email_validated = 1;
player:tell("Messages expired from ", this:mail_name(), " after ", (this.expire_period == 0) ? "never" | $time_utils:english_time(this.expire_period), " will be emailed to ", this.registered_email, " (which is your registered email address).");
elseif (reason = $network:invalid_email_address(iobjstr))
return player:tell(reason, ".");
elseif (!$network.active)
return player:tell("The network is not up at the moment. Please try again later or contact a wizard for help.");
else
password = $wiz_utils:random_password(5);
result = $network:sendmail(iobjstr, tostr($network.MOO_Name, " mailing list verification"), @$generic_editor:fill_string(tostr("The mailing list ", this:mail_name(), " on ", $network.MOO_Name, " has had this address designated as the recipient of expired mail messages. If this is not correct, then you need do nothing but ignore this message. If this is correct, you must log into the MOO and type:  `@validate ", this:mail_name(), " with ", password, "' to start receiving expired mail messages."), 75));
if (result != 0)
return player:tell("Mail sending did not work: ", result, ". Address not set.");
endif
this.registered_email = iobjstr;
this.email_validated = 0;
this.validation_password = password;
player:tell("Registration complete. Password sent to the address you specified. When you receive the email, log back in to validate it with the command:  @validate <recipient> with <password>. If you do not receive the password email, try again or notify a wizard if this is a recurring problem.");
endif
.
#45:41
"Syntax:  @validate <recipient> with <password>";
"";
"This command is used to validate an email address set to receive expired messages that did not match the list owner's registered email address. When using the @register command, a password was sent via email to the address specified. This command is to verify that the password was received properly.";
if ((caller_perms() != #-1) && (caller_perms() != player))
return player:tell(E_PERM);
elseif (!$perm_utils:controls(player, this))
return player:tell(E_PERM);
elseif (!this.registered_email)
return player:tell("No email address has even been set for ", this:mail_name(), ".");
elseif (this.email_validated)
return player:tell("The email address for ", this:mail_name(), " has already been validated.");
elseif (!iobjstr)
return player:tell("Usage:  @validate <recipient> with <password>");
elseif (iobjstr != this.validation_password)
return player:tell("That is not the correct password.");
else
this.email_validated = 1;
player:tell("Password validated. Messages that expire after ", (this.expire_period == 0) ? "never" | $time_utils:english_time(this.expire_period), " from ", this:mail_name(), " will be emailed to ", this.registered_email, ".");
endif
.
#46: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 (is_player(recip) && `recip:refuses_action(from, "mail") ! E_VERBNF')
player:tell(recip:mail_refused_msg());
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
.
#46:1
"sends_to(from,addr,rcpt[,seen]) ==> true iff mail sent to addr passes through rcpt.";
{from, addr, rcpt, ?seen = {}} = args;
if (addr == rcpt)
return 1;
elseif (!(addr in seen))
seen = {@seen, addr};
for a in ((typeof(fwd = this:mail_forward(addr, @from ? {} | {from})) == LIST) ? fwd | {})
if (this:sends_to(addr, a, rcpt, seen))
return 1;
endif
$command_utils:suspend_if_needed(0);
endfor
endif
return 0;
.
#46: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, to, orig_hdrs, msg} = args;
"...";
"... 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
.
#46: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, rcpts, from} = args;
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
set_task_perms($wiz_utils:random_wizard());
this:touch(rcpts);
actual_rcpts = resolve[2];
biffs = resolve[3];
results = {};
for recip in (actual_rcpts)
if ((ticks_left() < 10000) || (seconds_left() < 2))
"player:notify(tostr(recip))";
suspend(1);
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
"The following is because the scheduler can BITE ME. --Nosredna";
fork (0)
for b in (biffs)
if ((ticks_left() < 10000) || (seconds_left() < 2))
suspend(1);
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
"Last modified Thu Apr  8 12:12:21 1999 CDT by Wizard (#2).";
.
#46:4
who = args[1];
if ($object_utils:has_verb(who, verb))
return who:(verb)(@listdelete(args, 1));
else
return {};
endif
.
#46: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
.
#46:6
player:tell_lines(this.description);
for c in (this.contents)
c:look_self();
endfor
.
#46:7
"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.aliases)) && (what:description() != parent(what):description());
.
#46:8
"...make sure the list has at least one usable name.";
"...make sure none of the aliases are already taken.";
ok = 0;
for a in (args)
sub1 = strsub(a, "_", "-");
sub2 = strsub(a, "-", "_");
if (sub1 == sub2)
check = 0;
else
check = 1;
endif
if (index(a, " "))
elseif (rp = $mail_agent:reserved_pattern(a))
player:tell("Mailing list name \"", a, "\" uses a reserved pattern: ", rp[1]);
return 0;
elseif (valid(p = $mail_agent:match(a, #-1)) && (a in p.aliases))
player:tell("Mailing list name \"", a, "\" in use on ", p.name, "(", p, ")");
return 0;
elseif ((check && valid(p = $mail_agent:match(sub1, #-1))) && (sub1 in p.aliases))
player:tell("Mailing list name \"", sub1, "\" in use on ", p.name, "(", p, ")");
return 0;
elseif ((check && valid(p = $mail_agent:match(sub2, #-1))) && (sub2 in p.aliases))
player:tell("Mailing list name \"", sub2, "\" in use on ", p.name, "(", p, ")");
return 0;
else
ok = 1;
endif
endfor
return ok;
.
#46:9
":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..$];
endif
if (valid(o = $string_utils:literal_object(string)) && ($mail_recipient in $object_utils:ancestors(o)))
return o;
elseif (rp = this:reserved_pattern(string))
return rp[2]:match_mail_recipient(string);
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
$command_utils:suspend_if_needed(2);
endfor
endif
endfor
return partial && $failed_match;
endif
.
#46:10
":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, ?me = player} = args;
if (valid(me) && ($failed_match != (o = me:my_match_recipient(string))))
return o;
elseif (!string)
return $nothing;
elseif ((string[1] == "*") && (string != "*"))
return this:match(@args);
elseif (string[1] == "`")
args[1][1..1] = "";
return $string_utils:match_player(@args);
elseif (valid(o = $string_utils:match_player(@args)) || (o == $ambiguous_match))
return o;
else
return this:match(@args);
endif
.
#46:11
{match_result, string, ?cmd_id = ""} = args;
cmd_id = cmd_id || "";
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;
.
#46:12
":make_message(sender,recipients,subject/replyto/additional-headers,body)";
" => message in the form as it will get sent.";
{from, recips, hdrs, body} = args;
fromline = this:name_list(from);
if (typeof(recips) != LIST)
recips = {recips};
endif
recips = this:name_list(@recips);
others = {};
replyto = 0;
if (typeof(hdrs) != LIST)
hdrs = {hdrs};
endif
subj = hdrs[1];
if (sender = valid(from) && ((!is_player(from)) && this:name(from.owner)))
others = {"Sender:   " + sender};
endif
replyto = {@hdrs, 0}[2] && this:name_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};
.
#46:13
what = args[1];
if (!valid(what))
name = "???";
elseif ((!is_player(what)) && $object_utils:has_callable_verb(what, "mail_name"))
name = what:mail_name();
else
name = what.name;
endif
return tostr(strsub(strsub(name, "(", ""), ")", ""), " (", what, ")");
.
#46:14
return $string_utils:english_list($list_utils:map_arg(this, "name", args), "no one");
.
#46:15
":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..$];
endwhile
return objects;
.
#46:16
":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());
{msg_seq, ?preamble = ""} = args;
cur = date = 0;
for x in (msgs = caller:messages_in_seq(msg_seq))
cur = x[1];
date = x[2][1];
player:display_message(preamble ? strsub(preamble, "%d", tostr(cur)) | {}, caller:msg_full_text(@x[2]));
if ((ticks_left() < 500) || (seconds_left() < 2))
suspend(0);
endif
endfor
return {cur, date};
.
#46: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 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());
{msg_seq, ?cur = 0, ?last_old = $maxint} = args;
keep_seq = {@$seq_utils:contract(caller:kept_msg_seq(), $seq_utils:complement(msg_seq, 1, caller:length_all_msgs())), $maxint};
k = 1;
mcount = 0;
width = player:linelen() || 79;
for x in (msgs = caller:messages_in_seq(msg_seq))
if (keep_seq[k] <= (mcount = mcount + 1))
k = k + 1;
endif
annot = ((d = x[2][1]) > last_old) ? "+" | ((k % 2) ? " " | "=");
line = tostr($string_utils:right(x[1], 4, (cur == x[1]) ? ">" | " "), ":", annot, " ", caller:msg_summary_line(@x[2]));
player:tell(line[1..min(width, $)]);
if ((ticks_left() < 500) || (seconds_left() < 2))
suspend(0);
endif
endfor
player:tell("----+");
.
#46:18
":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 ($command_utils:running_out_of_time())
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..$]};
$command_utils:suspend_if_needed(0, "... rmm ...");
save_kept = $seq_utils:intersection(caller.messages_kept, seq);
$command_utils:suspend_if_needed(0, "... rmm ...");
new_kept = $seq_utils:contract(caller.messages_kept, seq);
$command_utils:suspend_if_needed(0, "... rmm ...");
caller.messages_going = save_kept ? {save_kept, save} | save;
caller.messages = new;
caller.messages_kept = new_kept;
if ($object_utils:has_callable_verb(caller, "_fix_last_msg_date"))
caller:_fix_last_msg_date();
endif
return $seq_utils:tostr(nums);
.
#46:19
":undo_rmm()  restores previously deleted messages in .messages_going to .messages.";
set_task_perms(caller_perms());
old = caller.messages;
going = caller.messages_going;
new = seq = {};
last = 0;
next = 1;
"there are two possible formats here:";
"OLD: {{n,msgs},{n,msgs},...}";
"NEW: {kept_seq, {{n,msgs},{n,msgs},...}}";
if (going && ((!going[1]) || (typeof(going[1][2]) == INT)))
kept = going[1];
going = going[2];
else
kept = {};
endif
for s in (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..$]};
caller.messages_going = {};
caller.messages_kept = $seq_utils:union(kept, $seq_utils:expand(caller.messages_kept, seq));
if ($object_utils:has_callable_verb(caller, "_fix_last_msg_date"))
caller:_fix_last_msg_date();
endif
return seq;
.
#46:20
":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());
cmg = caller.messages_going;
if (cmg && ((!cmg[1]) || (typeof(cmg[1][2]) == INT)))
kept = cmg[1];
cmg = cmg[2];
else
kept = {};
endif
if (verb == "expunge_rmm")
caller.messages_going = {};
count = 0;
for s in (cmg)
count = count + length(s[2]);
endfor
return count;
elseif (!cmg)
return 0;
else
msgs = seq = {};
next = 1;
for s in (cmg)
msgs = {@msgs, @s[2]};
seq = {@seq, next = next + s[1], next = next + length(s[2])};
endfor
kept = {@$seq_utils:contract(kept, $seq_utils:complement(seq, 1, $seq_utils:last(seq))), $maxint};
k = 1;
mcount = 0;
for x in (msgs)
if (kept[k] <= (mcount = mcount + 1))
k = k + 1;
endif
player:tell($string_utils:right(x[1], 4), ":", (k % 2) ? "  " | "= ", caller:msg_summary_line(@x[2]));
if ((ticks_left() < 500) || (seconds_left() < 2))
suspend(0);
endif
endfor
if (msgs)
player:tell("----+");
endif
return length(msgs);
endif
.
#46:21
":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 = 0} = args;
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
.
#46:22
":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))
c = player:ctime(args[1]);
date = c[5..11] + c[21..25];
else
date = player:ctime(args[1])[5..16];
endif
from = args[2];
if (args[4] != " ")
subject = args[4];
endif
return tostr(date, "   ", $string_utils:left(from, 20), "   ", subject);
.
#46: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());
{strings, ?cur = 0, ?last_old = 0} = args;
if (!(nummsgs = caller:length_all_msgs()))
return "%f %<has> no messages.";
elseif (typeof(strings) != LIST)
strings = {strings};
endif
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..$];
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..$])) != INT)
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 = toint(string[c + 1..$]))
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 ? toint(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 ? toint(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 = toint(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 been removed." | tostr("%f %<has> no message numbered `", string, "'.");
endif
elseif (((i = index(string, "..")) > 1) || ((i = index(string, "-")) > 1))
if ((start = toint(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..$]};
endif
j = (string[i] == ".") ? i + 2 | (i + 1);
if ((end = toint(est = string[j..$])) > 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..$]};
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..$]};
endif
endif
endfor
return {$seq_utils:union(result, seq)};
.
#46: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;
.
#46: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) || ((!toint(words[1])) || (E_TYPE == (year = $code_utils:toint({@words, "-1"}[3]))))))
time = "Date should be of the form `5-Jan', `5-Jan-92', `Wed',`Wednesday'";
else
day = toint(words[1]);
time = $time_utils:dst_midnight($time_utils:from_month(words[2], -1, day));
if (length(words) == 3)
thisyear = toint(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;
.
#46:26
":new_message_num() => number that the next incoming message will receive.";
set_task_perms(caller_perms());
new = (msgs = caller.messages) ? msgs[$][1] + 1 | 1;
if (rmsgs = caller.messages_going)
if ((!rmsgs[1]) || (typeof(rmsgs[1][2]) == INT))
rmsgs = rmsgs[2];
endif
lbrm = rmsgs[$][2];
return max(new, lbrm[$][1] + 1);
else
return new;
endif
.
#46:27
set_task_perms(caller_perms());
return length(caller.messages);
.
#46: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
.
#46: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
.
#46: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);
.
#46: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);
.
#46:32
":from_msg_seq(object or list[,mask])";
" => msg_seq of messages from any of these senders";
set_task_perms(caller_perms());
{plist, ?mask = {1}} = args;
if (typeof(plist) != LIST)
plist = {plist};
endif
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..$];
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 "));
.
#46: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());
{nlist, ?mask = {1}} = args;
if (typeof(nlist) != LIST)
nlist = {nlist};
endif
i = 1;
fseq = {};
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..$];
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 "));
.
#46:34
":to_msg_seq(object or list[,mask]) => msg_seq of messages to those people";
set_task_perms(caller_perms());
{plist, ?mask = {1}} = args;
if (typeof(plist) != LIST)
plist = {plist};
endif
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..$];
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 "));
.
#46: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());
{nlist, ?mask = {1}} = args;
if (typeof(nlist) != LIST)
nlist = {nlist};
endif
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 n in (nlist)
if (index(toline, n))
seq = $seq_utils:add(seq, i, i);
endif
endfor
else
mask = mask[3..$];
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 "));
.
#46:36
":subject_msg_seq(target) => msg_seq of messages with target in the Subject:";
set_task_perms(caller_perms());
{target, ?mask = {1}} = args;
i = 1;
seq = {};
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..$];
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return seq || (("%f %<has> no messages with subjects containing `" + target) + "'");
.
#46:37
":body_msg_seq(target[,mask]) => msg_seq of messages with target in the body";
set_task_perms(caller_perms());
{target, ?mask = {1}} = args;
i = 1;
seq = {};
for msg in (caller.messages)
if ((!mask) || (i < mask[1]))
elseif ({@mask, $maxint}[2] <= i)
mask = mask[3..$];
"Old code follows. Lets save ticks and munge up the whole message body into one big string and index it. Don't need to know where target is in there, just that it is or isn't there";
elseif (((bstart = "" in (msg = msg[2])) && (length(msg) > bstart)) && index(tostr(@msg[bstart + 1..$]), target))
seq = $seq_utils:add(seq, i, i);
"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.");
.
#46: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
.
#46:39
":to_text(@msg) => message in text form (suitable for printing)";
return {"Date:     " + ctime(args[1]), "From:     " + args[2], "To:       " + args[3], @(args[4] == " ") ? {} | {"Subject:  " + args[4]}, @args[5..$]};
.
#46:40
what = args[1];
if ($object_utils:isa(what, $mail_recipient))
return what:(verb)(@listdelete(args, 1));
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(args[2], what);
endif
.
#46:41
":reserved_pattern(string)";
"  if string matches one of the reserved patterns for mailing list names, ";
"  we return that element of .reserved_patterns.";
string = args[1];
for p in (this.reserved_patterns)
if (match(string, p[1]))
return p;
endif
endfor
return 0;
.
#46:42
return valid(what = args[1]) && (($mail_recipient_class in (ances = $object_utils:ancestors(what))) || ($mail_recipient in ances));
.
#46:43
":keep_message_seq(msg_seq)";
"...If msg_seq nonempty {}, this marks the indicated messages on this folder (caller)";
"...as immune from expiration.";
"...If msg_seq == {}, this clears all such marks.";
set_task_perms(caller_perms());
msg_seq = args[1];
if (!msg_seq)
caller.messages_kept = {};
return 1;
endif
prev_kept = caller.messages_kept;
caller.messages_kept = new_kept = $seq_utils:union(prev_kept, msg_seq);
added = $seq_utils:intersection(new_kept, $seq_utils:complement(prev_kept));
if (!added)
return "";
endif
"... urk.  now we need to get the actual numbers of the messages being kept.";
nums = {};
start = 0;
for a in (added)
nums = {@nums, (start = !start) ? caller:messages_in_seq(a)[1] | (caller:messages_in_seq(a - 1)[1] + 1)};
endfor
return $seq_utils:tostr(nums);
.
#46:44
":kept_msg_seq([mask])";
" => msg_seq of messages that are marked kept";
":unkept_msg_seq([mask])";
" => msg_seq of messages that are not marked kept";
set_task_perms(caller_perms());
{?mask = {1}} = args;
if (k = verb == "kept_msg_seq")
kseq = $seq_utils:intersection(mask, caller.messages_kept);
else
kseq = $seq_utils:intersection(mask, $seq_utils:range(1, caller:length_all_msgs()), $seq_utils:complement(caller.messages_kept));
endif
return kseq;
.
#46:45
"NEW";
":set_mail_name(object,parent,name)";
":add_mail_name(object,parent,name)";
"both add the parent/name pair to object.names if necessary";
"set_mail_name indicates that this is to be the primary name.";
"return true if successful, error if not.";
{object, parent, name} = args;
if ((((index(name, "(") || index(name, ")")) || index(name, ":")) || index(name, " ")) || index(name, "*"))
return E_INVARG;
elseif ((caller != object) && (!object:is_writable_by(caller_perms())))
return E_PERM;
elseif (this:includes(object, parent))
"... Don't introduce cycles!";
return E_RECMOVE;
elseif (!this:_accept_subname(parent, object, name, caller_perms()))
return E_NACC;
else
return $mail_name_db:add(object, parent, name, verb == "add_mail_name");
endif
.
#46:46
"NEW";
":remove_mail_name(object,parent,name)";
"removes parent/name pair from object.names if necessary.";
"return true if successful, error if not.";
{object, parent, name} = args;
if ((caller in {object, parent}) || (object:is_writable_by(caller_perms()) || (valid(parent) ? parent:is_writable_by(caller_perms()) | $perm_utils:controls(caller_perms(), this))))
$mail_name_db:remove(object, parent, name);
return 1;
else
return E_PERM;
endif
.
#46:47
"NEW";
":match(string[,player]) => mail recipient matching string.";
" (if player supplied, includes player.mail_lists)";
if (!(string = args[1]))
return $nothing;
elseif (string[1] == "*")
string = string[2..$];
endif
string = strsub(string, "_", "-");
if (valid(o = $string_utils:literal_object(string)) && $object_utils:isa(o, $mail_recipient))
return o;
elseif (rp = this:reserved_pattern(string))
"...This is going away REAL SOON...";
return rp[2]:match_mail_recipient(string);
else
c = index(string + ":", ":");
if (c == 1)
first = #-1;
elseif (!(first = $mail_name_db:find(":" + (name = string[1..c - 1]))))
return first;
elseif (length(first) > 1)
return $ambiguous_match;
else
first = first[1];
endif
if (c > length(string))
return first;
else
string[1..c] = "";
return this:match_subname(first, string);
endif
endif
.
#46:48
":includes(object,subobject)";
"Is subobject (nonstrictly) a subname of object?";
{object, sub} = args;
if (!valid(sub))
return 0;
elseif (object == sub)
return 1;
else
for p in (sub.names)
if (this:includes(object, p[1]))
return 1;
endif
endfor
endif
.
#46:49
":_accept_subname(parent,object,name[,perms])";
"determines if <perms> can add <object> as a subname of <parent> with the given <name>";
parent = args[1];
return (valid(parent) || (parent == #-1)) && ($perm_utils:controls({@args, $no_one}[4], this) || parent:accept_subname(@listdelete(args, 1)));
.
#46:50
":match_subname(recip,string)";
{recip, string} = args;
while (valid(recip = $mail_name_db:find(tostr(recip, ":", string[1..(c = index(string + ":", ":")) - 1]))))
if (c > length(string))
return recip;
endif
string[1..c] = "";
endwhile
return recip;
.
#46:51
":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, ?me = player} = args;
if (valid(me) && ($failed_match != (o = me:my_match_recipient(string))))
return o;
elseif (!string)
return $nothing;
elseif ((string[1] == "*") && (string != "*"))
return this:match(@args);
else
if (player_only = string[1] == "`")
string[1..1] = "";
endif
c = index(string + ":", ":");
who = $string_utils:match_player(string[1..c - 1], me);
if ((who == $failed_match) && (!player_only))
return this:match(@args);
elseif ((!valid(who)) || (c > length(string)))
return who;
else
string[1..c] = "";
return this:match_subname(who, string);
endif
endif
.
#46:52
":msg_seq_to_msg_num_string(msg_seq) => string giving the corresponding message numbers";
set_task_perms(caller_perms());
return $seq_utils:tostr($seq_utils:from_list($list_utils:slice(caller:messages_in_seq(args[1]))));
.
#46:53
":msg_seq_to_msg_num_list(msg_seq) => list of corresponding message numbers";
set_task_perms(caller_perms());
return $list_utils:slice(caller:messages_in_seq(args[1]));
.
#46:54
"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.";
"KLUDGE.  this may go away.";
"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)
text = $mail_agent:make_message(from, to, hdrs, msg);
return this:raw_send(text, to, perms);
else
return E_PERM;
endif
.
#46:55
":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 = listdelete(args, 1);
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..$]};
.
#46:56
"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, new_rcpts, from, to, hdrs, body} = args;
if (typeof(hdrs) != LIST)
hdrs = {hdrs, 0};
elseif (length(hdrs) < 2)
hdrs = {@hdrs || {""}, 0};
endif
hdrs[3..2] = {{"Resent-By", this:name_list(new_sender)}, {"Resent-To", this:name_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
.
#46:57
if (caller_perms().wizard)
this.reserved_patterns = {};
pass(@args);
endif
.
#47:0
return this:ok(who = args[1]) && tostr("a letter ", this:sending(who) ? "(in transit) " | "", "to ", this:recipient_names(who), (subject = `this.subjects[who] ! ANY') && tostr(" entitled \"", subject, "\""));
.
#47: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 = ">  ";
include = {@include, prefix};
else
include = {@include, @this:fill_string(">  " + line, 70, prefix)};
endif
endfor
endif
return {@to_subj, args[5], include};
endif
return 0;
.
#47:2
{who, recip, subj, replyto, msg} = args;
if (this:ok(who))
this.sending[who] = 0;
this.recipients[who] = recip;
this.subjects[who] = subj;
this.replytos[who] = replyto || {};
this:load(who, msg);
this.active[who]:tell("Composing ", this:working_on(who));
p = this.active[who];
"if (p:mail_option(\"enter\") && !args[5])";
"Changed from above so that @reply can take advantage of @mailoption +enter. Ho_Yan 11/9/94";
if (p:mail_option("enter"))
if (typeof(lines = $command_utils:read_lines()) == ERR)
p:tell(lines);
else
this:insert_line(p in this.active, lines, 0);
endif
endif
endif
.
#47:3
if (!dobjstr)
plyr = player;
elseif ($command_utils:player_match_result(plyr = $string_utils: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
.
#47: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));
.
#47: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
.
#47: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
.
#47:7
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
elseif ((!(task = this.sending[who])) || $code_utils:task_valid(task))
return task;
else
"... uh oh... sending task crashed...";
this:set_changed(who, 1);
return this.sending[who] = 0;
endif
.
#47:8
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
.
#47:9
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
.
#47:10
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
else
recips = this.recipients[who];
nonmrs = {};
mrs = {};
for o in (recips)
if ($object_utils:isa(o, $mail_recipient))
mrs = {@mrs, o};
else
nonmrs = {@nonmrs, o};
endif
endfor
for a in (args)
if (!a)
player:tell("\"\"?");
return;
elseif (valid(aobj = $mail_agent:match_recipient(a)) && (aobj in recips))
elseif ($failed_match != (aobj = $string_utils:literal_object(a)))
if (!(aobj in recips))
player:tell(aobj, " was not a recipient.");
return;
endif
elseif ((a[1] == "*") && valid(aobj = $string_utils:match(a[2..$], mrs, "aliases")))
elseif ((a[1] != "*") && valid(aobj = $string_utils:match(a, nonmrs, "aliases")))
elseif (valid(aobj = $string_utils:match(a, recips, "aliases")))
else
player:tell("couldn't find \"", a, "\" in To: list.");
return;
endif
recips = setremove(recips, aobj);
endfor
this.recipients[who] = recips;
this:set_changed(who, 1);
player:tell("Your message is now to ", this:recipient_names(who), ".");
endif
.
#47:11
"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;
cmd_id = cmd_id || "";
for s in ((typeof(l) == LIST) ? l | {l})
if (typeof(s) != STR)
if ($mail_agent:is_recipient(s))
recips = setadd(recips, s);
else
player:tell(cmd_id, s, " is not a valid mail recipient.");
endif
elseif (!$mail_agent:match_failed(md = $mail_agent:match_recipient(s), s, cmd_id))
recips = setadd(recips, md);
endif
endfor
return recips;
.
#47:12
return this:ok(who = args[1]) && $mail_agent:name_list(@this.recipients[who]);
.
#47:13
return $mail_agent:make_message(@args);
.
#47:14
"(obsolete verb... see $mail_agent:name_list)";
return $mail_agent:(verb)(@args[1]);
.
#47:15
"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, flags} = args;
replyall = "all" in flags;
objects = {};
if ("followup" in flags)
"...look for first non-player recipient in To: line...";
for o in ($mail_agent:parse_address_field(msg[3]))
if (objects)
break o;
elseif ($object_utils:isa(o, $mail_recipient))
objects = {o};
endif
endfor
endif
objects = 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};
.
#47:16
flags = {};
for o in ({"all", "include", "followup"})
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 +3#followup ";
for a in (args)
if (i = index(a, "="))
value = a[i + 1..$];
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", "followup"}[j];
flags = (flaglist[i - 2] == "+") ? setadd(flags, f) | setremove(flags, f);
if (f == "all")
flags = setremove(flags, "followup");
endif
elseif ((!value) || (value = this:parse_recipients({}, $string_utils:explode(value), "replyto flag:  ")))
reply_to = value || {};
endif
endfor
return {flags, reply_to};
.
#47:17
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
.
#47:18
"WIZARDLY";
if (!(who = this:loaded(player)))
player:notify(this:nothing_loaded_msg());
elseif (!(recips = this.recipients[who]))
player:notify("Umm... your message isn't addressed to anyone.");
elseif (this:sending(who))
player:notify("Again? ... relax... it'll get there eventually.");
else
msg = this:message_with_headers(who);
this.sending[who] = old_sending = task_id();
this:set_changed(who, 0);
player:notify("Sending...");
"... this sucker can suspend BIG TIME...";
result = $mail_agent:raw_send(msg, recips, player);
"... the world changes...";
who = player in this.active;
if (who && (this.sending[who] == old_sending))
"... same editing session; no problemo...";
previous = "";
this.sending[who] = 0;
else
"... uh oh, different session... tiptoe quietly out...";
"... Don't mess with the session.";
previous = "(prior send) ";
endif
if (!(e = result[1]))
player:notify(tostr(previous, (typeof(e) == ERR) ? e | ("Bogus recipients:  " + $string_utils:from_list(result[2]))));
player:notify(tostr(previous, "Mail not sent."));
previous || this:set_changed(who, 1);
elseif (length(result) == 1)
player:notify(tostr(previous, "Mail not actually sent to anyone."));
previous || this:set_changed(who, 1);
else
player:notify(tostr(previous, "Mail actually sent to ", $mail_agent:name_list(@listdelete(result, 1))));
if (previous)
"...don't even think about it...";
elseif (player.location == this)
if (ticks_left() < 10000)
suspend(0);
endif
this:done();
elseif (!this:changed(who))
"... player is gone, no further edits...";
this:kill_session(who);
endif
endif
endif
.
#47:19
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
.
#47:20
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
.
#47:21
player:tell("This command is obsolete.  Use @subscribe instead.  See `help @subscribe'");
return;
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].name, " to ", iobj.name, ":  ", e);
endif
endfor
if (added)
player:tell($string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%(name) (%#)", added)), " added to ", iobj.name, " (", iobj, ")");
endif
.
#47:22
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)
player:tell($string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%(name) (%#)", removed)), " removed from ", iobj.name, " (", iobj, ")");
endif
.
#47:23
return this:ok(who = args[1]) && (this:sending(who) || pass(@args));
.
#47:24
"recall that this only gets called if :retain_session_on_exit returns true";
return (this:ok(who = player in this.active) && (!this:changed(who))) ? {"Your message is in transit."} | this.(verb);
.
#47:25
lines = {"To:       " + (toline = $mail_agent:name_list(@args[1])), "Subject:  " + $string_utils:trim(subject = args[2])};
if (args[3])
lines = {@lines, "Reply-to: " + $mail_agent:name_list(@args[3])};
endif
lines = {@lines, "", @args[4]};
return {tostr("MOOMail", subject ? ("(" + subject) + ")" | (("-to(" + toline) + ")")), lines, "@@sendmail"};
.
#48: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
.
#48: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);
strmode = (length(text) <= 1) && this.strmode[who];
if (strmode)
text = text ? text[1] | "";
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 `mode 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), strmode ? " as a single string." | ".");
this:set_changed(who, 0);
endif
.
#48:2
if (this:ok(who = args[1]))
this.strmode[who] = strmode = typeof(text = args[3]) == STR;
this:load(who, strmode ? text ? {text} | {} | text);
this.objects[who] = args[2];
player:tell("Now editing ", this:working_on(who), ".", strmode ? "  [string mode]" | "");
endif
.
#48: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, ")");
.
#48: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]) + "          (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;
.
#48: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();
else
text = `spec[1].(spec[2]) ! ANY';
endif
if (((tt = typeof(text)) in {ERR, STR}) || ((tt == LIST) && ((!text) || (typeof(text[1]) == STR))))
return text;
else
return E_TYPE;
endif
.
#48:6
"WIZARDLY";
if ((caller != $note_editor) || (caller_perms() != $note_editor.owner))
return E_PERM;
endif
set_task_perms(player);
attempt = E_NONE;
if (typeof(spec = args[1]) == OBJ)
return spec:set_text(args[2]);
elseif ($object_utils:has_callable_verb(spec[1], "set_" + spec[2]))
attempt = spec[1]:("set_" + spec[2])(args[2]);
endif
if (typeof(attempt) == ERR)
return `spec[1].(spec[2]) = args[2] ! ANY';
else
return attempt;
endif
.
#48: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))
player:tell(object, " has no \".", prop, "\" property.");
else
return {note, prop};
endif
elseif ((!$object_utils:has_callable_verb(note, "text")) || (!$object_utils:has_callable_verb(note, "set_text")))
return {note, "description"};
"... what we used to do.  but why barf?   that's no fun...";
player:tell(object, "(", note, ") doesn't look like a note.");
else
return note;
endif
return 1;
.
#48:8
pass(@args);
if ((who = this:loaded(player)) && this.strmode[who])
player:tell("Text will be stored as a single string instead of a list when possible.");
endif
.
#48:9
"mode [string|list]";
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
endif
if (dobjstr && (index("string", dobjstr) == 1))
this.strmode[who] = mode = 1;
player:tell("Now in string mode:");
elseif (dobjstr && (index("list", dobjstr) == 1))
this.strmode[who] = mode = 0;
player:tell("Now in list mode:");
elseif (dobjstr)
player:tell("Unrecognized mode:  ", dobjstr);
player:tell("Should be one of `string' or `list'");
return;
else
player:tell("Currently in ", mode = this.strmode[who] ? "string " | "list ", "mode:");
endif
if (mode)
player:tell("  store text as a single string instead of a list when possible.");
else
player:tell("  always store text as a list of strings.");
endif
.
#48:10
{what, text} = args;
cmd = (typeof(text) == STR) ? "@set-note-string" | "@set-note-text";
name = (typeof(what) == OBJ) ? what.name | tostr(what[1].name, ".", what[2]);
note = (typeof(what) == OBJ) ? what | tostr(what[1], ".", what[2]);
return {name, text, tostr(cmd, " ", note)};
.
#48:11
if ($perm_utils:controls(caller_perms(), this))
return pass(@args);
else
return E_PERM;
endif
.
#49:0
if (!args)
player:tell("edit what?");
else
this:invoke(argstr, verb);
endif
.
#49:1
pas = {{}, {}};
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
elseif (!args)
object = this.objects[who];
vname = this.verbnames[who];
if (typeof(vname) == LIST)
vargs = listdelete(vname, 1);
vname = vname[1];
else
vargs = {};
endif
changeverb = 0;
elseif ((args[1] != "as") || ((length(args) < 2) || ((!(spec = $code_utils:parse_verbref(args[2]))) || ((typeof(pas = $code_utils:parse_argspec(@args[3..$])) != LIST) || pas[2]))))
if (typeof(pas) != LIST)
player:tell(pas);
elseif (pas[2])
player:tell("I don't understand \"", $string_utils:from_list(pas[2], " "), "\"");
endif
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];
vargs = pas[1] && {@pas[1], "none", "none"}[1..3];
if (vargs)
vargs[2] = $code_utils:full_prep(vargs[2]) || vargs[2];
endif
changeverb = 1;
endif
if (vargs)
vnum = $code_utils:find_verb_named(object, vname);
while (vnum && (verb_args(object, vnum) != vargs))
vnum = $code_utils:find_verb_named(object, vname, vnum + 1);
endwhile
if (!vnum)
player:tell("There is no ", object, ":", vname, " verb with args (", $string_utils:from_list(vargs, " "), ").");
if (!changeverb)
player:tell("Use 'compile as ...' to write your code to another verb.");
endif
return;
endif
objverbname = tostr(object, ":", vname, " (", $string_utils:from_list(vargs, " "), ")");
else
vnum = 0;
objverbname = tostr(object, ":", ($code_utils:toint(vname) == E_TYPE) ? vname | this:verb_name(object, vname));
endif
"...";
"...perform eval_subs on verb code if necessary...";
"...";
if (player.eval_subs && player:edit_option("eval_subs"))
verbcode = {};
for x in (this:text(who))
verbcode = {@verbcode, $code_utils:substitute(x, player.eval_subs)};
endfor
else
verbcode = this:text(who);
endif
"...";
"...write it out...";
"...";
if (result = this:set_verb_code(object, vnum ? vnum | vname, verbcode))
player:tell(objverbname, " 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 " + objverbname) + ".", ("The verb " + objverbname) + " does not exist (!?!)", ("The object " + tostr(object)) + " does not exist (!?!)"}[1 + (result in {E_PERM, E_VERBNF, E_INVARG})]);
if (!changeverb)
player:tell("Do 'compile as <object>:<verb>' to write your code to another verb.");
endif
changeverb = 0;
else
player:tell(objverbname, verbcode ? " successfully compiled." | " verbcode removed.");
this:set_changed(who, 0);
if ($code_utils:update_last_modified(object, vnum || vname))
player:notify("** Time-stamping failed.");
endif
endif
if (changeverb)
this.objects[who] = object;
this.verbnames[who] = vargs ? {vname, @vargs} | vname;
endif
"Last modified Sun Jul 20 07:48:13 1997 CDT by Wizard (#2).";
.
#49:2
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
else
object = this.objects[who];
verbname = this.verbnames[who];
if (typeof(verbname) == LIST)
return tostr(object, ":", verbname[1], " (", $string_utils:from_list(listdelete(verbname, 1), " "), ")");
else
return tostr(object, ":", this:verb_name(object, verbname), " (", this:verb_args(object, verbname), ")");
endif
endif
"return this:ok(who = args[1]) && tostr(this.objects[who]) + \":\" + this.verbnames[who];";
.
#49:3
{who, object, vname, vcode} = args;
if (this:ok(who))
this:load(who, vcode);
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, \":\", vname, \".\");";
endif
.
#49: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]);
if ((!vref) || (!(spec = $code_utils:parse_verbref(vref[1]))))
player:tell("Usage: ", args[2], " object:verb");
return;
endif
if (argspec = listdelete(vref, 1))
if (typeof(pas = $code_utils:parse_argspec(@argspec)) == LIST)
if (pas[2])
player:tell("I don't understand \"", $string_utils:from_list(pas[2], " "), "\"");
return;
endif
argspec = {@pas[1], "none", "none"}[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]))
vnum = $code_utils:find_verb_named(object, vname = spec[2]);
if (argspec)
while (vnum && (verb_args(object, vnum) != argspec))
vnum = $code_utils:find_verb_named(object, vname, vnum + 1);
endwhile
endif
if (vnum)
code = this:fetch_verb_code(object, vnum);
else
code = E_VERBNF;
endif
if (typeof(code) == ERR)
player:tell((code != E_VERBNF) ? code | "That object does not define that verb", argspec ? " with those args." | ".");
return code;
else
return {object, argspec ? {vname, @argspec} | vname, code};
endif
endif
return 0;
.
#49:5
"WIZARDLY";
if ((caller != $verb_editor) || (caller_perms() != $verb_editor.owner))
return E_PERM;
else
set_task_perms(player);
return `verb_code(args[1], args[2], !player:edit_option("no_parens")) ! ANY';
endif
.
#49:6
"WIZARDLY";
if ((caller != $verb_editor) || (caller_perms() != $verb_editor.owner))
return E_PERM;
else
set_task_perms(player);
return `set_verb_code(args[1], args[2], args[3]) ! ANY';
endif
.
#49:7
if (caller == $verb_editor)
set_task_perms(player);
endif
{object, vname, code} = args;
if (typeof(vname) == LIST)
vargs = tostr(" ", vname[2], " ", $code_utils:short_prep(vname[3]), " ", vname[4]);
vname = vname[1];
else
vargs = "";
endif
name = tostr(object.name, ":", vname);
"... 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.";
"vname = strsub($string_utils:explode(verb_info(object, vname)[3])[1], \"*\", \"\")";
"vargs = verb_args(object, vname)";
"";
return {name, code, tostr("@program ", object, ":", vname, vargs)};
.
#49: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.";
if ((caller != $verb_editor) || (caller_perms() != $verb_editor.owner))
return E_PERM;
else
set_task_perms(player);
given = args[2];
if (typeof(info = `verb_info(args[1], given) ! ANY') == ERR)
return tostr(given, "[", info, "]");
elseif (info[3] == given)
return given;
else
return tostr(given, "/\"", info[3], "\"");
endif
endif
.
#49:9
"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.";
if ((caller != $verb_editor) || (caller_perms() != $verb_editor.owner))
return E_PERM;
else
set_task_perms(player);
return $string_utils:from_list(`verb_args(args[1], args[2]) ! ANY', " ");
endif
.
#49:10
"Syntax: comment [<range>]";
"";
"Turns the specified range of lines, into comments.";
if ((caller != player) && (caller_perms() != player))
return E_PERM;
elseif (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
elseif (typeof(range = this:parse_range(who, {"."}, @args)) != LIST)
player:tell(tostr(range));
elseif (range[3])
player:tell_lines($code_utils:verb_documentation());
else
text = this.texts[who];
{from, to, crap} = range;
cut = $maxint;
for line in [from..to]
cut = min(cut, `match(text[line], "[^ ]")[1] ! E_RANGE => 1');
endfor
for line in [from..to]
text[line] = toliteral(text[line][cut..$]) + ";";
endfor
this.texts[who] = text;
player:tell((to == from) ? "Line" | "Lines", " changed.");
this.changes[who] = 1;
this.times[who] = time();
endif
.
#49:11
"Syntax: uncomment [<range>]";
"";
"Turns the specified range of lines from comments to, uh, not comments.";
if ((caller != player) && (caller_perms() != player))
return E_PERM;
elseif (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
elseif (typeof(range = this:parse_range(who, {"."}, @args)) != LIST)
player:tell(tostr(range));
elseif (range[3])
player:tell_lines($code_utils:verb_documentation());
else
text = this.texts[who];
{from, to, crap} = range;
bogus = {};
for line in [from..to]
if (match(text[line], "^ *\"%([^\\\"]%|\\.%)*\";$"))
"check from $code_utils:verb_documentation";
if (!bogus)
text[line] = $no_one:eval(text[line])[2];
endif
else
bogus = setadd(bogus, line);
endif
endfor
if (bogus)
player:tell((length(bogus) == 1) ? "Line" | "Lines", " ", $string_utils:english_list(bogus), " ", (length(bogus) == 1) ? "is" | "are", " not comments.");
player:tell("No changes.");
return;
endif
this.texts[who] = text;
player:tell((to == from) ? "Line" | "Lines", " changed.");
this.changes[who] = 1;
this.times[who] = time();
endif
.
#50: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
.
#50: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
.
#50:2
if (!this:loaded(player))
player:tell(this:nothing_loaded_msg());
else
lines = $command_utils:read_lines();
if (typeof(lines) == ERR)
player:notify(tostr(lines));
return;
endif
this:insert_line(this:loaded(player), lines, 0);
endif
.
#50: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 = $string_utils: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
.
#50:4
if (i = index(argstr, "\""))
text = argstr[i + 1..$];
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);
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
.
#50: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..$]};
if (!this.changes[who])
this.changes[who] = 1;
this.times[who] = time();
endif
this.inserting[who] = from;
endif
.
#50: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
.
#50: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)..$];
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..$]};
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
.
#50: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..$]))
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
.
#50: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
.
#50:10
fill_column = 70;
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] && ((range[3][1] != "@") || ((fill_column = toint(range[3][2..$])) < 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..$]};
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
.
#50: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
.
#50: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
.
#50: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
.
#50:14
if (!(caller in {this, player}))
return E_PERM;
elseif (!(who = player in this.active))
player:tell("You are not actually in ", this.name, ".");
return;
elseif (!valid(origin = this.original[who]))
player:tell("I don't know where you came here from.");
else
player:moveto(origin);
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 a teleporter.");
.
#50: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..$] + (argstr && " ")) + argstr;
return this:subst();
elseif ("/" == verb[1])
argstr = (verb + (argstr && " ")) + argstr;
return this:find();
else
pass(@args);
endif
.
#50:16
return this:ok(who = args[1]) && this.inserting[who];
.
#50:17
return this:ok(who = args[1]) && ((((ins = toint(args[2])) < 1) ? E_INVARG | ((ins <= (max = length(this.texts[who]) + 1)) || (ins = max))) && (this.inserting[who] = ins));
.
#50:18
return this:ok(who = args[1]) && this.changes[who];
.
#50:19
return this:ok(who = args[1]) && (((unchanged = !args[2]) || (this.times[who] = time())) && (this.changes[who] = !unchanged));
.
#50:20
return this:ok(who = args[1]) && this.original[who];
.
#50:21
return this:ok(who = args[1]) && (((valid(origin = args[2]) && (origin != this)) || ((origin == $nothing) || E_INVARG)) && (this.original[who] = origin));
.
#50:22
return (((who = args[1]) < 1) || (who > length(this.active))) ? E_RANGE | this.readable[who];
.
#50:23
return this:ok(who = args[1]) && (this.readable[who] = !(!args[2]));
.
#50:24
{?who = player in this.active} = args;
return (this:readable(who) || this:ok(who)) && this.texts[who];
.
#50: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();
.
#50:26
"Dummy routine.  The child editor should provide something informative";
return this:ok(who = args[1]) && (("something [in " + this.name) + "]");
.
#50: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
.
#50:28
return ((who = args[1] in this.active) && (typeof(this.texts[who]) == LIST)) && who;
.
#50: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
.
#50: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(args[1]) != INT)
args = {player in this.active, @args};
endif
{who, lines, ?quiet = this.active[who]:edit_option("quiet_insert")} = args;
if (!(fuckup = this:ok(who)))
return fuckup;
elseif (typeof(text = this.texts[who]) != LIST)
return E_NONE;
else
if (typeof(lines) != LIST)
lines = {lines};
endif
p = this.active[who];
insert = this.inserting[who];
this.texts[who] = {@text[1..insert - 1], @lines, @text[insert..$]};
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
.
#50: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(args[1]) != INT)
args = {player in this.active, @args};
endif
{who, string} = args;
if (!(fuckup = this:ok(who)))
return fuckup;
elseif ((append = this.inserting[who] - 1) < 1)
return this:insert_line(who, {string});
elseif (typeof(text = this.texts[who]) != LIST)
return E_NONE;
else
this.texts[who][append] = text[append] + string;
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
.
#50:32
{who, from, to, english} = args;
if (!(fuckup = this:ok(who)))
return fuckup;
elseif (from >= to)
return 0;
else
nline = "";
for line in ((text = this.texts[who])[from..to])
if (!english)
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..$]};
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
.
#50: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.";
{who, string, before} = args;
if (!(fuckup = this:ok(who)))
return fuckup;
endif
last = length(this.texts[who]);
ins = this.inserting[who] - 1;
after = !before;
if (!string)
return 0;
elseif ("." == string)
return ins + after;
elseif (!(i = index("_^$", string[slen = length(string)])))
return toint(string);
else
start = {ins + 1, ins, last + 1}[i];
n = 1;
if ((slen > 1) && (!(n = toint(string[1..slen - 1]))))
return 0;
elseif (i % 2)
return start - n;
else
return start + n;
endif
endif
.
#50: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..$], 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
.
#50: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, string} = args;
last = length(this.texts[who]) + 1;
ins = this.inserting[who];
if (i = index("-+", string[1]))
rest = string[2..$];
return ((n = toint(rest)) || (rest == "0")) ? {ins - n, ins + n}[i] | E_INVARG;
else
if (!(j = index(string, "^") || index(string, "_")))
offset = 0;
else
offset = (j == 1) || toint(string[1..j - 1]);
if (!offset)
return E_INVARG;
elseif (string[j] == "^")
offset = -offset;
endif
endif
rest = string[j + 1..$];
if (i = rest in {".", "$"})
return offset + {ins, last}[i];
elseif (!(n = toint(rest)))
return E_INVARG;
else
return (offset + (j && (string[j] == "^"))) + n;
endif
endif
.
#50:36
{cmd, ?recognized_flags = "gc", ?null_subst_msg = "Null substitution?"} = args;
if (!cmd)
return "/xxx/yyy[/[g][c]] [<range>] expected..";
endif
bchar = cmd[1];
cmd = cmd[2..$];
fromstr = cmd[1..(b2 = index(cmd + bchar, bchar, 1)) - 1];
cmd = cmd[b2 + 1..$];
tostr = cmd[1..(b2 = index(cmd + bchar, bchar, 1)) - 1];
cmd = cmd[b2 + 1..$];
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..$]};
.
#50:37
":invoke(...)";
"to find out what arguments this verb expects,";
"see this editor's parse_invoke verb.";
new = args[1];
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...";
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
spec = this:parse_invoke(@args);
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
.
#50: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();
who_obj:moveto(this);
if (who_obj.location == this)
try
"...forked, just in case loc:announce is broken...";
"changed to a try-endtry. Lets reduce tasks..Ho_Yan 12/20/96";
if (valid(loc) && (msg = this:depart_msg()))
loc:announce($string_utils:pronoun_sub(msg));
endif
except (ANY)
"Just drop it and move on";
endtry
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;
.
#50:39
"WIZARDLY";
{who_obj, from} = args;
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
.
#50: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
.
#50: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
.
#50: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
.
#50:43
return is_player(who_obj = args[1]) && (who_obj.wizard || pass(@args));
.
#50:44
who_obj = args[1];
if (who_obj.wizard && (!(who_obj in this.active)))
this:accept(who_obj);
endif
pass(@args);
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
.
#50:45
if (!(who = (who_obj = args[1]) in this.active))
elseif (this:retain_session_on_exit(who))
if (msg = this:no_littering_msg())
who_obj:tell_lines(msg);
endif
else
this:kill_session(who);
endif
pass(@args);
.
#50:46
"@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 ((caller_perms() != #-1) && (caller_perms() != player))
raise(E_PERM);
elseif (!$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 = toint(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
this:do_flush(t, "noisy");
endif
player:tell("Done.");
.
#50:47
if (!$perm_utils:controls(player, this))
player:tell(E_PERM);
return;
endif
if (i = index(dobjstr, "="))
default = dobjstr[i + 1..$];
prop = dobjstr[1..i - 1];
if (argstr[1 + index(argstr, "=")] == "\"")
elseif (default[1] == "#")
default = toobj(default);
elseif (index("0123456789", default[1]))
default = toint(default);
elseif (default == "{}")
default = {};
endif
else
default = 0;
prop = dobjstr;
endif
if (typeof(result = this:set_stateprops(prop, default)) == ERR)
player:tell((result == E_RANGE) ? tostr(".", prop, " needs to hold a list of the same length as .active (", length(this.active), ").") | ((result != E_NACC) ? result | (prop + " is already a property on an ancestral editor.")));
else
player:tell("Property added.");
endif
.
#50:48
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
.
#50:49
if ($perm_utils:controls(caller_perms(), this))
pass(@args);
this:kill_all_sessions();
endif
.
#50:50
if (caller_perms().wizard)
pass();
this:kill_all_sessions();
if (this == $generic_editor)
this.help = $editor_help;
endif
if ($object_utils:defines_verb(this, "is_not_banned"))
delete_verb(this, "is_not_banned");
endif
endif
.
#50:51
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) ! ANY => {}')
if (this:_stateprop_length(prop) != length(this.active))
return E_RANGE;
endif
this.stateprops = {{prop, args[2]}, @this.stateprops};
else
return $object_utils:has_property(this, prop) ? E_NACC | E_PROPNF;
endif
return 0;
.
#50:52
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") && (c[2] != "pass"))
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};
.
#50:53
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
.
#50:54
{objstr, ?who = player} = args;
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(objstr, who);
endif
endwhile
return who:my_match_object(objstr, #-1);
.
#50:55
who = args[1];
where = {#-1, @this.original}[1 + (who in this.active)];
return strsub(this.who_location_msg, "%L", `where:who_location_msg(who) ! ANY => "An Editor"');
return $string_utils:pronoun_sub(this.who_location_msg, who, this, where);
.
#50:56
return this.(verb);
.
#50:57
return;
.
#50:58
"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.";
{string, ?width = 1 + player:linelen(), ?prefix = ""} = args;
width = width + 1;
if (width < (3 + length(prefix)))
return E_INVARG;
endif
string = ("$" + string) + " $";
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;
.
#50:59
"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} = args;
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..$] + (argstr && " ")) + argstr;
this:subst();
return 1;
elseif ("/" == verb[1])
argstr = (verb + (argstr && " ")) + argstr;
this:find();
return 1;
else
return 0;
endif
.
#50:60
return $failed_match;
.
#50:61
":get_room([player])  => correct room to match in on invocation.";
{?who = player} = args;
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
.
#50:62
":invoke_local_editor(name, text, upload)";
"Spits out the magic text that invokes the local editor in the player's client.";
"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
{name, text, upload} = args;
if (typeof(text) == STR)
text = {text};
endif
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
.
#50:63
"+c properties on children cannot necessarily be read, so we need this silliness...";
if (caller != this)
return E_PERM;
else
return length(this.(args[1]));
endif
.
#50:64
txt = this:text(player in this.active);
if (typeof(txt) == LIST)
player:tell_lines(txt);
else
player:tell("Text unreadable:  ", txt);
endif
player:tell("--------------------------");
.
#50:65
return this:acceptable(who_obj = args[1]) && this:new_session(who_obj, who_obj.location);
.
#50:66
"Usage: yank from <note>";
"       yank <message-sequence> from <mail-recipient>";
"       yank from <object>:<verb>";
"       yank from <object>.<property>";
"Grabs the specified text and inserts it at the cursor.";
set_task_perms(player);
if (dobjstr)
"yank <message-sequence> from <mail-recipient>";
if (!(p = player:parse_mailread_cmd(verb, args, "", "from")))
return;
elseif ($seq_utils:size(sequence = p[2]) != 1)
player:notify(tostr("You can only ", verb, " one message at a time"));
return;
else
m = (folder = p[1]):messages_in_seq(sequence);
msg = m[1];
header = tostr("Message ", msg[1]);
if (folder != player)
header = tostr(header, " on ", $mail_agent:name(folder));
endif
header = tostr(header, ":");
lines = {header, @player:msg_full_text(@msg[2])};
this:insert_line(this:loaded(player), lines, 0);
endif
elseif (pr = $code_utils:parse_propref(iobjstr))
o = player:my_match_object(pr[1]);
if ($command_utils:object_match_failed(o, pr[1]))
return;
elseif ((lines = `o.(pr[2]) ! ANY') == E_PROPNF)
player:notify(tostr("There is no `", pr[2], "' property on ", $string_utils:nn(o), "."));
return;
elseif (lines == E_PERM)
player:notify(tostr("Error: Permission denied reading ", iobjstr));
return;
elseif (typeof(lines) == ERR)
player:notify(tostr("Error: ", lines, " reading ", iobjstr));
return;
elseif (typeof(lines) == STR)
this:insert_line(this:loaded(player), lines, 0);
return;
elseif (typeof(lines) == LIST)
for x in (lines)
if (typeof(x) != STR)
player:notify(tostr("Error: ", iobjstr, " does not contain a ", verb, "-able value."));
return;
endif
endfor
this:insert_line(this:loaded(player), lines, 0);
return;
else
player:notify(tostr("Error: ", iobjstr, " does not contain a ", verb, "-able value."));
return;
endif
elseif (pr = $code_utils:parse_verbref(iobjstr))
o = player:my_match_object(pr[1]);
if ($command_utils:object_match_failed(o, pr[1]))
return;
elseif (lines = `verb_code(o, pr[2], !player:edit_option("no_parens")) ! ANY')
this:insert_line(this:loaded(player), lines, 0);
return;
elseif (lines == E_PERM)
player:notify(tostr("Error: Permission denied reading ", iobjstr));
return;
elseif (lines == E_VERBNF)
player:notify(tostr("There is no `", pr[2], "' verb on ", $string_utils:nn(o), "."));
else
player:notify(tostr("Error: ", lines, " reading ", iobjstr));
return;
endif
elseif ($command_utils:object_match_failed(iobj = player:my_match_object(iobjstr), iobjstr))
return;
elseif ((lines = `iobj:text() ! ANY') == E_PERM)
player:notify(tostr("Error: Permission denied reading ", iobjstr));
return;
elseif (lines == E_VERBNF)
player:notify($string_utils:nn(iobj), " doesn't seem to be a note.");
elseif (typeof(lines) == ERR)
player:notify(tostr("Error: ", lines, " reading ", iobjstr));
return;
else
this:insert_line(this:loaded(player), lines, 0);
endif
.
#50:67
"Flushes editor sessions older than args[1].  If args[2] is true, prints status as it runs.  If args[2] is false, runs silently.";
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
else
{t, noisy} = args;
for i in [-length(this.active)..-1]
if (this.times[-i] < t)
if (noisy)
player:tell($string_utils:nn(this.active[-i]), ctime(this.times[-i]));
endif
this:kill_session(-i);
endif
endfor
endif
.
#51:0
":match(string, object-list)";
"Return object in 'object-list' aliased to 'string'.";
"Matches on a wide variety of syntax, including:";
" \"5th axe\" -- The fifth object matching \"axe\" in the object list.";
" \"where's sai\" -- The only object contained in 'where' matching \"sai\" (possible $ambiguous_match).";
" \"where's second staff\" -- The second object contained in 'where' matching \"staff\".";
" \"my third dagger\" -- The third object in your inventory matching \"dagger\".";
"Ordinal matches are determined according to the match's position in 'object-list' or, if a possessive (such as \"where\" above) is given, then the ordinal is the nth match in that object's inventory.";
"In the matching room (#3879@LambdaMOO), the 'object-list' consists of first the player's contents, then the room's, and finally all exits leading from the room.";
{string, olist} = args;
if (!string)
return $nothing;
elseif (string == "me")
return player;
elseif (string == "here")
return player.location;
elseif (valid(object = $string_utils:literal_object(string)))
return object;
elseif (valid(object = $string_utils:match(string, olist, "aliases")))
return object;
elseif (parsed = this:parse_ordinal_reference(string))
return this:match_nth(parsed[2], olist, parsed[1]);
elseif (parsed = this:parse_possessive_reference(string))
{whostr, objstr} = parsed;
if (valid(whose = this:match(whostr, olist)))
return this:match(objstr, whose.contents);
else
return whose;
endif
else
return object;
endif
.
#51:1
":match_nth(string, objlist, n)";
"Find the nth object in 'objlist' that matches 'string'.";
{what, where, n} = args;
for v in (where)
z = 0;
for q in (v.aliases)
z = z || (index(q, what) == 1);
endfor
if (z && (!(n = n - 1)))
return v;
endif
endfor
return $failed_match;
.
#51:2
"$match_utils:match_verb(verbname, object) => Looks for a command-line style verb named <verbname> on <object> with current values of prepstr, dobjstr, dobj, iobjstr, and iobj.  If a match is made, the verb is called with @args[3] as arguments and 1 is returned.  Otherwise, 0 is returned.";
{vrb, what, rest} = args;
if (where = $object_utils:has_verb(what, vrb))
if ((vargs = verb_args(where[1], vrb)) != {"this", "none", "this"})
if (((((((vargs[2] == "any") || ((!prepstr) && (vargs[2] == "none"))) || index(("/" + vargs[2]) + "/", ("/" + prepstr) + "/")) && (((vargs[1] == "any") || ((!dobjstr) && (vargs[1] == "none"))) || ((dobj == what) && (vargs[1] == "this")))) && (((vargs[3] == "any") || ((!iobjstr) && (vargs[3] == "none"))) || ((iobj == what) && (vargs[3] == "this")))) && index(verb_info(where[1], vrb)[2], "x")) && verb_code(where[1], vrb))
set_task_perms(caller_perms());
what:(vrb)(@rest);
return 1;
endif
endif
endif
.
#51:3
":match_list(string, object_list) -> List of all matches.";
{what, where} = args;
if (!what)
return {};
endif
r = {};
for v in (where)
if (!(v in r))
z = 0;
for q in (v.aliases)
z = z || (q && (index(q, what) == 1));
endfor
if (z)
r = listappend(r, v);
endif
endif
endfor
return r;
.
#51:4
":parse_ordref(string)";
"Parses strings referring to an 'nth' object.";
"=> {INT n, STR object} Where 'n' is the number the ordinal represents, and 'object' is the rest of the string.";
"=> 0 If the given string is not an ordinal reference.";
"  Example:";
":parse_ordref(\"second broadsword\") => {2, \"broadsword\"}";
":parse_ordref(\"second\") => 0";
"  Note that there must be more to the string than the ordinal alone.";
if (m = match(args[1], ("^" + this.ordinal_regexp) + " +%([^ ].+%)$"))
o = substitute("%1", m);
n = (o in this.ordn) || (o in this.ordw);
return n && {n, substitute("%2", m)};
else
return 0;
endif
.
#51:5
":parse_possessive_reference(string)";
"Parses strings in a possessive format.";
"=> {STR whose, STR object}  Where 'whose' is the possessor of 'object'.";
"If the string consists only of a possessive string (ie: \"my\", or \"yduJ's\"), then 'object' will be an empty string.";
"=> 0 If the given string is not a possessive reference.";
"  Example:";
":parse_possessive_reference(\"joe's cat\") => {\"joe\", \"cat\"}";
":parse_possessive_reference(\"sis' fish\") => {\"sis\", \"fish\"}";
"  Strings are returned as a value suitable for a :match routine, thus 'my' becoming 'me'.";
":parse_possessive_reference(\"my dog\") => {\"me\", \"dog\"}";
string = args[1];
if (m = match(string, "^my$%|^my +%(.+%)?"))
return {"me", substitute("%1", m)};
elseif (m = match(string, "^%([^ ]+s?%)'s? *%(.+%)?"))
return {substitute("%1", m), substitute("%2", m)};
else
return 0;
endif
.
#51:6
"Usage: object_match_failed(object, string[, ambigs])";
"Prints a message if string does not match object.  Generally used after object is derived from a :match_object(string).";
"ambigs is an optional list of the objects that were matched upon.  If given, the message printed will list the ambiguous among them as choices.";
{match_result, string, ?ambigs = 0} = args;
tell = (0 && $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("There is no \"", string, "\" that you can see."));
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("There is no \"", string, "\" that you can see."));
elseif (match_result == $ambiguous_match)
if (typeof(ambigs) != LIST)
player:(tell)(tostr("I don't know which \"", string, "\" you mean."));
return 1;
endif
ambigs = $match_utils:match_list(string, ambigs);
ambigs = $list_utils:map_property(ambigs, "name");
if ((length($list_utils:remove_duplicates(ambigs)) == 1) && $object_utils:isa(player.location, this.matching_room))
player:(tell)(tostr("I don't know which \"", string, "\" you mean.  Try using \"first ", string, "\", \"second ", string, "\", etc."));
else
player:(tell)(tostr("I don't know which \"", string, "\" you mean: ", $string_utils:english_list(ambigs, "nothing", " or "), "."));
endif
return 1;
elseif (!valid(match_result))
player:(tell)(tostr("The object you specified does not exist.  Seeing ghosts?"));
else
return 0;
endif
return 1;
.
#51:7
if (caller_perms().wizard)
pass();
this.matching_room = $nothing;
endif
.
#52:0
"Syntax:  has_property(OBJ, STR) => INT 0|1";
"";
"Does object have the specified property? Returns true if it is defined on the object or a parent.";
{object, prop} = args;
try
object.(prop);
return 1;
except (E_PROPNF, E_INVIND)
return 0;
endtry
"Old code...Ho_Yan 10/22/96";
if (prop in $code_utils.builtin_props)
return valid(object);
else
return !(!property_info(object, prop));
endif
.
#52:1
"Syntax:  all_properties (OBJ what)";
"         all_verbs      (OBJ what)";
"";
"Returns all properties or verbs defined on `what' and all of its ancestors. Uses wizperms to get properties or verbs if the caller of this verb owns what, otherwise, uses caller's perms.";
what = args[1];
if (what.owner != caller_perms())
set_task_perms(caller_perms());
endif
bif = (verb == "all_verbs") ? "verbs" | "properties";
res = `call_function(bif, what) ! E_PERM => {}';
while (valid(what = parent(what)))
res = {@`call_function(bif, what) ! E_PERM => {}', @res};
endwhile
return res;
.
#52: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) ! E_VERBNF, E_INVARG'))
object = parent(object);
endwhile
return vi ? {object} | 0;
.
#52: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))
"Don't need to catch E_VERBNF in verb_code(), since it will never get there with the 0 &&";
return {object};
endif
object = parent(object);
endwhile
return 0;
.
#52:4
":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).";
verbname = strsub(args[2], "*", "");
object = args[1];
while (E_VERBNF == (info = `verb_info(object, verbname) ! E_VERBNF, E_INVARG'))
object = parent(object);
endwhile
return info ? {object, verbname} | 0;
.
#52:5
":isa(x,y) == valid(x) && (y==x || y in :ancestors(x))";
what = args[1];
targ = args[2];
while (valid(what))
$command_utils:suspend_if_needed(1);
if (what == targ)
return 1;
endif
what = parent(what);
endwhile
return 0;
"Last modified Thu Apr  8 12:31:53 1999 CDT by Wizard (#2).";
.
#52:6
"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;
.
#52:7
r = {what = args[1]};
for k in (children(what))
r = {@r, @this:(verb)(k)};
endfor
return r;
.
#52:8
"$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, what} = args;
while (valid(what))
what = what.location;
if (what == loc)
return valid(loc);
endif
endwhile
return 0;
.
#52:9
"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;
.
#52:10
"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;
.
#52:11
"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;
.
#52:12
":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) ! E_PROPNF => 0')
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;
.
#52:13
":descendants_with_property_suspended(object,property)";
" => list of descendants of object on which property is defined.";
"calls suspend(0) as needed";
{object, prop} = args;
if ((caller == this) || (object.w || $perm_utils:controls(caller_perms(), object)))
$command_utils:suspend_if_needed(0);
if (`property_info(object, prop) ! E_PROPNF => 0')
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
.
#52:14
"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;
.
#52:15
"Syntax:  all_properties_suspended (OBJ what)";
"         all_verbs_suspended      (OBJ what)";
"";
"Returns all properties or verbs defined on `what' and all of its ancestors. Uses wizperms to get properties or verbs if the caller of this verb owns what, otherwise, uses caller's perms. Suspends as necessary";
what = args[1];
if (what.owner != caller_perms())
set_task_perms(caller_perms());
endif
bif = (verb == "all_verbs") ? "verbs" | "properties";
res = `call_function(bif, what) ! E_PERM => {}';
while (valid(what = parent(what)))
res = {@`call_function(bif, what) ! E_PERM => {}', @res};
$command_utils:suspend_if_needed(0);
endwhile
return res;
.
#52:16
":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 typeof(`connected_seconds(@args) ! E_INVARG') == INT;
.
#52:17
":isoneof(x,y) = x isa z, for some z in list y";
{what, targ} = args;
while (valid(what))
if (what in targ)
return 1;
endif
what = parent(what);
endwhile
return 0;
.
#52:18
"Returns 1 if the verb is actually *defined* on this object, 0 else.";
"Use this instead of :has_verb if your aim is to manipulate that verb code or whatever.";
return `verb_info(@args) ! ANY => 0' && 1;
"Old code below...Ho_Yan 10/22/96";
info = verb_info(@args);
return typeof(info) != ERR;
.
#52:19
":defines_property(OBJ object, STR property name) => Returns 1 if the property is actually *defined* on the object given";
if (!valid(o = args[1]))
return 0;
elseif (!valid(p = parent(o)))
return this:has_property(o, args[2]);
else
return (!this:has_property(p, args[2])) && this:has_property(o, args[2]);
endif
.
#52:20
":has_any_verb(object) / :has_any_property(object)";
" -- does `object' have any verbs/properties?";
return !(!`(verb == "has_any_verb") ? verbs(args[1]) | properties(args[1]) ! E_INVARG => 0');
.
#52:21
":has_readable_property(OBJ object, STR property name) => 1 if property exists and is publically readable (has the r flag set true).";
{object, prop} = args;
try
pinfo = property_info(object, prop);
return index(pinfo[2], "r") != 0;
except (E_PROPNF)
return (prop in $code_utils.builtin_props) > 0;
endtry
.
#52:22
what = args[1];
kids = children(what);
result = {};
for x in (kids)
$command_utils:suspend_if_needed(0);
result = {@result, @this:descendants(x)};
endfor
return {@kids, @result};
"Last modified Fri Feb  6 08:03:07 1998 CST by Wizard (#2).";
.
#52:23
":leaves (OBJ object) => {OBJs} descendants of <object> that have no children";
r = {args[1]};
i = 1;
while (i <= length(r))
if (kids = children(r[i]))
r[i..i] = kids;
else
i = i + 1;
endif
endwhile
return r;
.
#52:24
":branches (OBJ object) => {OBJs} descendants of <object> that have children";
r = args[1..1];
i = 1;
while (i <= length(r))
if (kids = children(r[i]))
r[i + 1..i] = kids;
i = i + 1;
else
r[i..i] = {};
endif
endwhile
return r;
.
#52:25
":descendants_suspended (OBJ object) => {OBJs} all nested children of <object>";
set_task_perms(caller_perms());
r = children(args[1]);
i = 1;
while (i <= length(r))
if (kids = children(r[i]))
r = {@r, @kids};
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endwhile
return r;
.
#52:26
":leaves_suspended (OBJ object) => {OBJs} descendants of <object> that have";
"                                         no children";
set_task_perms(caller_perms());
r = {args[1]};
i = 1;
while (i <= length(r))
if (kids = children(r[i]))
r[i..i] = kids;
else
i = i + 1;
endif
$command_utils:suspend_if_needed(0);
endwhile
return r;
.
#52:27
":branches_suspended (OBJ object) => {OBJs} all descendants of <object> that";
"                                           have children.";
set_task_perms(caller_perms());
r = args[1..1];
i = 1;
while (i <= length(r))
if (kids = children(r[i]))
r[i + 1..i] = kids;
i = i + 1;
else
r[i..i] = {};
endif
$command_utils:suspend_if_needed(0);
endwhile
return r;
.
#52:28
"Usage:  :fertile_objects() or :fertile_objects_suspended()";
"This verb compiles a list of all the fertile objects in the database, suspending if called as fertile_objects_suspended.";
suspendok = verb[$ - 9..$] == "_suspended";
result = {};
for o in [#0..max_object()]
if (valid(o) && o.f)
result = {@result, o};
endif
suspendok && $command_utils:suspend_if_needed(0);
endfor
return result;
"Last modified Sun Aug 17 11:10:54 1997 CDT by Wizard (#2).";
.
#53:0
this.input_string = args[1];
this.input_length = length(args[1]);
this.input_index = 1;
this.index_incremented = 0;
.
#53: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)
this.index_incremented = 0;
return "";
elseif ((ch = string[i]) in {"(", ")", "!", "?"})
this.input_index = i + 1;
this.index_incremented = 1;
return ch;
elseif (ch in {"&", "|"})
this.input_index = i = i + 1;
this.index_incremented = 1;
if ((i <= len) && (string[i] == ch))
this.input_index = i + 1;
this.index_incremented = 2;
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
this.index_incremented = (i - start) + 1;
return this:canonicalize_spaces(string[start..i]);
endif
.
#53:2
name = args[1];
while (index(name, "  "))
name = strsub(name, "  ", " ");
endwhile
return name;
.
#53:3
"parse_keyexp(STRING keyexpression, OBJ player) => returns a list containing the coded key, or a string containing an error message if the attempt failed.";
"";
"Grammar for key expressions:";
"";
"    E ::= A       ";
"       |  E || A  ";
"       |  E && A  ";
"    A ::= ( E )   ";
"       |  ! A     ";
"       |  object  ";
"       |  ? object  ";
this:init_scanner(args[1]);
this.player = args[2];
return this:parse_E();
.
#53:4
exp = this:parse_A();
if (typeof(exp) != STR)
while ((token = this:scan_token()) in {"&&", "||"})
rhs = this:parse_A();
if (typeof(rhs) == STR)
return rhs;
endif
exp = {token, exp, rhs};
endwhile
"The while loop above always eats a token. Reset it back so the iteration can find it again. Always losing `)'. Ho_Yan 3/9/95";
this.input_index = this.input_index - this.index_incremented;
endif
return exp;
.
#53: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
.
#53:6
"eval_key(LIST|OBJ coded key, OBJ testobject) => returns true if testobject will solve the provided key.";
{key, who} = args;
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
raise(E_DIV);
endif
.
#53:7
"used by $lock_utils to unparse a key expression so one can use `here' and `me' as well as doing the regular object matching.";
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
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
.
#53:8
":unparse_key(LIST|OBJ coded key) => returns a string describing the key in english/moo-code terms.";
"Example:";
"$lock_utils:unparse_key({\"||\", $hacker, $housekeeper}) => \"#18105[Hacker] || #36830[housekeeper]\"";
key = args[1];
type = typeof(key);
if (!(type in {LIST, OBJ}))
return "(None.)";
elseif (type == OBJ)
if (valid(key))
return tostr(key, "[", key.name, "]");
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
raise(E_DIV);
endif
endif
.
#53:9
set_task_perms($no_one);
{key, who} = args;
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
raise(E_DIV);
endif
.
#53: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
.
#54:0
who = valid(caller_perms()) ? caller_perms() | player;
if ($perm_utils:controls(who, this) || this:is_readable_by(who))
result = this:do_burn();
else
result = 0;
endif
player:tell(result ? this:burn_succeeded_msg() | this:burn_failed_msg());
if (msg = result ? this:oburn_succeeded_msg() | this:oburn_failed_msg())
player.location:announce(player.name, " ", msg);
endif
.
#54:1
return (msg = this.(verb)) ? $string_utils:pronoun_sub(msg) | "";
.
#54:2
if ((this != $letter) && ((caller == this) || $perm_utils:controls(caller_perms(), this)))
fork (0)
$recycler:_recycle(this);
endfork
return 1;
else
return E_PERM;
endif
.
#55:0
":make(n[,elt]) => a list of n elements, each of which == elt. elt defaults to 0.";
{n, ?elt = 0} = args;
if (n < 0)
return E_INVARG;
endif
ret = {};
build = {elt};
while (1)
if (n % 2)
ret = {@ret, @build};
endif
if (n = n / 2)
build = {@build, @build};
else
return ret;
endif
endwhile
.
#55:1
":range([m,]n) => {m,m+1,...,n}";
{?m = 1, n} = args;
ret = {};
for k in [m..n]
ret = {@ret, k};
endfor
return ret;
.
#55:2
set_task_perms(caller_perms());
{objs, prop} = args;
if (length(objs) > 50)
return {@this:map_prop(objs[1..$ / 2], prop), @this:map_prop(objs[($ / 2) + 1..$], prop)};
endif
strs = {};
for foo in (objs)
strs = {@strs, foo.(prop)};
endfor
return strs;
.
#55:3
set_task_perms(caller_perms());
{objs, vrb, @rest} = args;
if (length(objs) > 50)
return {@this:map_verb(@listset(args, objs[1..$ / 2], 1)), @this:map_verb(@listset(args, objs[($ / 2) + 1..$], 1))};
endif
strs = {};
for o in (objs)
strs = {@strs, o:(vrb)(@rest)};
endfor
return strs;
.
#55: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_verb_arg(o,v,{a...},a2,a3,a4,a5)={o:v(a,a2,a3,a4,a5),...}";
"map_verb_arg(4,o,v,a1,a2,a3,{a...},a5)={o:v(a1,a2,a3,a,a5),...}";
set_task_perms(caller_perms());
if (n = args[1])
{object, verb, @rest} = args[2..$];
else
object = n;
n = 1;
{verb, @rest} = args[2..$];
endif
results = {};
for a in (rest[n])
results = listappend(results, object:(verb)(@listset(rest, a, n)));
endfor
return results;
.
#55: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());
{objs, builtin} = args;
if (!`function_info(builtin) ! E_INVARG => 0')
return E_INVARG;
endif
if (length(objs) > 100)
return {@this:map_builtin(objs[1..$ / 2], builtin), @this:map_builtin(objs[($ / 2) + 1..$], builtin)};
endif
strs = {};
for foo in (objs)
strs = {@strs, call_function(builtin, foo)};
endfor
return strs;
.
#55:6
"find_insert(sortedlist,key) => 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.";
{lst, key} = args;
if ((r = length(lst)) < 25)
for l in [1..r]
if (lst[l] > key)
return l;
endif
$command_utils:suspend_if_needed(0);
endfor
return r + 1;
else
l = 1;
while (r >= l)
if (key < lst[i = (r + l) / 2])
r = i - 1;
else
l = i + 1;
endif
$command_utils:suspend_if_needed(0);
endwhile
return l;
endif
"Last modified Fri Oct  1 06:30:42 1999 CDT by Wizard (#2).";
.
#55: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;
.
#55: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
.
#55:9
":setremove_all(set,elt) => set with *all* occurences of elt removed";
{set, what} = args;
while (w = what in set)
set[w..w] = {};
endwhile
return set;
.
#55:10
"append({a,b,c},{d,e},{},{f,g,h},...) =>  {a,b,c,d,e,f,g,h}";
if (length(args) > 50)
return {@this:append(@args[1..$ / 2]), @this:append(@args[($ / 2) + 1..$])};
endif
l = {};
for a in (args)
l = {@l, @a};
endfor
return l;
.
#55:11
"reverse(list) => reversed list";
return this:_reverse(@args[1]);
.
#55:12
":_reverse(@list) => reversed list";
if (length(args) > 50)
return {@this:_reverse(@args[($ / 2) + 1..$]), @this:_reverse(@args[1..$ / 2])};
endif
l = {};
for a in (args)
l = listinsert(l, a);
endfor
return l;
.
#55: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
.
#55: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 = args[1];
unsorted_keys = (use_sorted_lst = length(args) >= 2) ? args[2] | 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;
.
#55:15
":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) != INT)
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;
.
#55:16
"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\"}}";
{thelist, ?ind = 1} = args;
slice = {};
if (typeof(ind) == LIST)
for elt in (thelist)
s = {elt[ind[1]]};
for i in (listdelete(ind, 1))
s = {@s, elt[i]};
endfor
slice = {@slice, s};
endfor
else
for elt in (thelist)
slice = {@slice, elt[ind]};
endfor
endif
return slice;
.
#55:17
"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, thelist, ?indx = 1} = args;
for t in (thelist)
if (`t[indx] == target ! E_TYPE => 0')
if ((typeof(t) == LIST) && (length(t) >= indx))
return t;
endif
endif
endfor
return {};
.
#55: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.";
{target, thelist, ?indx = 1} = args;
i = 1;
for lsti in (thelist)
if (`lsti[indx] == target ! E_TYPE => 0')
if ((typeof(lsti) == LIST) && (length(lsti) >= indx))
return i;
endif
endif
i = i + 1;
endfor
return 0;
.
#55:19
"iassoc_suspended(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, thelist, ?indx = 1} = args;
i = 1;
for lsti in (thelist)
if (`lsti[indx] == target ! E_TYPE => 0')
if ((typeof(lsti) == LIST) && (length(lsti) >= indx))
return i;
endif
endif
i = i + 1;
$command_utils:suspend_if_needed(1);
endfor
return 0;
.
#55:20
"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, thelist, ?indx = 1} = args;
for t in (thelist)
if ((typeof(t) == LIST) && ((length(t) >= indx) && (index(t[indx], target) == 1)))
return t;
endif
endfor
return {};
.
#55:21
"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;
.
#55:22
"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
.
#55:23
":sort_alist(alist[,n]) sorts a list of tuples by n-th (1st) element.";
{alist, ?sort_on = 1} = args;
if ((alist_length = length(alist)) < 25)
"use insertion sort on short lists";
return this:sort(alist, this:slice(@args));
endif
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};
.
#55:24
"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, alist, ?sort_on = 1} = args;
if ((alist_length = length(alist)) < 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";
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
.
#55:25
":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;
.
#55:26
"$list_utils:count(item, list)";
"Returns the number of occurrences of item in list.";
{x, xlist} = args;
if (typeof(xlist) != LIST)
return E_INVARG;
endif
counter = 0;
while (loc = x in xlist)
counter = counter + 1;
xlist = xlist[loc + 1..$];
endwhile
return counter;
.
#55:27
"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;
.
#55:28
"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;
.
#55:29
"check_nonstring_tell_lines(lines)";
if (caller_perms().wizard)
"don't let a nonwizard mess up our stats";
for line in (args[1])
if (typeof(line) != STR)
this.nonstring_tell_lines = listappend(this.nonstring_tell_lines, callers());
return;
endif
endfor
endif
.
#55:30
"reverse(list) => reversed list.  Does suspend(0) as necessary.";
set_task_perms(caller_perms());
"^^^For suspend task.";
return this:_reverse_suspended(@args[1]);
.
#55:31
":_reverse(@list) => reversed list";
set_task_perms(caller_perms());
$command_utils:suspend_if_needed(0);
if (length(args) > 50)
return {@this:_reverse_suspended(@args[($ / 2) + 1..$]), @this:_reverse_suspended(@args[1..$ / 2])};
endif
l = {};
for a in (args)
l = listinsert(l, a);
endfor
return l;
.
#55:32
":randomly_permute_suspended(list) => list with its elements randomly permuted";
"  each of the length(list)! possible permutations is equally likely";
set_task_perms(caller_perms());
plist = {};
for i in [1..length(ulist = args[1])]
plist = listinsert(plist, ulist[i], random(i));
$command_utils:suspend_if_needed(0);
endfor
return plist;
.
#55:33
"swap_elements -- exchange two elements in a list";
"Usage:  $list_utils:swap_elements(<list/LIST>,<index/INT>,<index/INT>)";
"        $list_utils:swap_elements({\"a\",\"b\"},1,2);";
{l, i, j} = args;
if (((typeof(l) == LIST) && (typeof(i) == INT)) && (typeof(j) == INT))
ll = length(l);
if (((i > 0) && (i <= ll)) && ((j > 0) && (j <= ll)))
t = l[i];
l[i] = l[j];
l[j] = t;
return l;
else
return E_RANGE;
endif
else
return E_TYPE;
endif
.
#55:34
"random_item -- returns a random element of the input list.";
if (length(args) == 1)
if (typeof(l = args[1]) == LIST)
if (length(l) > 0)
return l[random($)];
else
return E_RANGE;
endif
else
return E_TYPE;
endif
else
return E_ARGS;
endif
.
#55:35
"assoc_suspended(target,list[,index]) returns the first element of `list' whose own index-th element is target.  Index defaults to 1. Suspends as necessary.";
"returns {} if no such element is found";
set_task_perms(caller_perms());
{target, thelist, ?indx = 1} = args;
for t in (thelist)
if (`t[indx] == target ! E_TYPE => 0')
if ((typeof(t) == LIST) && (length(t) >= indx))
return t;
endif
endif
$command_utils:suspend_if_needed(0);
endfor
return {};
.
#55:36
"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
"Last modified Sun Oct  4 19:52:34 1998 CDT by Wizard (#2).";
.
#55:37
":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;
"Last modified Sun Oct  4 19:52:34 1998 CDT by Wizard (#2).";
.
#55:38
":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;
"Last modified Sun Oct  4 19:52:34 1998 CDT by Wizard (#2).";
.
#55:39
"Copyright (C) 1999, sindre.sorensen@uib.no";
mylist = args[1];
if (LIST != typeof(mylist))
return 0;
endif
for elem in (mylist)
if (STR != typeof(elem))
return 0;
endif
suspend(0);
endfor
return 1;
"Last modified Fri Oct  1 06:33:16 1999 CDT by Wizard (#2).";
.
#56: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, string} = args;
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("I see no \"", string, "\" here."));
elseif (match_result == $ambiguous_match)
player:(tell)(tostr("I don't know which \"", string, "\" you mean."));
elseif (!valid(match_result))
player:(tell)(tostr(match_result, " does not exist."));
else
return 0;
endif
return 1;
.
#56: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, strings, ?cmdid = ""} = args;
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};
.
#56:2
"$command_utils:read() -- read a line of input from the player and return it";
"Optional argument is a prompt portion to replace `a line of input' in the prompt.";
"";
"Returns E_PERM if the current task is not a command task that has never called suspend().";
{?prompt = "a line of input"} = args;
c = callers();
p = c[$][5];
p:notify(tostr("[Type ", prompt, " or `@abort' to abort the command.]"));
try
ans = read();
if ($string_utils:trim(ans) == "@abort")
p:notify(">> Command Aborted <<");
kill_task(task_id());
endif
return ans;
except error (ANY)
return error[1];
endtry
.
#56:3
"$command_utils:read_lines([max]) -- read zero or more lines of input";
"";
"Returns a list of strings, the (up to MAX, if given) 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 \".\".";
{?max = 0} = args;
c = callers();
p = c[$][5];
p:notify(tostr("[Type", max ? tostr(" up to ", max) | "", " lines of input; use `.' to end or `@abort' to abort the command.]"));
ans = {};
while (1)
try
line = read();
if ((line[1..min(6, $)] == "@abort") && ((tail = line[7..$]) == $string_utils:space(tail)))
p:notify(">> Command Aborted <<");
kill_task(task_id());
elseif ((!line) || (line[1] != "."))
ans = {@ans, line};
elseif ((tail = line[2..$]) == $string_utils:space(tail))
return ans;
else
ans = {@ans, tail};
endif
if (max && (length(ans) >= max))
return ans;
endif
except error (ANY)
return error[1];
endtry
endwhile
.
#56: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[$][5];
p:notify(tostr(args ? args[1] + " " | "", "[Enter `yes' or `no']"));
try
ans = read(@((caller == p) || $perm_utils:controls(caller_perms(), p)) ? {p} | {});
if (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
except error (ANY)
return error[1];
endtry
.
#56: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()";
{escapes, ?help = "You are currently in a read loop."} = args;
c = callers();
p = c[$][5];
escapes = {".", "@abort", @(typeof(escapes) == LIST) ? escapes | {escapes}};
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...";
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 (1)
try
line = read();
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
except error (ANY)
return error[1];
endtry
endwhile
.
#56: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.";
{time, ?output = 0} = args;
set_task_perms(caller_perms());
value = 0;
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
value = output;
endif
endif
return output;
.
#56:7
"Return true if we're running out of ticks or seconds.";
return (ticks_left() < 4000) || (seconds_left() < 2);
"If this verb is changed make sure to change :suspend_if_needed as well.";
.
#56: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 ((ticks_left() < 4000) || (seconds_left() < 2))
"Note: above computation should be the same as :running_out_of_time.";
if ((ann = listdelete(args, 1)) && valid(player))
player:tell(tostr(@ann));
endif
amount = max(args[1], min($login:current_lag(), 10));
set_task_perms(caller_perms());
"this is trying to back off according to lag...";
suspend(amount);
return 1;
endif
.
#56: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] | {}, "."};
.
#56:10
":explain_syntax(here,verb,args)";
verb = args[2];
for x in ({player, args[1], @valid(dobj) ? {dobj} | {}, @valid(iobj) ? {iobj} | {}})
what = x;
while (hv = $object_utils:has_verb(what, verb))
what = hv[1];
i = 1;
while (i = $code_utils:find_verb_named(what, verb, i))
if (evs = $code_utils:explain_verb_syntax(x, verb, @verb_args(what, i)))
player:tell("Try this instead:  ", evs);
return 1;
endif
i = i + 1;
endwhile
what = parent(what);
endwhile
endfor
return 0;
.
#56:11
":do_huh(verb,args)  what :huh should do by default.";
{verb, args} = args;
this.feature_task = {task_id(), verb, args, argstr, dobj, dobjstr, prepstr, iobj, iobjstr};
set_task_perms(cp = caller_perms());
notify = $perm_utils:controls(cp, player) ? "notify" | "tell";
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 (player:last_huh(verb, args))
"... player's second round found something to do ...";
elseif (dobj == $ambiguous_match)
if (iobj == $ambiguous_match)
player:(notify)(tostr("I don't understand that (\"", dobjstr, "\" and \"", iobjstr, "\" are both ambiguous names)."));
else
player:(notify)(tostr("I don't understand that (\"", dobjstr, "\" is an ambiguous name)."));
endif
elseif (iobj == $ambiguous_match)
player:(notify)(tostr("I don't understand that (\"", iobjstr, "\" is an ambiguous name)."));
else
player:(notify)("I don't understand that.");
player:my_explain_syntax(caller, verb, args) || (caller:here_explain_syntax(caller, verb, args) || this:explain_syntax(caller, verb, args));
endif
.
#56: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;
.
#56:13
if (caller_perms().wizard)
this.lag_samples = {};
this.feature_task = "hey, neat, no feature verbs have been run yet!";
endif
.
#56:14
"Kills this task if the current lag is greater than args[1].  Args[2..n] will be passed to player:tell.";
cutoff = args[1];
if ($login:current_lag() > cutoff)
player:tell(@listdelete(args, 1));
kill_task(task_id());
endif
.
#56:15
":validate_feature(verb, args)";
"  (where `verb' and `args' are the arguments passed to :my_huh)";
"  returns true or false based on whether this is the same command typed by the user (comparing it against $command_utils.feature_task, set by $command_utils:do_huh).";
"  assumes that the :my_huh parsing has not suspended";
return {task_id(), @args, argstr, dobj, dobjstr, prepstr, iobj, iobjstr} == this.feature_task;
.
#57: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 = $string_utils:match_player(args[2]);
bynumber = verb == "@chown#";
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)
vname = $code_utils:toint(vname);
if (vname == E_TYPE)
return player:notify("Verb number expected.");
elseif ((vname < 1) || (vname > length(verbs(object))))
return player:notify("Verb number out of range.");
endif
endif
info = `verb_info(object, vname) ! ANY';
if (info == E_VERBNF)
player:notify("That object does not define that verb.");
elseif (typeof(info) == ERR)
player:notify(tostr(info));
else
try
result = set_verb_info(object, vname, listset(info, owner, 1));
player:notify("Verb owner set.");
except e (ANY)
player:notify(e[2]);
endtry
endif
endif
elseif (bynumber)
player:notify("@chown# can only be used with 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
.
#57:1
if (caller != this)
raise(E_PERM);
endif
set_task_perms(player);
if ((length(args) == 1) && (argstr[1] == "\""))
argstr = args[1];
endif
shout = $gender_utils:get_conj("shouts", player);
for person in (connected_players())
if (person != player)
person:notify(tostr(player.name, " ", shout, ", \"", argstr, "\""));
endif
endfor
player:notify(tostr("You shout, \"", argstr, "\""));
.
#57:2
"@grant <object> to <player>";
"@grants <object> to <player>   --- same as @grant but may suspend.";
"@transfer <expression> to <player> -- like 'grant', but evalutes a possible list of objects to transfer, and modifies quota.";
"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))
return player:notify(tostr("Usage:  ", verb, " <object> to <player>"));
endif
if ($command_utils:player_match_failed(newowner = $string_utils:match_player(iobjstr), iobjstr))
"...newowner is bogus...";
return;
endif
if (verb == "@transfer")
objlist = player:eval_cmd_string(dobjstr, 0);
if (!objlist[1])
player:notify(tostr("Had trouble reading `", dobjstr, "': "));
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 `", dobjstr, "' is not an object or list:  ", toliteral(objlist[2])));
return;
else
objlist = objlist[2];
endif
elseif ($command_utils:object_match_failed(object = this:my_match_object(dobjstr), dobjstr))
"...object is bogus...";
return;
else
objlist = {object};
endif
"Used to check for quota of newowner, but doesn't anymore, cuz the quota check doesn't work";
suspendok = verb != "@grant";
player:tell("Transferring ", toliteral(objlist), " to ", $string_utils:nn(newowner));
for object in (objlist)
$command_utils:suspend_if_needed(0);
same = object.owner == newowner;
for vnum in [1..length(verbs(object))]
info = verb_info(object, vnum);
if (!((info[1] != object.owner) && (valid(info[1]) && is_player(info[1]))))
same = same && (info[1] == newowner);
set_verb_info(object, vnum, 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)
player:notify(tostr(newowner.name, " already owns everything ", newowner.ps, " is entitled to on ", object.name, "."));
else
player:notify(tostr("Ownership changed on ", $string_utils:nn(object), ", verb, properties and descendants' properties."));
endif
endfor
.
#57:3
set_task_perms(player);
dobj = $string_utils:match_player(dobjstr);
if (dobj == $nothing)
player:notify(tostr("Usage:  ", verb, " <playername>"));
elseif ($command_utils:player_match_result(dobj, dobjstr)[1])
elseif ($wiz_utils:check_prog_restricted(dobj))
return player:notify(tostr("Sorry,", dobj.name, " is not allowed to be a programmer."));
elseif ((dobj.description == $player.description) && (!$command_utils:yes_or_no($string_utils:pronoun_sub("@Programmer %d despite %[dpp] lack of description?"))))
player:notify(tostr("Okay, leaving ", dobj.name, " !programmer."));
return;
elseif (result = $wiz_utils:set_programmer(dobj))
player:notify(tostr(dobj.name, " (", dobj, ") is now a programmer.  ", dobj.ppc, " quota is currently ", $quota_utils:get_quota(dobj), "."));
player:notify(tostr(dobj.name, " and the other wizards 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.name, " (", dobj, ") is already a programmer..."));
else
player:notify(tostr(result));
endif
.
#57:4
if (!player.wizard)
player:notify("Nice try, but permission denied.");
return;
elseif (args == {})
player:notify(tostr("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, " ", toint(o = $quota_utils:bi_create(#1)), "' now."));
recycle(o);
return;
elseif (toobj(toint(args[1])) != max_object())
player:notify(tostr("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:notify("Sorry, but you must own this verb in order to use it.");
return;
endif
"----------------------------------------";
player:notify("Blowing away $local...");
$local = #-1;
"----------------------------------------";
player:notify("Identifying objects to be saved...");
saved = {#0, player};
saved_props = {};
for p in (properties(#0))
v = #0.(p);
if ((typeof(v) == OBJ) && valid(v))
saved = setadd(saved, v);
saved_props = {@saved_props, p};
endif
endfor
for o in (saved)
"Also save non-$ objects that are ancestors of $ objects";
"but leave out non-$ player classes and room classes";
"Come to think of it, what's the point of this? Can't we just give the generic gendered object a $-name? --Nosredna";
if ((!$object_utils:isa(o, $player)) && (!$object_utils:isa(o, $room)))
p = parent(o);
while (valid(p))
saved = setadd(saved, p);
p = parent(p);
endwhile
endif
endfor
$player_class = $mail_recipient_class;
"----------------------------------------";
player:notify("Killing all queued tasks ...");
for t in (queued_tasks())
kill_task(t[1]);
endfor
"----------------------------------------";
player:notify("Stripping you of any personal verbs and/or properties ...");
suspend(0);
for i in [1..length(verbs(player))]
delete_verb(player, 1);
endfor
for p in (properties(player))
delete_property(player, p);
endfor
chparent(player, $wiz);
for p in ($object_utils:all_properties(player))
clear_property(player, p);
endfor
player.name = "Wizard";
player.aliases = {"Wizard"};
player.description = "";
player.key = 0;
player.ownership_quota = 100;
player.password = 0;
player.last_password_time = 0;
$gender_utils:set(player, "neuter");
"----------------------------------------";
suspend(0);
player:notify("Making you or $hacker 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 (valid(o.owner) && o.owner.wizard)
o.owner = player;
else
o.owner = $hacker;
endif
old_verbs = {};
for j in [1..length(verbs(o))]
if ((seconds_left() < 1) || (ticks_left() < 2000))
suspend(0);
player:notify(tostr("... finished ", i - 1, " out of ", length(saved), " saved objects ..."));
endif
info = verb_info(o, j);
if (valid(info[1]) && info[1].wizard)
info = listset(info, player, 1);
else
info = listset(info, $hacker, 1);
endif
set_verb_info(o, j, info);
if (index(info[3], "(old)"))
old_verbs = {j, @old_verbs};
endif
endfor
for vname in (old_verbs)
delete_verb(o, vname);
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 (valid(info[1]) && info[1].wizard)
info = listset(info, player, 1);
else
info = listset(info, $hacker, 1);
endif
set_property_info(o, p, info);
endfor
endfor
"----------------------------------------";
player:notify("Removing all unsaved :recycle and :exitfunc verbs ...");
for o in [#0..max_object()]
i = toint(o);
if (i && ((i % 1000) == 0))
player:notify(tostr("... ", o));
endif
$command_utils:suspend_if_needed(0);
if (valid(o) && (!(o in saved)))
for v in ({"recycle", "exitfunc"})
while ($object_utils:defines_verb(o, v))
delete_verb(o, v);
endwhile
endfor
endif
endfor
"----------------------------------------";
player:notify("Recycling unsaved objects ...");
add_property(this, "mcd_pos", toint(max_object()), {player, "r"});
add_property(this, "mcd_save", {saved, saved_props}, {player, "r"});
suspend(0);
this:mcd_2(saved, saved_props);
.
#57:5
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 = toint(argstr[bounds[1]..bounds[2]]);
argstr = argstr[s[2] + 1..$];
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);
.
#57:6
set_task_perms(player);
dump_database();
player:notify("Dumping...");
.
#57:7
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 + "(");
.
#57:8
if (!caller_perms().wizard)
return;
elseif (!("mcd_pos" in properties(this)))
return;
endif
end = this.mcd_pos;
saved = args[1];
saved_props = args[2];
player:notify(tostr("*** Recycling from #", end, " ..."));
suspend(0);
fork (0)
this:mcd_2(saved, saved_props);
endfork
for i in [0..end]
this.mcd_pos = end - i;
o = toobj(end - 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
if (is_player(o))
o.features = {};
set_player_flag(o, 0);
endif
recycle(o);
endif
endfor
delete_property(this, "mcd_pos");
"----------------------------------------";
suspend(0);
player:notify("Killing queued tasks ...");
for t in (queued_tasks())
kill_task(t[1]);
endfor
"----------------------------------------";
player:notify("Compacting object numbers ...");
alist = {};
for p in (saved_props)
$command_utils:suspend_if_needed(0);
if (pair = $list_utils:assoc(#0.(p), alist))
#0.(p) = pair[2];
elseif (#0.(p) != player)
old = #0.(p);
#0.(p) = renumber(#0.(p));
alist = {@alist, {old, #0.(p)}};
endif
endfor
for o in (saved)
if (valid(o) && (o != player))
renumber(o);
endif
endfor
reset_max_object();
"----------------------------------------";
player:notify("Performing miscellaneous cleanups ...");
for o in [#0..max_object()]
$command_utils:suspend_if_needed(0);
try
move(o, ((o == player) || (o == $news)) ? $player_start | #-1);
except e (ANY)
player:notify(tostr("Couldn't move ", o, " => ", e[2]));
player:notify_lines(e[4]);
endtry
if ($object_utils:has_callable_verb(o, "init_for_core"))
try
o:init_for_core();
except e (ANY)
player:notify(tostr("Error from ", o, ":init_for_core() => ", e[2]));
player:notify_lines(e[4]);
endtry
endif
endfor
player:notify("Re-measuring everything ...");
for o in [#0..max_object()]
$command_utils:suspend_if_needed(0);
$byte_quota_utils:object_bytes(o);
endfor
$wiz_utils:initialize_owned();
$byte_quota_utils:summarize_one_user(player);
player:notify("Core database extraction is complete.  Type @shutdown to save it.");
"Last modified Tue Oct 14 14:30:11 1997 CDT by Wizard (#2).";
.
#57:9
"@toad[!][!] <player> [blacklist|redlist|graylist] [commentary]";
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 = $string_utils: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
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
if (!comment)
player:notify("So why is this person being toaded?");
comment = $command_utils:read();
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} | {}});
player:notify(tostr("Mail sent to ", $mail_agent:name($toad_log), "."));
.
#57:10
"@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
.
#57:11
"@quota <player> is [public] <number> [<reason>]";
"  changes a player's quota.  sends mail to the wizards.";
if (player != this)
return player:notify("Permission denied.");
endif
set_task_perms(player);
dobj = $string_utils:match_player(dobjstr);
if ($command_utils:player_match_result(dobj, dobjstr)[1])
return;
elseif (!valid(dobj))
player:notify("Set whose quota?");
return;
endif
if (iobjstr[1..min(7, $)] == "public ")
iobjstr[1..7] = "";
if ($object_utils:has_property($local, "public_quota_log"))
recipients = {$quota_log, $local.public_quota_log};
else
player:tell("No public quota log.");
return E_INVARG;
endif
else
recipients = {$quota_log};
endif
old = $quota_utils:get_quota(dobj);
qstr = iobjstr[1..(n = index(iobjstr + " ", " ")) - 1];
new = $code_utils:toint((qstr[1] == "+") ? qstr[2..$] | qstr);
reason = iobjstr[n + 1..$] || "(none)";
if (typeof(new) != INT)
player:notify(tostr("Set ", dobj.name, "'s quota to what?"));
return;
elseif (qstr[1] == "+")
new = old + new;
endif
result = $quota_utils:set_quota(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, recipients, tostr("@quota ", dobj.name, " (", dobj, ") ", new, " (from ", old, ")"), tostr("Reason for quota ", ((new - old) < 0) ? "decrease: " | "increase: ", reason, index("?.!", reason[$]) ? "" | "."));
.
#57:12
set_task_perms(player);
"The time below is Oct. 1, 1990, roughly the birthdate of the LambdaMOO server.";
start = 654768000;
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;
never_objects = never_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 ? toint(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 (seconds > (now - start))
if (is_player(o))
never_players = never_players + 1;
else
never_objects = never_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("Never:", col1), su:right(never_players, col2), su:right(totalp = totalp + never_players, col3), su:right((totalp * 100) / nump, col4), "%", with_objects ? tostr(su:right(never_objects, col5), su:right(totalo = totalo + never_objects, col6), su:right((totalo * 100) / numo, col7), "%") | ""));
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("");
.
#57:13
"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 = toint(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 = $string_utils: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};
.
#57:14
set_task_perms(player);
if (!args)
player:notify(tostr("Usage:  ", verb, " <pattern>"));
return;
endif
pattern = argstr;
regexp = verb == "@egrepcore";
player:notify(tostr("Searching for core verbs ", regexp ? "matching the regular expression " | "containing the string ", toliteral(pattern), " ..."));
player:notify("");
$code_utils:(regexp ? "find_verbs_matching" | "find_verbs_containing")(pattern, $core_objects());
.
#57:15
"@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.";
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.";
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
$wiz_utils:show_netwho_listing(player, unsorted);
else
$wiz_utils:show_netwho_from_listing(player, iobjstr);
endif
.
#57:16
"Creates a player.";
"Syntax:  @make-player name email-address real name....";
"Generates a random password for the player.";
if ((!player.wizard) || callers())
return E_PERM;
elseif (!args)
player:tell("Syntax:  @make-player name email-address real name....");
return;
elseif (args[2] == "for")
"common mistake: @make-player <name> for <email-address> ...";
args = listdelete(args, 2);
endif
return $wiz_utils:do_make_player(@args);
.
#57:17
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
.
#57:18
"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}";
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
.
#57:19
set_task_perms((caller in {this, $generic_editor, $verb_editor, $mail_editor, $note_editor}) ? this.owner | caller_perms());
return `move(this, args[1]) ! ANY';
.
#57:20
"@newt <player> [commentary]";
"turns a player into a newt.  It can get better...";
"adds player to $login.newted, they will not be allowed to log in.";
"Sends mail to $newt_log giving .all_connect_places and commentary.";
whostr = args[1];
comment = $string_utils:first_word(argstr)[2];
if (!player.wizard)
player:notify("Yeah, right.");
elseif ($command_utils:player_match_failed(who = $string_utils: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 (who in $login.newted)
player:notify(tostr(who.name, " appears to already be a newt."));
return;
else
$wiz_utils:newt_player(who, comment);
endif
.
#57:21
"@denewt <player> [commentary]";
"Remove the player from $Login.newted";
"Sends mail to $newt_log with commentary.";
whostr = args[1];
comment = $string_utils:first_word(argstr)[2];
if (!player.wizard)
player:notify("Yeah, right.");
elseif ($command_utils:player_match_failed(who = $string_utils:match_player(whostr), whostr))
return;
else
"Should parse email address and register user in some clever way.  Ick.";
if (!(who in $login.newted))
player:notify(tostr(who.name, " does not appear to be a newt."));
else
$login.newted = setremove($login.newted, who);
if (entry = $list_utils:assoc(who, $login.temporary_newts))
$login.temporary_newts = setremove($login.temporary_newts, entry);
endif
player:notify(tostr(who.name, " (", who, ") got better."));
$mail_agent:send_message(player, $newt_log, tostr("@denewt ", who.name, " (", who, ")"), comment ? {comment} | {});
endif
endif
.
#57:22
"Registers a player.";
"Syntax:  @register name email-address [additional commentary]";
"Email-address is stored in $registration_db and on the player object.";
if (!player.wizard)
return player:tell(E_PERM);
endif
$wiz_utils:do_register(@args);
.
#57:23
"@newpassword player is [string]";
"Set's a player's password; omit string to have one randomly generated.";
"Offer to email the password.";
if (!player.wizard)
return E_PERM;
elseif ($command_utils:player_match_failed(dobj = $string_utils:match_player(dobjstr), dobjstr))
return;
elseif (!(dobjstr in {@dobj.aliases, tostr(dobj)}))
player:notify(tostr("Must be a full name or an object number: ", dobj.name, " (", dobj, ")"));
else
$wiz_utils:do_new_password(dobj, iobjstr);
endif
.
#57:24
"@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
.
#57:25
set_task_perms(player);
n = (dobjstr == "all") ? 0 | $code_utils:toint(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 ($object_utils:leaves($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};
$command_utils:suspend_if_needed(0);
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($, 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
player:tell("@guests display complete.");
endif
.
#57:26
if (caller != this)
set_task_perms(valid(caller_perms()) ? caller_perms() | player);
endif
use = this.mail_identity;
if (valid(use) && (use != this))
return use:(verb)(@args);
else
return pass(@args);
endif
.
#57:27
"@[un]blacklist [<site or subnet>  [for <duration>] [commentary]]";
"@[un]graylist  [<site or subnet>  [for <duration>] [commentary]]";
"@[un]redlist   [<site or subnet>  [for <duration>] [commentary]]";
"@[un]spooflist [<site of subnet>  [for <duration>] [commentary]]";
"The `for <duration>' is for temporary colorlisting a site only. The duration should be in english time units:  for 1 hour, for 1 day 2 hours 15 minutes, etc. The commentary should be after all durations. Note, if you are -not- using a duration, do not start your commentary with the word `for'.";
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...";
this:display_list(which);
return;
endif
target = fw[1];
if (fw[2] && (parse = this:parse_templist_duration(fw[2]))[1])
if ((typeof(parse[3]) == ERR) || (!parse[3]))
player:notify(tostr("Could not parse the duration for @", which, "ing site \"", target, "\""));
return;
endif
start = parse[2];
duration = parse[3];
comment = parse[4] ? {parse[4]} | {};
comment = {tostr("for ", $time_utils:english_time(duration)), @comment};
elseif (fw[2])
comment = fw[2];
else
"Get the right vars set up as though parse had been called";
parse = {0, ""};
comment = {};
endif
if (is_literal = $site_db:domain_literal(target))
if (target[$] == ".")
target = target[1..$ - 1];
endif
fullname = "subnet " + target;
else
if (target[1] == ".")
target[1..1] = "";
endif
fullname = ("domain `" + target) + "'";
endif
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);
if (!(result = this:check_site_entries(undo, which, target, is_literal, entrylist))[1])
return;
endif
rm = result[2];
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
tempentrylist = $login.("temporary_" + which)[1 + (!is_literal)];
if ((!undo) && (target in $list_utils:slice(tempentrylist)))
player:notify(tostr(fullname, " is already temporarily ", which, "ed."));
return;
endif
if (en = $list_utils:assoc(target, tempentrylist))
tempentrylist = setremove(tempentrylist, en);
endif
if (!(result = this:check_site_entries(undo, which, target, is_literal, $list_utils:slice(tempentrylist)))[1])
return;
endif
rmtemp = result[2];
tempnamelist = $string_utils:english_list(rmtemp);
tempdowngraded = {};
if (rmtemp)
ntries = (length(rmtemp) == 1) ? "ntry" | "ntries";
if ($command_utils:yes_or_no(tostr("Remove e", ntries, " for ", tempnamelist, "?")))
dg = undo && (downgrade && $command_utils:yes_or_no(downgrade + " them?"));
for s in (rmtemp)
old = $list_utils:assoc(s, tempentrylist);
$login:(which + "_remove_temp")(s);
dg && ($login:(downgrade + "_add_temp")(s, old[2], old[3]) && (tempdowngraded = {@tempdowngraded, s}));
endfor
player:notify(tostr("E", ntries, " removed", @dg ? {" and ", downgrade, "ed with durations transferred."} | {"."}));
else
player:notify(tostr(tempnamelist, " will continue to be temporarily ", which, "ed."));
rmtemp = {};
endif
endif
if (tempdowngraded)
comment[1..0] = {tostr(downgrade, "ed ", $string_utils:english_list(tempdowngraded), ".")};
endif
if (!undo)
if (parse[1])
$login:(which + "_add_temp")(target, start, duration);
player:notify(tostr(fullname, " ", which, "ed for ", $time_utils:english_time(duration)));
else
$login:(which + "_add")(target);
player:notify(tostr(fullname, " ", which, "ed."));
endif
if (rm)
comment[1..0] = {tostr("Subsumes ", which, "ing for ", namelist, ".")};
endif
if (rmtemp)
comment[1..0] = {tostr("Subsumes temporary ", which, "ing for ", tempnamelist, ".")};
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 ((old = $list_utils:assoc(target, $login.("temporary_" + which)[1 + (!is_literal)])) && $login:(which + "_remove_temp")(target))
player:notify(tostr(fullname, " un", which, "ed."));
if (!downgrade)
elseif ($command_utils:yes_or_no(downgrade + " it?"))
$login:(downgrade + "_add_temp")(target, old[2], old[3]) && (tempdowngraded = {target, @tempdowngraded});
player:notify(tostr(fullname, " ", downgrade, "ed, currently for ", $time_utils:english_time(old[3]), " from ", $time_utils:time_sub("$1/$3", old[2])));
else
player:notify(tostr(fullname, " not ", downgrade, "ed."));
endif
if (tempdowngraded)
comment[1..0] = {tostr(downgrade, "ed ", $string_utils:english_list(tempdowngraded), "with durations transferred.")};
endif
if (rmtemp)
comment[1..0] = {tostr("Also removed ", tempnamelist, ".")};
endif
elseif (rm || rmtemp)
player:notify(tostr(fullname, " itself was never actually ", which, "ed."));
comment[1..0] = {tostr("Removed ", namelist, " from regular and ", tempnamelist, " from temporary.")};
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
.
#57:28
"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;
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..$];
endif
try
add_property(#0, iobjstr, dobj, {player, "r"});
except e (ANY)
return player:tell(e[1], ":", e[2]);
endtry
player:tell("Corified ", $string_utils:nn(dobj), " as $", iobjstr, ".");
"Last modified Thu Apr  8 10:35:55 1999 CDT by Wizard (#2).";
.
#57:29
"Usage:  @make-guest <guestname>";
"Creates a player called <guestname>_Guest owned by $hacker and a child of $guest. Or, if $local.guest exists, make a child of that, assuming that all other guests are children of it too.";
if (!player.wizard)
player:tell("If you think this MOO needs more guests, you should contact a wizard.");
return E_PERM;
endif
if (length(args) != 1)
player:tell("Usage: ", verb, " <guest name>");
return;
endif
guest_parent = (($object_utils:has_property($local, "guest") && valid($local.guest)) && $object_utils:isa($local.guest, $guest)) ? $local.guest | $guest;
i = length(children(guest_parent));
while (!$player_db:available(guestnum = tostr("Guest", i = i + 1)))
endwhile
guestname = args[1] + "_Guest";
guests = children($guest);
if (length(guests) == 0)
guestaliases = {guestname, adj = args[1], "Guest"};
else
guestaliases = {guestname, adj = args[1], guestnum};
endif
if (!player.wizard)
return;
elseif ($player_db.frozen)
player:tell("Sorry, the player db is frozen, so no players can be made right now.  Please try again in a few minutes.");
return;
elseif (!$player_db:available(guestname))
player:tell("\"", guestname, "\" is not an available name.");
return;
elseif (!$player_db:available(adj))
player:Tell("\"", adj, "\" is not an available name.");
return;
else
new = $quota_utils:bi_create(guest_parent, $hacker);
new:set_name(guestname);
new:set_aliases(guestaliases);
new.default_name = guestname;
new.default_aliases = guestaliases;
if (!(e = $wiz_utils:set_player(new, 1)))
player:Tell("Unable to make ", new.name, " (", new, ") a player.");
player:Tell(tostr(e));
else
player:Tell("Guest: ", new.name, " (", new, ") made.");
new.default_description = {"By definition, guests appear nondescript."};
new.description = new.default_description;
new.last_connect_time = $maxint;
new.last_disconnect_time = time();
new.password = 0;
new.size_quota = new.size_quota;
new:set_gender(new.default_gender);
move(new, $player_start);
player:tell("Now don't forget to @describe ", new, " as something.");
endif
endif
"Last modified Thu Apr  8 10:51:48 1999 CDT by Wizard (#2).";
.
#57:30
if (!player.wizard)
return player:tell("Permission denied.");
elseif (!valid(who = $string_utils:match_player(dobjstr)))
return $command_utils:player_match_failed(who, dobjstr);
elseif (((dobjstr != who.name) && (!(dobjstr in who.aliases))) && (dobjstr != tostr(who)))
return player:tell(tostr("Must be a full name or an object number:  ", who.name, "(", who, ")"));
elseif (who == player)
player:notify("If you want to newt yourself, you have to do it by hand.");
return;
elseif (!(howlong = $time_utils:parse_english_time_interval(iobjstr)))
return player:tell("Can't parse time: ", howlong);
else
if (who in $login.newted)
player:notify(tostr(who.name, " appears to already be a newt."));
else
$wiz_utils:newt_player(who, "", ("For " + iobjstr) + ".  ");
endif
if (index = $list_utils:iassoc(who, $login.temporary_newts))
$login.temporary_newts[index][2] = time();
$login.temporary_newts[index][3] = howlong;
else
$login.temporary_newts = {@$login.temporary_newts, {who, time(), howlong}};
endif
player:tell(who.name, " (", who, ") will be a newt until ", ctime(time() + howlong));
endif
.
#57:31
"@deprogrammer victim [for <duration>] [reason]";
"";
"Removes the prog-bit from victim.  If a duration is specified (see help $time_utils:parse_english_time_interval), then the victim is put into the temporary list. He will be automatically removed the first time he asks for a progbit after the duration expires.  Either with or without the duration you can specify a reason, or you will be prompted for one. However, if you don't have a duration, don't start the reason with the word `For'.";
set_task_perms(player);
if ((player != this) || (!player.wizard))
player:notify("No go.");
return;
endif
if (!args)
player:notify(tostr("Usage:  ", verb, " <playername> [for <duration>] [reason]"));
endif
fw = $string_utils:first_word(argstr);
if (fw[2] && (parse = this:parse_templist_duration(fw[2]))[1])
if ((typeof(parse[3]) == ERR) || (!parse[3]))
player:notify(tostr("Could not parse the duration for restricting programming for ", fw[1], "."));
return;
endif
start = parse[2];
duration = parse[3];
reason = parse[4] ? {parse[4]} | {};
reason = {tostr("for ", $time_utils:english_time(duration)), @reason};
else
start = duration = 0;
reason = fw[2] ? {fw[2]} | {};
endif
if ($command_utils:player_match_failed(victim = $string_utils:match_player(fw[1]), fw[1]))
"...done...";
elseif (result = $wiz_utils:unset_programmer(victim, reason || $command_utils:read("reason for resetting programmer flag"), @start ? {start, duration} | {}))
player:notify(tostr(victim.name, " (", victim, ") is no longer a programmer."));
elseif (result == E_NONE)
player:notify(tostr(victim.name, " (", victim, ") was already a nonprogrammer..."));
else
player:notify(tostr(result));
endif
.
#57:32
if ((caller != this) && (!caller_perms().wizard))
return E_PERM;
endif
which = args[1];
slist = {};
if (s = $login.(which)[1])
slist = {@slist, "--- Subnets ---", @s};
endif
if (s = $login.(which)[2])
slist = {@slist, "--- Domains ---", @s};
endif
if (s = $login.("temporary_" + which)[1])
slist = {@slist, "--- Temporary Subnets ---"};
for d in (s)
slist = {@slist, tostr(d[1], " until ", $time_utils:time_sub("$1/$3 $H:$M", d[2] + d[3]))};
$command_utils:suspend_if_needed(2);
endfor
endif
if (s = $login.("temporary_" + which)[2])
slist = {@slist, "--- Temporary Domains ---"};
for d in (s)
slist = {@slist, tostr(d[1], " until ", $time_utils:time_sub("$1/$3 $H:$M", d[2] + d[3]))};
$command_utils:suspend_if_needed(2);
endfor
endif
if (slist)
player:notify_lines($string_utils:columnize(slist, 2));
else
player:notify(tostr("The ", which, " is empty."));
endif
.
#57:33
"parses out the time interval at the beginning of the args[1], assumes rest is commentary.";
if ((fw = $string_utils:first_word(args[1]))[1] == "for")
words = $string_utils:words(fw[2]);
try_ = {};
ind = cont = 1;
while (cont)
word = words[ind];
cont = ind;
if (toint(word))
try_ = {@try_, word};
ind = ind + 1;
else
for set in ($time_utils.time_units)
if (word in set)
try_ = {@try_, word};
ind = ind + 1;
endif
endfor
endif
if ((cont == ind) || (ind > length(words)))
cont = 0;
endif
endwhile
dur = $time_utils:parse_english_time_interval(@try_);
rest = $string_utils:from_list(words[ind..$], " ");
return {1, time(), dur, rest};
else
return {0, argstr};
endif
.
#57:34
"Called by @[un]<color>list to check existence of the target site.";
"=> {done okay, LIST of sites to remove}";
if (caller != this)
return E_PERM;
endif
{undo, which, target, is_literal, entrylist} = args;
rm = {};
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 {0, {}};
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 {0, {}};
endif
endif
endfor
endif
return {1, rm};
.
#57:35
"Syntax:  @lock-login <message>";
"         @lock-login! <message>";
"         @unlock-login";
"";
"The @lock-login calls prevent all non-wizard users from logging in, displaying <message> to them when they try.  (The second syntax, with @lock-login!, additionally boots any nonwizards who are already connected.)  @unlock-login turns this off.";
if (caller != this)
raise(E_PERM);
elseif (verb[2] == "u")
$no_connect_message = 0;
player:notify("Login restrictions removed.");
elseif (!argstr)
player:notify("You must provide some message to display to users who attempt to login:  @lock-login <message>");
else
$no_connect_message = argstr;
player:notify(tostr("Logins are now blocked for non-wizard players.  Message displayed when attempted:  ", $no_connect_message));
if (verb == "@lock-login!")
wizards = $wiz_utils:all_wizards_unadvertised();
for x in (connected_players())
if (!(x in wizards))
boot_player(x);
endif
endfor
player:notify("All nonwizards have been booted.");
endif
endif
.
#57:36
if (!caller.wizard)
return E_PERM;
elseif (!args)
return player:tell("Syntax: @boot player-name");
else
victim = $string_utils:match_player(args[1]);
if ($command_utils:player_match_failed(victim, args[1]))
return;
elseif (!(victim in connected_players()))
return player:tell(victim.name, " is not connected.");
else
if ($command_utils:yes_or_no(("Are you sure you want to disconnect " + victim.name) + " from the MOO?"))
domain = $string_utils:connection_hostname(victim.last_connect_place);
victim:tell(">>> YOU HAVE BEEN DISCONNECTED FROM THIS MOO <<<");
boot_player(victim);
player:tell(victim.name, " has been disconnected from the MOO. If this person was a guest and you wish to prevent him or her from logging on again you can blacklist their site by typing '@blacklist ", domain, "' now. Please keep in mind that blacklisting will block all guest access from that site. If the person was a registered user you can use the '@newt' command to temporarily block their access to this MOO, or the '@toad' command if you need to remove this person's character permanently.");
else
player:tell("Okay, booting aborted.");
endif
endif
endif
"Last modified Sun Aug 17 11:01:38 1997 CDT by Wizard (#2).";
.
#57:37
"===========================================================";
"Copyright (C) 1998-2001, Jan Rune Holmevik";
"Verb that simplifies the process of setting important core preferences";
" Modified to start $quota_utils:measurement_task first time it's run";
"===========================================================";
if (!caller.wizard)
return E_PERM;
endif
finished = 0;
title = $encore_utils:make_title($network.MOO_name + " Configuration Menu");
while (!finished)
player:tell_lines(title);
player:tell("");
if (!$core_customized)
player:tell("Welcome to the High Wired enCore configuration program. Before you can start to use your MOO you need to customize it by setting a few preferences. This process will take a few minutes, and this program help you through it. Please note that you can abort the customization procedure at any point. The preferences you have set will be saved so you can always come back and continue the setup from where you left off. You should set all the preferences below before you start using your MOO");
else
player:tell("Your database was last configured on ", $time_utils:time_sub("$n $t, $Y", $core_customized));
endif
player:tell("");
player:tell("Please select one of the preferences below to change it.");
player:tell("");
player:tell("Wizard #2    Network        Login                 enCore Xpress");
player:tell("---------    -------        -------               -------------");
player:tell("1) Email     4) On/Off      10) Auto Create       15) On/Off");
player:tell("2) Real Name 5) MOO Name    11) Anonymity         16) HTTP/0.9 On/Off");
player:tell("3) Password  6) Port        12) Real Guest Names  17) Internet Mail");
player:tell("             7) Web Port    13) Guest Name Suffix 18) Base URL");
player:tell("             8) Domain Name 14) Welcome Screen");
player:tell("             9) Mail Server");
player:tell("");
player:tell("Please enter your choice (1-18), or type Q to quit");
ans = read(player);
if (ans in {"1", "2", "3"})
"----------------------- #2 Wizard Preferences -------------------";
player:tell("Preferences for Wizard #2");
player:tell("-------------------------");
if (ans == "1")
"------------- Set network and #2 email address ---------------";
player:tell("The network postmaster address is currently :", $network.postmaster);
player:tell("[Please enter a new email address as the default address for ALL network related email addresses, or press RETURN or ENTER to return to the main menu]");
email_address = read(player);
if (email_address)
$network.postmaster = $network.reply_address = $network.errors_to_address = $network.usual_postmaster = $network.password_postmaster = $network.envelope_from = email_address;
$wiz_utils:do_register(#2.name, email_address, #2.real_name);
player:tell("Email addresses registered.");
endif
elseif (ans == "2")
"------------- Set real name ---------------";
if (#2.real_name)
player:tell("#2's real name is currently :", #2.real_name);
endif
player:tell("[Please enter your full name as the identity of wizard #2, or press RETURN or ENTER to return to the main menu]");
name = read(player);
if (name)
$wiz_utils:do_register(#2.name, #2.email_address, name);
player:tell("New real name set.");
endif
elseif (ans == "3")
"------------- Set #2 password ---------------";
player:tell("[Please type in a password for wizard #2, or press RETURN or ENTER to return to the main menu]");
passwd = read(player);
if (passwd)
#2.password = crypt(tostr(passwd));
#2.last_password_time = time();
player:tell("Password for #2 set");
endif
endif
elseif (ans in {"4", "5", "6", "7", "8", "9"})
"----------------------- Network Preferences -------------------";
player:tell("Network Preferences");
player:tell("-------------------");
if (ans == "4")
"------------- Network on/off ---------------";
if ($network.active)
player:tell("[Your network is active. Type 'off' to disable it, or press RETURN or ENTER to return to the main menu]");
else
player:tell("[Your network is not active. If you wish to be able to send email from the MOO or enable commands like @newpassword, @registerme, @password, @mailme, @netforward, and others, you need to make it active by typing 'on', or press RETURN or ENTER to return to the main menu'");
endif
network = read(player);
if (network == "on")
$network.active = 1;
player:tell("Network activated");
elseif (network == "off")
$network.active = 0;
player:tell("Network deactivated");
endif
elseif (ans == "5")
"------------- MOO name ---------------";
player:tell("The name of the MOO is: ", $network.MOO_name);
player:tell("[Please type in a new name, or press RETURN or ENTER to return to the main menu]");
mooname = read(player);
if (mooname)
$network.MOO_name = mooname;
player:tell("New name set.");
endif
elseif (ans == "6")
"------------- MOO port ---------------";
player:tell("The MOO is currently listening for incoming connections on port: ", $network.port);
player:tell("Please note that if you change if from the default port, 7777, you must give the new port number as an argument next time you restart your moo. Example: restart moo 8888");
player:tell("[Please type a new port number, or press RETURN or ENTER to return to the main menu]");
port = read(player);
if (port)
$network.port = toint(port);
player:tell("New port set.");
endif
elseif (ans == "7")
"------------- Web port ---------------";
player:tell("The MOO's enCore web server is currently listening on port: ", $network.webport);
player:tell("Please note that changes you make will not take effect until you reboot the MOO");
player:tell("[Please type a new web port number, or press RETURN or ENTER to return to the main menu]");
port = read(player);
if (port)
try
unlisten($network.webport);
except e (ANY)
endtry
$network.webport = toint(port);
listen($httpd, $network.webport);
player:tell("New web port set.");
endif
elseif (ans == "8")
"------------- Domain name ---------------";
player:tell("The domain name of the machine your MOO is running on is currently: ", $network.site);
player:tell("[Please type in a new domain name (e.g. lingua.utdallas.edu), or press RETURN or ENTER to return to the main menu]");
site = read(player);
if (site)
$network.site = site;
player:tell("Domain name set.");
endif
elseif (ans == "9")
"------------- Mail server ---------------";
player:tell("The domain name of mail server your MOO is using is currently: ", $network.maildrop);
player:tell("You must specify the domain name of a valid mail server in order to be able to send email from the MOO.");
player:tell("[Please type in the domain name for the new mailserver (e.g. mailserver.utdallas.edu), or press RETURN or ENTER to return to the main menu]");
mailserver = read(player);
if (mailserver)
$network.maildrop = mailserver;
player:tell("Mailserver set.");
endif
endif
elseif (ans in {"10", "11", "12", "13", "14"})
"----------------------- Login Preferences -------------------";
player:tell("Login Preferences");
player:tell("-----------------");
if (ans == "10")
"------------- Automatic Character Creation ---------------";
if ($login.create_enabled)
player:tell("[Automatic character creation is enabeled. Type 'off' to disable it, or press RETURN or ENTER to return to the main menu]");
else
player:tell("[Automatic character creation is disabeled. In an educational environment, we recommend that you don't enable it. If you wish to enable it type 'on', or press RETURN or ENTER to return to the main menu']");
endif
auto = read(player);
if (auto == "on")
$login.create_enabled = 1;
player:tell("Automatic Character Creation has been enabled.");
elseif (auto == "off")
$login.create_enabled = 0;
player:tell("Automatic Character Creation has been disabled");
endif
elseif (ans == "11")
"------------- Anonymous users ---------------";
if ($anonymous_users)
player:tell("[Anonymity is on. Type 'off' to disable it, or press RETURN or ENTER to return to the main menu]");
else
player:tell("[Anonymity is off. All users are identified by their real name and email address. In an educational environment, we recommend that you don't allow anonymous users. If you wish to enable it type 'on', or press RETURN or ENTER to return to the main menu']");
endif
anon = read(player);
if (anon == "on")
$anonymous_users = 1;
player:tell("Anonymity has been enabled.");
elseif (anon == "off")
$anonymous_users = 0;
player:tell("Anonymity has been disabled");
endif
elseif (ans == "12")
"------------- Real Guest Names ---------------";
if ($real_guest_names)
player:tell("[Real guest names are enabeled. Type 'off' to disable this function, or press RETURN or ENTER to return to the main menu]");
else
player:tell("[Real guest names are currenly disabeled. Guests will be assigned predefined names. If you wish to allow guests to select their own names, type 'on', or press RETURN or ENTER to return to the main menu']");
endif
gname = read(player);
if (gname == "on")
$real_guest_names = 1;
player:tell("Real guest names has been enabled.");
elseif (gname == "off")
$real_guest_names = 0;
player:tell("Predefined guest names will be used");
endif
elseif (ans == "13")
"------------- Guest Name Suffix ---------------";
if ($guest_name_suffix)
player:tell("[Guest names are currently suffixed by ", $guest_name_suffix, ". Please type a new suffix, type 'none' if you want to use guest names without the suffix, or press RETURN or ENTER to return to the main menu]");
else
player:tell("[Guest name suffix is currently not in use. Please type a new suffix, type 'none' if you want to use guest names without the suffix, or press RETURN or ENTER to return to the main menu]");
endif
suffix = read(player);
if (suffix == "none")
$guest_name_suffix = "";
player:tell("Guest name suffix off");
elseif (suffix)
$guest_name_suffix = suffix;
player:tell("Guest name suffix set to ", $guest_name_suffix);
endif
elseif (ans == "14")
"------------- Welcome screen ---------------";
player:tell("The welcome screen is the first thing people see when they connect to your MOO. We recommend that you edit it to include information about how to log in, and the policy and purpose of your MOO.");
if (msg = $command_utils:read_lines())
player:tell("You typed:");
player:tell_lines(msg);
if ($command_utils:yes_or_no("Are you sure you want to change your welcome screen to this?"))
$login.welcome_message = msg;
player:tell("Welcome screen changed.");
else
player:tell("Welcome screen not changed");
endif
endif
endif
elseif (ans in {"15", "16", "17", "18"})
"----------------------- enCore Xpress Preferences -------------------";
player:tell("enCore Xpress Preferences");
player:tell("-----------------");
if (ans == "15")
"------------- Xpress Client on/off ---------------";
if ($xpress_client.on)
player:tell("[The enCore Xpress Client is running. Type 'off' to shut it down it, or press RETURN or ENTER to return to the main menu]");
else
player:tell("[The enCore Xpress Client is not running. Type 'on' to start it, or press RETURN or ENTER to return to the main menu']");
endif
xpress_on = read(player);
if (xpress_on == "on")
$Xpress_client.on = 1;
listen($httpd, $network.webport);
player:tell("enCore Xpress Client Started.");
elseif (xpress_on == "off")
$Xpress_client.on = 0;
unlisten($network.webport);
player:tell("enCore Xpress Client Stopped");
endif
elseif (ans == "16")
"------------- enable HTTP 0.9 ---------------";
if ($xpress_client.HTTP_09_enabled)
player:tell("[enCore Xpress is currently allowing anonymous HTTP requests for object descriptions. Type 'off' to disable this feature, or press RETURN or ENTER to return to the main menu]");
else
player:tell("[enCore Xpress is not allowing anonymous HTTP requests for object descriptions. Type 'on' to enable this feature, or press RETURN or ENTER to return to the main menu']");
endif
http_09 = read(player);
if (http_09 == "on")
$Xpress_client.http_09_enabled = 1;
player:tell("Anonymous HTTP requests enabled.");
elseif (http_09 == "off")
$Xpress_client.http_09_enabled = 0;
player:tell("Anonymous HTTP requests disabled");
endif
elseif (ans == "17")
"------------- Internet Mail ---------------";
if ($xpress_client.allow_internet_mail)
player:tell("[enCore Xpress is currently allowing users to send mail via the Internet. Type 'off' to disable this feature, or press RETURN or ENTER to return to the main menu]");
else
player:tell("[enCore Xpress is not allowing users to send mail via the Internet. Type 'on' to enable this feature, or press RETURN or ENTER to return to the main menu']");
endif
internet_mail = read(player);
if (internet_mail == "on")
$Xpress_client.allow_internet_mail = 1;
player:tell("Internet mail has been enabled.");
elseif (internet_mail == "off")
$Xpress_client.allow_internet_mail = 0;
player:tell("Internet mail has been disabled");
endif
elseif (ans == "18")
"------------- External base URL ---------------";
player:tell("[The external base URL for enCore Xpress files is currently ", $xpress_client.external_baseurl, ". Type a new URL to change it. Please note that this URL must point to a directory that is accessible to an ordinary web server such as Apache. The URL must also point to the same machine as the MOO itself is running on.  Press RETURN or ENTER to return to the main menu]");
base_url = read(player);
if (base_url && (base_url != $xpress_client.external_baseurl))
if (base_url[$] != "/")
base_url = base_url + "/";
endif
$xpress_client.external_baseurl = base_url;
player:tell("External enCore Xpress base URL set to ", base_url);
else
player:tell("External enCore Xpress base URL unchanged.");
endif
endif
else
finished = 1;
if (!$core_customized)
"Start measurement task on first run";
$quota_utils:schedule_measurement_task();
player:tell("Quota measurement task started.");
endif
$core_customized = time();
player:tell("Program Configure Stopped.");
endif
endwhile
"Last modified Fri Apr 13 19:13:14 2001 CDT by Wizard (#2).";
.
#57:38
if (!caller.wizard)
return E_PERM;
elseif (!$network.active)
player:tell("Sorry, the network is not active. I cannot send email.");
elseif (!iobjstr)
player:tell("This command sends an email message in the form of a $note to all users if the argument 'all' is given (i.e. @email note to all), or to a select group of users (i.e. @email note to Tom Jerry). You must specify one or more players to send this email to, or 'all' to send it to all registered players.");
elseif (!$object_utils:isa(dobj, $note))
player:tell("The object you have asked me to email is not a $note object. Please specify a new email to send.");
else
msg = dobj.text;
to = {};
if (iobjstr != "all")
recipients = $string_utils:explode(iobjstr);
for i in (recipients)
if (valid(a = $string_utils:match_player(i)))
if ((($object_utils:isa(a, $guest) || (a == $hacker)) || (a == $no_one)) || (a == $housekeeper))
"Do nothing";
else
to = {@to, a};
endif
else
player:tell("Unknown name: ", i, " skipping this person...");
endif
endfor
else
player:tell("Checking for duplicate email addresses...");
for n in (players())
$command_utils:suspend_if_needed(0);
duplicate = 0;
if ((($object_utils:isa(n, $guest) || (n == $hacker)) || (n == $no_one)) || (n == $housekeeper))
"Do nothing.";
else
for x in (to)
$command_utils:suspend_if_needed(0);
if (x.email_address == n.email_address)
duplicate = 1;
player:tell(n.name, " is already registered under ", x.email_address, " (", x.name, ")");
endif
endfor
if (!duplicate)
to = {@to, n};
endif
endif
endfor
endif
if (to)
if ($command_utils:yes_or_no(tostr("Do you want to send the email entitled >> ", dobj.name, " << to ", length(to), " player(s)?")))
subject = dobj.name;
reply_address = ($anonymous_users == 0) ? player.email_address | $network.reply_address;
for n in (to)
sent = $network:sendmail(n.email_address, subject, "Reply-to: " + reply_address, @msg);
success = (typeof(sent) == ERR) ? "not sent" | "sent";
player:tell(tostr("Email was ", success, " to ", n:title(), "."));
endfor
player:tell(">> EMAILING FINISHED <<");
else
player:tell("Okay, emailing aborted.");
endif
else
player:tell("I cannot find any players by that name.");
endif
endif
"Last modified Tue Oct 14 14:12:04 1997 CDT by Wizard (#2).";
.
#57:39
"Copyright (C) 1998, Rui Miguel Barbosa Pinto";
"Usage: @mvprop*erty <obj>.<property> to <newobject>";
if (!player.wizard)
return E_PERM;
endif
if (length(args) < 3)
player:tell("Move what property to what object?");
return;
endif
targ = $string_utils:explode(args[1], ".");
object1 = player:my_match_object(targ[1]);
object2 = player:my_match_object(args[3]);
if (!$object_utils:has_property(object1, targ[2]))
player:tell("Couldn't find ", args[1], ".");
return;
endif
if (!valid(object2))
player:tell("Couldn't find ", args[3], ".");
return;
endif
family = 0;
if (object2 in $object_utils:ancestors(object1))
kid = object1;
family = 1;
elseif (object2 in $object_utils:descendants(object1))
kid = object2;
family = 1;
else
family = 0;
endif
if ($object_utils:has_property(object2, targ[2]) && (!family))
player:tell(object2.name, " already has that property.");
return;
endif
if ((!$perm_utils:controls(player, object1)) || (!$perm_utils:controls(player, object2)))
player:tell("You don't have permissions for that.");
return;
endif
val = object1.(targ[2]);
inf = property_info(object1, targ[2]);
l = {};
if (family)
for x in ($object_utils:descendants(kid))
l = setadd(l, {x, x.(targ[2])});
$command_utils:suspend_if_needed(0);
endfor
endif
delete_property(object1, targ[2]);
add_property(object2, targ[2], val, inf);
if (family)
for x in (l)
x[1].(targ[2]) = x[2];
$command_utils:suspend_if_needed(0);
endfor
endif
player:tell("Property moved.");
"Last modified Fri Feb  6 08:03:07 1998 CST by Wizard (#2).";
.
#57:40
"Copyright (C) 1998, Rui Miguel Barbosa Pinto";
"Usage: @renprop <#obj>.<property> to <new-property-name>";
if (!player.wizard)
return E_PERM;
endif
if (length(args) < 3)
player:tell("Rename what property to what?");
return;
endif
targ = $string_utils:explode(args[1], ".");
object = player:my_match_object(targ[1]);
if (!$object_utils:has_property(object, targ[2]))
player:tell("Couldn't find ", args[1], ".");
return;
endif
if ($object_utils:has_property(object, args[3]))
player:tell("That object already has a property named ", args[3], ".");
return;
endif
if (!$perm_utils:controls(player, object))
player:tell("You don't have permissions for that.");
return;
endif
e = add_property(object, args[3], object.(targ[2]), property_info(object, targ[2]));
if (typeof(e) == ERR)
player:tell(e);
return;
endif
for x in ($object_utils:descendants(object))
$command_utils:suspend_if_needed(1);
x.(args[3]) = x.(targ[2]);
endfor
delete_property(object, targ[2]);
player:tell("Property name changed.");
"Last modified Fri Feb  6 08:03:32 1998 CST by Wizard (#2).";
.
#57:41
"Program that automates the process of setting up in-MOO news groups";
"Copyright (C) 1998, Jan Rune Holmevik";
if (!player.wizard)
return E_PERM;
endif
wiz_only = 0;
if (args)
name = args[1];
else
name = $command_utils:read("a name for the new mailing list");
endif
player:tell("Please state the purpose of this mailing list.");
description = $command_utils:read_lines();
if ($command_utils:yes_or_no("Is this a wizard only mailing list"))
wiz_only = 1;
endif
new_list = player:_create($mail_recipient, player);
if (typeof(new_list) == ERR)
player:notify(tostr(object));
else
$building_utils:set_names(new_list, name);
new_list.description = description;
if (!wiz_only)
new_list.readers = 1;
endif
move(new_list, $mail_agent);
player:tell("New list ", name, " created and moved to the ", $mail_agent.name, " (", $mail_agent, ").");
endif
"Last modified Fri Feb  6 08:40:25 1998 CST by Wizard (#2).";
.
#57:42
if (caller_perms().wizard)
pass();
this.features = $list_utils:append(this.features, {$lag_meter, $census_FO, $account_admin_FO, $SR_porter});
endif
"Last modified Fri Feb  6 08:47:08 1998 CST by Wizard (#2).";
.
#57:43
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Generates a dynamic web menu line based on which player class the user belongs to";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{menu, functions, preload, base_url} = pass(@args);
external_baseurl = (($xpress_client.external_baseurl + $xpress_client.themes_folder) + this.xpress_theme) + "/";
administration = {tostr("<A HREF=\"", base_url, "administration_module/main.html\" TARGET=\"", $administration_module:get_frame_name("openAdministrationModule"), "\" onClick=\"openAdministrationModule(); swapImage('wizard','", external_baseurl, "wizard1.jpg')\" onMouseOver=\"swapImage('wizard','", external_baseurl, "wizard2.jpg')\" onMouseOut=\"swapImage('wizard','", external_baseurl, "wizard1.jpg')\"><IMG SRC=\"", external_baseurl, "wizard1.jpg\" BORDER=0 ALIGN=bottom name=wizard ALT=\"MOO Administration\" TITLE=\"MOO Administration\"></A>")};
preload = {@preload, tostr("   wizard2 = new Image(); wizard2.src = \"", external_baseurl, "wizard2.jpg\";")};
openAdministrationModule = $encore_web_utils:javascript_window_open($administration_module, "openAdministrationModule", "");
menu = $list_utils:append(menu, administration);
functions = $list_utils:append(functions, openAdministrationModule);
return {menu, functions, preload, base_url};
"Last modified Fri Apr 13 19:05:56 2001 CDT by Wizard (#2).";
.
#58:0
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, talk to a wizard.");
return;
elseif (!$quota_utils:property_addition_permitted(player))
player:tell("Property addition not permitted because quota exceeded.");
return;
endif
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]..$]);
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
default = player:prog_option("@prop_flags");
if (!default)
default = "rc";
endif
perms = (nargs < 3) ? default | $perm_utils:apply(default, args[3]);
if (nargs < 4)
owner = player;
else
owner = $string_utils: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
try
add_property(object, name, value, {owner, perms});
player:notify(tostr("Property added with value ", toliteral(object.(name)), "."));
except (E_INVARG)
if ($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
except e (ANY)
player:notify(e[2]);
endtry
.
#58:1
set_task_perms(player);
bynumber = verb == "@chmod#";
if (length(args) != 2)
player:notify(tostr("Usage:  ", verb, " <object-or-property-or-verb> <permissions>"));
return;
endif
{what, perms} = args;
if (spec = $code_utils:parse_verbref(what))
if (!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.");
return;
endif
if (valid(object = player:my_match_object(spec[1])))
vname = spec[2];
if (bynumber)
vname = $code_utils:toint(vname);
if (vname == E_TYPE)
return player:notify("Verb number expected.");
elseif ((vname < 1) || `vname > length(verbs(object)) ! E_PERM => 0')
return player:notify("Verb number out of range.");
endif
endif
try
info = verb_info(object, vname);
if (!valid(owner = info[1]))
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."));
else
info[2] = perms = $perm_utils:apply(info[2], perms);
try
result = set_verb_info(object, vname, info);
player:notify(tostr("Verb permissions set to \"", perms, "\"."));
except (E_INVARG)
player:notify(tostr("\"", perms, "\" is not a valid permissions string for a verb."));
except e (ANY)
player:notify(e[2]);
endtry
endif
except (E_VERBNF)
player:notify("That object does not define that verb.");
except error (ANY)
player:notify(error[2]);
endtry
return;
endif
elseif (bynumber)
return player:notify("@chmod# can only be used for verbs.");
elseif (index(what, ".") && (spec = $code_utils:parse_propref(what)))
if (valid(object = player:my_match_object(spec[1])))
pname = spec[2];
try
info = property_info(object, pname);
info[2] = perms = $perm_utils:apply(info[2], perms);
try
result = set_property_info(object, pname, info);
player:notify(tostr("Property permissions set to \"", perms, "\"."));
except (E_INVARG)
player:notify(tostr("\"", perms, "\" is not a valid permissions string for a property."));
except error (ANY)
player:notify(error[2]);
endtry
except (E_PROPNF)
player:notify("That object does not have that property.");
except error (ANY)
player:notify(error[2]);
endtry
return;
endif
elseif (valid(object = player:my_match_object(what)))
perms = $perm_utils:apply(((object.r ? "r" | "") + (object.w ? "w" | "")) + (object.f ? "f" | ""), perms);
r = w = f = 0;
for i in [1..length(perms)]
if (perms[i] == "r")
r = 1;
elseif (perms[i] == "w")
w = 1;
elseif (perms[i] == "f")
f = 1;
else
player:notify(tostr("\"", perms, "\" is not a valid permissions string for an object."));
return;
endif
endfor
try
object.r = r;
object.w = w;
object.f = f;
player:notify(tostr("Object permissions set to \"", perms, "\"."));
except (E_PERM)
player:notify("Permission denied.");
endtry
return;
endif
$command_utils:object_match_failed(object, what);
.
#58:2
if (player != caller)
return;
endif
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, talk to a wizard.");
return;
endif
if (!(args && (spec = $code_utils:parse_verbref(args[1]))))
player:notify(tostr(args ? ("\"" + args[1]) + "\"?  " | "", "<object>:<verb>  expected."));
elseif ($command_utils:object_match_failed(object = player:my_match_object(spec[1]), spec[1]))
"...can't find object...";
else
if (verb == "@args#")
name = $code_utils:toint(spec[2]);
if (name == E_TYPE)
return player:notify("Verb number expected.");
elseif ((name < 1) || `name > length(verbs(object)) ! E_PERM => 0')
return player:notify("Verb number out of range.");
endif
endif
try
info = verb_args(object, name = spec[2]);
if (typeof(pas = $code_utils:parse_argspec(@listdelete(args, 1))) != LIST)
"...arg spec is bogus...";
player:notify(tostr(pas));
elseif (!(newargs = pas[1]))
player:notify($string_utils:from_list(info, " "));
elseif (pas[2])
player:notify(tostr("\"", pas[2][1], "\" unexpected."));
else
info[2] = info[2][1..index(info[2] + "/", "/") - 1];
info = {@newargs, @info[length(newargs) + 1..$]};
try
result = set_verb_args(object, name, info);
player:notify("Verb arguments changed.");
except (E_INVARG)
player:notify(tostr("\"", info[2], "\" is not a valid preposition (?)"));
except error (ANY)
player:notify(error[2]);
endtry
endif
except (E_VERBNF)
player:notify("That object does not have a verb with that name.");
except error (ANY)
player:notify(error[2]);
endtry
endif
.
#58:3
"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("I don't understand that.");
return;
elseif (!player.programmer)
player:tell("You need to be a programmer to eval code.");
return;
endif
set_task_perms(player);
result = player:eval_cmd_string(argstr, verb != "eval-d");
if (result[1])
player:notify(this:eval_value_to_string(result[2]));
if (player:prog_option("eval_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
.
#58:4
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
try
result = delete_property(object, pname);
player:notify("Property removed.");
except (E_PROPNF)
player:notify("That object does not define that property.");
except res (ANY)
player:notify(res[2]);
endtry
.
#58:5
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, talk to a wizard.");
return;
elseif (!$quota_utils:verb_addition_permitted(player))
player:tell("Verb addition not permitted because quota exceeded.");
return;
endif
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;
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 = n[1..i - 1] + n[i + 1..$];
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] || (player:prog_option("verb_args") || {});
verbargs = {@verbargs, "none", "none", "none"}[1..3];
rest = pas[2];
if (verbargs == {"this", "none", "this"})
perms = "rxd";
else
perms = "rd";
endif
if (rest)
perms = $perm_utils:apply(perms, rest[1]);
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 = $string_utils:match_player(rest[2]), rest[2])[1])
return;
elseif (owner == $nothing)
player:notify("Verb can't be owned by no one!");
return;
endif
try
x = add_verb(object, {owner, perms, name}, verbargs);
player:notify(tostr("Verb added (", length(verbs(object)), ")."));
except (E_INVARG)
player:notify(tostr(rest ? tostr("\"", perms, "\" is not a valid set of permissions.") | tostr("\"", verbargs[2], "\" is not a valid preposition (?)")));
except e (ANY)
player:notify(e[2]);
endtry
.
#58:6
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:toint(verbname);
if (loc == E_TYPE)
return player:notify("Verb number expected.");
elseif ((loc < 1) || (loc > `length(verbs(object)) ! E_PERM => 0'))
return player:notify("Verb number out of range.");
endif
else
if (index(verbname, "*") > 1)
verbname = strsub(verbname, "*", "");
endif
loc = $code_utils:find_last_verb_named(object, verbname);
if (argspec)
argspec[2] = $code_utils:full_prep(argspec[2]) || argspec[2];
while (loc && (`verb_args(object, loc) ! ANY' != argspec))
loc = $code_utils:find_last_verb_named(object, verbname, loc - 1);
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) ! ANY';
vargs = `verb_args(object, loc) ! ANY';
try
delete_verb(object, loc);
if (info)
player:notify(tostr("Verb ", object, ":", info[3], " (", loc, ") {", $string_utils:from_list(vargs, " "), "} removed."));
else
player:notify(tostr("Unreadable verb ", object, ":", loc, " removed."));
endif
except e (ANY)
player:notify(e[2]);
endtry
endif
.
#58:7
"Syntax:  @forked [player]";
"         @forked all wizards";
"";
"For a normal player, shows all the tasks you have waiting in your queue, especially those forked or suspended. A wizard will see all the tasks of all the players unless the optional argument is provided.";
"The second form is only usable by wizards and provides an output of all tasks owned by characters who are .wizard=1. Useful to find a task that may get put in a random queue due to $wiz_utils:random_wizard. Or even finding verbs that run with wizard permissions that shouldn't be.";
set_task_perms(player);
if (!dobjstr)
tasks = queued_tasks();
elseif ((dobjstr == "all wizards") && player.wizard)
tasks = {};
for t in (queued_tasks())
if (t[5].wizard)
tasks = {@tasks, t};
endif
$command_utils:suspend_if_needed(1);
endfor
elseif ($command_utils:player_match_result(dobj = $string_utils: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)
$command_utils:suspend_if_needed(0);
{q_id, start, nu, nu2, owner, vloc, vname, lineno, this} = task;
time = (start >= now) ? ctime(start)[5..24] | su:left((start == -1) ? "Reading input ..." | tostr(now - start, " seconds ago..."), 20);
owner_name = valid(owner) ? owner.name | tostr("Dead ", owner);
player:notify(tostr(su:left(tostr(q_id), 10), "  ", time, "  ", su:left(owner_name, 12), "  ", vloc, ":", vname, " (", lineno, ")", (this != vloc) ? tostr(" [", this, "]") | ""));
if (index(vname, "suspend") && (vloc == $command_utils))
"Find out the first line of the callers() list from task_stack()";
{sthis, svname, sprogger, svloc, splayer, slineno} = task_stack(q_id, 1)[2];
player:notify(tostr("                    Called By...  ", su:left(valid(sprogger) ? sprogger.name | tostr("Dead ", sprogger), 12), "  ", svloc, ":", svname, (sthis != svloc) ? tostr(" [", sthis, "]") | "", " (", slineno, ")"));
endif
endfor
player:notify("-----------------------------------------------------------------");
else
player:notify("No tasks.");
endif
.
#58:8
"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.";
"   The @killquiet alias kills tasks without the pretty printout if more than one task is being killed.";
set_task_perms(player);
quiet = index(verb, "q");
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 = toint(args[1]))
elseif (all = args[1] == "all")
everyone = 0;
realplayer = player;
if (player.wizard && (length(args) > 1))
realplayer = $string_utils: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 = toint(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..$]);
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 = toint(args[1][2..l]);
percent = toint("1" + "0000000000"[1..l - 1]);
elseif (colon = index(argstr, ":"))
whatstr = argstr[1..colon - 1];
vrb = argstr[colon + 1..$];
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)
try
kill_task(taskid);
player:notify(tostr("Killed task ", taskid, "."));
killed = 1;
except error (ANY)
player:notify(tostr("Can't kill task ", taskid, ": ", error[2]));
endtry
elseif (all)
for task in (queued_tasks)
if (everyone || (realplayer == task[5]))
`kill_task(task[1]) ! ANY';
killed = killed + 1;
if (!quiet)
this:_kill_task_message(task);
endif
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]) ! ANY';
killed = killed + 1;
if (!quiet)
this:_kill_task_message(task);
endif
endif
endfor
elseif (percent)
for task in (queued_tasks)
if (digits == (task[1] % percent))
`kill_task(task[1]) ! ANY';
killed = killed + 1;
if (!quiet)
this:_kill_task_message(task);
endif
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)))
`kill_task(task[1]) ! ANY';
killed = killed + 1;
if (!quiet)
this:_kill_task_message(task);
endif
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.");
elseif (quiet)
player:notify(tostr("Killed ", killed, " tasks."));
endif
.
#58:9
"Usage:  @copy 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.";
"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.";
"  if @copy-x is used, makes an unusable copy (!x, this none this).  If @copy-move is used, deletes the source verb as well.";
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, talk to a wizard.");
return;
elseif ((verb != "@copy-move") && (!$quota_utils:verb_addition_permitted(player)))
player:notify("Verb addition not permitted because quota exceeded.");
return;
elseif ((!(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;
elseif ($command_utils:object_match_failed(fobj = player:my_match_object(from[1]), from[1]))
return;
elseif (iobjstr[1] == ":")
to = {fobj, iobjstr[2..$]};
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;
if (verb == "@copy-move")
if ((!$perm_utils:controls(player, fobj)) && (!$quota_utils:verb_addition_permitted(player)))
player:notify("Won't be able to delete old verb.  Quota exceeded, so unable to continue.  Aborted.");
return;
elseif ($perm_utils:controls(player, fobj))
"only try to move if the player controls the verb. Otherwise, skip and treat as regular @copy";
if (typeof(result = $code_utils:move_verb(@from, @to)) == ERR)
player:notify(tostr("Unable to move verb from ", from[1], ":", from[2], " to ", to[1], ":", to[2], " --> ", result));
else
player:notify(tostr("Moved verb from ", from[1], ":", from[2], " to ", result[1], ":", result[2]));
endif
return;
else
player:notify("Won't be able to delete old verb.  Treating this as regular @copy.");
endif
endif
to_firstname = strsub(to[2][1..index(to[2] + " ", " ") - 1], "*", "") || "*";
if ((!(hv = $object_utils:has_verb(to[1], to_firstname))) || (hv[1] != to[1]))
if ((!(info = `verb_info(@from) ! ANY')) || (!(vargs = `verb_args(@from) ! ANY')))
player:notify(tostr("Retrieving ", from[1], ":", from[2], " --> ", info && vargs));
return;
endif
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
if (ERR == typeof(e = `add_verb(to[1], info, vargs) ! ANY'))
player:notify(tostr("Adding ", to[1], ":", to[2], " --> ", e));
return;
endif
endif
code = `verb_code(@from) ! ANY';
owner = `verb_info(@from)[1] ! ANY';
if (owner != player)
code = {tostr("\"Copied from ", from[1].name, " (", from[1], "):", from[2], (from[1] == owner) ? " " | tostr(" by ", owner.name, " (", owner, ") "), ctime(), "\";"), @code};
if (!player:prog_option("copy_expert"))
player:notify("Use of @copy is discouraged.  Please do not use @copy if you can use inheritance or features instead.  Use @copy carefully, and only when absolutely necessary, as it is wasteful of database space.");
endif
endif
if (ERR == typeof(e = `set_verb_code(to[1], to_firstname, code) ! ANY'))
player:notify(tostr("Copying ", from[1], ":", from[2], " to ", to[1], ":", to[2], " --> ", e));
else
player:notify(tostr(to[1], ":", to[2], " code set."));
endif
.
#58:10
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]) | ""));
.
#58:11
"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 (typeof(argspec = $code_utils:parse_argspec(@listdelete(args, 1))) != LIST)
player:notify(tostr(argspec));
elseif (verb == "@program#")
verbname = $code_utils:toint(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')
player:notify("Verb number out of range.");
else
argspec = 0;
punt = 0;
endif
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)
if (!(argspec[2] in {"none", "any"}))
argspec[2] = $code_utils:full_prep(argspec[2]);
endif
loc = $code_utils:find_verb_named(object, verbname);
while ((loc > 0) && (`verb_args(object, loc) ! ANY' != argspec))
loc = $code_utils:find_verb_named(object, verbname, loc + 1);
endwhile
if (!loc)
punt = "...can't find it....";
player:notify("That object has no verb matching that name + args.");
else
verbname = loc;
endif
else
loc = 0;
endif
"...";
"...get verb info...";
"...";
if (punt || (!(punt = "...reset punt to TRUE...")))
else
try
info = verb_info(object, verbname);
punt = 0;
aliases = info[3];
if (!loc)
loc = aliases in (verbs(object) || {});
endif
except (E_VERBNF)
player:notify(">>That object does not have that verb definition<<");
except error (ANY)
player:notify(error[2]);
endtry
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, ")."));
lines = $command_utils:read_lines();
try
if (result = set_verb_code(object, verbname, lines))
player:notify_lines(result);
player:notify(tostr(length(result), " error(s)."));
player:notify(">> Verb not programmed <<");
else
player:notify("0 errors.");
player:notify("Verb programmed.");
if ($code_utils:update_last_modified(object, verbname))
player:notify("** Time-stamping failed.");
endif
endif
except error (ANY)
player:notify(error[2]);
player:notify("Verb not programmed.");
endtry
endif
"Last modified Sat Apr 14 17:18:03 2001 CDT by Wizard (#2).";
.
#58:12
"Usage: @setenv <environment string>";
"Set your .eval_env property.";
set_task_perms(player);
if (!argstr)
player:notify(tostr("Usage:  ", verb, " <environment string>"));
return;
endif
player:notify(tostr("Current eval environment is: ", player.eval_env));
result = player:set_eval_env(argstr);
if (typeof(result) == ERR)
player:notify(tostr(result));
return;
endif
player:notify(tostr(".eval_env set to \"", player.eval_env, "\" (", player.eval_ticks, " ticks)."));
.
#58:13
"Usage: @prospectus <player> [from <start>] [to <end>]";
set_task_perms((caller_perms() == $nothing) ? player | caller_perms());
dobj = dobjstr ? $string_utils:match_player(dobjstr) | player;
if ($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..$];
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);
.
#58:14
"@display <object>[.[property]]*[,[inherited_property]]*[:[verb]]*[;[inherited_verb]]*";
if (player != this)
player:notify(tostr("Sorry, you can't use ", this:title(), "'s `", verb, "' command."));
return E_PERM;
endif
"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("thisonly"))
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) ! ANY => {}')
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("thisonly"))
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
if ($quota_utils.byte_based && $object_utils:has_property(it, "object_size"))
this:notify(tostr("  Size: ", $string_utils:group_number(it.object_size[1]), " bytes at ", this:ctime(it.object_size[2])));
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 = b[1];
q = b[2];
short = (typeof(q) == INT) ? q | strsub(y = index(q, " ") ? q[1..y - 1] | q, "*", "");
inf = `verb_info(where, short) ! ANY';
if ((typeof(inf) == LIST) || (inf == E_PERM))
name = (typeof(inf) == LIST) ? index(inf[3], " ") ? ("\"" + inf[3]) + "\"" | inf[3] | q;
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) ! ANY';
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 | (this:linelen() - 56);
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) ! ANY';
if (inf == E_PROPNF)
if (q in $code_utils.builtin_props)
this:notify(tostr($string_utils:left("," + q, 25), "Built in property            ", $string_utils:abbreviated_value(it.(q), max, depth)));
else
this:notify(tostr("  ** property not found, \"", q, "\" **"));
endif
else
pname = $string_utils:left(tostr((q in `properties(it) ! ANY => {}') ? "." | (`is_clear_property(it, q) ! ANY' ? " " | ","), 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" ! E_RANGE => 1' && (inf[2][1..0] = " ");
`inf[2][2] != "w" ! E_RANGE => 1' && (inf[2][2..1] = " ");
this:notify($string_utils:left(tostr($string_utils:left(tostr(pname, oname, " (", inf[1], ") "), 47), inf[2], " "), 54) + $string_utils:abbreviated_value(it.(q), max, depth));
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
.
#58:15
set_task_perms(player);
"Let 'em @kill it.";
count = 0;
for i in [#0..max_object()]
if ($command_utils:running_out_of_time())
player:notify(tostr("Counting... [", count, "/", i, "]"));
suspend(0);
endif
if (valid(i))
count = count + 1;
endif
endfor
player:notify(tostr("There are ", count, " valid objects out of ", toint(max_object()) + 1, " allocated object numbers."));
.
#58:16
"@gethelp [<topic>] [from <db or dblist>]";
"  Prints the raw text of topic from the appropriate help db.";
"  With no argument, gets the blank (\"\") topic from wherever it lives";
"  Text is printed as a script for changing this help topic ";
"  (somewhat like @dump...)";
if (!prepstr)
topic = argstr;
dblist = $code_utils:help_db_list();
elseif (prepstr != "from")
player:notify("Usage:  ", verb, " [<topic>] [from <db>]");
return;
elseif (!(e = $no_one:eval_d(iobjstr = argstr[$string_utils:word_start(argstr)[(prepstr in args) + 1][1]..$])))
player:notify(tostr(e));
return;
elseif (!e[1])
player:notify_lines(e[2]);
return;
elseif (!(typeof(dblist = e[2]) in {OBJ, LIST}))
player:notify(tostr(iobjstr, " => ", dblist, " -- not an object or a list"));
return;
else
topic = dobjstr;
if (typeof(dblist) == OBJ)
dblist = {dblist};
endif
endif
search = $code_utils:help_db_search(topic, dblist);
if (!search)
player:notify("Topic not found.");
elseif (search[1] == $ambiguous_match)
player:notify(tostr("Topic `", topic, "' ambiguous:  ", $string_utils:english_list(search[2], "none", " or ")));
elseif (typeof(text = (db = search[1]):dump_topic(fulltopic = search[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
.
#58:17
set_task_perms(player);
if (prepstr == "in")
pattern = dobjstr;
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:  ", toliteral(objlist[2])));
return;
else
objlist = objlist[2..2];
endif
elseif ((prepstr == "from") && (player.wizard && (n = toint(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 ? {prepstr, " ", iobjstr, " "} | {}, (verb == "@egrep") ? "matching the pattern " | "containing the string ", toliteral(pattern), " ..."));
player:notify("");
$code_utils:((verb == "@egrep") ? "find_verbs_matching" | "find_verbs_containing")(pattern, @objlist);
.
#58:18
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..$]) 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);
.
#58:19
"@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
.
#58:20
"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 (is_player(this) && $perm_utils:controls(caller_perms(), this))
program = args[1];
value = $no_one:eval_d((";ticks = ticks_left();" + program) + ";return ticks - ticks_left() - 2;");
if (!value[1])
return E_INVARG;
elseif (typeof(value[2]) == ERR)
return value[2];
endif
try
ok = this.eval_env = program;
this.eval_ticks = value[2];
return 1;
except error (ANY)
return error[1];
endtry
endif
.
#58:21
"@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...";
endif
try
if (is_clear_property(dobj, prop = l[2]))
player:notify(tostr("Property ", dobj, ".", prop, " is already clear!"));
return;
endif
clear_property(dobj, prop);
player:notify(tostr("Property ", dobj, ".", prop, " cleared; value is now ", toliteral(dobj.(prop)), "."));
except (E_INVARG)
player:notify(tostr("You can't clear ", dobj, ".", prop, "; none of the ancestors define that property."));
except error (ANY)
player:notify(error[2]);
endtry
.
#58:22
"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
.
#58:23
":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, ?debug = 1} = args;
program = program + ";";
debug = debug ? 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;
seconds = seconds_left();
value = debug ? eval(program) | $code_utils:eval_d(program);
seconds = seconds - seconds_left();
ticks = ticks - ticks_left();
return {@value, ticks, seconds};
.
#58:24
"@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..$];
elseif (o in {"noprops", "noverbs"})
options = setremove(options, o[3..$]);
elseif (o in {"create"})
create = 1;
else
player:notify(tostr("`", o, "' not understood as valid option."));
player:notify(tostr("Usage:  ", verb, " something [with [id=...] [noprops] [noverbs] [create]]"));
return;
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) ! ANY => {}' | {})
pquoted = toliteral(p);
try
info = property_info(dobj, p);
value = dobj.(p);
except error (ANY)
player:notify(tostr("\"", targname, ".(", pquoted, ") => ", toliteral(error[1]), " (", error[2], ")"));
continue p;
endtry
if (create)
uvalue = (typeof(value) == LIST) ? "{}" | 0;
player:notify(tostr("@prop ", targname, ".", pquoted, " ", uvalue || toliteral(value), " ", info[2] || "\"\"", (info[1] == dobj.owner) ? "" | tostr(" ", info[1])));
if (uvalue && value)
player:notify(tostr(";;", targname, ".(", pquoted, ") = ", toliteral(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, ") = ", toliteral(value)));
endif
$command_utils:suspend_if_needed(0);
endfor
for a in (("props" in options) ? $object_utils:ancestors(dobj) | {})
for p in (`properties(a) ! ANY => {}')
$command_utils:suspend_if_needed(1);
pquoted = toliteral(p);
try
value = dobj.(p);
except error (ANY)
player:notify(tostr("\"", targname, ".(", pquoted, ") => ", toliteral(error[1]), " (", error[2], ")"));
continue p;
endtry
avalue = `a.(p) ! ANY';
if ((typeof(avalue) == ERR) || (value != avalue))
player:notify(tostr(";;", targname, ".(", pquoted, ") = ", toliteral(value)));
endif
endfor
$command_utils:suspend_if_needed(1);
endfor
if (!("verbs" in options))
player:notify("\"***finished***");
return;
endif
player:notify("");
v = 1;
while ((info = `verb_info(dobj, v) ! ANY') || (info == E_PERM))
if (`index(info[3], "(old)") ! ANY' && 0)
"Thought about skipping (old) verbs...";
player:tell("Skipping ", dobj, ":\"", info[3], "\"...");
else
suspend(1);
if (typeof(info) == ERR)
player:notify(tostr("\"", dobj, ":", v, " --- ", 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, v);
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, v, 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
if (`index(tostr(" ", info[3], " "), " * ") ! ANY')
"... 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 = v + 1;
endif
$command_utils:suspend_if_needed(0);
endwhile
player:notify("\"***finished***");
.
#58:25
"Copied from Player Class hacked with eval that does substitutions and assorted stuff (#8855):# by Geust (#24442) Sun May  9 20:19:05 1993 PDT";
"#<string>[.<property>|.parent] [exit|player|inventory] [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";
"  #JoeFeedback.parent player for toint(%#)";
"will return 26026 (unless Joe has chparented since writing this).";
set_task_perms(player);
if (!(whatstr = verb[2..dot = min(index(verb + ".", "."), index(verb + ":", ":")) - 1]))
player:notify("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 = $string_utils:match_player(whatstr);
if ($command_utils:player_match_failed(what, whatstr))
return;
endif
else
what = player:my_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 (index(verb, ".parent") == (dot + 1))
what = parent(what);
dot = dot + 7;
endwhile
if (dot >= length(verb))
val = what;
elseif ((value = $code_utils:eval_d(tostr("return ", what, verb[dot + 1..$], ";")))[1])
val = value[2];
else
player:notify_lines(value[2]);
return;
endif
if (prepstr)
program = strsub(iobjstr + ";", "%#", toliteral(val));
end = 1;
"while (\"A\" <= (l = argstr[end]) && l <= \"Z\")";
while (("A" <= (l = program[end])) && (l <= "Z"))
end = end + 1;
endwhile
if ((program[1] == ";") || (program[1..end - 1] in {"if", "for", "fork", "return", "while", "try"}))
program = $code_utils:substitute(program, this.eval_subs);
else
program = $code_utils:substitute("return " + program, this.eval_subs);
endif
if ((value = eval(program))[1])
player:notify(this:eval_value_to_string(value[2]));
else
player:notify_lines(value[2]);
nerrors = length(value[2]);
player:notify(tostr(nerrors, " error", (nerrors == 1) ? "." | "s."));
endif
else
player:notify(this:eval_value_to_string(val));
endif
.
#58:26
set_task_perms(caller_perms());
if (typeof(val = args[1]) == OBJ)
return 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)
return tostr("=> ", toliteral(val), "  (", val, ")");
else
return tostr("=> ", toliteral(val));
endif
.
#58:27
"@<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 = "prog";
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
.
#58:28
":prog_option(name)";
"Returns the value of the specified prog option";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return $prog_options:get(this.prog_options, args[1]);
else
return E_PERM;
endif
.
#58:29
":set_prog_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
.
#58:30
"@list <obj>:<verb> [<dobj> <prep> <iobj>] [with[out] paren|num] [all] [ranges]";
set_task_perms(player);
bynumber = verb == "@list#";
pflag = player:prog_option("list_all_parens");
nflag = !player:prog_option("list_no_numbers");
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];
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>:<verbnumber> [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;
for what in ({object, @$object_utils:ancestors(object)})
if (bynumber)
vname = $code_utils:toint(spec[2]);
if (vname == E_TYPE)
return player:notify("Verb number expected.");
elseif ((vname < 1) || `vname > length(verbs(what)) ! E_PERM => 0')
return player:notify("Verb number out of range.");
endif
code = `verb_code(what, vname, pflag) ! ANY';
elseif (argspec)
vnum = $code_utils:find_verb_named(what, spec[2]);
while (vnum && (`verb_args(what, vnum) ! ANY' != argspec))
vnum = $code_utils:find_verb_named(what, spec[2], vnum + 1);
endwhile
vname = vnum;
code = (!vnum) ? E_VERBNF | `verb_code(what, vnum, pflag) ! ANY';
else
vname = spec[2];
code = `verb_code(what, vname, pflag) ! ANY';
endif
if (code != E_VERBNF)
if (shown_one)
player:notify("");
elseif (what != object)
player:notify(tostr("Object ", object, " does not define that verb", argspec ? " with those args" | "", ", but its ancestor ", what, " does."));
endif
if (typeof(code) == ERR)
player:notify(tostr(what, ":", vname, " -- ", code));
else
info = verb_info(what, vname);
vargs = verb_args(what, vname);
fullname = info[3];
if (index(fullname, " "))
fullname = toliteral(fullname);
endif
if (index(vargs[2], "/"))
vargs[2] = tostr("(", vargs[2], ")");
endif
player:notify(tostr(what, ":", fullname, "   ", $string_utils:from_list(vargs, " ")));
if (code == {})
player:notify("(That verb has not been programmed.)");
else
lineseq = {1, length(code) + 1};
range && (lineseq = $seq_utils:intersection(range, lineseq));
if (!lineseq)
player:notify("(No lines in that range.)");
endif
for k in [1..length(lineseq) / 2]
for i in [lineseq[(2 * k) - 1]..lineseq[2 * k] - 1]
if (nflag)
player:notify(tostr(" "[1..i < 10], i, ":  ", code[i]));
else
player:notify(code[i]);
endif
$command_utils:suspend_if_needed(0);
endfor
endfor
endif
endif
shown_one = 1;
endif
if (shown_one && (!aflag))
return;
endif
endfor
if (!shown_one)
player:notify(tostr("That object does not define that verb", argspec ? " with those args." | "."));
endif
.
#58:31
"Copied from Player Class hacked with eval that does substitutions and assorted stuff (#8855):set_eval_subs by Geust (#24442) Fri Aug  5 13:18:59 1994 PDT";
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
elseif (typeof(subs = args[1]) != LIST)
return E_TYPE;
else
for pair in (subs)
if (((length(pair) != 2) || typeof(pair[1] != STR)) || typeof(pair[2] != STR))
return E_INVARG;
endif
endfor
endif
return `this.eval_subs = subs ! ANY';
.
#58:32
set_task_perms(player);
if (!dobjstr)
try
if ((verb[7] != "(") && (verb[$] != ")"))
player:tell("Usage:  @verbs <object>");
return;
else
dobjstr = verb[8..$ - 1];
endif
except (E_RANGE)
return player:tell("Usage:  @verbs <object>");
endtry
endif
thing = player:my_match_object(dobjstr);
if (!$command_utils:object_match_failed(thing, dobjstr))
verbs = {};
i = 1;
while ((info = `verb_info(thing, i) ! ANY') != E_VERBNF)
verbs = {@verbs, info ? info[3] | E_PERM};
i = i + 1;
endwhile
player:tell(";verbs(", thing, ") => ", toliteral(verbs));
endif
.
#58:33
"Syntax:  @forked-v*erbose [player]";
"         @forked-v*erbose all wizards";
"";
"For a normal player, shows all the tasks you have waiting in your queue, especially those forked or suspended. A wizard will see all the tasks of all the players unless the optional argument is provided. For a task which has suspended, and not a fresh fork, shows the full callers() stack.";
"The second form is only usable by wizards and provides an output of all tasks owned by characters who are .wizard=1. Useful to find a task that may get put in a random queue due to $wiz_utils:random_wizard. Or even finding verbs that run with wizard permissions that shouldn't be.";
set_task_perms(player);
if (!dobjstr)
tasks = queued_tasks();
elseif ((dobjstr == "all wizards") && player.wizard)
tasks = {};
for t in (queued_tasks())
if (t[5].wizard)
tasks = {@tasks, t};
endif
$command_utils:suspend_if_needed(1);
endfor
elseif ($command_utils:player_match_result(dobj = $string_utils: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)
$command_utils:suspend_if_needed(0);
{q_id, start, nu, nu2, owner, vloc, vname, lineno, this} = task;
time = (start >= now) ? ctime(start)[5..24] | su:left((start == -1) ? "Reading input ..." | tostr(now - start, " seconds ago..."), 20);
owner_name = valid(owner) ? owner.name | tostr("Dead ", owner);
player:notify(tostr(su:left(tostr(q_id), 10), "  ", time, "  ", su:left(owner_name, 12), "  ", vloc, ":", vname, " (", lineno, ")", (this != vloc) ? tostr(" [", this, "]") | ""));
if (stack = `task_stack(q_id, 1) ! E_INVARG => 0')
for frame in (listdelete(stack, 1))
{sthis, svname, sprogger, svloc, splayer, slineno} = frame;
player:notify(tostr("                    Called By...  ", su:left(valid(sprogger) ? sprogger.name | tostr("Dead ", sprogger), 12), "  ", svloc, ":", svname, (sthis != svloc) ? tostr(" [", sthis, "]") | "", " (", slineno, ")"));
endfor
endif
endfor
player:notify("-----------------------------------------------------------------");
else
player:notify("No tasks.");
endif
.
#58:34
if (!args)
return player:tell("Usage: @code object:verb");
elseif (!(spec = $code_utils:parse_verbref(args[1])))
player:tell("Usage:  @code <object>:<verb>");
return E_INVARG;
endif
object = player:my_match_object(spec[1]);
if ($command_utils:object_match_failed(object, spec[1]))
return;
endif
what = object;
vname = spec[2];
while ((what != $nothing) && ((code = verb_code(what, vname)) == E_VERBNF))
what = parent(what);
endwhile
info = verb_info(what, vname);
if (code == E_VERBNF)
player:tell("That object does not define that verb.");
elseif (typeof(code) == ERR)
player:tell(code);
elseif (code == {})
player:tell("That verb has not been programmed.");
else
if (what != object)
player:tell("Object ", object, " does not define that verb, but its ancestor ", what, " does.");
endif
info = verb_info(what, vname);
vargs = verb_args(what, vname);
if (index(vargs[2], "/"))
vargs = listset(vargs, $code_utils:short_prep(vargs[2]), 2);
endif
if (spec[1][1] == "$")
vrb = spec[1];
else
vrb = what;
endif
player:tell(tostr("@verb ", vrb, ":", $string_utils:print(info[3]), " ", $string_utils:from_list(vargs, " "), " ", info[2]));
player:tell(tostr("@program ", vrb, ":", vname));
player:tell_lines(code);
player:tell(".");
endif
"Last modified Thu Apr  8 12:31:53 1999 CDT by Wizard (#2).";
.
#58:35
ob = this:my_match_object(argstr);
if (!$command_utils:object_match_failed(ob, argstr))
p = properties(ob);
set_task_perms(player);
np = {};
for prop in (p)
np = {@np, (typeof(ob.(prop)) != ERR) ? prop | E_PERM};
endfor
this:notify(tostr(";properties(", ob, ") => ", $string_utils:print(np)));
endif
"Last modified Tue Oct 14 14:14:08 1997 CDT by Wizard (#2).";
.
#58:36
"Copyright (C) 1998, Rui Miguel Barbosa Pinto";
"@generic-kids <object> - Returns a list of the descendants of that object";
"                        that are fertile (Generics)";
set_task_perms(player);
if ((!args) || (!valid(object = player:my_match_object(args[1]))))
player:tell("Usage: @generic-kids <object>");
return;
endif
lista = {};
for x in ($object_utils:descendants(object))
$command_utils:suspend_if_needed(0);
x.f && (lista = setadd(lista, x));
endfor
if (!lista)
player:tell("No Generic on the kids of ", object.name);
return;
endif
player:tell("Generic kids of ", $string_utils:nn(object), ":", $string_utils:nn_list(lista));
"Last modified Wed Apr 15 12:56:30 1998 CDT by Wizard (#2).";
.
#58:37
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Generates a dynamic web menu line based on which player class the user belongs to";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{menu, functions, preload, base_url} = pass(@args);
external_baseurl = (($xpress_client.external_baseurl + $xpress_client.themes_folder) + this.xpress_theme) + "/";
program = {tostr("<A HREF=\"", base_url, "Xpress_Program_Editor/main.html\" TARGET=\"", $Xpress_Program_Editor:get_frame_name("openProgramEditor"), "\" onClick=\"openProgramEditor(); swapImage('program','", external_baseurl, "program1.jpg')\" onMouseOver=\"swapImage('program','", external_baseurl, "program2.jpg')\" onMouseOut=\"swapImage('program','", external_baseurl, "program1.jpg')\"><IMG SRC=\"", external_baseurl, "program1.jpg\" BORDER=0 ALIGN=bottom name=program ALT=\"Edit Verbs and Properties\" TITLE=\"Edit Verbs and Properties\"></A>")};
preload = {@preload, tostr("   program2 = new Image(); program2.src = \"", external_baseurl, "program2.jpg\";")};
Window = $encore_web_utils:javascript_window_open($Xpress_Program_Editor, "openProgramEditor", "");
menu = $list_utils:append(menu, program);
functions = $list_utils:append(functions, Window);
return {menu, functions, preload, base_url};
"Last modified Fri Apr 13 19:05:56 2001 CDT by Wizard (#2).";
.
#59: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,\"eval_d_util\",{\"\\\"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, "eval_d_util", code))
lines = {};
for line in (svc)
if ((index(line, "Line ") == 1) && (n = toint(line[6..(colon = index(line + ":", ":")) - 1])))
lines = {@lines, tostr("Line ", n - 5, line[colon..$])};
else
lines = {@lines, line};
endif
endfor
return {0, lines};
else
set_task_perms(caller_perms());
return {1, this:eval_d_util()};
endif
.
#59:1
":toint(STR)";
"=> toint(s) if STR is numeric";
"=> E_TYPE if it isn't";
return match(s = args[1], "^ *[-+]?[0-9]+ *$") ? toint(s) | E_TYPE;
.
#59:2
":toobj(objectid as string) => objectid";
return match(s = args[1], "^ *#[-+]?[0-9]+ *$") ? toobj(s) | E_TYPE;
.
#59:3
"toerr(n), toerr(\"E_FOO\"), toerr(\"FOO\") => E_FOO.";
if (typeof(s = args[1]) != STR)
n = toint(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];
.
#59:4
"error_name(E_FOO) => \"E_FOO\"";
return toliteral(@args);
return this.error_names[toint(args[1]) + 1];
.
#59:5
set_task_perms(caller_perms());
{object, ?what = {"props", "verbs"}} = args;
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) && (vs = verbs(object)))
player:notify("Verb definitions:");
for v in (vs)
$command_utils:suspend_if_needed(0);
player:notify(tostr("    ", v));
endfor
endif
if ("props" in what)
if (ps = properties(object))
player:notify("Property definitions:");
for p in (ps)
$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);
strng = `toliteral(object.(p)) ! E_PERM => "(Permission denied.)"';
player:notify(tostr("    ", p, ": ", strng));
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
.
#59:6
set_task_perms(caller_perms());
{object, pname} = args;
if (pname in this.builtin_props)
player:notify(tostr(object, ".", pname));
player:notify("Built-in property.");
else
try
{owner, perms} = property_info(object, pname);
except error (ANY)
player:notify(error[2]);
return;
endtry
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))));
.
#59:7
set_task_perms(caller_perms());
{object, vname} = args;
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 error (ANY)
player:notify(error[2]);
return;
endtry
arg_specs = 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:    ", arg_specs[1]));
player:notify(tostr("Preposition:      ", arg_specs[2]));
player:notify(tostr("Indirect Object:  ", arg_specs[3]));
.
#59:8
if (args[4..5] == {"none", "this"})
return 0;
endif
{thisobj, verb, adobj, aprep, aiobj} = args;
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 | "");
.
#59:9
"returns the permissions of the current verb (either the owner or the result of the most recent set_task_perms()).";
return caller_perms();
.
#59:10
"returns the object where the current verb is defined.";
return callers()[1][4];
.
#59:11
":verb_documentation([object,verbname]) => documentation at beginning of verb code, if any";
"default is the calling verb";
set_task_perms(caller_perms());
c = callers()[1];
{?object = c[4], ?vname = c[2]} = args;
try
code = verb_code(object, vname);
except error (ANY)
return error[2];
endtry
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("; return " + line)[2]};
else
return doc;
endif
endfor
return doc;
.
#59:12
":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...";
set_task_perms(caller_perms());
{object, vname, text} = args;
if (typeof(code = `verb_code(object, vname) ! ANY') == ERR)
return code;
elseif (typeof(vd = $code_utils:verb_documentation(object, vname)) == ERR)
return vd;
elseif (!(typeof(text) in {LIST, STR}))
return E_INVARG;
else
newdoc = {};
for l in ((typeof(text) == LIST) ? text | {text})
if (typeof(l) != STR)
return E_INVARG;
endif
newdoc = {@newdoc, $string_utils:print(l) + ";"};
endfor
if (ERR == typeof(svc = `set_verb_code(object, vname, {@newdoc, @code[length(vd) + 1..$]}) ! ANY'))
"... this shouldn't happen.  I'm not setting this code -d just yet...";
return svc;
else
return 1;
endif
endif
.
#59:13
"$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 = index(s, "."))
object = s[1..dot - 1];
prop = s[dot + 1..$];
if ((object == "") || (prop == ""))
return 0;
elseif (object[1] == "$")
object = #0.(object[2..$]);
if (typeof(object) != OBJ)
return 0;
endif
object = tostr(object);
endif
elseif (index(s, "$") == 1)
object = "#0";
prop = s[2..$];
else
return 0;
endif
return {object, prop};
.
#59:14
"$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..$];
if (!(object && verbname))
return 0;
elseif ((object[1] == "$") && 0)
"Why was this check taking place here? -- from jhm";
pname = object[2..$];
if ((!(pname in properties(#0))) || (typeof(object = #0.(pname)) != OBJ))
return 0;
endif
object = tostr(object);
endif
return {object, verbname};
else
return 0;
endif
.
#59:15
":parse_arg_spec(@args)";
"  attempts to parse the given sequence of args into a verb_arg specification";
"returns {verb_args,remaining_args} if successful.";
"  e.g., :parse_arg_spec(\"this\",\"in\",\"front\",\"of\",\"any\",\"foo\"..)";
"           => {{\"this\",\"in front of\",\"any\"},{\"foo\"..}}";
"returns a string error message if parsing fails.";
nargs = length(args);
if (nargs < 1)
return {{}, {}};
elseif ((ds = args[1]) == "tnt")
return {{"this", "none", "this"}, listdelete(args, 1)};
elseif (!(ds in {"this", "any", "none"}))
return tostr("\"", ds, "\" is not a valid direct object specifier.");
elseif ((nargs < 2) || (args[2] in {"none", "any"}))
verbargs = args[1..min(3, nargs)];
rest = args[4..nargs];
elseif (!(gp = $code_utils:get_prep(@args[2..nargs]))[1])
return tostr("\"", args[2], "\" is not a valid preposition.");
else
verbargs = {ds, @gp[1..min(2, nargs = length(gp))]};
rest = gp[3..nargs];
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};
.
#59:16
if (server_version() != this._version)
this:_fix_preps();
endif
return this.prepositions;
.
#59:17
":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
.
#59:18
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
.
#59:19
":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)]
accum = (i == 1) ? args[1] | tostr(accum, " ", args[i]);
if (accum in allpreps)
prep = accum;
rest = i + 1;
endif
if (!(accum in this._multi_preps))
return {prep, @args[rest..$]};
endif
endfor
return {prep, @args[rest..$]};
.
#59:20
":_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"}) ! ANY') != 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;
.
#59:21
":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 (1..n-1) to be ignored.";
"  0 is returned if no verb is found.";
"  This routine does not find inherited verbs.";
{object, name, ?start = 1} = args;
for i in [start..length(verbs(object))]
verbinfo = verb_info(object, i);
if (this:verbname_match(verbinfo[3], name))
return i;
endif
endfor
return 0;
.
#59:22
":find_last_verb_named(object,name[,n])";
"  returns the *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.";
"  -1 is returned if no verb is found.";
"  This routine does not find inherited verbs.";
{object, name, ?last = -1} = args;
if (last < 0)
last = length(verbs(object));
endif
for i in [0..last - 1]
verbinfo = verb_info(object, last - i);
if (this:verbname_match(verbinfo[3], name))
return last - i;
endif
endfor
return -1;
.
#59:23
":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.";
"  0 is returned if no verb is found.";
"  This routine does not find inherited verbs.";
{object, name, ?start = 1} = args;
for i in [start..length(verbs(object))]
verbinfo = verb_info(object, i);
if (index(verbinfo[2], "x") && this:verbname_match(verbinfo[3], name))
return i;
endif
endfor
return 0;
.
#59:24
":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..$];
endwhile
endif
return 0;
.
#59:25
"$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, ?where = 0} = args;
count = 0;
if (typeof(where) == INT)
for o in [toobj(where)..max_object()]
if (valid(o))
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, " verbs."));
.
#59:26
":_find_verbs_containing(pattern,object)";
"number of verbs in object with code having a line containing pattern";
"prints verbname and offending line to player";
set_task_perms(caller_perms());
{pattern, o} = args;
count = 0;
try
verbs = verbs(o);
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], " [", 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;
except e (ANY)
player:notify(tostr("verbs(", o, ") => ", e[2]));
endtry
.
#59:27
"$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) == INT)
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, " verbs."));
.
#59:28
":_find_verbs_matching(regexp,object[,casematters])";
"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, ?casematters = 0} = args;
count = 0;
try
verbs = verbs(o);
for vnum in [1..length(verbs)]
if (l = this:_egrep_verb_code(pattern, o, vnum, casematters))
owner = verb_info(o, vnum)[1];
player:notify(tostr(o, ":", verbs[vnum], " [", 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;
except e (ANY)
player:notify(tostr("verbs(", o, ")  => ", e[2]));
endtry
.
#59:29
":_grep_verb_code(pattern,object,verbname) => line number or 0";
"  returns line number on which pattern occurs in code for object:verbname";
set_task_perms(caller_perms());
pattern = args[1];
"The following gross kluge is due to Quade (#82589).  tostr is fast, and so we can check for nonexistence of a pattern very quickly this way rather than checking line by line.  MOO needs a compiler.  --Nosredna";
vc = `verb_code(@listdelete(args, 1)) ! ANY';
if ((typeof(vc) == ERR) || (!index(tostr(@vc), pattern)))
return 0;
else
for line in (vc)
if (index(line, pattern))
return line;
endif
endfor
return 0;
endif
.
#59:30
":_egrep_verb_code(regexp,object,verbname[,casematters]) => 0 or line number";
"  returns line number of first line matching regexp in object:verbname code";
set_task_perms(caller_perms());
{pattern, object, vname, ?casematters = 0} = args;
for line in (vc = `verb_code(object, vname) ! ANY => {}')
try
if (match(line, pattern, casematters))
return line;
endif
except (E_INVARG)
raise(E_INVARG, "Malformed regular expression.");
endtry
endfor
return 0;
.
#59:31
"Parse [from <start>] [to <end>] [for <name>].";
"Takes a series of strings, most likely @args with dobjstr removed.";
"Returns a list {INT start, INT end, STR name}, or {} if there is an error.";
fail = length(args) % 2;
start = 0;
end = toint(max_object());
match = "";
while (args && (!fail))
prep = args[1];
if (prep == "from")
if ((start = player.location:match_object(args[2])) >= #0)
start = toint(start);
else
start = toint(args[2]);
endif
elseif (prep == "to")
if ((end = player.location:match_object(args[2])) >= #0)
end = toint(end);
else
end = toint(args[2]);
endif
elseif (prep == "for")
match = args[2];
else
fail = 1;
endif
args = args[3..length(args)];
endwhile
return fail ? {} | {start, end, match};
.
#59:32
":help_db_list([player]) => list of help dbs";
"in the order that they are consulted by player";
{?who = player} = args;
olist = {who, @$object_utils:ancestors(who)};
if (valid(who.location))
olist = {@olist, who.location, @$object_utils:ancestors(who.location)};
endif
dbs = {};
for o in (olist)
h = `o.help ! ANY => 0';
if (typeof(h) == OBJ)
h = {h};
endif
if (typeof(h) == LIST)
for db in (h)
if ((typeof(db) == OBJ) && (valid(db) && (!(db in dbs))))
dbs = {@dbs, db};
endif
endfor
endif
endfor
return {@dbs, $help};
.
#59:33
":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, dblist} = args;
topics = {};
help = 1;
for db in (dblist)
$command_utils:suspend_if_needed(0);
if ({what} == (ts = `db:find_topics(what) ! ANY => 0'))
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)
return {$ambiguous_match, topics};
elseif (topics)
return {help, topics[1]};
else
return {};
endif
.
#59:34
":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];
"Just in case #0 is !r on some idiot core.";
for p in (`properties(#0) ! ANY => {}')
"And if for some reason, some #0 prop is !r.";
if (`#0.(p) ! ANY' == object)
return "$" + p;
endif
endfor
return tostr(object);
.
#59:35
"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;
.
#59:36
"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, or E_PERM if you don't have permission to read the property.";
set_task_perms(caller_perms());
{object, name, @rest} = args;
return `object:(name)(@rest) ! E_VERBNF, E_INVIND => `object.(name) ! ANY'';
.
#59:37
"task_valid(INT id)";
"Return true iff there is currently a valid task with the given id.";
set_task_perms($no_one);
id = args[1];
return (id == task_id()) || (E_PERM == `kill_task(id) ! ANY');
.
#59:38
":task_owner(INT task_id) => returns the owner of the task belonging to the id.";
if (a = $list_utils:assoc(args[1], queued_tasks()))
return a[5];
else
return E_INVARG;
endif
.
#59:39
":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[1];
argstr = {@args, argstr}[3];
n = length(args = args[2]);
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)) == (n + 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
.
#59:40
":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..$], " ")) - 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..$];
endwhile
endif
return 0;
.
#59:41
"$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, subs} = args;
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[$])))) && ((!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;
.
#59:42
":show_who_listing(players[,more_players])";
" 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.";
{plist, ?more_plist = {}} = args;
idles = itimes = offs = otimes = {};
argstr = dobjstr = iobjstr = prepstr = "";
for p in (more_plist)
if (!valid(p))
caller:notify(tostr(p, " <invalid>"));
elseif (typeof(t = `p.last_disconnect_time ! E_PROPNF') == INT)
if (!(p in offs))
offs = {@offs, p};
otimes = {@otimes, {-t, -t, p}};
endif
elseif (is_player(p))
caller:notify(tostr(p.name, " (", p, ") ", (t == E_PROPNF) ? "is not a $player." | "has a garbled .last_disconnect_time."));
else
caller:notify(tostr(p.name, " (", p, ") is not a player."));
endif
endfor
for p in (plist)
if (p in offs)
elseif (!valid(p))
caller:notify(tostr(p, " <invalid>"));
elseif (typeof(i = `idle_seconds(p) ! ANY') != ERR)
if (!(p in idles))
idles = {@idles, p};
itimes = {@itimes, {i, connected_seconds(p), p}};
endif
elseif (typeof(t = `p.last_disconnect_time ! E_PROPNF') == INT)
offs = {@offs, p};
otimes = {@otimes, {-t, -t, p}};
elseif (is_player(p))
caller:notify(tostr(p.name, " (", p, ") not logged in.", (t == E_PROPNF) ? "  Not a $player." | "  Garbled .last_disconnect_time."));
else
caller:notify(tostr(p.name, " (", p, ") is not a player."));
endif
endfor
if (!(idles || offs))
return 0;
endif
idles = $list_utils:sort_alist(itimes);
offs = $list_utils:sort_alist(otimes);
"...";
"... calculate widths...";
"...";
headers = {"Name", @idles ? {"Connected", "Idle time"} | {"Last disconnect time", ""}, "Location"};
total_width = `caller:linelen() ! ANY => 0' || 79;
max_name = total_width / 4;
name_width = length(headers[1]);
names = locations = {};
for lst in ({@idles, @offs})
$command_utils:suspend_if_needed(0);
p = lst[3];
namestr = tostr(p.name[1..min(max_name, $)], " (", p, ")");
name_width = max(length(namestr), name_width);
names = {@names, namestr};
if (typeof(wlm = `p.location:who_location_msg(p) ! ANY') != STR)
wlm = valid(p.location) ? p.location.name | tostr("** Nowhere ** (", p.location, ")");
endif
locations = {@locations, tostr(wlm, " ", p.doing)};
endfor
time_width = 3 + (offs ? 12 | length("59 minutes"));
before = {0, w1 = 3 + name_width, w2 = w1 + time_width, w2 + time_width};
"...";
"...print headers...";
"...";
su = $string_utils;
tell1 = headers[1];
tell2 = su:space(tell1, "-");
for j in [2..4]
tell1 = su:left(tell1, before[j]) + headers[j];
tell2 = su:left(tell2, before[j]) + su:space(headers[j], "-");
endfor
caller:notify(tell1[1..min($, total_width)]);
caller:notify(tell2[1..min($, total_width)]);
"...";
"...print lines...";
"...";
active = 0;
for i in [1..total = (ilen = length(idles)) + length(offs)]
if (i <= ilen)
lst = idles[i];
if (lst[1] < (5 * 60))
active = active + 1;
endif
l = {names[i], su:from_seconds(lst[2]), su:from_seconds(lst[1]), locations[i]};
else
lct = offs[i - ilen][3].last_connect_time;
ldt = offs[i - ilen][3].last_disconnect_time;
ctime = `caller:ctime(ldt) ! ANY => 0' || ctime(ldt);
l = {names[i], (lct <= time()) ? ctime | "Never", "", locations[i]};
if ((i == (ilen + 1)) && idles)
caller:notify(su:space(before[2]) + "------- Disconnected -------");
endif
endif
tell1 = l[1];
for j in [2..4]
tell1 = su:left(tell1, before[j]) + l[j];
endfor
caller:notify(tell1[1..min($, total_width)]);
if ($command_utils:running_out_of_time())
if ($login:is_lagging())
"Check lag two ways---global lag, but we might still fail due to individual lag of the queue this runs in, so check again later.";
caller:notify(tostr("Plus ", total - i, " other players (", total, " total; out of time and lag is high)."));
return;
endif
now = time();
suspend(0);
if ((time() - now) > 10)
caller:notify(tostr("Plus ", total - i, " other players (", total, " total; out of time and lag is high)."));
return;
endif
endif
endfor
"...";
"...epilogue...";
"...";
caller:notify("");
if (total == 1)
active_str = ", who has" + ((active == 1) ? "" | " not");
else
if (active == total)
active_str = (active == 2) ? "s, both" | "s, all";
elseif (active == 0)
active_str = "s, none";
else
active_str = tostr("s, ", active);
endif
active_str = tostr(active_str, " of whom ha", (active == 1) ? "s" | "ve");
endif
caller:notify(tostr("Total: ", total, " person", active_str, " been active recently."));
return total;
"Last modified Mon Oct 19 10:49:13 1998 CDT by Wizard (#2).";
.
#59:43
":_egrep_verb_code_all(regexp,object,verbname) => list of lines number";
"  returns list of all lines matching regexp in object:verbname code";
set_task_perms(caller_perms());
{pattern, object, vname} = args;
lines = {};
for line in (vc = `verb_code(object, vname, 1, 0) ! ANY => {}')
if (match(line, pattern))
lines = {@lines, line};
endif
endfor
return lines;
.
#59:44
":_grep_verb_code_all(pattern,object,verbname) => list of lines";
"  returns list of lines on which pattern occurs in code for object:verbname";
set_task_perms(caller_perms());
{pattern, object, vname} = args;
lines = {};
for line in (vc = `verb_code(object, vname) ! ANY => {}')
if (index(line, pattern))
lines = {@lines, line};
endif
endfor
return lines;
.
#59:45
":verb_usage([object,verbname]) => usage string at beginning of verb code, if any";
"default is the calling verb";
set_task_perms(caller_perms());
c = callers()[1];
{?object = c[4], ?vname = c[2]} = args;
if (typeof(code = `verb_code(object, vname) ! ANY') == ERR)
return code;
else
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 (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;
endif
.
#59:46
"returns the callers() frame for the current verb.";
return callers()[1];
.
#59:47
"returns {this:verb_frame(), @callers()}.";
return callers();
.
#59:48
":move_verb(OBJ from, STR verb name, OBJ to, [STR new verb name]) -> Moves the specified verb from one object to another. Returns {OBJ, Full verb name} where the verb now resides if successful, error if not. To succeed, caller_perms() must control both objects and own the verb, unless called with wizard perms. Supplying a fourth argument moves the verb to a new name.";
"Should handle verbnames with aliases and wildcards correctly.";
who = caller_perms();
{from, origverb, to, ?destverb = origverb} = args;
if ((((typeof(from) != OBJ) || (typeof(to) != OBJ)) || (typeof(origverb) != STR)) || (typeof(destverb) != STR))
"check this first so we can parse out long verb names next";
return E_TYPE;
endif
origverb_first = strsub(origverb[1..index(origverb + " ", " ") - 1], "*", "") || "*";
destverb_first = strsub(destverb[1..index(destverb + " ", " ") - 1], "*", "") || "*";
if ((!valid(from)) || (!valid(to)))
return E_INVARG;
elseif ((from == to) && (destverb == origverb))
"Moving same origverb onto the same object puts the verbcode in the wrong one. Just not allow";
return E_NACC;
elseif (((!$perm_utils:controls(who, from)) && (!from.w)) || ((!$perm_utils:controls(who, to)) && (!to.w)))
"caller_perms() is not allowed to hack on either object in question";
return E_PERM;
elseif (!$object_utils:defines_verb(from, origverb_first))
"verb is not defined on the from object";
return E_VERBNF;
elseif ((vinfo = verb_info(from, origverb_first)) && (!$perm_utils:controls(who, vinfo[1])))
"caller_perms() is not permitted to add a verb with the existing verb owner";
return E_PERM;
elseif (!who.programmer)
return E_PERM;
else
"we now know that the caller's perms control the objects or the objects are writable, and we know that the caller's perms control the prospective verb owner (by more traditional means)";
vcode = verb_code(from, origverb_first);
vargs = verb_args(from, origverb_first);
vinfo[3] = (destverb == origverb) ? vinfo[3] | destverb;
if (typeof(res = `add_verb(to, vinfo, vargs) ! ANY') == ERR)
return res;
else
set_verb_code(to, destverb_first, vcode);
delete_verb(from, origverb_first);
return {to, vinfo[3]};
endif
endif
.
#59:49
":move_prop(OBJ from, STR prop name, OBJ to, [STR new prop name]) -> Moves the specified property and its contents from one object to another. Returns {OBJ, property name} where the property now resides if successful, error if not. To succeed, caller_perms() must control both objects and own the property, unless called with wizard perms. Supplying a fourth argument gives the property a new name on the new object.";
who = caller_perms();
{from, origprop, to, ?destprop = origprop} = args;
if ((((typeof(from) != OBJ) || (typeof(to) != OBJ)) || (typeof(origprop) != STR)) || (typeof(destprop) != STR))
return E_TYPE;
elseif ((!valid(from)) || (!valid(to)))
return E_INVARG;
elseif ((from == to) && (destprop == origprop))
"Moving same prop onto the same object puts the contents in the wrong one. Just not allow";
return E_NACC;
elseif (((!$perm_utils:controls(who, from)) && (!from.w)) || ((!$perm_utils:controls(who, to)) && (!to.w)))
"caller_perms() is not allowed to hack on either object in question";
return E_PERM;
elseif (!$object_utils:defines_property(from, origprop))
"property is not defined on the from object";
return E_PROPNF;
elseif ((pinfo = property_info(from, origprop)) && (!$perm_utils:controls(who, pinfo[1])))
"caller_perms() is not permitted to add a property with the existing property owner";
return E_PERM;
elseif (!who.programmer)
return E_PERM;
else
"we now know that the caller's perms control the objects or the objects are writable, and we know that the caller's perms control the prospective property owner (by more traditional means)";
pdata = from.(origprop);
pname = (destprop == origprop) ? origprop | destprop;
if (typeof(res = `add_property(to, pname, pdata, pinfo) ! ANY') == ERR)
return res;
else
delete_property(from, origprop);
return {to, pname};
endif
endif
.
#59:50
"Do not remove this verb!  This is an auxiliary verb for :eval_d().";
.
#59:51
":display_callers([callers() style list]) - displays the output of the given argument, assumed to be a callers() output. See `help callers()' for details. Will use callers() explicitly if no argument is passed.";
call = (caller_perms() == player) ? "notify_lines" | "tell_lines";
player:(call)(this:callers_text(@args));
.
#59:52
":callers_text([callers() style list]) - returns the output of the given argument, assumed to be a callers() output. See `help callers()' for details. Will use callers() explicitly if no argument is passed.";
linelen = player:linelen();
text = {};
su = $string_utils;
lu = $list_utils;
verbwidth = 0;
{?match = callers()} = args;
for verbitem in (lu:slice(match, 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) + su:left({"This", "Verb", "Permissions", "VerbLocation", "Player"}[x], -widths[x]);
l = (l + between) + su:space(widths[x], "-");
between = " ";
endfor
text = listappend(text, top);
text = listappend(text, l);
for line in (match)
output = {};
for bit in [1..5]
$command_utils:suspend_if_needed(3);
output = {@output, su:left((typeof(word = line[bit]) == STR) ? word | tostr(word, "(", valid(word) ? lu:shortest({word.name, @word.aliases}) | ((word == $nothing) ? "invalid" | ((word == $ambiguous_match) ? "ambiguous match" | "Error")), ")"), -widths[bit]), " "};
endfor
text = listappend(text, su:trimr(tostr(@output)));
endfor
text = listappend(text, l);
return text;
.
#59:53
":set_property_value(object, property, value)";
" set_verb_or_property(same) -- similar to `verb_or_property'";
"  -- attempts to set <object>.<property> to <value>.  If there exists <object>:set_<property>, then it is called and its returned value is returned.  If not, we try to set the property directly; the result of this is returned.";
set_task_perms(caller_perms());
if (length(args) != 3)
return E_ARGS;
elseif (typeof(o = args[1]) != OBJ)
return E_INVARG;
elseif (!$recycler:valid(o))
return E_INVIND;
elseif (typeof(p = args[2]) != STR)
return E_INVARG;
elseif ($object_utils:has_callable_verb(o, v = "set_" + p))
return o:(v)(args[3]);
else
return o.(p) = args[3];
endif
.
#59:54
"$code_utils:owns_task(who, task_id)";
"The purpose of this is to be faster than $code_utils:task_owner(task_id) in those cases where you are interested in whether a certain person owns the task rather than in determining the owner of a task where you have no preconceived notion of the owner.";
return $list_utils:assoc(args[1], $wiz_utils:queued_tasks(args[2]));
.
#59:55
"Syntax:  $code_utils:dflag_on()   => 0|1";
"";
"Returns true if the verb calling the verb that called this verb has the `d' flag set true. Returns false if it is !d. If there aren't that many callers, or the calling verb was a builtin such as eval, assume the debug flag is on for traceback purposes and return true.";
"This is useful for determining whether the calling verb should return or raise an error to the verb that called it.";
return (length(c = callers()) >= 2) ? `index(verb_info(c[2][4], c[2][2])[2], "d") && 1 ! E_INVARG => 1' | 1;
.
#59:56
perms = ((caller == $verb_editor) || (perms = caller_perms()).wizard) ? player | perms;
"usual callers are $verb_editor:compile and $prog:@program";
set_task_perms(perms);
object = args[1];
verbname = args[2];
oldcode = code = verb_code(object, verbname);
while (((code && (last = code[length(code)])) && (length(last) >= 15)) && (last[1..15] == "\"Last modified "))
code = code[1..length(code) - 1];
endwhile
code = {@code, tostr("\"Last modified ", ctime(), " by ", player.name, " (", player, ").\";")};
set_verb_code(object, verbname, code);
return oldcode == verb_code(object, verbname);
.
#59:57
set_task_perms(caller_perms());
tasks = {};
l = min(length(args), 9);
for t in (queued_tasks())
keep = 1;
for c in [1..l]
if ((args[c] != E_NONE) && (t[c + 4] != args[c]))
keep = 0;
endif
endfor
if (keep)
tasks = {@tasks, t};
endif
endfor
return tasks || {{0}};
"Last modified Sat Oct 18 16:56:35 1997 CDT by Wizard (#2).";
.
#60:0
return $player.ownership_quota;
.
#60:1
return $prog.ownership_quota;
.
#60:2
text = pass(@args);
object = $string_utils:match_object(what = args[1], player.location);
if ((text != E_PROPNF) || (!valid(object)))
return text;
elseif (ohelp = `object:help_msg() ! ANY' || `object.help_msg ! ANY')
return {tostr(object.name, " (", object, "):"), "----", @(typeof(ohelp) == LIST) ? ohelp | {ohelp}};
else
about = $object_utils:has_verb(object, "about");
return {tostr("Sorry, but no help is available on ", object.name, " (", object, ")."), tostr("Try `@examine ", what, "'", @about ? {" or `about ", what, "'"} | {}, ".")};
endif
.
#60:3
topiclist = pass(@args);
if (topiclist || (!args))
return topiclist;
elseif (valid(o = $string_utils:match_object(what = args[1], player.location)))
return {what};
else
return {};
endif
.
#60:4
text = {};
for db in ($code_utils:help_db_list())
if ($object_utils:has_callable_verb(db, "index"))
text = {@text, @db:index({tostr(db.name, " (", db, ")")})};
endif
endfor
return text;
.
#60:5
hdr = "Available Help Indices";
text = {"", hdr, $string_utils:space(hdr, "-")};
for db in ($code_utils:help_db_list())
try
for p in (db:find_index_topics())
text = {@text, tostr($string_utils:left(p, 14), " -- ", `db.(p)[2] ! ANY' || db.name, " (", db, ")")};
endfor
except (ANY)
"generally it will be E_TYPE when :find_index_topics returns an ERR. Just skip";
continue db;
endtry
endfor
if (full = this:find_full_index_topic())
text = {@text, "", tostr($string_utils:left(full, 14), " -- ", "EVERYTHING")};
endif
return text;
.
#60:6
wizzes = {};
for w in ($object_utils:leaves($wiz))
if (w.wizard && (w.advertised && is_player(w)))
wizzes = {@wizzes, w};
endif
endfor
wizzes = {#2, @$list_utils:randomly_permute(setremove(wizzes, #2))};
numwiz = length(wizzes);
hlist = {"ArchWizard:", "Wizard" + ((numwiz == 2) ? ":" | "s:"), @$list_utils:make(max(0, numwiz - 2), "")};
slist = {};
su = $string_utils;
for i in [1..numwiz]
wiz = wizzes[i];
slist = {@slist, tostr(su:left(hlist[i], 13), su:left(wiz.name, 16), (wpi = `wiz.public_identity.name ! ANY') ? (" (a.k.a. " + wpi) + ")" | "")};
endfor
return slist;
.
#60:7
if (((text = pass(@args)) != E_PROPNF) || ((!valid(object = $string_utils:match_object(what = args[1], player.location))) || (!$object_utils:has_property(object, "help_msg"))))
return text;
else
return {tostr(";;", $code_utils:corify_object(object), ".help_msg = $command_utils:read_lines()"), @$command_utils:dump_lines((typeof(text = object.help_msg) == LIST) ? text | {text})};
endif
.
#60:8
":find_full_index_topic([search])";
"Return the *full_index* topic or 0";
"If search argument is given and true, we don't depend on cached info.";
{?search = 0} = args;
"... N.B.  There is no cached info; it turns out that";
"... full-index is near enough to the beginning of $help's property list";
"... that there's no point to doing this.  --Rog";
for p in (`properties(this) ! E_PERM => {}')
if (`this.(p)[1] ! ANY' == "*full_index*")
return p;
endif
endfor
return 0;
.
#61:0
return this:contents();
.
#61:1
article = $code_utils:tonum(args[1]);
articles = this:articles();
if (((article == E_TYPE) || (article < 0)) || (article > length(articles)))
out = tostr("\"", args[1], "\" is not a valid news item.");
if (articles)
out = tostr(out, "  Valid items are in the range 1 to ", length(articles), ".");
endif
player:tell(out);
return E_INVARG;
endif
return articles[article];
.
#61:2
"Check player's new news items";
for item in (this:articles())
if (!item:read_by(player))
player:tell("ATTENTION:  There are new news items to read!  Type 'news' for a summary.");
return;
endif
endfor
.
#61:3
foo = tostr($time_utils:day(args[1]), ",");
return (foo + " ") + $time_utils:time_sub("$N $t $Y", args[1]);
.
#61:4
spc = length(tostr(length(articles = $news:articles())));
blank = $string_utils:space(spc + 4);
ret = {tostr(blank, $string_utils:left("Title", 42), $string_utils:left("Last Update", 28))};
ret = {@ret, tostr(blank, $string_utils:left($string_utils:space(5, "-"), 42), $string_utils:left($string_utils:space(11, "-"), 28))};
count = 0;
for o in (articles)
count = count + 1;
read_by = o:read_by(player) ? "  " | "* ";
ret = {@ret, tostr(read_by, $string_utils:left(count, spc), ": ", $string_utils:left(o:title(), 42), $string_utils:left($news:date(o.date), 28))};
endfor
return ret;
.
#61:5
return this.blessed_task == task_id();
.
#61:6
for o in (connected_players())
o:tell("There is a new addition to the newspaper, enter 'news new' to read it, or 'news' for an index.");
endfor
.
#61:7
this:notify_update();
.
#61:8
if (caller in this.contents)
for o in (connected_players())
o:tell("There has been a change to news article ", caller in this.contents, " (", caller.name, ").  Please check \"news\" for more information.");
endfor
else
return E_PERM;
endif
.
#61:9
if ((!$perm_utils:controls(player, this)) || (caller_perms() != player))
return player:tell("I don't think so.");
endif
if ($command_utils:object_match_failed(article = player:my_match_object(argstr), argstr))
return;
elseif (!$object_utils:isa(article, $news_item))
return player:tell("That doesn't appear to be a $news_item.");
endif
this.blessed_task = task_id();
article:moveto(this);
this.blessed_task = 0;
.
#61:10
if ((!$perm_utils:controls(player, this)) || (caller_perms() != player))
return player:tell("I don't think so.");
endif
if (((number = tonum(dobjstr)) < 1) || (NUM > length(this.contents)))
return player:tell("There doesn't appear to be an article number \"", dobjstr, "\".");
endif
this.blessed_task = task_id();
(article = this.contents[number]):moveto(player);
this.blessed_task = 0;
if (article.location != this)
player:tell("Article ", number, ": ", article.name, " (", article, ") removed from the news listings.");
else
player:tell("I can't seem to move object ", article, " to remove it from $news.");
endif
.
#61:11
if (caller_perms().wizard)
pass();
this.blessed_task = 0;
move(this, $player_start);
endif
"Last modified Tue Oct 14 14:19:40 1997 CDT by Wizard (#2).";
.
#61:12
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
user = args[1];
html = pass(@args);
contents = {};
if (this.contents)
titles = {};
dates = {};
articles = $news:articles();
titles = $encore_web_utils:generate_links(user, this.contents);
for article in (articles)
dates = {@dates, $news:date(article.date)};
endfor
titles = {"<B>Title</B>", @titles};
dates = {"<B>Last Updated</B>", @dates};
contents = $encore_web_utils:generate_table(user, {titles, dates}, this, "1");
else
contents = {@contents, "<B>Sorry, there are no news articles to read.</B>"};
endif
html = $list_utils:append(html, contents);
return html;
"Last modified Fri Apr 13 19:05:33 2001 CDT by Wizard (#2).";
.
#61:13
news_item = args[1];
news_item.date = time();
news_item.read_by = {};
if (news_item in this.contents)
for o in (connected_players())
o:tell("There has been a change to news article ", news_item in this.contents, " (", news_item.name, ").  Please check \"news\" for more information.");
endfor
else
return E_PERM;
endif
"Last modified Fri Apr 13 19:02:02 2001 CDT by Wizard (#2).";
.
#62:0
"Copied from The Coat Closet (#11):disfunc by Haakon (#2) Mon May  8 10:41:04 1995 PDT";
if ((((cp = caller_perms()) == (who = args[1])) || $perm_utils:controls(cp, who)) || (caller == this))
"need the first check since guests don't control themselves";
if (who.home == this)
move(who, $limbo);
this:announce("You hear a quiet popping sound; ", who.name, " has disconnected.");
else
pass(who);
endif
endif
.
#62:1
"Copied from The Coat Closet (#11):enterfunc by Haakon (#2) Mon May  8 10:41:38 1995 PDT";
who = args[1];
if ($limbo:acceptable(who))
move(who, $limbo);
else
pass(who);
endif
.
#62:2
"Copied from The Coat Closet (#11):match by Lambda (#50) Mon May  8 10:42:01 1995 PDT";
m = pass(@args);
if (m == $failed_match)
"... it might be a player off in the body bag...";
m = $string_utils:match_player(args[1]);
if (valid(m) && (!(m.location in {this, $limbo})))
return $failed_match;
endif
endif
return m;
.
#62:3
"Copied from The Coat Closet (#11):init_for_core by Nosredna (#2487) Mon May  8 10:42:52 1995 PDT";
if (!caller_perms().wizard)
return E_PERM;
endif
for v in ({"announce*", "emote", "button", "knob"})
if (`verb_info($player_start, v) ! E_VERBNF => 0')
delete_verb($player_start, v);
endif
endfor
for p in ({"out", "quiet", "button"})
if (p in properties($player_start))
delete_property($player_start, p);
endif
endfor
for p in ($object_utils:all_properties($room))
clear_property($player_start, p);
endfor
$player_start.name = "enCore Starting Point";
$player_start.aliases = {};
$player_start.description = this.core_description;
$player_start.exits = $player_start.entrances = {};
"Last modified Fri Aug  1 11:32:39 1997 CDT by Wizard (#2).";
.
#62:4
"Copied from The Coat Closet (#11):keep_clean by Haakon (#2) Mon May  8 10:47:08 1995 PDT";
if ($perm_utils:controls(caller_perms(), this))
junk = {};
while (1)
for x in (junk)
$command_utils:suspend_if_needed(0);
if (x in this.contents)
"This is old junk that's still around five minutes later.  Clean it up.";
if (!valid(x.owner))
move(x, $nothing);
#2:tell(">**> Cleaned up orphan object `", x.name, "' (", x, "), owned by ", x.owner, ", to #-1.");
elseif (!$object_utils:contains(x, x.owner))
move(x, x.owner);
x.owner:tell("You shouldn't leave junk in ", this.name, "; ", x.name, " (", x, ") has been moved to your inventory.");
#2:tell(">**> Cleaned up `", x.name, "' (", x, "), owned by `", x.owner.name, "' (", x.owner, "), to ", x.owner, ".");
endif
endif
endfor
junk = {};
for x in (this.contents)
if ((seconds_left() < 2) || (ticks_left() < 1000))
suspend(0);
endif
if (!is_player(x))
junk = {@junk, x};
endif
endfor
suspend(5 * 60);
endwhile
endif
.
#63: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 = #1, ?who = caller_perms()} = args;
if (!(caller_perms().wizard || (who == caller_perms())))
return E_PERM;
elseif (!(valid(what) && is_player(who)))
return E_INVARG;
elseif ((((who != what.owner) && (!what.f)) && (!who.wizard)) && (!caller_perms().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;
.
#63:1
"Take the object in args[1], and turn it into a child of #1 owned by $hacker.";
"If the object is a player, decline.";
item = args[1];
if (!$perm_utils:controls(caller_perms(), item))
raise(E_PERM);
elseif (is_player(item))
raise(E_INVARG);
endif
this:addhist(caller_perms(), item);
"...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);
item.name = tostr("Recyclable ", item);
move(item, this);
.
#63:2
e = `set_task_perms(caller_perms()) ! ANY';
if (typeof(e) == ERR)
return e;
else
val = this:_recreate(@args);
return (val == E_NONE) ? $quota_utils:bi_create(@args) | val;
endif
.
#63: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
.
#63: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
.
#63:5
"added check that obj is already $garbage - Bits 12/16/5";
dobj = valid(dobj) ? dobj | $string_utils:match_object(dobjstr, player.location);
if (!valid(dobj))
dobj = (n = toint(dobjstr)) ? toobj(n) | #-1;
endif
if (!valid(dobj))
player:tell("Couldn't parse ", dobjstr, " as a valid object number.");
elseif (!(dobj in this.contents))
player:tell("Couldn't find ", dobj, " in ", this.name, ".");
elseif (!$object_utils:isa(dobj, $garbage))
player:tell("Sorry, that isn't recyclable.");
elseif ($object_utils:has_callable_verb(this, "request_refused") && (msg = this:request_refused(player, dobj)))
player:tell("Sorry, can't do that:  ", msg);
else
if (typeof(emsg = this:setup_toad(dobj, player, $root_class)) != ERR)
dobj:moveto(player);
dobj.aliases = {dobj.name = "Object " + tostr(dobj)};
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
.
#63:6
"this:setup_toad(objnum,new_owner,parent)";
"Called by :_create and :request.";
if (caller != this)
return E_PERM;
endif
{potential, who, what} = args;
if (!$quota_utils:creation_permitted(who))
return E_QUOTA;
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
.
#63:7
if (caller == this)
this.orphans = setadd(this.orphans, args[1]);
endif
.
#63:8
if (caller == this)
this.orphans = setremove(this.orphans, args[1]);
endif
.
#63:9
"Usage:  valid(object)";
"True if object is valid and not $garbage.";
return valid(args[1]) && (parent(args[1]) != $garbage);
.
#63:10
if (caller_perms().wizard)
this.orphans = {};
this.history = {};
this.lost_souls = {};
pass();
endif
.
#63:11
who = caller_perms();
if (!valid(parent = {@args, $garbage}[1]))
return E_INVARG;
elseif (!who.wizard)
return E_PERM;
elseif (typeof(o = renumber($quota_utils:bi_create(parent, $hacker))) == ERR)
"..death...";
elseif (parent == $garbage)
$recycler:_recycle(o);
else
o.aliases = {o.name = tostr("Resurrectee ", o)};
$wiz_utils:set_owner(o, who);
move(o, who);
endif
reset_max_object();
return o;
.
#63:12
if (!caller_perms().wizard)
raise(E_PERM);
endif
fork (1800)
this:(verb)();
endfork
for x in (this.lost_souls)
this.lost_souls = setremove(this.lost_souls, x);
if ((valid(x) && (typeof(x.owner.owned_objects) == LIST)) && (!(x in x.owner.owned_objects)))
x.owner.owned_objects = setadd(x.owner.owned_objects, x);
$quota_utils:summarize_one_user(x.owner);
endif
$command_utils:suspend_if_needed(0);
endfor
.
#63:13
if (prepstr in {"in", "inside", "into"})
recycler = this;
linelen = ((linelen = abs(player.linelen)) < 20) ? 78 | linelen;
intercolumn_gap = 2;
c_width = length(tostr(max_object())) + intercolumn_gap;
n_columns = (linelen + (c_width - 1)) / c_width;
things = $list_utils:sort_suspended(0, this.contents);
header = tostr(this.name, " (", this, ") contains:");
player:tell_lines({header, @$string_utils:columnize_suspended(0, things, n_columns)});
else
return pass(@args);
endif
"This code contributed by Mickey.";
.
#63:14
who = args[1];
if ($quota_utils.byte_based && (is_clear_property(who, "size_quota") || is_clear_property(who, "owned_objects")))
raise(E_QUOTA);
endif
cheater = 0;
other_cheaters = {};
for x in (this.lost_souls)
if (((valid(x) && ((owner = x.owner) != $hacker)) && (typeof(owner.owned_objects) == LIST)) && (!(x in owner.owned_objects)))
if (owner == who)
who.owned_objects = setadd(who.owned_objects, x);
cheater = 1;
else
"it's someone else's quota scam we're detecting...";
other_cheaters = setadd(other_cheaters, owner);
owner.owned_objects = setadd(owner.owned_objects, x);
this.lost_souls = setremove(this.lost_souls, x);
endif
endif
this.lost_souls = setremove(this.lost_souls, x);
endfor
if ($quota_utils.byte_based)
if (cheater)
$quota_utils:summarize_one_user(who);
endif
if (other_cheaters)
fork (0)
for x in (other_cheaters)
$quota_utils:summarize_one_user(x);
endfor
endfork
endif
endif
.
#63:15
for x in (this.orphans)
if ((!valid(x)) || ((x.owner != $hacker) && (x in x.owner.owned_objects)))
this.orphans = setremove(this.orphans, x);
endif
endfor
.
#64:0
return ("Garbage object " + tostr(this)) + ".";
.
#64:1
player:tell(this:description());
.
#64:2
return tostr("Recyclable ", this);
.
#64:3
return;
.
#65:0
if (i = args[1] in {"noinclude", "sender"})
return {{{"include", "all"}[i], !args[2]}};
else
return {args};
endif
.
#65:1
"... we'll take anything...";
raw = args[2];
if (raw == 1)
"...+@mail => @mailo=new";
return {args[1], "new"};
else
return args[1..2];
endif
.
#65:2
{oname, raw, data} = args;
if (typeof(raw) == LIST)
if (length(raw) > 1)
return "Too many arguments.";
endif
raw = raw[1];
elseif (typeof(raw) == INT)
return {oname, raw && ((oname == "manymsgs") ? 20 | 1)};
endif
if ((value = $code_utils:toint(raw)) == E_TYPE)
return tostr("`", raw, "'?  Number expected.");
endif
return {oname, value};
.
#65:3
{oname, raw, data} = args;
if (typeof(raw) == STR)
raw = $string_utils:explode(raw, ",");
elseif (typeof(raw) == INT)
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
.
#65: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
.
#65: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
.
#65:6
if (value = this:get(@args))
return {"", {tostr("Default message sequence for @mail:  ", (typeof(value) == STR) ? value | $string_utils:from_list(value, " "))}};
else
default = $mail_agent.("player_default_@mail");
return {0, {tostr("Default message sequence for @mail:  ", (typeof(default) == STR) ? default | $string_utils:from_list(default, " "))}};
endif
.
#65: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
.
#65: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
.
#65: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
.
#65:10
if (value = this:get(@args))
return {value, {"Have MOO-mail automatically forwarded to me at", "my 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).";
.
#65:11
":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] && (reason = $network:email_will_fail(player.email_address)))
return tostr("Invalid registered .email_address: ", reason);
endif
return args;
.
#65:12
value = this:get(args[1], "expire");
if (value < 0)
return {1, {"Messages will not expire."}};
else
return {value, {tostr("Unkept messages expire in ", $time_utils:english_time(value || $mail_agent.player_expire_time), value ? "" | " (default)")}};
endif
.
#65:13
{oname, value, data} = args;
if ((typeof(value) == STR) && index(value, " "))
value = $string_utils:explode(value, " ");
if (!value)
return {oname, 0};
endif
endif
if (value == 1)
return {oname, -1};
elseif (typeof(value) == LIST)
if (length(value) > 1)
nval = $time_utils:parse_english_time_interval(@value);
if (typeof(nval) == ERR)
return "Time interval should be of a form like \"30 days, 10 hours and 43 minutes\".";
else
return {oname, nval};
endif
endif
value = value[1];
endif
if ((nval = $code_utils:toint(value)) || (nval == 0))
return {oname, (nval < 0) ? -1 | nval};
elseif (value == "Never")
return {oname, -1};
else
return "Number, time interval (e.g., \"30 days\"), or \"Never\" expected";
endif
.
#65:14
if (caller_perms().wizard)
for x in ({"fast_check", "idle_check", "idle_threshold"})
this:remove_name(x);
for y in ({"show", "check", "parse"})
delete_verb(this, (y + "_") + x);
delete_property(this, (y + "_") + x);
endfor
endfor
endif
.
#65:15
if ((what = args[1]) in {"new", "contents", "all"})
return {what};
else
return "Error: `news' option must be one of `new' or `contents' or `all'";
endif
.
#65:16
if (typeof(args[2]) == INT)
return tostr(strsub(verb, "parse_", ""), " is not a boolean option.");
else
return {args[1], (typeof(args[2]) == STR) ? args[2] | $string_utils:from_list(args[2], " ")};
endif
.
#65:17
if ((value = this:get(@args)) == "all")
return {value, {"the `news' command will show all news"}};
elseif (value == "contents")
return {value, {"the `news' command will show the titles of all articles"}};
elseif (value == "new")
return {value, {"the `news' command will show only new news"}};
else
return {0, {"the `news' command will show all news"}};
endif
.
#66:0
if (i = args[1] in {"parens", "noisy_insert"})
return {{{"no_parens", "quiet_insert"}[i], !args[2]}};
else
return {args};
endif
.
#66:1
if (o = (name = args[2]) in {"parens", "noisy_insert"})
args[2] = {"no_parens", "quiet_insert"}[o];
return {@pass(@args), tostr("(", name, " is a synonym for -", args[2], ")")};
else
return pass(@args);
endif
.
#68: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
.
#68: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, oname, value} = args;
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, value} = nv;
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;
.
#68: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..$];
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 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..$]);
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
.
#68: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..$], char);
return namestr[i + 1..(i + j) - 1];
endif
.
#68: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
.
#68: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
.
#68: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 ") + toliteral(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] = toliteral(value);
value = "";
endif
endif
if (value in {0, 1})
which = "-+"[value + 1] + name;
elseif ((typeof(value) in {OBJ, STR, INT}) && (value != ""))
which = tostr(" ", name, "=", value);
else
which = " " + 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;
.
#68: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.";
.
#68: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;
.
#68: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;
.
#68: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 {INT, 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 ");
.
#68: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
.
#69:0
raise(@args);
"this:(this.names[tonum(args[1]) + 1])()";
.
#69:1
"... hmmm... don't know how to raise E_NONE...";
return E_NONE;
.
#69:2
"...raise E_TYPE ...";
1[2];
.
#69:3
"...raise E_DIV ...";
1 / 0;
.
#69:4
"...raise E_PERM ...";
this.owner.password;
.
#69:5
"...raise E_PROPNF ...";
this.a;
.
#69:6
"...raise E_VERBNF ...";
this:a();
.
#69:7
"...raise E_VARNF ...";
a;
.
#69:8
"...raise E_INVIND ...";
#-1.a;
.
#69:9
move(this, this);
.
#69:10
"...raise E_MAXREC ...";
this:(verb)();
.
#69:11
"...raise E_RANGE ...";
{}[1];
.
#69:12
"...raise E_ARGS ...";
toint();
.
#69:13
"...raise E_NACC ...";
move($hacker, this);
.
#69:14
"...raise E_INVARG ...";
parent(#-1);
.
#69:15
set_task_perms($no_one);
"...raise E_QUOTA ...";
create($thing);
.
#69:16
return 0;
.
#69:17
return toliteral(args[1]);
"return this.names[tonum(args[1]) + 1];";
.
#69:18
"toerr -- given a string or a number, return the corresponding ERR.";
"If not found or an execution error, return -1.";
if (typeof(string = args[1]) == STR)
for e in (this.all_errors)
if (tostr(e) == string)
return e;
endif
endfor
elseif (typeof(number = args[1]) == INT)
for e in (this.all_errors)
if (toint(e) == number)
return e;
endif
endfor
endif
return -1;
.
#69:19
"match_error -- searches for tostr(E_WHATEVER) in a string, returning the ERR, returns -1 if no error string is found.";
string = args[1];
for e in (this.all_errors)
if (index(string, tostr(e)))
return e;
endif
endfor
return -1;
.
#70:0
if (caller_perms().wizard)
pass();
"this:rm_message_seq({1, 1 + this:length_all_msgs()})";
"this:expunge_rmm()";
for p in (properties(this))
$command_utils:suspend_if_needed(0);
if (p && (p[1] == " "))
delete_property(this, p);
endif
endfor
while ("secret_SHHH" in verbs(this))
delete_verb(this, "secret_SHHH");
endwhile
this.messages = this.messages_going = {};
this.mail_forward = {};
this.mail_notify = {player};
player.current_message = {@player.current_message, {this, 0, 0}};
for p in ({"moderator_forward", "moderator_notify", "writers", "readers", "expire_period", "last_used_time"})
this.(p) = $mail_recipient.(p);
endfor
this.moderated = 1;
else
return E_PERM;
endif
.
#71:0
player:tell_lines(this:description());
player:tell($string_utils:pronoun_sub("%S %<is> moving around from room to room, cleaning up.", this));
.
#71:1
"$housekeeper:cleanup([insist]) => clean up player's objects. Argument is 'up' or 'up!' for manually requested cleanups (notify player differently)";
if (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.");
.
#71:2
"replace the object given to its proper spot (if there is one).";
{object, ?insist = 0} = args;
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!")
if ($code_utils:verb_or_property(object, "in_use"))
return ("Not returning " + object.name) + " because it claims to be in use";
endif
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:acceptable(object)))
return (place.name + " won't accept ") + object.name;
endif
try
requestor:tell("As you requested, the housekeeper tidies ", $string_utils:nn(object), " from ", $string_utils:nn(loc), " to ", $string_utils:nn(place), ".");
if ($object_utils:has_verb(loc, "announce_all_but"))
loc:announce_all_but({requestor, object}, "At ", requestor.name, "'s request, the ", this.name, " sneaks in, picks up ", object.name, " and hurries off to put ", ($object_utils:has_property(object, "po") && (typeof(object.po) == STR)) ? object.po | "it", " away.");
endif
except (ANY)
"Ignore errors";
endtry
this:moveit(object, place, requestor);
if ((loc = object.location) == oldloc)
return (object.name + " wouldn't go; ") + ((!place:acceptable(object)) ? (" perhaps " + $string_utils:nn(place)) + " won't let it in" | ((" perhaps " + $string_utils:nn(loc)) + " won't let go of it"));
endif
try
object:tell("The housekeeper puts you away.");
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
except (ANY)
"Ignore errors";
endtry
return "";
.
#71: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("------------------------------------------------------------------");
.
#71:4
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
endif
{what, ?who = player, ?where = what.location} = args;
if ((what < #1) || (!valid(what)))
return "invalid object";
endif
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
if (is_player(what))
return ("The " + this.name) + " doesn't do players, except to cart them home when they fall asleep.";
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("The ", this.name, " will keep ", what.name, " (", what, ") at ", valid(where) ? ((where.name + " (") + tostr(where)) + ")" | where, ".");
.
#71:5
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
endif
{what, ?who = player} = args;
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
.
#71:6
"does player control entry I?";
{i, who} = args;
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 to, or the destination is no longer valid.";
else
return "";
endif
.
#71: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 ($code_utils:task_valid(this.task))
taskn = this.task;
this.task = 0;
kill_task(taskn);
endif
fork taskn (0)
while (1)
index = 1;
while (index < length(this.clean))
x = this.clean[index];
index = index + 1;
try
this:replace(x);
except id (ANY)
endtry
suspend(this.testing ? 2 | this:time());
endwhile
suspend(5);
this:litterbug();
endwhile
endfork
this.task = taskn;
.
#71:8
for room in (this.public_places)
for thingy in (room.contents)
suspend(10);
if (((thingy.location == room) && this:is_litter(thingy)) && (!this:is_watching(thingy, $nothing)))
"if it is litter and no-one is watching";
fork (0)
this:send_home(thingy);
endfork
suspend(0);
endif
endfor
endfor
.
#71:9
return valid(thing = args[1]) && thing:is_listening();
.
#71: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
home: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
.
#71:11
"Wizardly verb to move object with requestor's permission";
if (caller != this)
return E_PERM;
else
set_task_perms(player = args[3]);
return args[1]:moveto(args[2]);
endif
.
#71: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
.
#71:13
what = args[1];
if (!(where = what in this.clean))
return 0;
else
return {this.destination[where], this.requestors[where]};
endif
.
#71: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;
.
#71:15
if (caller_perms().wizard)
this.password = "Impossible password to type";
this.last_password_time = 0;
this.litter = {};
this.public_places = {};
this.requestors = {};
this.destination = {};
this.clean = {};
this.eschews = {};
this.recycle_bins = {};
this.cleaning = #-1;
this.task = 0;
this.owners = {#2};
this.mail_forward = {#2};
this.player_queue = {};
this.move_player_task = 0;
this.moveto_task = 0;
pass(@args);
endif
.
#71: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()));
.
#71: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
.
#71: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 + $login:current_lag(), length(this.clean) ? 3600 / length(this.clean) | 0);
.
#71:19
return caller == this;
.
#71:20
if (!$perm_utils:controls(caller_perms(), this))
"perms don't control the $housekeeper; probably not called by $room:disfunc then. Used to let args[1] call this. No longer.";
return E_PERM;
endif
this.player_queue = {@this.player_queue, {args[1], time() + 300}};
if ($code_utils:task_valid(this.move_player_task))
"the move-players-home task is already running";
return;
endif
fork tid (10)
while (this.player_queue)
if ((mtime = this.player_queue[1][2]) < (time() + 10))
if ((is_player(who = this.player_queue[1][1]) && (!$object_utils:connected(who))) && (who.location != who.home))
"Make sure it's a player. Somehow non-player objects have gotten in the queue";
player = who;
this:move_em(who);
endif
this.player_queue = listdelete(this.player_queue, 1);
else
suspend(mtime - time());
endif
$command_utils:suspend_if_needed(1);
endwhile
endfork
this.move_player_task = tid;
.
#71:21
if (caller == this)
who = args[1];
set_task_perms(who);
fork (0)
fork (0)
"This is forked so that it's protected from aborts due to errors in the player's :moveto verb.";
if (who.location != who.home)
"Unfortunately, if who is -already- at $player_start, move() won't call :enterfunc and the sleeping body never goes to $limbo. Have to call explicitly for that case. Ho_Yan 11/2/95";
if (who.location == $player_start)
$player_start:enterfunc(who);
else
move(who, $player_start);
endif
endif
endfork
start = who.location;
this:set_moveto_task();
who:moveto(who.home);
if (who.location != start)
start:announce(this:take_away_msg(who));
endif
if (who.location == who.home)
who.home:announce(this:drop_off_msg(who));
endif
endfork
else
return E_PERM;
endif
.
#71:22
return $string_utils:pronoun_sub(this.(verb), args[1], this);
.
#71:23
"sets $housekeeper.moveto_task to the current task_id() so player:moveto's can check for validity.";
if (caller != this)
return E_PERM;
endif
this.moveto_task = task_id();
.
#72:0
"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..$]} | {address, ""};
.
#72:1
"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 (match(site, "^[0-9.]+$"))
return E_INVARG;
elseif (!site)
return "";
elseif (!(dot = rindex(site, ".")))
dot = rindex(site = this.site, ".");
endif
if ((!dot) || (!(dot = rindex(site[1..dot - 1], "."))))
return site;
else
domain = site[dot + 1..$];
site = site[1..dot - 1];
while (site && (domain in this.large_domains))
if (dot = rindex(site, "."))
domain = tostr(site[dot + 1..$], ".", domain);
site = site[1..dot - 1];
else
return tostr(site, ".", domain);
endif
endwhile
return domain;
endif
.
#72:2
":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).";
"Returns the (initial) connection or an error, as in open_network_connection";
if (!this:trust(caller_perms()))
return E_PERM;
endif
address = args[1];
port = args[2];
if (length(args) < 3)
connect_to = $nothing;
elseif ((typeof(connect_to = args[3]) == OBJ) && (valid(connect_to) && is_player(connect_to)))
if (!$perm_utils:controls(caller_perms(), connect_to))
return E_PERM;
endif
else
return E_INVARG;
endif
if (typeof(connection = `open_network_connection(address, port) ! ANY') != ERR)
if (valid(connect_to))
this.connect_connections_to = {@this.connect_connections_to, {connection, connect_to}};
endif
endif
return connection;
.
#72:3
if (!this:trust(caller_perms()))
return E_PERM;
endif
con = args[1];
if (!index(`connection_name(con) ! ANY => ""', " to "))
return E_INVARG;
endif
boot_player(con);
if (i = $list_utils:iassoc(con, $network.connect_connections_to))
$network.connect_connections_to = listdelete($network.connect_connections_to, i);
endif
return 1;
.
#72:4
"sendmail(to, subject, line1, line2, ...)";
"  sends mail to internet address 'to', with given subject.";
"  It fills in various fields, such as date, from (from player), etc.";
"  the rest of the arguments are remaining lines of the message, and may begin with additional header fields.";
"  (must match RFC822 specification).";
"Requires $network.trust to call (no anonymous mail from MOO).";
"Returns 0 if successful, or else error condition or string saying why not.";
if (!this:trust(caller_perms()))
return E_PERM;
endif
mooname = this.MOO_name;
mooinfo = tostr(mooname, " (", this.site, " ", this.port, ")");
if (reason = this:invalid_email_address(to = args[1]))
return reason;
endif
"took out Envelope-from:  + this.errors_to_address";
return this:raw_sendmail(to, "Date: " + ctime(), "From: " + this:return_address_for(player), "To: " + to, "Subject: " + args[2], "Errors-to: " + this.errors_to_address, "X-Mail-Agent: " + mooinfo, @args[3..$]);
.
#72:5
return (who = args[1]).wizard || (who in this.trusts);
.
#72:6
if (caller_perms().wizard)
pass(@args);
this.active = 0;
this.reply_address = "moomailreplyto@yourhost";
this.errors_to_address = "moomailerrors@yourhost";
this.site = "yoursite";
this.postmaster = "postmastername@yourhost";
this.usual_postmaster = "postmastername@yourhost";
this.password_postmaster = "postmastername@yourhost";
this.envelope_from = "postmastername@yourhost";
this.blank_envelope = 0;
this.MOO_name = $core_name;
this.maildrop = "localhost";
this.port = 7777;
this.webport = 7000;
this.large_domains = {};
this.trusts = {$hacker};
this.connect_connections_to = {};
endif
"Last modified Tue Oct 14 14:21:10 1997 CDT by Wizard (#2).";
.
#72:7
"rawsendmail(to, @lines)";
"sends mail without processing. Returns 0 if successful, or else reason why not.";
if (!caller_perms().wizard)
return E_PERM;
endif
if (!this.active)
return "Networking is disabled.";
endif
if (typeof(this.debugging) == LIST)
"who to notify";
debugging = this.debugging;
else
"notify this owner";
debugging = this.debugging && {this.owner};
endif
address = args[1];
body = listdelete(args, 1);
data = {"HELO " + this.site, ("MAIL FROM:<" + this.envelope_from) + ">", ("RCPT TO:<" + address) + ">", "DATA"};
blank = 0;
for x in (body)
this:suspend_if_needed(0);
if (!(blank || match(x, "[a-z0-9-]*: ")))
if (x)
data = {@data, ""};
endif
blank = 1;
endif
data = {@data, (x == ".") ? "." + x | x};
endfor
data = {@data, ".", "QUIT", ""};
suspend(0);
target = $network:open(this.maildrop, 25);
if (typeof(target) == ERR)
"special handling for when the maildrop declines connection -- Heathcliff 3/17/96";
if (task_id() != this.queued_mail_task)
this:add_queued_mail(@args);
"we'll return as normal on the assumption the mail will go later";
return;
else
return tostr("Cannot open connection to maildrop ", this.maildrop, ": ", target);
endif
endif
fork (0)
for line in (data)
this:suspend_if_needed(0);
while (!notify(target, line, 1))
suspend(0);
endwhile
if (debugging)
notify(debugging[1], "SEND:" + line);
endif
endfor
endfork
expect = {"2", "2", "2", "2", "3", "2"};
while (expect && (typeof(line = `read(target) ! ANY') != ERR))
if (line)
if (debugging)
notify(debugging[1], "GET: " + line);
endif
if (!index("23", line[1]))
$network:close(target);
return line;
"error return";
else
if (line[1] != expect[1])
expect = {@expect, "2", "2", "2", "2", "2"};
else
expect = listdelete(expect, 1);
endif
endif
endif
endwhile
$network:close(target);
return 0;
.
#72:8
"invalid_email_address(email) -- check to see if email looks like a valid email address. Return reason why not.";
"Modified by Jan to make error messages compatible with javascript alert format";
address = args[1];
if (!address)
return "no email address supplied";
endif
if (!(at = rindex(address, "@")))
return "The recipients Internet email address is not valid. Please notify a MOO administrator.";
endif
name = address[1..at - 1];
host = address[at + 1..$];
if (match(name, "^in%%") || match(name, "^smtp%%"))
return tostr(name, "' does not look like a valid username (try removing the in% or smtp%)");
endif
if (!match(host, $network.valid_host_regexp))
return tostr(host, " does not look like a valid internet host");
endif
if (!match(name, $network.valid_email_regexp))
return tostr(name, " does not look like a valid user name for internet mail");
endif
return "";
"Last modified Thu Apr  8 12:05:00 1999 CDT by Wizard (#2).";
.
#72:9
return match(args[1], this.valid_host_regexp) ? "" | tostr("'", args[1], "' doesn't look like a valid internet host name");
.
#72:10
":email_will_fail(email-address[, display?]) => Makes sure the email-address is one that can actually be used by $network:sendmail().";
{email, ?display = 0} = args;
reason = this:invalid_email_address(email);
if (reason && display)
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
if (!this:valid_email_address(email))
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 (display)
player:tell(msg);
return 1;
else
return msg;
endif
"Last modified Tue Jun 15 00:19:01 1993 EDT by Ranma (#200).";
.
#72:11
"for trusted players, they can read from objects they own or open connections";
if (!this:trust(caller_perms()))
return E_PERM;
elseif (valid(x = args[1]))
if ((x.owner == x) || (x.owner != caller_perms()))
return E_INVARG;
endif
elseif (!this:is_outgoing_connection(x))
return E_PERM;
endif
return `read(x) ! ANY';
.
#72:12
":is_open(object)";
"return true if the object is somehow connected, false otherwise.";
return typeof(`idle_seconds(@args) ! ANY') == INT;
"Relies on test in idle_seconds, and the error catching";
.
#72:13
"Peer at an incoming connection.  Decide if it should be connected to something, return that object. If it should be ignored (outbound connection), return 1. Called only by #0:do_login_command";
if (caller != #0)
return;
endif
what = args[1];
"this code for unix servers >= 1.7.5 only";
if (index(`connection_name(what) ! ANY => ""', " to "))
"outbound connection";
if (ct = $list_utils:assoc(what, this.connect_connections_to))
this.connect_connections_to = setremove(this.connect_connections_to, ct);
return ct[2];
else
return 1;
endif
else
return 0;
endif
.
#72:14
":return_address_for(player) => string of 'return address'. Currently inbound mail doesn't work, so this is a bogus address. Changed this verb to return senders real name and email address if anonymous users is off";
who = args[1];
if (valid(who) && is_player(who))
sender = ($anonymous_users == 0) ? who.real_name | tostr(who.name, " at ", this.moo_name);
email_address = ($anonymous_users == 0) ? who.email_address | "";
return tostr("\"", sender, "\" <", email_address, ">");
else
return tostr("(non-player ", who, ") ", $login.registration_address);
endif
"Last modified Tue Oct 14 14:12:30 1997 CDT by Wizard (#2).";
.
#72:15
"called when restarting to clean out state.";
if (caller != #0)
return E_PERM;
endif
this.connect_connections_to = {};
.
#72:16
return index(`connection_name(args[1]) ! ANY => ""', " to ");
.
#72:17
"for trusted players, they can write to connections";
if (!this:trust(caller_perms()))
return E_PERM;
elseif (valid(x = args[1]))
return E_INVARG;
elseif (!this:is_outgoing_connection(x))
return E_PERM;
endif
return notify(x, args[2]);
.
#72:18
"$command_utils:suspend_if_needed but chowned to player";
if ($command_utils:running_out_of_time())
set_task_perms(caller_perms().wizard ? player | caller_perms());
return $command_utils:suspend_if_needed(@args);
endif
.
#72:19
":error(ERN, host, port) interpret open_network_connection(host, port) error";
{msg, host, port} = args;
if (msg == E_PERM)
return "Networking not enabled in server, or else user doesn't have permission to call o_n_c();";
elseif (msg == E_INVARG)
return tostr("The host/port ", toliteral(host), "/", toliteral(port), " is invalid or is not responding.");
elseif (msg == E_QUOTA)
return tostr("The connection to ", toliteral(host), "/", toliteral(port), " cannot be made at this time.");
else
return tostr("Unusual error: ", toliteral(msg));
endif
.
#72:20
"'cause this doesn't have a $_utils name";
return this:description();
.
#72:21
"adjust_postmaster_for_password(enter_or_exit): permits the MOO to have two different postmasters for different kinds of bounces.  If entering password (argument \"enter\"), change to $network.password_postmaster, else (argument \"exit\") change to $network.usual_postmaster.";
if (args[1] == "enter")
$network.postmaster = $network.password_postmaster;
$network.errors_to_address = $network.password_postmaster;
$network.envelope_from = $network.password_postmaster;
else
$network.postmaster = $network.usual_postmaster;
$network.errors_to_address = $network.usual_postmaster;
$network.envelope_from = $network.blank_envelope ? "" | $network.usual_postmaster;
endif
.
#72:22
"$network:add_queued_mail( mail message )";
"  -- where `mail message' is in the same format as passed to :raw_sendmail";
if (caller == this)
this.queued_mail = {@this.queued_mail, {time(), args}};
if (!$code_utils:task_valid(this.queued_mail_task))
fork fid (3600)
this:send_queued_mail();
endfork
this.queued_mail_task = fid;
endif
return 1;
else
return E_PERM;
endif
.
#72:23
"$network:send_queued_mail()";
"  -- tries to send the mail stored in the .queued_mail property";
while (queued_mail = this.queued_mail)
message = queued_mail[1];
if (!this:raw_sendmail(@message[2]))
this.queued_mail = setremove(this.queued_mail, message);
else
"wait an hour";
suspend(3600);
endif
endwhile
.
#73:0
":_make(...) => new node with value {...}";
if (!(caller in {this._mgr, this}))
return E_PERM;
endif
prop = this:_genprop();
add_property(this, prop, args, {$generic_biglist_home.owner, ""});
return prop;
.
#73:1
":_kill(node) destroys the given node.";
if (!(caller in {this, this._mgr}))
return E_PERM;
endif
delete_property(this, args[1]);
.
#73:2
return (caller == this._mgr) ? this.(args[1]) | E_PERM;
.
#73:3
return (caller == this._mgr) ? this.(args[1]) = listdelete(args, 1) | E_PERM;
.
#73:4
gp = this._genprop;
ngp = "";
for i in [1..length(gp)]
if (gp[i] != "z")
ngp = (ngp + "bcdefghijklmnopqrstuvwxyz"[strcmp(gp[i], "`")]) + gp[i + 1..$];
return " " + (this._genprop = ngp);
endif
ngp = ngp + "a";
endfor
return " " + (this._genprop = ngp + "a");
.
#73:5
"this 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.  So for an ordinary alist, where all of the leaves are of the form {key,datum}, you want:";
return args[1][1];
.
#73:6
if (!caller_perms().wizard)
return E_PERM;
endif
this.mowner = $hacker;
this._mgr = $biglist;
.
#74:0
all_help = this.help_msg;
if (typeof(all_help) == STR)
all_help = {all_help};
endif
helpless = {};
for vrb in (this.feature_verbs)
if (loc = $object_utils:has_verb(this, vrb))
loc = loc[1];
help = $code_utils:verb_documentation(loc, vrb);
if (help)
all_help = {@all_help, "", tostr(loc, ":", verb_info(loc, vrb)[3]), @help};
else
helpless = {@helpless, vrb};
endif
endif
endfor
if (helpless)
all_help = {@all_help, "", ("No help found on " + $string_utils:english_list(helpless, "nothing", " or ")) + "."};
endif
return {@all_help, "----"};
.
#74:1
"Definition from #1";
desc = this:description();
if (desc)
player:tell_lines(desc);
else
player:tell("You see nothing special.");
endif
player:tell("Please type \"help ", this, "\" for more information.");
.
#74:2
"Proper usage for the Generic Feature Object:";
"";
"First of all, the Generic Feature Object is constructed with the idea";
"that its children will be @moved to #24300, which is kind of a warehouse";
"for feature objects.  If there's enough interest, I'll try to make the";
"stuff that works with that in mind optional.";
"";
"Make a short description.  This is so I can continue to have looking at";
"#24300 give the descriptions of each of the objects in its .contents.";
"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.  (You need to use the :set_feature_verbs";
"verb to do this.)";
"";
"When someone types `help <this object>', they will be told the comment";
"strings from each of the verbs named in .feature_verbs.";
.
#74:3
return this in args[1].features;
.
#74:4
if ($perm_utils:controls(caller_perms(), this) || (caller == this))
return this.feature_ok = args[1];
else
return E_PERM;
endif
.
#74:5
"Can't see `get' unless it's in the room; can't see `drop' unless it's in the player.  Should possibly go on $thing.";
"Should use :contents, but I'm in a hurry.";
hidden = pass(@args);
if (this.location != args[1])
hidden = setadd(hidden, {$thing, verb_info($thing, "drop")[3], {"this", "none", "none"}});
hidden = setadd(hidden, {$thing, verb_info($thing, "give")[3], {"this", "at/to", "any"}});
endif
if (this.location != args[1].location)
hidden = setadd(hidden, {$thing, verb_info($thing, "get")[3], {"this", "none", "none"}});
endif
return hidden;
.
#74:6
if ($perm_utils:controls(caller_perms(), this) || (caller == this))
return this.feature_verbs = args[1];
else
return E_PERM;
endif
.
#74:7
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
pass(@args);
this.feature_verbs = {};
else
return E_PERM;
endif
.
#74:8
if (caller_perms().wizard)
if ($code_utils:verb_location() == this)
this.warehouse = $feature_warehouse;
`delete_property(this, "guest_ok") ! ANY';
`delete_verb(this, "set_ok_for_guest_use") ! ANY';
else
clear_property(this, "warehouse");
move(this, $feature_warehouse);
endif
endif
"Last modified Sun Aug 17 11:14:20 1997 CDT by Wizard (#2).";
.
#74:9
"This is just a blank verb definition to encourage others to use this verb name if they care when a user is no longer using that feature.";
.
#75:0
"Usage:  get_now(site, port, message)";
"Returns a list of strings, or an error if we couldn't connect.";
{host, port, message, ?extra = {0}} = args;
if (!this:trusted(caller_perms()))
return E_PERM;
elseif ((!match(host, $network.valid_host_regexp)) && (!match(host, "[0-9]+%.[0-9]+%.[0-9]+%.[0-9]+")))
"allow either welformed internet hosts or explicit IP addresses.";
return E_INVARG;
elseif ((port < 100) && (!(port in {70, 80, 81, 79})))
"disallow connections to low number ports; necessary?";
return E_INVARG;
endif
opentime = time();
con = $network:open(host, port);
opentime = time() - opentime;
if (typeof(con) == ERR)
return con;
endif
notify(con, message);
results = {};
count = this.limit;
"perhaps this isn't necessary, but if a gopher source is slowly spewing things, perhaps we don't want to hang forever -- perhaps this should just fork a process to close the connection instead?";
now = time();
timeout = 30;
end = "^%.$";
if (extra[1] == "2")
end = "^[2-9]";
endif
while ((((typeof(string = `read(con) ! ANY') == STR) && (!match(string, end))) && ((count = count - 1) > 0)) && ((now + timeout) > (now = time())))
if (string && (string[1] == "."))
string = string[2..$];
endif
results = {@results, string};
endwhile
$network:close(con);
if (opentime > 0)
"This is to keep repeated calls to $network:open to 'slow responding hosts' from totally spamming.";
suspend(0);
endif
return results;
.
#75: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;
tab = index(string, "	");
label = string[1..tab - 1];
string = string[tab + 1..$];
tab = index(string, "	");
tag = string[1..tab - 1];
string = string[tab + 1..$];
tab = index(string, "	");
host = string[1..tab - 1];
if (host[$] == ".")
host = host[1..$ - 1];
endif
string = string[tab + 1..$];
tab = index(string, "	");
port = toint(tab ? string[1..tab - 1] | string);
return {host, port, tag, label};
"ignore extra material after port, if any";
.
#75:2
"$gopher:show_text(who, start, end, ..node..)";
"like who:notify_lines($gopher:get(..node..)[start..end]), but pipelined";
if (!caller_perms().wizard)
return E_PERM;
endif
{who, start, end, @args} = args;
con = $network:open(who, start);
if (typeof(con) == ERR)
player:tell("Sorry, can't get this information now.");
return;
endif
notify(con, end);
line = 0;
sent = 0;
end = end || this.limit;
while (((string = `read(con) ! ANY') != ".") && (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..$];
endif
who:notify(string);
else
notify(who, string);
endif
endif
endwhile
$network:close(con);
return sent;
.
#75: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";
.
#75: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..$];
if (type == "menu")
elseif (type == "search")
label = (("<" + parse[3][rindex(parse[3], "	") + 1..$]) + "> ") + 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]};
.
#75:5
"Usage: get(site, port, selection)";
"returns a list of strings, or an error if it couldn't connect. Results are cached.";
request = args[1..3];
while ((index = request in this.cache_requests) && (this.cache_times[index] > time()))
if (typeof(result = this.cache_values[index]) != INT)
return result;
endif
if ($code_utils:task_valid(result))
"spin, let other process getting same data win, or timeout";
suspend(1);
else
"well, other process crashed, or terminated, or whatever.";
this.cache_times[index] = 0;
endif
endwhile
if (!this:trusted(caller_perms()))
return E_PERM;
endif
while (this.cache_times && (this.cache_times[1] < time()))
$command_utils:suspend_if_needed(0);
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";
endwhile
$command_utils:suspend_if_needed(0);
this:cache_entry(@request);
value = this:get_now(@args);
$command_utils:suspend_if_needed(0);
index = this:cache_entry(@request);
this.cache_times[index] = time() + ((typeof(value) == ERR) ? 120 | 1800);
this.cache_values[index] = value;
return value;
.
#75:6
if (!this:trusted(caller_perms()))
return E_PERM;
endif
if (!args)
this.cache_values = this.cache_times = this.cache_requests = {};
elseif (index = args[1..3] in this.cache_requests)
this.cache_requests = listdelete(this.cache_requests, index);
this.cache_times = listdelete(this.cache_times, index);
this.cache_values = listdelete(this.cache_values, index);
endif
.
#75:7
"unparse(host, port, tag, label) => string";
{host, port, tag, label} = args;
if (tab = index(tag, "	"))
"remove search terms from search nodes";
tag = tag[1..tab - 1];
endif
return tostr(label, "	", tag, "	", host, "	", port);
.
#75:8
"return an explanation for a 'false' $gopher:get result";
value = args[1];
if (value == E_INVARG)
return "That gopher server is not reachable or is not responding.";
elseif (value == E_QUOTA)
return "Gopher connections cannot be made at this time because of system resource limitations!";
elseif (typeof(value) == ERR)
return tostr("The gopher request results in an error: ", value);
else
return "The gopher request has no results.";
endif
.
#75:9
"default -- gopher trusts everybody";
return 1;
.
#75:10
"_textp(parsed node)";
"Return true iff the parsed info points to a text node.";
return index("02", args[1][4][1]);
.
#75:11
"_mail_text(parsed node)";
"Return the text to be mailed out for the given node.";
where = args[1];
if (this:_textp(where))
return $gopher:get(@where);
else
text = {};
for x in ($gopher:get(@where))
parse = $gopher:parse(x);
sel = parse[4];
text = {@text, "Type=" + sel[1], "Name=" + sel[2..$], "Path=" + parse[3], "Host=" + parse[1], "Port=" + tostr(parse[2]), "#"};
endfor
return text;
endif
.
#75:12
if (caller_perms().wizard)
this:clear_cache();
pass(@args);
endif
.
#75:13
"Just for debugging -- shows what's in the gopher cache";
req = this.cache_requests;
tim = this.cache_times;
val = this.cache_values;
"save values in case cache changes while printing";
player:tell("size -- expires -- host (port) ------ selector ------------");
for i in [1..length(req)]
re = req[i];
host = $string_utils:left(re[1] + ((re[2] == 70) ? "" | tostr(" (", re[2], ")")), 24);
expires = $string_utils:right($time_utils:dhms(tim[i] - time()), 8);
va = val[i];
if (typeof(va) == LIST)
va = length(va);
elseif (typeof(va) == ERR)
va = toliteral(va);
else
va = tostr(va);
endif
selector = re[3];
if (length(selector) > 40)
selector = "..." + selector[$ - 37..$];
endif
player:tell($string_utils:right(va, 8), expires, " ", host, selector);
endfor
player:tell("--- end cache display -------------------------------------");
.
#75:14
"Usage: get_cache(site, port, selection)";
"return current cache";
request = args[1..3];
if (index = request in this.cache_requests)
if (this.cache_times[index] > now)
return this.cache_values[index];
endif
endif
return 0;
.
#75:15
if (index = args in this.cache_requests)
return index;
else
this.cache_times = {@this.cache_times, time() + 240};
this.cache_values = {@this.cache_values, task_id()};
this.cache_requests = {@this.cache_requests, args};
return length(this.cache_requests);
endif
.
#75:16
return this:description();
.
#76:0
if (i = args[1] in {"list_numbers"})
return {{{"list_no_numbers"}[i], !args[2]}};
else
return {args};
endif
.
#76:1
if (o = (name = args[2]) in {"list_numbers"})
args[2] = {"list_no_numbers"}[o];
return {@pass(@args), tostr("(", name, " is a synonym for -", args[2], ")")};
else
return pass(@args);
endif
.
#76:2
if (value = this:get(@args))
return {value, {tostr("Default args for @verb:  ", $string_utils:from_list(value, " "))}};
else
return {0, {"Default args for @verb:  none none none"}};
endif
.
#76:3
value = args[1];
if (typeof(value) != LIST)
return "List expected";
elseif (length(value) != 3)
return "List of length 3 expected";
elseif (!(value[1] in {"this", "none", "any"}))
return tostr("Invalid dobj specification:  ", value[1]);
elseif (!((p = $code_utils:short_prep(value[2])) || (value[2] in {"none", "any"})))
return tostr("Invalid preposition:  ", value[2]);
elseif (!(value[3] in {"this", "none", "any"}))
return tostr("Invalid iobj specification:  ", value[3]);
else
if (p)
value[2] = p;
endif
return {value};
endif
.
#76:4
{oname, raw} = args;
if (typeof(raw) == STR)
raw = $string_utils:explode(raw, " ");
elseif (typeof(raw) == INT)
return raw ? {oname, {"this", "none", "this"}} | {oname, 0};
endif
value = $code_utils:parse_argspec(@raw);
if (typeof(value) != LIST)
return tostr(value);
elseif (value[2])
return tostr("I don't understand \"", $string_utils:from_list(value[2], " "), "\"");
else
value = {@value[1], "none", "none", "none"}[1..3];
return {oname, (value == {"none", "none", "none"}) ? 0 | value};
endif
.
#76:5
value = this:get(@args);
if (value)
return {value, {tostr("Default permissions for @property=`", value, "'.")}};
else
return {0, {"Default permissions for @property=`rc'."}};
endif
.
#76:7
{oname, raw} = args;
if (typeof(raw) != STR)
return "Must be a string composed of the characters `rwc'.";
endif
len = length(raw);
for x in [1..len]
if (!(raw[x] in {"r", "w", "c"}))
return "Must be a string composed of the characters `rwc'.";
endif
endfor
return {oname, raw};
.
#77:0
value = args[1];
if (m = match(value, "[^rwf]"))
return tostr("Unknown object flag:  ", value[m[1]]);
else
return {tostr(index(value, "r") ? "r" | "", index(value, "w") ? "w" | "", index(value, "f") ? "f" | "")};
endif
.
#77:1
if (value = this:get(@args))
return {value, {tostr("Object flags for @create:  ", value)}};
else
return {0, {tostr("@create leaves all object flags reset")}};
endif
.
#77:2
raw = args[2];
if (raw == 1)
"...+create_flags => create_flags=r";
return {args[1], "r"};
elseif (typeof(raw) == STR)
return args[1..2];
elseif (typeof(raw) != LIST)
return "???";
elseif (length(raw) > 1)
return tostr("I don't understand \"", $string_utils:from_list(listdelete(raw, 1), " "), "\"");
else
return {args[1], raw[1]};
endif
.
#77:3
name = args[2];
what = (verb == "show_dig_room") ? "room" | "exit";
if ((value = this:get(args[1], name)) == 0)
return {0, {tostr("@dig ", what, "s are children of $", what, ".")}};
else
return {value, {tostr("@dig ", what, "s are children of ", value, " (", valid(value) ? value.name | "invalid", ").")}};
endif
.
#77:4
{oname, raw, @extra} = args;
if (typeof(raw) == LIST)
if (length(raw) > 1)
return tostr("I don't understand \"", $string_utils:from_list(listdelete(raw, 1), " "), "\".");
endif
raw = raw[1];
endif
if (typeof(raw) != STR)
return "You need to give an object id.";
elseif ($command_utils:object_match_failed(value = player:my_match_object(raw), raw))
return "Option unchanged.";
endif
what = (verb == "parse_dig_room") ? "room" | "exit";
generic = #0.(what);
if (value == generic)
return {oname, 0};
else
if (!$object_utils:isa(value, generic))
player:tell("Warning: ", value, " is not a descendant of $", what, ".");
endif
return {oname, value};
endif
"Last modified Tue Oct 14 14:13:04 1997 CDT by Wizard (#2).";
.
#78:0
":add(object,parent,name[,is_alias])";
"returns 1 if successful";
"        #o if #o already has that parent/name pair";
if (!(caller in {this, $mail_agent}))
return E_PERM;
endif
{object, parent, name, ?is_alis = 0} = args;
pn = {parent, name};
if (oops = this:insert(tostr(parent, ":", name), object))
if (oops[1] != object)
return oops[1];
endif
elseif (olist = this:insert(colon_name = ":" + name, {object}))
"...there was already a list of objects for this name.";
"...reinsert them.";
this:insert(colon_name, setadd(olist[1], object));
endif
if (!(i = pn in object.names))
object.names = is_alias ? {@object.names, pn} | {pn, @object.names};
"...maybe fix capitalization...";
elseif (strcmp(object.names[i][2], name) != 0)
object.names[i][2] = name;
endif
return 1;
.
#78:1
":remove(object,parent,name)";
"removes {parent,name}.";
if (!(caller in {this, $mail_agent}))
return E_PERM;
endif
{object, parent, name} = args;
object.names = setremove(object.names, {parent, name});
this:delete2(tostr(parent, ":", name), object);
if ($list_utils:assoc(name, object.names, 2))
"...object still uses this name under other parents...";
elseif (others = this:delete2(colon_name = ":" + name, {object}))
"...other objects still use this name...";
this:insert(colon_name, setremove(others[1], object));
endif
.
#78:2
if (!caller_perms().wizard)
return E_PERM;
endif
for m in ($object_utils:descendents($mail_recipient))
for n in (m.names || {})
if (typeof(n) != LIST)
player:notify(tostr(o:mail_name(), "n=", n));
elseif (o = this:add(m, @n))
else
player:notify(tostr(m:mail_name(), " ", n[1], ":", n[2], " ", o));
endif
$command_utils:suspend_if_needed(0);
endfor
endfor
.
#78:3
if (caller_perms().wizard)
pass();
this:clearall();
this:load();
endif
.
#80: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
.
#80:1
if (!caller_perms().wizard)
return E_PERM;
else
$quota_utils = this;
set_verb_code(this, "can_peek", {"return args[1]==this.owner || $perm_utils:controls(args[1], args[2]);"});
set_verb_code(this, "can_touch", {"return args[1].wizard;"});
this.exempted = {};
this.working = #2;
this.task_time_limit = 500;
this.repeat_cycle = 0;
this.large_objects = {};
this.report_recipients = {#2};
endif
"Last modified Mon Jul 21 09:48:55 1997 CDT by Wizard (#2).";
.
#80:2
return 0;
.
#80:3
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);
this:charge_quota(who, value);
return value;
else
return E_QUOTA;
endif
"Last modified Sun Feb 13 08:54:15 2000 CST by Wizard (#2).";
.
#80:4
if ((caller != this) && (!caller_perms().wizard))
return E_PERM;
else
args[1].ownership_quota = 1;
endif
.
#80:5
if ((caller != this) && (!caller_perms().wizard))
return E_PERM;
else
args[1].ownership_quota = this.large_negative_number;
endif
.
#80:6
"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].";
{what, ?who = #-1} = args;
if (!valid(who))
return caller_perms();
elseif ($perm_utils:controls(caller_perms(), who))
return who;
else
return E_INVARG;
endif
.
#80:7
"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 ((!$object_utils:has_property(who, "size_quota")) || is_clear_property(who, "size_quota"))
return 0;
endif
$recycler:check_quota_scam(who);
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 0;
else
return 1;
endif
.
#80:8
{who} = args;
if ((caller != this) && (!this:can_peek(caller_perms(), who)))
return E_PERM;
elseif ($object_utils:has_property($local, "second_char_registry"))
seconds = $local.second_char_registry:all_second_chars(who);
if (seconds == E_INVARG)
return {who};
else
return seconds;
endif
else
return {who};
endif
.
#80:9
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, usage, timestamp, unmeasured} = x.size_quota;
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
.
#80:10
return args[1].size_quota[1];
.
#80:11
"Charge args[1] for the quota required to own args[2]";
{who, what} = args;
if ((caller == this) || caller_perms().wizard)
usage_index = 2;
unmeasured_index = 4;
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
.
#80:12
"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.";
{who, what} = args;
if ((caller == this) || caller_perms().wizard)
usage_index = 2;
unmeasured_index = 4;
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
.
#80:13
"Set args[1]'s quota to args[2]";
if ((caller_perms().wizard || (caller == this)) || this:can_touch(caller_perms()))
"Size_quota[1] is the total quota permitted.";
return args[1].size_quota[1] = args[2];
else
return E_PERM;
endif
.
#80:14
"Return args[1]'s quotas.  second arg of 1 means add all second chars.";
{who, ?all = 0} = args;
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;
.
#80:15
{who, quota, usage, timestamp, unmeasured, unmeasurable} = args;
player:tell(who.name, " has a total building quota of ", $string_utils:group_number(quota), " bytes.");
player:tell($gender_utils:get_pronoun("P", who), " total usage was ", $string_utils:group_number(usage), " as of ", player:ctime(timestamp), ".");
if (usage > quota)
player:tell(who.name, " is over quota by ", $string_utils:group_number(usage - quota), " bytes.");
else
player:tell(who.name, " may create up to ", $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
.
#80:16
"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
.
#80:17
"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
.
#80:18
return value_bytes(args[1]);
set_task_perms(caller_perms());
v = args[1];
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
.
#80:19
"No need for lengthy algorithms to measure an object, we have a builtin now. Ho_Yan 10/31/96";
set_task_perms($wiz_utils:random_wizard());
o = args[1];
if (((($object_utils:has_property(o, "object_size") && (o.object_size[1] > this.too_large)) && (!caller_perms().wizard)) && (caller_perms() != this.owner)) && (caller_perms() != $hacker))
return o.object_size[1];
endif
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"))
"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
if (b > this.too_large)
this.large_objects = setadd(this.large_objects, o);
endif
return b;
.
#80:20
who = args[1];
results = this:summarize_one_user(who);
{total, nuncounted, nzeros, oldest, eldest} = results;
player:tell(who.name, " statistics:");
player:tell("  ", $string_utils:group_number(total), " bytes of storage measured.");
player:tell("  Oldest measurement date ", ctime(oldest), " (", $string_utils:from_seconds(time() - oldest), " ago) of object ", eldest, " (", valid(eldest) ? eldest.name | "$nothing", ")");
if (nzeros || nuncounted)
player:tell("  Number of objects with no statistics recorded:  ");
player:tell("      ", nzeros, " recently created, ", nuncounted, " not descendents of #1");
endif
.
#80:21
"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;
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
if (size >= 0)
total = total + size;
endif
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};
.
#80:22
":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, ?since = this.cycle_days} = args;
if (!valid(object))
return 0;
elseif (`object.object_size[2] ! ANY => 0' > (time() - (((since * 24) * 60) * 60)))
"Trap error when doesn't have .object_size for some oddball reason ($garbage). Ho_Yan 11/19/96";
return object.object_size[1];
else
return this:object_bytes(object);
endif
.
#80:23
if (!caller_perms().wizard)
return E_PERM;
else
start_time = time();
{num_processed, num_repetitions} = this:measurement_task_body(args[1]);
players = players();
lengthp = length(players);
if ((!num_repetitions) && (num_processed < (lengthp / 2)))
"Add this in because we aren't getting people summarized like we should.  We're going to work for way longer now, cuz we're going to do a second pass, but we really need to get those summaries done.  Only do this if we hardly did any work.  Note the -1 here: measure all newly created objects as well.  More work, sigh.";
extra_end = time() + (3600 * 3);
for x in (players)
if (is_player(x) && (time() < extra_end))
"Robustness as above, plus don't run all day.  My kingdom for a break statement";
this:summarize_one_user(x, -1);
endif
$command_utils:suspend_if_needed(0);
endfor
endif
$mail_agent:send_message(player, 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 + (lengthp * num_repetitions), " players in this run in ", num_repetitions, " time", (num_repetitions == 1) ? "" | "s", " through all players.  Total time spent:  ", $time_utils:dhms(time() - start_time), ".")});
endif
.
#80:24
return (args[1] == this.owner) || $perm_utils:controls(args[1], args[2]);
.
#80:25
return args[1].wizard;
.
#80:26
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 = 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 = (value_bytes(verb_code(dobj, x, 0, 0)) + length(vname)) + 1;
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;
.
#80:27
object = args[1];
return ((13 * 4) + length(object.name)) + 1;
.
#80:28
{o, ?ps = $object_utils:all_properties_suspended(o)} = args;
return (value_bytes(properties(o)) - 4) + ((length(ps) * 4) * 4);
.
#80:29
o = args[1];
vs = verbs(o);
return (length(vs) * 5) * 4;
.
#80:30
":add_owned_object(who, what) -- adds what to whose .owned_objects.";
{who, what} = args;
if ((typeof(who.owned_objects) == LIST) && (what.owner == who))
who.owned_objects = setadd(who.owned_objects, what);
endif
.
#80:31
"This is a one-shot run of the measurement task, as opposed to :measurement_task, which will fork once per day.";
if (!caller_perms().wizard)
return E_PERM;
else
{num_processed, num_repetitions} = this:measurement_task_body();
$mail_agent:send_message(player, player, "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 + (lengthp * num_repetitions), " players in this run in ", num_repetitions, " times through all players.")});
endif
.
#80:32
if (!caller_perms().wizard)
return E_PERM;
else
num_processed = 0;
num_repetitions = 0;
usage_index = 2;
time_index = 3;
unmeasured_index = 4;
players = setremove(players(), $hacker);
lengthp = length(players);
index = this.working in players;
keep_going = 1;
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
day = (60 * 60) * 24;
stop = time() + args[1];
early = time() - (day * this.cycle_days);
tooidle = day * this.cycle_days;
"tooidletime is only used if !this.repeat_cycle.";
tooidletime = time() - tooidle;
local_per_player_hack = $object_utils:has_verb($local, "per_player_daily_scan");
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...";
if ((!this.repeat_cycle) || ((who.last_disconnect_time > tooidletime) && (who.last_disconnect_time != $maxint)))
"only measure people who login regularly if we're a big moo.";
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)
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"))
who.size_quota[usage_index] = usage;
who.size_quota[unmeasured_index] = this.unmeasured_multiplier * unmeasured;
who.size_quota[time_index] = earliest;
else
$mail_agent:send_message(player, player, "Quota Violation", {tostr(who, " has a clear .size_quota property."), $string_utils:names_of({who, @$object_utils:ancestors(who)})});
endif
elseif (who.size_quota[unmeasured_index])
"If they managed to create an object *despite* being too idle (presumably programmatically), measure it.";
this:summarize_one_user(who, -1);
endif
elseif (is_player(who))
"They don't have a size_quota property.  Whine.";
$mail_agent:send_message(player, player, "Quota Violation", {tostr(who, " doesn't seem to have a .size_quota property."), $string_utils:names_of({who, @$object_utils:ancestors(who)})});
endif
if (local_per_player_hack)
$local:per_player_daily_scan(who);
endif
if (index >= lengthp)
index = 1;
else
index = index + 1;
endif
num_processed = num_processed + 1;
if (num_processed > lengthp)
if (this.repeat_cycle)
"If we've gotten everyone up to threshold, try measuring some later than that.";
early = early + ((24 * 60) * 60);
tooidle = tooidle * 4;
tooidletime = tooidletime - tooidle;
num_repetitions = num_repetitions + 1;
num_processed = 0;
if (early > time())
"Don't spin our wheels when we've measured everything!";
keep_going = 0;
endif
else
keep_going = 0;
endif
endif
this.working = players[index];
endwhile
return {num_processed, num_repetitions};
endif
.
#80:33
if ((caller == this) || caller_perms().wizard)
day = 24 * 3600;
hour_of_day_GMT = 8;
fork ((((hour_of_day_GMT * 60) * 60) + day) - (time() % day))
this:schedule_measurement_task();
this:measurement_task(this.task_time_limit);
endfork
endif
.
#80:34
"Put all your wizards in $byte_quota_utils.wizards.  Then various long-running tasks will cycle among the permissions, spreading out the scheduler-induced personal lag.";
$wiz_utils.old_task_perms_user = setadd($wiz_utils.old_task_perms_user, caller);
return $wiz_utils:random_wizard();
.
#80:35
"this:property_exists(object, property)";
" => does the specified property exist?";
return !(!`property_info(@args) ! ANY');
.
#81:0
"*Must* be called with PDATA first, and LINES second.";
if ((caller != this) && (!caller_perms().wizard))
return E_PERM;
else
try
this.(args[2]);
except (E_PROPNF)
add_property(this, args[2], {}, {$hacker, ""});
endtry
try
this.(args[3]);
except (E_PROPNF)
add_property(this, args[3], 5, {$hacker, ""});
endtry
endif
.
#81:1
if (!caller_perms().wizard)
return;
else
for x in (properties(this))
delete_property(this, x);
$command_utils:suspend_if_needed(0);
endfor
endif
.
#81:2
{who, newdata} = args;
if ($perm_utils:controls(caller_perms(), who))
d = tostr(who, "pdata");
l = tostr(who, "lines");
this:ensure_props_exist(who, d, l);
data = this.(d);
lines = this.(l);
"Icky G7 code copied straight out of $player:tell.";
if (((len = length(this.(d) = {@data, newdata})) * 2) > (lines * 3))
this.(d) = this.(d)[(len - lines) + 1..len];
endif
else
return E_PERM;
endif
.
#81:3
who = args[1];
if ($perm_utils:controls(caller_perms(), who))
d = tostr(who, "pdata");
if (typeof(`this.(d) ! ANY') == LIST)
return this.(d);
else
return {};
endif
else
return E_PERM;
endif
.
#81:4
who = args[1];
if ($perm_utils:controls(caller_perms(), who))
d = tostr(who, "pdata");
"OK if this would toss its cookies if no prop, no damage.";
`this.(d) = {} ! ANY';
else
return E_PERM;
endif
.
#81:5
maximum = 20;
who = args[1];
if ($perm_utils:controls(caller_perms(), who))
l = tostr(who, "lines");
this:ensure_props_exist(who, l, l);
this.(l) = min(args[2], maximum);
else
return E_PERM;
endif
.
#81:6
if (((caller_perms() != #-1) && (caller_perms() != player)) || (!player.wizard))
$error:raise(E_PERM);
endif
threshold = ((60 * 60) * 24) * 3;
for x in (properties(this))
l = length(x);
who = toobj(x[1..l - 5]);
if (((!valid(who)) || (!is_player(who))) || (!this:is_paranoid(who)))
delete_property(this, x);
else
if (index(x, "lines"))
if (typeof(this.(x)) != INT)
this.(x) = 10;
endif
elseif (index(x, "pdata"))
if ((who.last_disconnect_time < (time() - threshold)) && (who.last_connect_time < (time() - threshold)))
this.(x) = {};
endif
if (typeof(this.(x)) != LIST)
this.(x) = {};
endif
endif
endif
$command_utils:suspend_if_needed(0);
endfor
.
#81:7
return this:description();
.
#81:8
if (!caller_perms().wizard)
return E_PERM;
else
week = (7 * 24) * 3600;
fork ((((7 * 60) * 60) + week) - (time() % week))
this:weekly();
endfork
this:gc();
endif
.
#81:9
"Some people make their .paranoid !r.  Wizardly verb to retrieve value.";
return `args[1].paranoid ! ANY';
.
#82:0
if (!caller_perms().wizard)
return E_PERM;
else
args[1].ownership_quota = $wiz_utils.default_player_quota;
endif
.
#82:1
if (!caller_perms().wizard)
return E_PERM;
else
"Switched to byte-based-quota and commented out this line - Jan";
"$quota_utils = this;";
endif
"Last modified Mon Jul 21 09:48:21 1997 CDT by Wizard (#2).";
.
#82:2
if (!caller_perms().wizard)
return E_PERM;
else
victim = args[1];
oldquota = victim.ownership_quota;
if ($object_utils:has_property($local, "second_char_registry") && $local.second_char_registry:is_second_char(victim))
"don't increment quota for 2nd chars when programmering";
victim.ownership_quota = oldquota;
else
victim.ownership_quota = oldquota + ($wiz_utils.default_programmer_quota - $wiz_utils.default_player_quota);
endif
endif
.
#82:3
"Calls built-in create.";
set_task_perms(caller_perms());
return `create(@args) ! ANY';
.
#82:4
$recycler:check_quota_scam(args[1]);
return args[1].ownership_quota > 0;
.
#82:5
return 1;
.
#82:6
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
.
#82:7
if ($perm_utils:controls(caller_perms(), args[1]) || (caller == this))
return args[1].ownership_quota;
else
return E_PERM;
endif
.
#82:8
"Charge args[1] for the quota required to own args[2]";
{who, what} = args;
if ((caller == this) || caller_perms().wizard)
who.ownership_quota = who.ownership_quota - 1;
else
return E_PERM;
endif
.
#82:9
"Reimburse args[1] for the quota required to own args[2]";
{who, what} = args;
if ((caller == this) || caller_perms().wizard)
who.ownership_quota = who.ownership_quota + 1;
else
return E_PERM;
endif
.
#82:10
"Set args[1]'s quota to args[2]";
{who, quota} = args;
if (caller_perms().wizard || (caller == this))
return who.ownership_quota = quota;
else
return E_PERM;
endif
.
#82:11
return 0;
.
#82:12
"Is args[1] permitted to examine args[2]'s quota information?";
return $perm_utils:controls(args[1], args[2]);
.
#82:13
"Is args[1] permitted to examine args[2]'s quota information?";
return args[1].wizard;
.
#83:0
output = {"On $server_options, the following settings have been established by the wizards:", ""};
wizonly = {};
etc = {};
mentioned = {};
for x in (setremove(properties(this), "help_msg"))
if (index(x, "protect_") == 1)
mentioned = {@mentioned, x[9..$]};
wizonly = {@wizonly, tostr(x[9..$], "() is ", this.(x) ? "" | "not ", "wizonly.")};
else
etc = {@etc, tostr("$server_options.", x, " = ", $string_utils:print(this.(x)))};
endif
endfor
if ("set_verb_code" in wizonly)
wizonly = {@wizonly, "", "Note: since the 'set_verb_code' built-in function is wiz-only, then the '.program' built-in command is wiz-only too."};
endif
if (bf = $set_utils:intersection(verbs(#0), mentioned))
bf = $list_utils:sort(bf);
etc = {@etc, "", "In your code, #0:(built-in)(@args) should be called rather than built-in(@args) when you would use one of the following built-in functions:", $string_utils:english_list(bf) + ".", ((("Example: #0:" + bf[1]) + "(@args) should be used instead of ") + bf[1]) + "(@args)"};
endif
return {@this.help_msg, @output, @wizonly, "", @etc};
.
#83:1
if (!caller_perms().wizard)
raise(E_PERM);
endif
this.support_numeric_verbname_strings = 0;
.
#84:0
"Copied from Features Feature Object (#24300):list by Joe (#2612) Mon Oct 10 21:07:35 1994 PDT";
if (this.contents)
player:tell(".features objects:");
player:tell("----------------------");
first = 1;
for thing in (this.contents)
$command_utils:kill_if_laggy(10, "Sorry, the MOO is very laggy, and there are too many feature objects in here to list!");
$command_utils:suspend_if_needed(0);
if (!first)
player:tell();
endif
player:tell(thing.name, ":");
thing:look_self();
first = 0;
endfor
player:tell("----------------------");
else
player:tell("No objects in ", this.name, ".");
endif
.
#85:0
if (!caller_perms().wizard)
return E_PERM;
else
this.("@quota") = {"*forward*", "object-quota"};
endif
.
#87:0
if (!this:trusted(caller_perms()))
return E_PERM;
endif
{host, ?user = "", ?pass = ""} = args;
if (typeof(conn = $network:open(host, this.port)) == ERR)
return {"Unable to connect to host."};
endif
this.connections = {@this.connections, {conn, caller_perms(), {}, 0, {}}};
if (((!this:wait_for_response(conn)) || (user && (!this:do_command(conn, "USER " + user)))) || (pass && (!this:do_command(conn, "PASS " + pass))))
messages = this:get_messages(conn);
this.connections = listdelete(this.connections, $list_utils:iassoc(conn, this.connections));
$network:close(conn);
return messages;
endif
return conn;
.
#87:1
conn = args[1];
if (!this:controls(caller_perms(), conn))
return E_PERM;
endif
this:do_command(conn, "QUIT");
info = $list_utils:assoc(conn, this.connections);
this.connections = setremove(this.connections, info);
$network:close(conn);
if ($network:is_open(info[4]))
$network:close(info[4]);
endif
return info[3];
.
#87:2
{conn, cmd, ?nowait = 0} = args;
if (!this:controls(caller_perms(), conn))
return E_PERM;
endif
$network:notify(conn, cmd);
return nowait ? 1 | this:wait_for_response(conn);
.
#87:3
{conn, ?first_only = 0} = args;
if (!this:controls(caller_perms(), conn))
return E_PERM;
endif
matchstr = first_only ? "^[1-9][0-9][0-9] " | "^[2-9][0-9][0-9] ";
messages = {};
result = "";
while ((typeof(result) == STR) && (!match(result, matchstr)))
result = $network:read(conn);
messages = {@messages, result};
endwhile
i = $list_utils:iassoc(conn, this.connections);
this.connections[i][3] = {@this.connections[i][3], @messages};
if (typeof(result) == STR)
if (result[1] in {"4", "5"})
player:tell(result);
return E_NONE;
else
return 1;
endif
else
return result;
endif
.
#87:4
return args[1].wizard || ({@$list_utils:assoc(args[2], this.connections), 0, 0}[2] == args[1]);
.
#87:5
{conn, ?keep = 0} = args;
if (!this:controls(caller_perms(), conn))
return E_PERM;
endif
i = $list_utils:iassoc(conn, this.connections);
messages = this.connections[i][3];
if (!keep)
this.connections[i][3] = {};
endif
return messages;
.
#87:6
conn = args[1];
if (!this:controls(caller_perms(), conn))
return E_PERM;
endif
i = $list_utils:iassoc(conn, this.connections);
if (!$network:is_open(this.connections[i][4]))
this:do_command(conn, "PASV");
msg = (msg = this:get_messages(conn, 1))[$];
if (msg[1..3] != "227")
return E_TYPE;
elseif (!(match = match(msg, "(%([0-9]+%),%([0-9]+%),%([0-9]+%),%([0-9]+%),%([0-9]+%),%([0-9]+%))")))
return E_TYPE;
elseif (typeof(dconn = $network:open(substitute("%1.%2.%3.%4", match), (toint(substitute("%5", match)) * 256) + toint(substitute("%6", match)))) == ERR)
return dconn;
else
this.connections[i][4] = dconn;
endif
this.connections[i][5] = E_INVARG;
set_task_perms(caller_perms());
fork (0)
this:listen(conn, dconn);
endfork
endif
return 1;
.
#87:7
{conn, ?nowait = 0} = args;
if (!this:controls(caller_perms(), conn))
return E_PERM;
endif
i = $list_utils:iassoc(conn, this.connections);
while ((!nowait) && (this.connections[i][5] == E_INVARG))
suspend(0);
endwhile
return this.connections[i][5];
.
#87:8
{conn, data} = args;
if (!this:controls(caller_perms(), conn))
return E_PERM;
endif
i = $list_utils:iassoc(conn, this.connections);
dconn = this.connections[i][4];
if (!$network:is_open(dconn))
return E_INVARG;
else
for line in (data)
notify(dconn, line);
$command_utils:suspend_if_needed(0);
endfor
this:close_data(conn);
this.connections[i][4] = 0;
endif
.
#87:9
return args[1].wizard || ((typeof(this.trusted) == LIST) ? args[1] in this.trusted | this.trusted);
.
#87:10
if (caller != this)
return E_PERM;
endif
{conn, dconn} = args;
data = {};
line = `read(dconn) ! ANY';
while (typeof(line) == STR)
data = {@data, line};
line = read(dconn);
$command_utils:suspend_if_needed(0);
endwhile
if (i = $list_utils:iassoc(conn, this.connections))
this.connections[i][5] = data;
endif
.
#87:11
conn = args[1];
if (!this:controls(caller_perms(), conn))
return E_PERM;
endif
if (!$network:is_open(dconn = $list_utils:assoc(conn, this.connections)[4]))
return E_INVARG;
else
$network:close(dconn);
"...let the reading task come to terms with its abrupt superfluousness...";
suspend(0);
return 1;
endif
.
#87:12
":get(host, username, password, filename)";
if (!this:trusted(caller_perms()))
return E_PERM;
endif
if (typeof(conn = this:open(@args[1..3])) != OBJ)
return E_NACC;
else
result = (this:open_data(conn) && this:do_command(conn, "RETR " + args[4])) && this:get_data(conn);
this:close(conn);
return result;
endif
.
#87:13
if (caller_perms().wizard)
this.connections = {};
this.trusted = 1;
pass(@args);
endif
.
#87:14
":put(host, username, password, filename, data)";
if (!this:trusted(caller_perms()))
return E_PERM;
endif
if (typeof(conn = this:open(@args[1..3])) != OBJ)
return E_NACC;
else
result = (this:open_data(conn) && this:do_command(conn, "STOR " + args[4], 1)) && this:put_data(conn, args[5]);
this:close(conn);
return result;
endif
.
#87:15
"return the data connection associated with the control connection args[1]";
conn = args[1];
i = $list_utils:iassoc(conn, this.connections);
return this.connections[i][4];
.
#88:0
if (typeof(base = this.(verb)) == STR)
base = {base};
endif
base = {@base, "", tostr(".minimum_password_length = ", toliteral(x = this.minimum_password_length)), x ? tostr("Passwords are required to be a minimum of ", $string_utils:english_number(x), " characters in length.") | "There is no minimum length requirement for passwords."};
base = {@base, "", tostr(".check_against_moo = ", toliteral(x = this.check_against_moo)), tostr("Passwords ", x ? "may not" | "may", " be variants on the MOO's name (", $network.MOO_name, ").")};
base = {@base, "", tostr(".check_against_name = ", toliteral(x = this.check_against_name)), tostr("Passwords ", x ? "may not" | "may", " be variants on the player's MOO name and/or aliases.")};
base = {@base, "", tostr(".check_against_email = ", toliteral(x = this.check_against_email)), x ? "Passwords may not be variants on the player's email address." | "Passwords are not checked against the player's email address."};
base = {@base, "", tostr(".check_against_hosts = ", toliteral(x = this.check_against_hosts)), x ? "Passwords may not be variants on the player's hostname(s)." | "Passwords are not checked against the player's hostname(s)."};
base = {@base, "", tostr(".check_against_dictionary = ", toliteral(x = this.check_against_dictionary)), tostr("Passwords ", (typeof(x) in {LIST, OBJ}) ? "may not" | "may", " be dictionary words.", (x && (!$network.active)) ? "  (This option is set but unavailable.)" | "")};
base = {@base, "", tostr(".require_funky_characters = ", toliteral(x = this.require_funky_characters)), tostr("Non-alphabetic characters are ", x ? "" | "not ", "required in passwords.")};
base = {@base, "", tostr(".check_obscure_stuff = ", toliteral(x = this.check_obscure_stuff)), x ? "Misc. obscure checks enabled" | "No obscure checks in use."};
return base;
.
#88:1
":reject_password ( STR password [ , OBJ for-whom ] );";
"=> string value [if the password is rejected, why?]";
"=> false value [if the password isn't rejected]";
if (length(args) == 1)
trust = 0;
else
if ($perm_utils:controls(caller_perms(), args[2]))
trust = 1;
else
return "Permissions don't permit setting of that password.";
endif
endif
"this is gonna be huge";
return (((((((this:trivial_check(@args) || (this.minimum_password_length && this:check_length(@args))) || ((this.check_against_name && trust) && this:check_name(@args))) || ((this.check_against_email && trust) && this:check_email(@args))) || ((this.check_against_hosts && trust) && this:check_hosts(@args))) || ((typeof(this.check_against_dictionary) in {LIST, OBJ}) && this:check_dictionary(@args))) || (this.require_funky_characters && this:check_for_funky_characters(@args))) || (this.check_against_moo && this:check_against_moo(@args))) || (this.check_obscure_stuff && this:check_obscure_combinations(@args));
.
#88:2
if (typeof(pwd = args[1]) != STR)
return "Passwords must be strings.";
elseif (index(pwd, " "))
return "Passwords may not contain spaces.";
elseif (length(args) == 2)
if (((typeof(who = args[2]) != OBJ) || (!valid(who))) || (!is_player(who)))
return "That's not a player.";
elseif (!$perm_utils:controls(caller_perms(), who))
return "You can't set the password for that player.";
elseif ($object_utils:isa(who, $guest))
return "Sorry, but guest characters are not allowed to change their passwords.";
endif
endif
.
#88:3
if ((l = this.minimum_password_length) && (length(args[1]) < l))
return tostr("Passwords must be a minimum of ", $string_utils:english_number(l), (l == 1) ? " character " | " characters ", "long.");
endif
.
#88:4
pwd = args[1];
if (valid($player_db:find_exact(pwd)))
return "Passwords may not be close to a player's name/alias pair.";
elseif (valid($player_db:find($string_utils:reverse(pwd))))
return "Passwords ought not be the reverse of a player's name/alias.";
endif
.
#88:5
{pwd, who} = args;
if (!$perm_utils:controls(caller_perms(), who))
return "Permission denied.";
endif
email = who.email_address;
if (!email)
"can't check";
return;
endif
if (index(email, pwd))
return "Passwords can't match your registered email address.";
endif
.
#88:6
{pwd, who} = args;
if (!$perm_utils:controls(caller_perms(), who))
return "Permission denied.";
endif
hosts = who.all_connect_places;
for x in (hosts)
if (index(x, pwd))
return "Passwords may not match hostnames.";
endif
endfor
.
#88:7
pwd = args[1];
if ((typeof(dict = this.check_against_dictionary) == LIST) && $network.active)
"assume we're checking an on-line dictionary";
dict[3] = dict[3] + pwd;
result = $gopher:get(@dict);
if (typeof(result) == ERR)
"we probably can't check the dictionary anyway";
return;
elseif ((result[1] && (result[1][1] != "0")) && (!this:_is_funky_case(pwd)))
return "Dictionary words are not permitted for passwords.";
endif
elseif (typeof(dict) == OBJ)
"assume we're checking mr spell";
try
if (dict:find_exact(pwd) && (!this:_is_funky_case(pwd)))
return "Dictionary words are not permitted for passwords.";
endif
except (ANY)
"in case this is messed up. Just let it go and return 0;";
endtry
endif
.
#88:8
if (this:_is_funky_case(pwd = args[1]))
return;
endif
alphabet = $string_utils.alphabet;
for i in [1..length(pwd)]
if (!index(alphabet, pwd[i]))
return;
endif
endfor
return "At least one unusual capitalization and/or numeric or punctuation character is required.";
.
#88:9
pwd = args[1];
moo = $network.MOO_Name;
if (this:_is_funky_case(pwd))
return;
endif
if (pwd == moo)
return "The MOO's name is not secure as a password.";
endif
if (moo[$ - 2..$] == "MOO")
if (pwd == moo[1..$ - 3])
return "The MOO's name is not secure as a password.";
endif
endif
.
#88:10
pwd = args[1];
if (!strcmp(pwd, u = $string_utils:uppercase(pwd)))
return 0;
elseif (!strcmp(pwd, l = $string_utils:lowercase(pwd)))
return 0;
elseif (!strcmp(pwd, tostr(u[1], l[2..$])))
return 0;
else
return 1;
endif
.
#88:11
pwd = args[1];
if (match(pwd, "^[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]$"))
return "Social security numbers are potentially insecure passwords.";
elseif (match(pwd, "^[0-9]+/[0-9]+/[0-9]+$"))
return "Passwords which look like dates are potentially insecure passwords.";
endif
.
#88:12
if (caller_perms().wizard)
pass(@args);
this.minimum_password_length = this.check_against_name = this.check_against_email = this.check_against_hosts = this.check_against_dictionary = this.require_funky_characters = this.check_against_moo = this.check_obscure_stuff = 0;
endif
.
#89:0
":receive_message";
" -- this is a lame hack to take advantage of the *New-Prog-Log parentage";
"    apologies are due to Rog for abuse of code";
if (!this:is_writable_by(caller_perms()))
return E_PERM;
else
args[1][4] = strsub(args[1][4], "@newpassword", "@programmer");
return pass(@args);
endif
.
#89:1
":to_msg_seq()";
" -- this is a lame hack to take advantage of the *New-Prog-Log parentage";
"    apologies are due to Rog for abuse of code";
if (!this:ok(caller, caller_perms()))
return E_PERM;
else
base = pass(@args);
if (typeof(base) == STR)
base = strsub(base, "@programmer", "@newpassword");
endif
return base;
endif
.
#89:2
if (caller_perms().wizard)
pass();
this.mail_notify = {#2};
this.mail_forward = {};
endif
.
#90:0
"'@rooms' - List the rooms which are known by name.";
line = "";
for item in (this.rooms)
line = (((line + item[1]) + "(") + tostr(item[2])) + ")   ";
endfor
player:tell(line);
.
#90:1
"Return a string giving the names of the objects in a list. Now on $string_utils";
return $string_utils:names_of(@args);
.
#90:2
"Look up a room in your personal database of room names, returning its object number. If it's not in your database, it checks to see if it's a number or a nearby object.";
room = args[1];
if (room == "home")
return player.home;
elseif (room == "me")
return player;
elseif (room == "here")
return player.location;
elseif (!room)
return $failed_match;
endif
index = this:index_room(room);
if (index)
return this.rooms[index][2];
else
return this:my_match_object(room);
"old code no longer used, 2/11/96 Heathcliff";
source = player.location;
if (!(valid(source) && ($room in $object_utils:ancestors(source))))
source = $room;
endif
return source:match_object(room);
endif
.
#90:3
"Teleport a player or object. For printing messages, there are three cases: (1) teleport self (2) teleport other player (3) teleport object. There's a spot of complexity for handling the invalid location #-1.";
{thing, dest} = args;
source = thing.location;
if (valid(dest))
dest_name = dest.name;
else
dest_name = tostr(dest);
endif
if (source == dest)
player:tell(thing.name, " is already at ", dest_name, ".");
return;
endif
thing:moveto(dest);
if (thing.location == dest)
tsd = {thing, source, dest};
if (thing == player)
this:teleport_messages(@tsd, this:self_port_msg(@tsd), this:oself_port_msg(@tsd), this:self_arrive_msg(@tsd), "");
elseif (is_player(thing))
this:teleport_messages(@tsd, this:player_port_msg(@tsd), this:oplayer_port_msg(@tsd), this:player_arrive_msg(@tsd), this:victim_port_msg(@tsd));
else
this:teleport_messages(@tsd, this:thing_port_msg(@tsd), this:othing_port_msg(@tsd), this:thing_arrive_msg(@tsd), this:object_port_msg(@tsd));
endif
elseif (thing.location == source)
if ($object_utils:contains(thing, dest))
player:tell("Ooh, it's all twisty. ", dest_name, " is inside ", thing.name, ".");
else
if ($object_utils:has_property(thing, "po"))
pronoun = thing.po;
else
pronoun = "it";
endif
player:tell("Either ", thing.name, " doesn't want to go, or ", dest_name, " didn't accept ", pronoun, ".");
endif
else
thing_name = (thing == player) ? "you" | thing.name;
player:tell("A strange force deflects ", thing_name, " from the destination.");
endif
.
#90:4
"Send teleport messages. There's a slight complication in that the source and dest need not be valid objects.";
{thing, source, dest, pmsg, smsg, dmsg, tmsg} = args;
if (pmsg)
"The player's own message.";
player:tell(pmsg);
endif
if (smsg && valid(source))
"A message to the victim's original location, if it is a room.";
if ($object_utils:has_callable_verb(source, "announce_all_but"))
source:announce_all_but({thing, player}, smsg);
endif
endif
if (dmsg && valid(dest))
"A message to the destination, if it is a room.";
if ($object_utils:has_callable_verb(dest, "announce_all_but"))
dest:announce_all_but({thing, player}, dmsg);
endif
endif
if (tmsg)
"A message to the victim being teleported.";
thing:tell(tmsg);
endif
.
#90:5
"'@move <object> to <place>' - Teleport an object. Example: '@move trash to #11' to move trash to the closet.";
here = player.location;
if ((prepstr != "to") || (!iobjstr))
player:tell("Usage: @move <object> to <location>");
return;
endif
if ((!dobjstr) || (dobjstr == "me"))
thing = this;
else
if (valid(here) && $object_utils:has_callable_verb(here, "match_object"))
thing = here:match_object(dobjstr);
endif
if (thing == $failed_match)
thing = player:my_match_object(dobjstr);
endif
endif
if ($command_utils:object_match_failed(thing, dobjstr))
return;
endif
if ((!player.programmer) && ((thing.owner != player) && (thing != player)))
player:tell("You can only move your own objects.");
return;
endif
dest = this:lookup_room(iobjstr);
if ((dest == #-1) || (!$command_utils:object_match_failed(dest, iobjstr)))
this:teleport(thing, dest);
endif
.
#90:6
"'index_room (<room name>)' - Look up a room in your personal database of room names, returning its index in the list. Return 0 if it is not in the list. If the room name is the empty string, then only exact matches are considered; otherwise, a leading match is good enough.";
room = tostr(args[1]);
size = length(room);
index = 1;
match = 0;
for item in (this.rooms)
item_name = item[1];
if (room == item_name)
return index;
elseif ((size && (length(item_name) >= size)) && (room == item_name[1..size]))
match = index;
endif
index = index + 1;
endfor
return match;
.
#90:7
"'@addroom <name> <object>', '@addroom <object> <name>', '@addroom <name>', '@addroom <object>', '@addroom' - Add a room to your personal database of teleport destinations. Example: '@addroom Kitchen #24'. Reasonable <object>s are numbers (#17) and 'here'. If you leave out <object>, the object is the current room. If you leave out <name>, the name is the specified room's name. If you leave out both, you get the current room and its name.";
if (((!caller) && (player != this)) || (caller && (callers()[1][3] != this)))
if (!caller)
player:tell(E_PERM);
endif
return E_PERM;
endif
if (!dobjstr)
object = this.location;
name = valid(object) ? object.name | "Nowhere";
elseif (command = this:parse_out_object(dobjstr))
name = command[1];
object = command[2];
else
name = dobjstr;
object = this.location;
endif
if (!valid(object))
player:tell("This is not a valid location.");
return E_INVARG;
endif
player:tell("Adding ", name, "(", tostr(object), ") to your database of rooms.");
this.rooms = {@this.rooms, {name, object}};
.
#90:8
"'@rmroom <roomname>' - Remove a room from your personal database of teleport destinations. Example: '@rmroom library'.";
if (((!caller) && (player != this)) || (caller && (callers()[1][3] != this)))
if (!caller)
player:tell(E_PERM);
endif
return E_PERM;
endif
index = this:index_room(dobjstr);
if (index)
player:tell("Removing ", this.rooms[index][1], "(", this.rooms[index][2], ").");
this.rooms = listdelete(this.rooms, index);
else
player:tell("That room is not in your database of rooms. Check '@rooms'.");
endif
.
#90:9
"'@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..$];
this:find_verb(name);
return;
elseif (dobjstr[1] == ".")
name = dobjstr[2..$];
this:find_property(name);
return;
elseif (dobjstr[1] == "#")
target = toobj(dobjstr);
if (!valid(target))
player:tell(target, " does not exist.");
endif
else
target = $string_utils:match_player(dobjstr);
$command_utils:player_match_result(target, dobjstr);
endif
if (valid(target))
player:tell(target.name, " (", target, ") is at ", valid(target.location) ? target.location.name | "Nowhere", " (", target.location, ").");
endif
.
#90:10
"'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
.
#90:11
"'@ways', '@ways <room>' - List any obvious exits from the given room (or this room, if none is given).";
if (dobjstr)
room = dobj;
else
room = this.location;
endif
if ((!valid(room)) || (!($room in $object_utils:ancestors(room))))
player:tell("You can only pry into the exits of a room.");
return;
endif
exits = {};
if ($object_utils:has_verb(room, "obvious_exits"))
exits = room:obvious_exits();
endif
exits = this:checkexits(this:obvious_exits(), room, exits);
exits = this:findexits(room, exits);
this:tell_ways(exits, room);
.
#90:12
"Add to the 'exits' list any exits in the room which have a single-letter alias.";
{room, exits} = args;
alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
for i in [1..length(alphabet)]
found = room:match_exit(alphabet[i]);
if (valid(found) && (!(found in exits)))
exits = {@exits, found};
endif
endfor
return exits;
.
#90:13
"Check a list of exits to see if any of them are in the given room.";
{to_check, room, exits} = args;
for word in (to_check)
found = room:match_exit(word);
if (valid(found) && (!(found in exits)))
exits = {@exits, found};
endif
endfor
return exits;
.
#90:14
"This verb returns messages that go only to you. You don't need to have your name tacked on to the beginning of these. Heh.";
msg = this.(verb);
if (msg && (length(args) >= 3))
msg = this:msg_sub(msg, @args);
endif
return msg;
.
#90:15
"This verb returns messages that go to other players. It does pronoun substitutions; if your name is not included in the final string, it adds the name in front.";
msg = this.(verb);
if (msg && (length(args) >= 3))
msg = this:msg_sub(msg, @args);
endif
if (msg && (!$string_utils:index_delimited(msg, player.name)))
msg = (player.name + " ") + msg;
endif
return msg;
.
#90:16
"Do pronoun and other substitutions on the teleport messages. The arguments are: 1. The original message, before any substitutions; 2. object being teleported; 3. from location; 4. to location. The return value is the final message.";
{msg, thing, from, to} = args;
msg = $string_utils:substitute(msg, $string_utils:pronoun_quote({{"%<from room>", valid(from) ? from.name | "Nowhere"}, {"%<to room>", valid(to) ? to.name | "Nowhere"}}));
msg = $string_utils:pronoun_sub(msg, thing);
return msg;
.
#90:17
"'obvious_exits()' - Return a list of common exit names which are obviously worth looking for in a room.";
return {"n", "ne", "e", "se", "s", "sw", "w", "nw", "north", "northeast", "east", "southeast", "south", "southwest", "west", "northwest", "u", "d", "up", "down", "out", "exit", "leave", "enter"};
.
#90:18
":tell_ways (<list of exits>)' - Tell yourself a list of exits, for @ways. You can override it to print the exits in any format.";
exits = args[1];
answer = {};
for e in (exits)
answer = {@answer, ((e.name + " (") + $string_utils:english_list(e.aliases)) + ")"};
endfor
player:tell("Obvious exits: ", $string_utils:english_list(answer), ".");
.
#90:19
"Return the name and number of an object, e.g. 'Root Class (#1)'.";
o = args[1];
return (((valid(o) ? o.name | "Nothing") + " (") + tostr(o)) + ")";
.
#90:20
"'parse_out_object (<string>)' -> {<name>, <object>}, or 0. Given a string, attempt to find an object at its beginning or its end. An object can be either an object number, or 'here'. If this succeeds, return a list of the object and the unmatched part of the string, called the name. If it fails, return 0.";
words = $string_utils:words(args[1]);
if (!length(words))
return 0;
endif
word1 = words[1];
wordN = words[$];
if (length(word1) && (word1[1] == "#"))
start = 2;
finish = length(words);
what = toobj(word1);
elseif (word1 == "here")
start = 2;
finish = length(words);
what = this.location;
elseif (length(wordN) && (wordN[1] == "#"))
start = 1;
finish = length(words) - 1;
what = toobj(wordN);
elseif (wordN == "here")
start = 1;
finish = length(words) - 1;
what = this.location;
else
return 0;
endif
"toobj() has the nasty property that invalid strings get turned into #0. Here we just pretend that all references to #0 are actually meant for #-1.";
if (what == #0)
what = $nothing;
endif
name = $string_utils:from_list(words[start..finish], " ");
if (!name)
name = valid(what) ? what.name | "Nowhere";
endif
return {name, what};
.
#90:21
"'enlist (<x>)' - If x is a list, just return it; otherwise, return {x}. The purpose here is to turn message strings into lists, so that lines can be added. It is not guaranteed to work for non-string non-lists.";
x = args[1];
if (!x)
return {};
elseif (typeof(x) == LIST)
return x;
else
return {x};
endif
.
#90:22
"@spellproperties <object>";
"@spellmessages <object>";
"Spell checks the string properties of an object, or the subset of said properties which are suffixed _msg, respectively.";
set_task_perms(player);
if (!dobjstr)
player:notify(tostr("Usage: ", verb, " <object>"));
return;
elseif ($command_utils:object_match_failed(dobj = player:my_match_object(dobjstr), dobjstr))
return;
elseif (typeof(props = $object_utils:all_properties(dobj)) == ERR)
player:notify("Permission denied to read properties on that object.");
return;
endif
props = setremove(props, "messages");
if (verb[1..7] == "@spellm")
spell = {};
for prop in (props)
if ((index(prop, "_msg") == (length(prop) - 3)) && index(prop, "_msg"))
spell = {@spell, prop};
endif
endfor
props = spell;
endif
if (props == {})
player:notify(tostr("No ", (verb[1..7] == "@spellm") ? "messages" | "properties", " found to spellcheck on ", dobj, "."));
return;
endif
for data in (props)
if (typeof(dd = `dobj.(data) ! ANY') == LIST)
text = {};
for linenum in (dd)
text = listappend(text, linenum);
endfor
elseif ((((typeof(dd) == OBJ) || (typeof(dd) == INT)) || (typeof(dd) == ERR)) || (typeof(dd) == FLOAT))
text = "";
elseif (typeof(dd) == STR)
text = dd;
endif
if (typeof(text) == STR)
text = {text};
endif
linenumber = 0;
for thisline in (text)
$command_utils:suspend_if_needed(0);
linenumber = linenumber + 1;
if (((((typeof(thisline) != LIST) && (typeof(thisline) != OBJ)) && (typeof(thisline) != INT)) && (typeof(thisline) != FLOAT)) && (typeof(thisline) != ERR))
i = $string_utils:strip_chars(thisline, "!@#$%^&*()_+1234567890={}[]<>?:;,./|\"~'");
if (i)
i = $string_utils:words(i);
for ii in [1..length(i)]
$command_utils:suspend_if_needed(0);
if (!$spell:valid(i[ii]))
if ((rindex(i[ii], "s") == length(i[ii])) && $spell:valid(i[ii][1..$ - 1]))
msg = "Possible match: " + i[ii];
elseif ((rindex(i[ii], "'s") == (length(i[ii]) - 1)) && $spell:valid(i[ii][1..$ - 2]))
msg = "Possible match: " + i[ii];
else
msg = "Unknown word: " + i[ii];
endif
if (length(text) == 1)
foo = ": ";
else
foo = (" (line " + tostr(linenumber)) + "): ";
endif
player:notify(tostr(dobj, ".", data, foo, msg));
endif
endfor
endif
endif
endfor
endfor
player:notify(tostr("Done spellchecking ", dobj, "."));
.
#90:23
"'@at' - Find out where everyone is. '@at <player>' - Find out where <player> is, and who else is there. '@at <obj>' - Find out who else is at the same place as <obj>. '@at <place>' - Find out who is at the place. The place can be given by number, or it can be a name from your @rooms list. '@at #-1' - Find out who is at #-1. '@at me' - Find out who is in the room with you. '@at home' - Find out who is at your home.";
this:internal_at(argstr);
.
#90:24
"'at_players ()' - Return a list of players to be displayed by @at.";
return connected_players();
.
#90:25
"'do_at_all ()' - List where everyone is, sorted by popularity of location. This is called when you type '@at'.";
locations = {};
parties = {};
counts = {};
for who in (this:at_players())
loc = who.location;
if (i = loc in locations)
parties[i] = setadd(parties[i], who);
counts[i] = counts[i] - 1;
else
locations = {@locations, loc};
parties = {@parties, {who}};
counts = {@counts, 0};
endif
endfor
locations = $list_utils:sort(locations, counts);
parties = $list_utils:sort(parties, counts);
this:print_at_items(locations, parties);
.
#90:26
"'do_at (<location>)' - List the players at a given location.";
loc = args[1];
party = {};
for who in (this:at_players())
if (who.location == loc)
party = setadd(party, who);
endif
endfor
this:print_at_items({loc}, {party});
.
#90:27
"'print_at_items (<locations>, <parties>)' - Print a list of locations and people, for @at. Override this if you want to make a change to @at's output that you can't make in :at_item.";
{locations, parties} = args;
for i in [1..length(locations)]
$command_utils:suspend_if_needed(0);
player:tell_lines(this:at_item(locations[i], parties[i]));
endfor
.
#90:28
"'at_item (<location>, <party>)' - Given a location and a list of the people there, return a string displaying the information. Override this if you want to change the format of each line of @at's output.";
{loc, party} = args;
su = $string_utils;
if (this.at_number)
number = su:right(tostr(loc), 7) + " ";
else
number = "";
endif
room = su:left(valid(loc) ? loc.name | "[Nowhere]", this.at_room_width);
if (length(room) > this.at_room_width)
room = room[1..this.at_room_width];
endif
text = (number + room) + " ";
if (party)
filler = su:space(length(text) - 2);
line = text;
text = {};
for who in (party)
name = " " + (valid(who) ? who.name | "[Nobody]");
if ((length(line) + length(name)) > this:linelen())
text = {@text, line};
line = filler + name;
else
line = line + name;
endif
endfor
text = {@text, line};
else
text = text + " [deserted]";
endif
return text;
.
#90:29
"'internal_at (<argument string>)' - Perform the function of @at. The argument string is whatever the user typed after @at. This is factored out so that other verbs can call it.";
where = $string_utils:trim(args[1]);
if (where)
if (where[1] == "#")
result = toobj(where);
if ((!valid(result)) && (result != #-1))
player:tell("That object does not exist.");
return;
endif
else
result = this:lookup_room(where);
if (!valid(result))
result = $string_utils:match_player(where);
if (!valid(result))
player:tell("That is neither a player nor a room name.");
return;
endif
endif
endif
if (valid(result) && (!$object_utils:isa(result, $room)))
result = result.location;
endif
this:do_at(result);
else
this:do_at_all();
endif
.
#90:30
"'confunc ()' - Besides the inherited behavior, notify the player's feature objects that the player has connected.";
if ((valid(cp = caller_perms()) && (caller != this)) && (!$perm_utils:controls(cp, this)))
return E_PERM;
endif
pass(@args);
set_task_perms(this);
for feature in (this.features)
try
feature:player_connected(player, @args);
except (E_VERBNF)
continue feature;
except id (ANY)
player:tell("Feature initialization failure for ", feature, ": ", id[2], ".");
endtry
endfor
.
#90:31
"'disfunc ()' - Besides the inherited behavior, notify the player's feature objects that the player has disconnected.";
if ((valid(cp = caller_perms()) && (caller != this)) && (!$perm_utils:controls(cp, this)))
return E_PERM;
endif
pass(@args);
"This is forked off to protect :disfunc from buggy :player_disconnected verbs.";
set_task_perms(this);
fork (max(0, $login:current_lag()))
for feature in (this.features)
try
feature:player_disconnected(player, @args);
except (ANY)
continue feature;
endtry
endfor
endfork
.
#90:32
set_task_perms(player);
if ((verb == "@adddict") && (!((player in $spell.trusted) || player.wizard)))
player:tell("You may not add to the master dictionary. The following words will instead by put in a list of words to be approved for later addition to the dictionary. Thanks for your contribution.");
endif
if (!argstr)
player:notify(tostr("Usage: ", verb, " one or more words"));
player:notify(tostr("       ", verb, " object:verb"));
player:notify(tostr("       ", verb, " object.prop"));
elseif (data = $spell:get_input(argstr))
num_learned = 0;
for i in [1..length(data)]
line = $string_utils:words(data[i]);
for ii in [1..length(line)]
if (seconds_left() < 2)
suspend(0);
endif
if (verb == "@adddict")
result = $spell:add_word(line[ii]);
if (result == E_PERM)
if ($spell:find_exact(line[ii]) == $failed_match)
player:notify(tostr("Submitted for approval:  ", line[ii]));
$spell:submit(line[ii]);
else
player:notify(tostr("Already in dictionary:  " + line[ii]));
endif
elseif (typeof(result) == ERR)
player:notify(tostr(result));
elseif (result)
player:notify(tostr("Word added:  ", line[ii]));
num_learned = num_learned + 1;
else
player:notify(tostr("Already in dictionary:  " + line[ii]));
endif
elseif (!$spell:valid(line[ii]))
player.dict = listappend(player.dict, line[ii]);
player:notify(tostr("Word added:  ", line[ii]));
num_learned = num_learned + 1;
endif
endfor
endfor
player:notify(tostr(num_learned ? num_learned | "No", " word", (num_learned != 1) ? "s " | " ", "added to ", (verb == "@adddict") ? "main " | "personal ", "dictionary."));
endif
.
#90:33
"@spell a word or phrase  -- Spell check a word or phrase.";
"@spell thing.prop  -- Spell check a property. The value must be a string or a list of strings.";
"@spell thing:verb  -- Spell check a verb. Only the quoted strings in the verb are checked.";
"@cspell word  -- Spell check a word, and if it is not in the dictionary, offset suggestions about what the right spelling might be. This actually works with thing.prop and thing:verb too, but it is too slow to be useful--it takes maybe 30 seconds per unknown word.";
"@complete prefix  -- List all the word in the dictionary which begin with the given prefix. For example, `@complete zoo' lists zoo, zoologist, zoology, and zoom.";
"";
"Mr. Spell was written by waffle (waffle@euclid.humboldt.edu), for use by";
"MOOers all over this big green earth. (....and other places....)";
"This monstrosity programmed Sept-Oct 1991, when I should have been studying.";
set_task_perms(player);
if (!argstr)
if (verb == "@complete")
player:notify(tostr("Usage: ", verb, " word-prefix"));
else
player:notify(tostr("Usage: ", verb, " object.property"));
player:notify(tostr("       ", verb, " object:verb"));
player:notify(tostr("       ", verb, " one or more words"));
endif
elseif (verb == "@complete")
if ((foo = $string_utils:from_list($spell:sort($spell:find_all(argstr)), " ")) == "")
player:notify(tostr("No words found that begin with `", argstr, "'"));
else
player:notify(tostr(foo));
endif
else
"@spell or @cspell.";
corrected_words = {};
data = $spell:get_input(argstr);
if (data)
misspelling = 0;
for i in [1..length(data)]
line = $string_utils:words(data[i]);
for ii in [1..length(line)]
$command_utils:suspend_if_needed(0);
if (!$spell:valid(line[ii]))
if ((rindex(line[ii], "s") == length(line[ii])) && $spell:valid(line[ii][1..$ - 1]))
msg = "Possible match: " + line[ii];
msg = (msg + " ") + ((length(data) != 1) ? ("(line " + tostr(i)) + ")  " | "  ");
elseif ((rindex(line[ii], "'s") == (length(line[ii]) - 1)) && $spell:valid(line[ii][1..$ - 2]))
msg = "Possible match: " + line[ii];
msg = (msg + " ") + ((length(data) != 1) ? ("(line " + tostr(i)) + ")  " | "  ");
else
misspelling = misspelling + 1;
msg = ("Unknown word: " + line[ii]) + ((length(data) != 1) ? (" (line " + tostr(i)) + ")  " | "  ");
if ((verb == "@cspell") && (!(line[ii] in corrected_words)))
corrected_words = listappend(corrected_words, line[ii]);
guesses = $string_utils:from_list($spell:guess_words(line[ii]), " ");
if (guesses == "")
msg = msg + "-No guesses";
else
msg = msg + "-Possible correct spelling";
msg = msg + (index(guesses, " ") ? "s: " | ": ");
msg = msg + guesses;
endif
endif
endif
player:notify(tostr(msg));
endif
endfor
endfor
player:notify(tostr("Found ", misspelling ? misspelling | "no", " misspelled word", (misspelling == 1) ? "." | "s."));
elseif (data != $failed_match)
player:notify(tostr("Nothing found to spellcheck!"));
endif
endif
.
#90:34
set_task_perms(player);
if (argstr in player.dict)
player.dict = setremove(player.dict, argstr);
player:notify(tostr("`", argstr, "' removed from personal dictionary."));
else
player:notify(tostr("`", argstr, "' not found in personal dictionary."));
endif
.
#90:35
set_task_perms(player);
result = $spell:remove_word(argstr);
if (result == E_PERM)
player:notify("You may not remove words from the main dictionary. Use `@rmword' to remove words from your personal dictionary.");
elseif (typeof(result) == ERR)
player:notify(tostr(result));
elseif (result)
player:notify(tostr("`", argstr, "' removed."));
else
player:notify(tostr("`", argstr, "' not found in dictionary."));
endif
.
#90:36
"'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
.
#90:37
"'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};
.
#90:38
"'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()};
.
#90: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, prop} = args;
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;
.
#90:40
"'@refuse <action(s)> [ from <player> ] [ for <time> ]' - Refuse all of a list of one or more actions. If a player is given, refuse actions from the player; otherwise, refuse all actions. If a time is specified, refuse the actions for the given amount of time; otherwise, refuse them for a week. If the actions are already refused, then the only their times are adjusted.";
if (!argstr)
player:tell("@refuse <action(s)> [ from <player> ] [ for <time> ]");
return;
endif
stuff = this:parse_refuse_arguments(argstr);
if (stuff)
if (((typeof(who = stuff[1]) == OBJ) && (who != $nothing)) && (!is_player(who)))
player:tell("You must give the name of some player.");
else
"'stuff' is now in the form {<origin>, <actions>, <duration>}.";
this:add_refusal(@stuff);
player:tell("Refusal of ", this:refusal_origin_to_name(stuff[1]), " for ", $time_utils:english_time(stuff[3]), " added.");
endif
endif
.
#90:41
"'@unrefuse <action(s)> [ from <player> ]' - Stop refusing all of a list of actions. If a player is given, stop refusing actions by the player; otherwise, stop refusing all actions of the given kinds. '@unrefuse everything' - Remove all refusals.";
if (argstr == "everything")
if ($command_utils:yes_or_no("Do you really want to erase all your refusals?"))
this:clear_refusals();
player:tell("OK, they are gone.");
else
player:tell("OK, no harm done.");
endif
return;
endif
stuff = this:parse_refuse_arguments(argstr);
if (!stuff)
return;
endif
"'stuff' is now in the form {<origin>, <actions>, <duration>}.";
origins = stuff[1];
actions = stuff[2];
if (typeof(origins) != LIST)
origins = {origins};
endif
n = 0;
for origin in (origins)
n = n + this:remove_refusal(origin, actions);
endfor
plural = ((n == 1) && (length(origins) == 1)) ? "" | "s";
if (n)
player:tell("Refusal", plural, " removed.");
else
player:tell("You have no such refusal", plural, ".");
endif
.
#90:42
"'@refusals' - List your refusals. '@refusals for <player>' - List the given player's refusals.";
if (iobjstr)
who = $string_utils:match_player(iobjstr);
if ($command_utils:player_match_failed(who, iobjstr))
return;
endif
if (!$object_utils:has_verb(who, "refusals_text"))
player:tell("That player does not have the refusal facility.");
return;
endif
else
who = player;
endif
who:remove_expired_refusals();
player:tell_lines(this:refusals_text(who));
.
#90:43
"'@refusal-reporting' - See if refusal reporting is on. '@refusal-reporting on', '@refusal-reporting off' - Turn it on or off..";
if (!argstr)
player:tell("Refusal reporting is ", this.report_refusal ? "on" | "off", ".");
elseif (argstr in {"on", "yes", "y", "1"})
this.report_refusal = 1;
player:tell("Refusals will be reported to you as they happen.");
elseif (argstr in {"off", "no", "n", "0"})
this.report_refusal = 0;
player:tell("Refusals will happen silently.");
else
player:tell("@refusal-reporting on     - turn on refusal reporting");
player:tell("@refusal-reporting off    - turn it off");
player:tell("@refusal-reporting        - see if it's on or off");
endif
.
#90:44
"'parse_refuse_arguments (<string>)' -> {<who>, <actions>, <duration>} - Parse the arguments of a @refuse or @unrefuse command. <who> is the player requested, or $nothing if none was. <actions> is a list of the actions asked for. <duration> is how long the refusal should last, or 0 if no expiration is given. <errors> is a list of actions (or other words) which are wrong. If there are any errors, this prints an error message and returns 0.";
words = $string_utils:explode(args[1]);
possible_actions = this:refusable_actions();
who = $nothing;
actions = {};
until = this.default_refusal_time;
errors = {};
skip_to = 0;
for i in [1..length(words)]
word = words[i];
if (i <= skip_to)
elseif (which = $string_utils:find_prefix(word, possible_actions))
actions = setadd(actions, possible_actions[which]);
elseif ((word[$] == "s") && (which = $string_utils:find_prefix(word[1..$ - 1], possible_actions)))
"The word seems to be the plural of an action.";
actions = setadd(actions, possible_actions[which]);
elseif (results = this:translate_refusal_synonym(word))
actions = $set_utils:union(actions, results);
elseif ((word == "from") && (i < length(words)))
"Modified to allow refusals from all guests at once. 5-27-94, Gelfin";
if (words[i + 1] == "guests")
who = "all guests";
elseif (!(typeof(who = $code_utils:toobj(words[i + 1])) == OBJ))
who = $string_utils:match_player(words[i + 1]);
if ($command_utils:player_match_failed(who, words[i + 1]))
return 0;
endif
endif
skip_to = i + 1;
elseif ((word == "for") && (i < length(words)))
n_words = this:parse_time_length(words[i + 1..$]);
until = this:parse_time(words[i + 1..i + n_words]);
if (!until)
return 0;
endif
skip_to = i + n_words;
else
errors = {@errors, word};
endif
endfor
if (errors)
player:tell((length(errors) > 1) ? "These parts of the command were not understood: " | "This part of the command was not understood: ", $string_utils:english_list(errors, 0, " ", " ", " "));
return 0;
endif
return {this:player_to_refusal_origin(who), actions, until};
.
#90:45
"'time_word_to_seconds (<string>)' - The <string> is expected to be a time word, 'second', 'minute', 'hour', 'day', 'week', or 'month'. Return the number of seconds in that amount of time (a month is taken to be 30 days). If <string> is not a time word, return 0. This is used both as a test of whether a word is a time word and as a converter.";
return $time_utils:parse_english_time_interval("1", args[1]);
.
#90:46
"'parse_time_length (<words>)' -> n - Given a list of words which is expected to begin with a time expression, return how many of them belong to the time expression. A time expression can be a positive integer, a time word, or a positive integer followed by a time word. A time word is anything that this:time_word_to_seconds this is one. The return value is 0, 1, or 2.";
words = {@args[1], "dummy"};
n = 0;
if (toint(words[1]) || this:time_word_to_seconds(words[1]))
n = 1;
endif
if (this:time_word_to_seconds(words[n + 1]))
n = n + 1;
endif
return n;
.
#90:47
"'parse_time (<words>)' -> <seconds> - Given a list of zero or more words, either empty or a valid time expression, return the number of seconds that the time expression refers to. This is a duration, not an absolute time.";
words = args[1];
"If the list is empty, return the default refusal time.";
if (!words)
return this.default_refusal_time;
endif
"If the list has one word, either <units> or <n>.";
"If it is a unit, like 'hour', return the time for 1 <unit>.";
"If it is a number, return the time for <n> days.";
if (length(words) == 1)
return this:time_word_to_seconds(words[1]) || (toint(words[1]) * this:time_word_to_seconds("days"));
endif
"The list must contain two words, <n> <units>.";
return toint(words[1]) * this:time_word_to_seconds(words[2]);
.
#90:48
"'clear_refusals ()' - Erase all of this player's refusals.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
this.refused_origins = {};
this.refused_actions = {};
this.refused_until = {};
this.refused_extra = {};
.
#90:49
"'set_default_refusal_time (<seconds>)' - Set the length of time that a refusal lasts if its duration isn't specified.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
this.default_refusal_time = toint(args[1]);
.
#90:50
"'refusable_actions ()' -> {'page', 'whisper', ...} - Return a list of the actions that can be refused. This is a verb, rather than a property, so that it can be inherited properly. If you override this verb to add new refusable actions, write something like 'return {@pass (), 'action1', 'action2', ...}'. That way people can add new refusable actions at any level of the player class hierarchy, without clobbering any that were added higher up.";
return {"page", "whisper", "move", "join", "accept", "mail"};
.
#90:51
"'translate_refusal_synonym (<word>)' -> list - If the <word> is a synonym for some set of refusals, return the list of those refusals. Otherwise return the empty list, {}. Programmers can override this verb to provide more synonyms.";
word = args[1];
if (word == "all")
return this:refusable_actions();
endif
return {};
.
#90:52
"'default_refusals_text_filter (<origin>, <actions>)' - Return any actions by this <origin> which should be included in the text returned by :refusals_text. This is the default filter, which includes all actions.";
return args[2];
.
#90:53
"'refusals_text (<player>, [<filter verb name>])' - Return text describing the given player's refusals. The filter verb name is optional; if it is given, this verb takes an origin and a list of actions and returns any actions which should be included in the refusals text. This verb works only if <player> is a player who has the refusals facility; it does not check for this itself.";
{who, ?filter_verb = "default_refusals_text_filter"} = args;
text = {};
for i in [1..length(who.refused_origins)]
origin = who.refused_origins[i];
actions = this:(filter_verb)(origin, who.refused_actions[i]);
if (actions)
line = "";
for action in (actions)
line = (line + " ") + action;
endfor
line = (this:refusal_origin_to_name(origin) + ": ") + line;
line = (ctime(who.refused_until[i]) + " ") + line;
text = {@text, line};
endif
endfor
if (!text)
text = {"No refusals."};
endif
return text;
.
#90:54
"'player_to_refusal_origin (<player>)' -> <origin> - Convert a player to a unique identifier called the player's 'refusal origin'. For most players, it's just their object number. For guests, it is a hash of the site they are connecting from. Converting an origin to an origin is a safe no-op--the code relies on this.";
who = args[1];
if ((((typeof(who) == OBJ) && valid(who)) && (parent(who) == $object_utils:has_property($local, "guest"))) ? $local.guest | $guest)
return who:connection_name_hash("xx");
else
return who;
endif
.
#90:55
"'refusal_origin_to_name (<origin>)' -> string - Convert a refusal origin to a name.";
origin = args[1];
if (origin in {"all guests", "everybody"})
return origin;
elseif (typeof(origin) != OBJ)
return "a certain guest";
elseif (origin == #-1)
return "Everybody";
else
return $string_utils:name_and_number(origin);
endif
.
#90:56
"'check_refusal_actions (<actions>)' - Check a list of refusal actions, and return whether they are all legal.";
actions = args[1];
legal_actions = this:refusable_actions();
for action in (actions)
if (!(action in legal_actions))
return 0;
endif
endfor
return 1;
.
#90:57
"'add_refusal (<origin>, <actions> [, <duration> [, <extra>]])' - Add refusal(s) to this player's list. <Actions> is a list of the actions to be refused. The list should contain only actions, no synonyms. <Origin> is the actor whose actions are to be refused. <Until> is the time that the actions are being refused until, in the form returned by time(). It is optional; if it's not given, it defaults to .default_refusal_time. <Extra> is any extra information; it can be used for comments, or to make finer distinctions about the actions being refused, or whatever. If it is not given, it defaults to 0. The extra information is per-action; that is, it is stored separately for each action that it applies to.";
if (caller != this)
return E_PERM;
endif
{orig, actions, ?duration = this.default_refusal_time, ?extra = 0} = args;
origins = this:player_to_refusal_origin(orig);
if (typeof(origins) != LIST)
origins = {origins};
endif
if (typeof(actions) != LIST)
actions = {actions};
endif
if (!this:check_refusal_actions(actions))
return E_INVARG;
endif
until = time() + duration;
for origin in (origins)
if (i = origin in this.refused_origins)
this.refused_until[i] = until;
for action in (actions)
if (j = action in this.refused_actions[i])
this.refused_extra[i][j] = extra;
else
this.refused_actions[i] = {@this.refused_actions[i], action};
this.refused_extra[i] = {@this.refused_extra[i], extra};
endif
endfor
else
this.refused_origins = {@this.refused_origins, origin};
this.refused_actions = {@this.refused_actions, actions};
this.refused_until = {@this.refused_until, until};
this.refused_extra = {@this.refused_extra, $list_utils:make(length(actions), extra)};
endif
endfor
.
#90:58
"'remove_refusal (<origin>, <actions>)' - Remove any refused <actions> by <origin>. The <actions> list should contain only actions, no synonyms. Return the number of such refusals found (0 if none).";
if (caller != this)
return E_PERM;
endif
{origin, actions} = args;
if (typeof(actions) != LIST)
actions = {actions};
endif
count = 0;
i = origin in this.refused_origins;
if (i)
for action in (actions)
if (j = action in this.refused_actions[i])
this.refused_actions[i] = listdelete(this.refused_actions[i], j);
this.refused_extra[i] = listdelete(this.refused_extra[i], j);
count = count + 1;
endif
endfor
if (!this.refused_actions[i])
this.refused_origins = listdelete(this.refused_origins, i);
this.refused_actions = listdelete(this.refused_actions, i);
this.refused_until = listdelete(this.refused_until, i);
this.refused_extra = listdelete(this.refused_extra, i);
endif
endif
return count;
.
#90:59
"'remove_expired_refusals ()' - Remove refusal entries which are past their time limits.";
origins = {};
"Before removing any refusals, figure out which ones to remove. Removing one changes the indices and invalidates the loop invariant.";
for i in [1..length(this.refused_origins)]
if (time() >= this.refused_until[i])
origins = {@origins, this.refused_origins[i]};
endif
endfor
for origin in (origins)
this:remove_refusal(origin, this:refusable_actions());
endfor
.
#90:60
"'refuses_action (<origin>, <action>, ...)' - Return whether this object refuses the given <action> by <origin>. <Origin> is typically a player. Extra arguments after <origin>, if any, are used to further describe the action.";
"Modified by Diopter (#98842) at LambdaMOO";
{origin, action, @extra_args} = args;
extra_args = {origin, @extra_args};
rorigin = this:player_to_refusal_origin(origin);
if (((which = rorigin in this.refused_origins) && (action in this.refused_actions[which])) && this:("refuses_action_" + action)(which, @extra_args))
return 1;
elseif (((((typeof(rorigin) == OBJ) && valid(rorigin)) && (which = rorigin.owner in this.refused_origins)) && (action in this.refused_actions[which])) && this:("refuses_action_" + action)(which, @extra_args))
return 1;
elseif ((((which = $nothing in this.refused_origins) && (rorigin != this)) && (action in this.refused_actions[which])) && this:("refuses_action_" + action)(which, @extra_args))
return 1;
elseif ((((which = "all guests" in this.refused_origins) && $object_utils:isa(origin, $guest)) && (action in this.refused_actions[which])) && this:("refuses_action_" + action)(which, @extra_args))
return 1;
endif
return 0;
.
#90:61
"'refuses_action_* (<which>, <origin>, ...)' - The action (such as 'whisper' for the verb :refuses_action_whisper) is being considered for refusal. Return whether the action should really be refused. <Which> is an index into this.refused_origins. By default, always refuse non-outdated actions that get this far.";
{which, @junk} = args;
if (time() >= this.refused_until[which])
fork (0)
"This <origin> is no longer refused. Remove any outdated refusals.";
this:remove_expired_refusals();
endfork
return 0;
else
return 1;
endif
.
#90:62
"'report_refusal (<player>, <message>, ...)' - If refusal reporting is turned on, print the given <message> to report the refusal of some action by <player>. The message may take more than one argument. You can override this verb to do more selective reporting.";
if (this.report_refusal)
this:tell(@listdelete(args, 1));
endif
.
#90:63
"'whisper <message> to <this player>' - Whisper a message to this player which nobody else can see.";
if (this:refuses_action(player, "whisper"))
player:tell(this:whisper_refused_msg());
this:report_refusal(player, "You just refused a whisper from ", player.name, ".");
else
pass(@args);
endif
.
#90:64
"'receive_page (<message>)' - Receive a page. If the page is accepted, pass(@args) shows it to the player.";
if (this:refuses_action(player, "page"))
this.page_refused = task_id();
return 0;
endif
this.page_refused = 0;
return pass(@args);
.
#90:65
"'page_echo_msg ()' - Return a message to inform the pager what happened to their page.";
if (task_id() == this.page_refused)
this:report_refusal(player, "You just refused a page from ", player.name, ".");
return this:page_refused_msg();
else
return pass(@args);
endif
.
#90:66
"'moveto (<destination>)', 'accept (<object>)' - Check whether this :moveto or :accept is allowed or refused. If it is allowed, do it. This code is slightly modified from an original verb by Grump.  Upgraded by Bits to account for forthcoming 1.8.0 behavior of callers().";
by = callers();
"Ignore all the verbs on this.";
while (((y = by[1])[1] == this) && (y[2] == verb))
by = listdelete(by, 1);
endwhile
act = (verb == "moveto") ? "move" | "accept";
if ((player != this) && this:refuses_action(player, act, args[1]))
"check player";
return 0;
endif
last = #-1;
for k in (by)
if ((((perms = k[3]) == #-1) && (k[2] != "")) && (k[1] == #-1))
elseif ((!perms.wizard) && (perms != this))
if (perms != last)
"check for possible malicious programmer";
if (this:refuses_action(perms, act, args[1]))
return 0;
endif
last = perms;
endif
endif
endfor
return pass(@args);
.
#90:67
"'receive_message (<message>, <sender>)' - Receive the given mail message from the given sender. This version handles refusal of the message.";
if ((!$perm_utils:controls(caller_perms(), this)) && (caller != this))
return E_PERM;
elseif (this:refuses_action(args[2], "mail"))
return this:mail_refused_msg();
else
return pass(@args);
endif
.
#90:68
"'whisper_refused_msg()', 'page_refused_msg()', etc. - Return a message string.";
return $string_utils:pronoun_sub(this.(verb), this);
.
#90:69
set_task_perms(caller_perms());
if (pass(@args))
return 1;
endif
{verb, args} = args;
if (valid(dobj = $string_utils:literal_object(dobjstr)) && (r = $match_utils:match_verb(verb, dobj, args)))
return r;
elseif (valid(iobj = $string_utils:literal_object(iobjstr)) && (r = $match_utils:match_verb(verb, iobj, args)))
return r;
else
return 0;
endif
.
#90:70
":ping_features()";
" -- cleans up the .features list to remove !valid objects";
" ==> cleaned-up .features list";
features = this.features;
for x in (features)
if (!$recycler:valid(x))
features = setremove(features, x);
endif
endfor
return this.features = features;
.
#90:71
":set_owned_objects( LIST owned-objects list )";
"  -- set your .owned_objects, ordered as you please";
"  -- no, it will NOT let you set to to anything you want";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
new = args[1];
old = this.owned_objects;
"make sure they're the same";
if (length(new) != length(old))
return E_INVARG;
endif
for i in (new)
old = setremove(old, i);
endfor
if (old)
"something's funky";
return E_INVARG;
endif
return this.owned_objects = new;
else
return E_PERM;
endif
.
#90:72
if (caller_perms().wizard)
pass();
if ($code_utils:verb_location() == this)
this.rooms = {};
else
clear_property(this, "rooms");
endif
endif
"Last modified Sun Aug 17 11:14:20 1997 CDT by Wizard (#2).";
.
#91:0
if (((verb[1] == "`") || (verb[1] == "-")) && (length(verb) > 1))
name = verb[2..length(verb)];
else
name = argstr[1..index(argstr, " ") - 1];
endif
if (verb[1] == "`")
text = argstr;
else
text = argstr[index(argstr, " ") + 1..length(argstr)];
endif
who = $string_utils:match_player(name, player);
if ((!valid(who)) || (who.location != player.location))
who = $string_utils:match_object(name, player.location);
endif
who = valid(who) ? who.name | name;
argstr = tostr("[to ", who, "]: ", text);
if ($object_utils:has_callable_verb(player, "emote"))
player:emote(argstr);
elseif ($object_utils:has_callable_verb(player.location, "emote"))
player.location:emote(argstr);
endif
"Last modified Tue Nov  4 12:24:18 1997 CST by Wizard (#2).";
.
#91: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.name + " ") + verb) + " ") + argstr);
.
#91: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.]";
player.location:announce_all("[", (((player.name + " ") + verb[2..$]) + (argstr ? " " + argstr | "")) + "]");
.
#91:3
"Point to yourself.";
"Usage:";
"  <message";
"Example:";
"  Muchkin decides he's being strange. He types:";
"    <being strange.";
"  The room sees:";
"    Munchkin <- being strange.";
player.location:announce_all((((player.name + " <- ") + verb[2..$]) + " ") + argstr);
.
#91:4
"Usage: big-sign <text>";
"       bb <text>";
"       Holds up a big sign that has <text> written on it.";
lines = player:linesplit(argstr, 75 - length((player.name + " holds up a BIG sign: | ") + " )"));
length = 0;
for line in (lines)
length = max(length, length(line));
endfor
space = $string_utils:space(length(player.name) + 15);
bound = $string_utils:space(length + 2, "_");
player.location:announce_all(space, "        ", bound);
player.location:announce_all(space, "       |", $string_utils:space(length), "  |");
i = 0;
h = length(lines) / 2;
for line in (lines)
if (i == h)
player.location:announce_all(player.name, " holds up a BIG sign: | ", $string_utils:center(line, length), " |");
else
player.location:announce_all(space, "       | ", $string_utils:center(line, length), " |");
endif
i = i + 1;
endfor
player.location:announce_all(space, "       |", bound, "|");
"Last modified Tue Oct 14 14:14:10 1997 CDT by Wizard (#2).";
.
#91:5
"Shorthand method to emote text in a 'think bubble'.";
"Usage:  think <text>";
player.location:announce_all(player:titlec(), " . o O ( ", argstr, " )");
"Last modified Tue Oct 14 14:14:10 1997 CDT by Wizard (#2).";
.
#92:0
"Usage: @paste <prefix> <suffix>";
"Announce a series of entered lines to the room the player is in.";
"Before the lines are quoted, player.paste_header is run through";
"$string_utils:pronoun_sub(), and if the result contains the player's";
"name, it is used as a header.  Otherwise player.name centered in a";
"line of dashes is used.";
"A footer comes afterwards, likewise derived from player.paste_footer.";
"<prefix> and <suffix> are placed before and after each line.";
"";
"This verb is, as one might guess, designed for pasting text to MOO using";
"GnuEmacs or a windowing system.  You should remember that after you";
"have pasted the lines in, you must type . on a line by itself, or you'll";
"sit around waiting for $command_utils:read_lines() to finish _forever_.";
{?prefix = "", ?suffix = ""} = args;
lines = $command_utils:read_lines();
header = $string_utils:pronoun_sub_secure($code_utils:verb_or_property(player, "paste_header"), "") || $string_utils:center(player.name, 75, "-");
to_tell = {header};
for line in (lines)
to_tell = listappend(to_tell, (prefix + line) + suffix);
endfor
to_tell = listappend(to_tell, $string_utils:pronoun_sub_secure($code_utils:verb_or_property(player, "paste_footer"), "") || $string_utils:center("finished", 75, "-"));
for thing in (player.location.contents)
$command_utils:suspend_if_needed(0);
thing:tell_lines(to_tell);
endfor
player:tell("Done @pasting.");
.
#92:1
"Echo a line prefaced by a vertical bar.";
"Usage:";
"  |message";
"Example:";
"  Hacker wants to echo to the room what he just saw. He enters (either by hand, or with Emacs or a windowing system):";
"      |Haakon has disconnected.";
"  The room sees:";
"      Hacker | Haakon has disconnected.";
player.location:announce_all((((player.name + " | ") + verb[2..$]) + " ") + argstr);
.
#92:2
"Syntax: @paste-to <player>";
"";
"Which will then prompt you for the lines to privately send to <player>. The lines will be surrounded by a default footer and header.";
target = $string_utils:match_player(dobjstr);
$command_utils:player_match_result(target, dobjstr);
if (!valid(target))
return;
endif
prefix = "";
suffix = "";
lines = $command_utils:read_lines();
to_tell = {$string_utils:center("Private message from " + player.name, 75, "-")};
for line in (lines)
to_tell = listappend(to_tell, (prefix + line) + suffix);
endfor
to_tell = listappend(to_tell, $string_utils:center("end message", 75, "-"));
target:tell_lines(to_tell);
player:tell("Done @pasting.");
.
#93:0
":vector_add(V1 [,V2 ...]) => VN such that VN[n] = V1[n] + V2[n]...";
":vector_sub(V1 [,V2 ...]) => VN such that VN[n] = V1[n] - V2[n]...";
":vector_mul(V1 [,V2 ...]) => VN such that VN[n] = V1[n] * V2[n]...";
":vector_div(V1 [,V2 ...]) => VN such that VN[n] = V1[n] / V2[n]...";
"Vectors do not need to be the same length, but they should be. VN's length will be the length of the longest vector in the arguments. :vector_add and :vector_sub will pad out the smaller vectors with 0's or 0.0's. :vector_mul and :vector_div will pad out the smaller vectors with 1's or 1.0's. Vectors do not need to contain homogeneous data, but the nth term of each vector must be of the same type.";
"I can see a reason for wanting to do vector addition or subtraction, but multiplication and divareion is usually handled in other ways. I've included them here for novelty, and becuase it was easy enough to do.";
"";
"Vector addition is used when two or more similar vector quantities are at work and need to be resolved into a single vector. For instance, a ship travelling in a current will be acted upon by (at least) two forces: a force propelling it forward (its engine), and a force pushing it off course (the current). The sum of these two forces gives the resultant net force acting upon the ship and, since Force = Mass * Acceleration, the direction the ship is accelerating.";
"";
"Vector subtraction can be used to reverse the process of vector addition. In the ship problem above, let's say the actual resultant force is known, but it does not match the result of adding the propelling force and the drifting force. Friction is probably acting against the motion of the ship. Subtracting the computed resultant force from the known net force will yield the frictional force acting against the progress of the ship.";
"";
"Vector multiplication and division do not have RL examples, but vector multiplication of this type makes computing the dot product of two vectors simple.";
"";
if (length(args) == 1)
return args;
elseif (!args)
return raise(E_INVARG);
endif
type = verb[$ - 2..$];
lresult = max = length(args[1]);
results = args[1];
for n in [2..length(args)]
if (type == "add")
for m in [1..min(lcurr = length(args[n]), lresult)]
results[m] = results[m] + args[n][m];
endfor
if (lcurr > lresult)
results[lresult + 1..lcurr] = args[n][lresult + 1..lcurr];
endif
elseif (type == "sub")
for m in [1..min(lcurr = length(args[n]), lresult)]
results[m] = results[m] - args[n][m];
endfor
if (lcurr > lresult)
for m in [lresult + 1..lcurr]
results = {@results, -args[n][m]};
endfor
endif
elseif (type == "mul")
for m in [1..min(lcurr = length(args[n]), lresult)]
results[m] = results[m] * args[n][m];
endfor
if (lcurr > lresult)
results[lresult + 1..lcurr] = args[n][lresult + 1..lcurr];
endif
else
for m in [1..min(lcurr = length(args[n]), lresult)]
results[m] = results[m] / args[n][m];
endfor
if (lcurr > lresult)
for m in [lresult + 1..lcurr]
results = {@results, (typeof(foo = args[n][m]) == INT) ? 1 / foo | (1.0 / foo)};
endfor
endif
endif
endfor
return results;
.
#93:1
":matrix_add(M1 [, M2 ...]) => MN such that MN[m][n] = M1[m][n] + M2[m][n]...";
":matrix_sub(M1 [, M2 ...]) => MN such that MN[m][n] = M1[m][n] - M2[m][n]...";
"Matrices should all be of the same size.";
"";
"Matrix addition and subtraction is simply the addition or subtraction of the vectors contained in the matrices. See 'help $matrix_utils:vector_add' for more help.";
type = verb[$ - 2..$];
results = args[1];
if (typeof(results[1][1]) == LIST)
for n in [1..length(results)]
results[n] = this:(verb)(results[n], @$list_utils:slice(args[2..$], n));
endfor
else
for n in [1..length(results)]
results[n] = this:("vector_" + type)(results[n], @$list_utils:slice(args[2..$], n));
endfor
endif
return results;
.
#93:2
":transpose(Mmn) => Mnm";
"Transpose an m by n matrix into an n by m matrix by making the rows in the original the columns in the output.";
{mat} = args;
if (!this:is_matrix(mat))
return raise("E_INVMAT", "Invalid Matrix Format");
endif
j = this:dimensions(mat)[2];
result = {};
for n in [1..j]
result = {@result, this:column(mat, n)};
endfor
return result;
.
#93:3
":determinant(M) => NUM the determinant of the matrix.";
"";
"There are several properties of a matrix's determinant. Adding or subtracting a row or column from another row or colum of a matrix does not hange the value of its determinant. Multiplying a row or column of a matrix by a single scalar value has the effect of multiplying the matrix's determinant by the same scalar.";
"";
"However, the most dramatic use of determinants is in solving linear equations. For example, the solution to this system of equations:";
"";
"Ax1 + Bx2 + Cx3 = D";
"Ex1 + Fx2 + Gx3 = H";
"Ix1 + Jx2 + Kx3 = L";
"";
"is";
"";
"     1 |D B C|         1 |A D C|        1 |A B D|";
"x1 = - |H F G|    x2 = - |E H G|   x3 = - |E F H|";
"     Z |L J K|         Z |I L K|        Z |I J L|";
"";
"          |A B C|";
"where Z = |E F G|";
"          |I J K|";
"";
"or, in other words, x1, x2, and x3 are some determinant divided by Z, another determinant.";
"";
"Determinants are also used in computing the cross product of two vectors. See 'help $matrix_utils:cross_prod' for more info.";
"";
{mat} = args;
if (!this:is_square(mat))
return raise("E_INVMAT", "Invalid Matrix Format");
elseif (this:dimensions(mat)[1] == 2)
return (mat[1][1] * mat[2][2]) - (mat[1][2] * mat[2][1]);
else
result = 0;
coeff = 1;
for n in [1..length(mat[1])]
result = result + ((coeff * mat[1][n]) * this:determinant(this:submatrix(1, n, mat)));
coeff = -coeff;
endfor
return result;
endif
.
#93:4
":inverse(M) => MN such that M * MN = I";
"";
"The inverse of a matrix is very similar to the reciprocal of a scalar number. If two numbers, A and B, equal 1 (the scalar identity number) when multiplied together (AB=1), then B is said the be the reciprocal of A, and A is the reciprocal of B. If A and B are matrices, and the result of multiplying them togeter is the Identity Matrix, then B is the inverse of A, and A is the inverse of B.";
"";
"Computing the inverse involves the solutions of several linear equations. Since linear equations can be easily solved with determinants, this is rather simple. See 'help $matrix_utils:determinant' for more on how determinants solve linear equations.";
"";
{mat} = args;
{i, j} = this:dimensions(mat);
det = this:determinant(mat);
result = {};
for k in [1..i]
sub = {};
for l in [1..j]
sub = {@sub, (tofloat($math_utils:pow(-1, i + j)) * this:determinant(this:submatrix(j, i, mat))) / det};
endfor
result = {@result, sub};
endfor
return result;
.
#93:5
":identity(INT <size>) => Identity matrix (I) of dimensions <size> by <size>.";
"All elements of I are 0, except for the diagonal elements which are 1.";
"";
"The Identity Matrix has the unique property such that when another matrix is multiplied by it, the other matrix remains unchanged. This is similar to the number 1. a*1 = a. A * I = A, if the dimensions of I and A are the same.";
"";
n = args[1];
result = this:null(n, n);
for i in [1..n]
result[i][i] = 1;
endfor
return result;
.
#93:6
":null(INT <size>) => Null matrix (O) of dimensions <size> by <size>.";
"All elements of O are 0.";
"";
"The Null Matrix has the property that is equivalent to the number 0; it reduces the original matrix to itself. a * 0 = 0. A * N = N.";
"";
{m, ?n = m} = args;
result = {};
for i in [1..m]
result = {@result, {}};
for j in [1..n]
result[i] = {@result[i], 0};
endfor
endfor
return result;
.
#93:7
":is_square(M) => 1 iff dimensions of M are equal to each other.";
{m} = args;
return (this:is_matrix(m) && (this:order(m) == 2)) && ((dim = this:dimensions(m))[1] == dim[2]);
.
#93:8
":is_null(M) => 1 iff M is O.";
m = length(mat = args[1]);
if (!this:is_square(mat))
return 0;
endif
for i in [1..m]
for j in [1..m]
if (mat[i][j] != 0)
return 0;
endif
endfor
endfor
return 1;
.
#93:9
":is_identity(M) => 1 iff M is I.";
m = length(mat = args[1]);
if (!this:is_square(mat))
return 0;
endif
for i in [1..m]
for j in [1..m]
if ((mat[i][j] != 0) && ((i != j) ? 1 | (mat[i][j] != 1)))
return 0;
endif
endfor
endfor
return 1;
.
#93:10
":cross_prod(V1, V2) => VN, the vector perpendicular to both V1 and V2 with length equal to the area of the parallelogram spanned by V1 and V2, and direction governed by the rule of thumb.";
"";
"If A = a1i + a2j + a3k, represented as a list as {a1, a2, a3}";
"and B = b1i + b2j + b3k, or {b1, b2, b3}, then";
"";
"        |i  j  k |";
"A x B = |a1 a2 a3| = |a2 a3|i - |a1 a3|j + |a1 a2|k";
"        |b1 b2 b3| = |b2 b3|    |b1 b3|    |b1 b2|";
"";
"or, in list terms, as the list of the coefficients of i, j, and k.";
"";
"Note: i, j, and k are unit vectors in the x, y, and z direction respectively.";
"";
"The rule of thumb: A x B = C  If you hold your right hand out so that your fingers point in the direction of A, and so that you can curl them through B as you make a hitchhiking fist, your thumb will point in the direction of C.";
"";
"Put another way, A x B = ABsin(THETA) (A cross B equals the magnitude of A times the magnitude of B times the sin of the angle between them) This is expressed as a vector perpendicular the the A-B plane, pointing `up' if you curl your right hand fingers from A to B, and `down' if your right hand fingers curl from B to A.";
"";
"The cross product has many uses in physics. Angular momentum is the cross product of a particles position vector from the point it is rotating around and it's linear momentum (L = r x p). Torque is the cross product of position and Force (t = r x F).";
"";
{v1, v2} = args;
if (((((l = length(v1)) != length(v2)) || (l != 3)) || (!this:is_vector(v1))) || (!this:is_vector(v2)))
return raise("E_INVVEC", "Invalid Vector Format");
endif
mat = {{1, 1, 1}, v1, v2};
coeff = 1;
result = {};
for n in [1..3]
result = {@result, coeff * this:determinant(this:submatrix(1, n, mat))};
coeff = -coeff;
endfor
return result;
.
#93:11
":norm(V) => FLOAT";
":length(V) => FLOAT";
"The norm is the length of a vector, the square root of the sum of the squares of its elements.";
"";
"In school, we all should have learned the Pythagorean Theorem of right triangles: The sum of the squares of the sides of a right triagle is equal to the square of the hypoteneuse. The Theorem holds true no matter how many dimensions are being considered. The length of a vector is equal to the square root of the sum of the squares of its components. The dot product of a vector with itself happens to be the sum of the squares of its components.";
"";
{v} = args;
return this:is_vector(v) ? sqrt(tofloat(this:dot_prod(v, v))) | E_TYPE;
.
#93:12
":submatrix(i, j, M1) => M2, the matrix formed from deleting the ith row and jth column from M1.";
{i, j, mat} = args;
{k, l} = this:dimensions(mat);
result = {};
for m in [1..k]
sub = {};
for n in [1..l]
if ((m != i) && (n != j))
sub = {@sub, mat[m][n]};
endif
endfor
if (sub)
result = {@result, sub};
endif
endfor
return result;
.
#93:13
":dot_prod(V1, V2) => NUM";
":inner_prod(V1, V2) => NUM";
"The dot, or inner, product of two vectors is the sum of the products of the corresponding elements of the vectors.";
"If V1 = {1, 2, 3} and V2 = {4, 5, 6}, then V1.V2 = 1*4 + 2*5 + 3*6 = 32";
"";
"The dot product is useful in computing the angle between two vectors, and the length of a vector. See 'help $matrix_utils:subtended_angle' and 'help $matrix_utils:length'.";
"";
"A . B = ABcos(THETA)  (A dot B equals the magnitude of A times the magnitude of B times the cosine of the angle between them.)";
"";
{v1, v2} = args;
if ((((l = length(v1)) != length(v2)) || (!this:is_vector(v1))) || (!this:is_vector(v2)))
return raise("E_INVVEC", "Invalid Vector Format");
endif
temp = this:vector_mul(v1, v2);
result = (typeof(temp[1]) == INT) ? 0 | 0.0;
for n in [1..l]
result = result + temp[n];
endfor
return result;
.
#93:14
":dimensions(M) => LIST of dimensional sizes.";
l = {length(m = args[1])};
if (typeof(m[1]) == LIST)
l = {@l, @this:dimensions(m[1])};
endif
return l;
.
#93:15
":order(M) => INT how many dimensions does this matrix have? 1 means vector";
return length(this:dimensions(args[1]));
.
#93:16
":scalar_vector_add(S, V) => VN such that VN[n] = V[n] + S...";
":scalar_vector_sub(S, V) => VN such that VN[n] = V[n] - S...";
":scalar_vector_mul(S, V) => VN such that VN[n] = V[n] * S...";
":scalar_vector_div(S, V) => VN such that VN[n] = V[n] / S...";
"Actually, arguments can be (S, V) or (V, S). Each element of V is augmented by S. S should be either an INT or a FLOAT, as appropriate to the values in V.";
"";
"I can see a reason for wanting to do scalar/vector multiplcation or division, but addition and subtraction between vector and scalar types is not done. I've included them here for novelty, and because it was easy enough to to.";
"";
"Scalar-vector multiplication stretches a vector along its direction, generating points along a line. One of the more famous uses from physics is Force equals mass times acceleration. F = ma. Force and acceleration are both vectors. Mass is a scalar quantity.";
"";
if (typeof(args[1]) == LIST)
{vval, sval} = args;
else
{sval, vval} = args;
endif
if (!this:is_vector(vval))
return raise("E_INVVEC", "Invalid Vector Format");
endif
type = verb[$ - 2..$];
for n in [1..length(vval)]
if (type == "add")
vval[n] = vval[n] + sval;
elseif (type == "sub")
vval[n] = vval[n] - sval;
elseif (type == "mul")
vval[n] = vval[n] * sval;
else
vval[n] = vval[n] / sval;
endif
endfor
return vval;
.
#93:17
":subtended_angle(V1, V2) => FLOAT smallest angle defined by V1, V2 in radians";
"";
"Any two vectors define two angles, one less than or equal to 180 degrees, the other 180 degrees or more. The larger can be determined from the smaller, since their sum must be 360 degrees.";
"";
"The dot product of the two angles, divided by the lengths of each of the vectors is the cosine of the smaller angle defined by the two vectors.";
"";
{v1, v2} = args;
if ((((l = length(v1)) != length(v2)) || (!this:is_vector(v1))) || (!this:is_vector(v2)))
return raise("E_INVVEC", "Invalid Vector Format");
endif
return acos(tofloat(this:dot_prod(v1, v2)) / (this:norm(v1) * this:norm(v2)));
.
#93:18
":column(M, INT <n>) => LIST the nth column of M.";
{mat, i} = args;
j = this:dimensions(mat)[1];
result = {};
for m in [1..j]
result = {@result, mat[m][i]};
endfor
return result;
.
#93:19
":matrix_mul(M1, M2) => MN such that MN[m][n] = the dot product of the mth row of M1 and the transpose of thenth column of M2.";
"";
"Matrix multiplication is the most common and complex operation performed on two matrices. First, matrices can only be multiplied if they are of compatible sizes. An i by j matrix can only be multiplied by a j by k matrix, and the results of this multiplication will be a matrix of size i by k. Each element in the resulting matrix is the dot product of a row from the first matrix and a column from the second matrix. (See 'help $matrix_utils:dot_prod'.)";
"";
{m1, m2} = args;
{i, j} = this:dimensions(m1);
{k, l} = this:dimensions(m2);
if (((j != k) || (!this:is_matrix(m1))) || (!this:is_matrix(m2)))
return raise("E_INVMAT", "Invalid Matrix Format");
endif
result = {};
for m in [1..i]
sub = {};
for n in [1..l]
sub = {@sub, this:dot_prod(m1[m], this:column(m2, n))};
endfor
result = {@result, sub};
endfor
return result;
.
#93:20
":scalar_matrix_add(S, M) => MN such that MN[m][n] = MN[m][n] + S...";
":scalar_matrix_sub(S, M) => MN such that MN[m][n] = MN[m][n] - S...";
":scalar_matrix_mul(S, M) => MN such that MN[m][n] = MN[m][n] * S...";
":scalar_matrix_div(S, M) => MN such that MN[m][n] = MN[m][n] / S...";
"Actually, arguments can be (S, M) or (M, S). Each element of M is augmented by S. S should be either an INT or a FLOAT, as appropriate to the values in M.";
"I can see a reason for wanting to do scalar/matrix multiplication or division, but addition and subtraction between matrix and scalar types is not done. I've included them here for novelty, and because it was easy enough to do.";
type = verb[$ - 2..$];
if (typeof(args[1]) == LIST)
{mval, sval} = args;
else
{sval, mval} = args;
endif
if (!this:is_matrix(mval))
return raise("E_INVMAT", "Invalid Matrix Format");
endif
results = {};
if (typeof(mval[1][1] == LIST))
for n in [1..length(mval)]
results = {@results, this:(verb)(mval[n], sval)};
endfor
else
for n in [1..length(results)]
results = {@results, this:("scalar_vector_" + type)(mval[n], sval)};
endfor
endif
return results;
.
#93:21
"A matrix is defined as a list of vectors, each having the smae number of elements.";
{m} = args;
if ((typeof(m) != LIST) || (typeof(m[1]) != LIST))
return 0;
endif
len = length(m[1]);
for v in (m)
if ((!this:is_vector(v)) || (length(v) != len))
return 0;
endif
endfor
return 1;
.
#93:22
"A vector shall be defined as a list of INTs or FLOATs. (I'm not gonna worry about them all being the same type.)";
flag = 1;
{v} = args;
if (typeof(v) != LIST)
return 0;
endif
for n in (v)
if (((ntype = typeof(n)) != INT) && (ntype != FLOAT))
flag = 0;
break;
endif
endfor
return flag;
.
#93:23
":is_reflexive   (M) => 1 if M is a reflexive relation, -1 if areflexive,";
"                       0 otherwise.";
":is_areflexive does the same, but with 1 and -1 reversed.";
{m} = args;
if (!this:is_square(m))
return raise("E_INVMAT", "Invalid Matrix Format");
endif
good = bad = 0;
for n in [1..length(m)]
if (!m[n][n])
bad = 1;
else
good = 1;
endif
endfor
return this:_relation_result(good, bad, verb[4] == "a");
.
#93:24
":is_symmetric   (M) => 1 if M is a symmetric relation, -1 if asymmetric,";
"                       0 otherwise.";
":is_asymmetric does the same, but with 1 and -1 reversed.";
{mat} = args;
if (!this:is_square(mat))
return raise("E_INVMAT", "Invalid Matrix Format");
endif
good = bad = 0;
for m in [1..len = length(mat)]
for n in [m + 1..len]
if (mat[m][n] == mat[n][m])
good = 1;
else
bad = 1;
endif
endfor
endfor
return this:_relation_result(good, bad, verb[4] == "a");
.
#93:25
":is_transitive  (M) => 1 if M is a transitive relation, -1 if atransitive,";
"                       0 otherwise.";
":is_atransitive does the same, but with 1 and -1 reversed.";
{mat} = args;
if (!this:is_square(mat))
return raise("E_INVMAT", "Invalid Matrix Format");
endif
good = bad = 0;
for m in [1..len = length(mat)]
for n in [1..len]
if (mat[m][n])
for l in [1..len]
if (mat[n][l])
if (mat[m][l])
good = 1;
else
bad = 1;
endif
endif
endfor
endif
endfor
endfor
return this:_relation_result(good, bad, verb[4] == "a");
.
#93:26
"Common code for is_reflexive, is_symmetric, and is_transitive.";
{good, bad, flag} = args;
if (good && (!bad))
result = 1;
elseif ((!good) && bad)
result = -1;
else
result = 0;
endif
return flag * result;
.
#93:27
":is_partial_ordering(M) => 1 iff M is a reflexive, asymmetric, transitive relation.";
{mat} = args;
return ((this:is_asymmetric(mat) == this:is_reflexive(mat)) == this:is_transitive(mat)) == 1;
.
#94:0
"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:set.";
if (!($perm_utils:controls(caller_perms(), this) || (this == caller)))
return E_PERM;
else
result = $gender_utils:set(this, args[1]);
this.gender = (typeof(result) == STR) ? result | args[1];
return result;
endif
.
#94:1
if (player.wizard || (player == this.owner))
player:tell(this:set_gender(iobjstr) ? "Gender and pronouns set." | "Gender set.");
else
player:tell("Permission denied.");
endif
.
#95:0
if (this:is_writable_by(caller_perms()))
this.date = time();
this.read_by = {};
if (this.location == $news)
$news:notify_change();
endif
return pass(@args);
else
return E_PERM;
endif
.
#95:1
return args[1] in this.read_by;
.
#95:2
this.read_by = setadd(this.read_by, args[1]);
.
#95:3
astr = tostr("Author: ", this.owner:title(), " (", this.owner, ")");
text = {tostr(astr, $string_utils:right(tostr("Subject: ", this:title()), player:linelen() - length(astr)))};
text = {@text, tostr("Last Update: ", $news:date(this.date)), "", @this:text(), $string_utils:space(20, "-")};
this:mark(player);
return text;
.
#95:4
this.date = time();
.
#95:5
if (!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:look_self();
player:tell();
for line in (this:text())
player:tell(line);
endfor
player:tell();
endif
.
#95:6
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:40 1997 CDT by Wizard (#2).";
.
#96:0
if (!$object_utils:isa(player, $builder))
return player:tell("Sorry, you are not authorized to operate this machine.");
endif
selection = {};
button = tonum(args[1]);
if ((!this.on) && (button != 0))
return player:tell(this.name, " is turned off. Type 'press 0 on ", this.name, "' to switch it on.");
endif
if (button < 6)
if (button == 0)
this:on_off();
this:look_self();
elseif (button == 1)
player:tell("Start recorder(s) submenu:");
selection = this:select();
this:start(selection);
this:look_self();
elseif (button == 2)
player:tell("Stop recorder(s) submenu:");
selection = this:select();
this:stop(selection);
this:look_self();
elseif (button == 3)
player:tell("Switch on microphone(s) submenu:");
selection = this:select();
this:mic_on(selection);
this:look_self();
elseif (button == 4)
player:tell("Switch off microphone(s) submenu:");
selection = this:select();
this:mic_off(selection);
this:look_self();
elseif (button == 5)
player:tell_lines(this.help_msg);
endif
else
player:tell("Oops...seems like you missed the right button? Please try again (0-5)");
endif
"Last modified Sat Mar 23 07:14:52 1996 CST by Jan (#2).";
.
#96:1
"create a new note and give it a name";
if (caller != this)
return E_PERM;
endif
new = player:_create($note, player);
if (typeof(new) == ERR)
player:notify(">>>   A L E R T   <<<");
player:notify("Your resource quota is exceeded, so I can't create any new logs for you at this time. Please check to see if you can free up quota by recycling some of the objects you already own, or type '@measure new me' if you have created more than 10 new objects recently. If this does not help and you still need more quota send a MOO mail to Cynthia or Jan.");
return $nothing;
endif
message = "a label for this log";
label = $command_utils:read(message);
$building_utils:set_names(new, label);
player:tell(">> Loading tape ", label, ". Done. <<");
return new;
"Last modified Wed Feb  5 15:25:29 1997 CST by Jan (#2).";
.
#96:2
"Routine that assigns a note to a recorder and turns the recorder on";
if (caller != this)
return E_PERM;
endif
recorder = args[1];
log = this:create_note();
if (log != $nothing)
player:tell("Starting recorder in ", recorder.location.name, ".");
player:tell(">> Recorder in ", recorder.location.name, " turned on <<");
string = (((">> A red light on the " + recorder.name) + " flashes to indicate that the recorder in ") + recorder.location.name) + " has been turned on <<";
this:announce(recorder.location, string);
recorder.note = log;
recorder.on = 1;
recorder.note.text = {@recorder.note.text, (("-- Start log: " + $time_utils:time_sub("$D, $N $t, $Y $o:$M:$S $p ")) + $network.MOO_name) + " time --"};
recorder.note.text = {@recorder.note.text, ""};
else
player:tell(">>> NO RECORDERS STARTED <<<");
endif
"Last modified Fri Oct  1 06:46:07 1999 CDT by Wizard (#2).";
.
#96:3
"Stop recorder and forward log via email";
if (caller != this)
return E_PERM;
endif
recorder = args[1];
recorder.note.text = {@recorder.note.text, ""};
recorder.note.text = {@recorder.note.text, (("-- End log: " + $time_utils:time_sub("$D, $N $t, $Y $o:$M:$S $p ")) + $network.MOO_name) + " time --"};
recorder.note.text = {@recorder.note.text, ""};
string = (((">> The red light on " + recorder.name) + " goes out. The recorder in ") + recorder.location.name) + " has been turned off. <<";
this:announce(recorder.location, string);
player:tell(">> Recorder in ", recorder.location.name, " turned off. <<");
player:tell("");
player:tell("PS! You can get an HTML formatted version of ", recorder.note.name, " by pointing your web browser to http://", $network.site, ":", $network.webport, "/", tonum(recorder.note), " and saving it as HTML source to your machine.");
if (player == recorder.note.owner)
question = ("Would you like me to send the log '" + recorder.note.name) + "' to your registered email address?";
if ($command_utils:yes_or_no(question))
player:tell("Mailing ", recorder.note.name, " to ", player.email_address, ".");
player:tell("... ", length(recorder.note.text), " lines ...");
suspend(0);
$network:sendmail(player.email_address, recorder.note.name, "", @recorder.note.text);
endif
endif
player:tell(">> Ejecting tape ", recorder.note.name, ". Moving it to you. Done. <<");
recorder.note:moveto(player);
recorder.on = 0;
recorder.note = #-1;
"Last modified Fri Feb  6 08:37:28 1998 CST by Wizard (#2).";
.
#96:4
if (caller != this)
return E_PERM;
endif
selection = args[1];
for item in (selection)
if (!this:check_recorder(item, verb))
player:notify((verb == "start") ? ("Recorder in " + n.location.name) + " is already turned on." | (("Recorder in " + item.location.name) + " is already turned off"));
else
if (verb == "start")
this:start_recorder(item);
else
this:stop_recorder(item);
endif
endif
endfor
if (verb == "stop")
player:tell("A gentle reminder: Logs take up a lot of database space so please remember to recycle them when you no longer need them. Thank you.");
endif
"Last modified Thu Mar 21 09:51:35 1996 CST by Jan (#2).";
.
#96:5
if (caller != this)
return E_PERM;
endif
recorder = args[1];
action = args[2];
if ((action == "start") && recorder.on)
return 0;
elseif ((action == "stop") && (!recorder.on))
return 0;
endif
return 1;
"Last modified Sun Mar 17 14:56:17 1996 CST by Jan (#2).";
.
#96:6
if (!this.on)
this:erase_display();
else
this:refresh_display();
endif
"Last modified Thu Mar 21 01:02:48 1996 CST by Jan (#2).";
.
#96:7
if (caller != this)
return E_PERM;
endif
LIST = args[1];
element = args[2];
LIST = {@LIST, element};
return LIST;
"Last modified Mon Mar 18 05:15:44 1996 CST by Jan (#2).";
.
#96:8
"Modified 8/25/99 by Jan to clean up the display for use in Xpress";
if (caller != this)
return E_PERM;
endif
rooms = args[1];
record = args[2];
icom = args[3];
su = $string_utils;
if (length(this.name) > 37)
name = this.name[1..36];
else
name = this.name;
endif
player:tell("  ___________________________________");
player:tell(" /__________________________________/|");
if (this.on)
player:tell(" |    [0] POWER OFF  [5] HELP       ||");
player:tell(" |    [1] START REC  [3] MIC ON     ||");
player:tell(" |    [2] STOP REC   [4] MIC OFF    ||");
usage = tostr("Type 'Press 0-5 on ", this.name, "' to operate.");
else
player:tell(" |    [0] POWER ON   [5] HELP       ||");
usage = tostr("Type 'Press 0 on ", this.name, "' to power up.");
endif
player:tell(" |----------------------------------||");
player:tell(" |ROOM|", su:center(rooms[1], 5), "|", su:center(rooms[2], 5), "|", su:center(rooms[3], 5), "|", su:center(rooms[4], 5), "|", su:center(rooms[5], 5), "||");
player:tell(" |----+-----+-----+-----+-----+-----||");
player:tell(" |REC |", su:center(record[1], 5), "|", su:center(record[2], 5), "|", su:center(record[3], 5), "|", su:center(record[4], 5), "|", su:center(record[5], 5), "||");
player:tell(" |----+-----+-----+-----+-----+-----||");
player:tell(" |MIC |", su:center(icom[1], 5), "|", su:center(icom[2], 5), "|", su:center(icom[3], 5), "|", su:center(icom[4], 5), "|", su:center(icom[5], 5), "||");
player:tell(" |__________________________________||");
player:tell("/____/_____/_____/_____/_____/_____/ |");
player:tell("|___________________________jan96__|/");
player:tell(usage);
"Last modified Fri Oct  1 06:44:34 1999 CDT by Wizard (#2).";
.
#96:9
if (caller != this)
return E_PERM;
endif
rooms = {};
record = {};
icom = {};
for n in [1..5]
rooms = this:insert(rooms, "");
record = this:insert(record, "");
icom = this:insert(icom, "");
endfor
this:display(rooms, record, icom);
"Last modified Wed Mar 20 23:51:23 1996 CST by Jan (#2).";
.
#96:10
"Modified 8/25/99 by Jan to clean up the display for use in Xpress";
if (caller != this)
return E_PERM;
endif
rooms = {};
record = {};
icom = {};
i = 0;
for n in (this.recorders)
i = i + 1;
if (length(n.location.name) > 5)
rooms = this:insert(rooms, n.location.name[1..5]);
else
rooms = this:insert(rooms, n.location.name);
endif
if (n.on)
record = this:insert(record, " [ON]");
else
record = this:insert(record, "[OFF]");
endif
if (n.mic)
icom = this:insert(icom, "[ON] ");
else
icom = this:insert(icom, "[OFF]");
endif
endfor
while (i < 5)
rooms = this:insert(rooms, "");
record = this:insert(record, "");
icom = this:insert(icom, "");
i = i + 1;
endwhile
this:display(rooms, record, icom);
"Last modified Fri Oct  1 06:45:51 1999 CDT by Wizard (#2).";
.
#96:11
if (caller != this)
return E_PERM;
endif
microphones = {};
if (this.on)
player:tell("Shutting down...");
for n in (this.recorders)
if (n.mic)
microphones = {@microphones, n};
endif
if (n.on)
this:stop_recorder(n);
endif
endfor
this:mic_off(microphones);
this.on = 0;
player:tell("System shut down.");
player.location:announce(player.name, " turns ", this.name, " off.");
else
player:tell("You hear a low humming noice as ", this.name, " powers up and the display lights up.");
player.location:announce(player.name, " turns ", this.name, " on.");
this.on = 1;
endif
"Last modified Sat Mar 23 06:51:19 1996 CST by Jan (#2).";
.
#96:12
return this.name + (this.on ? " (ON)" | "");
"Last modified Wed Mar 20 23:17:41 1996 CST by Jan (#2).";
.
#96:13
if (caller != this)
return E_PERM;
endif
recorders = this.recorders;
selected = {};
if (!recorders)
return player:tell("Sorry, no recording and intercom devices connected. Please choose setup to configure the system.");
else
done = 0;
i = 0;
message = "a room number or type 'all' to select all";
for n in (recorders)
i = i + 1;
player:tell("   [", i, "] ", n.location.name);
endfor
while (!done)
choice = $command_utils:read(message);
if ((tonum(choice) > tonum(i)) && (choice != "all"))
player:tell("Please choose a number between 1 and ", length(recorders));
else
done = 1;
endif
endwhile
if (choice == "all")
selected = recorders;
else
selected = {@selected, recorders[tonum(choice)]};
endif
return selected;
endif
"Last modified Thu Mar 21 11:24:55 1996 CST by Jan (#2).";
.
#96:14
if (caller != this)
return E_PERM;
endif
selection = args[1];
for item in (selection)
if (!this:check_mic(item, verb))
player:notify((verb == "mic_on") ? ("Microphone in " + item.location.name) + " is already turned on." | (("Microphone in " + item.location.name) + " is already turned off"));
else
if (verb == "mic_on")
item.mic = 1;
player:tell(">> Microphone in ", item.location.name, " switched on <<");
string = (((">> A green light on the " + item.name) + " flashes to indicate that the microphone in ") + item.location.name) + " has been switched on <<";
this:announce(item.location, string);
else
item.mic = 0;
player:tell(">> Microphone in ", item.location.name, " switched off <<");
string = (((">> The green light on the " + item.name) + " goes out. The microphone in ") + item.location.name) + " has been switched off <<";
this:announce(item.location, string);
endif
endif
endfor
"Last modified Fri Mar 22 07:06:51 1996 CST by Jan (#2).";
.
#96:15
if (caller != this)
return E_PERM;
endif
recorder = args[1];
action = args[2];
if ((action == "mic_on") && recorder.mic)
return 0;
elseif ((action == "mic_off") && (!recorder.mic))
return 0;
endif
return 1;
"Last modified Thu Mar 21 10:27:49 1996 CST by Jan (#2).";
.
#96:16
message = args[1];
origin = args[2];
destination = this.recorders;
origin_name = args[2].location.name;
string = (("[" + origin_name) + "] ") + message;
if (origin.location == this.location)
for intercom in (destination)
$command_utils:suspend_if_needed(0);
if (intercom.mic && (intercom.location != origin.location))
if (intercom.on)
intercom.note.text = {@intercom.note.text, string};
endif
this:announce(intercom.location, string);
endif
endfor
else
this:announce(this.location, string);
endif
"Last modified Sat Mar 23 06:39:37 1996 CST by Jan (#2).";
.
#96:17
location = args[1];
message = args[2];
for person in (location.contents)
$command_utils:suspend_if_needed(0);
person:notify(message);
endfor
"Last modified Sat Mar 23 06:34:31 1996 CST by Jan (#2).";
.
#96:18
if (caller_perms().wizard)
pass();
this.recorders = {};
this.on = 0;
this.design = "Jan@LinguaMOO, 1996";
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:40 1997 CDT by Wizard (#2).";
.
#97:0
note = this.note;
origin = this;
what = "";
for item in (args)
$command_utils:suspend_if_needed(0);
what = what + tostr(item);
endfor
if (this.on)
note.text = {@note.text, what};
endif
if (this.mic)
this.control_panel:broadcast(what, origin);
endif
"Last modified Sat Mar 23 06:36:09 1996 CST by Jan (#2).";
.
#97:1
return (this.name + (this.on ? " (recording)" | "")) + (this.mic ? " (transmitting)" | "");
"Last modified Sat Mar 23 07:03:06 1996 CST by Jan (#2).";
.
#97:2
pass(@args);
player:notify("                      ____________________________");
player:notify("                     / _________________________ /|");
player:notify("                    / __ __________________  __ / |");
player:notify("                   / __ /  ___     ___    / __ / #|");
player:notify("                  / __ /  (_*_)   (_*_)  / __ / ##|");
player:notify("                 / __ /_________________/ __ / ##/");
player:notify("                /___________________________/ ##/");
player:notify(this.on ? "                | (*) RECORDER          mic |##/" | "                | ( ) RECORDER          mic |##/");
player:notify(this.mic ? "                | (*) MICROPHONE        ### |#/" | "                | ( ) MICROPHONE        ### |#/");
player:notify("                |_______________________###_|/");
player:notify("");
"Last modified Thu Mar 21 01:22:16 1996 CST by Jan (#2).";
.
#97:3
if (caller_perms().wizard)
pass();
this.mic = 0;
this.control_panel = #-1;
this.on = 0;
this.note = #-1;
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:40 1997 CDT by Wizard (#2).";
.
#98:0
if (!args)
notify(player, "[ Please enter you question as one string and hit return. ]");
player.memory = {};
argstr = read(player);
totell = player.memory;
clear_property(player, "memory");
player:tell_lines(totell);
endif
question = (player.name + ": ") + argstr;
this.queue = listappend(this.queue, question);
player:tell("Your question has been sent to the moderator. There are ", length(this.queue), " questions in the queue.");
if ((this.moderator != 0) && (this.moderator in this.contents))
this.moderator:tell(player.name, " has just submitted a question. There are now ", length(this.queue), " questions in the queue. Type 'questions' to see them.");
endif
"Last modified Wed Jul 12 09:40:58 1995 CDT by Jan (#2).";
.
#98:1
if (((player in this.authorized) || (player == this.owner)) || player.wizard)
if (this.moderator != 0)
question = ("The current moderator is " + this.moderator.name) + ". Do you wish to take over?";
answer = $command_utils:yes_or_no(question);
if (!answer)
return player:tell("OK, command aborted.");
endif
endif
this.moderator = player;
format = (this.silent == 0) ? "open" | "panel";
lock = (this.panel_locked == 0) ? "off" | "on";
player:tell("You are now the moderator.");
player:tell("The room is currently formatted for ", format, " discussion.");
player:tell("The panel lock is currently ", lock);
player.location:announce(player.name, " is now the moderator.");
else
player:tell("Sorry, you are currently not authorized to be a moderator. If you want to hold a meeting here please talk to one of the administrators. Type 'help wizard-list'");
endif
"Last modified Wed Jul 12 09:36:53 1995 CDT by Jan (#2).";
.
#98:2
if (((player == this.moderator) || (player == this.owner)) || player.wizard)
if (length(this.queue) == 0)
return player:tell("The queue is empty.");
endif
if (!args)
player:tell("Questions:");
player:tell("---------------------------------------------------------------------------");
for n in [1..length(this.queue)]
player:tell(n, ": ", this.queue[n]);
player:tell("---------------------------------------------------------------------------");
endfor
player:tell("Commands: 'q pop', 'q pop #', 'q erase' and 'q erase #'.");
elseif (args[1] == "erase")
if (length(args) == 2)
if (tonum(args[2]) <= length(this.queue))
this.queue = listdelete(this.queue, tonum(args[2]));
return player:tell("Question number ", args[2], " erased.");
else
return player:tell(args[2], " is not the number in the queue, please try again.");
endif
else
this.queue = {};
player:tell("Queue erased.");
endif
elseif (args[1] == "pop")
if (length(args) == 2)
if (tonum(args[2]) <= length(this.queue))
this:announce_question(tonum(args[2]));
else
return player:tell(args[2], " is not the number in the queue, please try again.");
endif
else
this:announce_question(1);
endif
endif
else
player:tell("Sorry, only the moderator can retrieve questions.");
endif
"Last modified Thu Jul 13 19:47:43 1995 CDT by Bitstream (#100).";
.
#98:3
if (((player == this.moderator) || (player == this.owner)) || player.wizard)
if (!args)
format = (this.silent == 0) ? "open" | "panel";
player:tell(this.name, " is currentry formatted for ", format, " discussion.");
return;
endif
if ((!argstr) in {"open", "panel"})
return player:notify("Usage: format open|panel");
endif
this.silent = (argstr == "open") ? 0 | 1;
if (argstr == "open")
this:announce_all(player.name, " formats the room for open discussion. You may now address the panel freely.");
else
this:announce_all(player.name, " formats the room for panel discussion. Type 'ask' followed by enter to ask a question to the panel.");
endif
else
player:tell("Sorry, but only authorized persons are allowed to set the discussion format.");
endif
"Last modified Wed Jul 12 22:10:59 1995 CDT by Jan (#2).";
.
#98:4
"Copyright (C) 1999, Jan Rune Holmevik";
if (caller != this)
return E_PERM;
endif
question = args[1];
for n in (this.contents)
n:tell(player.name, ", the moderator, says, \"Question from ", this.queue[question], "\"");
endfor
this.queue = listdelete(this.queue, question);
"Last modified Sun Nov 28 04:15:41 1999 CST by Wizard (#2).";
.
#98:5
if (player in this.panel)
player:tell("You are already in the panel.");
elseif (this.panel_locked)
player:tell("I'm sorry but the panel is locked for entry.");
if (this.moderator)
this.moderator:tell(player, " name attempts to enter the panel.");
endif
else
this.audience = setremove(this.audience, player);
this.panel = listappend(this.panel, player);
this:stand();
player:tell("You step up onto the small platform stage.");
this:look_self();
player.location:announce(player.name, " steps up on the platform stage in front of ", this.name);
endif
"Last modified Fri Jul 21 19:35:41 1995 CDT by Jan (#2).";
.
#98:6
if (!(player in this.panel))
return player:tell("You are already on ", this.name, " main floor.");
endif
this.panel = setremove(this.panel, player);
if (player in this.sitters)
this:stand();
endif
this.audience = listappend(this.audience, player);
player:tell("You step down from the platform onto the main floor.");
this:look_self();
player.location:announce(player.name, " steps down from the panel and onto the main floor.");
"Last modified Fri Jul 21 21:03:32 1995 CDT by Jan (#2).";
.
#98:7
clapstring = "                                                 <Clap Clap>"[random(49)..60];
for n in (this.contents)
$command_utils:suspend_if_needed(0);
n:tell(clapstring);
endfor
"Last modified Tue Apr 30 21:26:33 1996 CDT by Jan (#2).";
.
#98:8
if (!args)
notify(player, ("Please enter your " + ((verb == "say") ? "statement" | "emote")) + " and hit return.");
player.memory = {};
argstr = read(player);
totell = this.memory;
clear_property(this, "memory");
player:tell_lines(totell);
if (!argstr)
return player:tell("You stay silent.");
endif
endif
if (verb == "say")
player:tell("You say, \"", argstr, "\"");
if (player in this.panel)
this:announce("[PANEL] ", player.name, " says, \"", argstr, "\"");
else
this:announce(player.name, " says, \"", argstr, "\"");
endif
else
this:announce_all(player.name, " ", argstr);
endif
"Last modified Sun Jul 23 12:02:06 1995 CDT by Jan (#2).";
.
#98:9
"Copyright (C) 1999, Jan Rune Holmevik";
pass(@args);
if (!player.ts_client)
if (player in this.panel)
player:tell(this:title());
player:tell_lines(this.panel_description);
this:tell_contents(setremove(this:contents(), player), this.ctype);
else
player:tell(this:title());
player:tell_lines(this.drawing);
player:tell_lines(this.description);
this:tell_contents(setremove(this:contents(), player), this.ctype);
if (this.tell_exits)
this:tell_exits();
endif
endif
endif
"Last modified Sun Nov 28 04:15:41 1999 CST by Wizard (#2).";
.
#98:10
contents = args[1];
ctype = args[2];
if ((!this.dark) && (contents != {}))
players = things = panelists = {};
for x in (contents)
if (is_player(x))
if (x in this.panel)
panelists = {@panelists, x};
else
players = {@players, x};
endif
else
things = {@things, x};
endif
endfor
if (things)
player:tell(("You see " + $string_utils:title_list(things)) + " here.");
endif
if (player in this.panel)
if (panelists)
player:tell($string_utils:title_listc(panelists), (length(panelists) == 1) ? $string_utils:pronoun_sub(" %<is>", panelists[1]) | " are", " here.");
endif
if (players)
player:tell("In the auditorium you see: " + $string_utils:title_listc(players));
endif
else
if (players)
player:tell($string_utils:title_listc(players), (length(players) == 1) ? $string_utils:pronoun_sub(" %<is>", players[1]) | " are", " here.");
endif
if (panelists)
player:tell("In the panel you see: " + $string_utils:title_listc(panelists));
endif
endif
endif
"Last modified Tue Nov  4 12:01:35 1997 CST by Wizard (#2).";
.
#98:11
if ((!args) || (!(argstr in {"on", "off"})))
return player:tell("Usage: headset on|off");
endif
if (argstr == "on")
if (player in this.gags)
player:tell("You already have the headset on.");
else
this.gags = listappend(this.gags, player);
player:tell("You find a headset in your seatpocket and put it on. You will now only hear what is being said in the panel.");
this:announce(player.name, " puts on ", player.pp, " headset.");
endif
else
if (player in this.gags)
this.gags = setremove(this.gags, player);
player:tell("You take off your headset and put it in your seatpocket. You will now be able to hear everything that's being said in ", this.name);
this:announce(player.name, " takes off ", player.pp, " headset. ", player.psc, " will now be able to hear everything that's being said in ", this.name);
else
player:tell("You don't have the headset on.");
endif
endif
"Last modified Mon Jul 10 18:16:49 1995 CDT by Bitstream (#100).";
.
#98:12
if (caller != player)
return;
endif
if (player in this.sitters)
this.sitters = setremove(this.sitters, player);
player:tell("You stand up");
this:announce(player.name, " gets up from ", player.pp, " seat.");
endif
if (player in connected_players())
this:announce(player.name, " tiptoes out.");
endif
if (player in this.panel)
this.panel = setremove(this.panel, player);
endif
if (player in this.gags)
this.gags = setremove(this.gags, player);
player:tell("You take off your headset before leaving ", this.name);
endif
if (player in this.audience)
this.audience = setremove(this.audience, player);
endif
if (player == this.moderator)
this.moderator = 0;
player:tell("You unmoderate automatically when leaving ", this.name, " to resume the role as moderator type 'moderator' when you get back.");
endif
pass(@args);
return;
"Last modified Sun Jul 23 10:13:05 1995 CDT by Jan (#2).";
.
#98:13
if (player in this.sitters)
return player:tell("You are already seated.");
else
this.sitters = listappend(this.sitters, player);
endif
if (player in this.panel)
player:tell("You sit down in a comfortable executive chair in the panel.");
this:announce(player.name, " finds a seat in the panel.");
else
player:tell("You sit down in one of the comfortable auditorium chairs.");
this:announce(player.name, " finds a seat in ", this.name, ".");
endif
if (this.program)
player:tell("There is a program on your seat, it reads. . .");
this:display_program();
endif
"Last modified Wed Jul 12 05:56:50 1995 CDT by Bitstream (#100).";
.
#98:14
if (!args)
return this:display_program();
endif
if (((player == this.moderator) || (player == this.owner)) || player.wizard)
if (args[1] == "setup")
this:announce_all(player.name, " sets up the program for ", this.name);
player:tell("Welcome to ", this.name, " setup program. You will be guided through a set of questions which will allow you to enter information about your meeting and set the room's discussion format.");
topic = $command_utils:read("title of event");
player:tell("[Please enter name (and affiliation) of the speakers, or type `.' to bypass.]");
speakers = $command_utils:read_lines();
player:tell("[Please enter the program for this event, or type `.' to bypass. MAX 50 chars. per line]");
program = $command_utils:read_lines();
this.topic = topic;
this.speakers = speakers;
this.program = program;
player:tell("Ok, program information has been recorded. Type `program' to view it, or `program setup' to change it.");
elseif (args[1] == "erase")
this:announce_all(player.name, " erases the program for ", this.name);
this.topic = "";
this.speakers = "";
this.program = "";
player:tell("Ok, ", this.name, " program erased.");
endif
else
player:tell("Sorry, but only the moderator is allowed to set up a program for ", this.name, ".");
endif
"Last modified Tue Jul 11 11:39:03 1995 CDT by Jan (#2).";
.
#98:15
"Modified by Jan 8/28/99 to prevent code from breaking when used with Xpress. Need to write a permanent fix later.";
pass(@args);
try
this.audience = listappend(this.audience, player);
this:announce(player.name, " quietly enters.");
welcome_message = ("Welcome to the " + this.name) + ". ";
if (this.topic)
welcome_message = ((welcome_message + "Today's event is ") + this.topic) + ". ";
endif
if (this.program)
welcome_message = welcome_message + "Please type `sit' to be seated. ";
endif
if (this.silent)
welcome_message = welcome_message + "The room is currently formatted for panel discussion. Type `ask' followed by enter to address the panel. ";
endif
welcome_message = welcome_message + "Type `help here' for more information about this room.";
player:tell(welcome_message);
except ERROR (ANY)
"Need to add an error handling routine here";
endtry
"Last modified Fri Oct  1 06:46:54 1999 CDT by Wizard (#2).";
.
#98:16
if ((this.topic || this.speakers) || this.program)
su = $string_utils;
width = 69;
delimiter = "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~";
heading = "*  A U D I T O R I U M    P R O G R A M  *";
player:tell(su:left("", 5), su:center(delimiter, width));
player:tell(su:right("|", 5), su:center(heading, width), su:left("|", 5));
if (this.topic)
player:tell(su:right("|", 5), su:right("|", 70));
player:tell(su:right("|", 5), su:center("EVENT:", width), su:left("|", 5));
player:tell(su:right("|", 5), su:center(this.topic, width), su:left("|", 5));
endif
if (this.speakers)
player:tell(su:right("|", 5), su:right("|", 70));
player:tell(su:right("|", 5), su:center("SPEAKERS:", width), su:left("|", 5));
for n in (this.speakers)
player:tell(su:right("|", 5), su:center(n, width), su:left("|", 5));
endfor
endif
if (this.program)
player:tell(su:right("|", 5), su:right("|", 70));
player:tell(su:right("|", 5), su:center("PROGRAM:", width), su:left("|", 5));
for n in (this.program)
player:tell(su:right("|", 5), su:center(n, width), su:left("|", 5));
endfor
endif
player:tell(su:right("|", 5), su:right("|", 70));
player:tell(su:left("", 5), su:center(delimiter, width));
else
player:tell("No program has been set up. Type `program setup' to set up a program.");
endif
"Last modified Tue Jul 11 11:36:51 1995 CDT by Jan (#2).";
.
#98:17
if (!caller.wizard)
return player:tell("I'm sorry, but only wizards can authorize moderators.");
endif
if (!args)
player:tell("In addition to the owner of this room, ", this.owner.name, ", and the wizards of ", $network.moo_name, ", the following person(s) are authorized as moderators: ");
for n in (this.authorized)
player:tell(n.name);
endfor
player:tell("To authorize new, or remove existing moderators type `authorized add|remove player-name'");
elseif (length(args) < 2)
player:tell("Usage: authorized add|remove player-name");
else
action = args[1];
candidate = args[2];
if ((!action) in {"add", "remove"})
return player:tell("You need to specify either `add' or `remove'. Please try again.");
endif
candidate = this:check_player(candidate);
if (candidate == 0)
return player:tell(args[2], " is not a registered player. Please try again.");
endif
if (action == "add")
if (candidate in this.authorized)
return player:tell(candidate.name, " is already an authorized moderator.");
else
this.authorized = listappend(this.authorized, candidate);
player:tell(candidate.name, " has been added to the list of authorized moderators for ", this.name);
if (candidate in connected_players())
candidate:tell("You have just been added to the list of authorized moderators for ", this.name, " by ", player.name, ".");
endif
endif
else
if ((!candidate) in this.authorized)
return player:tell(candidate, " is not on the list of authorized moderators.");
else
this.authorized = setremove(this.authorized, candidate);
player:tell(candidate.name, " has been removed from the list of authorized moderators for ", this.name);
if (candidate in connected_players())
candidate:tell("You have just been removed from the list of authorized moderators for ", this.name, " by ", player.name, ".");
endif
endif
endif
endif
"Last modified Tue Jul 11 12:46:53 1995 CDT by Jan (#2).";
.
#98:18
if (caller != this)
return E_PERM;
endif
counter = 0;
ok = 0;
player_object_number = 0;
search_string = args[1];
hits = {};
for n in (players())
if (match(toobj(n).name, search_string))
hits = {@hits, n};
endif
endfor
if (!hits)
player_object_number = 0;
elseif (length(hits) == 1)
player_object_number = hits[1];
else
player:tell("The name ", search_string, " could refer to the following players:");
while (!ok)
for n in (hits)
counter = counter + 1;
player:tell("  ", counter, ": ", n.name);
endfor
player:tell("Select the correct name and enter the corresponding number (1 - ", counter, ")");
code = tonum(read(player));
if ((code == 1) || (code <= counter))
ok = 1;
else
player:tell(code, " is not between 1 and ", counter, ". Please enter code again.");
endif
endwhile
player_object_number = hits[code];
endif
return player_object_number;
"Last modified Tue Jul 11 12:17:46 1995 CDT by Jan (#2).";
.
#98:19
if (((player == this.moderator) || (player == this.owner)) || player.wizard)
if ((!args) || (!(argstr in {"on", "off"})))
status = (this.panel_locked == 0) ? "off" | "on";
player:tell("The panel lock is currentry ", status);
player:notify("Usage: lock on|off");
else
this.panel_locked = (argstr == "off") ? 0 | 1;
if (argstr == "off")
this:announce_all(player.name, " opens the entry to auditorium panel.");
else
this:announce_all(player.name, " locks entry to auditorium panel.");
endif
endif
else
player:tell("I'm sorry, but only authorized persons are allowed to operate the panel lock.");
endif
"Last modified Wed Jul 12 13:21:47 1995 CDT by Bitstream (#100).";
.
#98:20
if (player in this.sitters)
player:tell("You stand up");
this:announce(player.name, " gets up from ", player.pp, " chair.");
this.sitters = setremove(this.sitters, player);
elseif (caller == player)
player:tell("You are not sitting down.");
endif
"Last modified Fri Jul 21 20:05:54 1995 CDT by Jan (#2).";
.
#98:21
if (caller != this)
return E_PERM;
endif
listeners = this.contents;
if (player in this.audience)
if (this.silent)
for n in (this.panel)
if (n in listeners)
listeners = setremove(listeners, n);
endif
endfor
endif
for n in (this.gags)
if (n in listeners)
listeners = setremove(listeners, n);
endif
endfor
endif
return listeners;
"Last modified Sat Jul 22 15:07:50 1995 CDT by Jan (#2).";
.
#98:22
listeners = this:find_listeners();
for listener in (setremove(listeners, player))
$command_utils:suspend_if_needed(0);
listener:tell(@args);
endfor
"Last modified Tue Jul 25 15:26:11 1995 CDT by Jan (#2).";
.
#98:23
listeners = this:find_listeners();
for listener in (listeners)
$command_utils:suspend_if_needed(0);
listener:tell(@args);
endfor
"Last modified Wed Jul 26 08:49:31 1995 CDT by Bitstream (#100).";
.
#98:24
if (player in this.panel)
this.panel = setremove(this.panel, player);
endif
if (player in this.gags)
this.gags = setremove(this.gags, player);
endif
if (player in this.audience)
this.audience = setremove(this.audience, player);
endif
if (player in this.sitters)
this.sitters = setremove(this.sitters, player);
endif
if (player == this.moderator)
this.moderator = 0;
endif
pass(@args);
"Last modified Wed Jul 12 21:01:40 1995 CDT by Bitstream (#100).";
.
#98:25
if (player in this.panel)
text = listdelete(args, 1);
listeners = this:find_listeners();
for l in (args[1])
$command_utils:suspend_if_needed(0);
listeners = setremove(listeners, l);
endfor
for listener in (listeners)
$command_utils:suspend_if_needed(0);
listener:tell(@text);
endfor
endif
"Last modified Wed Jul 26 08:50:49 1995 CDT by Bitstream (#100).";
.
#98:26
if (caller_perms().wizard)
pass();
this.silent = 0;
this.moderator = 0;
this.queue = {};
this.panel = {};
this.authorized = {};
this.program = {};
this.panel_description = {"You see a table facing the audience and a row of executive chairs behind", "it.  On the table are several pitchers of water and some flowers.", "Type 'down' to get down from the stage."};
this.description = {"You enter a spacious auditorium with rows of chairs and a small platform stage . Type 'up' to enter the stage."};
this.speakers = "";
this.gags = {};
this.topic = "";
this.audience = {};
this.panel_locked = 0;
this.sitters = {};
this.design = "Jan@LinguaMOO, 1995";
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:40 1997 CDT by Wizard (#2).";
.
#99:0
"return player location: 0 for not at table, else tablenumber";
p = args[1];
table = 0;
for t in [1..length(this.tables)]
recipients = $list_utils:append(this.tables[t][2], this.tables[t][3]);
if (p in recipients)
table = t;
endif
endfor
return table;
"Last modified Fri Jun  9 15:18:19 2000 CDT by Wizard (#2).";
.
#99:1
"finds object location: 0 for nf, else tablenumber";
thingy = args[1];
table = 0;
for t in [1..length(this.tables)]
if (thingy in this.tables[t][3])
table = t;
endif
endfor
return table;
.
#99:2
table = 0;
for t in [1..length(this.tables)]
if (index(this.tables[t][1], args[1]) == 1)
table = t;
endif
endfor
return table;
.
#99:3
if (!argstr)
pass(@args);
else
if (dobjstr == "clock")
player:tell("The clock on the wall reads: ", $time_utils:ampm(time()));
player:tell("The time is: ", ctime());
GMT = ctime((time() + 18000) + (3600 * this.GMT_correction));
GMT = GMT[1..length(GMT) - 4];
player:tell("             ", GMT, " GMT");
elseif (index(dobjstr, "blac") == 1)
this:readboard();
elseif (t = this:is_table(dobjstr))
player:tell(this.tables[t][4]);
folks = setremove(this.tables[t][2], player);
if (folks)
player:tell("Seated at ", this.tables[t][1], " you see ", $string_utils:title_list(folks), ".");
endif
stuff = this.tables[t][3];
if (stuff)
player:tell("On ", this.tables[t][1], " you see ", $string_utils:title_list(stuff), ".");
endif
else
pass(@args);
endif
endif
.
#99:4
player:tell("==================== BLACKBOARD ===========================");
if (this.writing)
player:tell();
for line in [1..length(this.writing)]
player:tell(line, ")  ", this.writing[line]);
endfor
player:tell();
else
player:tell("");
endif
player:tell("===========================================================");
.
#99:5
this:announce_all(player.name, " writes on the blackboard...");
this.writing = {@this.writing, argstr};
.
#99:6
line = $code_utils:tonum(dobjstr);
numlines = length(this.writing);
if (line > numlines)
player:tell("Sorry, there are only ", numlines, " lines on the blackboard...");
return;
endif
this.writing = listdelete(this.writing, line);
this:announce_all(player.name, " erases some stuff from the blackboard...");
.
#99:7
if (this:is_authorized(player))
this:announce_all(player.name, " cleans the blackboard with a soapy sponge...");
this.writing = {};
endif
.
#99:8
if (!argstr)
player:tell("Sign is currently ", this.session ? "on" | "off", ".  It reads: '", this:title(), "'");
return;
endif
if (argstr == "on")
if (this:is_authorized(player))
this.session = 1;
this:announce_all(player.name, " turns on @who room message to read: '", this:title(), "'");
endif
elseif (argstr == "off")
if (this:is_authorized(player))
this.session = 0;
this:announce_all(player.name, " adjusts @who room message to read: '", this:title(), "'");
endif
else
player:tell("Usage: session on|off");
endif
.
#99:9
if (this:is_authorized(player))
if (argstr)
if (this:is_table(argstr))
player:tell("Sorry, there is already something here by that name. . .");
return;
endif
player:tell("Please describe ", argstr, ". . .");
descrip = $command_utils:read();
s = $command_utils:yes_or_no("Is this object sittable? ");
this.tables = {@this.tables, {argstr, {}, {}, descrip, s}};
this:announce_all(player.name, " sets up a ", argstr, " in the room. . .");
else
player:tell("To add a new object you must give it a name:  '@addfurniture <name>'");
endif
endif
.
#99:10
if (this:is_authorized(player))
if (!argstr)
player:tell("To remove something you must name it:  '@rmfurniture <name>'");
return;
endif
if (found = this:is_table(argstr))
this:announce_all(player.name, " removes ", this.tables[found][1], ". . .");
this.tables = listdelete(this.tables, found);
else
player:tell("Sorry, there is no furniture here by that name. . .");
endif
endif
.
#99:11
if (t = this:player_loc(player))
player:tell("You are already sitting at ", this.tables[t][1], ". . .");
return;
endif
n = 0;
if (!argstr)
sittables = {};
for n in [1..length(this.tables)]
if (this.tables[n][5])
sittables = {@sittables, n};
endif
endfor
if (sittables)
n = random(length(sittables));
endif
else
n = this:is_table(argstr);
endif
if (n)
if (this.tables[n][5])
this:announce_all(player.name, " sits down at ", this.tables[n][1], ". . .");
this.tables[n][2] = {@this.tables[n][2], player};
else
player:tell("Sorry, that is not something you can sit on...");
endif
else
player:tell("Please try again. I see no ", dobjstr, " to sit at. . .");
endif
.
#99:12
if (t = this:player_loc(player))
this.tables[t][2] = setremove(this.tables[t][2], player);
this:announce_all(player.name, " stands up from ", this.tables[t][1], ". . .");
endif
if ((!t) && (caller == player))
player:tell("You already are standing...");
endif
.
#99:13
if (is_player(args[1]))
this:stand();
endif
pass(@args);
.
#99:14
player:tell(this:title());
if ((player.display_drawings && this.drawing) && (!player.ts_client))
player:tell_lines(this.drawing);
endif
player:tell_lines(this:description());
tables = {};
for t in (this.tables)
tables = {@tables, t[1]};
endfor
if (tables)
player:tell("You see ", $string_utils:english_list(tables), ".");
endif
if (t = this:player_loc(player))
player:tell("You are sitting at ", this.tables[t][1], ".");
stuff = this.tables[t][3];
if (stuff)
player:tell("On ", this.tables[t][1], " you see ", $string_utils:title_list(stuff), ".");
endif
endif
sitters = {};
stuff = {};
for t in (this.tables)
if (t[2])
sitters = {@sitters, @t[2]};
if (folks = setremove(t[2], player))
player:tell("At ", t[1], " you see ", $string_utils:title_list(folks), ".");
endif
endif
if (t[3])
stuff = {@stuff, @t[3]};
endif
endfor
standees = {};
floorstuff = {};
for x in (this.contents)
if (is_player(x) && (x in connected_players()))
if (!(x in sitters))
standees = {@standees, x};
endif
else
if (!(x in stuff))
floorstuff = {@floorstuff, x};
endif
endif
endfor
if (folks = setremove(standees, player))
player:tell("You see ", $string_utils:title_list(folks), " standing about.");
endif
if (floorstuff)
player:tell("You see ", $string_utils:title_list(floorstuff), ".");
endif
if (this.tell_exits)
this:tell_exits();
endif
"Last modified Fri Feb  6 08:39:56 1998 CST by Wizard (#2).";
.
#99:15
for t in [1..length(this.tables)]
for p in (this.tables[t][2])
if (!((p in this.contents) && (p in connected_players())))
this.tables[t][2] = setremove(this.tables[t][2], p);
endif
endfor
for p in (this.tables[t][3])
if (!(p in this.contents))
this.tables[t][3] = setremove(this.tables[t][2], p);
endif
endfor
endfor
.
#99:16
this:correct();
pass(@args);
.
#99:17
if (t = this:player_loc(player))
"Modified to let objects such as recorders on tables pick up conversation";
recipients = $list_utils:append(this.tables[t][2], this.tables[t][3]);
for p in (recipients)
p:tell(player.name, " [to others at ", this.tables[t][1], "]: ", argstr);
endfor
else
player:tell("You say, \"", argstr, "\"");
this:announce(player.name, " says, \"", argstr, "\"");
endif
"Last modified Fri Jun  9 15:18:19 2000 CDT by Wizard (#2).";
.
#99:18
player:tell("You speak up, \"", argstr, "\"");
this:announce(player.name, " speaks up, \"", argstr, "\"");
.
#99:19
if (!valid(thingy = $string_utils:match_object(dobjstr, this)))
if (valid(thingy = $string_utils:match_object(iobjstr, this)))
thingy:put(@args);
return;
else
player:tell("huh?");
return;
endif
endif
if (!(t = this:is_table(iobjstr)))
if (valid(thingy = $string_utils:match_object(iobjstr, this)))
thingy:put(@args);
return;
else
player:tell("I see no ", iobjstr, " here...");
return;
endif
endif
if (!(thingy.location == player))
player:tell("You don't have that!");
return;
endif
if (is_player(thingy))
this:announce_all(player.name, " horses around. . .");
return;
endif
thingy:moveto(this);
if (thingy.location == this)
this:announce_all(player.name, " puts ", thingy.name, " on ", this.tables[t][1], ".");
this.tables[t][3] = setadd(this.tables[t][3], thingy);
else
player:tell("Hmm, for some reason you can't put that there...");
endif
.
#99:20
if (!valid(thingy = $string_utils:match_object(dobjstr, this)))
player:tell("huh? get what?");
return;
endif
loc = this:object_loc(thingy);
if ((x = $object_utils:has_callable_verb(dobj, verb)) && (verb_args(@x, verb) == {"this", "none", "none"}))
dobj:(verb)(@args);
else
$last_huh:(verb)(@args);
endif
if (thingy.location == player)
if (loc)
this.tables[loc][3] = setremove(this.tables[loc][3], thingy);
endif
endif
.
#99:21
t = this:player_loc(player);
if ((argstr != "") && (argstr[1] == ":"))
if (t && (!this.emote_ok))
for p in (this.tables[t][2])
p:tell(player.name, argstr[2..length(argstr)]);
endfor
else
this:announce_all(player.name, argstr[2..length(argstr)]);
endif
else
if (t && (!this.emote_ok))
for p in (this.tables[t][2])
p:tell(player.name, " ", argstr);
endfor
else
this:announce_all(player.name, " ", argstr);
endif
endif
.
#99:22
if (!argstr)
player:tell("Emoting from tables to whole room is currently ", this.emote_ok ? "enabled" | "not enabled");
return;
endif
if (argstr == "off")
if (this:is_authorized(player))
this:announce_all(player.name, " enables emoting from seats to whole room...");
this.emote_ok = 1;
endif
elseif (argstr == "on")
if (this:is_authorized(player))
this:announce_all(player.name, " disables emoting from seats to whole room...");
this.emote_ok = 0;
endif
else
player:tell("Usage: @stifle on|off");
endif
.
#99:23
if (verb[1] == "`")
if (length(verb) > 1)
name = verb[2..length(verb)];
else
name = argstr[1..index(argstr, " ") - 1];
endif
text = argstr;
else
name = argstr[1..index(argstr, " ") - 1];
text = argstr[index(argstr, " ") + 1..length(argstr)];
endif
who = $string_utils:match_player(name, player);
t = this:player_loc(player);
if ((t && (!this.emote_ok)) && (!(who in this.tables[t][2])))
if ((!valid(who)) || (who.location != player.location))
player:tell("I see no ", name, " here.");
else
who:tell(player.name, " whispers from ", this.tables[t][1], ", \"", text, "\"");
player:tell("You whisper, \"", text, "\" to ", who.name, ".");
endif
else
if ((!valid(who)) || (who.location != player.location))
who = $string_utils:match_object(name, player.location);
endif
who = valid(who) ? who.name | name;
argstr = tostr("[to ", who, "]: ", text);
if ($object_utils:has_callable_verb(player, "emote"))
player:emote(argstr);
elseif ($object_utils:has_callable_verb(player.location, "emote"))
player.location:emote(argstr);
endif
endif
"Last modified Tue Nov  4 12:04:40 1997 CST by Wizard (#2).";
.
#99:24
this:correct();
player:tell("You fix the room so tables have correct persons and objects...");
.
#99:25
"Modified, 3/23/01 by Jan. Fix for Xpress. If door is closed, treat this room as locked";
who = args[1];
if (((((who == this.owner) || this.door_open) || (this.current && (who in this.classes[this.current][2]))) || (!is_player(who))) || (args[1] in this.authorized))
return 1;
else
return 0;
endif
"Last modified Fri Apr 13 19:03:56 2001 CDT by Wizard (#2).";
.
#99:26
if (!argstr)
player:tell("The door to ", this.name, " is presently ", this.door_open ? "open." | "closed.");
return;
endif
if (index(argstr, "op"))
if (!this.door_open)
if (this:is_authorized(player))
this:announce_all(player.name, " opens the door to ", this.name, ". . .");
this.door_open = 1;
endif
else
player:tell("Uh, the door is already open...");
endif
elseif (index(argstr, "clos"))
if (this.door_open)
if (this:is_authorized(player))
this:announce_all(player.name, " closes the door to ", this.name, "...");
this.door_open = 0;
endif
else
player:tell("Uh, the door is already closed...");
endif
else
player:tell("Usage: door open|closed");
endif
.
#99:27
found = 0;
for c in [1..length(this.classes)]
if (index(this.classes[c][1], argstr) == 1)
found = c;
endif
endfor
return found;
.
#99:28
if (this:is_authorized(player))
class = this:find_class(argstr);
if (class)
this.current = class;
this:announce_all(player.name, " sets up room for ", this.classes[this.current][1], ". . .");
else
player:tell("Sorry, there does not appear to be any class by that name...");
endif
endif
.
#99:29
if (this:is_authorized(player))
c = this:find_class(argstr);
if (c && (this.classes[c][1] == argstr))
player:tell("Sorry, there is already a class by that name...");
else
this.classes = setadd(this.classes, {argstr, {}});
this.current = this:find_class(argstr);
this:announce_all(player.name, " sets up a new class called ", argstr, ". . .");
endif
endif
.
#99:30
if (this:is_authorized(player))
c = this:find_class(argstr);
if (!c)
player:tell("Sorry, there does not appear to be a class by that name...");
else
this.classes = listdelete(this.classes, c);
this.current = this.classes ? 1 | 0;
this:announce_all(player.name, " removes a class called ", argstr, ". . .");
endif
endif
.
#99:31
if (this:is_authorized(player))
if (!valid(dobj))
dobj = $player_db:find(dobjstr);
endif
if ((!valid(dobj)) || (!is_player(dobj)))
player:tell("Could not find a person named \"", dobjstr, "\".");
return;
endif
if (this.classes)
this.classes[this.current][2] = setadd(this.classes[this.current][2], dobj);
this:announce_all(player.name, " adds a name to ", this.classes[this.current][1], ".");
else
player:tell("You must @mkclass <classname> before you can register students..");
endif
endif
.
#99:32
if (this:is_authorized(player))
if (!valid(dobj))
dobj = $player_db:find(dobjstr);
endif
if ((!valid(dobj)) || (!is_player(dobj)))
player:tell("Could not find a person named \"", dobjstr, "\".");
return;
endif
if (this.classes)
if (!(dobj in this.classes[this.current][2]))
player:tell("Sorry, ", dobj.name, " does not appear to be in ", this.classes[this.current][1], ".");
else
this.classes[this.current][2] = setremove(this.classes[this.current][2], dobj);
this:announce_all(player.name, " removes a name from ", this.classes[this.current][1], ".");
endif
else
player:tell("Sorry, there are no classes established yet...");
endif
endif
.
#99:33
if (((args[1] == this.owner) || (!this.restrictions)) || (args[1] in this.authorized))
return 1;
else
player:tell("Sorry, you are not authorized to do that . . .");
return 0;
endif
.
#99:34
if (!valid(dobj))
dobj = $player_db:find(dobjstr);
endif
if ((!valid(dobj)) || (!is_player(dobj)))
player:tell("Could not find a person named \"", dobjstr, "\".");
return;
endif
if ((player == this.owner) || (player in this.authorized))
this.authorized = setadd(this.authorized, dobj);
player:tell("You add ", dobj.name, " to the list of authorized users.");
else
player:tell("Sorry, you are not authorized to do that...");
endif
.
#99:35
if (!valid(dobj))
dobj = $player_db:find(dobjstr);
endif
if ((!valid(dobj)) || (!is_player(dobj)))
player:tell("Could not find a person named \"", dobjstr, "\".");
return;
endif
if ((player == this.owner) || (player in this.authorized))
this.authorized = setremove(this.authorized, dobj);
player:tell("You remove ", dobj.name, " from the list of authorized users.");
else
player:tell("Sorry, you are not authorized to do that...");
endif
.
#99:36
if (!argstr)
player:tell("Restrictions to remodeling, registering, moderating, etc. are currently ", this.restrictions ? "in effect. " | "not in effect. ");
return;
endif
if ((player == this.owner) || (player in this.authorized))
if (argstr == "on")
this.restrictions = 1;
player:tell("Restrictions to remodeling, registering, moderating, etc. are now  in effect. ");
elseif (argstr == "off")
this.restrictions = 0;
player:tell("Restrictions to remodeling, registering, moderating, etc. are no longer in effect. ");
else
player:tell("Usage: restrictions on|off");
endif
else
player:tell("Sorry, you are not authorized to do that...");
endif
.
#99:37
player:tell("- - - - - - - - - Room STATUS Report - - - - - - - - - -");
player:tell("The door to ", this.name, " is presently ", this.door_open ? "open." | "closed.");
player:tell("Emoting from tables to whole room is currently ", this.emote_ok ? "enabled" | "not enabled");
player:tell("Restrictions to remodeling, moderating, etc. are currently ", this.restrictions ? "in effect. " | "not in effect. ");
if (this.current)
player:tell("Room can be setup for any of the following:");
for c in [1..length(this.classes)]
player:tell("    ", $string_utils:left(this.classes[c][1], 25), length(this.classes[c][2]), " registered");
endfor
player:tell("Room is currently setup for ", this.classes[this.current][1], ".");
if (this.classes[this.current][2])
player:tell("Class roster includes: ", $string_utils:title_list(this.classes[this.current][2]));
else
player:tell("Roster is empty.");
endif
endif
if (this.authorized)
player:tell("Authorized list includes: ", $string_utils:title_list(this.authorized));
else
player:tell("Authorized list is empty.");
endif
player:tell("Session Sign is currently ", this.session ? "on. " | "off. ", "It reads: '", this:title(), "'.");
player:tell("- - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
.
#99:38
player:tell_lines(this.t_text);
player:tell("<end of tutorial>");
.
#99:39
if (this.session)
return this.name + this.session_msg;
else
return this.name;
endif
.
#99:40
set_task_perms(caller_perms());
ok = this:acceptable(@args);
if (!ok)
args[1]:tell("Sorry, the door to ", this.name, " is closed...");
endif
return ok;
.
#99:41
if (this.writing == {})
player:tell(this.empty_msg);
return;
else
new_note = $recycler:_create($note, player);
if (typeof(new_note) == ERR)
player:tell(this.printout_failed_msg);
return;
endif
player:tell($string_utils:pronoun_sub(this.success_msg));
player.location:announce_all_but({player}, $string_utils:pronoun_sub(this.room_sucess_msg));
new_note.name = this.name + "_blackboard(copy)";
new_note.description = ((((("A clean white sheet of paper containing the data from the blackboard in " + this.name) + ".  Printed on ") + $time_utils:mmddyy(time())) + ". 'Read ") + this.name) + "_blackboard(copy)' for details...";
new_note.text = this.writing;
new_note:moveto(player);
endif
.
#99:42
x = $code_utils:tonum(dobjstr);
if ((x < 1) || (x > length(this.writing)))
player:tell("item number must be between 1 and ", length(this.writing), ".");
return;
endif
y = $code_utils:tonum(iobjstr);
if (x == y)
player:tell("..no changes made");
return;
endif
if ((y < 1) || (y > length(this.writing)))
player:tell("location must be between 1 and ", length(this.writing), ".");
return;
endif
if (y > length(this.writing))
y = length(this.writing);
endif
first = this.writing[1..y - 1];
last = this.writing[y..length(this.writing)];
this.writing = $list_utils:append(first, {this.writing[x]}, last);
if (x < y)
this.writing = listdelete(this.writing, x);
else
this.writing = listdelete(this.writing, x + 1);
endif
this:announce_all(player.name, " reorders a blackboard item...");
player:tell("... done");
.
#99:43
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:40 1997 CDT by Wizard (#2).";
.
#99:44
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Lock or unlock objects via Xpress.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
if (verb == "lock_html")
this.door_open = 0;
else
this.door_open = 1;
endif
return pass(@args);
"Last modified Fri Apr 13 19:03:56 2001 CDT by Wizard (#2).";
.
#100:0
this.on = 0;
this.location:announce_all(player.name, " hushes ", this.name, ".");
.
#100:1
this.on = 1;
this.location:announce_all(player.name, " activates ", this.name, ".");
for n in [1..length(this.keywords)]
try
this.keywords[n][2] = 0;
except error (ANY)
endtry
endfor
this.bogusnum = 0;
suspend(2);
player.location:announce_all_but({this}, this.name, " says, \"", this.wakeup_msg, "\"");
"Last modified Sat Apr 14 17:17:48 2001 CDT by Wizard (#2).";
.
#100:2
if (this.owner != player)
player:tell("sorry, you must be the owner of ", this.name, " to add a new word...");
return;
endif
player:tell("Type in key word: (Don't use quotes) ");
w = $command_utils:read();
player:tell("Type in desired responses to key word.");
player:tell("End each response with a carriage return. (Do NOT use quotes!)");
r = $command_utils:read_lines();
this.keywords = listappend(this.keywords, {w, 0, r});
player.location:announce_all(player.name, " teaches ", this.name, " a new word.");
.
#100:3
if ((player == this.owner) || player.wizard)
key = this.keywords[tonum(dobjstr)][1];
this.keywords = listdelete(this.keywords, tonum(dobjstr));
player:tell("deleted:  ", key);
else
player:tell("Sorry.. see ", this.owner.name, " for help in deleting key words...");
endif
"Last modified Fri Apr 13 19:13:29 2001 CDT by Wizard (#2).";
.
#100:4
player.location:announce_all(player.name, " scrutinizes ", this.name, "'s word list...");
player:tell("KEYWORDS:");
n = 1;
for x in (this.keywords)
$command_utils:suspend_if_needed(0);
player:tell($string_utils:left(n, 3), " ", x[1]);
for z in [1..length(x[3])]
player:tell("           ", x[3][z]);
endfor
n = n + 1;
endfor
.
#100:5
player:tell("WARNING: Adding patterns requires proficiency in the use of Regular");
player:tell("Expressions. You may wish to quit at this point and type 'seepats bot'");
player:tell("for a review before proceeding. If you just want to add a single ");
player:tell("keyword then quit and try 'addword' instead.");
if ($command_utils:yes_or_no("Do you really wish to continue?"))
player:tell("Add a pattern in the form of a Regular Expression.  For example,");
player:tell("suppose you wanted a pattern  that would take an input sentence ");
player:tell("'I like my mother very much' and you wanted a response of the form");
player:tell("'Tell me more about your mother' you would add the pattern:");
player:tell("my %(%w*%)");
pat = $command_utils:read();
player:tell("Now you must create some response templates. Following the previous");
player:tell("example. You would create a template like this:");
player:tell("Tell me more about your %1.");
player:tell("Enter one or more templates (hit return after each one).");
temp = $command_utils:read_lines();
this.pats = {@this.pats, {pat, temp}};
player:tell("pattern created...");
else
player:tell("I think you made the right decision  :)");
endif
.
#100:6
if (player == this.owner)
key = this.pats[tonum(dobjstr)][1];
this.pats = listdelete(this.pats, tonum(dobjstr));
player:tell("deleted:  ", key);
else
player:tell("Sorry.. see ", this.owner.name, " for help in deleting patterns...");
endif
.
#100:7
player.location:announce_all(player.name, " scrutinizes ", this.name, "'s pattern list...");
n = 1;
player:tell("PATTERNS:");
for x in (this.pats)
player:tell($string_utils:left(n, 3), " ", x[1]);
for z in [1..length(x[2])]
player:tell("           ", x[2][z]);
endfor
n = n + 1;
endfor
.
#100:8
x = $code_utils:tonum(dobjstr);
if ((x < 1) || (x > length(this.pats)))
player:tell("pattern number must be between 1 and ", length(this.pats), ".");
return;
endif
player:tell("Enter number of desired location: ");
y = $code_utils:tonum($command_utils:read());
if (x == y)
player:tell("..no changes made");
return;
endif
if ((y < 1) || (y > length(this.pats)))
player:tell("location must be between 1 and ", length(this.pats), ".");
return;
endif
if (y > length(this.pats))
y = length(this.pats);
endif
first = this.pats[1..y - 1];
last = this.pats[y..length(this.pats)];
this.pats = $list_utils:append(first, {this.pats[x]}, last);
if (x < y)
this.pats = listdelete(this.pats, x);
else
this.pats = listdelete(this.pats, x + 1);
endif
player:tell("... done");
.
#100:9
if (this.owner != player)
player:tell("sorry, you must be the owner of ", this.name, " to add a new  word...");
return;
endif
player:tell("Type in a new random response: (Don't use quotes) ");
w = $command_utils:read();
this.randresponses = listappend(this.randresponses, w);
player.location:announce_all(player.name, " teaches ", this.name, " a new  random response.");
.
#100:10
if (player == this.owner)
key = this.randresponses[tonum(dobjstr)];
this.randresponses = listdelete(this.randresponses, tonum(dobjstr));
player:tell("deleted:  ", key);
else
player:tell("Sorry.. see ", this.owner.name, " for help in deleting random responses...");
endif
.
#100:11
player.location:announce_all(player.name, " scrutinizes ", this.name, "'s random response list...");
player:tell("RANDOM RESPONSES:");
n = 1;
for x in (this.randresponses)
$command_utils:suspend_if_needed(0);
player:tell($string_utils:left(n, 3), " ", x);
n = n + 1;
endfor
.
#100:12
if (this.owner != player)
player:tell("sorry, you must be the owner of ", this.name, " to add a new question response...");
return;
endif
player:tell("Type in a new question response: (Don't use quotes) ");
w = $command_utils:read();
this.questionresponses = listappend(this.questionresponses, w);
player.location:announce_all(player.name, " teaches ", this.name, " a new question response.");
.
#100:13
if (player == this.owner)
key = this.questionresponses[tonum(dobjstr)];
this.questionresponses = listdelete(this.questionresponses, tonum(dobjstr));
player:tell("deleted:  ", key);
else
player:tell("Sorry.. see ", this.owner.name, " for help in deleting question responses...");
endif
.
#100:14
player.location:announce_all(player.name, " scrutinizes ", this.name, "'s question response list...");
player:tell("QUESTION RESPONSES:");
n = 1;
for x in (this.questionresponses)
$command_utils:suspend_if_needed(0);
player:tell($string_utils:left(n, 3), " ", x);
n = n + 1;
endfor
.
#100:15
try
if (this.on)
stringer = tostr(@args);
argystring = $string_utils:strip_chars(stringer, ",\"?.!-<>");
if (index(argystring, "[") && (!match(argystring, this.name)))
return;
endif
for p in (this.pats)
$command_utils:suspend_if_needed(0);
if (x = match(argystring, p[1]))
pr = substitute(p[2][random(length(p[2]))], x);
if (length(pr) > 6)
delay = 4 + random(5);
else
delay = 3 + random(3);
endif
fork (delay)
if (random(2) == 2)
this.location:announce_all_but({this}, tostr(this.name, " [to ", player.name, "]: ", pr));
else
this.location:announce_all_but({this}, tostr(this.name, " says, \"", pr, "\""));
endif
endfork
return;
endif
endfor
words = $string_utils:words(argystring);
for w in (words)
$command_utils:suspend_if_needed(0);
i = $list_utils:iassoc(w, this.keywords);
if (i)
fork (2 + random(5))
pat = this.keywords[i];
count = pat[2] + 1;
if (random(2) == 2)
this.location:announce_all_but({this}, tostr(this.name, " [to ", player.name, "]: ", pat[3][random(length(pat[3]))]));
else
this.location:announce_all_but({this}, tostr(this.name, " says, \"", pat[3][random(length(pat[3]))], "\""));
endif
if (count > 30)
this.location:announce_all_but({this}, "<<POOF>> ", this.name, " has blown a fuse!  Reactivate bot to continue...");
this.on = 0;
else
this.keywords[i][2] = pat[2] + 1;
endif
endfork
return;
endif
endfor
if (index(stringer, "?"))
fork (3 + random(3))
qresponse = this.questionresponses[random(length(this.questionresponses))];
this.location:announce_all_but({this}, tostr(this.name, " says, \"", qresponse, "\""));
endfork
return;
endif
if ((index(stringer, "\"") || index(stringer, "[")) || index(stringer, "<"))
this.bogusnum = this.bogusnum + 1;
if (this.bogusnum > 100)
this.location:announce_all_but({this}, "<<POOF>> ", this.name, " has blown a fuse!  Reactivate bot to continue...");
this.on = 0;
else
fork (2 + random(3))
if (random(4) == 1)
bogusresponse = this:context_response();
else
bogusresponse = this.randresponses[random(length(this.randresponses))];
endif
this.location:announce_all_but({this}, tostr(this.name, " says, \"", bogusresponse, "\""));
endfork
endif
endif
endif
except error (ANY)
endtry
"Last modified Fri Apr 13 19:13:29 2001 CDT by Wizard (#2).";
.
#100:16
if ($object_utils:isa(player, $guest))
resp = this.guest_responses[random(length(this.guest_responses))];
else
rn = random(100);
if (rn > 70)
resp = ("So, you call '" + player.home.name) + "' your home?";
elseif (rn > 50)
resp = ("Where'd you think up a name like '" + player.name) + "' anyway?";
elseif (rn > 30)
if (player.contents)
thingy = player.contents[random(length(player.contents))];
resp = ("Interesting " + thingy.name) + " you got there. Whats it for?";
else
resp = "You seem kind of empty handed, dont you carry stuff around with you?";
endif
else
spot = this.location.name;
resp = ("I think " + spot) + " is an excellent name for this place, dont you?";
endif
endif
return resp;
"Last modified Fri Apr 13 19:13:45 2001 CDT by Wizard (#2).";
.
#100:17
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:40 1997 CDT by Wizard (#2).";
.
#101:0
"Modifications by Rv";
if ((!(this in player.location.contents)) && (this.location != player))
player:tell("I see no ", this.name, " here...");
return;
endif
sn = tonum(dobjstr);
if (sn > 10)
player:tell("sorry, ", this.name, " holds a max of 10 slides.");
return;
endif
s = this.("s" + dobjstr);
size = length(s);
for thing in (player.location.contents)
thing:tell("");
thing:tell(player.name, " shows slide #", sn, ".");
thing:tell("");
thing:tell("                      * * * * * * * * * * * * * * * * * * * *");
thing:tell("");
for n in [1..size]
thing:tell(s[n]);
endfor
thing:tell("");
thing:tell("                      * * * * * * * * * * * * * * * * * * * *");
thing:tell("");
$command_utils:suspend_if_needed(0);
endfor
"Last modified Mon Sep 17 10:09:56 2001 CDT by Wizard (#2).";
.
#101:1
if ((!(this in player.location.contents)) && (this.location != player))
player:tell("I see no ", this.name, " here...");
return;
endif
sn = tonum(dobjstr);
if (sn > 10)
player:tell("sorry, ", this.name, " holds a max of 10 slides.");
return;
endif
player:tell("");
player:tell("You review slide #", sn, ".");
player:tell("");
player:tell("                      * * * * * * * * * * * * * * * * * * * *");
s = this.("s" + dobjstr);
size = length(s);
if (size > 22)
size = 22;
endif
remainder = 15 - size;
for n in [1..remainder / 2]
player:tell("");
endfor
for n in [1..size]
player:tell(s[n]);
endfor
for n in [1..remainder / 2]
player:tell("");
endfor
player:tell("                      * * * * * * * * * * * * * * * * * * * *");
.
#101:2
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:41 1997 CDT by Wizard (#2).";
.
#102:0
pass(@args);
if (this.on)
player:tell("A flashing red light on top of the camera warns you that taping is in progress. .");
endif
.
#102:1
if (this.location == player)
player:tell("better set the camera down first. . .");
return;
endif
if (!this.tapeloaded)
player:tell("better put in a tape first. . .");
else
player.location:announce_all(player.name, " turns on camera to begin recording. . .");
this.on = 1;
this.framees = {};
this.tapenum.used = 1;
endif
.
#102:2
if (!this.on)
player:tell("its not on. . .");
return;
endif
player.location:announce_all_but({this}, player.name, " turns off the camera. . .");
this.on = 0;
this.framees = {};
n = length(this.tapenum.actions);
t = time();
this.tapenum.actions = listappend(this.tapenum.actions, {"pause", t});
this.tapenum.actions[n][2] = t - this.tapenum.actions[n][2];
.
#102:3
if (this.on)
n = length(this.tapenum.actions);
if ((n >= this.tapenum.len) && this.on)
player:tell("A warning light flashes on ", this.name, ":  << Tape Full >>");
return;
endif
if ((!this.framees) || (player in this.framees))
if (this.on)
t = time();
this.tapenum.actions = listappend(this.tapenum.actions, {tostr(@args), t});
if (n > 0)
this.tapenum.actions[n][2] = t - this.tapenum.actions[n][2];
endif
endif
endif
endif
.
#102:4
if (!$object_utils:has_property(dobj, "designers"))
player:tell(dobjstr, " is not a recognizable tape. . .");
return;
endif
if (this.tapeloaded && (this.tapenum != dobj))
player:tell(". . .better remove ", this.tapenum.name, " first.");
return;
endif
pass(@args);
if (dobj.used)
player:tell("Caution: tape is not blank. . .");
endif
this.tapeloaded = 1;
this.tapenum = dobj;
dobj.loc = this;
.
#102:5
if (!this.tapeloaded)
player:tell("there is no tape loaded. .");
else
pass(@args);
if (!this.contents)
this.tapeloaded = 0;
this.tapenum.loc = 0;
this.tapenum = 0;
this.on = 0;
endif
endif
.
#102:6
if (this.location == player)
player:tell("better set the camera down first. . .");
return;
endif
if (!this.tapeloaded)
player:tell("better put in a tape first. . .");
return;
endif
if (!this.on)
player:tell("better start the camera first. .");
return;
endif
this:tell(". . . the camera pans left to right over ", this.location.name, " .  . .");
if (typeof(this.location.description) == LIST)
for n in (this.location.description)
this:tell(n);
endfor
else
this:tell(this.location.description);
endif
player.location:announce_all_but({this}, player.name, " pans the camera left and right over ", this.location.name, ". . .");
.
#102:7
if (this.location == player)
player:tell("better set the camera down first. . .");
return;
endif
if (!this.tapeloaded)
player:tell("better put in a tape first. . .");
return;
endif
if (!this.on)
player:tell("better start the camera first. .");
return;
endif
this:tell("Time Stamp: ", $time_utils:dhms(time()));
player:tell("you put a timestamp on ", this.tapenum.name, ".");
.
#102:8
player.location:announce_all(player.name, " consults the manual for ", this.name, ".");
player:tell("Operating Instructions:");
player:tell(" 1.  'Drop camera'       - set camera down before recording");
player:tell(" 2.  'Put tape1 in camera'       - tapes must be cdr/format");
player:tell(" 3.  'Remove tape1 from camera' -(recommend 'protect tape')");
player:tell(" 4.  'Start camera'                       - begin recording");
player:tell(" 5.  'Stop camera'                         - stop recording");
player:tell(" 6.  'Pan camera'             - record location description");
player:tell(" 7.  'Zoom camera on object/person'    - record o/p descrip");
player:tell(" 8.  'Timestamp camera'        - affix month/day/time stamp");
player:tell(" 9.  'ss camera with The Sun rises'        - set scenic background");
player:tell("If you experience any difficulties see CDR for assistance.");
.
#102:9
if ($command_utils:object_match_failed(dobj, iobjstr))
return;
endif
this.framees = setadd(this.framees, player);
this.framees = setadd(this.framees, dobj);
player:tell("frame list now includes: ");
for f in (this.framees)
player:tell("    ", f.name);
endfor
.
#102:10
this.framees = {};
player:tell("you clear the frame list..");
.
#102:11
if ($command_utils:object_match_failed(iobj, iobjstr))
return;
endif
if (this.location == player)
player:tell("better set the camera down first. . .");
return;
endif
if (!this.tapeloaded)
player:tell("better put in a tape first. . .");
return;
endif
if (!this.on)
player:tell("better start the camera first. .");
return;
endif
this:tell(". . .the camera zooms in on ", iobj.name, ". . .");
this:tell(iobj.description);
player.location:announce_all_but({this}, player.name, " zooms the camera in on ", iobj.name, ". . .");
.
#102:12
if (this.location == player)
player:tell("better set the camera down first. . .");
return;
endif
if (!this.tapeloaded)
player:tell("better put in a tape first. . .");
return;
endif
if (!this.on)
player:tell("better start the camera first. .");
return;
endif
this:tell(iobjstr);
player:tell("you set scene:: ", iobjstr);
.
#102:13
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:41 1997 CDT by Wizard (#2).";
.
#103:0
pass(@args);
if (this.on)
player:tell("It is running. . .");
else
player:tell("It's turned off. . .");
endif
.
#103:1
if (this.location == player)
player:tell("better set the VCR down first. . .");
return;
endif
if (this.hookedup == 0)
player:tell("better hook up the VCR to a tv first. . .");
return;
endif
if (!this.tapeloaded)
player:tell("better put in a tape first. . .");
return;
endif
player.location:announce_all(player.name, " starts playing ", this.tapenum.name, " on the VCR. . .");
this.on = 1;
this.ffon = 0;
tv = this.hookedup;
events = this.tapenum.actions;
while (((tv.on && this.on) && (!this.ffon)) && (this.tapenum.index < length(events)))
act = events[this.tapenum.index];
if (act[2] > 20)
act[2] = 20;
endif
tv.location:announce_all(" ");
tv.location:announce_all("[on ", tv.name, "]      ", act[1]);
this.tapenum.index = this.tapenum.index + 1;
if (act[1] == "pause")
suspend(2);
else
if (this.tapenum.index < length(events))
suspend(act[2]);
endif
endif
endwhile
if (((tv.on && this.on) && (!this.ffon)) && (this.tapenum.index >= length(events)))
tv.location:announce_all("[on ", tv.name, "]  << END of TAPE >>");
this.on = 0;
endif
.
#103:2
if (this.location == player)
player:tell("better set the VCR down first. . .");
return;
endif
if (this.hookedup == 0)
player:tell("better hook up the VCR to a tv first. . .");
return;
endif
if (!this.tapeloaded)
player:tell("better put in a tape first. . .");
return;
endif
player.location:announce_all(player.name, " fast forwards ", this.tapenum.name, " on the VCR. .");
this.on = 1;
this.ffon = 1;
tv = this.hookedup;
events = this.tapenum.actions;
while (((tv.on && this.on) && this.ffon) && (this.tapenum.index <= length(events)))
tv.location:announce_all("[on ", tv.name, "]  ", events[this.tapenum.index][1]);
this.tapenum.index = this.tapenum.index + 1;
endwhile
if (((tv.on && this.on) && this.ffon) && (this.tapenum.index > length(events)))
tv.location:announce_all("[on ", tv.name, "]  << END of TAPE >>");
this.on = 0;
endif
.
#103:3
player.location:announce_all(player.name, " turns off the VCR. . .");
this.on = 0;
.
#103:4
if (!this.tapeloaded)
player:tell("better put in a tape first. . .");
return;
endif
player.location:announce_all(player.name, " rewinds ", this.tapenum.name, " on ", this.name, ".");
this.tapenum.index = 1;
this.on = 0;
.
#103:5
if ($command_utils:object_match_failed(dobj, dobjstr))
return;
endif
if (this.tapeloaded && (this.tapenum != dobj))
player:tell(". .you must remove ", this.tapenum.name, " first.");
return;
endif
if (!$object_utils:has_property(dobj, "designers"))
player:tell(dobjstr, " is not a recognizable tape. . .");
return;
endif
if (dobj.used)
pass(@args);
this.tapeloaded = 1;
this.tapenum = dobj;
dobj.loc = this;
else
player:tell("tape ejected:  BLANK. . .");
endif
.
#103:6
if (!this.tapeloaded)
player:tell("there is no tape loaded. .");
else
pass(@args);
if (!this.contents)
this.tapeloaded = 0;
this.tapenum.loc = 0;
this.tapenum = 0;
endif
endif
.
#103:7
if (this.location != iobj.location)
player:tell("The VCR and the tv need to be in the same location, they are too far apart to hook up.");
return;
endif
if (!(iobj in $object_utils:descendants(this.gentv)))
player:tell("sorry- your VCR can't be connected to ", iobjstr, ".  If you experience further difficulties contact cdr for assistance . . .");
return;
endif
this.hookedup = iobj;
player.location:announce_all(player.name, " hooks up a VCR to ", iobj.name, ".");
.
#103:8
if (this.hookedup == 0)
player:tell("the vcr is not hooked up. . .");
else
player.location:announce_all(player.name, " unhooks the vcr from ", this.hookedup.name);
endif
.
#103:9
player:tell("Owners Manual: To operate your VCR just hook it up to any compatible tv, put in a tape and play!");
player:tell("Your VCR is equipped with the following controls:");
player:tell("    1. 'play vcr'        - play vcr at current tape position");
player:tell("    2. 'rewind vcr'          - rewind tape to start position");
player:tell("    3. 'stop vcr'                        -  stop playing vcr");
player:tell("    4. 'ff vcr'                          -  fast forward vcr");
player:tell("    5. 'hookup vcr to tv' - hook up vcr to any compatible tv");
player:tell("    6. 'unhook vcr'                     - unhook vcr from tv");
player:tell("If you should experience any difficulties, please contact CDR for assistance.");
.
#103:10
if (caller_perms().wizard)
pass();
this.gentv = $tv;
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:41 1997 CDT by Wizard (#2).";
.
#104:0
if (this.location == player)
player:tell("you must set the tv down first...");
return;
endif
player.location:announce_all(player.name, " turns on ", this.name, ".");
this.on = 1;
this.pause = 0;
.
#104:1
player.location:announce_all(player.name, " turns off ", this.name, ".");
this.on = 0;
this.tapenum = 0;
.
#104:2
this.channel = $code_utils:tonum(iobjstr);
this.location:announce_all(player.name, " tunes ", this.name, " to channel #", this.channel, ".");
.
#104:3
pass(@args);
if (this.on)
player:tell("It's turned on and tuned to channel #", this.channel, ".");
else
player:tell("It's turned off. . .");
endif
.
#104:4
player.location:announce_all(player.name, " consults the tv schedule. .");
player:tell("The following tapes in the tape Library can be viewed at ANY time. Just tune your tv to channel 11, turn it on, and 'play <selection #> on tv'.");
player:tell(" ");
n = 1;
for tape in (this.library.tapes)
if (length(tape.labl) > 30)
lab = tape.labl[1..30];
else
lab = tape.labl;
endif
su = $string_utils;
player:tell(su:left(n, 5), su:left(lab, 31), su:left(tape.owner.name, 15));
n = n + 1;
endfor
.
#104:5
if (!this.on)
player:tell("better turn the tv on first...");
return;
endif
if (this.channel != 11)
player:tell("To watch a selected tape you must first tune the tv to channel 11. . .");
return;
endif
n = tonum(dobjstr);
if ((n > 0) && (n <= length(this.library.tapes)))
this.tapenum = this.library.tapes[n];
player.location:announce_all(player.name, " selects the tape <<", this.tapenum.labl, ">> for viewing..");
suspend(3);
this.index = 1;
len = length(this.tapenum.actions);
while ((this.on && (this.index <= len)) && (this.pause == 0))
act = this.tapenum.actions[this.index];
if (act[2] > 20)
act[2] = 20;
endif
if (this.channel == 11)
if (act[2] > -1)
this.location:announce_all(" ");
endif
if (act[2] > -2)
this.location:announce_all("[on ", this.name, "]      ", act[1]);
else
this.location:announce_all(act[1]);
endif
endif
this.index = this.index + 1;
if (act[2] >= 0)
suspend(act[2]);
endif
endwhile
if (this.pause == 2)
act = this.tapenum.actions[this.index];
if (act[2] > 20)
act[2] = 20;
endif
if (this.channel == 11)
if (act[2] > -1)
this.location:announce_all(" ");
endif
if (act[2] > -2)
this.location:announce_all("[on ", this.name, "]      ", act[1]);
else
this.location:announce_all(act[1]);
endif
endif
this.index = this.index + 1;
endif
else
player:tell("selection error: type 'schedule ", this.name, "' to see choices..");
return;
endif
.
#104:6
if (this.pause == 0)
player.location:announce_all(player.name, " pauses ", this.name, " momentarily. . .");
this.pause = 1;
endif
.
#104:7
if (!this.on)
player:tell("better turn the tv on first...");
return;
endif
if (this.channel != 11)
player:tell("To resume watching a selected tape you must  tune the tv back to channel 11. . .");
return;
endif
if ((this.tapenum != 0) && (this.index <= length(this.tapenum.actions)))
if (this.pause == 1)
this.location:announce_all(player.name, " resumes viewing the tape.");
endif
suspend(3);
if (this.pause == 1)
this.pause = 0;
endif
len = length(this.tapenum.actions);
while ((this.on && (this.index <= len)) && (!this.pause))
act = this.tapenum.actions[this.index];
if (act[2] > 20)
act[2] = 20;
endif
if (this.channel == 11)
if (act[2] > -1)
this.location:announce_all(" ");
endif
if (act[2] > -2)
this.location:announce_all("[on ", this.name, "]      ", act[1]);
else
this.location:announce_all(act[1]);
endif
endif
this.index = this.index + 1;
if (act[2] >= 0)
suspend(act[2]);
endif
endwhile
if (this.pause == 2)
act = this.tapenum.actions[this.index];
if (act[2] > 20)
act[2] = 20;
endif
if (this.channel == 11)
if (act[2] > -1)
this.location:announce_all(" ");
endif
if (act[2] > -2)
this.location:announce_all("[on ", this.name, "]      ", act[1]);
else
this.location:announce_all(act[1]);
endif
endif
this.index = this.index + 1;
endif
else
player:tell("You need to select a tape to view first,  type 'schedule ", this.name, "'.");
endif
.
#104:8
if (this.pause != 2)
player.location:announce_all(player.name, " sets ", this.name, " on 'auto-pause'.");
player:tell("type 'res ", this.name, "' to resume playing after each pause...");
this.pause = 2;
else
player.location:announce_all(player.name, " turns off 'auto-pause'.");
player:tell("type 'res ", this.name, "' to resume playing...");
this.pause = 0;
endif
.
#104:9
if (this.tapenum != 0)
player.location:announce_all(player.name, " dumps the tape <<", this.tapenum.labl, ">>.");
n = 1;
len = length(this.tapenum.actions);
while (n <= len)
act = this.tapenum.actions[n];
player:tell(act[1]);
n = n + 1;
endwhile
else
player:tell("You need to select a tape to dump first,  type 'schedule ", this.name, "'.");
endif
.
#104:10
if (caller_perms().wizard)
pass();
this.library = $tape_library;
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:41 1997 CDT by Wizard (#2).";
.
#105:0
if (this.protected)
player:tell("error: tape is write protected. . .");
return;
endif
player.location:announce_all(player.name, " erases ", this.name, ".");
this.actions = {{"pause", 0}};
this.used = 0;
.
#105:1
this.labl = iobjstr;
player.location:announce_all(player.name, " labels ", dobj.name, ".");
.
#105:2
pass(@args);
player:tell("  The label reads--> " + this.labl);
if (this.used)
player:tell("It's got stuff stored on it. .");
else
player:tell("It's blank. .");
endif
if (this.loc != 0)
player:tell("It's inside ", this.loc.name, "(", this.loc, ").");
endif
if (this.protected)
player:tell("-- write protected --");
endif
.
#105:3
this.protected = 1;
player.location:announce_all(player.name, " write protects ", this.name, ".");
.
#105:4
if (!((player == this.owner) || player.wizard))
player:tell("error: only the owner of the tape can remove write protection.");
return;
endif
this.protected = 0;
player.location:announce_all(player.name, " removes write protect from ", this.name, ".");
.
#105:5
if (!player.wizard)
player:tell("Sorry:  Tape length locked.  See cdr for assistance.");
return;
endif
length = $code_utils:tonum(iobjstr);
player.location:announce_all(player.name, " sets length of ", this.name, " to ", length, ".");
this.len = length;
.
#105:6
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:41 1997 CDT by Wizard (#2).";
.
#106:0
this:announce_all(player.name, " looks at the ", this.name, " . . .");
pass(@args);
n = 1;
for tape in (this.tapes)
if (length(tape.labl) > 30)
lab = tape.labl[1..30];
else
lab = tape.labl;
endif
su = $string_utils;
player:tell(su:left(n, 5), su:left(lab, 31), su:left(tape.owner.name, 15), su:left(tape.name, 15));
n = n + 1;
endfor
.
#106:1
if (!(dobj in $object_utils:descendants(this.gentape)))
player:tell("sorry- ", dobjstr, " is not a cdr/format tape..");
return;
endif
this.tapes = setadd(this.tapes, dobj);
player.location:announce_all("Robbie dutifully adds ", dobj.name, " to ", this.name, ".");
.
#106:2
if (!(dobj in this.tapes))
player:tell("sorry- ", dobjstr, " is not presently stored in ", this.name, "...");
return;
endif
this.tapes = setremove(this.tapes, dobj);
player.location:announce_all("Robbie dutifully removes ", dobj.name, " from ", this.name, ".");
.
#106:3
if ((player == this.owner) || player.wizard)
pass(@args);
else
player:tell("Sorry, the Library Tape List must stay here.  To see what's scheduled just type 'schedule <any tv>'.");
endif
.
#106:4
if (caller_perms().wizard)
pass();
this.tapes = {$prog_tutorial};
this.gentape = $tape;
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:41 1997 CDT by Wizard (#2).";
.
#107:0
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:41 1997 CDT by Wizard (#2).";
.
#108:0
if (this.location != player)
player:tell("You can't read it--you're not holding it.");
elseif (!this:is_readable_by(valid(caller_perms()) ? caller_perms() | player))
player:tell("This code is baffling. You can't even make it out enough to read it aloud.");
else
player:tell("You take up reading ", this.name, " from line ", this.index, ".");
player.location:announce(player.name, " starts reading from ", this.name, ".");
fork recital (this.delay)
text = this:text();
for line in [this.index..length(text)]
this.index = line;
if (text[line] == "")
player:tell("[You pause.]");
player.location:announce("[", player.name, " pauses.]");
else
player:tell("[You read]:  ", text[line]);
player.location:announce("[", player.name, " reads]:  ", text[line]);
endif
suspend(this.delay);
endfor
this.index = 1;
player.location:announce_all();
player:tell("(You finish reading.)");
player.location:announce("[", player.name, " finishes reading.]");
endfork
this.recital = recital;
endif
.
#108:1
if (player != this.owner)
player:tell("You're not allowed to set the delay on this ", this.name);
else
this.delay = tonum(iobjstr);
player:tell("You set the delay on ", this.name, " to ", this.delay, " seconds.");
endif
.
#108:2
if ($code_utils:task_valid(this.recital))
kill_task(this.recital);
player:tell("You've stopped reciting ", this.name, " at line ", this.index, ".");
player.location:announce(player.name, " puts ", this.name, " down.");
endif
.
#108:3
this:pause(@args);
this.index = 1;
.
#108:4
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:41 1997 CDT by Wizard (#2).";
.
#109:0
if (this.location != player)
player:tell("You are not holding ", this.name, ".");
return;
endif
n = $code_utils:tonum(iobjstr);
player:tell("You set the speech pause interval to ", n, " seconds...");
this.delay = n;
.
#109:1
if (this.location != player)
player:tell("You are not holding ", this.name, ".");
return;
endif
this.pause = 0;
len = length(this.text);
while (((this.index <= len) && (!this.pause)) && (this.location == player))
s = this.text[this.index];
player.location:announce_all();
player.location:announce_all(player.name, " says, \"", s, "\"");
this.index = this.index + 1;
suspend(this.delay);
endwhile
.
#109:2
if (this.location != player)
player:tell("You are not holding ", this.name, ".");
return;
endif
this.pause = 1;
player:tell("You pause for a moment...");
.
#109:3
if (this.location != player)
player:tell("You are not holding ", this.name, ".");
return;
endif
this.index = 1;
this:resume();
.
#109:4
if (this.location != player)
player:tell("You are not holding ", this.name, ".");
return;
endif
player:tell();
for line in [1..length(this.text)]
player:tell(line, ")  ", this.text[line]);
endfor
player:tell();
.
#109:5
if (this.location != player)
player:tell("You are not holding ", this.name, ".");
return;
endif
this.text = {};
player:tell("You erase ", this.name, ".");
.
#109:6
if (this.location != player)
player:tell("You are not holding ", this.name, ".");
return;
endif
if (dobjstr == "next")
sn = this.index;
elseif ($string_utils:is_numeric(dobjstr))
sn = tonum(dobjstr);
if (sn > length(this.text))
player:tell("Try again: lecture only has ", length(this.text), " lines.");
return;
endif
else
player:tell("Usage:  give <number|next> on <lecture name>");
return;
endif
s = this.text[sn];
player.location:announce_all();
player.location:announce_all(player.name, " says, \"", s, "\"");
this.index = sn + 1;
this.pause = 1;
.
#109:7
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:41 1997 CDT by Wizard (#2).";
.
#109:8
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Assemble the most important properties for quick and easy editing.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
options = pass(@args);
options = {@options, "<OPTION VALUE=\"edit_lecture_text\">Edit Lecture Text"};
return options;
"Last modified Fri Apr 13 19:02:19 2001 CDT by Wizard (#2).";
.
#110:0
"modified by Albert 11/16/93 to work with v1.7.6. and #0:user_connected";
user = args[1];
if (callers()[1][2] != "user_connected")
return E_PERM;
endif
if (user in this.users)
this.listening = setadd(this.listening, user);
endif
if ($object_utils:has_property(caller, "hide_wizard") && caller.hide_wizard)
this:announce3("connected", user);
return;
endif
this:announce2("connected", user);
.
#110:1
"modified by Albert 11/16/93 to work with v1.7.6. and #0:user_disconnected";
user = args[1];
if ((caller != #0) || (callers()[1][2] != "user_disconnected"))
return E_PERM;
endif
if (!valid(user))
return E_ARGS;
endif
if ($object_utils:has_property(caller, "hide_wizard") && caller.hide_wizard)
this:announce3("disconnected", user);
return;
endif
this:announce2("disconnected", user);
.
#110:2
if (!((caller == this) || (player == this.owner)))
return E_PERM;
endif
who = args[1];
this.users = listappend(this.users, who);
this.interesting = listappend(this.interesting, {});
this.options = listappend(this.options, {0});
this.listening = setadd(this.listening, who);
.
#110:3
"A toggle between watching the logins and ignoring them.";
"Usage:  @login";
"";
"Note:  You can customize what you see by making a property on yourself.";
"";
"The .login_watcher property:";
"This should be a string containing your message, with substitutions.";
"     %t - time            %d - date";
"     %w - day of week";
"     %n - name of character";
"     %r - real name of character's owner";
"     %# - object number of character";
"     %l - location of character";
"     %m - message (connected or disconnected)";
"     %c - number of connected characters";
"The default message is: \"< %m: %n. Total: %c >\".";
"So you'll get messages like < connected: waffle.  Total: 42 >.";
"For messages like \"waffle connected at 12:45, for 42 users online.\",";
"set your .login_watcher property to \"%n %m at %t, for %c users online.\" ";
"Modified by Daniel@MediaMOO to include %r & %l.";
if ($object_utils:isa(player, $guest))
player:tell("Sorry, guests cannot use the login watcher.");
return;
endif
if (player in this.listening)
this.listening = setremove(this.listening, player);
player:tell("You are no longer listening to ", this.name, ".");
else
this.listening = {@this.listening, player};
player:tell("You are now listening to ", this.name, ".");
if (user = player in this.users)
if (this.interesting[user] != {})
player:tell("Login watcher is announcing only interesting people.");
endif
else
this:add_user(player);
endif
endif
.
#110:4
"@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.";
interested = player in this.users;
if (!interested)
this:add_user(player);
"Following line added 12/10/93 by Darcy";
interested = player in this.users;
endif
if ((length(args) == 1) && (args[1] == "everyone"))
this.interesting[interested] = {};
player:tell("Flushed your list of especially interesting people. Login watcher will now report all logins/logouts.");
return;
endif
interesting = this.interesting[interested];
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.interesting[player in this.users] = 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 ", this:name_and_number_list(interesting, "nobody"), (length(interesting) > 1) ? " are" | " is", " interesting.");
endif
else
players = listdelete($command_utils:player_match_result($string_utils:match_player(args), args), 1);
already = $set_utils:intersection(interesting, players);
if (already)
player:tell("You already think ", this:name_and_number_list(already), (length(already) > 1) ? " are" | " is", " interesting.");
endif
players = $set_utils:diff(players, already);
this.interesting[interested] = {@this.interesting[interested], @players};
player:tell("Added ", this:name_and_number_list(players, "nobody"), " to yourlist of interesting players.");
endif
.
#110:5
"@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
interested = player in this.users;
if (!interested)
player:tell("You already think that no one is especially interesting.");
return;
endif
interesting = this.interesting[interested];
if ((length(args) == 1) && (args[1] == "everyone"))
this.interesting[interested] = {};
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.interesting[interested] = interesting;
players = listdelete($command_utils:player_match_result($string_utils: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 ", this:name_and_number_list(already), (length(already) > 1) ? " are" | " is", " uninteresting.");
endif
this.interesting[interested] = $set_utils:diff(this.interesting[interested], remove);
player:tell("Removed ", this:name_and_number_list(remove, "nobody"), " from your list of interesting players.");
endif
.
#110:6
return $string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%n (%[#n])", args[1]), @args[2..length(args)]);
.
#110:7
"To notify you when an online-but-idle player becomes active in the MOO.";
"Usage:  @watch <player>";
"";
"Not required for login watching (unrelated actually).";
if (!valid(dobj))
dobj = $string_utils:match_player(dobjstr);
if ($command_utils:player_match_failed(dobj, dobjstr))
return;
endif
elseif (!is_player(dobj))
player:tell(dobj.name, " is not a player.");
return;
endif
if (!(dobj in connected_players()))
player:tell("< ", dobj.name, " is not logged on. >");
return;
endif
if (idle_seconds(dobj) < 60)
player:tell("< ", dobj.name, " is not idle. >");
return;
endif
player:tell("< Watching ", dobj.name, ". >");
while ((player in connected_players()) && (dobj in connected_players()))
if (idle_seconds(dobj) < 60)
player:tell("< ", dobj.name, " is no longer idle. >");
return;
endif
$command_utils:suspend(10);
endwhile
if (player in connected_players())
player:tell("< ", dobj.name, " has logged off. >");
endif
.
#110:8
return (caller_perms() == #908) ? #930:int(args[1]) | E_PERM;
.
#110:9
if (!(callers()[1][4] in {this, #4020}))
return E_PERM;
endif
return this.interesting[args[1] in this.users];
.
#110:10
if (valid(caller) && (caller != player))
return E_PERM;
endif
user = player in this.users;
if ((!user) || (this.interesting[user] == {}))
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;
endif
this:gc_intlist(user);
if (argstr == "all")
if (this.options[user][1])
this.options[user][1] = 0;
player:tell("@wwho all toggled off. Only awake people will appear in your @wwho list.");
else
this.options[user][1] = 1;
player:tell("@wwho all toggled on. Both awake and sleeping players will appear in your @wwho list.");
endif
return;
endif
if (this.options[user][1])
interesting = this.interesting[user];
else
interesting = $set_utils:intersection(this.interesting[user], connected_players());
endif
if (interesting)
this:add_relaying_task(task_id(), player);
$code_utils:show_who_listing(interesting);
this:delete_relaying_task(task_id(), player);
else
player:tell("No interesting players are online now.");
endif
.
#110:11
if (caller != this)
return E_PERM;
endif
user = args[1];
for thing in (this.interesting[user])
$command_utils:suspend_if_needed(0);
if (!($recycler:valid(thing) && is_player(thing)))
player:tell("(Login watcher is removing ", tostr(thing), " from interesting list.)");
this.interesting[user] = setremove(this.interesting[user], thing);
endif
endfor
.
#110:12
"add_relaying_task(task, target)";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this:set_relaying_tasks(listinsert(this.relaying_tasks, {this:set_relaying_task(args[1]), this:set_relaying_target(args[2])}));
else
return E_PERM;
endif
.
#110:13
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
if (this.relaying_task == args[1])
this:set_relaying_target($failed_match);
endif
return this:set_relaying_tasks(setremove(this.relaying_tasks, args));
else
return E_PERM;
endif
.
#110:14
"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
.
#110:15
"WIZARDLY: relays text coming back from $code_utils:show_who_listing";
tid = task_id();
if (tid != this.relaying_task)
this:set_relaying_task(tid);
this:set_relaying_target(this:find_relaying_target(tid));
endif
if (valid(this.relaying_target))
this.relaying_target:(verb)(@args);
endif
.
#110:16
tid = args[1];
rts = this.relaying_tasks;
changed = 0;
for rt in (rts)
if (rt[1] == tid)
if (changed)
this:set_relaying_tasks(rts);
endif
return rt[2];
elseif (!$code_utils:task_valid(rt[1]))
rts = setremove(rts, rt);
changed = 1;
endif
endfor
if (changed)
this:set_relaying_tasks(rts);
endif
return $failed_match;
.
#110:17
user = args[1];
if (callers()[2][2] != "user_connected")
return E_PERM;
endif
set_task_perms(this.owner);
if (user in this.users)
this.listening = setadd(this.listening, user);
endif
.
#110:18
if ((caller != this) || ($code_utils:verb_location() != this))
return E_PERM;
endif
message = args[1];
who = args[2];
fork (0)
hearing = {};
for some in (this.listening)
if (some.wizard)
hearing = {@hearing, some};
endif
endfor
for i in (hearing)
$command_utils:suspend_if_needed(10);
"I left the next line as connected_players() just in case a netlink wants to listen for connections. -- DavidSan";
if (valid(i) && ((i in connected_players()) || (i in this.non_player_listeners)))
if (i.location != who.location)
interesting = #-1;
if (!(i in this.users))
interesting = who;
elseif (!this.interesting[i in this.users])
interesting = who;
else
if (who in this.interesting[i in this.users])
interesting = who;
endif
endif
if (valid(interesting))
if (!$object_utils:has_property(i, "login_watcher"))
custom = "< %m: %n. Total: %c >";
elseif (typeof(i.login_watcher) == STR)
custom = i.login_watcher;
else
custom = "< %m: %n. Total: %c > (your .login_watcher property must be a string)";
endif
custom = $string_utils:substitute(custom, {{"%t", ctime()[12..16]}, {"%d", ctime()[5..10]}, {"%w", ctime()[1..3]}, {"%n", who.name}, {"%l", who.location:title()}, {"%m", message}, {"%#", tostr(who)}, {"%c", tostr(length(connected_players()))}});
i:tell(custom);
endif
endif
else
this.listening = setremove(this.listening, i);
endif
endfor
endfork
.
#110:19
if ((caller != this) || ($code_utils:verb_location() != this))
return E_PERM;
endif
message = args[1];
who = args[2];
fork (0)
hearing = this.listening;
for i in (hearing)
$command_utils:suspend_if_needed(10);
"I left the next line as connected_players() just in case a netlink wants to listen for connections. -- DavidSan";
if (valid(i) && ((i in connected_players()) || (i in this.non_player_listeners)))
if (i.location != who.location)
interesting = #-1;
if (!(i in this.users))
interesting = who;
elseif (!this.interesting[i in this.users])
interesting = who;
else
if (who in this.interesting[i in this.users])
interesting = who;
endif
endif
if (valid(interesting))
if (!$object_utils:has_property(i, "login_watcher"))
custom = "< %m: %n. Total: %c >";
elseif (typeof(i.login_watcher) == STR)
custom = i.login_watcher;
else
custom = "< %m: %n. Total: %c > (your .login_watcher property must be a string)";
endif
custom = $string_utils:substitute(custom, {{"%t", ctime()[12..16]}, {"%d", ctime()[5..10]}, {"%w", ctime()[1..3]}, {"%n", who.name}, {"%l", who.location:title()}, {"%m", message}, {"%#", tostr(who)}, {"%c", tostr(length(connected_players()))}, {"%f", i.wizard ? $string_utils:connection_hostname(who.last_connect_place) | "NO PERMISSION!"}});
i:tell(custom);
endif
endif
else
this.listening = setremove(this.listening, i);
endif
endfor
endfork
.
#110:20
if (caller_perms().wizard)
pass();
this.listening = this.users = this.interesting = this.options = this.non_player_listeners = this.relaying_tasks = {};
this.relaying_task = 0;
this.relaying_target = #-1;
endif
"Last modified Sun Aug 17 11:15:35 1997 CDT by Wizard (#2).";
.
#111:0
if (player.ts_client || player.html_view)
newdesc = pass(@args);
else
newdesc = {""};
counter = 1;
desc = "";
if (typeof(this.description) == STR)
desc = this.description;
else
for i in (this.description)
desc = desc + i;
endfor
endif
desc = $string_utils:subst(desc, {{"<", "<|"}});
tempdesc = $string_utils:explode(desc, "<");
for i in (tempdesc)
$command_utils:suspend_if_needed(0);
if (i[1] == "|")
temp2desc = $string_utils:explode(i, ">");
if ((temp2desc[1] == "|BR") || (temp2desc[1] == "|P"))
counter = counter + 1;
newdesc = {@newdesc, ""};
endif
if (length(temp2desc) > 1)
newdesc[counter] = newdesc[counter] + temp2desc[2];
endif
else
newdesc[counter] = newdesc[counter] + i;
endif
endfor
endif
return newdesc;
"Last modified Mon Apr 12 16:06:03 1999 CDT by Wizard (#2).";
.
#111:1
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:41 1997 CDT by Wizard (#2).";
.
#111:2
"===========================================================";
"Copyright (C) 1995-2001, Jan Rune Holmevik";
"This object assumes that the user marks up the page in the description, so we do not pass to parent";
"===========================================================";
user = args[1];
contents = exits = {};
html = this.description;
additional = 0;
if (this.contents)
contents = $encore_web_utils:generate_links(user, this.contents);
contents = {$string_utils:english_list(contents)};
contents = {this.web_contents_msg, @contents};
additional = 1;
endif
if (this.exits)
exits = $encore_web_utils:generate_links(user, this.exits);
exits = {$string_utils:english_list(exits)};
exits = {this.web_exits_msg, @exits};
additional = 1;
endif
if (additional)
html = {@html, tostr("<!-- Internal MOO links generated by ", $httpd.server_name, $core_version, "><P>")};
table = $encore_web_utils:generate_table(user, {contents, exits}, this, "0");
html = $list_utils:append(html, table);
"Move user to new location if java client on";
if (user.ts_client && (user.location != this))
$encore_web_utils:move_via_web(user, this);
endif
html = {@html, "<!-- End internal MOO links>"};
endif
return html;
"Last modified Fri Apr 13 19:05:15 2001 CDT by Wizard (#2).";
.
#112:0
"Syntax: code_info(<keyword/value argument pairs>)";
"Keywords accepted:";
"PREFIX_: <output prefix>";
"           defaults to this.default_prefix";
"CODE_REF_: <object:verb_name>";
"CODE_TYPE_: tag ignored since this is not a MOOSE";
"Last modified by jaime (#2) Wed Jul 17 11:02:35 1996 EDT.";
if (caller != #0)
return E_PERM;
endif
set_task_perms(caller_perms());
"This verb relies on being !d";
"Ensure proper input format.";
prefix = ((spot = "PREFIX_:" in args) && (spot < length(args))) ? args[spot + 1] | this.default_prefix;
ref = this:to_next_tag_str(args, "CODE_REF_:");
if (ref)
if (!(coderef = $code_utils:parse_verbref(ref)))
player:notify(prefix + " ERROR_: NOT_A_CODE_REF");
return;
endif
elseif ((oname = this:to_next_tag_str(args, "OBJ_:")) && (cname = this:to_next_tag_str(args, "CODE_NAME_:")))
coderef = {oname, cname};
else
player:notify(prefix + " ERROR_: MISSING_REQUIRED_ARG CODE_REF");
return;
endif
"Parse object";
if (coderef[1][1] == "#")
object = toobj(coderef[1]);
else
if (!valid(object = player:my_match_object(coderef[1])))
player:notify(prefix + " ERROR_: OBJECT_NOT_FOUND");
return;
endif
endif
code_name = strsub(coderef[2], " - ", "-");
code_name = this:simple_verb_name(code_name);
header = (((((prefix + " OBJ_: ") + tostr(object)) + " OBJ_NAME_: ") + object.name) + " CODE_NAME_: ") + code_name;
header = header + this:verb_status(object, code_name);
player:notify(header);
return;
"Amy 11/95";
"Amy 7/96: Removed code needed only for MOOSE for this version";
.
#112:1
"Last modified by jaime (#2) Wed Jul 17 11:11:58 1996 EDT.";
if (caller != #0)
return E_PERM;
endif
set_task_perms(caller_perms());
moosep = 0;
prefix = ((spot = "PREFIX_:" in args) && (spot < length(args))) ? args[spot + 1] | this.default_prefix;
result = (((((((((prefix + " MOOSEP_: ") + tostr(moosep)) + " SERVER_VERSION_: ") + server_version()) + " MACMOOSE_UTILS_VERSION_: ") + this.version) + " PROG_: ") + tostr(player.programmer)) + " WIZARD_: ") + tostr(player.wizard);
player:notify(result);
return;
.
#112:2
"Syntax: verb_status(<object>, <vname>)";
"Last modified by jaime (#2) Tue Nov 19 17:17:35 1996 EST.";
if (caller != this)
return E_PERM;
endif
set_task_perms(caller_perms());
"This verb relies on being !d";
object = args[1];
vname = args[2];
result = "";
verb_loc = $object_utils:has_verb(object, vname);
if (verb_loc)
if (verb_loc[1] != object)
result = (result + " VERB_STATUS_: EXISTS_ON_PARENT ") + tostr(verb_loc[1]);
result = (result + " VERB_PARENT_NAME_: ") + verb_loc[1].name;
else
result = result + " VERB_STATUS_: EXISTS";
endif
result = result + $macmoose_utils:verb_info_and_args(verb_loc[1], vname);
vi = verb_info(object, vname);
changeable = (player == vi[1]) || $perm_utils:controls(player, object);
result = (result + " VERB_CHANGEABLE_: ") + (changeable ? "1" | "0");
else
"Doesn't exist";
status = $perm_utils:controls(player, object) ? "COULD_DECLARE" | "CANT_DECLARE";
result = (result + " VERB_STATUS_: ") + status;
endif
return result;
"Amy 11/95";
"Amy  7/96: Removed code needed for MOOSE from this version";
.
#112:3
"Syntax: list_code(<keywordvalue argument pairs";
"Keywords accepted:";
"PREFIX_: <output prefix>";
"           defaults to this.default_prefix";
"CODE_TYPE_: tag ignored, because this is not a MOOSE";
"CODE_NAME_: <name>";
"OBJ_: <objectnum>";
"Last modified by jaime (#2) Wed Jul 17 10:53:36 1996 EDT.";
if (caller != #0)
return E_PERM;
endif
set_task_perms(caller_perms());
"This verb relies on being !d";
prefix = ((spot = "PREFIX_:" in args) && (spot < length(args))) ? args[spot + 1] | this.default_prefix;
if (!(code_name = this:to_next_tag_str(args, "CODE_NAME_:")))
player:notify(prefix + " ERROR_: MISSING_REQUIRED_ARG CODE_NAME");
return;
endif
"code_name = strsub(code_name, \" - \", \"-\")";
"Previous line should be needed only for MOOSE";
if ((spot = "OBJ_:" in args) && (spot < length(args)))
object = toobj(args[spot + 1]);
if (!valid(object))
player:notify(prefix + " ERROR_: OBJECT_NOT_FOUND");
return;
endif
else
player:notify(prefix + " ERROR_: MISSING_REQUIRED_ARG OBJ");
return;
endif
player:notify(prefix + $macmoose_utils:verb_status(object, code_name));
code = verb_code(object, code_name);
if (typeof(code) == ERR)
player:notify((prefix + " ERROR_: CANT_GET_VERB_CODE ") + tostr(code));
return;
endif
for line in (code)
player:notify((prefix + " CODE_LINE_: ") + line);
endfor
player:notify(prefix + " CODE_END");
"Amy 12/95";
"Amy  7/96: Removed code for MOOSE for this version";
.
#112:4
"...this code explicitly relies on being !d in several places...";
"Amy 12/95: added support for object.help_msg";
"If you port this to a non-MOOSE, you'll need to remove the assumption that #1.help_msg exists";
"Modified from #6:help";
"Last modified by jaime (#2) Mon Aug 12 17:29:22 1996 EDT.";
if (caller != #0)
return E_PERM;
endif
set_task_perms(caller_perms());
topic = this:to_next_tag_str(args, "TOPIC_:");
prefix = ((spot = "PREFIX_:" in args) && (spot < length(args))) ? args[spot + 1] | this.default_prefix;
"...find a db that claims to know about `what'...";
dblist = $code_utils:help_db_list();
result = $code_utils:help_db_search(topic, dblist);
if (!result)
if (valid(object = $player:match_object(topic)) && object.help_msg)
player:notify(prefix + " HELP_START");
this:prefix_notify_lines(object.help_msg, player, prefix + " HELP_LINE_:");
player:notify(prefix + " HELP_END");
return;
endif
"... note: all of the last-resort stuff...";
"... is now located on $help:find_topics/get_topic...";
$wiz_utils:missed_help(topic, result);
player:notify(prefix + " HELP_START");
player:notify(prefix + " ERROR_: HELP_NOT_FOUND");
elseif (result[1] == $ambiguous_match)
$wiz_utils:missed_help(topic, result);
player:notify_lines(tostr(prefix, " HELP_LINE_: Sorry, but the topic-name `", topic, "' is ambiguous.  I don't know which of the following topics you mean:"));
for x in ($string_utils:columnize($help:sort_topics(result[2]), 3, 60))
player:notify(tostr(prefix, " HELP_LINE_: ", "   ", x));
endfor
player:notify(prefix + " HELP_END");
else
help = result[1];
topic = result[2];
player:notify(prefix + " HELP_START");
if (topic != topic)
player:notify(tostr(prefix, "HELP_LINE_: Showing help on `", topic, "':"));
player:notify(prefix + "HELP_LINE_: ----");
endif
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)
"...these can get long...";
for line in ((typeof(text) == LIST) ? text | {text})
if (typeof(line) != STR)
player:notify(prefix + " HELP_LINE_: Odd results from help -- complain to a wizard.");
else
player:notify((prefix + " HELP_LINE_: ") + line);
endif
$command_utils:suspend_if_needed(0);
endfor
else
player:notify(prefix + " HELP_START");
player:notify(tostr((prefix + " HELP_LINE_:") + "Help DB ", help, " thinks it knows about `", topic, "' but something's messed up."));
player:notify(tostr((prefix + " HELP_LINE_: ") + "Tell ", help.owner.wizard ? "" | tostr(help.owner.name, " (", help.owner, ") or "), "a wizard."));
endif
player:notify(prefix + " HELP_END");
endif
"Amy  7/96: removed code needed for MOOSE from this version";
"Amy  8/96: added HELP_START token to help scrolling";
.
#112:5
"Last modified by jaime (#2) Mon Mar 11 14:35:29 1996 EST.";
if (caller != this)
return E_PERM;
endif
lines = args[1];
who = args[2];
prefix = args[3];
if (typeof(lines) != LIST)
lines = {lines};
endif
for line in (lines)
who:notify(tostr(prefix, " ", line));
endfor
"Amy 12/95";
.
#112:6
"Syntax: list_code(<keyword/value argument pairs";
"Keywords accepted:";
"PREFIX_: <output prefix>";
"           defaults to this.default_prefix";
"CODE_TYPE_: tag ignored, because this is not a MOOSE";
"CODE_NAME_: <name>";
"OBJ_: <objectnum>";
"VERB_DOBJ_: <dobj; defaults to none>";
"VERB_PREP_: <prep; defaults to none>";
"VERB_IOBJ_: <iobj; defaults to none>";
"PERMS_: <verb perms; defaults to rxd>";
"OWNER_: <code owner; defaults to player>";
"Returns:";
"<prefix> DECLARE_CODE_: <boolean success value>";
"Other possible values returned: ";
"<prefix> ERROR_: MISSING_REQUIRED_ARG <OBJ/CODE_NAME>";
"<prefix> ERROR_: OBJECT_NOT_FOUND";
"<prefix> ERROR_: ALREADY_DEFINED";
"<prefix> ERROR_: PERMISSION_DENIED";
"<prefix> ERROR_: UNEXPECTED_ERROR";
"Last modified by jaime (#2) Tue Nov 19 17:32:21 1996 EST.";
if (caller != #0)
return E_PERM;
endif
set_task_perms(caller_perms());
"This verb relies on being !d";
"SORT OUT VALUES FROM ARGS";
prefix = ((spot = "PREFIX_:" in args) && (spot < length(args))) ? args[spot + 1] | this.default_prefix;
owner = ((spot = "OWNER_:" in args) && (spot < length(args))) ? args[spot + 1] | player;
verb_dobj = ((spot = "VERB_DOBJ_:" in args) && (spot < length(args))) ? args[spot + 1] | this.default_verb_dobj;
verb_prep = (spot = this:to_next_tag_str(args, "VERB_PREP_:")) ? spot | this.default_verb_prep;
verb_iobj = ((spot = "VERB_IOBJ_:" in args) && (spot < length(args))) ? args[spot + 1] | this.default_verb_iobj;
code_args = {verb_dobj, verb_prep, verb_iobj};
spot = "PERMS_:" in args;
if (!spot)
perms = "rxd";
else
if ((spot == length(args)) || match(args[spot + 1], "_:$"))
perms = "";
else
perms = args[spot + 1];
endif
endif
code_name = this:to_next_tag_str(args, "CODE_NAME_:");
if (!code_name)
player:notify(prefix + " ERROR_: MISSING_REQUIRED_ARG CODE_NAME");
player:notify(prefix + " DECLARE_CODE_: 0");
return;
endif
if ((spot = "OBJ_:" in args) && (spot < length(args)))
object = toobj(args[spot + 1]);
if (!valid(object))
player:notify(prefix + " ERROR_: OBJECT_NOT_FOUND");
player:notify(prefix + " DECLARE_CODE_: 0");
return;
endif
else
player:notify(prefix + " ERROR_: MISSING_REQUIRED_ARG OBJ");
player:notify(prefix + " DECLARE_CODE_: 0");
return;
endif
"DECLARE VERB";
if (!$perm_utils:controls(player, object))
player:notify(prefix + " ERROR_: PERMISSION_DENIED");
player:notify(prefix + " DECLARE_CODE_: 0");
return;
endif
result = add_verb(object, {owner, perms, code_name}, code_args);
if (typeof(result) == ERR)
if (result == E_PERM)
player:notify(prefix + " ERROR_: PERMISSION_DENIED");
elseif (result == E_INVARG)
player:notify(prefix + " ERROR_: INVALID_ARGUMENT");
else
player:notify(prefix + " ERROR_: UNEXPECTED_ERROR");
endif
player:notify(prefix + " DECLARE_CODE_: 0");
return;
endif
"Verb added successfully";
player:notify(prefix + " DECLARE_CODE_: 1");
"Amy 12/95";
.
#112:7
"Syntax: to_next_tag(list, tag)";
"returns a list of all args from after TAG_: to next TAG_: or end of args";
"Last modified by jaime (#2) Mon Mar 11 14:35:34 1996 EST.";
data = args[1];
tag = args[2];
result = {};
if ((!(spot = tag in data)) && (spot < length(data)))
return 0;
endif
while (spot < length(data))
$command_utils:suspend_if_needed(0);
spot = spot + 1;
if (match(data[spot], "_:$"))
return result;
endif
result = {@result, data[spot]};
endwhile
return result;
"Amy 1295";
.
#112:8
"Syntax: set_code(<keyword/value argument pairs";
"Keywords accepted:";
"PREFIX_: <output prefix>";
"           defaults to this.default_prefix";
"CODE_TYPE_: tag ignored, because this is not a MOOSE";
"CODE_NAME_: <name>";
"OBJ_: <objectnum>";
"VALUE_: <text of code, with / separating lines and real /'s escaped with \\>";
"VERB_DOBJ_: <dobj; defaults to none>";
"VERB_PREP_: <prep; defaults to none; might be multiple words>";
"VERB_IOBJ_: <iobj; defaults to none>";
"PERMS_: <verb perms; defaults to rxd>";
"OWNER_: <code owner; defaults to player>";
"Last modified by jaime (#2) Mon Jul 22 00:57:53 1996 EDT.";
if (caller != #0)
return E_PERM;
endif
set_task_perms(caller_perms());
"This verb relies on being !d";
prefix = ((spot = "PREFIX_:" in args) && (spot < length(args))) ? args[spot + 1] | this.default_prefix;
code_name = this:to_next_tag_str(args, "CODE_NAME_:");
if (!code_name)
player:notify(prefix + " ERROR_: MISSING_REQUIRED_ARG CODE_NAME");
return;
endif
if ((spot = "OBJ_:" in args) && (spot < length(args)))
object = toobj(args[spot + 1]);
if (!valid(object))
player:notify(prefix + " ERROR_: OBJECT_NOT_FOUND");
return;
endif
else
player:notify(prefix + " ERROR_: MISSING_REQUIRED_ARG OBJ");
return;
endif
if ("VALUE_:" in args)
valuep = 1;
check_quotesp = 1;
value = this:list_from_tokens(args, "VALUE_:", check_quotesp);
if ((length(value) > 1) && (value[1] == "_QUOTE_ERROR"))
for error_msg in (value[2..length(value)])
player:notify((prefix + " TEXT_COLOR_: RED FEEDBACK_: ") + error_msg);
endfor
player:notify(prefix + " SET_CODE_: CODE_CHANGED? 0");
return;
endif
else
valuep = 0;
endif
if ((("VERB_DOBJ_:" in args) || ("VERB_PREP_:" in args)) || ("VERB_IOBJ_:" in args))
"There is an argument change";
old_args = verb_args(object, code_name);
if (typeof(old_args) == ERR)
player:notify((prefix + " ERROR_: ") + tostr(old_args));
return;
endif
new_args = old_args;
new_args[1] = ((spot = "VERB_DOBJ_:" in args) && (spot < length(args))) ? args[spot + 1] | new_args[1];
new_args[2] = ((spot = "VERB_PREP_:" in args) && (spot < length(args))) ? this:to_next_tag_str(args, "VERB_PREP_:") | new_args[2];
new_args[3] = ((spot = "VERB_IOBJ_:" in args) && (spot < length(args))) ? args[spot + 1] | new_args[3];
args_result = set_verb_args(object, code_name, new_args);
if (typeof(args_result) == ERR)
player:notify((prefix + " ERROR_: CANT_CHANGE_ARGS ") + tostr(args_result));
else
player:notify(prefix + " VERB_ARGS_CHANGED? 1");
player:notify(prefix + " FEEDBACK_: Verb arguments changed.");
endif
endif
if (("PERMS_:" in args) || ("OWNER_:" in args))
old_info = verb_info(object, code_name);
if (typeof(old_info) == ERR)
player:notify((prefix + " ERROR_: ") + tostr(old_info));
return;
endif
new_info = old_info;
if (spot = "PERMS_:" in args)
if ((spot == length(args)) || match(args[spot + 1], "_:$"))
new_info[2] = "";
else
new_info[2] = args[spot + 1];
endif
endif
if ((spot = "OWNER_:" in args) && (spot < length(args)))
new_info[1] = toobj(args[spot + 1]);
endif
info_result = set_verb_info(object, code_name, new_info);
if (typeof(info_result) == ERR)
player:notify((prefix + " ERROR_: CANT_CHANGE_INFO ") + tostr(info_result));
player:notify(prefix + " TEXT_COLOR_: RED FEEDBACK_: Can't change verb permissions.");
else
player:notify(prefix + " VERB_INFO_CHANGED? 1");
player:notify(prefix + " FEEDBACK_: Verb permissions changed.");
endif
endif
if (valuep)
result = set_verb_code(object, code_name, value);
if (result || (typeof(result) == ERR))
result = (typeof(result) == LIST) ? result | {result};
for item in (result)
player:notify((prefix + " TEXT_COLOR_: RED FEEDBACK_: ") + tostr(item));
endfor
player:notify(prefix + " TEXT_COLOR_: RED FEEDBACK_: Verb not compiled.");
player:notify(prefix + " SET_CODE_: CODE_CHANGED? 0");
return;
endif
player:notify(prefix + " SET_CODE_: CODE_CHANGED? 1");
player:notify(prefix + " FEEDBACK_: Verb changed.");
endif
"Amy 12/95";
.
#112:9
"Syntax: to_next_tag_str(tokens, tag)";
"returns a string from after TAG_: to next TAG_: or end of args";
"Last modified by jaime (#2) Mon Jun  3 13:52:35 1996 EDT.";
tokens = args[1];
tag = args[2];
result = $macmoose_utils:to_next_tag(tokens, tag);
$command_utils:suspend_if_needed(0);
return result ? this:reassemble_line(result) | "";
"Amy 12/95";
.
#112:10
"Syntax: set_context(list <{object_name, prop}, object <context>>)";
"Last modified by jaime (#2) Wed May  7 13:01:54 1997 EDT.";
c = caller_perms();
set_task_perms((c == #-1) ? player | c);
object_name = args[1][1];
prop = args[1][2];
if ((length(args) < 2) || (args[2] == {}))
context = player;
"Context defaults to player";
else
context = args[2];
endif
if (object_name in {$failed_match, $ambiguous_match})
return {0, object_name};
endif
if (object_name in {"me", "this", "context", "each"})
return {1, context};
endif
if (object_name == "player")
return {1, player};
endif
if ($object_utils:isa(context, $player))
who = context;
else
who = player;
endif
if ($object_utils:isa(context, $room))
where = context;
else
where = context.location;
endif
if ((typeof(object_name) == OBJ) || (object_name in {"player", "caller"}))
object = object_name;
elseif (object_name in {"my", "this"})
object_name = context;
object = context;
else
object = $string_utils:match_object(tostr(object_name), where, who);
if (object in {$failed_match, $ambiguous_match})
return {0, object};
endif
endif
return {1, object};
"Amy 7/11/94";
.
#112:11
"Syntax: prop_info(<keyword/value argument pairs>)";
"Keywords accepted:";
"PREFIX_: <output prefix>";
"           defaults to this.default_prefix";
"PROP_REF_: <prop ref in MOOSE or MOO format>";
"Last modified by jaime (#2) Wed Jul 17 11:07:21 1996 EDT.";
if (caller != #0)
return E_PERM;
endif
set_task_perms(caller_perms());
"This verb relies on being !d";
"Ensure proper input format.";
prefix = ((spot = "PREFIX_:" in args) && (spot < length(args))) ? args[spot + 1] | this.default_prefix;
if ((spot = "PROP_REF_:" in args) && (spot < length(args)))
ref = this:to_next_tag(args, "PROP_REF_:");
if (!(propref = this:parse_propref(ref)))
player:notify(prefix + " ERROR_: NOT_A_PROP_REF");
return;
endif
"Parse object";
if (propref[1][1] == "#")
object = toobj(propref[1]);
if (!valid(object))
player:notify(prefix + " ERROR_: OBJECT_NOT_FOUND");
return;
endif
elseif (propref[1] in {"this", "me", "my", "player", "context"})
object = player;
else
if (!valid(object = player:my_match_object(propref[1])))
player:notify(prefix + " ERROR_: OBJECT_NOT_FOUND");
return;
endif
endif
if (length(propref[2]) > 1)
propref = this:resolve_compound_propref(propref, player);
endif
prop_name = propref[2][1];
elseif ((oname = this:to_next_tag_str(args, "OBJ_:")) && (prop_name = this:to_next_tag_str(args, "PROP_NAME_:")))
"Object and prop name supplied separately";
object = player:my_match_object(oname);
if (!valid(object))
player:notify(prefix + " ERROR_: OBJECT_NOT_FOUND");
return;
endif
else
"No property name supplied";
player:notify(prefix + " ERROR_: MISSING_REQUIRED_ARG PROP_REF");
endif
header = (((((prefix + " OBJ_: ") + tostr(object)) + " OBJ_NAME_: ") + object.name) + " PROP_NAME_: ") + prop_name;
if (!$object_utils:has_property(object, prop_name))
header = (header + " PROP_STATUS_: ") + ($perm_utils:controls(player, object) ? "COULD_DECLARE" | "CANT_DECLARE");
player:notify(header);
return;
else
header = header + " PROP_STATUS_: EXISTS";
endif
if (prop_name in $code_utils.builtin_props)
"Is this the right thing?";
if (prop_name in {"r", "f", "w"})
perms = "rc";
owner = object.owner;
else
owner = #2;
perms = "r";
endif
else
info = property_info(object, prop_name);
if (typeof(info) == ERR)
player:notify((header + " ERROR_: CANT_GET_PROP_INFO ") + tostr(info));
return;
endif
owner = info[1];
perms = info[2];
endif
header = (header + " PROP_OWNER_: ") + tostr(owner);
header = (header + " PROP_PERMS_: ") + perms;
changeable = $perm_utils:controls(player, owner);
header = (header + " PROP_CHANGEABLE_: ") + tostr(changeable);
value = object.(prop_name);
if (typeof(value) == ERR)
player:notify((header + " ERROR_: CANT_GET_PROP_VALUE ") + tostr(value));
return;
endif
header = (header + " PROP_VALUE_: ") + $string_utils:print(value);
player:notify(header);
return;
"Amy  1/96";
"Amy  7/96: removed code needed for MOOSE from this version";
.
#112:12
"Syntax: prop_eval(string <property reference>, object <context>)";
"Returns {succcess_value, result}";
"Last modified by jaime (#2) Wed Jul 17 11:11:39 1996 EDT.";
set_task_perms(caller_perms());
context = {@args, player}[2];
"Context defaults to player";
eval_text = tostr("this=", context, "; return ", args[1], ";");
result = $code_utils:eval_d(eval_text);
if (typeof(result[2]) == ERR)
return {0, result[2]};
else
return result;
endif
"Amy 1/95 -- Made return 0 if result is E_PROPNF";
"This will not do the right thing if the content of your property is";
"Legitimatelly E_PROPNF!";
.
#112:13
"Syntax: prop_resolve(list <{object_name, prop}>, object <context>)";
"Returns {success_value, value or error message}";
"Last modified by jaime (#2) Wed May  7 12:59:42 1997 EDT.";
c = caller_perms();
set_task_perms((c == #-1) ? player | c);
prop = args[1][2];
context = {@args, player}[2];
"Context defaults to player";
object_found = this:prop_object(args[1], context);
if (object_found[1])
object = object_found[2];
else
"Object was not found";
return object_found;
endif
if (typeof(prop) == LIST)
"Handle compound of form {name, {ref1, ref2, ref3}}";
result = {1, object};
for ref in (prop)
result = this:prop_resolve_internal({result[2], ref}, context);
if ((!result[1]) || (typeof(result[2]) == ERR))
return {0, result[2]};
endif
endfor
return result;
endif
"Not a compound";
prop_ref = (tostr(object) + ".") + this:trim_prop(prop);
result = this:prop_eval(prop_ref, context);
if ((!result[1]) || typeof(result[2] == ERR))
return {0, result[2]};
else
return result;
endif
"Amy  7/94";
"Amy  7/94: Moved part of code into prop_object verb";
"Amy  2/95: Fixed set_task_perms to caller_perms rather than player!";
"Amy  6/95: Made handle compound proprefs recursively";
.
#112:14
"Syntax: prop_resolve(list <{object_name, prop}>, object <context>)";
"Returns the value, or registers an error and returns $moose_error";
"Last modified by jaime (#2) Wed May  7 13:00:41 1997 EDT.";
c = caller_perms();
set_task_perms((c == #-1) ? player | c);
prop = args[1][2];
context = {@args, player}[2];
"Context defaults to player";
object_found = this:prop_object(args[1], context);
if (object_found[1])
object = object_found[2];
else
"Object was not found";
player:tell("Can't find an object named " + tostr(args[1][1]));
return;
endif
if (typeof(prop) == LIST)
"Handle compound of form {name, {ref1, ref2, ref3}}";
result = {1, object};
for ref in (prop)
last = result[2];
result = this:prop_resolve_internal({result[2], ref}, context);
if ((!result[1]) || (typeof(result[2]) == ERR))
if (result[2] == E_PERM)
error_msg = {"Permission denied.", ((("The property " + $string_utils:print(ref)) + " of object '") + $string_utils:nn(last)) + "' is not readable by you."};
else
error_msg = ((("Can't find a property " + $string_utils:print(ref)) + " of an object '") + $string_utils:nn(last)) + "'.";
endif
player:tell(error_msg);
return;
endif
endfor
return result[2];
endif
"Not a compound";
if (typeof(prop) != STR)
"Amy 10/95--are there situations in which this should return an error?";
prop = tostr(prop);
endif
prop_ref = (tostr(object) + ".") + this:trim_prop(prop);
result = this:prop_eval(prop_ref, context);
if ((!result[1]) || typeof(result[2] == ERR))
if (result[2] == E_PERM)
error_msg = {"Permission denied.", ((("The property " + $string_utils:print(ref)) + " of object '") + result[2]) + "' is not readable by you."};
else
error_msg = ((("Can't find a property '" + prop) + "' of an object '") + tostr(args[1][1])) + "'.";
endif
player:tell(error_msg);
return;
else
return result[2];
endif
"Amy  7/94";
"Amy  7/94: Moved part of code into prop_object verb";
"Amy  2/95: Fixed set_task_perms to caller_perms rather than player!";
"Amy  6/95: Made handle compound proprefs recursively";
.
#112:15
"Syntax: find_propref_object(list <property reference>)";
"Returns a two-valued list: {string <object of ref>, list <rest of ref>}";
"i.e. 'the big dog's friend's color' (input as a list of tokens) ==> ";
"{'the big dog', {'friend's', 'color'}}";
"Last modified by jaime (#2) Wed Sep 18 11:14:37 1996 EDT.";
ref = args[1];
if ((!ref) || (ref && ((typeof(ref) != LIST) || (typeof(ref[1]) != STR))))
return 0;
endif
"Remove surrounding parentheses -- Amy 5/96";
while ((ref[1] == "(") && (ref[length(ref)] == ")"))
ref = ref[2..length(ref) - 1];
endwhile
if (ref[1] in {"my", "\"my\""})
return {ref[1], ref[2..length(ref)]};
endif
object = "";
while (ref)
if (typeof(ref[1]) != STR)
ref[1] = tostr(ref[1]);
endif
if ((dot = index(ref[1], ".")) && (dot < length(ref[1])))
object = object + ref[1][1..dot - 1];
result = {object, {ref[1][dot + 1..length(ref[1])]}};
return result;
elseif ((pos = match(ref[1], "'s\"?,? *$")) && (length(ref) > 1))
object = object + ref[1][1..pos[1] - 1];
result = {object, ref[2..length(ref)]};
return result;
elseif ((pos = match(ref[1], "s'\"?,? *$")) && (length(ref) > 1))
"Handle s' case";
object = object + ref[1][1..pos[1]];
result = {object, ref[2..length(ref)]};
return result;
else
"Multiple word object name";
object = (object + ref[1]) + " ";
ref = listdelete(ref, 1);
endif
endwhile
"If you get here, it's not a well-formed property reference";
return 0;
"Amy 1/95 -- Removed a call to change me or my; that should be done elsewhere.";
.
#112:16
"Syntax: trim_prop(string <prop>)";
"Removes leading ., if any.";
"Used to distinguish a property from a variable of the same name";
"Last modified by jaime (#2) Mon Mar 11 14:35:45 1996 EST.";
prop = args[1];
if ((length(prop) > 1) && (prop[1] == "."))
prop = prop[2..length(prop)];
endif
return prop;
"Amy  6/95";
"Amy  6/95: Made no longer remove quotes";
.
#112:17
"Copied from Property Utilities (#145):parse_propref by Amy (#78) Fri Jan  5 18:08:14 1996 EST";
"Syntax: parse_propref(list <MOOSE property reference>)";
"Given a MOOSE or MOO property reference,";
"Returns a list: {object, {property, subproperty}}";
"Returns 0 if this is not a well formed property reference.";
"";
"Note: in the compiler, prop references of the MOO form foo.bar";
"Are compiled by $compiler:vars_for_dot_syntax";
"Which is called from $compiler:variable_values_line";
"Last modified by jaime (#2) Mon Mar 11 14:35:46 1996 EST.";
ref = args[1];
result = "";
split_ref = this:find_propref_object(ref);
if (!split_ref)
"Not a valid property reference";
return 0;
endif
object = split_ref[1];
ref = split_ref[2];
prop = {};
while (length(ref) > 1)
if (spos = match(ref[1], "'s\"?,? *$"))
prop = {@prop, this:trim_prop(ref[1][1..spos[1] - 1])};
ref = listdelete(ref, 1);
elseif (apos = match(ref[1], "s'\"?,? *$"))
"Handle s' forms";
"My cousins' car ==> this.cousins.car";
prop = {@prop, this:trim_prop(ref[1][1..apos[1]])};
ref = listdelete(ref, 1);
else
"Ill-formed prop ref: token which is not the last is not possessive.";
return 0;
endif
endwhile
if (!ref)
"Ill-formed prop ref: Nothing after the my or last possessive.";
return 0;
endif
"One token left.";
prop = {@prop, this:trim_prop(ref[1])};
if (!(object && prop))
result = 0;
else
result = {object, prop};
endif
return result;
"Amy  7/94";
"Amy  6/95: Changed output from from {object, prop.subprop}";
"           to {object, {prop, subprop}}";
"Amy  6/95: Made remove quotes around parts of prop";
.
#112:18
"Syntax: resolve_compound_propref({object <object>, list <property_names>}, object <context>";
"Last modified by jaime (#2) Wed May  7 13:05:00 1997 EDT.";
c = caller_perms();
set_task_perms((c == #-1) ? player | c);
object = args[1][1][1];
propnames = args[1][2];
context = args[2];
if (length(propnames) <= 1)
return args[1];
endif
final_pname = propnames[length(propnames)];
propnames = listdelete(propnames, length(propnames));
new_object = this:prop_resolve({object, propnames}, context);
if (typeof(new_object) != OBJ)
player:tell("Error in resolving property.");
return;
endif
result = {new_object, {final_pname}};
return result;
"Amy 12/95";
.
#112:19
"Syntax: list_prop(<keyword/value argument pairs";
"Keywords accepted:";
"PREFIX_: <output prefix>";
"           defaults to this.default_prefix";
"PROP_NAME_: <name>";
"OBJ_: <objectnum>";
"PERMS_: <verb perms; defaults to rc>";
"OWNER_: <prop owner; defaults to player>";
"VALUE_: <value; defaults to 0>";
"Returns:";
"<prefix> DECLARE_PROP_: <boolean success value>";
"Other possible values returned: ";
"<prefix> ERROR_: MISSING_REQUIRED_ARG <OBJ/PROP_NAME>";
"<prefix> ERROR_: OBJECT_NOT_FOUND";
"<prefix> ERROR_: ALREADY_DEFINED";
"<prefix> ERROR_: BAD_PROP_NAME";
"<prefix> ERROR_: PERMISSION_DENIED";
"<prefix> ERROR_: UNEXPECTED_ERROR";
"Last modified by jaime (#2) Tue Nov 19 17:32:35 1996 EST.";
if (caller != #0)
return E_PERM;
endif
set_task_perms(caller_perms());
"This verb relies on being !d";
"SORT OUT VALUES FROM ARGS";
prefix = ((spot = "PREFIX_:" in args) && (spot < length(args))) ? args[spot + 1] | this.default_prefix;
owner = ((spot = "OWNER_:" in args) && (spot < length(args))) ? args[spot + 1] | player;
spot = "PERMS_:" in args;
if (!spot)
perms = "rc";
else
if ((spot == length(args)) || match(args[spot + 1], "_:$"))
perms = "";
else
perms = args[spot + 1];
endif
endif
value = ((spot = "VALUE_:" in args) && (spot < length(args))) ? this:coerce(this:to_next_tag_str(args, "VALUE_:")) | 0;
if ((spot = "PROP_NAME_:" in args) && (spot < length(args)))
prop_name = args[spot + 1];
else
player:notify(prefix + " ERROR_: MISSING_REQUIRED_ARG PROP_NAME");
player:notify(prefix + " DECLARE_PROP_: 0");
return;
endif
if ((spot = "OBJ_:" in args) && (spot < length(args)))
object = toobj(args[spot + 1]);
if (!valid(object))
player:notify(prefix + " ERROR_: OBJECT_NOT_FOUND");
player:notify(prefix + " DECLARE_PROP_: 0");
return;
endif
else
player:notify(prefix + " ERROR_: MISSING_REQUIRED_ARG OBJ");
player:notify(prefix + " DECLARE_PROP_: 0");
return;
endif
if ($object_utils:has_property(object, prop_name))
player:notify(prefix + " ERROR_: ALREADY_DEFINED");
player:notify(prefix + " DECLARE_PROP_: 0");
return;
endif
"if prop_name ends in _txt or _text and the value is 0, set the beginning value to {}";
if (value == 0)
val_set = 0;
else
val_set = 1;
endif
text_strs = {"_text", "_txt"};
for text_str in (text_strs)
if (val_set == 0)
indx = index(prop_name, text_str);
if (indx != 0)
if (((length(text_str) + indx) - 1) == length(prop_name))
value = {};
val_set = 1;
endif
endif
endif
endfor
result = add_property(object, prop_name, value, {owner, perms});
if (typeof(result) == ERR)
"Try prop_to_parent";
result = this:prop_to_parent(object, prop_name, value, {owner, perms});
endif
if (typeof(result) == ERR)
player:notify((prefix + " ERROR_: CANT_DECLARE_PROP ") + tostr(result));
player:notify(prefix + " DECLARE_PROP_: 0");
return;
endif
"Property added successfully";
player:notify(prefix + " DECLARE_PROP_: 1");
"Amy 12/95";
.
#112:20
"Last modified by jaime (#2) Tue Nov 19 17:30:41 1996 EST.";
set_task_perms(caller_perms());
"Syntax: prop_to_parent(objnum <parent>, string <propname>, any <initial_value>, list <propinfo> [, boolean <preserve_child_permsP>])";
"Adds property propname to object parent, when it is already defined on";
"one or more children of that object.  Preserves the property's value";
"on the children.  Preserves the property info (owner and permissions)";
"on the children, unless the optional last argument preserve_child_permsP";
"is set to false, in which case the new parent property property info";
"override the child property info.";
parent = args[1];
propname = args[2];
initial_value = args[3];
propinfo = args[4];
"Defaults to preserving permissions on existing slots as they originally were.";
preserve_child_permsP = {@args, 1}[5];
"This verb is WIZARDLY";
if (!$perm_utils:controls(player, parent))
return E_PERM;
elseif ((propinfo[1] != player) && (!player.wizard))
return E_PERM;
endif
if ($object_utils:has_property(parent, propname))
"object already has property";
return $no_value;
endif
conflicts = $object_utils:descendants_with_property_suspended(parent, propname);
if (!conflicts)
"No property conflicts.  Adding property.";
return add_property(parent, propname, initial_value, propinfo);
endif
"Find an unused property name";
seeking_prop = 1;
$command_utils:suspend_if_needed(0);
while (seeking_prop)
temp_prop = "temp_for_prop_to_parent" + tostr(random(1000));
seeking_prop = $object_utils:descendants_with_property_suspended(parent, temp_prop);
endwhile
result = add_property(parent, temp_prop, {}, {player, ""});
for conflict in (conflicts)
$command_utils:suspend_if_needed(0);
for object in ({conflict, @$object_utils:descendants(conflict)})
object.(temp_prop) = {object.(propname), property_info(object, propname)};
endfor
endfor
for conflict in (conflicts)
$command_utils:suspend_if_needed(0);
delete_property(conflict, propname);
endfor
add_property(parent, propname, initial_value, propinfo);
"Put values on kids back";
for conflict in (conflicts)
for object in ({conflict, @$object_utils:descendants(conflict)})
$command_utils:suspend_if_needed(0);
object.(propname) = object.(temp_prop)[1];
if (preserve_child_permsP || (!$perm_utils:controls(player, object)))
"You must preserve the perms if you don't control the object";
set_property_info(object, propname, object.(temp_prop)[2]);
endif
endfor
endfor
delete_property(parent, temp_prop);
"Amy 8/15/94";
.
#112:21
"Syntax: tokenize(string <command>)";
"Adapted from $string_utils:words";
"This parses input supplied in either program or command line style";
"Returning a list of tokens";
"Last modified by jaime (#2) Wed May  7 13:21:11 1997 EDT.";
rest = args[1];
special = {"+", "*", "%", "-", "<", ">", "(", ")", "{", "}", ",", "/", "!", "=", "&", "|", "[", "]"};
"Should these be special?: ! = & |";
"...trim leading blanks...";
rest[1..match(rest, "^ *")[2]] = "";
if (!rest)
return {};
endif
quote = 0;
toklist = {};
token = "";
operators = "][+*/%<>(){},=+-";
normal_pattern = (" +%|[" + operators) + "]%|\"%|[[({]";
in_a_quoted_string_pattern = "%([^\\]%|^%)\"";
pattern = normal_pattern;
while (m = match(rest, pattern))
if (quote)
"... Closing quoted string.  Restore old pattern ...";
pattern = normal_pattern;
quote = 0;
token = token + rest[1..m[2]];
if (token != "")
toklist = {@toklist, token};
token = "";
endif
else
char = rest[m[1]];
endchar = rest[m[2]];
token = token + rest[1..m[1] - 1];
if (char == " ")
if (token != "")
toklist = {@toklist, token};
token = "";
endif
elseif (char == "\"")
"... beginning of quoted string ...";
"... within a quoted string nothing is special ...";
"... Opening a quoted string.  Set new pattern ...";
pattern = in_a_quoted_string_pattern;
quote = 1;
token = token + "\"";
elseif (((char == "/") && token) && (token[length(token)] == "\\"))
"Escaped /";
"Include / as a separated item";
if (length(token) > 1)
toklist = {@toklist, token[1..length(token) - 1]};
endif
toklist = {@toklist, "\\/"};
token = "";
elseif (char in special)
if (token != "")
toklist = {@toklist, token};
endif
toklist = {@toklist, char};
token = "";
elseif (char == "\\")
"... include next char literally if there is one ...";
token = token + rest[m[2]];
"Bug: seems to be losing backslash in double-backslash";
else
player:tell("Error: unknown case.");
endif
endif
rest[1..m[2]] = "";
$command_utils:suspend_if_needed(0);
endwhile
if (rest || token)
toklist = {@toklist, token + rest};
endif
return toklist;
"Amy 12/93";
"Amy  2/94: made it leave in quotes.";
"Amy  1/95: made split on arithmetic operators and comparative operators";
"Amy  2/95: made splitting on / an option";
"Amy  3/95: made leave in commas";
"Amy 12/95: major simplifications, removing compound operators and split_on_slashP";
"Amy  1/96: made split on [ and ]";
"Amy 6/96: major rewrite";
.
#112:22
"Copied from Parse Utilities (#89):coerce_list by Hacker ($hacker) Mon Mar 20 18:43:20 1995 EST";
"Syntax: coerce_list(string <text, list as string>)";
"Convert list as a string to list of possibly other data types";
"Recursively goes through sub-lists.";
"Last modified by jaime (#2) Mon Mar 11 14:35:53 1996 EST.";
text = args[1];
"Chop off opening {";
open = match(text, "^ *{");
text = (open && (length(text) > open[2])) ? text[open[2] + 1..length(text)] | "";
"Chop off closing }";
end = rmatch(text, "} *$");
text = (end && (end[1] > 1)) ? text[1..end[1] - 1] | "";
text = this:tokenize(text);
result = {};
for item_number in [1..length(text)]
$command_utils:suspend_if_needed(0);
if (!(text[item_number] == ","))
if (match(text[item_number], "^{.*}$"))
result = {@result, this:coerce_list(text[item_number])};
else
result = {@result, this:coerce(text[item_number])};
endif
endif
endfor
return result;
"Amy 10/94";
"Amy  5/95: Made remove commas in middle of seq; they were getting added as elements to the list";
.
#112:23
"Syntax: coerce(argument)";
"Last modified by jaime (#2) Mon Mar 11 14:35:53 1996 EST.";
set_task_perms(caller_perms());
if (typeof(args[1]) != STR)
return args[1];
"This function is designed to convert strings to other types.";
"If the input is not a string, return it as is.";
endif
"Force the argument to be a type other than string, if possible.";
string = args[1];
if (!length(string))
return string;
endif
first = string[1];
if ((first == "#") && ((result = $code_utils:toobj(string)) != E_TYPE))
return result;
elseif ((0 && (first == "{")) && (result = this:coerce_list_string(string)))
return result;
endif
result = $code_utils:eval_d(tostr("return ", args[1], ";"));
"It would be nice if this called eval_env; right now it doesn't";
if (((args[1] == "") || (result[1] == 0)) || ((typeof(result[2]) == ERR) && (!(args[1] in this.error_codes))))
return args[1];
"Can't coerce it into anything; return the original";
else
return result[2];
endif
"Amy 12/93";
"Amy  7/95: Fixed bug in how lists are handled";
.
#112:24
"Syntax: set_prop(<keyword/value argument pairs";
"Keywords accepted:";
"PREFIX_: <output prefix>";
"           defaults to this.default_prefix";
"PROP_NAME_: <name>";
"OBJ_: <objectnum>";
"VALUE_: <value>";
"PERMS_: <prop perms; defaults to rc>";
"Last modified by jaime (#2) Fri Aug  2 14:30:50 1996 EDT.";
if (caller != #0)
return E_PERM;
endif
set_task_perms(caller_perms());
"This verb relies on being !d";
prefix = ((spot = "PREFIX_:" in args) && (spot < length(args))) ? args[spot + 1] | this.default_prefix;
if ((spot = "PROP_NAME_:" in args) && (spot < length(args)))
prop_name = args[spot + 1];
else
player:notify(prefix + " ERROR_: MISSING_REQUIRED_ARG PROP_NAME");
return;
endif
if ((spot = "OBJ_:" in args) && (spot < length(args)))
object = toobj(args[spot + 1]);
if (!valid(object))
player:notify(prefix + " ERROR_: OBJECT_NOT_FOUND");
return;
endif
else
player:notify(prefix + " ERROR_: MISSING_REQUIRED_ARG OBJ");
return;
endif
if ("VALUE_:" in args)
valuep = 1;
value = this:to_next_tag_str(args, "VALUE_:");
if ((length(value) > 1) && (value[1] == "/"))
"Removing leading /";
value = value[2..length(value)];
endif
expect_list = (value && (value[1] == "{")) && (value[length(value)] == "}");
value = this:coerce(value);
if (expect_list && (typeof(value) != LIST))
"We have a list with mismatched {} or commas or quotes";
player:notify(prefix + " TEXT_COLOR_: RED FEEDBACK_: Bad list?");
player:notify(prefix + " TEXT_COLOR_: RED FEEDBACK_: Maybe a mismatched { or } ?");
player:notify(prefix + " TEXT_COLOR_: RED FEEDBACK_: Property not changed.");
player:notify(prefix + " SET_PROP_: 0");
return;
endif
else
valuep = 0;
endif
result = object.(prop_name) = value;
if (typeof(result) == ERR)
player:notify((prefix + " ERROR_: CANT_SET_PROP ") + tostr(result));
player:notify(prefix + " SET_PROP_: 0");
return;
else
player:notify(prefix + " SET_PROP_: 1");
player:notify(prefix + " FEEDBACK_: Property changed.");
endif
if (spot = "PERMS_:" in args)
if ((spot == length(args)) || match(args[spot + 1], "_:$"))
perms = "";
else
perms = args[spot + 1];
endif
old_info = property_info(object, prop_name);
if (typeof(old_info) == ERR)
player:notify((prefix + " ERROR_: CANT_GET_PROP_INFO ") + tostr(old_info));
else
new_info = old_info;
new_info[2] = perms;
result = set_property_info(object, prop_name, new_info);
if (typeof(result) == ERR)
player:notify((prefix + " ERROR_: CANT_SET_PROP_INFO ") + tostr(result));
else
player:notify(prefix + " SET_PROP_: PERMS_CHANGED");
player:notify(prefix + " FEEDBACK_: Property permissions changed.");
endif
endif
elseif (!valuep)
"Neither value nor perms supplied";
player:notify(prefix + " ERROR_: NOTHING_TO_CHANGE");
endif
"Amy  1/96";
.
#112:25
"Syntax: object_info(<keyword/value argument pairs>)";
"Keywords accepted:";
"PREFIX_: <output prefix>";
"           defaults to this.default_prefix";
"OBJ_: <object>";
"FILTER_UNDERSCORE_PROPS_: <boolean; defaults to this.filter_underscore_props_default>";
"FILTER_UNDERSCORE_VERBS_: <boolean; defaults to this.filter_underscore_verbs_default>";
"Last modified by jaime (#2) Wed Jul 17 11:16:57 1996 EDT.";
if (caller != #0)
return E_PERM;
endif
set_task_perms(caller_perms());
"Ensure proper input format.";
prefix = ((spot = "PREFIX_:" in args) && (spot < length(args))) ? args[spot + 1] | this.default_prefix;
filter_propsp = ((spot = "FILTER_UNDERSCORE_PROPS_:" in args) && (spot < length(args))) ? tonum(args[spot + 1]) | this.filter_underscore_props_default;
filter_verbsp = ((spot = "FILTER_UNDERSCORE_VERBS_:" in args) && (spot < length(args))) ? tonum(args[spot + 1]) | this.filter_underscore_verbs_default;
if (!(object = this:to_next_tag_str(args, "OBJ_:")))
player:notify(prefix + " ERROR_: MISSING_REQUIRED_ARG OBJ");
return;
endif
"Parse object";
if (!valid(object = player:my_match_object(object)))
player:notify(prefix + " ERROR_: OBJECT_NOT_FOUND");
return;
endif
header = (((prefix + " OBJ_: ") + tostr(object)) + " OBJ_NAME_: ") + object.name;
properties = this:findable_properties(object, filter_propsp);
if (typeof(properties) == ERR)
player:notify((prefix + " ERROR_: CANT_GET_PROPS ") + tostr(properties));
else
header = (header + " PROPS_: ") + this:scrunch_list(properties);
endif
verbs = this:verbs(object, filter_verbsp);
if (typeof(verbs) == ERR)
player:notify((prefix + " ERROR_: CANT_GET_VERBS ") + tostr(verbs));
else
header = (header + " VERBS_: ") + this:scrunch_list(verbs);
endif
player:notify(header);
"Amy 1/96";
.
#112:26
"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).  Optionally filters out properties whose name begins with an underscore.";
"Modified from $object_utils:findable_properties to support the filter_underscore feature -- Amy 9/95";
"Last modified by jaime (#2) Mon Mar 11 14:35:58 1996 EST.";
what = args[1];
filter_underscore = {@args, 0}[2];
props = {};
who = caller_perms();
while (what != $nothing)
if ((what.r || (who == what.owner)) || who.wizard)
item_props = properties(what);
new = {};
if (filter_underscore)
for prop in (item_props)
if (prop && (prop[1] != "_"))
new = {prop, @new};
endif
endfor
else
new = item_props;
endif
props = {@new, @props};
endif
what = parent(what);
endwhile
return props;
"Amy  9/95: Modified to optionally filter out _ properties";
"           Defaults to not filtering them out.";
.
#112:27
"Syntax: scrunch_list(list)";
"Takes multiple lines of a list, and makes them into one string separated by /'s";
"/'s are escaped with 's";
"Note this will not do the right thing if the elements of the list are";
"themselves lists.  You'd need to use $string_utils:print for that, which";
"is not used here to save time.";
"Last modified by jaime (#2) Mon Mar 11 14:35:59 1996 EST.";
data = args[1];
result = "";
for item in (data)
result = (result + "/") + strsub(tostr(item), "/", "\\/");
endfor
result = result + "/";
return result;
"Amy 1/96";
.
#112:28
"Syntax: verbs(object[,filter_underscore])";
"For the record: this will give you a list of the verb names on a !r object.  This is the same behavior as the @verbs function in LambdaCore.  yduJ told me it was included there because using numeric verbnames you can get that info anyway.";
"Last modified by jaime (#2) Wed May  7 14:22:34 1997 EDT.";
object = args[1];
filter_underscore = {@args, this.filter_underscore_verbs_default}[2];
verbs = verbs(object);
if ((!filter_underscore) || (typeof(verbs) == ERR))
return verbs;
endif
verbs_filtered = {};
for verb in (verbs)
if ((!verb) || (!(verb[1] == "_")))
verbs_filtered = {@verbs_filtered, verb};
endif
endfor
return verbs_filtered;
"Amy  1/96";
.
#112:29
"Syntax: object_parents(<keyword/value argument pairs>)";
"Keywords accepted:";
"PREFIX_: <output prefix>";
"           defaults to this.default_prefix";
"OBJ_: <object>";
"Returns: ";
"OBJ_: <object>";
"OBJ_NAME_: <object name>";
"PARENT_OBJS_: <list of object numbers, separated by />";
"PARENT_NAMES_: <list of name and number pairs, separated by />";
"Last modified by jaime (#2) Wed Jul 17 11:17:10 1996 EDT.";
if (caller != #0)
return E_PERM;
endif
set_task_perms(caller_perms());
"Ensure proper input format.";
prefix = ((spot = "PREFIX_:" in args) && (spot < length(args))) ? args[spot + 1] | this.default_prefix;
if (!(object = this:to_next_tag_str(args, "OBJ_:")))
player:notify(prefix + " ERROR_: MISSING_REQUIRED_ARG OBJ");
return;
endif
"Parse object";
if (!valid(object = player:my_match_object(object)))
player:notify(prefix + " ERROR_: OBJECT_NOT_FOUND");
return;
endif
header = (((prefix + " OBJ_: ") + tostr(object)) + " OBJ_NAME_: ") + object.name;
parents = {object, @$object_utils:ancestors(object)};
"I don't think it's possible to get an error here... no need to check for it.";
header = (header + " PARENT_OBJS_: ") + this:scrunch_list_and_reverse(parents);
parent_names = {};
for OBJ in (parents)
parent_names = {@parent_names, $string_utils:nn(OBJ)};
endfor
header = (header + " PARENT_NAMES_: ") + this:scrunch_list_and_reverse(parent_names);
player:notify(header);
"Amy 1/96";
.
#112:30
"Syntax: verify_mail_recipients(<keyword/value argument pairs>)";
"Keywords accepted:";
"PREFIX_: <output prefix>";
"           defaults to this.default_prefix";
"RECIPIENTS_: <list of recipients, separated by /'s; /'s escaped with 's>";
"Returns:";
"VALID_RECIPIENTS_: <list of valid recipient object numbers, separated by /'s>";
"NONEXISTENT_RECIPIENTS_: <list of invalid recipient strings, separated by /'s>";
"CANT_SEND_TO_: <list of recipient strings you do not have permission to mail to>";
"Last modified by jaime (#2) Mon Nov 25 15:57:54 1996 EST.";
if (caller != #0)
return E_PERM;
endif
set_task_perms(caller_perms());
prefix = ((spot = "PREFIX_:" in args) && (spot < length(args))) ? args[spot + 1] | this.default_prefix;
recipients = this:list_from_tokens(args, "RECIPIENTS_:", 0);
if (!recipients)
player:notify(prefix + " ERROR_: MISSING_REQUIRED_ARG RECIPIENTS");
return;
endif
valid = {};
invalid = {};
no_write = {};
for recip in (recipients)
if (valid(OBJ = $mail_agent:match_recipient(recip)))
if ($object_utils:isa(OBJ, $mail_recipient) && (!OBJ:is_usable_by(player)))
no_write = {@no_write, OBJ};
else
valid = {@valid, OBJ};
endif
else
invalid = {@invalid, recip};
endif
endfor
header = prefix;
if (valid)
header = (header + " VALID_RECIPIENTS_: ") + this:scrunch_list(valid);
endif
if (invalid)
header = (header + " NONEXISTENT_RECIPIENTS_: ") + this:scrunch_list(invalid);
endif
if (no_write)
header = (header + " CANT_SEND_TO_: ") + this:scrunch_list(no_write);
endif
player:notify(header);
"Amy  1/96";
.
#112:31
"Syntax: verify_mail_recipients(<keyword/value argument pairs>)";
"Keywords accepted:";
"PREFIX_: <output prefix>";
"           defaults to this.default_prefix";
"RECIPIENTS_: <list of valid recipients, separated by /'s; /'s escaped with 's>";
"SUBJECT_: <mail subject; could be more than one word>";
"BODY_: <mail body; lines separated by /'s; /'s escaped with 's>";
"Returns:";
"SEND_MAIL_: <1/0>";
"Last modified by jaime (#2) Mon Nov 25 15:58:11 1996 EST.";
if (caller != #0)
return E_PERM;
endif
set_task_perms(caller_perms());
prefix = ((spot = "PREFIX_:" in args) && (spot < length(args))) ? args[spot + 1] | this.default_prefix;
recipients = this:list_from_tokens(args, "RECIPIENTS_:");
if (!recipients)
player:notify(prefix + " ERROR_: MISSING_REQUIRED_ARG RECIPIENTS");
return;
endif
to = {};
for recip in (recipients)
to = recip ? {@to, toobj(recip)} | to;
endfor
if (#0 in to)
player:notify(prefix + " ERROR_: INVALID_RECIPIENT");
return;
endif
subject = ((spot = "SUBJECT_:" in args) && (spot < length(args))) ? this:to_next_tag_str(args, "SUBJECT_:") | "";
body = this:list_from_tokens(args, "BODY_:");
$command_utils:suspend_if_needed(0);
result = $mail_agent:send_message(player, to, subject, body);
if (typeof(result) == ERR)
player:notify((prefix + " ERROR_: CANT_SEND_MESSAGE ") + tostr(result));
elseif (!result[1])
player:notify((prefix + " ERROR_: INVALID_RECIPIENTS ") + this:scrunch_list(result[2]));
else
header = prefix + " SEND_MAIL_: 1";
player:notify(header);
player:notify(("Mail sent to " + $string_utils:name_and_number_list(to)) + ".");
endif
"Amy  1/96";
.
#112:32
"Syntax: verb_info_and_args(<object>, <vname>)";
"Assumes the verb already exists";
"Last modified by jaime (#2) Wed Jul 17 11:15:06 1996 EDT.";
if (caller != this)
return E_PERM;
endif
set_task_perms(caller_perms());
"This verb relies on being !d";
object = args[1];
vname = args[2];
result = "";
vi = verb_info(object, vname);
if (typeof(vi) == ERR)
result = (result + " CANT_GET_VERB_INFO ") + tostr(vi);
else
result = (result + " VERB_OWNER_: ") + tostr(vi[1]);
result = (result + " VERB_PERMS_: ") + vi[2];
endif
vargs = verb_args(object, vname);
if (typeof(vargs) == ERR)
result = (result + " CANT_GET_VERB_ARGS ") + tostr(vargs);
else
result = (result + " VERB_DOBJ_: ") + vargs[1];
result = (result + " VERB_PREP_: ") + vargs[2];
result = (result + " VERB_IOBJ_: ") + vargs[3];
endif
return result;
"Amy 11/95";
.
#112:33
"Copied from MacMOOSE Utils ($macmoose_utils):scrunch_list by Hacker (#38) Tue Feb  6 18:56:05 1996 EST";
"Syntax: scrunch_list(list)";
"Takes multiple lines of a list, and makes them into one string separated by /'s";
"/'s are escaped with 's";
"Note this will not do the right thing if the elements of the list are";
"themselves lists.  You'd need to use $string_utils:print for that, which";
"is not used here to save time.";
"Last modified by jaime (#2) Mon Mar 11 14:36:03 1996 EST.";
data = args[1];
result = "";
for item in (data)
result = (strsub(tostr(item), "/", "\\/") + "/") + result;
endfor
result = "/" + result;
return result;
"Amy 1/96";
.
#112:34
"Syntax: list_from_tokens(list <tokens>, string <tag>[, boolean <check_unmatched_quotesp>)";
"Last modified by jaime (#2) Mon Nov 25 15:58:38 1996 EST.";
tokens = args[1];
tag = args[2];
lines = {};
current = {};
tokens = $macmoose_utils:to_next_tag(tokens, tag);
if (!tokens)
return 0;
endif
for index in [1..length(tokens)]
$command_utils:suspend_if_needed(0);
tok = tokens[index];
if (tok == "/")
if (index != 1)
"Don't add extra leading blank";
lines = {@lines, current};
current = {};
endif
else
current = {@current, tok};
endif
endfor
lines = current ? {@lines, current} | lines;
result = {};
for line in (lines)
line = this:reassemble_line(line);
line = strsub(line, "\\/", "/");
result = {@result, line};
endfor
return result;
"Amy 6/96: Added support for compound operators";
.
#112:35
"Syntax: reassemble_line(<lists of tokens>)";
"Reassembles one line from a list of tokens";
"Last modified by jaime (#2) Mon Nov 25 15:59:19 1996 EST.";
line = args[1];
special = {"/", " "};
punctuation = {",", ".", " ", ";", ":"};
no_space_before = {",", ")", "[", "]"};
no_space_after = {"(", "[", "{"};
if (typeof(line) != LIST)
"$command_utils:warn(Trying to reassemble non-list: , line)";
return tostr(line);
endif
if ((l = length(line)) == 0)
return "";
else
line_string = line[1];
endif
if (l > 1)
no_space = 0;
for token_number in [2..l]
$command_utils:suspend_if_needed(0);
next = line[token_number];
next_leading = ((typeof(next) == STR) && length(next)) ? next[1] | "";
last = length(line_string) ? line_string[length(line_string)] | "";
if ((((((!(last in special)) && (!(next_leading in special))) && (!(last in no_space_after))) && (!(next_leading in no_space_before))) && (!no_space)) && (!((next_leading == last) && (next_leading == "-"))))
line_string = line_string + " ";
endif
line_string = line_string + next;
no_space = 0;
endfor
endif
return line_string;
"Amy  4/94";
"Amy 12/94: Added $no_space as a way to indicate not to add a space.";
"Amy  5/95: Fixed bug with punctuation after '";
"Amy  6/96: Major simplification--removed counting of quotes";
.
#112:36
"Returns the output of 'print' without adding surrounding quotes";
"Last modified by jaime (#2) Mon May 13 12:36:30 1996 EDT.";
value = args[1];
type = typeof(value);
if (type == LIST)
string = $string_utils:print(@args);
$command_utils:suspend_if_needed(0);
if ((((l = length(string)) > 1) && (string[1] == string[l])) && (string[1] == "\""))
string = string[2..length(string) - 1];
endif
return string;
else
return tostr(value);
endif
"Amy 9/94";
"Amy 1/95 -- Made not add  before quotes and  ";
.
#112:37
"Syntax: simple_tokenize(string <command>)";
"Adapted from $string_utils:words";
"Parses input, splitting on spaces and MacMOOSE line separators";
"Line separators are slashes not escaped by a backslash.";
"Last modified by jaime (#2) Wed Nov 27 15:33:34 1996 EST.";
rest = args[1];
"...trim leading blanks...";
rest[1..match(rest, "^ *")[2]] = "";
if (!rest)
return {};
endif
quote = 0;
toklist = {};
token = "";
operators = "/";
in_a_quoted_string_pattern = "%(\\.%|^/%|%([^\\]%|^%)[/\"]%)";
normal_pattern = " +%|" + in_a_quoted_string_pattern;
pattern = normal_pattern;
while (m = match(rest, pattern))
$command_utils:suspend_if_needed(0);
char = rest[m[1]];
endchar = rest[m[2]];
if (char == "\\")
"Backslash.  Include the next character literally.";
token = token + rest[1..m[2]];
elseif (quote)
"... Closing quoted string.  Restore old pattern ...";
pattern = normal_pattern;
quote = 0;
token = token + rest[1..m[2]];
if (token && (token[length(token)] == "/"))
"Uneven number of quotes on line; end quoted string and start new line";
if (length(token) > 1)
toklist = {@toklist, token[1..length(token) - 1]};
endif
toklist = {@toklist, "/"};
elseif (token != "")
toklist = {@toklist, token};
endif
token = "";
else
if (endchar == " ")
token = token + rest[1..m[1] - 1];
if (token != "")
toklist = {@toklist, token};
token = "";
endif
elseif (endchar == "\"")
token = token + rest[1..m[2] - 1];
if (!((m[2] != m[1]) && (rest[m[1]] == "\"")))
"... beginning of quoted string ...";
"... Opening a quoted string.  Set new pattern ...";
pattern = in_a_quoted_string_pattern;
quote = 1;
endif
token = token + "\"";
elseif (endchar == "/")
token = token + rest[1..m[2] - 1];
if (token != "")
toklist = {@toklist, token};
endif
toklist = {@toklist, endchar};
token = "";
else
player:tell("Error: unknown case.");
endif
endif
rest[1..m[2]] = "";
endwhile
if (rest || token)
toklist = {@toklist, token + rest};
endif
return toklist;
"Amy 12/93";
"Amy  2/94: made it leave in quotes.";
"Amy  1/95: made split on arithmetic operators and comparative operators";
"Amy  2/95: made splitting on / an option";
"Amy  3/95: made leave in commas";
"Amy 12/95: major simplifications, removing compound operators and split_on_slashP";
"Amy  1/96: made split on [ and ]";
.
#112:38
"Turn a complex verb specifier like 'g*et take' into simply g";
"Useful for attempting to actually call a verb by that name";
"Amy Bruckman 11/22/93";
"Last modified by jaime (#2) Tue Jun 11 14:58:22 1996 EDT.";
simple_name = $string_utils:trim(args[1]);
if (typeof(simple_name) != STR)
return simple_name;
endif
"Trim off an asterisk and anything following it";
if (star = index(simple_name, "*"))
simple_name = simple_name[1..star - 1];
endif
"Trim off multiple names for simple_name with an alias";
if (space = index(simple_name, " "))
simple_name = simple_name[1..space - 1];
endif
return simple_name;
.
#113:0
player:tell();
for line in [1..length(this.slides)]
player:tell(line, ")  ", this.slides[line]);
endfor
"Last modified Fri Feb  7 18:06:49 1997 CST by Jan (#2).";
.
#113:1
"Modified to let output go to new browser window. Jan, 1/4/01";
if (dobjstr == "next")
sn = this.index;
elseif ($string_utils:is_numeric(dobjstr))
sn = tonum(dobjstr);
else
player:tell("Usage:  display <number|next> on <projector name>");
return;
endif
if (sn > length(this.slides))
player:tell("Try again: projector only has ", length(this.slides), " slides.");
return;
endif
s = this.slides[sn];
player.location:announce_all(player.name, " displays slide #", sn, " on ", this.name, ":");
if (this.display_in_xpress)
player.location:announce_all("  <", s, ">.");
else
player.location:announce_all("  <", s, " _blank>.");
endif
this.index = sn + 1;
"Last modified Fri Apr 13 19:03:37 2001 CDT by Wizard (#2).";
.
#113:2
"Modified to let output go to new browser window. Jan, 1/4/01";
if (dobjstr == "next")
sn = this.index;
elseif ($string_utils:is_numeric(dobjstr))
sn = tonum(dobjstr);
else
player:tell("Usage:  view <number|next> on <projector name>");
return;
endif
if (sn > length(this.slides))
player:tell("Try again: projector only has ", length(this.slides), " slides.");
return;
endif
s = this.slides[sn];
player:tell("You view slide #", sn, " on ", this.name, ":");
if (this.display_in_xpress)
player:tell(" <", s, ">.");
else
player:tell(" <", s, " _blank>.");
endif
this.index = sn + 1;
"Last modified Fri Apr 13 19:03:37 2001 CDT by Wizard (#2).";
.
#113:3
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:42 1997 CDT by Wizard (#2).";
.
#113:4
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Assemble the most important properties for quick and easy editing.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
options = pass(@args);
options = {@options, "<OPTION VALUE=\"edit_web_slides\">Edit Web Slides"};
return options;
"Last modified Fri Apr 13 19:02:19 2001 CDT by Wizard (#2).";
.
#113:5
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Routine for starting and stopping a slide show";
"===========================================================";
if (verb == "start")
if (this.on)
player:tell(tostr("Slide show is already running. To stop it, type Stop ", this.name));
else
if (length(this.slides > 1))
this.on = 1;
player:tell("Slide show started.");
player.location:announce(player.name, " starts slide show on ", this.name);
this:slideshow();
else
player:tell("Sorry, there are no slides in the slide show. Go to edit and add some URLs.");
endif
endif
elseif (verb == "stop")
if (!this.on)
player:tell(tostr("Slide show is not running. To start it, type Start ", this.name));
else
this.on = 0;
player:tell("Slide show stopped.");
player.location:announce(player.name, " stops slide show on ", this.name);
endif
elseif (verb == "reset")
this.on = 0;
this.index = 1;
player:tell("You reset the slide show on ", this.name, ".");
endif
"Last modified Mon Sep 17 10:39:45 2001 CDT by Wizard (#2).";
.
#113:6
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Routine displaying a slide show";
"===========================================================";
if (this.on)
if (this.index > length(this.slides))
this.on = 0;
this.index = 1;
player.location:announce_all(this.name, " has reached the last slide and stops running.");
else
slide = this.slides[this.index];
player.location:announce_all(player.name, " displays slide #", this.index, " on ", this.name, ":");
if (this.display_in_xpress)
player.location:announce_all("  <", slide, ">.");
else
player.location:announce_all("  <", slide, " _blank>.");
endif
this.index = this.index + 1;
fork (this.delay)
this:slideshow();
endfork
endif
else
return;
endif
"Last modified Mon Sep 17 10:09:42 2001 CDT by Wizard (#2).";
.
#113:7
if ((player != this.owner) || (!player.wizard))
player:tell("You're not allowed to set the delay on this ", this.name);
else
this.delay = tonum(iobjstr);
player:tell("You set the slide show delay on ", this.name, " to ", this.delay, " seconds.");
endif
"Last modified Mon Sep 17 10:09:56 2001 CDT by Wizard (#2).";
.
#114:0
":_social(victim | list of victims | $no_one if no victim[, social-verb])";
"The first arg should be a single objnum or a list of objnums or $no_one if there is no victim.";
"The second arg should be wave or wink or yawn or one of the other current social verbs.  The second arg is optional; you can just call the verb with the verbname \"_\"+action instead.";
if (!valid(caller))
return;
endif
where = player.location;
victim = args[1];
verb = (length(args) == 2) ? args[2] | verb;
whose = $object_utils:has_property(player, verb) ? player | this;
this.custom = setadd(this.custom, player);
if (length(emote = whose.(verb)) < 7)
overb = (verb[l = length(verb)] == "y") ? tostr(verb[1..l - 1], "ies") | tostr(verb, "s");
emote = {tostr("You ", verb, "."), tostr("%n ", overb, "."), tostr("You ", verb, " at %d."), tostr("%n ", overb, " at you."), tostr("%n ", overb, " at %d."), tostr("You ", verb, " at yourself."), tostr("%n ", overb, " at %r.")};
endif
if (victim == $no_one)
player:tell($string_utils:pronoun_sub(emote[1], player, victim));
where:announce($string_utils:pronoun_sub(emote[2], player, victim));
elseif ((typeof(victim) == OBJ) && (victim.location != player.location))
player:tell(victim.name, " isn't here.");
elseif (victim == player)
player:tell($string_utils:pronoun_sub(emote[6], player, victim));
where:announce($string_utils:pronoun_sub(emote[7], player, victim));
elseif (typeof(victim) == OBJ)
player:tell($string_utils:pronoun_sub(emote[3], player, player, where, victim));
victim:tell($string_utils:pronoun_sub(emote[4], player, player, where, victim));
where:announce_all_but({player, victim}, $string_utils:pronoun_sub(emote[5], player, player, where, victim));
else
player:tell(this:super_sub(emote[3], player, player, player, where, victim));
where:announce_all_but({player, @victim}, this:super_sub(emote[5], $no_one, player, player, where, victim));
for v in (victim)
$command_utils:suspend_if_needed(0);
v:tell(this:super_sub(emote[5], v, player, player, where, victim));
endfor
endif
if ($object_utils:has_verb(where, "notify_social"))
where:notify_social(verb, victim);
endif
.
#114:1
"Syntax: <verb> [<player>]";
if (verb != "social")
if (typeof(victim = this:discern_everything()) != ERR)
this:("_" + verb)((typeof(victim) == LIST) ? (length(victim) > 1) ? victim | (length(victim) ? victim[1] | $no_one) | victim, verb);
endif
else
player:tell("Current Social Verbs:");
player:tell_lines($string_utils:columnize(setremove($string_utils:explode(verb_info(this, "social")[3]), "social"), 4));
endif
.
#114:2
if (caller != this)
return E_PERM;
else
dobj = player.location:match_object(dobjstr);
if (((dobjstr in {"all", "everyone", "everything", "everybody"}) || (!dobjstr)) || (!$command_utils:object_match_failed(dobj, dobjstr)))
victim = dobjstr ? (dobjstr in {"all", "everyone", "everybody"}) ? $you | ((dobjstr in {"everything"}) ? this:("everything_ok?")() ? setremove(player.location.contents, player) | $no_one | (valid(dobj) ? dobj | $no_one)) | $no_one;
if (victim == $you)
if (this:("everything_ok?")())
for v in (victim = setremove(player.location.contents, player))
if (!is_player(v))
victim = setremove(victim, v);
endif
endfor
else
victim = $no_one;
endif
endif
return victim;
endif
return E_NONE;
endif
.
#114:3
return length(connected_players()) < 70;
.
#114:4
":_unsocial(victim | list of victims | $no_one if no victim[, unsocial-verb])";
"The first arg should be a single objnum or a list of objnums or $no_one if there is no victim.";
"The second arg should be wave or wink or yawn or one of the other current unsocial verbs.  The second arg is optional; you can just call the verb with the verbname \"_\"+action instead.";
if (!valid(caller))
return;
endif
where = player.location;
victim = args[1];
verb = (length(args) == 2) ? args[2] | verb;
whose = $object_utils:has_property(player, verb) ? player | this;
this.custom = setadd(this.custom, player);
if (length(emote = whose.(verb)) < 7)
overb = (verb[l = length(verb)] == "y") ? tostr(verb[1..l - 1], "ies") | tostr(verb, "s");
emote = {tostr("You ", verb, "."), tostr("%n ", overb, "."), tostr("You ", verb, " at %d."), tostr("%n ", overb, " at you."), tostr("%n ", overb, " at %d."), tostr("You ", verb, " at yourself."), tostr("%n ", overb, " at %r.")};
endif
if (victim == $no_one)
player:tell($string_utils:pronoun_sub(emote[1], player, victim));
where:announce($string_utils:pronoun_sub(emote[2], player, victim));
elseif ((typeof(victim) == OBJ) && (victim.location != player.location))
player:tell(victim.name, " isn't here.");
elseif (victim == player)
player:tell($string_utils:pronoun_sub(emote[6], player, victim));
where:announce($string_utils:pronoun_sub(emote[7], player, victim));
elseif (typeof(victim) == OBJ)
player:tell($string_utils:pronoun_sub(emote[3], player, player, where, victim));
victim:tell($string_utils:pronoun_sub(emote[4], player, player, where, victim));
where:announce_all_but({player, victim}, $string_utils:pronoun_sub(emote[5], player, player, where, victim));
else
player:tell(#86:super_sub(emote[3], player, player, player, where, victim));
where:announce_all_but({player, @victim}, #86:super_sub(emote[5], $no_one, player, player, where, victim));
for v in (victim)
pause = ((ticks_left() < 3000) || (seconds_left() < 3)) ? suspend(0) | 0;
v:tell(#86:super_sub(emote[5], v, player, player, where, victim));
endfor
endif
if ($object_utils:has_verb(where, "notify_social"))
where:notify_social(verb, victim);
endif
.
#114:5
"Usage: feelings";
"       Lists all the verbs on the Social Verb Core (#2212), and some from other places.  Yeeeeeeehaw.";
verb = "social";
this:social(@args);
verb = "unsocial";
this:unsocial(@args);
verb = "useful";
this:useful(@args);
player:tell("Also check out Juli Burk's Aloha/Lei verbs: lei, lei-c, lei-g, lei-a, lei-t, and lei-l.");
"Last modified Tue Oct 14 14:17:10 1997 CDT by Wizard (#2).";
.
#114:6
"Syntax: <verb> [<player>]";
if (verb != "unsocial")
dobj = player.location:match_object(dobjstr);
if (((dobjstr in {"all", "everyone", "everything", "everybody"}) || (!dobjstr)) || (!$command_utils:object_match_failed(dobj, dobjstr)))
victim = dobjstr ? (dobjstr in {"all", "everyone", "everybody"}) ? $you | ((dobjstr in {"everything"}) ? setremove(player.location.contents, player) | (valid(dobj) ? dobj | $no_one)) | $no_one;
if (victim == $you)
for v in (victim = setremove(player.location.contents, player))
if (!is_player(v))
victim = setremove(victim, v);
endif
endfor
endif
this:("_" + verb)((typeof(victim) == LIST) ? (length(victim) > 1) ? victim | (length(victim) ? victim[1] | $no_one) | victim, verb);
endif
else
player:tell("Current Weird Social Verbs:");
player:tell_lines($string_utils:columnize(setremove($string_utils:explode(verb_info(this, "unsocial")[3]), "unsocial"), 4));
endif
.
#114:7
"Syntax: <verb> [<player>]";
if (verb != "useful")
if (typeof(victim = this:discern_everything()) != ERR)
this:("_" + verb)((typeof(victim) == LIST) ? (length(victim) > 1) ? victim | (length(victim) ? victim[1] | $no_one) | victim, verb);
endif
else
player:tell("Current Truly Useful Social Verbs:");
player:tell_lines($string_utils:columnize(setremove($string_utils:explode(verb_info(this, "useful")[3]), "useful"), 4));
endif
.
#114:8
if (!valid(caller))
return;
endif
where = player.location;
victim = args[1];
verb = (length(args) == 2) ? args[2] | verb;
whose = $object_utils:has_property(player, verb) ? player | this;
this.custom = setadd(this.custom, player);
if (length(emote = whose.(verb)) < 7)
overb = (verb[l = length(verb)] == "y") ? tostr(verb[1..l - 1], "ies") | tostr(verb, "s");
emote = {tostr("You ", verb, "."), tostr("%n ", overb, "."), tostr("You ", verb, " at %d."), tostr("%n ", overb, " at you."), tostr("%n ", overb, " at %d."), tostr("You ", verb, " at yourself."), tostr("%n ", overb, " at %r.")};
endif
if (victim == $no_one)
player:tell($string_utils:pronoun_sub(emote[1], player, victim));
where:announce($string_utils:pronoun_sub(emote[2], player, victim));
elseif ((typeof(victim) == OBJ) && (victim.location != player.location))
player:tell(victim.name, " isn't here.");
elseif (victim == player)
player:tell($string_utils:pronoun_sub(emote[6], player, victim));
where:announce($string_utils:pronoun_sub(emote[7], player, victim));
elseif (typeof(victim) == OBJ)
player:tell($string_utils:pronoun_sub(emote[3], player, player, where, victim));
victim:tell($string_utils:pronoun_sub(emote[4], player, player, where, victim));
where:announce_all_but({player, victim}, $string_utils:pronoun_sub(emote[5], player, player, where, victim));
else
player:tell(#86:super_sub(emote[3], player, player, player, where, victim));
where:announce_all_but({player, @victim}, #86:super_sub(emote[5], $no_one, player, player, where, victim));
for v in (victim)
$command_utils:suspend_if_needed(0);
v:tell(#86:super_sub(emote[5], v, player, player, where, victim));
endfor
endif
if ($object_utils:has_verb(where, "notify_social"))
where:notify_social(verb, victim);
endif
.
#114:9
"Copied from New Social Verbs (#94):useful by Hacker (#37) Sun Mar  5 21:49:22 1995 CST";
"Syntax: <verb> [<player>]";
if (verb != "useful")
if (typeof(victim = this:discern_everything()) != ERR)
this:("_" + verb)((typeof(victim) == LIST) ? (length(victim) > 1) ? victim | (length(victim) ? victim[1] | $no_one) | victim, verb);
endif
else
player:tell("Current Truly Useful Social Verbs:");
player:tell_lines($string_utils:columnize(setremove($string_utils:explode(verb_info(this, "useful")[3]), "useful"), 4));
endif
.
#114:10
"Copied from New Social Verbs (#94):_useful by Hacker (#37) Sun Mar  5 21:50:41 1995 CST";
if (!valid(caller))
return;
endif
where = player.location;
victim = args[1];
verb = (length(args) == 2) ? args[2] | verb;
whose = $object_utils:has_property(player, verb) ? player | this;
this.custom = setadd(this.custom, player);
if (length(emote = whose.(verb)) < 7)
overb = (verb[l = length(verb)] == "y") ? tostr(verb[1..l - 1], "ies") | tostr(verb, "s");
emote = {tostr("You ", verb, "."), tostr("%n ", overb, "."), tostr("You ", verb, " at %d."), tostr("%n ", overb, " at you."), tostr("%n ", overb, " at %d."), tostr("You ", verb, " at yourself."), tostr("%n ", overb, " at %r.")};
endif
if (victim == $no_one)
player:tell($string_utils:pronoun_sub(emote[1], player, victim));
where:announce($string_utils:pronoun_sub(emote[2], player, victim));
elseif ((typeof(victim) == OBJ) && (victim.location != player.location))
player:tell(victim.name, " isn't here.");
elseif (victim == player)
player:tell($string_utils:pronoun_sub(emote[6], player, victim));
where:announce($string_utils:pronoun_sub(emote[7], player, victim));
elseif (typeof(victim) == OBJ)
player:tell($string_utils:pronoun_sub(emote[3], player, player, where, victim));
victim:tell($string_utils:pronoun_sub(emote[4], player, player, where, victim));
where:announce_all_but({player, victim}, $string_utils:pronoun_sub(emote[5], player, player, where, victim));
else
player:tell(#86:super_sub(emote[3], player, player, player, where, victim));
where:announce_all_but({player, @victim}, #86:super_sub(emote[5], $no_one, player, player, where, victim));
for v in (victim)
$command_utils:suspend_if_needed(0);
v:tell(#86:super_sub(emote[5], v, player, player, where, victim));
endfor
endif
if ($object_utils:has_verb(where, "notify_social"))
where:notify_social(verb, victim);
endif
.
#114:11
"Copied from Social Features (#92):super_sub by Hacker (#37) Thu Apr  6 02:04:49 1995 CDT";
"Copied from port (#613):super_sub by Bitstream (#100) Tue Apr  4 20:48:14 1995 CDT";
"Copied from Social Features (#86):super_sub by Knekkebjoern (#2) Tue Apr  4 21:40:13 1995 EDT";
"Copied from APHiD's Utilities (#15411):super_sub_alt by APHiD (#31783) Wed Dec 16 12:33:19 1";
"Copied from APHiD's Utilities (#15411):ss by Hacker (#18105) Wed Dec 16 12:25:02 1992 PST";
"Pronoun (and other things) substitution. See 'help pronouns' for details.";
"syntax:  #15411:super_sub(text[,listener[,who[,thing[,location[,dobj[,iobj]]]]])";
"%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> -> whatever <who> does when normal people foo. This is determined by calling :verb_sub() on the <who>.";
"%(foo) => <who>.foo and %(Foo) => <who>.foo capitalized. %[dfoo] => dobj.foo, etc..";
"%<d:foo> -> whatever <dobj> does when normal people foo.";
"The main advantage of :super_sub() is that any of the objects can be a list and by defining the listener (second arg), all references to the  listener are used with $You.";
"set_task_perms($no_one);";
if (typeof(args[1]) == LIST)
nargs = listdelete(args, 1);
plines = {};
for line in (args[1])
plines = {@plines, this:(verb)(line, @nargs)};
endfor
return plines;
endif
listener = (length(args) >= 2) ? args[2] | player;
who = (length(args) >= 3) ? args[3] | player;
thing = (length(args) >= 4) ? args[4] | caller;
where = (length(args) >= 5) ? args[5] | (((typeof(who) == OBJ) && valid(who)) ? who.location | $nothing);
old = tostr(args[1]);
new = "";
objspec = "nditl";
objects = {};
for o in ({who, (length(args) >= 6) ? args[6] | dobj, (length(args) >= 7) ? args[7] | iobj, thing, where})
if (typeof(o) == LIST)
while (i = listener in o)
o[i] = $you;
endwhile
if (length(o) == 1)
o = o[1];
endif
else
o = (o == listener) ? $you | o;
endif
objects = {@objects, o};
endfor
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 = objects[1];
if ((length(vb) > 2) && (vb[2] == ":"))
" %<d:verb>";
vbs = objects[index(objspec, vb[1]) || 1];
vb = vb[3..length(vb)];
endif
vbs = (typeof(vbs) == LIST) ? this.they | vbs;
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 = {};
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 = {objects[1], p};
elseif (p[1] == "#")
"%[#n] => object number";
s = (o = index(objspec, p[2])) ? (typeof(object[o]) == LIST) ? $string_utils:english_list($list_utils:map_builtin(objects[o], "tostr")) | tostr(objects[o]) | (("[" + p) + "]");
elseif (!(o = index(objspec, p[1])))
s = ("[" + p) + "]";
else
" %[dproperty] ";
cp_args = {(typeof(objects[o]) == LIST) ? ($you in objects[o]) ? $you | this.they | 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};
elseif (w = index(prnspec, s, 1))
cp_args = {objects[1], prprops[w]};
elseif (s == "#")
s = (typeof(objects[1]) == LIST) ? $list_utils:map_builtin(objects[1], "tostr") | tostr(objects[1]);
elseif (s != "%")
s = "%" + s;
endif
new = (new + old[1..prcnt - 1]) + ((!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;
"Last modified Tue Apr 15 11:55:28 1997 CDT by Gustavo (#999).";
.
#114:12
if (caller_perms().wizard)
pass();
this.custom = {};
endif
"Last modified Sun Aug 17 11:15:35 1997 CDT by Wizard (#2).";
.
#114:13
victim = $player_db:find(dobjstr);
if (!valid(victim))
player:tell("This person is not here now, share your lei with someone else.");
return;
endif
if (!(victim in connected_players()))
player:tell(tostr(victim.name, " is sleeping soundly and probably wouldn't want to be awoken."));
return;
endif
if (!iobjstr)
flower = "plumeria";
else
flower = iobjstr;
endif
victim:tell($string_utils:pronoun_sub(tostr("%N places a fragrant ", flower, " lei around your neck in congratulations.")));
player:tell($string_utils:pronoun_sub(tostr("You place a fragrant ", flower, " lei around %D's neck in congratulations.")));
player.location:announce_all_but({victim, player}, $string_utils:pronoun_sub(tostr("%N places a fragrant ", flower, " lei around %D's neck in congratulations.")));
"Last modified Tue Oct 14 14:17:07 1997 CDT by Wizard (#2).";
.
#114:14
victim = $player_db:find(dobjstr);
if (!valid(victim))
player:tell("This person is not here now, share your lei with someone else.");
return;
endif
if (!(victim in connected_players()))
player:tell(tostr(victim.name, " is sleeping soundly and probably wouldn't want to be awoken."));
return;
endif
if (!iobjstr)
flower = "plumeria";
else
flower = iobjstr;
endif
victim:tell($string_utils:pronoun_sub(tostr("%N places a fragrant ", flower, " lei around your neck in greeting.")));
player:tell($string_utils:pronoun_sub(tostr("You place a fragrant ", flower, " lei around %D's neck in greeting.")));
player.location:announce_all_but({victim, player}, $string_utils:pronoun_sub(tostr("%N places a fragrant ", flower, " lei around %D's neck in greeting.")));
"Last modified Tue Oct 14 14:17:07 1997 CDT by Wizard (#2).";
.
#114:15
victim = $player_db:find(dobjstr);
if (!valid(victim))
player:tell("This person is not here now, share your lei with someone else.");
return;
endif
if (!(victim in connected_players()))
player:tell(tostr(victim.name, " is sleeping soundly and probably wouldn't want to be awoken."));
return;
endif
if (!iobjstr)
flower = "plumeria";
else
flower = iobjstr;
endif
victim:tell($string_utils:pronoun_sub(tostr("%N places a fragrant ", flower, " lei around your neck in admiration.")));
player:tell($string_utils:pronoun_sub(tostr("You place a fragrant ", flower, " lei around %D's neck in admiration.")));
player.location:announce_all_but({victim, player}, $string_utils:pronoun_sub(tostr("%N places a fragrant ", flower, " lei around %D's neck in admiration.")));
"Last modified Tue Oct 14 14:17:08 1997 CDT by Wizard (#2).";
.
#114:16
victim = $player_db:find(dobjstr);
if (!valid(victim))
player:tell("This person is not here now, share your lei with someone else.");
return;
endif
if (!(victim in connected_players()))
player:tell(tostr(victim.name, " is sleeping soundly and probably wouldn't want to be awoken."));
return;
endif
if (!iobjstr)
flower = "plumeria";
else
flower = iobjstr;
endif
victim:tell($string_utils:pronoun_sub(tostr("%N places a fragrant ", flower, " lei around your neck in thanks.")));
player:tell($string_utils:pronoun_sub(tostr("You place a fragrant ", flower, " lei around %D's neck in thanks.")));
player.location:announce_all_but({victim, player}, $string_utils:pronoun_sub(tostr("%N places a fragrant ", flower, " lei around %D's neck in thanks.")));
"Last modified Tue Oct 14 14:17:08 1997 CDT by Wizard (#2).";
.
#114:17
victim = $player_db:find(dobjstr);
if (!valid(victim))
player:tell("This person is not here now, share your lei with someone else.");
return;
endif
if (!(victim in connected_players()))
player:tell(tostr(victim.name, " is sleeping soundly and probably wouldn't want to be awoken."));
return;
endif
if (!iobjstr)
flower = "plumeria";
else
flower = iobjstr;
endif
victim:tell($string_utils:pronoun_sub(tostr("%N places a fragrant ", flower, " lei around your neck for good luck.")));
player:tell($string_utils:pronoun_sub(tostr("You place a fragrant ", flower, " lei around %D's neck for good luck.")));
player.location:announce_all_but({victim, player}, $string_utils:pronoun_sub(tostr("%N places a fragrant ", flower, " lei around %D's neck for good luck.")));
"Last modified Tue Oct 14 14:17:08 1997 CDT by Wizard (#2).";
.
#114:18
victim = $player_db:find(dobjstr);
if (!valid(victim))
player:tell("this person is not here now, share your lei with someone else.");
return;
endif
if (!(victim in connected_players()))
player:tell(tostr(victim.name, " is sleeping soundly and probably wouldn't want to be awoken."));
return;
endif
if (!iobjstr)
flower = "plumeria";
else
flower = iobjstr;
endif
victim:tell($string_utils:pronoun_sub(tostr("%N places a fragrant ", flower, " lei around your neck.")));
player:tell($string_utils:pronoun_sub(tostr("You place a fragrant ", flower, " lei around %D's neck.")));
player.location:announce_all_but({victim, player}, $string_utils:pronoun_sub(tostr("%N places a fragrant ", flower, " lei around %D's neck.")));
"Last modified Tue Oct 14 14:17:10 1997 CDT by Wizard (#2).";
.
#115:0
"create a new note and give it a name";
if (caller != this)
return E_PERM;
endif
new = player:_create($note, player);
if (typeof(new) == ERR)
player:notify(">>>   A L E R T   <<<");
player:notify("Your resource quota is exceeded, so I can't create any new logs for you at this time. Please check to see if you can free up quota by recycling some of the objects you already own, or type '@measure new me' if you have created more than 10 new objects recently. If this does not help and you still need more quota contact one of the administrators.");
return $nothing;
endif
message = "a label for this log";
label = $command_utils:read(message);
$building_utils:set_names(new, label);
player:tell(">> Loading log ", label, ". Done. <<");
return new;
"Last modified Sun Jul 20 11:06:17 1997 CDT by Wizard (#2).";
.
#115:1
"Routine that assigns a note to a recorder and turns the recorder on";
if (caller != this)
return E_PERM;
endif
log = this:create_note();
if (log != $nothing)
string = (((">> A red light on the " + this.name) + " flashes to indicate that it has been turned on and is now recording everything that is being said in ") + this.location.name) + " <<";
this.location:announce_all(string);
this.log = log;
this.recording = 1;
this.log.text = {@this.log.text, (("-- Start log: " + $time_utils:time_sub("$D, $N $t, $Y $o:$M:$S $p ")) + $network.MOO_name) + " time --"};
this.log.text = {@this.log.text, ""};
else
player:tell(">>> RECORDER NOT STARTED <<<");
endif
"Last modified Sun Jul 20 11:23:06 1997 CDT by Wizard (#2).";
.
#115:2
"Stop recorder and forward log via email";
"Updated 2/3/98 to take advantage of new enCore Web interface feature";
if (caller != this)
return E_PERM;
endif
this.log.text = {@this.log.text, ""};
this.log.text = {@this.log.text, (("-- End log: " + $time_utils:time_sub("$D, $N $t, $Y $o:$M:$S $p ")) + $network.MOO_name) + " time --"};
this.log.text = {@this.log.text, ""};
player:tell("");
player:tell("PS! You can get an HTML formatted version of ", this.log.name, " by pointing your web browser to http://", $network.site, ":", $network.webport, "/", tonum(this.log), " and saving it as HTML source to your machine.");
if (player == this.log.owner)
question = ("Would you like me to send the log '" + this.log.name) + "' to your registered email address?";
if ($command_utils:yes_or_no(question))
player:tell("Mailing ", this.log.name, " to ", player.email_address, ".");
player:tell("... ", length(this.log.text), " lines ...");
suspend(0);
$network:sendmail(player.email_address, this.log.name, "", @this.log.text);
endif
endif
player:tell(">> ", this.log.name, " moved to ", this.name, " <<");
this.log:moveto(this);
this.recording = 0;
this.log = #-1;
string = (((">> The red light on " + this.name) + " goes out. The recorder in ") + this.location.name) + " has been turned off. <<";
this.location:announce_all(string);
"Last modified Fri Feb  6 08:37:27 1998 CST by Wizard (#2).";
.
#115:3
if (this.recording)
return this.name + " (recording)";
elseif (this.playing)
return this.name + " (playing)";
else
return this.name;
endif
"Last modified Sun Jul 20 11:11:19 1997 CDT by Wizard (#2).";
.
#115:4
if (this.contents)
su = $string_utils;
n = 0;
channel = this;
player:tell("Contents:");
player:tell("--- Title ------------------------------------------- Lenght ----------- Owner");
for thing in (channel.contents)
$command_utils:suspend_if_needed(0);
n = n + 1;
size = tostr(length(thing.text));
size = size + " lines";
if (length(thing.name) > 45)
title = thing.name[1..44];
else
title = thing.name;
endif
if (length(thing.owner.name) > 18)
owner = thing.owner.name[1..17];
else
owner = thing.owner.name;
endif
player:tell(su:left(n, 4), su:left(title, 45), su:right(size, 11), su:right(owner, 18));
endfor
player:tell("------------------------------------------------------------------------------");
endif
"Last modified Sun Jul 20 11:17:05 1997 CDT by Wizard (#2).";
.
#115:5
log = this.log;
origin = this;
what = "";
for item in (args)
$command_utils:suspend_if_needed(0);
what = what + tostr(item);
endfor
if (this.recording)
log.text = {@log.text, what};
endif
"Last modified Sun Jul 20 11:33:38 1997 CDT by Wizard (#2).";
.
#115:6
if (!$object_utils:isa(dobj, $note))
return player:notify("Sorry, incompatible format.");
endif
pass(@args);
"Last modified Sun Jul 20 11:47:27 1997 CDT by Wizard (#2).";
.
#115:7
if (!this.contents)
player:tell(this.name, " is empty.");
else
try
n = tonum(args[1]);
selection = this.contents[n];
player:tell("You select ", selection.name, " from  ", this.name);
player.location:announce(player.name, " selects ", selection.name, " from ", this.name);
this.playing = 1;
text = selection:text();
for line in [1..length(selection.text)]
if (this.playing == 0)
player.location:announce_all(player.name, " stops ", this.name, ".");
this.playing = 0;
return;
endif
if (line == "")
player.location:announce_all("");
else
player.location:announce_all("<On ", this.name, "> ", selection.text[line]);
endif
suspend(this.delay);
endfor
player.location:announce_all(this.name, " stops.");
this.playing = 0;
except (ANY)
player:tell("Sorry, cannot find the log you want. Please refer to logs by index number. If you want to play log number 1, you should type: 'play 1 on ", this.name, "'.");
endtry
endif
"Last modified Fri Apr 23 15:45:47 1999 CDT by Wizard (#2).";
.
#115:8
this.delay = tonum(iobjstr);
player:tell("You set the delay on ", this.name, " to ", this.delay, " seconds.");
"Last modified Sun Jul 20 12:15:40 1997 CDT by Wizard (#2).";
.
#115:9
player:notify("                      ____________________________");
player:notify("                     / _________________________ /|");
player:notify("                    / __ __________________  __ / |");
player:notify("                   / __ /  ___     ___    / __ / #|");
player:notify("                  / __ /  (_*_)   (_*_)  / __ / ##|");
player:notify("                 / __ /_________________/ __ / ##/");
player:notify("                /___________________________/ ##/");
player:notify(this.recording ? "                | (*) RECORDING         mic |##/" | "                | ( ) RECORDING         mic |##/");
player:notify(this.playing ? "                | (*) PLAYING           ### |#/" | "                | ( ) PLAYING           ### |#/");
player:notify("                |_________________jan97_###_|/");
player:notify("");
pass(@args);
"Last modified Sun Jul 20 12:38:26 1997 CDT by Wizard (#2).";
.
#115:10
if (caller_perms().wizard)
pass();
this.recording = 0;
this.playing = 0;
this.log = #-1;
this.delay = 3;
this.design = "jan@LinguaMOO";
move(this, $tool_box);
endif
"Last modified Tue Oct 14 14:19:42 1997 CDT by Wizard (#2).";
.
#115:11
"===========================================================";
"Copyright (C) 1995-2001, Jan Rune Holmevik and Mark Blanchard";
"===========================================================";
html = pass(@args);
user = args[1];
contents = {};
if (this.contents)
titles = this.contents;
lengths = {};
owners = {};
for n in (titles)
lengths = {@lengths, tostr(length(n.text), " lines")};
owners = {@owners, n.owner.name};
endfor
titles = $encore_web_utils:generate_links(user, titles);
titles = {"<B>Title</B>", @titles};
lengths = {"<B>Length</B>", @lengths};
owners = {"<B>Owner</B>", @owners};
titles = $encore_web_utils:insert_line_breaks(titles);
lengths = $encore_web_utils:insert_line_breaks(lengths);
owners = $encore_web_utils:insert_line_breaks(owners);
contents = $encore_web_utils:generate_table(user, {titles, lengths, owners}, this, "1");
contents = {"<P><B>Contents:</B><P>", @contents};
else
contents = {@contents, "<P>It is empty."};
endif
html = $list_utils:append(html, contents);
return html;
"Last modified Fri Apr 13 19:05:33 2001 CDT by Wizard (#2).";
.
#115:12
"Copyright (C) 1999, Jan Rune Holmevik";
"Routine for starting and stopping the recorder";
set_task_perms(player);
if ((!this.private) || (this.private && ((player == this.owner) || player.wizard)))
if (verb == "start")
if (!$object_utils:isa(player, $builder))
return player:tell("Sorry, you are not authorized to record.");
endif
if (this.recording)
return player:tell("The ", this.name, " is already on. You need to stop the recording in progress before you can start another one. Type 'stop ", this.name, "'.");
endif
if (this.playing)
return player:tell("The ", this.name, " is playing a tape. Please stop it before starting a recording. Type 'stop ", this.name, "'.");
endif
if (!$object_utils:isa(this.location, $room))
return player:tell("You must drop ", this.name, " in a room before you can use it.");
endif
this:start_recorder();
else
if (this.recording)
this:stop_recorder();
elseif (this.playing)
this.playing = 0;
else
player:tell(this.name, " is already stopped.");
endif
endif
else
player:tell("Sorry, only the owner may operate the recording function on ", this.name);
endif
"Last modified Sun Nov 28 04:23:18 1999 CST by Wizard (#2).";
.
#115:13
"Copyright (C) 1999, Jan Rune Holmevik";
"Routine for setting recorder public or private";
set_task_perms(player);
if ((!this.private) || (this.private && ((player == this.owner) || player.wizard)))
if (length(args) != 3)
player:tell("Usage: set recorder-name to public/private");
else
if (args[3] == "public")
this.private = 0;
player:tell(this.name, " set to public. Anyone can now operate it.");
elseif (args[3] == "private")
this.private = 1;
player:tell(this.name, " set to private. Only the owner or a wizard can now operate it.");
else
player:tell("Sorry, the setting '", args[3], "' is not valid. Correct settings are 'public' and 'private'.");
endif
endif
else
player:tell("Sorry, only the owner may reconfigure ", this.name);
endif
"Last modified Sun Nov 28 04:23:33 1999 CST by Wizard (#2).";
.
#116:0
if (caller_perms().wizard)
pass();
this.date = time();
this.read_by = {};
move(this, $news);
endif
"Last modified Tue Oct 14 14:19:40 1997 CDT by Wizard (#2).";
.
#117:0
if (caller_perms().wizard)
pass();
this.date = time();
this.read_by = {};
move(this, $news);
endif
"Last modified Tue Oct 14 14:19:40 1997 CDT by Wizard (#2).";
.
#118:0
allrooms = $object_utils:descendants_suspended($room);
for room in (allrooms)
$command_utils:suspend_if_needed(0);
this:insert(room.name, room);
endfor
"Last modified Sun Aug 17 12:04:17 1997 CDT by Wizard (#2).";
.
#118:1
if (caller_perms().wizard)
pass();
this:clearall();
this.updating = 0;
this.last_update = 0;
endif
"Last modified Sun Aug 17 12:19:46 1997 CDT by Wizard (#2).";
.
#119:0
"'description ()' - Return the meter's display.";
this:touch1();
settings = (((((((" The meter is adjusted to report the average lag over " + tostr(this.sample_count * this.cycle_time)) + " seconds") + " by taking ") + tostr(this.sample_count)) + " samples,") + " one each ") + tostr(this.cycle_time)) + " seconds.";
desc = {this.description + settings, this:reading()};
if (this.active && (this.cycles < this.sample_count))
desc = {@desc, "The meter is still warming up. Its display may be off."};
endif
return desc;
"Last modified Tue Oct 14 14:16:07 1997 CDT by Wizard (#2).";
.
#119:1
"'about meter' - Tell about the lag meter.";
this:touch1();
player:tell_lines(this.about);
"Last modified Tue Oct 14 14:16:07 1997 CDT by Wizard (#2).";
.
#119:2
"'turn meter on', 'turn meter off', 'turn on meter', 'turn off meter' - Turn the meter on or off. When things are very lagged, you might want to turn it off to keep it from making the situation worse.";
this:touch1();
if (prepstr == "on")
if (this.active)
player:tell("The meter is already on. If it's not working, turn it off, wait at least ", this.cycle_time, " seconds for it to cool down, and turn it back on again.");
else
this:startup();
player:tell("You turn on the meter. It will take about ", this.cycle_time * this.sample_count, " seconds to warm up fully.");
this.location:announce(player.name, " turns on the lag meter.");
endif
elseif (prepstr == "off")
if (this.active)
this.active = 0;
player:tell("You turn off the meter.");
this.location:announce(player.name, " turns off the lag meter.");
else
player:tell("The meter is already off.");
endif
else
player:tell("Usage: 'turn meter on', 'turn meter off'.");
endif
"Last modified Tue Oct 14 14:16:07 1997 CDT by Wizard (#2).";
.
#119:3
"'startup ()' - Start the meter up.";
this:initialize();
this.active = 1;
this.cycle_scheduled = time() + this.cycle_time;
fork (this.cycle_time)
this:lag_cycle();
endfork
"Last modified Tue Oct 14 14:16:07 1997 CDT by Wizard (#2).";
.
#119:4
"'lag_cycle ()' - Collect statistics.";
if (caller != this)
return E_PERM;
endif
while (this.active)
lag = time() - this.cycle_scheduled;
i = this:first_index();
this.total = (this.total + lag) - this.samples[i];
this.samples[i] = lag;
this.cycles = this.cycles + 1;
"Start the next cycle.";
this.cycle_scheduled = time() + this.cycle_time;
suspend(this.cycle_time);
endwhile
"Last modified Tue Oct 14 14:16:07 1997 CDT by Wizard (#2).";
.
#119:5
"'initialize ()' - Clear the meter's data statistics to zero. The length of the .samples list depends on the current .sample_count, so this verb should be called every time .sample_count changes.";
pass(@args);
this.cycles = 1;
this.total = 0;
this.samples = $list_utils:make(this.sample_count, 0);
"Last modified Tue Oct 14 14:16:07 1997 CDT by Wizard (#2).";
.
#119:6
"'reset meter', 'reset meter to <time> <samples>' - Reset the meter, clearing its statistics. If you provide <time> and <samples>, then the meter reports the average lag over an interval of <time> seconds, as measured by taking <samples> number of samples. The meter will refuse to sample more often than once every five seconds.";
if ((prepstr && (prepstr != "to")) || (dobj != this))
player:tell(this.reset_usage_msg);
return;
endif
if (prepstr)
numbers = $string_utils:explode(iobjstr, " ");
if (((length(numbers) != 2) || (!tonum(numbers[1]))) || (!tonum(numbers[2])))
player:tell(this.reset_usage_msg);
return;
endif
interval = tonum(numbers[1]);
sample_count = tonum(numbers[2]);
if ((interval < 5) || (sample_count < 1))
player:tell("The time interval must be at least 5 seconds, and the meter must take at least one sample over the interval.");
return;
endif
cycle_time = interval / sample_count;
if (cycle_time < 5)
player:tell("That would cause the meter to sample more often than once every five seconds. Type 'about meter' for information.");
return;
endif
if ((cycle_time * sample_count) != interval)
player:tell("The numbers don't work out evenly. The meter will actually average over a time of ", cycle_time * sample_count, " seconds.");
endif
this.cycle_time = cycle_time;
this.sample_count = sample_count;
endif
player:tell("You reset the meter.");
this.location:announce(player.name, " resets the lag meter.");
this:initialize();
"Last modified Tue Oct 14 14:16:08 1997 CDT by Wizard (#2).";
.
#119:7
"'read meter' - Read the meter. This gives you the one-line display without the description.";
player:tell(this:reading());
"Last modified Tue Oct 14 14:16:08 1997 CDT by Wizard (#2).";
.
#119:8
"'reading () -> string' - Return a string, the meter's current reading. You can call this yourself, if you happen to want the reading for some other purpose.";
if (this.active)
if (this.total == 0)
display = "The lag is 0 seconds flat";
else
lag = this:decimal1(this.total, this.sample_count);
display = (("The lag is about " + lag) + " second") + ((lag == "1.0") ? "" | "s");
endif
display = display + ". It is ";
diff = this:trend();
delta = this.total / 5;
small = this.sample_count / 3;
if ((diff > delta) && (diff > small))
display = display + "getting worse";
elseif ((diff < (-delta)) && (diff < (-small)))
display = display + "getting better";
elseif (diff == 0)
display = display + "staying level";
else
display = display + "holding nearly even";
endif
display = display + ".";
else
display = "It is turned off. The display is dark.";
endif
return display;
"Last modified Tue Oct 14 14:16:09 1997 CDT by Wizard (#2).";
.
#119:9
"'ratioX10 (a, b) -> \"n.d\"' - Return a string, the decimal expansion of a/b to one decimal place.";
s = tostr((10 * args[1]) / args[2]);
if (length(s) == 1)
s = "0" + s;
endif
return (s[1..length(s) - 1] + ".") + s[length(s)];
"Last modified Tue Oct 14 14:16:09 1997 CDT by Wizard (#2).";
.
#119:10
"'trend_of (<samples>)' - <Samples> is a list of lag samples, in temporal order. There are at least two samples in the list. This verb returns a rough measure of the trend of the samples, up or down.";
samples = args[1];
len = length(samples);
half = len / 2;
even = !(len % 2);
trend = 0;
for i in [1..len]
if (i <= half)
trend = trend - samples[i];
elseif ((i > (half + 1)) || ((i == (half + 1)) && even))
trend = trend + samples[i];
endif
endfor
return trend;
"Last modified Tue Oct 14 14:16:09 1997 CDT by Wizard (#2).";
.
#119:11
"'trend ()' - Return a lag trend measurement.";
return this:trend_of(this:get_lags());
"Last modified Tue Oct 14 14:16:10 1997 CDT by Wizard (#2).";
.
#119:12
"'@lag' - Report the lag.";
player:tell(this:reading());
"Last modified Tue Oct 14 14:16:10 1997 CDT by Wizard (#2).";
.
#119:13
"'help_msg ()' - Return help information.";
return this.about;
"Last modified Tue Oct 14 14:16:10 1997 CDT by Wizard (#2).";
.
#119:14
"'get_lags ()' -> {0, 3, 0, ...} - Return the list of lag samples, in seconds. ";
i = this:first_index();
return {@this.samples[i..this.sample_count], @this.samples[1..i - 1]};
"Last modified Tue Oct 14 14:16:10 1997 CDT by Wizard (#2).";
.
#119:15
"'lags_reading ()' -> string - Return a string, the list of lag samples.";
lags = this:get_lags();
return (("One each " + tostr(this.cycle_time)) + " seconds, oldest to newest: ") + $string_utils:english_list(lags, "none", " ", " ", "");
"Last modified Tue Oct 14 14:16:10 1997 CDT by Wizard (#2).";
.
#119:16
"'lags on meter' - See the lag samples.";
this:touch1();
player:tell(this:lags_reading());
"Last modified Tue Oct 14 14:16:10 1997 CDT by Wizard (#2).";
.
#119:17
"'@lags' - Show the lag samples.";
player:tell(this:lags_reading());
"Last modified Tue Oct 14 14:16:10 1997 CDT by Wizard (#2).";
.
#119:18
"'first_index ()' -> <n> - Return the index of the 'first' lag sample in the list this.samples. The first lag is the one which was measured earliest. The lags start at the first one and wrap around to the beginning of the list.";
return (this.cycles % this.sample_count) + 1;
"Last modified Tue Oct 14 14:16:11 1997 CDT by Wizard (#2).";
.
#119:19
return;
"Last modified Tue Oct 14 14:16:11 1997 CDT by Wizard (#2).";
.
#119:20
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Fri Feb  6 08:47:29 1998 CST by Wizard (#2).";
.
#120:0
if (caller_perms().wizard)
pass();
move(this, $player_start);
this.opened = 1;
this.dark = 0;
this.description = "A large box for storing educational tools and other objects.";
endif
"Last modified Tue Oct 14 14:37:05 1997 CDT by Wizard (#2).";
.
#121:0
if (caller_perms().wizard)
pass();
this.description = "A quiet place where you can complete your character request. Please be sure that you have read and understood the purpose and theme of this MOO, as well as the code of conduct expected.";
move(this, $nowhere);
endif
"Last modified Wed Apr 15 12:16:32 1998 CDT by Wizard (#2).";
.
#122:0
if (caller_perms().wizard)
pass();
this.name = "Character-Request-List";
this.description = "List where new character requests are sent.";
this.guests_can_send_here = 1;
move(this, $mail_agent);
endif
"Last modified Tue Oct 14 14:22:19 1997 CDT by Wizard (#2).";
.
#123:0
player:tell("Preparing census...");
suspend(0);
su = $string_utils;
lu = $list_utils;
cu = $command_utils;
guest = $guest;
aplayer = $player;
title = {"Guests", "Players", "Builders", "Programmers", "Wizards"};
u = lu:make(7, 0);
c = lu:make(length(title), u);
whom = players();
for i in (this.not_in_census)
whom = setremove(whom, i);
endfor
for i in (whom)
cu:suspend_if_needed(1);
if (i.wizard)
wh = 5;
elseif (i.programmer)
wh = 4;
elseif (parent(i) == guest)
wh = 1;
elseif (parent(i) == aplayer)
wh = 2;
else
wh = 3;
endif
if (i in connected_players())
if (idle_seconds(i) < 300)
c[wh][1] = c[wh][1] + 1;
else
c[wh][2] = c[wh][2] + 1;
endif
else
dt = `time() - i.last_disconnect_time ! E_PROPNF => $maxint';
if ((time() - `i.last_connect_time ! E_PROPNF => 0') < 0)
c[wh][7] = c[wh][7] + 1;
elseif (dt < 86400)
c[wh][3] = c[wh][3] + 1;
elseif (dt < 604800)
c[wh][4] = c[wh][4] + 1;
elseif (dt < 2419200)
c[wh][5] = c[wh][5] + 1;
else
c[wh][6] = c[wh][6] + 1;
endif
endif
endfor
player:tell("Status          Conn  Day Week Month Never Total  Active Idle");
player:tell("----------------------------------------------------------------");
ac = id = co = dy = wk = mn = nv = tt = 0;
for i in [1..length(title)]
ac = ac + (act = c[i][1]);
id = id + (idl = c[i][2]);
co = co + (con = act + idl);
dy = dy + (day = con + c[i][3]);
wk = wk + (wek = day + c[i][4]);
mn = mn + (mnt = wek + c[i][5]);
nv = nv + (nvr = c[i][7]);
tt = tt + (ttl = (mnt + nvr) + c[i][6]);
player:tell(su:left(title[i], 15, " "), su:right(con, 5, " "), su:right(day, 5, " "), su:right(wek, 5, " "), su:right(mnt, 6, " "), su:right(nvr, 6, " "), su:right(ttl, 6, " "), su:right(act, 8, " "), su:right(idl, 5, " "));
endfor
player:tell("----------------------------------------------------------------");
player:tell(su:left("Total", 15, " "), su:right(co, 5, " "), su:right(dy, 5, " "), su:right(wk, 5, " "), su:right(mn, 6, " "), su:right(nv, 6, " "), su:right(tt, 6, " "), su:right(ac, 8, " "), su:right(id, 5, " "));
"Last modified Sat Oct 18 15:07:54 1997 CDT by Wizard (#2).";
.
#123:1
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Fri Feb  6 08:47:29 1998 CST by Wizard (#2).";
.
#124:0
"Copyright (C) 1998, Jan Rune Holmevik";
text = $string_utils:uppercase(args[1]);
border = "";
for n in [1..length(text)]
border = border + "=";
endfor
return {border, text, border};
"Last modified Wed Apr 15 12:51:07 1998 CDT by Wizard (#2).";
.
#125:0
"===========================================================";
"Copyright (C) 1997-2001, Jan Rune Holmevik";
"Creates an HTML Header for a page. In addition to the object number of the object or application, this verb also accepts three optional arguments which specifies an alternative page title a base target, and content type.";
"THIS VERB HAS BEEN DEPRECIATED. PROGRAMMERS USE MAKE_HEAD INSTEAD";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{page, ?page_title = page:title(), ?target = "", ?content_type = $httpd.content_type, ?doctype = page.doctype_transitional} = args;
if (target != "")
target = {tostr("   <BASE TARGET=", target, ">")};
else
target = {};
endif
head = {doctype, "<HTML>", "<HEAD>"};
meta = {tostr("   <META HTTP-EQUIV=\"content-type\" CONTENT=\"", content_type, "\">")};
meta = {@meta, tostr("   <META NAME=\"Generator\" CONTENT=\"", $httpd.server_name, $core_version, "\">")};
meta = {@meta, tostr("   <META NAME=\"Description\" CONTENT=\"This page was generated by the ", $network.MOO_name, " ", $httpd.server_name, $core_version, " web server on ", $time_utils:time_sub("$N $3. $Y at $H:$M:$S, $Z", time()), "\">")};
meta = {@meta, tostr("   <META NAME=\"Keywords\" CONTENT=\"", $core_name, ", enCore Xpress, ", $network.MOO_name, ", MOO, MUD\">")};
meta = {@meta, tostr("   <META NAME=\"Copyright\" CONTENT=\"High Wired enCore and enCore Xpress is Copyright (C) 1997-2001 of the enCore Open Source MOO Project. All Rights Reserved.\">")};
title = {tostr("   <TITLE>", page_title, " (", $network.MOO_name, ")</TITLE>")};
header = $list_utils:append(head, meta, title, target, {"</HEAD>"});
return header;
"Last modified Fri Apr 13 19:12:35 2001 CDT by Wizard (#2).";
.
#125:1
"Copyright (C) 1999, Jan Rune Holmevik";
object = args[1];
html = {};
if (object.footer)
html = {@html, "<!-- Footer -->"};
html = {@html, object.footer};
endif
html = {@html, "</BODY>"};
html = {@html, "</HTML>"};
return html;
"Last modified Thu Apr  8 12:21:05 1999 CDT by Wizard (#2).";
.
#125:2
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"If the variable exit name is specified as 0 then this verb will use the exit's destination as link name";
"===========================================================";
{user, domain, ?target = "", ?use_exit_name = 0} = args;
result = {};
base_url = tostr("http://", $network.site, ":", $network.webport, "/");
for object in (domain)
suspend(0);
if ($object_utils:isa(object, $encore_web_object))
if ($object_utils:isa(object, $exit) && (!use_exit_name))
linkname = object.dest.name;
link = tonum(object.dest);
elseif (is_player(object))
linkname = object:titlec();
link = tonum(object);
else
linkname = object.name;
link = tonum(object);
endif
link = tostr("<A HREF=\"", base_url, link, "/\"", (target == "") ? "" | tostr(" TARGET=\"", target, "\""), ">", $encore_web_utils:get_icon(user, object), linkname, "</A>");
else
link = object.name;
endif
result = {@result, link};
endfor
return result;
"Last modified Fri Apr 13 19:04:45 2001 CDT by Wizard (#2).";
.
#125:3
"===========================================================";
"Copyright (C) 1995-2001, Jan Rune Holmevik and Mark Blanchard";
"Verb that checks if a user can view a room, read a note, or open a container. If the object is locked and the user does not have the key, 1 is returned";
"===========================================================";
{user, object} = args;
locked = 0;
if ($object_utils:isa(object, $room))
object.blessed_object = user;
object.blessed_task = task_id();
if (!object:acceptable(user))
locked = 1;
endif
elseif ($object_utils:isa(object, $note) && (!object:is_readable_by(user)))
locked = 1;
elseif ($object_utils:isa(object, $exit) && this:is_locked(user, object.dest))
locked = 1;
endif
return locked;
"Last modified Fri Apr 13 19:04:45 2001 CDT by Wizard (#2).";
.
#125:4
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
object = args[1];
locked_msg = "";
"Modified by Traveller 7/15/00[41 to allow objects to have custom locked_msg";
if ($code_utils:verb_or_property(object, "locked_msg") != E_PROPNF)
locked_msg = $code_utils:verb_or_property(object, "locked_msg");
elseif ($object_utils:isa(object, $room))
locked_msg = tostr("Sorry, the room ", object.name, " is locked and not accessible to you.");
elseif ($object_utils:isa(object, $note))
locked_msg = tostr("Sorry, but ", object.name, " seems to be written in some code that you cannot read.");
endif
if (locked_msg)
locked_msg = tostr("alert('", $string_utils:subst(locked_msg, {{"'", ""}, {"\"", ""}}), "');");
endif
return locked_msg;
"Last modified Sun Sep 30 13:16:56 2001 CDT by Wizard (#2).";
.
#125:5
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Get object title and insert contextual menu if appropriate";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object, ?title = object.name} = args;
if (verb == "get_title")
{functions, buttons} = object:make_contextual_menu(user);
title = $list_utils:append(functions, {tostr("<DIV CLASS=Title>", title)}, {"</DIV>"}, buttons);
else
title = {tostr("<DIV CLASS=SubTitle>", title, "</DIV>")};
endif
return title;
"Last modified Sun Sep 30 13:11:33 2001 CDT by Wizard (#2).";
.
#125:6
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"This verb generates a table based on arguments passed from the calling verb. The first argument should be the user, the second, a list containing the data (a list or set of lists), and the third argument should contain the number of the calling object. In addition, the following optional arguments may be passed to this verb:";
"The 4rd argument should be an alpha numerical value 0/1. If 1 is passed, a table with number of rows equal to the longest collumn (list) in data will be created. Else, a simple 1 row table will be generated";
"===========================================================";
{user, data, object, ?layout = "0"} = args;
table = "<TABLE";
table = table + tostr(" BORDER=\"", object.table_border, "\" VALIGN=\"", object.table_vertical_alignment, "\" WIDTH=\"", object.table_width, "\">");
table = {table};
if ((layout == "0") || (layout == 0))
rows = 1;
else
"Find length of longest list";
rows = 0;
for lst in (data)
suspend(0);
if (length(lst) > rows)
rows = length(lst);
endif
endfor
endif
"Generate table rows";
for row in [1..rows]
table = {@table, tostr("<TR VALIGN=\"", object.table_vertical_alignment, "\">")};
"Generate table cells";
for item in [1..length(data)]
suspend(0);
if (rows == 1)
cell = data[item];
elseif (row > length(data[item]))
cell = {" "};
else
cell = {data[item][row]};
endif
cell = {tostr("<TD WIDTH=\"", 100 / length(data), "%\">"), @cell, "</TD>"};
table = $list_utils:append(table, cell);
endfor
table = {@table, "</TR>"};
endfor
table = {@table, "</TABLE>"};
return table;
"Last modified Fri Apr 13 19:06:34 2001 CDT by Wizard (#2).";
.
#125:7
"Copyright (C) 1995-2001, Jan Rune Holmevik and Mark Blanchard";
"DEPRECATED. Use $encore_web_utils:align instead.";
return {"<CENTER>", @args[1], "</CENTER>"};
"Last modified Sun Sep 30 12:48:18 2001 CDT by Wizard (#2).";
.
#125:8
"Copyright (C) 1995-98, Jan Rune Holmevik and Mark Blanchard";
return {"<PRE>", @args[1], "</PRE>"};
"Last modified Sat Feb  7 23:36:36 1998 CST by Wizard (#2).";
.
#125:9
"Copyright (C) 1995-98, Jan Rune Holmevik and Mark Blanchard";
result = {};
text = args[1];
if (typeof(text) == LIST)
for line in (text)
$command_utils:suspend_if_needed(1);
result = {@result, line};
endfor
else
result = {@result, text};
endif
return result;
"Last modified Sat Feb  7 23:36:53 1998 CST by Wizard (#2).";
.
#125:10
"===========================================================";
"Copyright (C) 1995-2001, Jan Rune Holmevik, Mark Blanchard, and Herv Collin";
"Returns a list with a <BR> tag appended to the end of each line.";
"===========================================================";
result = {};
text = args[1];
if (typeof(text) == LIST)
for line in [1..length(text)]
$command_utils:suspend_if_needed(1);
if ($string_utils:explode(text[line], "<") || $string_utils:explode(text[line], ">"))
string = $string_utils:subst($string_utils:subst(text[line], {{"<", "&lt;"}}), {{">", "&gt;"}}) + "<BR>";
result = {@result, string};
elseif (text[line] == "")
result = {@result, "<BR>"};
endif
endfor
endif
return result;
"Last modified Fri Apr 13 19:12:50 2001 CDT by Wizard (#2).";
.
#125:11
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Returns web properties of the requested object. If a second argument is provided, a JavaScript onLoad call will be inserted. Modified to accommodate new layout system.";
"THIS VERB HAS BEEN DEPRECIATED. USE MAKE_BODY INSTEAD";
"===========================================================";
{user, object, ?onload = "", ?frame_identity = ""} = args;
background = "";
if (frame_identity)
frame_identity = tostr("ID=\"", frame_identity, "\" NAME=\"", frame_identity, "\"");
endif
if (object.web_background)
if ($object_utils:isa(object, $encore_web_application))
background = tostr($xpress_client.external_baseurl, $xpress_client.icons_folder, object.web_background);
else
background = object.web_background;
endif
endif
page_properties = {tostr("<BODY ", frame_identity, " ", onload, " BGCOLOR=\"", object.web_bgcolor, "\" BACKGROUND=\"", background, "\" TEXT=\"", object.web_text_color, "\" LINK=\"", object.web_link_color, "\" ALINK=\"", object.web_alink_color, "\" VLINK=\"", object.web_vlink_color, "\">")};
return page_properties;
"Last modified Fri Apr 13 19:12:35 2001 CDT by Wizard (#2).";
.
#125:12
"===========================================================";
"Copyright (C) 1995-2001, Jan Rune Holmevik and Mark Blanchard";
"===========================================================";
{user, object} = args;
url = object.url_address;
width = (caller.web_width == "") ? "" | tostr(" WIDTH=\"", caller.web_width, "\"");
height = (caller.web_height == "") ? "" | tostr(" HEIGHT=\"", caller.web_height, "\"");
filetype = $encore_web_utils:get_file_type(url);
if (filetype == "A HREF=")
img = tostr($xpress_client.external_baseurl, $xpress_client.icons_folder, "weblink.gif");
html = tostr("<", filetype, caller.url_address, " TARGET=\"_blank\">[Click to view web page associated with this object]</A>");
elseif (filetype == "APPLET")
codebase = "";
url = $string_utils:explode(url, "/");
sourcefile = url[length(url)];
for n in [1..length(url) - 1]
if (n == 1)
codebase = (codebase + url[n]) + "//";
else
codebase = (codebase + url[n]) + "/";
endif
endfor
html = tostr("<", filetype, " CODE=\"", sourcefile, "\" CODEBASE=\"", codebase, "\" ", width, " ", height, " ALIGN=\"", caller.web_align, "\"</APPLET>");
else
html = tostr("<", filetype, " SRC=\"", caller.url_address, "\" ", width, " ", height, " ALIGN=\"", caller.web_align, "\">");
endif
return {html};
"Last modified Fri Apr 13 18:52:23 2001 CDT by Wizard (#2).";
.
#125:13
"Copyright (C) 1995-98, Jan Rune Holmevik and Mark Blanchard";
url = $string_utils:explode(args[1], ".");
ext = url[length(url)];
if (ext in $httpd.inline_image_suffixes)
type = "IMG";
elseif ((ext == "class") || (ext == "jar"))
type = "APPLET";
elseif (ext in $httpd.embed_file_suffixes)
type = "EMBED";
else
type = "A HREF=";
endif
return type;
"Last modified Mon Apr 12 16:04:29 1999 CDT by Wizard (#2).";
.
#125:14
"===========================================================";
"Copyright (C) 1995-2001, Jan Rune Holmevik and Mark Blanchard";
"===========================================================";
text = args[1];
result = {};
newline = "";
for line in (text)
$command_utils:suspend_if_needed(0);
result = {@result, line + "<BR>"};
endfor
return result;
"Last modified Fri Apr 13 19:06:33 2001 CDT by Wizard (#2).";
.
#125:15
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Create a sound embed tag";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, audio_file, ?name = "sound"} = args;
sound = {};
if (user.sound_volume)
if (!index(audio_file, "http://"))
"If complete url has not been specified, use sound file from encore";
audio_file = ($xpress_client.external_baseurl + $xpress_client.sounds_folder) + audio_file;
endif
sound = {tostr("<EMBED SRC=\"", audio_file, "\" WIDTH=\"1\" HEIGHT=\"1\" NAME=\"", name, "\" HIDDEN=\"true\" AUTOSTART=\"true\" VOLUME=\"", user.sound_volume, "\" LOOP=\"false\" MASTERSOUND>")};
endif
return sound;
"Last modified Mon Sep 17 10:07:03 2001 CDT by Wizard (#2).";
.
#125:16
"Copyright (C) 1999, Jan Rune Holmevik";
if (!caller_perms().wizard)
return E_PERM;
endif
{body, ?form = 0, ?window = "parent"} = args;
result = {};
if (form != 0)
result = $list_utils:append(body, {tostr("<FORM><INPUT TYPE=button NAME=close_window VALUE=\"Close\" onClick=\"", window, ".close()\"></FORM>")});
else
result = $list_utils:append(body, {tostr("<INPUT TYPE=button NAME=close_window VALUE=\"Close\" onClick=\"", window, ".close()\">")});
endif
return result;
"Last modified Thu Apr  8 12:20:41 1999 CDT by Wizard (#2).";
.
#125:17
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik and Matthew Beermann";
"Checks validity of HTTP request, and returns object and verb if valid, else $nothing is returned";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
requested_object = requested_verb = data = "";
"------------------------------------------------------------";
"Break up the parts of the requested URL";
"------------------------------------------------------------";
url = $string_utils:explode(args[1], "/");
if (length(url) == 2)
verb_and_data = $string_utils:explode(url[2], "?");
requested_object = url[1];
requested_verb = verb_and_data[1];
if (length(verb_and_data) == 2)
data = verb_and_data[2];
endif
elseif (length(url) == 1)
requested_object = url[1];
endif
if (requested_object == "")
"------------------------------------------------------------";
"No object or app specified. We need to return user to the login page";
"------------------------------------------------------------";
return {$Xpress_login, "login", data};
elseif ($object_utils:has_property($sysobj, requested_object))
"------------------------------------------------------------";
"User has requested a web application";
"------------------------------------------------------------";
application = $sysobj.(requested_object);
"Make sure web application is in fact a web application, and if the requested verb exists on that application";
if ($object_utils:isa(application, $enCore_web_application))
requested_verb = $string_utils:substitute(requested_verb, {{".", "_"}});
if ($object_utils:has_callable_verb(application, requested_verb))
return {application, requested_verb, data};
endif
endif
elseif ($object_utils:isa(toobj(requested_object), $encore_web_object))
"------------------------------------------------------------";
"User has requested an enCore web object";
"------------------------------------------------------------";
requested_verb = $string_utils:substitute(requested_verb, {{".", "_"}});
if ((requested_verb && $object_utils:has_callable_verb(toobj(requested_object), requested_verb)) && index(requested_verb, "_html"))
return {toobj(requested_object), requested_verb, data};
else
return {toobj(requested_object), "_html", data};
endif
"Modified by Traveller 7/27/99 to allow custom verbs on non-core objects";
else
return $nothing;
endif
"Last modified Fri Apr 13 18:51:48 2001 CDT by Wizard (#2).";
.
#125:18
"Copyright (C) 1999-2000, Jan Rune Holmevik";
"Generates HTML code for a form based on parameters passed by the calling verb. The first argument must be the form data, and should be of type LIST, the other four arguments, form name, action, Javascript onSubmit call and target, are optional, and should be of type STR.";
if (!caller_perms().wizard)
return E_PERM;
endif
{data, ?name = "Untitled_Form", ?action = "/", ?onSubmit = "", ?target = "", ?method = "POST"} = args;
form = tostr("<FORM NAME=\"", name, "\" ACTION=\"http://", $network.site, ":", $network.webport, action, "\" ");
if (onSubmit)
form = ((form + " onSubmit=\"") + onSubmit) + "\"";
endif
if (target)
form = (form + " TARGET=") + target;
endif
if ("POST" == method)
"Internet Explorer 4.x will not send CRLF after FORM-posts with the default enctype (application/x-www-form-urlencoded)";
enctype = "multipart/form-data";
form = ((((form + " METHOD=\"") + method) + "\" ENCTYPE=\"") + enctype) + "\"";
endif
form = form + ">";
result = $list_utils:append({form}, data, {"</FORM>"});
return result;
"Last modified Fri Mar 24 21:02:45 2000 CST by Wizard (#2).";
.
#125:19
"Copyright (C) 1999, Jan Rune Holmevik";
"Generates a Javascript window.open function";
function = {};
{application, function_name, url, ?window_width = application.browser_window_width, ?window_height = application.browser_window_height, ?toolbar = application.browser_toolbar} = args;
window_name = application:get_frame_name(function_name);
toolbar = tostr("toolbar=", toolbar);
location = tostr(",location=", application.browser_location);
directories = tostr(",directories=", application.browser_directories);
status = tostr(",status=", application.browser_status);
menubar = tostr(",menubar=", application.browser_menubar);
scrollbars = tostr(",scrollbars=", application.browser_scrollbars);
resizable = tostr(",resizable=", application.browser_resizable);
copyhistory = tostr(",copyhistory=", application.browser_copyhistory);
line1 = {tostr("function ", function_name, "() {")};
if (window_height == "screen.availHeight")
line2 = {tostr("   var height = (", window_height, "-72);")};
else
line2 = {tostr("   var height = ", window_height, ";")};
endif
if (window_width == "screen.availWidth")
line3 = {tostr("   var width = (", window_width, "-30);")};
else
line3 = {tostr("   var width = ", window_width, ";")};
endif
line4 = {tostr("   newWin = window.open(\"", url, "\", \"", window_name, "\", \"screenX=0,screenY=0,top=0,left=0,height=\" + height + \",width=\" + width + \",", toolbar, location, directories, status, menubar, scrollbars, resizable, copyhistory, "\")")};
line5 = {"}"};
function = $list_utils:append(line1, line2, line3, line4, line5);
return function;
"Last modified Tue Apr 13 23:38:09 1999 CDT by Wizard (#2).";
.
#125:20
"Copyright (C) 1999, Jan Rune Holmevik";
":insert_javascript({javascript}, ?{noscript}) -- insert javascript inside a SCRIPT tag, with an optional NOSCRIPT element for browsers not supporting javascript.";
{functions, ?noscript = {}} = args;
start_script = {tostr("<SCRIPT LANGUAGE=\"", $xpress_client.script_language, "\">"), "<!-- Hide script from old browsers"};
end_script = {"// -->", "</SCRIPT>"};
start_noscript = {"<NOSCRIPT>"};
end_noscript = {"</NOSCRIPT>"};
script = $list_utils:append(start_script, functions, end_script);
if (noscript)
script = $list_utils:append(script, start_noscript, noscript, end_noscript);
endif
return script;
"Last modified Thu Apr  8 12:26:26 1999 CDT by Wizard (#2).";
.
#125:21
"Copyright (C) 1999, Jan Rune Holmevik";
result = {};
object = args[1];
if (object.banner)
result = {@result, object.banner};
endif
return result;
"Last modified Thu Apr  8 12:26:26 1999 CDT by Wizard (#2).";
.
#125:22
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Verb for moving players via web links";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, destination} = args;
origin = user.location;
"------------------------------------------------------";
"--             Default movement messages            --";
"------------------------------------------------------";
leave_msg = tostr("You move to ", destination.name);
oleave_msg = tostr(user.name, " leaves for ", destination.name);
oarrive_msg = tostr(user.name, " arrives from ", origin.name);
"------------------------------------------------------";
"-- Find correct exit and use messages if any        --";
"------------------------------------------------------";
for exit in (origin.exits)
if (exit.dest == destination)
"Exit found, check for messages";
if (exit.leave_msg != "")
leave_msg = exit.leave_msg;
endif
if (exit.oleave_msg != "")
oleave_msg = tostr(user.name, " ", $string_utils:pronoun_sub(exit.oleave_msg));
endif
if (exit.oarrive_msg != "")
oarrive_msg = tostr(user.name, " ", $string_utils:pronoun_sub(exit.oarrive_msg));
endif
break;
endif
endfor
"------------------------------------------------------";
"--        Move player and make announcements        --";
"------------------------------------------------------";
"Modified by Traveller 7/15/01 so that exitfunc and enterfunc get the 'real' value of player";
player = user;
user:tell(leave_msg);
destination:announce(oarrive_msg);
user.update_web_display = 0;
user:moveto(destination);
origin:announce_all(oleave_msg);
return;
"Last modified Sun Sep 30 13:16:56 2001 CDT by Wizard (#2).";
.
#125:23
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Generate hyper links with object numbers as arguments";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{objects, base_url} = args;
links = {};
for object in (objects)
if ($object_utils:has_property(object, "icon") && (object.icon != ""))
links = {@links, tostr("<A HREF=\"", base_url, "?", toint(object), "\">", $encore_web_utils:get_icon(user, object), object.name, "</A>")};
else
links = {@links, tostr("<A HREF=\"", base_url, "?", toint(object), "\">", object.name, "</A>")};
endif
endfor
return links;
"Last modified Fri Apr 13 19:04:45 2001 CDT by Wizard (#2).";
.
#125:24
"===========================================================";
"Copyright (C) 1995-98, Jan Rune Holmevik";
"THIS VERB HAS BEEN DEPRECIATED. NEW SYSTEM USES STYLE SHEETS";
"===========================================================";
{data, size} = args;
result = {tostr("<FONT SIZE=\"", size, "\">"), @data};
result = {@result, "</FONT>"};
return result;
"Last modified Fri Apr 13 19:12:35 2001 CDT by Wizard (#2).";
.
#125:25
"===========================================================";
"Copyright (C) 1995-2001, Jan Rune Holmevik";
"Define the appearance of web text by inserting FONT face, size and color tags. ";
"THIS VERB HAS BEEN DEPRECIATED. NEW SYSTEM USES STYLE SHEETS";
"===========================================================";
{user, data, object, ?font_face = object.web_font, ?font_size = object.web_font_size, ?font_color = object.web_text_color} = args;
if (typeof(data) != LIST)
data = {data};
endif
result = $list_utils:append({tostr("<FONT FACE=\"", font_face, "\" SIZE=\"", font_size, "\" COLOR=\"", font_color, "\">")}, data, {"</FONT>"});
return result;
"Last modified Fri Apr 13 19:12:35 2001 CDT by Wizard (#2).";
.
#125:26
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Inserts a context sensitive help button or icon, and creates a javascript window open function for the context sensitive help text";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object, help_property, ?width = $help_browser.context_help_width, ?height = $help_browser.context_help_height} = args;
external_baseurl = $xpress_client.external_baseurl + $xpress_client.icons_folder;
help = {tostr("<A HREF=\"http://", $network.site, ":", $network.webport, "/help_browser/view_html?", toint(object), "&", help_property, "\" onClick=\"ContextSensitiveHelp()\" TARGET=\"", $help_browser:get_frame_name("ContextSensitiveHelp"), "\">", $encore_web_utils:get_icon(user, $help_browser, "help.gif", "Help and information"), "</A>")};
context_help_function = $encore_web_utils:javascript_window_open($help_browser, "ContextSensitiveHelp", "", width, height);
return {help, context_help_function};
"Last modified Fri Apr 13 19:04:45 2001 CDT by Wizard (#2).";
.
#125:27
"Copyright (C) 1995-98, Jan Rune Holmevik";
return {"<I>", @args[1], "</I>"};
"Last modified Thu Apr  8 12:30:17 1999 CDT by Wizard (#2).";
.
#125:28
"Copyright (C) 1999-2000, Jan Rune Holmevik and Matthew Beermann";
"Searches for URLs in text and marks them up.";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, text, ?treat_as_HTML = 0} = args;
if (typeof(text) == STR)
text = {text};
endif
result = {};
for line in (text)
$command_utils:suspend_if_needed(0);
new_line = line;
more_urls = 1;
rest_line = line;
while (more_urls)
if ((position = index(rest_line, "http://")) && (!(((treat_as_HTML && index(rest_line, "<")) && (index(rest_line, "<") < position)) && (index(rest_line, ">") > position))))
"A url was detected in this line";
"Find the end of the URL by searching for the first empty space";
rest_line = rest_line[position..$];
url = "";
for next in [1..length(rest_line)]
if (rest_line[next] == " ")
break;
else
url = url + rest_line[next];
endif
endfor
"URL assembled, now check if the last character is punctuation";
while (url[$] in {")", "]", "}", ">", ":", ";", ".", ",", "!", "'", "\""})
url = url[1..length(url) - 1];
endwhile
link = tostr("<A HREF=\"", url, "\" TARGET=\"_blank\">", url, "</A>");
rest_line = rest_line[next..$];
new_line = $string_utils:substitute(line, {{url, link}});
else
more_urls = 0;
endif
line = new_line;
endwhile
result = {@result, line};
endfor
return result;
"Last modified Fri Mar 24 21:04:37 2000 CST by Wizard (#2).";
.
#125:29
"Adapted from BioGate";
su = $string_utils;
data = args[1];
if (!args)
return E_ARGS;
elseif (typeof(line = args[1]) != STR)
return E_INVARG;
endif
eol = "<EOL>";
eollen = length(eol);
fa = caller_perms().wizard ? su.full_ascii | su.ansi;
fa = fa || tostr(su:space(31), su.ascii, ((su:space(34) + su.latin1) + su:space(1)) || su:space(129));
line = strsub(line, "+", " ");
line = strsub(line, "%0D%0A", eol);
line = strsub(line, "%0A", eol);
line = strsub(line, "%0D", eol);
done = 1;
while (stuff = match(line[done..length(line)], "%%%([0-9A-F][0-9A-F]%)"))
code = substitute("%1", stuff);
hexa = "0123456789ABCDEF";
n = ((16 * (index(hexa, code[1]) - 1)) + index(hexa, code[2])) - 1;
line = strsub(line, "%" + code, fa[n]);
done = done + stuff[1];
endwhile
return line;
"Last modified Thu Apr  8 12:30:42 1999 CDT by Wizard (#2).";
.
#125:30
"Adapted from BioGate parse_form Copyright (C) BioGate partners 1997, Modifications by Jan Rune Holmevik, 1998-1999";
if (!caller_perms().wizard)
return E_PERM;
endif
form = args[1];
form_fields = {{}, {}};
field_name = field_data = "";
data_sets = $string_utils:explode(form, "&");
for data_set in (data_sets)
data_pair = $string_utils:explode(data_set, "=");
if (length(data_pair) == 2)
field_name = data_pair[1];
field_data = data_pair[2];
elseif (index(data_set, "=") == 1)
field_name = "";
field_data = data_set[2..$];
elseif (index(data_set, "=") == length(data_set))
field_name = data_set[1..$ - 1];
field_data = "";
else
field_name = "";
field_data = data_pair[1];
endif
field_name = $encore_web_utils:decode_url(field_name);
field_data = $encore_web_utils:decode_url(field_data);
form_fields[1] = {@form_fields[1], field_name};
form_fields[2] = {@form_fields[2], field_data};
endfor
"Insert line breaks in data fields if found";
for n in [1..length(form_fields[2])]
field = form_fields[2][n];
new_field = {};
found = 0;
while (eol = index(field, "<EOL>"))
"Line break found at position eol";
$command_utils:suspend_if_needed(0);
line = field[1..eol - 1];
field = field[eol + 5..$];
new_field = {@new_field, line};
found = 1;
endwhile
if (found)
"Check if there are more characters left in line";
last_line = field[1..$];
if (length(last_line) > 0)
new_field = {@new_field, last_line};
endif
form_fields[2][n] = new_field;
endif
endfor
return form_fields;
"Last modified Sun Nov 28 04:24:30 1999 CST by Wizard (#2).";
.
#125:31
"Copyright (C) 1999-2000, sindre.sorensen@rasmus.uib.no, Matthew Beerman and Jan Rune Holmevik";
"Added a call to $string_utils:trim in order to remove extra quote marks inserted by client browser";
if (!caller_perms().wizard)
return E_PERM;
endif
data = args[1];
boundary = args[2];
parts = {{}, {}};
part_count = 0;
i = 1;
while (i < length(data))
if ((("--" + boundary) + "--") == data[i])
return parts;
endif
if (("--" + boundary) == data[i])
i = i + 1;
part_count = part_count + 1;
parts[2] = {@parts[2], {}};
if (index = index(data[i], "Content-Disposition: form-data; name="))
"Need to trim off leading and trailing quote marks added by the client browser to prevent problems with verb property and property names in other modules. Jan 02/10/00";
name = $string_utils:trim(data[i][37 + index..$], "\"");
parts[1] = {@parts[1], name};
i = i + 1;
endif
endif
i = i + 1;
while (((("--" + boundary) != data[i]) && ((("--" + boundary) + "--") != data[i])) && (i < length(data)))
parts[2][$] = {@parts[2][$], data[i]};
i = i + 1;
endwhile
"Had to comment out the following if-statement to prevent this verb from returning an empty list for empty strings. Jan 02/13/00";
"if (parts[2][$] == {\"\"})";
"parts[2][$] = {}";
if (1 == length(parts[2][$]))
parts[2][$] = parts[2][$][1];
elseif (typeof(parts[2][$]) == LIST)
"Remove trailing empty lines in lists";
while (parts[2][$][$] == "")
parts[2][$] = parts[2][$][1..$ - 1];
endwhile
endif
"Take the last data_element out if it is only one line";
if (length(parts[1]) > length(parts[2]))
parts[2] = {@parts[2], ""};
endif
endwhile
return parts;
"Last modified Sun Feb 13 08:52:43 2000 CST by Wizard (#2).";
.
#125:32
"Copyright (C) 1999, sindre.sorensen@rasmus.uib.no";
if (!caller_perms().wizard)
return E_PERM;
endif
content_type = boundary = "";
{data, ?content_type, ?boundary} = args;
if ("" == data)
return {{}, {}};
endif
if (index(content_type, "multipart/form-data"))
if (!args[3])
return E_NARGS;
else
boundary = args[3];
return this:parse_multipart_form_data(data, boundary);
endif
else
"==== according to the HTML-4.0-specification, \"application/x-www-form-urlencoded\" is the default FORM content-type. ====";
return this:parse_url_encoded_form_data(data);
endif
"Last modified Thu Apr  8 12:31:30 1999 CDT by Wizard (#2).";
.
#125:33
"This verb is a suspended copy of $string_utils:substitute";
"Modified by Jan Rune Holmevik";
{ostr, subs, ?case = 0} = args;
if (typeof(ostr) != STR)
return ostr;
endif
len = length(ostr);
" - - - find the first instance of each substitution - -";
indices = {};
substs = {};
for s in (subs)
$command_utils:suspend_if_needed(0);
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)
suspend(0);
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;
"Last modified Sun May 16 17:39:02 1999 CDT by Wizard (#2).";
.
#125:34
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Make image tag for icon.";
"===========================================================";
{user, object, ?icon = "", ?alt = "", ?action = ""} = args;
result = locked = "";
if ((!icon) && $object_utils:has_property(object, "icon"))
icon = object.icon;
endif
try
size = user.icon_size;
base_url = $xpress_client.external_baseurl + $xpress_client.icons_folder;
"---------------------------------------";
"Check if object is locked";
"---------------------------------------";
if ($encore_web_utils:is_locked(user, object))
icon = base_url + object.locked_icon;
elseif (!match(icon, "http://"))
if (icon == "")
"----------------------------------------------------";
"User has deleted the icon, or no icon is present";
"Return parent's icon if it exists.";
"----------------------------------------------------";
icon = ($object_utils:has_property(parent(object), "icon") == 1) ? $string_utils:trim(parent(object).icon) | "";
endif
icon = base_url + icon;
endif
if (icon != "")
result = tostr("<IMG SRC=\"", icon, "\" HEIGHT=\"", size, "\" BORDER=\"", object.icon_border, "\" ALIGN=\"", object.icon_alignment, "\"", (alt == "") ? "" | tostr(" ALT=\"", alt, "\" TITLE=\"", alt, "\""), (action == "") ? "" | tostr(" onClick=\"", action, "\""), ">");
endif
except error (ANY)
endtry
return result;
"Last modified Mon Sep 17 10:04:01 2001 CDT by Wizard (#2).";
.
#125:35
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Under certain conditions users should be able to bypass the authentication requirements. This verb defines those conditions and evaluates if a request qualifies to bypass authentication. The verb returns 1 of the request qualifies and 0 if not. This system allows a user to perform GET and POST actions without having to be logged in.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{OBJ, vrb, method} = args;
OBJ = toobj(OBJ);
"=======================================================";
"==    User is requesting a web object and simple     ==";
"==         HTTP/0.9 requests are enabled             ==";
"=======================================================";
if (method = (("GET" && $object_utils:isa(OBJ, $encore_web_object)) && $xpress_client.HTTP_09_enabled) && (vrb == "_html"))
return 1;
elseif (($object_utils:isa(OBJ, $encore_cgi_application) && OBJ.allow_anonymous_access) && $object_utils:isa(OBJ.owner, $wiz))
"================================================================";
"== User is requesting a web application where anonymous       ==";
"== access is enabled. For security reasons the application    ==";
"== must be owned by a wizard and be defined on the System     ==";
"== Object.                                                    ==";
"================================================================";
return 1;
else
return 0;
endif
"Last modified Fri Apr 13 18:50:51 2001 CDT by Wizard (#2).";
.
#125:36
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Allows a user to share objects with others. This verb checks whether the user owns the object, is a wizard, or is in the list of the object's shared owners. The verb returns 1 if the user is permitted to edit the object, 0 if not.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
result = 0;
if ((user == object.owner) || user.wizard)
result = 1;
else
for authorized in (object.shared_owners)
if (user == toobj(authorized))
result = 1;
endif
endfor
endif
return result;
"Last modified Fri Apr 13 19:00:43 2001 CDT by Wizard (#2).";
.
#125:37
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Format a javascript alert message.";
"===========================================================";
alert = args[1];
if (alert != "")
alert = tostr("alert('", $string_utils:subst(alert, {{"'", ""}, {"\"", ""}}), "');");
endif
return alert;
"Last modified Fri Apr 13 19:04:26 2001 CDT by Wizard (#2).";
.
#125:38
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Verb that checks if a user can take or drop an object via Xpress. If true, 1 is returned, otherwise, 0 is returned. Currently, users can only handle $things that they own, or are shared owners of";
"===========================================================";
{user, object} = args;
if (!caller_perms().wizard)
return E_PERM;
endif
result = 0;
if ((((user == object.owner) || (user in object.shared_owners)) || user.wizard) && $object_utils:isa(object, $thing))
result = 1;
endif
return result;
"Last modified Fri Apr 13 19:04:45 2001 CDT by Wizard (#2).";
.
#125:39
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Returns 1 if the first line on the page matches <HTML> or <!DOCTYPE. Returns 0 if not";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
return (match(args[1][1], "<HTML>") || match(args[1][1], "<!DOCTYPE")) ? 1 | 0;
"Last modified Fri Apr 13 19:04:59 2001 CDT by Wizard (#2).";
.
#125:40
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Returns a page enclosed in BODY tags.";
"===========================================================";
{user, object, ?page = {}, ?onload = "", ?frame_identity = ""} = args;
if (frame_identity)
frame_identity = tostr(" ID=\"", frame_identity, "\" NAME=\"", frame_identity, "\"");
endif
body = $list_utils:append({tostr("<BODY", frame_identity, " ", onload, ">")}, page, {"</BODY>"});
return body;
"Last modified Fri Apr 13 19:06:33 2001 CDT by Wizard (#2).";
.
#125:41
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Enclose a page in HTML tags and insert DOCTYPE";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object, page, ?doctype = object.default_doctype} = args;
if (doctype == "")
doctype = object.default_doctype;
endif
html = $list_utils:append({doctype}, {"<HTML>"}, page, {"</HTML>"});
return html;
"Last modified Fri Apr 13 19:06:34 2001 CDT by Wizard (#2).";
.
#125:42
"===========================================================";
"Copyright (C) 1997-2001, Jan Rune Holmevik";
"Creates an HTML Header for a page. In addition to the object number of the object or application, this verb also accepts three optional arguments which specifies an alternative page title a base target, and content type.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object, ?title = object:title(), ?target = "", ?content_type = $httpd.content_type, ?style = 1} = args;
style_sheet = {};
if (target != "")
target = {tostr("   <BASE TARGET=\"", target, "\">")};
else
target = {};
endif
head = {"<HEAD>"};
meta = {tostr("   <META HTTP-EQUIV=\"content-type\" CONTENT=\"", content_type, "\">")};
meta = {@meta, tostr("   <META NAME=\"Generator\" CONTENT=\"", $httpd.server_name, $core_version, "\">")};
meta = {@meta, tostr("   <META NAME=\"Description\" CONTENT=\"This page was generated by the ", $network.MOO_name, " ", $httpd.server_name, $core_version, " web server on ", $time_utils:time_sub("$N $3. $Y at $H:$M:$S, $Z", time()), "\">")};
meta = {@meta, tostr("   <META NAME=\"Keywords\" CONTENT=\"", $core_name, ", enCore Xpress, ", $network.MOO_name, ", MOO, MUD\">")};
meta = {@meta, tostr("   <META NAME=\"Copyright\" CONTENT=\"High Wired enCore and enCore Xpress is Copyright (C) 1997-2001 of the enCore Open Source MOO Project. All Rights Reserved.\">")};
title = {tostr("   <TITLE>", title, " (", $network.MOO_name, ")</TITLE>")};
if (style)
style_sheet = this:make_style_sheet(user, object);
endif
head = $list_utils:append(head, meta, title, target, style_sheet, {"</HEAD>"});
return head;
"Last modified Fri Apr 13 19:06:51 2001 CDT by Wizard (#2).";
.
#125:43
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Creates a stylesheet for a page.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
style_sheet = {};
if (($object_utils:isa(object, $encore_web_object) && $encore_web_object.use_external_stylesheet) || ($object_utils:isa(object, $encore_web_application) && $encore_web_application.use_external_stylesheet))
"--------------------------------------------------";
"Use specified external style sheet";
"--------------------------------------------------";
ref = ($object_utils:isa(object, $encore_web_object) == 1) ? $encore_web_object.external_stylesheet | $encore_web_application.external_stylesheet;
if (!match(ref, "http://"))
ref = tostr($xpress_client.external_baseurl, $xpress_client.stylesheets_folder, ref);
endif
style_sheet = {tostr("   <LINK REL=StyleSheet HREF=\"", ref, "\" TYPE=\"text/css\" MEDIA=\"screen\">")};
else
"--------------------------------------------------";
"Make embedded style sheet based of object apperance values";
"--------------------------------------------------";
if (user.override_object_styles && object.allow_override_styles)
object = user;
endif
background = "";
if (match(object.web_background, "http://"))
background = object.web_background;
elseif (object.web_background != "")
background = tostr($xpress_client.external_baseurl, $xpress_client.icons_folder, object.web_background);
endif
style_sheet = {"   <STYLE TYPE=\"text/css\" MEDIA=\"screen\">", "      <!--"};
style_sheet = {@style_sheet, "      /* Body definitions */"};
style_sheet = {@style_sheet, tostr("      BODY, P, TD { color: ", object.web_text_color, "; font-family: ", object.web_font, "; font-size: ", object.web_font_size, "; }")};
style_sheet = {@style_sheet, tostr("      BODY { ", bkground = (background == "") ? "" | tostr("background: url(", background, "); "), bgcolor = (object.web_bgcolor == "") ? "" | tostr("background-color: ", object.web_bgcolor, "; "), " }")};
style_sheet = {@style_sheet, tostr("      DIV.Xpress-menu { background-image: url(\"", $xpress_client.external_baseurl, $xpress_client.icons_folder, "background-gray.jpg", "\") }")};
style_sheet = {@style_sheet, tostr("      DIV.Title { color: ", object.title_font_color, "; font-size: ", object.title_font_size, "; ", background = ($object_utils:isa(object, $encore_web_application) == 1) ? tostr(" background-image: url(\"", $xpress_client.external_baseurl, $xpress_client.themes_folder, user.xpress_theme, "/theme-background.jpg\")") | "", " }")};
style_sheet = {@style_sheet, tostr("      DIV.SubTitle { color: ", object.title_font_color, "; font-size: ", object.subtitle_font_size, "; ", background = ($object_utils:isa(object, $encore_web_application) == 1) ? tostr(" background-image: url(\"", $xpress_client.external_baseurl, $xpress_client.themes_folder, user.xpress_theme, "/theme-background.jpg\")") | "", " }")};
style_sheet = {@style_sheet, tostr("      PRE { font-family: ", object.fixed_width_font, "; font-size: ", object.fixed_width_font_size, " }")};
style_sheet = {@style_sheet, "      /* Form element definitions */"};
style_sheet = {@style_sheet, tostr("      TEXTAREA { font-family: ", object.fixed_width_font, "; font-size: ", object.fixed_width_font_size, " }")};
if (object == $xpress_login)
style_sheet = {@style_sheet, tostr("    INPUT.Login { font-family: ", object.fixed_width_font, "; font-size: ", object.fixed_width_font_size, " }")};
endif
style_sheet = {@style_sheet, "      /* Link definitions */"};
style_sheet = {@style_sheet, tostr("      A { font-family: ", object.web_font, "; font-size: ", object.web_font_size, ";", (object.underline_links == 0) ? " text-decoration: none" | "", " }")};
style_sheet = {@style_sheet, tostr("      A:link { color: ", object.web_link_color, " }")};
style_sheet = {@style_sheet, tostr("      A:visited { color: ", object.web_vlink_color, " }")};
style_sheet = {@style_sheet, tostr("      A:hover { color: ", object.web_alink_color, " }")};
style_sheet = $list_utils:append(style_sheet, {"      -->", "   </STYLE>"});
endif
return style_sheet;
"Last modified Fri Apr 13 19:06:51 2001 CDT by Wizard (#2).";
.
#125:44
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Align content.";
"===========================================================";
{content, alignment} = args;
return {tostr("<DIV ALIGN=\"", alignment, "\">"), @content, "</DIV>"};
"Last modified Sun Sep 30 12:48:18 2001 CDT by Wizard (#2).";
.
#126:0
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Verb that generates HTML source code on-the-fly for a MOO object that is sub class of $enCore_web_object.";
"===========================================================";
user = args[1];
if ($encore_web_utils:is_locked(user, this) && (user.location != this))
"Locked, direct user to $player_start if anonymous";
html = this:build(user, (loc = (user == $no_one) ? $player_start | user.location):_html(user), loc.name, $encore_web_utils:tell_locked(this));
else
this:update_hits();
audio = visual = drawing = title = {};
base_url = tostr("http://", $network.site, ":", $network.webport, "/");
title = $encore_web_utils:get_title(user, this);
title = {@title, "<P></P>"};
"---------------------------------------------------------";
"Add audio content";
"---------------------------------------------------------";
if (this.audio_url)
audio = $encore_web_utils:make_sound(user, this.audio_url);
audio = $list_utils:append({"<!-- Embedded audio content>"}, audio, {"<!-- end>"});
endif
"---------------------------------------------------------";
"Add visual multi-media content";
"---------------------------------------------------------";
if (this.url_address)
visual = $encore_web_utils:resolve_url(user, this);
visual = $encore_web_utils:align(visual, this.media_content_alignment);
visual = $list_utils:append({"<!-- Embedded visual content>", "<P>"}, visual, {"</P>", "<!-- end>"});
endif
if ((((this.drawing != {}) && (this.drawing != {""})) && (this.drawing != "")) && this.display_ascii)
drawing = $encore_web_utils:preformat(this.drawing);
endif
"---------------------------------------------------------";
"Add object textual content and assemble parts";
"---------------------------------------------------------";
description = $encore_web_utils:detect_urls(user, this.description);
description = $encore_web_utils:insert_line_breaks(description);
description = $encore_web_utils:align(description, this.text_alignment);
description = $list_utils:append({"<!-- Object description>", "<P>"}, description, {"<!-- end>", "</P>"});
html = $list_utils:append(title, audio, visual, drawing, description);
endif
return html;
"Last modified Sun Sep 30 13:14:37 2001 CDT by Wizard (#2).";
.
#126:1
"Usage:   view object";
"Example: view DoggiePicture";
"If DoggiePicture has an associated URL address and you are using the";
"'Surf&Turf' WebMOO Client you will be able to see the associated Web site.";
"To associate a URL address with an object:  'connect object to urladdress'";
if (this.url_address)
player:tell("You view ", this.name, "...");
player:tell(" <", this.url_address, ">.");
else
player:tell(this.name, " does not have an associated URL Address..");
endif
.
#126:2
"Copyright (C) 1997 Ken Schweller";
if (this.url_address)
player.location:announce_all(player.name, " displays:");
player.location:announce_all("  <", this.url_address, ">.");
else
player:tell(this.name, " does not have an associated URL Address..");
endif
"Last modified Fri Oct  1 06:46:54 1999 CDT by Wizard (#2).";
.
#126:3
"Usage:   connect object to <url address>";
"Example: connect DoggiePicture to http://www.wherever.edu/doggie.html";
"DoggiePicture is now associated with a URL address and can be viewed";
"using the 'Surf&Turf' WebMOO client. To see the Web picture:";
"   'view DoggiePicture'     or    'display DoggiePicture'";
if ((this.owner == player) || player.wizard)
this.url_address = iobjstr;
player:tell("URL address of ", this.name, " now set to '", iobjstr, "'...");
else
player:tell("Sorry, only the owner can reset the URL address...");
endif
.
#126:4
"Copyright (C) 1998, Jan Rune Holmevik";
"Verb that is used to set enCore web properties for an object.";
if ((player != this.owner) && (!player.wizard))
return player:tell("Sorry, only the owner of an object may change it's web properties.");
endif
finished = 0;
title = $encore_utils:make_title($network.MOO_name + " Web Properties Setup");
while (!finished)
ans = "";
player:tell_lines(title);
player:tell("Editing web preferences for ", this.name, ", object ", this);
player:tell("Please select one of the web preferences below to change it.");
player:tell("");
player:tell("Multimedia Icon      Web Display      Link Color       Table Layout");
player:tell("---------- ----      -----------      ----------       ----------");
player:tell("1) URL     5) URL     9) Backgr color 15) Link         18) Layout");
player:tell("2) Height  6) Height 10) Backgr URL   16) Visited link 19) Border");
player:tell("3) Width   7) Width  11) Text color   17) Active link  20) Width");
player:tell("4) Alignm  8) Alignm 12) Font face                     21) Alignm");
player:tell("                     13) Font size");
player:tell("                     14) Title size");
player:tell();
ans = toint($command_utils:read("Please enter your choice (1-21), or type Q to quit"));
if (ans in {1, 2, 3, 4})
"------------------------- Multimedia specifications  -------------";
player:tell("Multimedia Content Specifications");
player:tell("---------------------------------");
if (ans == 1)
"------------- Set URL address for the object ---------------";
if (this.url_address)
player:tell("The multimedia content of this object is currently located at: ", this.url_address);
else
player:tell("No multimedia content has been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance with graphical images, sound files, animations and other plugins, and even Java applets by specifying a URL address that points to the multimedia content you wish to include.");
endif
player:tell();
url = $command_utils:read("[Please enter a new URL you wish to connect to this object, or press RETURN or ENTER to return to the main menu]");
if (url)
this.url_address = url;
player:tell("This object is now connected to ", url);
endif
elseif (ans == 2)
"------------- Set Height of Multimedia Content ---------------";
if (this.web_height)
player:tell("The height of the area in which the multimedia content will be displayed is currently ", this.web_height, " pixels.");
else
player:tell("The height of the multimedia display area has not been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by customizing the height of the area in which additional multimedia content is displayed.");
endif
player:tell();
height = $command_utils:read("[Please enter a new numerical value for this property, or press RETURN or ENTER to return to the main menu]");
if (height)
this.web_height = height;
player:tell("Height of display area set to ", height, " pixels.");
endif
elseif (ans == 3)
"------------- Set Width of Multimedia Content ---------------";
if (this.web_width)
player:tell("The width of the area in which the multimedia content will be displayed is currently ", this.web_width, " pixels.");
else
player:tell("The width of the multimedia display area has not been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by customizing the width of the area in which additional multimedia content is displayed.");
endif
player:tell();
width = $command_utils:read("[Please enter a new numerical value for this property, or press RETURN or ENTER to return to the main menu]");
if (width)
this.web_width = width;
player:tell("Width of display area set to ", width, " pixels.");
endif
elseif (ans == 4)
"------------- Set Alignment of Multimedia Content ---------------";
if (this.web_align)
player:tell("The alignment of the area in which the multimedia content will be displayed is currently ", this.web_align, ".");
else
player:tell("The alignment of the multimedia display area has not been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by customizing the alignment of the area in which multimedia content is displayed.");
endif
player:tell();
alignment = $command_utils:read("[Please enter a new value for this property (TOP, MIDDLE, or BOTTOM), or press RETURN or ENTER to return to the main menu]");
if (alignment)
this.web_align = alignment;
player:tell("Alignment of display area set to ", alignment, ".");
endif
endif
elseif (ans in {5, 6, 7, 8})
"------------------------- Icon for MOO objects  -------------";
player:tell("Icon Specifications");
player:tell("-------------------");
if (ans == 5)
"------------- Set URL address for an object's icon ---------------";
if (this.icon)
player:tell("The icon for this object is currently located at: ", this.icon);
else
player:tell("No icon has been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance with a graphical icon that appears next to the object's name.");
endif
player:tell();
icon = $command_utils:read("[Please enter a URL address for the icon you wish to connect to this object, or press RETURN or ENTER to return to the main menu]");
if (icon)
this.icon = icon;
player:tell("This object is now connected to ", icon);
endif
elseif (ans == 6)
"------------- Set Height of Icon ---------------";
if (this.icon_height)
player:tell("The height of the area in which the icon will be displayed is currently ", this.icon_height, " pixels.");
else
player:tell("The height of the icon display area has not been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by customizing the height of the area in which icons are displayed.");
endif
player:tell();
height = $command_utils:read("[Please enter a new numerical value for this property, or press RETURN or ENTER to return to the main menu]");
if (height)
this.icon_height = height;
player:tell("Height of the icon display area set to ", height, " pixels.");
endif
elseif (ans == 7)
"------------- Set Width of Icon ---------------";
if (this.icon_width)
player:tell("The width of the area in which the icon will be displayed is currently ", this.icon_width, " pixels.");
else
player:tell("The width of the icon display area has not been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by customizing the width of the area in which icons are displayed.");
endif
player:tell();
width = $command_utils:read("[Please enter a new numerical value for this property, or press RETURN or ENTER to return to the main menu]");
if (width)
this.icon_width = width;
player:tell("Width of the icon display area set to ", width, " pixels.");
endif
elseif (ans == 8)
"------------- Set Icon Alignment ---------------";
if (this.icon_alignment)
player:tell("The alignment of the area in which the icon will be displayed is currently ", this.icon_alignment, ".");
else
player:tell("The alignment of the icon display area has not been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by customizing the alignment of the area in which icons are displayed.");
endif
player:tell();
alignment = $command_utils:read("[Please enter a new value for this property (TOP, MIDDLE, or BOTTOM), or press RETURN or ENTER to return to the main menu]");
if (alignment)
this.icon_alignment = alignment;
player:tell("Alignment of the icon display area set to ", alignment, ".");
endif
endif
elseif (ans in {9, 10, 11, 12, 13, 14})
"------------------------- Web Display Specifications  -------------";
player:tell("Web Display Specifications");
player:tell("--------------------------");
if (ans == 9)
"------- Set background color of the object page ---------";
if (this.web_bgcolor)
player:tell("The background color code for this object is currently : ", this.web_bgcolor);
else
player:tell("No background color has been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by customizing the color of the page it's displayed on.");
endif
player:tell();
bgcolor = $command_utils:read("[Please enter a background color code (i.e. #FFFFFF = white) for this object, or press RETURN or ENTER to return to the main menu]");
if (bgcolor)
this.web_bgcolor = bgcolor;
player:tell("The background color code for this object has been set to ", bgcolor);
endif
elseif (ans == 10)
"------- Set background image of the object page ---------";
if (this.web_background)
player:tell("The background image for this object is currently located at: ", this.web_background);
else
player:tell("No background image has been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by specifying a graphical image to be used as the background of the page it's displayed on.");
endif
player:tell();
background = $command_utils:read("[Please enter the URL for an image file to be used as web background for this object (Note that this will override any background color you may have set for this object), or press RETURN or ENTER to return to the main menu]");
if (background)
this.web_background = background;
player:tell("The background color code for this object has been set to ", background);
endif
elseif (ans == 11)
"------- Set text color of the object page ---------";
if (this.web_text_color)
player:tell("The text color code for this object is currently : ", this.web_text_color);
else
player:tell("No text color code has been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by customizing the color of text on the page it's displayed on.");
endif
player:tell();
text_color = $command_utils:read("[Please enter a text color code (i.e. #000000 = black) for this object, or press RETURN or ENTER to return to the main menu]");
if (text_color)
this.web_text_color = text_color;
player:tell("The text color code for this object has been set to ", text_color);
endif
elseif (ans == 12)
"------- Set the font of the object page ---------";
if (this.web_font)
player:tell("The font that is currently being used for this object is : ", this.web_font);
else
player:tell("No font has been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by customizing the font that is used on the page it's displayed on.");
endif
player:tell();
font = $command_utils:read("[Please type the name of the font you wish to use (i.e. Courier) for this object, or press RETURN or ENTER to return to the main menu]");
if (font)
this.web_font = font;
player:tell("The font for this object has been set to ", font);
endif
elseif (ans == 13)
"------- Set the size of the font on the object page ---------";
if (this.web_font_size)
player:tell("The size of the font that is currently being used for this object is : ", this.web_font_size);
else
player:tell("No font size has been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by customizing the size of the font that is used on the page it's displayed on.");
endif
player:tell();
size = $command_utils:read("[Please enter the font size you wish to use (1 = smallest, 3 = normal, 6 = largest) for this object, or press RETURN or ENTER to return to the main menu]");
if (size)
this.web_font_size = size;
player:tell("The font size for this object has been set to ", size);
endif
elseif (ans == 14)
"------- Set the size of the page title  ---------";
if (this.title_font_size)
player:tell("The font size of the title of this page is currently: ", this.title_font_size);
else
player:tell("No title font size has been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by customizing the size of the title of the page it's displayed on.");
endif
player:tell();
size = $command_utils:read("[Please enter the title font size you wish to use (1 = smallest, 3 = normal, 6 = largest) for this object, or press RETURN or ENTER to return to the main menu]");
if (size)
this.title_font_size = size;
player:tell("The title font size for this object has been set to ", size);
endif
endif
elseif (ans in {15, 16, 17})
"------------------------- Hyperlink Colors  -------------";
player:tell("Web Hyperlink Colors");
player:tell("--------------------");
if (ans == 15)
"------- Set link color of the object page ---------";
if (this.web_link_color)
player:tell("The hyperlink color code for this object is currently : ", this.web_link_color);
else
player:tell("No hyperlink color code has been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by customizing the color of hyperlinks on the page it's displayed on.");
endif
player:tell();
link_color = $command_utils:read("[Please enter a hyperlink color code (i.e. #0000FF = blue) for this object, or press RETURN or ENTER to return to the main menu]");
if (link_color)
this.web_link_color = link_color;
player:tell("The hyperlink color code for this object has been set to ", link_color);
endif
elseif (ans == 16)
"------- Set visited link color of the object page ---------";
if (this.web_vlink_color)
player:tell("The visited hyperlink color code for this object is currently : ", this.web_vlink_color);
else
player:tell("No visited hyperlink color code has been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by customizing the color of visited hyperlinks on the page it's displayed on.");
endif
player:tell();
vlink_color = $command_utils:read("[Please enter a visited hyperlink color code (i.e. #0000FF = blue) for this object, or press RETURN or ENTER to return to the main menu]");
if (vlink_color)
this.web_vlink_color = vlink_color;
player:tell("The visited hyperlink color code for this object has been set to ", vlink_color);
endif
elseif (ans == 17)
"------- Set active link color of the object page ---------";
if (this.web_alink_color)
player:tell("The active hyperlink color code for this object is currently : ", this.web_alink_color);
else
player:tell("No active hyperlink color code has been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by customizing the color of active hyperlinks on the page it's displayed on.");
endif
player:tell();
alink_color = $command_utils:read("[Please enter an active hyperlink color code (i.e. #0000FF = blue) for this object, or press RETURN or ENTER to return to the main menu]");
if (alink_color)
this.web_alink_color = alink_color;
player:tell("The active hyperlink color code for this object has been set to ", alink_color);
endif
endif
elseif (ans in {18, 19, 20, 21})
"------------------------- Web Layout Options  -------------";
player:tell("Web Layout Options");
player:tell("------------------");
if (ans == 18)
"------- Toggle table layout on or off ---------";
if (this.table_layout_if_possible)
if ($command_utils:yes_or_no("This object is currently being displayed in table format. Do you wish to turn table format off?"))
this.table_layout_if_possible = 0;
player:tell("Table format has been disabeled for this object.");
else
player:tell("Okay, setting not changed.");
endif
else
if ($command_utils:yes_or_no("This object is currently not being displayed in table format. Do you wish to turn table format on?"))
this.table_layout_if_possible = 1;
player:tell("Table format has been enabeled for this object.");
else
player:tell("Okay, setting not changed.");
endif
endif
elseif (ans == 19)
"------- Set the width of table borders  ---------";
if (this.table_layout_if_possible)
if (this.table_border)
player:tell("The width of the table borders on this object is currently: ", this.table_border, " pixels.");
else
player:tell("No table border width has been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by customizing the size of the table borders on the page it's displayed on.");
endif
player:tell();
border = $command_utils:read("[Please enter a new numerical value for this property, or press RETURN or ENTER to return to the main menu]");
if (border)
this.table_border = border;
player:tell("The width of table borders for this object has been set to ", border, " pixels.");
endif
else
player:tell("Tables are currenly disabled, please turn table format on before setting the border width.");
endif
elseif (ans == 20)
if (this.table_width)
player:tell("The width of the tables generated for this object is currently ", this.table_width, ".");
else
player:tell("The width of tables has not been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by customizing the width of the tables that are displayed.");
endif
player:tell();
width = $command_utils:read("[Please enter a new numerical value for this property, or press RETURN or ENTER to return to the main menu]");
if (width)
this.table_width = width;
player:tell("Width of tables set to ", width, ".");
endif
elseif (ans == 21)
"------------- Set Alignment of Tables ---------------";
if (this.table_vertical_alignment)
player:tell("The alignment of the tables generated for this object is currently ", this.table_vertical_alignment, ".");
else
player:tell("No table alignment has been specified for this object. With the enCore Web Interface, you can enhance an object's web appearance by customizing the alignment of tables  on the page it's displayed on.");
endif
player:tell();
alignment = $command_utils:read("[Please enter a new value for this property (TOP, MIDDLE, or BOTTOM), or press RETURN or ENTER to return to the main menu]");
if (alignment)
this.table_vertical_alignment = alignment;
player:tell("Alignment of tables set to ", alignment, ".");
endif
endif
else
finished = 1;
player:tell("Exiting...");
endif
endwhile
"Last modified Sun Sep 30 13:19:26 2001 CDT by Wizard (#2).";
.
#126:5
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Verb that updates the hits counter for an object each time it is requested through the Xpress interface";
"===========================================================";
this.hits = this.hits + 1;
return;
"Last modified Fri Apr 13 18:52:23 2001 CDT by Wizard (#2).";
.
#126:6
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Lock or unlock objects via Xpress.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
if (data != {{}, {}})
app = toobj(data[2][1]);
vrb = tostr(data[2][2]);
else
app = user.location;
vrb = "_html";
endif
alert = "";
if (verb == "lock_html")
this.key = user;
alert = tostr(this.name, " has been locked.");
else
this.key = 0;
alert = tostr(this.name, " has been unlocked.");
endif
user.location:announce_all_but({user}, tostr(user.name, (this.key == 0) ? " unlocks " | " locks ", this.name));
html = this:build(user, app:(vrb)(user), app.name, $encore_web_utils:make_alert(alert));
return html;
"Last modified Fri Apr 13 19:03:56 2001 CDT by Wizard (#2).";
.
#126:7
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Display obvious verbs via Xpress.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
body = vrbs = help = {};
title = $encore_web_utils:get_subtitle(user, this, tostr("Information about ", this.name));
"List obvious verbs on object";
if (vrbs = this:examine_verbs(user))
vrbs = $list_utils:append({"<P>Here are some of the things you can do with this object. Type the obvious verbs shown here into the Xpress talk area. The word \"anything\" should be substituted for a valid object name.</P>"}, $encore_web_utils:preformat(vrbs));
endif
if ($object_utils:has_property(this, "help_msg"))
"Add help on object";
help = $list_utils:append(body, $encore_web_utils:get_text_linebreak(this.help_msg));
endif
body = $list_utils:append(title, vrbs, help);
result = this:build(user, body);
return result;
"Last modified Fri Apr 13 19:04:26 2001 CDT by Wizard (#2).";
.
#127:0
":get_verb_defs(connection,thing) => verbs";
"Retrieve remote verb definitions for thing through connection.";
"Returns the list of +r verb definitions.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
con = args[1];
thing = args[2];
verbs = {};
vnums = {};
epv = 0;
line = "";
$command_utils:suspend_if_needed(0);
rverstr = this:remote_eval(con, "server_version()");
this:msg("Remote server is version : ", rverstr);
rver8 = index(rverstr, "1.8");
i = rver8 ? 1 | 0;
while (line != E_VERBNF)
if (rver8)
com = tostr("verb_info(", thing, ",", i, ");");
else
com = tostr("verb_info(", thing, ",tostr(", i, "));");
endif
line = this:remote_eval(con, com);
if (typeof(line) == LIST)
verbs = {@verbs, line};
vnums = {@vnums, rver8 ? i | tostr(i)};
endif
if (line == E_PERM)
epv = epv + 1;
endif
i = i + 1;
endwhile
epv ? this:msg("Found ", epv, " unreadable verbs.") | 0;
this:msg("Found ", length(verbs), " verb definitions.");
return {verbs, vnums};
"Last modified Fri Feb  6 08:15:38 1998 CST by Wizard (#2).";
.
#127:1
":get_verb_defs(connection,thing,verbdefs,verbnums) => vcodes";
"Retrieve remote verb code for verbdefs/nums on thing through connection.";
"Returns a list of lists containing the verbs' code.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
con = args[1];
thing = args[2];
verbdefs = args[3];
verbnums = args[4];
if (length(verbdefs) == 0)
return {};
endif
vcodes = {};
for vn in (verbnums)
if (typeof(vn) == STR)
com = tostr("verb_code(", thing, ",tostr(", vn, "));");
else
com = tostr("verb_code(", thing, ",", vn, ");");
endif
code = this:remote_eval(con, com);
vcodes = {@vcodes, code};
endfor
this:msg("Retrieved code for ", length(vcodes), " verbs.");
return vcodes;
"Last modified Fri Feb  6 08:15:38 1998 CST by Wizard (#2).";
.
#127:2
":install_verbs(connection,thing,verbdefs,verbargs,vcodes)";
"Install retrieved verbs locally.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
set_task_perms(player);
con = args[1];
thing = args[2];
verbdefs = args[3];
verbargs = args[4];
vcodes = args[5];
this:msg("Installing verbs locally...");
wizvrbs = {};
for vnum in [1..length(verbdefs)]
$command_utils:suspend_if_needed(0);
cmd = tostr(verbdefs[vnum][1], ".wizard");
wiz = this:remote_eval(con, cmd);
vname = verbdefs[vnum][3];
vargs = verbargs[vnum];
vsname = this:strip_verb_name(vname);
if (wiz)
wizvrbs = {@wizvrbs, vsname};
endif
verbdefs[vnum][1] = player;
"Change ownership of verb to local player";
actualvnum = this:_has_verb(thing, vname, vargs);
if (!actualvnum)
this.debug && this:msg("INSTALL VERBS : ", thing, ",", vname);
res1 = this:_add_verb(thing, verbdefs[vnum], vargs);
actualvnum = length(verbs(thing));
"Try to figure out where the verb is actually at now without using it's name...";
else
res1 = -1;
endif
if (typeof(res1) == ERR)
if (res1 == E_PERM)
this:msg("*** Verb ", vname, " not added because of ", res1, ".");
elseif (res1 == E_INVARG)
this:msg("*** Verb ", vname, " could not be added to ", thing, ".");
elseif (res1 == E_QUOTA)
this:msg("*** Verb ", vname, " could not be added due to lack of quota.");
else
this.debug && this:msg("INSTALL VERBS : Res1 : ", res1);
this:msg("*** Verb ", vname, " could not be added due to a bad verb def.");
endif
else
if (res1 == -1)
this:msg("*** Verb ", vname, " already existed, overwriting code.");
endif
vc = vcodes[vnum];
if (!vc)
this:msg("*** Verb ", vname, " was not programmed remotely");
elseif (typeof(vc) == ERR)
this:msg("*** Verb ", vname, "'s code was unreadable remotely.");
else
res2 = set_verb_code(thing, actualvnum, vcodes[vnum]);
if (typeof(res2) == ERR)
this:msg("*** Verb ", vname, " has errors and did not compile.");
endif
endif
endif
endfor
if (wizvrbs)
this:msg("Verbs that were wizardly on the remote MOO : ", $string_utils:english_list(wizvrbs));
endif
this:msg("Finished installing retrieved verbs.");
"Last modified Fri Feb  6 08:15:38 1998 CST by Wizard (#2).";
.
#127:3
":open(host,port,cstr) => result";
"Given a host, port, and connection string try to open a connection.";
"Returns 0 for no connection, 1 for  successful login, 2 for failed login,";
"3 for failed network connection, and  4 for timed out trying to login.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
host = args[1];
port = args[2];
cstr = args[3];
connected = 0;
con = #-1;
"0 no connection, 1 successful login, 2 failed login 3 failed network connection, 4 timed out trying to login -SR";
this:msg("Trying ", host, " port ", port, " ...");
con = $network:open(host, port);
if (typeof(con) == OBJ)
set_connection_option(con, "hold-input", 1);
"1.8.0+";
notify(con, cstr);
res = read(con);
start = time();
ft = start + this.login_timeout;
"This is annoying, there's no easy way to tell if you're connected, people change the connect message, or have *** stuff in welcome_msg... ergh...";
while ((!(index(res, "*** ") || index(res, "does not exist"))) && (time() <= ft))
res = read(con);
endwhile
if (index(res, "***") && (!index(res, "Timed-out")))
connected = 1;
elseif (index(res, "does not exist"))
connected = 2;
$network:close(con);
elseif ((time() > ft) || index(res, "Timed-out"))
connected = 4;
$network:close(con);
else
"Fail safe, just in case... shouldn't ever get to this. -SR";
$network:close(con);
this:msg("*** Something screwy happened.  Aborting.");
kill_task(task_id());
endif
elseif (typeof(con) == ERR)
connected = 3;
endif
return {con, connected};
"Last modified Fri Feb  6 08:15:39 1998 CST by Wizard (#2).";
.
#127:4
":get_verb_args(connection,thing,verbnums) => vargs";
"Retrieve remote verb code for verbdefs on thing through connection.";
"Returns a list of +r verb arguments.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
con = args[1];
thing = args[2];
verbnums = args[3];
vargs = {};
i = 1;
epv = 0;
line = "";
while ((i <= length(verbnums)) && (line != E_VERBNF))
vnum = verbnums[i];
if (typeof(vnum) == STR)
com = tostr("verb_args(", thing, ",tostr(", vnum, "));");
else
com = tostr("verb_args(", thing, ",", vnum, ");");
endif
line = this:remote_eval(con, com);
if ((typeof(line) == LIST) && (length(line) > 1))
vargs = {@vargs, line};
else
vargs = {@vargs, {"none", "none", "none"}};
"Something failed, fall back to something...";
epv = epv + 1;
endif
i = i + 1;
epv && this:msg("ERR : There were ", epv, " unreadable verbs.");
endwhile
this:msg("Retrieved ", length(vargs), " verb arguments.");
return vargs;
"Last modified Fri Feb  6 08:16:27 1998 CST by Wizard (#2).";
.
#127:5
":get_prop_names(connection,thing) => dpnames";
"Retrieve names of properties defined on thing.";
"Returns a list of property names if the object is +r.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
con = args[1];
thing = args[2];
com = tostr("properties(", thing, ")");
line = this:remote_eval(con, com);
if (typeof(line) == ERR)
line = {};
endif
this:msg("Found ", length(line), " defined properties.");
return line;
"Last modified Fri Feb  6 08:16:28 1998 CST by Wizard (#2).";
.
#127:6
":remote_eval(connection,string) => result";
"Returns the result of eval-d'ing the string remotely.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
con = args[1];
if (!this:is_open(con))
this:msg(this.lostcon_msg);
kill_task(task_id());
endif
cstr = args[2];
cstr = tostr("eval-d ", cstr);
this.debug && this:msg("DEBUG : Remote Eval : ", cstr);
notify(con, cstr);
line = read(con);
cnt = 0;
tb = 0;
while (!index(line, "=> "))
if (line[1] == "#")
"We've had a traceback...";
this.debug && this:msg("DEBUG : Remote Eval : Traceback recvd");
tb = 1;
break;
endif
if (cnt > 2)
notify(con, cstr);
endif
cnt = cnt + 1;
line = read(con);
this.debug && this:msg("DEBUG : Eval READ : ", line);
if (typeof(line) == ERR)
this:msg(this.lostcon_msg);
kill_task(task_id());
endif
endwhile
if (tb)
while (!index(line, "(End of traceback)"))
line = read(con);
if (typeof(line) == ERR)
this:msg(this.lostcon_msg);
kill_task(task_id());
endif
endwhile
return {};
"Returning {} should work in most cases.";
endif
line = line[4..length(line)];
if ((line[1] == "E") || (line[1] == "#"))
line = $string_utils:words(line)[1];
endif
return this:eval_cmd_string(line)[2];
"Last modified Fri Feb  6 08:16:29 1998 CST by Wizard (#2).";
.
#127:7
":strip_verb_name(string) => sstring";
"Returns a verb name minus * and all it's aliases.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
verbn = args[1];
verbn = $string_utils:words(verbn)[1];
verbn = $string_utils:strip_chars(verbn, "*");
return verbn;
"Last modified Fri Feb  6 08:16:29 1998 CST by Wizard (#2).";
.
#127:8
":get_prop_info(connection,thing,pnames) => dpinfo";
"Retrieve remote property info for all properties in pnames on thing through connection.";
"Return a list of property definitions.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
con = args[1];
thing = args[2];
pnames = args[3];
dpns = {};
for p in (pnames)
com = tostr("property_info(", thing, ",\"", p, "\")");
line = this:remote_eval(con, com);
dpns = {@dpns, line};
endfor
this:msg("Retrieved info for ", length(dpns), " defined properties.");
return dpns;
"Last modified Fri Feb  6 08:16:29 1998 CST by Wizard (#2).";
.
#127:9
":install_dprops(connection,thing,pnames,pinfo,pdata)";
"Install retrieved defined properties on local object thing.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
set_task_perms(player);
con = args[1];
thing = args[2];
pnames = args[3];
pinfo = args[4];
pdata = args[5];
notinst = {};
eperms = {};
this:msg("Installing defined properties locally...");
for pnum in [1..length(pnames)]
$command_utils:suspend_if_needed(0);
pn = pnames[pnum];
if (pdata[pnum] == E_PERM)
eperms = setadd(eperms, tostr(pn));
else
pinfo[pnum][1] = player;
"Change ownership of property to local player";
res = this:_add_property(thing, pn, pdata[pnum], pinfo[pnum]);
if (typeof(res) == ERR)
if (res == E_QUOTA)
this:msg("*** Defined property ", pn, " not installed to to lack of quota.");
endif
notinst = {pn, @notinst};
endif
endif
endfor
if (eperms)
this:msg("*** Defined properties that were unreadable : ", $string_utils:english_list(eperms));
endif
if (notinst)
this:msg("*** Defined properties not installed : ", $string_utils:english_list(notinst));
endif
this:msg("Finished installing retrieved defined properties.");
"Last modified Fri Feb  6 08:17:18 1998 CST by Wizard (#2).";
.
#127:10
":get_prop_data(con,thing,pnames) => data";
"Get the data for a list of properties on thing through con.";
"Returns a list of the data in the properties.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
con = args[1];
thing = args[2];
pnames = args[3];
dpns = {};
for p in (pnames)
com = tostr(thing, ".(\"", p, "\")");
line = this:remote_eval(con, com);
dpns = {@dpns, line};
endfor
this:msg("Retrieved data for ", length(dpns), " properties.");
return dpns;
"Last modified Fri Feb  6 08:17:18 1998 CST by Wizard (#2).";
.
#127:11
":get_iprop_names(connection,thing) => ipnames";
"Attempts to retrieve a list of inherited properties on the remote object.";
"Returns a list of inherited property names.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
con = args[1];
thing = args[2];
com = tostr("$object_utils:all_properties(parent(", thing, "))");
line = this:remote_eval(con, com);
if (typeof(line) != LIST)
line = this.builtin_props;
else
line = {@this.builtin_props, @line};
endif
this:msg("Found ", length(line), " inherited properties.");
return line;
"Last modified Fri Feb  6 08:17:18 1998 CST by Wizard (#2).";
.
#127:12
":install_iprops(connection,thing,pnames,pdata)";
"Install retrieved inherited properties on local object thing.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
set_task_perms(player);
con = args[1];
thing = args[2];
pnames = args[3];
pdata = args[4];
this:msg("Installing inherited properties...");
notinst = {};
for pnum in [1..length(pnames)]
$command_utils:suspend_if_needed(0);
pn = pnames[pnum];
if ($object_utils:has_property(thing, tostr(pn)))
thing.(tostr(pn)) = pdata[pnum];
else
notinst = {tostr(pn), @notinst};
endif
endfor
if (notinst)
this:msg("*** Inherited properties not installed : ", $string_utils:english_list(notinst));
endif
this:msg("Finished installing retrieved inherited properties.");
"Last modified Fri Feb  6 08:17:18 1998 CST by Wizard (#2).";
.
#127:13
":init_remote(connection)";
"Sends some initialization commands to the connection.  Attempts to disable";
"things like ANSI, pagelength, word wrap, etc.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
"Gets commands from .init_commands and sends each string to the connection.";
"Turn off buffering, flush the buffer, turn off line wrapping, and ANSI. -SR";
if (caller != this)
return E_PERM;
endif
con = args[1];
this:msg("Initializing remote player.");
for c in (this.init_commands)
this:remote_command(con, c);
endfor
this:msg("Remote player initialized.");
"Last modified Fri Feb  6 08:18:03 1998 CST by Wizard (#2).";
.
#127:14
"@netcopy  : Copies an entire object from a remote MOO. (verbs and props)";
"";
"Takes one optional argument.  You can specify an object number";
"for the porter to install onto locally.  This argument has to";
"be a object number, this is to help prevent accidental";
"overwrites.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (!((caller_perms() == player) && (player == caller)))
return E_PERM;
endif
"Thanks to {Phantom, Reorx}@BayMOO for input on this check.";
set_task_perms(player);
if (!this:trust(player))
player:tell(this:nodice_msg());
return;
endif
su = $string_utils;
this:check_args(@args);
this:check_network();
{host, port, cstr, robj} = this:get_info();
"1.8.0+";
{lobj, clobj, parent} = this:handle_local_object(@args ? {args[1]} | {});
"1.8.0+";
{con, conres} = this:open(host, port, cstr);
cstr = "Overwrite this just in case";
this:handle_login_result(conres);
this:sentinel(con);
this:init_remote(con);
this:check_remote_object(con, robj);
if (clobj)
lobj = this:create_local_object(con, parent);
else
this:msg("Existing local object ", su:nn(lobj), " with parent ", su:nn(parent(lobj)), " used.");
endif
$command_utils:suspend_if_needed(0);
{verbdefs, verbnums} = this:get_verb_defs(con, robj);
if (verbdefs)
verbargs = this:get_verb_args(con, robj, verbnums);
vcodes = this:get_verb_codes(con, robj, verbdefs, verbnums);
this:install_verbs(con, lobj, verbdefs, verbargs, vcodes);
else
this:msg("Unable to retrieve any verbs.");
endif
$command_utils:suspend_if_needed(0);
dpropnames = this:get_prop_names(con, robj);
if (dpropnames)
dpropinfo = this:get_prop_info(con, robj, dpropnames);
dpropdata = this:get_prop_data(con, robj, dpropnames);
this:install_dprops(con, lobj, dpropnames, dpropinfo, dpropdata);
else
this:msg("Unable to retrieve any defined properties.");
endif
$command_utils:suspend_if_needed(0);
ipropnames = this:get_iprop_names(con, robj);
if (ipropnames)
ipropdata = this:get_prop_data(con, robj, ipropnames);
this:install_iprops(con, lobj, ipropnames, ipropdata);
else
this:msg("Unable to retrieve any inherited properties.");
endif
$command_utils:suspend_if_needed(0);
this:close(con);
this:msg("Connection closed.");
this:msg("Object succesfully installed as ", $string_utils:nn(lobj));
"Last modified Fri Feb  6 08:18:04 1998 CST by Wizard (#2).";
.
#127:15
"@propcopy : The same as @verbcopy except for properties.  It's behavior";
"            is slightly differen that @netcopy's.  It takes the list";
"            of props and attempts to install them locally as inherited";
"            props (just sets the data) if they exist locally, otherwise";
"            if creates them and sets them.  If you specify '*' it copies";
"            all the properties the same way as @netcopy does.";
"";
"Takes one optional argument.  You can specify an object number";
"for the porter to install onto locally.  This argument has to";
"be a object number, this is to help prevent accidental";
"overwrites.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (!((caller_perms() == player) && (player == caller)))
return E_PERM;
endif
"Thanks to {Phantom, Reorx}@BayMOO for input on this check.";
set_task_perms(player);
if (!this:trust(player))
player:tell(this:nodice_msg());
return;
endif
su = $string_utils;
this:check_args();
this:check_network();
{host, port, cstr, robj} = this:get_info();
"1.8.0+";
{lobj, clobj, parent} = this:handle_local_object(@args ? {args[1]} | {});
{con, conres} = this:open(host, port, cstr);
cstr = "Overwrite this just in case";
this:handle_login_result(conres);
this:init_remote(con);
this:check_remote_object(con, robj);
if (clobj)
lobj = this:create_local_object(con, parent);
else
this:msg("Existing local object ", su:nn(lobj), " with parent ", su:nn(parent(lobj)), " used.");
endif
$command_utils:suspend_if_needed(0);
player:tell("List of properties (use * to indicate all).");
propnames = $string_utils:words(this:read());
this:remote_eval(con, "1");
"Make the connection unidle real quick before we fire up the sentinel.";
suspend(1);
this:sentinel(con);
"Dont fire up the sentinel to now, in case they are slow inputting their list.";
if (propnames == "*")
dpropnames = this:get_prop_names(con, robj);
if (dpropnames)
dpropinfo = this:get_prop_info(con, robj, dpropnames);
dpropdata = this:get_prop_data(con, robj, dpropnames);
this:install_dprops(con, lobj, dpropnames, dpropinfo, dpropdata);
else
this:msg("Unable to retrieve any defined properties.");
endif
$command_utils:suspend_if_needed(0);
ipropnames = this:get_iprop_names(con, robj);
if (ipropnames)
ipropdata = this:get_prop_data(con, robj, ipropnames);
$command_utils:suspend_if_needed(0);
this:install_iprops(con, lobj, ipropnames, ipropdata);
else
this:msg("Unable to retrieve any inherited properties.");
endif
$command_utils:suspend_if_needed(0);
else
{dpropnames, ipropnames} = this:get_spec_prop_names(con, robj, lobj, propnames);
dpropinfo = this:get_prop_info(con, robj, dpropnames);
if (dpropinfo)
dpropdata = this:get_prop_data(con, robj, dpropnames);
this:install_dprops(con, lobj, dpropnames, dpropinfo, dpropdata);
else
this:msg("Unable to retrieve any defined properties.");
endif
$command_utils:suspend_if_needed(0);
ipropdata = this:get_prop_data(con, robj, ipropnames);
if (ipropdata)
this:install_iprops(con, lobj, ipropnames, ipropdata);
else
this:msg("Unable to retrieve any inherited properties.");
endif
$command_utils:suspend_if_needed(0);
endif
this:close(con);
this:msg("Connection closed.");
this:msg("Object succesfully installed as ", $string_utils:nn(lobj));
"Last modified Fri Feb  6 08:18:05 1998 CST by Wizard (#2).";
.
#127:16
":read() => string";
"Hacked $command_utils:read that automatically reads from player";
"and will function after calls have been made to suspend.  Used";
"to get information from the player.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
prompt = "a line of input";
player:notify(tostr("[Type ", prompt, " or `@abort' to abort the command.]"));
ans = read(player);
"So it can be run after calls to suspend(). -SR";
if (typeof(ans) == ERR)
return ans;
elseif ($string_utils:trim(ans) == "@abort")
p:notify(">> Command Aborted <<");
kill_task(task_id());
else
return ans;
endif
"Last modified Fri Feb  6 08:18:47 1998 CST by Wizard (#2).";
.
#127:17
":get_verb_defs_args(connection,thing,vnames) =>  {vdefs,vargs,vnums}";
"Retrieve lists of verb definitions and argument lists for all +r verbs.";
"Returns a list of lists.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
con = args[1];
thing = args[2];
verbnames = args[3];
verbdefs = {};
verbargs = {};
verbnums = {};
vns = {};
vds = {};
epv = 0;
for vn in (verbnames)
vn = this:strip_verb_name(vn);
vdcom = tostr("verb_info(", thing, ",\"", vn, "\");");
if ((res = this:remote_eval(con, vdcom)) != E_VERBNF)
vns = {@vns, vn};
vds = {@vds, res};
endif
endfor
line = "";
$command_utils:suspend_if_needed(0);
rverstr = this:remote_eval(con, "server_version()");
this:msg("Remote server is version : ", rverstr);
rver8 = index(rverstr, "1.8");
i = rver8 ? 1 | 0;
while (line != E_VERBNF)
if (rver8)
com = tostr("verb_info(", thing, ",", i, ");");
else
com = tostr("verb_info(", thing, ",tostr(", i, "));");
endif
line = this:remote_eval(con, com);
if ((typeof(line) == LIST) && (line in vds))
verbdefs = {@verbdefs, line};
verbnums = {@verbnums, rver8 ? i | tostr(i)};
elseif (line == E_PERM)
epv = epv + 1;
endif
i = i + 1;
endwhile
epv ? this:msg("Found ", epv, " unreadable verbs.") | 0;
this:msg("Found ", length(verbdefs), " verb definitions.");
for vn in (verbnums)
if (rver8)
vacom = tostr("verb_args(", thing, ",", vn, ");");
else
vacom = tostr("verb_args(", thing, ",tostr(", vn, "));");
endif
vares = this:remote_eval(con, vacom);
if (typeof(vares) == LIST)
verbargs = {@verbargs, vares};
endif
endfor
this:msg("Found ", length(verbdefs), " verb definitions and arguments.");
return {verbdefs, verbargs, verbnums};
"Last modified Fri Feb  6 08:18:47 1998 CST by Wizard (#2).";
.
#127:18
"@verbcopy : Copies verbs from a remote MOO off of one remote object. You";
"            specify which verbs or use '*' to indicate all.";
"";
"Takes one optional argument.  You can specify an object number";
"for the porter to install onto locally.  This argument has to";
"be a object number, this is to help prevent accidental";
"overwrites.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (!((caller_perms() == player) && (player == caller)))
return E_PERM;
endif
"Thanks to {Phantom, Reorx}@BayMOO for input on this check.";
set_task_perms(player);
if (!this:trust(player))
player:tell(this:nodice_msg());
return;
endif
su = $string_utils;
this:check_args();
this:check_network();
{host, port, cstr, robj} = this:get_info();
"1.8.0+";
{lobj, clobj, parent} = this:handle_local_object(@args ? {args[1]} | {});
"1.8.0+";
{con, conres} = this:open(host, port, cstr);
cstr = "Overwrite this just in case";
this:handle_login_result(conres);
this:init_remote(con);
this:check_remote_object(con, robj);
if (clobj)
lobj = this:create_local_object(con, parent);
else
this:msg("Existing local object ", su:nn(lobj), " with parent ", su:nn(parent(lobj)), " used.");
endif
$command_utils:suspend_if_needed(0);
player:tell("List of verbs (do not use * except by itself to indicate all verbs).");
verbnames = $string_utils:words(this:read());
this:remote_eval(con, "1");
"Make the connection unidle real quick before we fire up the sentinel.";
suspend(1);
this:sentinel(con);
"Dont fire up the sentinel till now, in case they are slow inputting their list.";
if ((length(verbnames) == 1) && (verbnames[1] == "*"))
this:msg("Attempting to retrieve all verbs.");
{verbdefs, verbnums} = this:get_verb_defs(con, robj);
verbargs = this:get_verb_args(con, robj, verbnums);
else
this:msg("Attempting to retrieve ", length(verbnames), " verbs.");
{verbdefs, verbargs, verbnums} = this:get_verb_defs_args(con, robj, verbnames);
endif
if (verbdefs)
vcodes = this:get_verb_codes(con, robj, verbdefs, verbnums);
this:install_verbs(con, lobj, verbdefs, verbargs, vcodes);
else
this:msg("Unable to retrieve any verbs.");
endif
this:close(con);
this:msg("Connection closed.");
this:msg("Verbs succesfully installed on ", $string_utils:nn(lobj));
"Last modified Fri Feb  6 08:18:48 1998 CST by Wizard (#2).";
.
#127:19
":get_spec_prop_names(connection,thing,lobj,pnames) => {dpnames, ipnames}";
"Determines how to handle a list of requested property names from the player";
"based on how they exist remotely.  Sorts the list into what should be treated";
"as defined properties and what should be treated as inherited.";
"Returns a list of lists.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
con = args[1];
thing = args[2];
lobj = args[3];
pns = args[4];
pnames = {};
epp = 0;
for pn in (pns)
com = tostr(thing, ".(\"", pn, "\");");
res = this:remote_eval(con, com);
if ((res == E_PERM) || (res == E_PROPNF))
epp = epp + 1;
else
pnames = {@pnames, pn};
endif
endfor
dpnames = {};
ipnames = $set_utils:intersection(pnames, this.builtin_props);
pnames = $set_utils:diff(pnames, ipnames);
for pn in (pnames)
if ($object_utils:has_property(lobj, pn))
ipnames = {@ipnames, pn};
else
dpnames = {@dpnames, pn};
endif
endfor
if (epp)
this:msg("Unable to find/read ", epp, " properties.");
endif
if (dpnames)
this:msg("Found ", length(dpnames), " defined properties.");
endif
if (ipnames)
this:msg("Found ", length(ipnames), " inherited properties.");
endif
return {dpnames, ipnames};
"Last modified Fri Feb  6 08:19:31 1998 CST by Wizard (#2).";
.
#127:20
":check_network()";
"See if we 'should' open a connection based on $network.active.";
"If we shouldn't abort.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (!$network.active)
player:tell("The network is not active.");
kill_task(task_id());
else
player:tell("The network is active.");
endif
"Last modified Fri Feb  6 08:19:32 1998 CST by Wizard (#2).";
.
#127:21
":trust(who) => result";
"Do we trust this person to use the porter?";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
"Paranoia here... don't want others to see who can use this.";
return this:is_wizard(who = args[1]) || (who in this.trusts);
"Last modified Fri Feb  6 08:19:32 1998 CST by Wizard (#2).";
.
#127:22
":is_wizard(who) => result";
"A hopefully more portable is_wizard.";
"";
":is_wizard(who) => whether `who' is a wizard or is the .public_identity of some wizard.";
"This verb is used for permission checks on commands that should only be accessible to wizards or their ordinary-player counterparts.  It will return true for unadvertised wizards.";
who = args[1];
if (who.wizard)
return 1;
else
for w in ($object_utils:leaves($wiz))
if ((w.wizard && is_player(w)) && (who == w.public_identity))
return 1;
endif
endfor
endif
return 0;
"Last modified Fri Feb  6 08:19:32 1998 CST by Wizard (#2).";
.
#127:23
":is_open(con) => result";
"Is the connection still open?";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
return typeof(idle_seconds(@args)) != ERR;
"Last modified Fri Feb  6 08:19:32 1998 CST by Wizard (#2).";
.
#127:24
":remote_command(connection,cmd) => result";
"Executes a command remotely.";
"Returns any text gathered from the verbs execution.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
con = args[1];
cmd = args[2];
if (!this:is_open(con))
player:tell(this.lostcon_msg);
kill_task(task_id());
endif
this.debug && this:msg("DEBUG : Remote Cmd : ", cmd ? cmd | "<Empty String>");
if (!cmd)
while (read(con, 1) != 0)
endwhile
"We don't have a PREFIX to look for with a empty string, so we just run out whatever is there.";
notify(con, "");
return {};
endif
notify(con, tostr("PREFIX ", this.remote_prefix));
notify(con, tostr("SUFFIX ", this.remote_suffix));
notify(con, tostr(cmd));
line = read(con);
while (!index(line, this.remote_prefix))
line = read(con);
endwhile
recvd = {};
line = read(con);
while (!index(line, this.remote_suffix))
recvd = {@recvd, line};
line = read(con);
endwhile
notify(con, "PREFIX");
notify(con, "SUFFIX");
return recvd;
"Last modified Fri Feb  6 08:19:33 1998 CST by Wizard (#2).";
.
#127:25
"Do this cause need to close with wiz perms -SR";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
$network:close(args[1]);
"Last modified Fri Feb  6 08:20:37 1998 CST by Wizard (#2).";
.
#127:26
":handle_login_result(code)";
"Handles the result from an attempted login.  Stops the task if it was a";
"failure message.";
"";
"Print out messages to players, etc. -SR";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
res = args[1];
if (res == 1)
this:msg("Connection sucessfully established and logged in.");
return;
elseif (res == 2)
this:msg("Login failed.");
elseif (res == 3)
this:msg("Unabled to connect to remote MOO.");
elseif (res == 4)
this:msg("Timed out while trying to log in.");
endif
kill_task(task_id());
"Last modified Fri Feb  6 08:20:38 1998 CST by Wizard (#2).";
.
#127:27
":handle_local_object(slobj) => {lobj,clobj,parent};";
"Takes the argument of a suggested local object to use.";
"Returns actual local obj, did we create it, and local object parent.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
lobj = #-1;
if (args)
if (tostr(toobj(args[1])) == args[1])
lobj = toobj(args[1]);
else
this:msg("You have to specify the local object by it's object number, to help prevent mistakes.");
lobj = #-1;
endif
endif
clobj = 1;
parent = #-1;
"clobj, if true create a local object, otherwise use a specified one.";
if (args && (!valid(lobj)))
this:msg("The local object is not valid. Create one?");
if (!$command_utils:yes_or_no())
this:msg("Aborted.");
kill_task(task_id());
else
clobj = 1;
endif
elseif (args && valid(lobj))
clobj = 0;
endif
if (clobj)
player:tell("Local Object Parent? (defaults to #1)");
parent = toobj(player:eval_cmd_string(this:read())[2]);
parent = (parent == #0) ? #1 | parent;
if ((!valid(parent)) || ((!parent.f) && (!caller_perms().wizard)))
player:tell("You can't create a child of that object number.");
this:msg("Aborted.");
kill_task(task_id());
endif
endif
return {lobj, clobj, parent};
"Last modified Fri Feb  6 08:20:38 1998 CST by Wizard (#2).";
.
#127:28
":check_remote_object(connection,robj)";
"Check if the remote object is valid, if not stop execution.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
con = args[1];
robj = args[2];
this:msg("Checking remote object validity.");
if (!this:remote_eval(con, tostr("valid(", robj, ")")))
this:msg("The remote object is not valid.  Closing connection.");
$network:close(con);
this:msg("Connection closed.");
kill_task(task_id());
endif
this:msg("Remote object is ", this:remote_eval(con, tostr("$string_utils:nn(", robj, ")")), " with parent ", this:remote_eval(con, tostr("$string_utils:nn(parent(", robj, "))")), ".");
"Last modified Fri Feb  6 08:20:38 1998 CST by Wizard (#2).";
.
#127:29
":get_info() => {host, port, cstr, robj}";
"Ask the player for info on what they want to port.";
"Return host, port, connection string, and remote object.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
player:tell("Host Name?");
host = tostr(this:read());
lr = this:db_lookup(host);
if (!lr)
player:tell("Port?");
port = tonum(this:read());
else
{host, port} = lr;
player:tell("Entry found in db, using ", host, ":", port);
endif
player:tell("Connection String? (ie connect someone foobar)");
cstr = tostr(this:read());
player:tell("Remote Object Number?");
robj = tostr(this:read());
"Yeah a string... why tostr it every time to send it out? -SR";
return {host, port, cstr, robj};
"Last modified Fri Feb  6 08:20:39 1998 CST by Wizard (#2).";
.
#127:30
":create_local_object(connection,parent) => lobj";
"If we need to create a local object it's done here.  All errors are handled";
"and if there is a error the connection is closed and execution halted.";
"Return the local object if successful.";
"";
"Creates the local object with args[2] as the parent.  args[1] is";
"is the connection in case we need to abort and close it.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
set_task_perms(player);
"secure??";
con = args[1];
parent = args[2];
parent = (valid(parent) && (parent != #0)) ? parent | #1;
"Creating kids of #0 can cause problems ...";
"Hope no one needs to make kids of #0... why would you? -SR";
lobj = player:_create(parent);
if (typeof(lobj) == ERR)
if (lobj == E_PERM)
player:tell("You can't create a child of ", parent, ".");
elseif (lobj == E_QUOTA)
player:tell("You don't have enough quota to create an object locally.");
else
player:tell("A useable object can't be created locally.");
endif
$network:close(con);
this:msg("Aborted.");
kill_task(task_id());
elseif (!valid(lobj))
player:tell("A useable object can't be created locally.");
$network:close(con);
this:msg("Aborted.");
kill_task(task_id());
endif
this:msg("Object ", lobj, " with parent ", $string_utils:nn(parent(lobj)), " created locally.");
fork (0)
lobj:moveto(player);
"Buggy movetos gotta love em.";
endfork
return lobj;
"Last modified Fri Feb  6 08:20:39 1998 CST by Wizard (#2).";
.
#127:31
"**Currently unused, wasn't very portable.  Looking into other methods of verifying.";
"";
":check_cmdline_called() => result";
"Attempts to make sure the calling verb was run from the command line.";
"Basically some good old fashioned paranoia.  Doesn't appear to work ";
"correctly with WinMOO servers.  Set this.winmoo to true for a workaround.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
if (this.winmoo)
return;
"WinMOO botches up on this check, have to bypass it.";
endif
callers = args[1];
if (length(callers) == 3)
fc = ((callers[1][1] == player) && (callers[1][2] == "my_huh")) && (callers[1][4] == $player);
sc = ((callers[2][1] == $command_utils) && (callers[2][2] == "do_huh")) && (callers[2][4] == $command_utils);
names = args[2];
names = $string_utils:strip_chars(names, "*");
names = $string_utils:words(names);
tc = 0;
for n in (names)
tc = (callers[3][2] == n) ? 1 | tc;
endfor
tc = tc ? (callers[3][1] == player.location) && (callers[3][4] == $root_class) | 0;
if ((fc && sc) && tc)
return;
endif
endif
$error:raise(E_PERM);
"Last modified Fri Feb  6 08:20:39 1998 CST by Wizard (#2).";
.
#127:32
":eval_cmd_string(exp) => result";
"My version of $no_one:eval_d, that little :bad_eval verb causes lotsa ";
"problems (mainly ticking out on large lists...). ";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
set_task_perms($no_one);
"Keep ppl from doing weird things like trying to peek at stuff they shouldnt etc...";
exp = args[1];
$command_utils:suspend_if_needed(0);
value = $code_utils:eval_d(tostr("this=", $no_one, "; return ", exp, ";"));
suspend(0);
return value;
"Last modified Fri Feb  6 08:20:40 1998 CST by Wizard (#2).";
.
#127:33
":msg(str)";
"Print a message to player prefixed by .msg_prefix";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
player:tell(this.msg_prefix, tostr(@args));
"Last modified Fri Feb  6 08:20:40 1998 CST by Wizard (#2).";
.
#127:34
":*_msg() => string";
"Returns a message with pronoun's subbed.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
return $string_utils:pronoun_sub(this.(verb), this.owner, this, player.location);
"Last modified Fri Feb  6 08:20:40 1998 CST by Wizard (#2).";
.
#127:35
"Copied from #0:bf_add_verb in the Feb 3, 1997 LambdaMOO Core.";
"This is included because most people don't/won't set it up correctly on their MOO.";
"";
"add_verb() -- see help on the builtin for more information. This verb is called by   the server when $server_options.protect_add_verb exists and is true and   caller_perms() are not wizardly.";
if (caller != this)
return E_PERM;
endif
who = caller_perms();
what = args[1];
info = args[2];
if (typeof(what) != OBJ)
retval = E_TYPE;
elseif (!valid(what))
retval = E_INVARG;
elseif ((!$perm_utils:controls(who, what)) && (!what.w))
"caller_perms() is not allowed to hack on the object in question";
retval = E_PERM;
elseif (!$perm_utils:controls(who, info[1]))
"caller_perms() is not permitted to add a verb with the specified owner.";
retval = E_PERM;
elseif (index(info[2], "w") && (!$server_options.permit_writable_verbs))
retval = E_INVARG;
elseif (!$quota_utils:verb_addition_permitted(who))
retval = E_QUOTA;
elseif (((what.owner != who) && (!who.wizard)) && (!$quota_utils:verb_addition_permitted(what.owner)))
retval = E_QUOTA;
elseif (!who.programmer)
retval = E_PERM;
else
"we now know that the caller's perms control the object or the object is writable,   and we know that the caller's perms control the prospective verb owner (by more traditional means)";
retval = `add_verb(@args) ! ANY';
endif
return retval;
"Last modified Fri Feb  6 08:20:40 1998 CST by Wizard (#2).";
.
#127:36
"Copied from #0:bf_add_property in the Feb 3, 1997 LambdaMOO Core.";
"This is included because most people don't/won't set it up correctly on their MOO.";
"";
"add_property() -- see help on the builtin for more information. This verb is called by the server when $server_options.protect_add_property exists and is true and caller_perms() are not wizardly.";
if (caller != this)
return E_PERM;
endif
who = caller_perms();
{what, propname, value, info} = args;
if (typeof(what) != OBJ)
retval = E_TYPE;
elseif (!valid(what))
retval = E_INVARG;
elseif ((!$perm_utils:controls(who, what)) && (!what.w))
retval = E_PERM;
elseif (!$perm_utils:controls(who, info[1]))
retval = E_PERM;
elseif (!$quota_utils:property_addition_permitted(who))
retval = E_QUOTA;
elseif (((what.owner != who) && (!who.wizard)) && (!$quota_utils:property_addition_permitted(what.owner)))
retval = E_QUOTA;
"elseif (!who.programmer)";
"  return E_PERM;     I wanted to do this, but $builder:@newmessage relies upon nonprogs being able to call add_property.  --Nosredna";
elseif ((propname in {"object_size", "size_quota", "queued_task_limit"}) && (!who.wizard))
retval = E_PERM;
else
"we now know that the caller's perms control the object (or the object is writable), and that the caller's perms are permitted to control the new property's owner.";
retval = `add_property(@args) ! ANY';
endif
return retval;
"Last modified Fri Feb  6 08:21:58 1998 CST by Wizard (#2).";
.
#127:37
"Copied from #0:bf_set_verb_info in the Feb 3, 1997 LambdaMOO Core.";
"This is included because most people don't/won't set it up correctly on their MOO.";
"";
"set_verb_info() -- see help on the builtin for more information. This verb is called by the server when $server_options.protect_set_verb_info exists and is true and caller_perms() are not wizardly.";
if (caller != this)
return E_PERM;
endif
{o, v, i} = args;
if (typeof(vi = `verb_info(o, v) ! ANY') == ERR)
"probably verb doesn't exist";
retval = vi;
elseif (!$perm_utils:controls(cp = caller_perms(), vi[1]))
"perms don't control the current verb owner";
retval = E_PERM;
elseif ((typeof(i) != LIST) || (typeof(no = i[1]) != OBJ))
"info is malformed";
retval = E_TYPE;
elseif ((!valid(no)) || (!is_player(no)))
"invalid new verb owner";
retval = E_INVARG;
elseif (!$perm_utils:controls(cp, no))
"perms don't control prospective verb owner";
retval = E_PERM;
elseif (index(i[2], "w") && (!`$server_options.permit_writable_verbs ! E_PROPNF, E_INVIND => 1'))
retval = E_INVARG;
else
retval = `set_verb_info(o, v, i) ! ANY';
endif
return ((typeof(retval) == ERR) && $code_utils:dflag_on()) ? raise(retval) | retval;
"Last modified Fri Feb  6 08:21:59 1998 CST by Wizard (#2).";
.
#127:38
":has_verb(what,vname,vargs) => vindx";
"Attempts to find vname (can handle aliases/* verbs) on what.";
"Returns the verb's 1.8.0 style number on the object if found.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
what = args[1];
vname = args[2];
vargs = args[3];
vnames = $string_utils:words(vname);
vindx = 0;
res = 0;
for i in [1..length(verbs(what))]
res = verb_info(what, i);
res2 = verb_args(what, i);
if (typeof(res) == LIST)
m = $set_utils:intersection(vnames, $string_utils:words(res[3]));
found = m && (res2 == vargs);
endif
if (found)
vindx = i;
break i;
endif
endfor
return vindx;
"Last modified Fri Feb  6 08:21:59 1998 CST by Wizard (#2).";
.
#127:39
":check_args(@args) => true/false";
"Verify early on if the arguments passed in were valid.";
"Currently only argument is local object to port to, has to be a #";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (args)
w = args[1];
case1 = tostr(toobj(w)) == w;
this.debug && this:msg("DEBUG : Args : ", w);
this.debug && this:msg("DEBUG : Case1 : ", case1);
if (!case1)
player:tell("You have to specify the local object by it's object number, to help prevent mistakes.");
kill_task(task_id());
endif
endif
"Last modified Fri Feb  6 08:21:59 1998 CST by Wizard (#2).";
.
#127:40
"Syntax : @srp-addname";
"Allows trusted people to add names and addresses for MOOs into the porter's db.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
this:check_cmdline_called(callers(), verb);
if (!this:trust(caller_perms()))
player:tell(this:nodice_msg());
return;
endif
player:tell("Enter the MOOs address :");
address = this:read();
player:tell("Enter the MOOs port :");
port = tonum(this:read());
player:tell("What do you wish to refer to this entry as?");
name = this:read();
this.db_names = {@this.db_names, name};
this.db_data = {@this.db_data, {address, port}};
player:tell("Done.");
"Last modified Fri Feb  6 08:22:39 1998 CST by Wizard (#2).";
.
#127:41
"Syntax : @srp-rmname";
"Allows trusted people to remove names and addresses for MOOs into the porter's  db.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
this:check_cmdline_called(callers(), verb);
if (!this:trust(caller_perms()))
player:tell(this:nodice_msg());
return;
endif
player:tell("Entries in db : ");
if (!this.db_names)
player:tell("The db is empty");
return;
endif
l = length(this.db_names);
for i in [1..l]
data = this.db_data[i];
player:tell(i, " : ", this.db_names[i], " (", data[1], ":", data[2], ")");
endfor
player:tell("Enter the number of the entry to remove :");
n = tonum(this:read());
if ((n >= 1) && (n <= l))
this.db_names = listdelete(this.db_names, n);
this.db_data = listdelete(this.db_data, n);
else
player:tell("Not a valid entry.");
endif
player:tell("Done.");
"Last modified Fri Feb  6 08:22:40 1998 CST by Wizard (#2).";
.
#127:42
"Syntax : @srp-listnames";
"Allows trusted people to list entries in the database.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
this:check_cmdline_called(callers(), verb);
if (!this:trust(caller_perms()))
player:tell(this:nodice_msg());
return;
endif
player:tell("Entries in db : ");
if (!this.db_names)
player:tell("The db is empty");
return;
endif
l = length(this.db_names);
for i in [1..l]
data = this.db_data[i];
player:tell(i, " : ", this.db_names[i], " (", data[1], ":", data[2], ")");
endfor
player:tell("Done.");
"Last modified Fri Feb  6 08:22:40 1998 CST by Wizard (#2).";
.
#127:43
":db_lookup(name) => {hostname,port}";
"Search for name in the porter's db.";
"Returns a list with hostname and port, or a empty list if no match was found.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
name = args[1];
names = this.db_names;
for n in [1..length(names)]
if (name == names[n])
return this.db_data[n];
endif
endfor
return {};
"Last modified Fri Feb  6 08:22:40 1998 CST by Wizard (#2).";
.
#127:44
":sentinel(connection) => taskid";
"This verb is passed a connection to monitor, if it goes idle for too long it boots the connection, and kills the calling task.";
"";
"SR Porter. Copyright (C) 1995-1998 by Michael Thompson.";
if (caller != this)
return E_PERM;
endif
con = args[1];
maintask = task_id();
fork ftask (0)
this.debug && this:msg("SENTINEL : Start : MTASK  (", maintask, ")  FTASK (", ftask, ")");
while (typeof(i = idle_seconds(con)) != ERR)
this.debug && this:msg("SENTINEL : idle : ", i);
(i > 5) && this:msg("The connection is experiencing lag. Idle : ", i, " seconds");
tasks = queued_tasks();
i = maintask in $list_utils:slice(tasks);
info = i ? tasks[i] | {};
if (!info)
"Something happened, a TB whatever, the maintask is gone, and the connections still up.";
boot_player(con);
this:msg("Local failure.  Closing connection, port aborted.");
else
if ((info[2] == -1) && (i >= this.idle_timeout))
"If it's stuck reading with no input for too long abort...";
this:msg("The connection has been idle for too long.  Closing and aborting port.");
kill_task(maintask);
boot_player(con);
else
suspend(5);
endif
endif
endwhile
suspend(1);
kill_task(maintask);
"Clean up";
this.debug && this:msg("SENTINEL : Done.");
endfork
return ftask;
"Last modified Fri Feb  6 08:22:40 1998 CST by Wizard (#2).";
.
#127:45
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Fri Feb  6 08:47:47 1998 CST by Wizard (#2).";
.
#128:0
return ((!this.restricted) || (player in this.accept)) || ((!player) in this.reject);
"Last modified Fri Feb  6 08:23:13 1998 CST by Wizard (#2).";
.
#128:1
if (caller != this.comm_fo)
return E_PERM;
endif
if (player in this.c_lis)
return 0;
else
this.c_lis = {@this.c_lis, player};
return 1;
endif
"Last modified Fri Feb  6 08:23:53 1998 CST by Wizard (#2).";
.
#128:2
if (caller != this.comm_fo)
return E_PERM;
endif
if (!(player in this.c_lis))
return 0;
else
this.c_lis = setremove(this.c_lis, player);
return 1;
endif
"Last modified Fri Feb  6 08:23:53 1998 CST by Wizard (#2).";
.
#128:3
if (player == this.owner)
if (is_player(dobj))
this.accept = {@this.accept, dobj};
player:tell(dobj.name, " added to ", this.name, ".");
else
player:tell(dobj.name, " is not a player.");
endif
else
player:tell("You can't set trusted players to this Channel.");
endif
"Last modified Fri Feb  6 08:23:53 1998 CST by Wizard (#2).";
.
#128:4
if (caller != this.comm_fo)
return E_PERM;
endif
this.c_talk = {@this.c_talk, player};
"Last modified Fri Feb  6 08:23:53 1998 CST by Wizard (#2).";
.
#128:5
if (caller != this.comm_fo)
return E_PERM;
endif
this.c_talk = setremove(this.c_talk, player);
"Last modified Fri Feb  6 08:23:54 1998 CST by Wizard (#2).";
.
#128:6
if (player == this.owner)
if (is_player(dobj))
this.reject = {@this.reject, dobj};
player:tell(dobj.name, " added to rejected list on ", this.name, ".");
else
player:tell(dobj.name, " is not a player.");
endif
else
player:tell("You can't set rejected players on this Channel.");
endif
"Last modified Fri Feb  6 08:23:54 1998 CST by Wizard (#2).";
.
#128:7
if (caller != this.comm_fo)
return E_PERM;
endif
head = tostr("[", this.name, "]: ");
for who in (this:list_online())
who:tell(tostr(head, @args));
endfor
"Last modified Fri Feb  6 08:23:54 1998 CST by Wizard (#2).";
.
#128:8
if (caller != this)
return E_PERM;
endif
return this.c_lis;
"Last modified Fri Feb  6 08:23:54 1998 CST by Wizard (#2).";
.
#128:9
if (caller != this)
return E_PERM;
endif
return $set_utils:intersection(this.c_lis, connected_players());
"Last modified Fri Feb  6 08:23:54 1998 CST by Wizard (#2).";
.
#128:10
if (caller_perms().wizard)
pass();
this.comm_fo = $channel_FO;
move(this, $tool_box);
endif
"Last modified Fri Feb  6 08:47:47 1998 CST by Wizard (#2).";
.
#129:0
if (!dobjstr)
return player:tell("Usage:  @xs  Channel");
endif
if (!valid(chan = this:match_channel(dobjstr)))
return player:tell(dobjstr, " is not a valid Channel.");
endif
if (player in chan.c_lis)
this:remove_talker_all();
chan:add_talker();
player:tell("You switch to ", chan.name, ".");
else
player:tell("You are not listening to ", chan.name, ".");
endif
"Last modified Wed Apr 15 12:58:06 1998 CDT by Wizard (#2).";
.
#129:1
return $string_utils:match(argstr, children(this.gen_chan), "name", children(this.gen_chan), "aliases");
"Last modified Fri Feb  6 08:26:01 1998 CST by Wizard (#2).";
.
#129:2
if (caller != this)
return E_PERM;
endif
for n in (children(this.gen_chan))
n:remove_talker();
endfor
"Last modified Fri Feb  6 08:26:01 1998 CST by Wizard (#2).";
.
#129:3
if (caller != this)
return E_PERM;
endif
for n in (children(this.gen_chan))
n:remove_listener();
endfor
"Last modified Fri Feb  6 08:26:01 1998 CST by Wizard (#2).";
.
#129:4
if (!player.wizard)
player:tell("You can't shout on all the channels!");
else
for n in (children(this.gen_chan))
n:transmit(tostr(player:title(), " shouts, \"", argstr, "\""));
endfor
endif
"Last modified Fri Feb  6 08:26:01 1998 CST by Wizard (#2).";
.
#129:5
if (!dobjstr)
player:tell("Syntax:   @add-c Channel#");
return;
endif
if (!valid(chan = this:match_channel(dobjstr)))
player:tell(dobjstr, " is not a valid Channel!");
return;
endif
if (!chan:ok_listen())
player:tell("You are not allowed on that Channel!");
elseif (!chan:add_listener())
player:tell("You are already listening on that Channel!");
else
this:remove_talker_all();
chan:add_talker();
player:tell("You are now listening to ", chan.name);
endif
"Last modified Fri Feb  6 08:26:01 1998 CST by Wizard (#2).";
.
#129:6
if (!dobjstr)
player:tell("Syntax:   @xdisconnect Channel_Name");
return;
endif
if (!valid(chan = this:match_channel(dobjstr)))
return player:tell("There is no ", dobjstr, " Channel!");
endif
if (chan:remove_listener())
chan:remove_talker();
player:tell("You are no longer listening to ", chan.name, ".");
else
player:tell("You are not listening to ", chan.name, ".");
endif
"Last modified Fri Feb  6 08:26:01 1998 CST by Wizard (#2).";
.
#129:7
if (!argstr)
return player:tell("Syntax:   xm message");
endif
if ((i = this:c_channel()) != 0)
i:transmit(tostr(player:title(), " says, \"", argstr, "\""));
else
player:tell("You are not talking on any Channel!");
endif
"Last modified Fri Feb  6 08:26:01 1998 CST by Wizard (#2).";
.
#129:8
message = "";
if (verb[1..2] == "x:")
message = (((verb[3] == ":") ? verb[4..length(verb)] | (" " + verb[3..length(verb)])) + " ") + argstr;
else
message = (argstr[1] == ":") ? argstr[2..length(argstr)] | (" " + argstr);
endif
if (!message)
return player:tell("Syntax:   x: message");
endif
if ((i = this:c_channel()) != 0)
i:transmit(tostr(player:title(), message));
else
player:tell("You are not talking on any Channel!");
endif
"Last modified Fri Feb  6 08:26:02 1998 CST by Wizard (#2).";
.
#129:9
if (caller != this)
return E_PERM;
endif
for i in (children(this.gen_chan))
if (player in i.c_talk)
return i;
endif
endfor
return 0;
"Last modified Fri Feb  6 08:26:45 1998 CST by Wizard (#2).";
.
#129:10
return 0;
"Last modified Fri Feb  6 08:26:45 1998 CST by Wizard (#2).";
.
#129:11
if (!argstr)
zz = this:c_channel();
zzz = 0;
else
zzz = 1;
if (!valid(zz = this:match_channel(args[1])))
player:tell(args[1], " not valid Channel!");
return;
endif
endif
idles = itimes = offs = otimes = {};
zaz = zzz ? zz.c_lis | $set_utils:intersection(zz.c_lis, connected_players());
for p in (zaz)
if (p in offs)
elseif (!valid(p))
player:notify(tostr(p, " <invalid>"));
elseif (typeof(i = idle_seconds(p)) != ERR)
if (!(p in idles))
idles = {@idles, p};
itimes = {@itimes, {i, connected_seconds(p), p}};
endif
elseif (typeof(t = p.last_disconnect_time) == NUM)
offs = {@offs, p};
otimes = {@otimes, {-t, -t, p}};
elseif (is_player(p))
player:notify(tostr(p.name, " (", p, ") not logged in.", (t == E_PROPNF) ? "  Not a $player." | "  Garbled .last_disconnect_time."));
else
player:notify(tostr(p.name, " (", p, ") is not a player."));
endif
endfor
if (!(idles || offs))
return 0;
endif
idles = $list_utils:sort_alist(itimes);
offs = $list_utils:sort_alist(otimes);
"...";
"... calculate widths...";
"...";
headers = {"Name", @idles ? {"Idle time"} | {"Last Disconnected"}, "Location", "Channel"};
total_width = player:linelen() || 79;
max_name = total_width / 4;
loc_width = length(headers[2]);
name_width = length(headers[1]);
names = locations = {};
for lst in ({@idles, @offs})
$command_utils:suspend_if_needed(0);
p = lst[3];
t = p:title();
namestr = tostr(t[1..min(max_name, length(t))], " (", p, ")");
name_width = max(length(namestr), name_width);
names = {@names, namestr};
if (typeof(wlm = p.location:who_location_msg(p)) != STR)
wlm = valid(p.location) ? p.location.name | tostr("** Nowhere ** (", p.location, ")");
endif
loc_width = max(length(wlm), loc_width);
locations = {@locations, wlm};
endfor
time_width = 3 + (offs ? 12 | length("59 minutes"));
before = {0, w1 = 3 + name_width, w2 = w1 + time_width, (w2 + 2) + loc_width};
"...";
"...print headers...";
"...";
su = $string_utils;
tell1 = headers[1];
tell2 = su:space(tell1, "-");
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[1..min(length(tell1), total_width)]);
player:notify(tell2[1..min(length(tell2), total_width)]);
"...";
"...print lines...";
"...";
active = 0;
for i in [1..total = (ilen = length(idles)) + length(offs)]
if (i <= ilen)
lst = idles[i];
if (lst[1] < (5 * 60))
active = active + 1;
endif
l = {names[i], su:from_seconds(lst[1]), locations[i], zz.name};
else
lct = offs[i - ilen][3].last_connect_time;
ldt = offs[i - ilen][3].last_disconnect_time;
ctime = player:ctime(ldt) || ctime(ldt);
l = {names[i], (lct <= time()) ? ctime | "Never", "", locations[i]};
if ((i == (ilen + 1)) && idles)
player:notify(su:space(before[2]) + "------- Disconnected -------");
endif
endif
tell1 = l[1];
for j in [2..4]
tell1 = su:left(tell1, before[j]) + l[j];
endfor
player:notify(tell1[1..min(length(tell1), total_width)]);
if ($command_utils:running_out_of_time())
now = time();
suspend(0);
if ((time() - now) > 10)
player:notify(tostr("Plus ", total - i, " other players (", total, " total; out of time and lag is high)."));
return;
endif
endif
endfor
"...";
"...epilogue...";
"...";
player:notify("");
if (total == 1)
active_str = ", who has" + ((active == 1) ? "" | " not");
else
if (active == total)
active_str = (active == 2) ? "s, both" | "s, all";
elseif (active == 0)
active_str = "s, none";
else
active_str = tostr("s, ", active);
endif
active_str = tostr(active_str, " of whom ha", (active == 1) ? "s" | "ve");
endif
player:notify(tostr("Total: ", total, " person", active_str, " been active recently."));
return total;
"Last modified Fri Feb  6 08:26:47 1998 CST by Wizard (#2).";
.
#129:12
player:tell("The Channels that are available!");
player:tell($string_utils:space(32, "-"));
for i in (children(this.gen_chan))
player:tell(i.name);
player:tell("It is ", i.restricted ? "" | "not", " restricted!");
player:tell("You are ", i:ok_listen() ? "" | "not", " allowed to use this channel!");
player:tell("");
endfor
"Last modified Fri Feb  6 08:26:47 1998 CST by Wizard (#2).";
.
#129:13
if (caller_perms().wizard)
pass();
this.gen_chan = $channel;
move(this, $tool_box);
endif
"Last modified Fri Feb  6 08:47:47 1998 CST by Wizard (#2).";
.
#130:0
"Copyright (C) 1998, Jan Rune Holmevik";
this:trust(caller_perms());
finished = 0;
title = $encore_utils:make_title($network.MOO_name + " User Account Administration");
while (!finished)
player:tell_lines(title);
if (!this.purge_queue)
player:tell("There are no characters in the purging queue");
player:tell("");
player:tell("1) Find users who was created ", $time_utils:english_time(7776000), " ago but has never connected");
player:tell("2) Find users who has not connected in the last ", $time_utils:english_time(15638400));
player:tell("3) Protect a character from being purged");
player:tell("");
player:tell("Please enter your choice (1-3) or type Q to quit");
choice = read(player);
if (choice == "1")
this:find_unconnecteds();
elseif (choice == "2")
this:find_idlers();
elseif (choice == "3")
this:protect();
else
player:tell("Exiting.");
finished = 1;
endif
else
interval = time() - this.lastrun;
elapsed_time = $time_utils:english_time(interval);
last_updated = $time_utils:time_sub("$D, $N $T, $Y.", this.lastrun);
player:tell("There are currently ", length(this.purge_queue), " characters in the purging queue, which was compiled ", elapsed_time, " ago, on ", last_updated);
player:tell("");
player:tell("1) Display a list of the characters that are up for purging");
player:tell("2) Protect a character from being purged");
player:tell("3) Send a purge warning by email to all characters in the queue.");
player:tell("4) Purge all characters in the queue");
player:tell("5) Reset purge queue");
player:tell("");
player:tell("Please enter your choice (1-5) or type Q to quit.");
choice = read(player);
if (choice == "1")
this:report();
elseif (choice == "2")
this:protect();
elseif (choice == "3")
this:_send_mail(this.purge_warning);
elseif (choice == "4")
this:_purge();
elseif (choice == "5")
this.purge_queue = {};
else
player:tell("Exiting.");
finished = 1;
endif
endif
endwhile
"Last modified Fri Feb  6 08:27:36 1998 CST by Wizard (#2).";
.
#130:1
return player.wizard;
"Last modified Fri Feb  6 08:28:25 1998 CST by Wizard (#2).";
.
#130:2
"Look for people who've never connected in the period defined by .unconnected_threshold on this object.";
this:trust(caller_perms());
t = time() - this.unconnected_threshold;
for x in (players())
if (((x.last_connect_time == $maxint) && (x.last_disconnect_time < t)) && (!this:is_protected(x)))
this.purge_queue = {@this.purge_queue, x};
endif
$command_utils:suspend_if_needed(0);
endfor
player:tell("Found ", length(this.purge_queue), " characters.");
this.lastrun = time();
"Last modified Fri Feb  6 08:28:27 1998 CST by Wizard (#2).";
.
#130:3
if (!args[1].wizard)
$error:raise(E_PERM);
endif
"Last modified Fri Feb  6 08:28:27 1998 CST by Wizard (#2).";
.
#130:4
return (args[1] in this.protected) || args[1].wizard;
"Last modified Fri Feb  6 08:28:27 1998 CST by Wizard (#2).";
.
#130:5
this:trust(caller_perms());
t = time() - this.idle_threshold;
mail = this.purge_warning;
for x in (players())
if (this:idle_check(x, t))
this.purge_queue = {@this.purge_queue, x};
endif
$command_utils:suspend_if_needed(0);
endfor
player:tell("Found ", length(this.purge_queue), " characters.");
this.lastrun = time();
"Last modified Fri Feb  6 08:28:27 1998 CST by Wizard (#2).";
.
#130:6
x = args[1];
t = args[2];
if (((is_player(x) && (x.last_connect_time < t)) && (x.last_disconnect_time < t)) && (!this:is_protected(x)))
for y in (setremove(x.owned_objects, x))
if (verbs(y) || properties(y))
return 0;
endif
endfor
return 1;
else
return 0;
endif
"Last modified Fri Feb  6 08:28:27 1998 CST by Wizard (#2).";
.
#130:7
this:trust(caller_perms());
player:tell("Please enter the name of the character you wish to premanently protect from automatic purging");
who = $string_utils:match_player(read(player));
if (!valid(who))
return $command_utils:player_match_failed(who, dobjstr);
endif
if (who in this.protected)
player:tell(who.name, " is already protected.");
else
this.protected = {@this.protected, who};
if (who in this.purge_queue)
this.purge_queue = setremove(this.purge_queue, who);
endif
player:tell(who.name, " (", who, ") will be protected from automatic purging.");
endif
"Last modified Fri Feb  6 08:28:28 1998 CST by Wizard (#2).";
.
#130:8
this:trust(caller_perms());
total_objects = 0;
size = 0;
for who in (this.purge_queue)
$command_utils:suspend_if_needed(5);
player:tell("Name           : ", who.name, " (", who, ")");
player:tell("Real name      : ", who.real_name);
player:tell("Email Address  : ", who.email_address);
if (who.last_connect_time == $maxint)
player:tell("Never Connected, created :", $time_utils:english_time(time() - who.last_disconnect_time), " ago");
else
player:tell("Last connected :", ctime(who.last_disconnect_time));
endif
player:pros(dobjstr = tostr(dobj = who));
for x in (who.owned_objects)
size = size + x.object_size[1];
total_objects = total_objects + 1;
endfor
player:tell("");
endfor
player:tell("Total bytes to be purged: ", $string_utils:group_number(size), " in ", total_objects, " objects.");
"Last modified Fri Feb  6 08:28:28 1998 CST by Wizard (#2).";
.
#130:9
this:trust(caller_perms());
if ($command_utils:yes_or_no(tostr("Send mail to ", length(this.purge_queue), " players?")))
msg = args[1];
subject = ("Your " + $network.MOO_name) + " Account";
to = {};
for plyr in (this.purge_queue)
to = {@to, plyr};
endfor
for n in (to)
sent = $network:sendmail(n.email_address, subject, "Reply-to: " + $login.registration_address, @msg);
success = (typeof(sent) == ERR) ? "not sent" | "sent";
player:tell(tostr("Mail was ", success, " to ", n:title(), "."));
endfor
player:tell("FINISHED!");
else
player:tell("Okay, mail not sent.");
endif
"Last modified Fri Feb  6 08:28:28 1998 CST by Wizard (#2).";
.
#130:10
"Toads and recycles all players in the purge queue.";
this:trust(caller_perms());
if ($command_utils:yes_or_no(">>> WARNING: This will recycle all characters in the queue and all their objects. Are you sure you wish to continue? <<<"))
total = 0;
bytes = 0;
mail = this.goodbye_mail;
player:tell("Emailing purge notification");
this:_send_mail(mail);
for object in (this.purge_queue)
if (is_player(object))
if (((email = object.email_address) && (current = $registration_db:find_exact(email))) && (ind = $list_utils:iassoc(object, current)))
current[ind] = {@current[ind], "purged due to inactivity"};
$registration_db:insert(email, current);
endif
$wiz_utils:unset_player(object);
endif
for x in ({@setremove(object.owned_objects, object), object})
if (x.owner == object)
player:tell("x ", $building_utils:object_audit_string(x));
$command_utils:suspend_if_needed(0);
if ($object_utils:has_property(x, "object_size"))
bytes = bytes + x.object_size[1];
endif
$recycler:_recycle(x);
total = total + 1;
endif
endfor
this.purge_queue = setremove(this.purge_queue, object);
endfor
player:tell("Purging complete.  ", total, " objects recycled for a total of ", $string_utils:group_number(bytes), " bytes.");
else
player:tell("Okay, purging aborted.");
endif
"Last modified Fri Feb  6 08:28:29 1998 CST by Wizard (#2).";
.
#130:11
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Fri Feb  6 08:47:46 1998 CST by Wizard (#2).";
.
#131:0
"pick <entry> on slate";
"  entry is either a line number or an initial substring of a line description";
"  select that entry: if it is a menu, go to that node. If it is a search,";
"  asks you for the search term & does the search.";
"  Some kinds of nodes are not implemented.";
if (this:_textp() || (!(this.stack || this.remembered)))
return player:tell("There's nothing to pick.");
endif
if (this:busy("picking"))
return;
endif
if (!(which = this:match_choice(dobjstr)))
"match_choice took care of it.";
this:busy(0);
return;
endif
if ((tostr(tonum(dobjstr)) == dobjstr) && (!({player, @this:_place()} in this.seen)))
player:tell($string_utils:pronoun_sub("Oooops, perhaps you should look at the %t first."));
this:busy(0);
return;
endif
parse = $gopher:parse(this.value[which]);
desc = this.desclines[which];
this:announce_op("%N %<picks> '", desc, "' on the %t.");
this:do_pick(@parse);
return;
"Last modified Fri Feb  6 08:29:32 1998 CST by Wizard (#2).";
.
#131:1
"reset slate";
"  reset the slate to its set of 'remembered' selections";
if (why = this:is_locked(player))
return player:tell($string_utils:pronoun_sub("Sorry, %t seems to be "), why, ".");
elseif (this:busy("resetting"))
return;
endif
this:announce_op("%N %<resets> the %t.");
this.seen = {};
this:set_pointer();
this:busy(0);
"Last modified Fri Feb  6 08:30:11 1998 CST by Wizard (#2).";
.
#131:2
"back this [by <n>]";
"  move back up the gopher stack to the previous menu";
"  or previous N menus.";
n = 1;
if (iobjstr && (!(iobjstr == tostr(n = tonum(iobjstr)))))
return player:tell("Sorry, '", iobjstr, "' doesn't look like a number.");
endif
if (length(this.stack) < n)
player:tell("Sorry, there aren't ", n, " levels to go back.");
return;
endif
if (this:busy("going back"))
return;
endif
this:announce_op("%N %<goes> back up ", (n == 1) ? "a level" | tostr(n, " levels"), " on the %t.");
this:set_pointer(@this.stack[n + 1..length(this.stack)]);
this:busy(0);
"Last modified Fri Feb  6 08:30:11 1998 CST by Wizard (#2).";
.
#131:3
"location_string([location])";
"A nice-looking version of the location provided, or current location.";
loc = (args && args[1]) || this.stack[1];
where = loc[1];
if (st = loc[4])
"human readable string";
return ((st[2..length(st)] + " (from ") + where) + ")";
return (where + ": ") + st[2..length(st)];
endif
if (loc[3])
return ((loc[3] + " (from ") + where) + ")";
return (where + ": ") + loc[3];
endif
return where;
"Last modified Fri Feb  6 08:30:12 1998 CST by Wizard (#2).";
.
#131:4
"stack slate";
"  show a summary of the gopher stack";
max = 0;
if (!this.stack)
return player:tell($string_utils:pronoun_sub("%T is at the top level."));
endif
for x in (this.stack)
max = max(max, length(x[1]));
endfor
max = max + 6;
for x in ($list_utils:reverse(this.stack))
summary = $gopher:summary(x);
player:tell($string_utils:left(summary[1], max), " ", summary[2]);
endfor
"Last modified Fri Feb  6 08:30:12 1998 CST by Wizard (#2).";
.
#131:5
"interlock for caching -- mark cache busy or clear; return true of interlock failed";
if (args[1])
if ((args[1] != "reading") && (why = this:is_locked(player)))
player:tell($string_utils:pronoun_sub("Sorry, %t seems to be "), why, ".");
return 1;
endif
"make player running this watch it.";
this.watching = setadd(this.watching, player);
"set busy";
if (this.busy && (this.busy[1] > time()))
player:tell("***Sorry, ", this.name, " is busy ", this.busy[2], " for ", this.busy[3], " -- wait a bit.");
return 1;
else
this.busy = {time() + (60 * 5), args[1], player.name, task_id()};
return 0;
endif
else
this.busy = 0;
return 0;
endif
"Last modified Fri Feb  6 08:30:12 1998 CST by Wizard (#2).";
.
#131:6
"match_choice(input string)";
"returns the index of the choice, or 0.";
"is noisy.";
if (this:_textp())
player:tell($string_utils:pronoun_sub("%T is looking at a text node and has no choices."));
return 0;
endif
input = args[1];
which = $code_utils:tonum(input);
len = length(value = this.value);
if (typeof(which) == NUM)
if ((which < 1) || (which > len))
player:tell("Sorry, ", input, " isn't a number between 1 and ", len, ".");
return 0;
endif
return which;
else
exact = partial = {};
for choice in [1..len]
valchoice = value[choice][2..index(value[choice], "	") - 1];
if (input == valchoice)
exact = {@exact, choice};
elseif (index(valchoice, input) == 1)
partial = {@partial, choice};
endif
endfor
if (length(exact) > 1)
player:tell("I'm not sure whether you meant ", $string_utils:english_list(exact, "", " or "), ".");
return 0;
elseif (exact)
return exact[1];
elseif (length(partial) > 1)
player:tell("I'm not sure whether you meant ", $string_utils:english_list(partial, "", " or "), ".");
return 0;
elseif (partial)
return partial[1];
else
player:tell("Sorry, there is no choice named ", $string_utils:print(input), ".");
return 0;
endif
endif
"Last modified Fri Feb  6 08:30:13 1998 CST by Wizard (#2).";
.
#131:7
"goto <host> [socket] on slate";
"  given an explicit host name and optional socket, attempt to open a";
"  gopher connection to that socket";
words = $string_utils:words(dobjstr);
if (!words)
player:tell("Usage:  ", verb, " <host> [socket]", prepstr ? tostr(" on ", iobjstr) | "");
return;
endif
host = words[1];
socket = 70;
if (length(words) > 1)
socket = tonum(words[2]);
if (socket < 3)
player:tell("The value '", words[2], "' is not a valid socket.");
return;
endif
endif
path = "";
if (length(words) > 2)
path = dobjstr[(index(dobjstr, words[2]) + length(words[2])) + 1..length(dobjstr)];
endif
if (this:busy(tostr("jumping to ", host, " socket ", socket)))
return;
endif
this:announce_op(tostr("%N %<jumps> to ", host, " socket ", socket, path ? " " | "", path, " on the %t."));
parse = {host, socket, path, "1<jump>"};
this:set_pointer(parse, @this:_textp() ? listdelete(this.stack, 1) | this.stack);
this:busy(0);
"Last modified Fri Feb  6 08:31:00 1998 CST by Wizard (#2).";
.
#131:8
if (!(which = this:match_choice(dobjstr)))
"match_choice took care of it.";
return;
endif
parse = $gopher:parse(this.value[which]);
sel = parse[4];
if (sel)
for x in ({"Type=" + sel[1], "Name=" + sel[2..length(sel)], "Path=" + parse[3], "Host=" + parse[1], "Port=" + tostr(parse[2]), "#"})
player:tell(x);
endfor
else
player:tell("**** ERROR, ", which, " is not a valid entry.");
endif
"Last modified Fri Feb  6 08:31:01 1998 CST by Wizard (#2).";
.
#131:9
if (!args)
value = this.remembered;
else
value = $gopher:get(@args[1]);
endif
if (!value)
this:busy(0);
this:announce_op($gopher:interpret_error(value));
return 0;
endif
if (value[1][1] == "3")
this:busy(0);
this:announce_op("The gopher request results in an error:");
for x in (value)
this:announce_op(": ", x ? x[2..length(x)] | x);
endfor
return 0;
endif
if (args && (args[1][4][1] == "0"))
"text node";
desc = value;
else
desc = {};
cnt = 1;
for x in (value)
$command_utils:suspend_if_needed(0);
type = $gopher:type(x[1]);
if (type == "text")
type = "";
else
type = (" (" + type) + ")";
endif
tab = index(x, "	");
label = x[2..tab - 1];
desc = {@desc, tostr(cnt, ". ", label, type)};
cnt = cnt + 1;
endfor
endif
$command_utils:suspend_if_needed(0);
this.desclines = desc;
this.stack = args;
this.value = value;
this:busy(0);
this:show_results();
return 1;
"Last modified Fri Feb  6 08:31:01 1998 CST by Wizard (#2).";
.
#131:10
"do_pick(host, port, path, string) -- take parsed output & interact with user as appropriate.";
string = args[4];
if ((!string) || index("1?", type = string[1]))
"menu";
this:set_pointer(args, @this.stack);
elseif (type == "7")
player:tell("Search for what? Enter search line or @abort:");
search = read();
if (search != "@abort")
this:announce_op("%N %<searches> for ", search, " on %t.");
this:set_pointer({args[1], args[2], (args[3] + "	") + search, args[4]}, @this.stack);
else
this:busy(0);
this:announce_op("%N %<decides> not to search.");
endif
elseif (type == "3")
this:busy(0);
this:announce_op("%N chose an error line.");
elseif (type == "0")
"slates can point at text nodes";
this:set_pointer(args, @this.stack);
elseif (type == "2")
search = $command_utils:read("one of 'name=<name>' 'phone=<phone>' 'email=<email>'");
if (!match(search, "[a-z]+=[a-z0-9@-]+"))
this:busy(0);
player:tell((search == "@abort") ? "No search." | ("Invalid query: " + search));
return;
endif
this:announce_op("%N %<searches> for ", search, " on %t.");
this:set_pointer({args[1], args[2], (args[3] + "	query ") + search, args[4]}, @this.stack);
elseif ($object_utils:has_property(player, "gopher_local") && player.gopher_local)
this:busy(0);
notify(player, tostr("#$# gopher	", args[1], "	", args[2], "	", args[4], "	", args[3]));
else
this:busy(0);
this:announce_op("Type ", type, " (", $gopher:type(type), ") gopher requests not implemented.");
if (type == "8")
player:tell("**** telnet ", args[1], (args[2] in {23, 0}) ? "" | (" " + tostr(args[2])));
if (args[3])
player:tell("     log in as: ", args[3]);
endif
endif
endif
"Last modified Fri Feb  6 08:31:02 1998 CST by Wizard (#2).";
.
#131:11
"remember <entry/here> on <this>";
"  add the entry (or this menu) to the 'remembered set' for this room.";
"  use 'remembered' to retrieve the set.";
if (!this.stack)
return player:tell("Sorry, remembering remembered nodes doesn't work.");
endif
if (dobjstr == "")
parse = this.stack[1];
desc = "the current menu";
elseif (choice = this:match_choice(dobjstr))
parse = $gopher:parse(this.value[choice]);
desc = this.desclines[choice];
else
"Match_choice took care of it.";
return;
endif
parse[4] = parse[4][1] + $command_utils:read("description for " + desc);
this.remembered = {@this.remembered, $gopher:unparse(@parse)};
this:announce_op("%N %<remembers> ", desc, " on the %t as ", parse[4][2..length(parse[4])], ".");
"Last modified Fri Feb  6 08:31:02 1998 CST by Wizard (#2).";
.
#131:12
"forget <entry> on slate";
"  erase an entry from the 'remembered set'";
"  only works if you're looking at the 'remembered set'";
if (this.stack)
player:tell("You're not looking at the top.");
return;
endif
if (!(choice = this:match_choice(dobjstr)))
return;
endif
this:announce_op("%N %<forgets> '", this.desclines[choice], "' on the %t.");
this.remembered = listdelete(this.remembered, choice);
this:set_pointer();
"Last modified Fri Feb  6 08:31:02 1998 CST by Wizard (#2).";
.
#131:13
if (this.stack)
sum = $gopher:summary(this.stack[1]);
player:tell(this:titlec(), ": ", sum[1], " ", sum[2]);
else
player:tell(this:titlec());
endif
player:tell_lines(this:description());
this:_tell_desc();
state = "";
if (valid(this.controlled))
state = ($string_utils:pronoun_sub("The %t is being controlled by ") + this.controlled:title()) + ".";
endif
if ((busy = this:_is_busy()) || state)
player:tell(state ? state + " " | "", busy ? $string_utils:pronoun_sub(tostr("The %t is busy ", this.busy[2], " for ", this.busy[3], ".")) | "");
endif
"Last modified Fri Feb  6 08:31:54 1998 CST by Wizard (#2).";
.
#131:14
who = args ? args[1] | player;
plen = (length(args) > 1) ? args[2] | this.length;
header = (length(args) > 2) && args[3];
if (this:_textp())
text = this:text();
len = length(text);
if ((!plen) || (len <= plen))
$command_utils:suspend_if_needed(0);
"6/24/93 change tell_lines to notify_lines to reduce lag.";
if (header)
who:tell("--------------- ", this.name, "-----");
who:notify_lines(text);
who:tell("--------------- ", this.name, "-----");
else
who:notify_lines(text);
endif
return;
endif
offset = this:offset();
npages = (len / plen) + 1;
thispage = (offset / plen) + 1;
if ((offset != 1) || header)
who:tell("--", thispage, " of ", npages, "----- 'prev on ", this.name, "' for previous----");
endif
end = (offset + plen) - 1;
who:tell_lines(text[offset..min(len, end)]);
if ((len > end) || header)
who:tell("--", thispage, " of ", npages, "----- 'next on ", this.name, "' for more --------");
endif
return;
endif
this.seen = setadd(this.seen, {who, @this:_place()});
len = length(this.desclines);
if (header)
who:tell("--------------- ", this.name, "-----");
endif
if (plen && (len > plen))
offset = this:offset();
who:tell_lines(this.desclines[offset..min((offset + this.length) - 1, len)]);
nxt = "next on " + this.name;
prv = "previous on " + this.name;
who:tell("---- '", (offset == 1) ? nxt | (((offset + plen) > len) ? prv | (((("'" + nxt) + "' or '") + prv) + "'")), "' to see additional choices (", len, " total) ---");
else
who:tell_lines(this.desclines || {$string_utils:pronoun_sub("%T is empty right now.")});
if (header)
who:tell("--------------- ", this.name, "-----");
endif
endif
"Last modified Fri Feb  6 08:31:55 1998 CST by Wizard (#2).";
.
#131:15
if (this:busy("reading"))
"can't 'next' if it is busy";
return;
endif
this:busy(0);
n = tonum(dobjstr) || 1;
if (verb != "next")
n = -n;
verb = "previous";
endif
offset = this:offset();
new = offset + (n * this.length);
if (new < 1)
if (offset == 1)
return player:tell("You're already at the beginning.");
else
new = 1;
endif
elseif (new > length(this.desclines))
return player:tell("You're already at the end.");
endif
this:announce_op("%N %<looks> at the ", verb, " ", this:_textp() ? "page" | "results", " on the %t.");
this:offset(new);
this:show_results();
"Last modified Fri Feb  6 08:31:55 1998 CST by Wizard (#2).";
.
#131:16
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
"don't call this unless you mean it.";
this.seen = {};
this.desclines = {};
"The default is that slate's inherit the 'remembered' from their parent. This means, though, that they're initially blank but have to be 'reset' to fire up. See :do_reset";
"this.remembered = {}";
this.busy = 0;
this.stack = {};
this.watching = {};
this.controlled = #-1;
pass(@args);
endif
"Last modified Fri Feb  6 08:31:56 1998 CST by Wizard (#2).";
.
#131:17
msg = tostr(@args);
player:tell($string_utils:pronoun_sub(msg, $you));
if (this.location != player)
this.location:announce($string_utils:pronoun_sub(msg));
endif
return;
"announcing only to watching";
if (watching = setremove($set_utils:intersection(this.watching, this.location:contents()), player))
msg = $string_utils:pronoun_sub(msg);
for x in (watching)
x:tell(msg);
endfor
endif
"Last modified Fri Feb  6 08:31:56 1998 CST by Wizard (#2).";
.
#131:18
return this.stack && this.stack[1][1..3];
"Last modified Fri Feb  6 08:31:56 1998 CST by Wizard (#2).";
.
#131:19
return this.stack && index("02", this.stack[1][4][1]);
"Last modified Fri Feb  6 08:31:56 1998 CST by Wizard (#2).";
.
#131:20
if ((!argstr) || ((dobj == this) && (!prepstr)))
this:_tell_desc(player, 0);
elseif (which = this:match_choice((($code_utils:short_prep(prepstr) == "on") && (iobj == this)) ? dobjstr | argstr))
where = $gopher:parse(this.value[which]);
if (index("02", where[4][1]))
this:announce_op("%N %<reads> '", this.desclines[which], "' on the %t.");
$gopher:show_text(player, 0, 0, @where);
player:tell("-------");
else
player:tell("Item '", this.desclines[which], "' isn't text and can't be read.");
endif
else
player:tell("Read what?");
endif
"Last modified Fri Feb  6 08:31:56 1998 CST by Wizard (#2).";
.
#131:21
if ((player == this.owner) || player.wizard)
this.locked = verb == "lock";
this:announce_op("%N %<", $string_utils:lowercase(verb), "s> %t.");
else
player:tell("Sorry, but only the owner may ", verb, " ", this.name);
endif
"Last modified Fri Feb  6 08:31:56 1998 CST by Wizard (#2).";
.
#131:22
return this.value;
"don't update slates";
"Last modified Fri Feb  6 08:31:57 1998 CST by Wizard (#2).";
.
#131:23
if (this:busy("updating", 1))
return;
endif
this:announce_op("%N %<updates> %t.");
if (this.stack)
$gopher:clear_cache(@this.stack[1]);
endif
this:set_pointer(@this.stack);
"Last modified Fri Feb  6 08:31:57 1998 CST by Wizard (#2).";
.
#131:24
if (this:_textp())
return this.value;
else
text = {};
for x in (this.value)
parse = $gopher:parse(x);
sel = parse[4];
text = {@text, "Type=" + sel[1], "Name=" + sel[2..length(sel)], "Path=" + parse[3], "Host=" + parse[1], "Port=" + tostr(parse[2]), "#"};
endfor
return text;
endif
"Last modified Fri Feb  6 08:31:57 1998 CST by Wizard (#2).";
.
#131:25
"after a selection is made, this verb is used to show the results; usually to 'player'";
inhere = $object_utils:isa(this.location, $room) ? this.location:contents() | {player};
for x in (this.watching = setadd(this.watching, player))
$command_utils:suspend_if_needed(0);
if (x in inhere)
this:_tell_desc(x, this.length, player != x);
else
this.watching = setremove(this.watching, x);
endif
endfor
"Last modified Fri Feb  6 08:31:57 1998 CST by Wizard (#2).";
.
#131:26
was = player in this.watching;
this.watching = (verb == "watch") ? setadd(this.watching, player) | setremove(this.watching, player);
is = player in this.watching;
if (was == is)
player:tell("You already were ", (verb == "watch") ? "watching" | "ignoring", " ", this:title(), ".");
elseif (this.location == player)
player:tell("You start to ", verb, " ", this:title(), ".");
else
$you:say_action(("%N %<starts> to " + verb) + " %t.");
endif
"Last modified Fri Feb  6 08:32:46 1998 CST by Wizard (#2).";
.
#131:27
if (!valid(iobj))
return player:tell("I don't see '", iobjstr, "' here.");
endif
$you:say_action("%N %<shows> %t to %i.");
this:_tell_desc(iobj, this.length, 1);
"Last modified Fri Feb  6 08:32:46 1998 CST by Wizard (#2).";
.
#131:28
if (this.busy)
if (this.busy[1] > time())
return 1;
else
this.busy = 0;
endif
endif
return 0;
"Last modified Fri Feb  6 08:32:46 1998 CST by Wizard (#2).";
.
#131:29
if (this.controlled == player)
player:tell("You are already controlling ", this:title(), ".");
return;
endif
from = valid(this.controlled) ? (" from " + this.controlled:title()) + "." | ".";
if (this.location != player)
this.location:announce_all_but({player}, $string_utils:pronoun_sub("%N takes the controls of %t"), from);
endif
player:tell("You take the controls of ", this:title(), from);
this.controlled = player;
"Last modified Fri Feb  6 08:32:46 1998 CST by Wizard (#2).";
.
#131:30
if (this.controlled == player)
$you:say_action("%N %<releases> the controls of %t.");
this.controlled = #-1;
else
player:tell("You weren't holding the controls of ", this.name, ".");
endif
"Last modified Fri Feb  6 08:32:46 1998 CST by Wizard (#2).";
.
#131:31
"is this locked?";
if (this.locked)
return "locked";
elseif (valid(this.controlled) && (this.controlled != args[1]))
if (this.location in {this.controlled, this.controlled.location})
return "controlled by " + this.controlled.name;
else
this.controlled = #-1;
endif
endif
return 0;
"Last modified Fri Feb  6 08:32:46 1998 CST by Wizard (#2).";
.
#131:32
"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.focus_object == this) && (this.location in {player, player.location}))
vrb = args[1];
dlist = args[2];
plist = args[3];
ilist = args[4];
if (((vrb in {"pick", "jump", "goto", "details", "remember", "forget", "delete", "next", "prev", "previ", "previo", "previou", "previous"}) && ("none" in plist)) && ("none" in ilist))
return 1;
elseif (((vrb in {"read", "ignore", "watch"}) && ("none" in dlist)) && ("none" in plist))
return 1;
elseif (((vrb in {"show"}) && ("none" in dlist)) && ("at/to" in plist))
return 1;
elseif ((vrb in {"reset", "stack", "mailme", "lock", "unlock", "update", "control", "release"}) && (!("on top of/on/onto/upon" in plist)))
return 1;
elseif (((vrb in {"pop", "back"}) && ("none" in dlist)) && (("none" in plist) || ("for/about" in plist)))
return 1;
endif
endif
return pass(@args);
"Last modified Fri Feb  6 08:32:46 1998 CST by Wizard (#2).";
.
#131:33
"This is a JaysHouseMOO verb -- probably doesn't work on other MOOs without a 'focus' object.";
if (valid(player:set_focus_object(this)))
$you:say_action(this.work_with_msg);
else
player:tell("You just can't seem to focus on that.");
endif
"Last modified Fri Feb  6 08:32:47 1998 CST by Wizard (#2).";
.
#131:34
"mailme note";
if ((caller_perms() != player) && (caller != player))
return player:tell("Someone tried to mail you some text, but it didn't work.");
endif
if (!player.email_address)
return player:tell("Sorry, you don't have a registered email address.");
endif
if ((!argstr) || ((dobj == this) && (!prepstr)))
where = this.stack[1];
elseif (which = this:match_choice((($code_utils:short_prep(prepstr) == "on") && (iobj == this)) ? dobjstr | argstr))
where = $gopher:parse(this.value[which]);
endif
if (where)
player:tell("Mailing ", this:location_string(where), " to ", player.email_address, ".");
text = $gopher:_mail_text(where);
player:tell("... ", length(text), " lines ...");
text = {tostr("(Mail initiated by ", player.name, " (", player, ") connected from ", $string_utils:connection_hostname(connection_name(player)), " using ", this.name, ")"), @text};
suspend(0);
result = $network:sendmail(player.email_address, this:location_string(where), @text);
if (result == 0)
player:tell("Mail sent successfully.");
else
player:tell("Mail sending error: ", result, ".");
endif
else
player:tell("Sorry, can't mail this.");
endif
"Last modified Fri Feb  6 08:32:47 1998 CST by Wizard (#2).";
.
#131:35
"used by _tell_desc for prefix & suffix lines";
args[1]:tell("------- ", $string_utils:left($string_utils:pronoun_sub(tostr(@listdelete(args, 1), " ")), args[1]:linelen(), "-"));
"Last modified Fri Feb  6 08:32:47 1998 CST by Wizard (#2).";
.
#131:36
if (!this.stack)
return 1;
endif
menu = this.stack[1];
if (args)
if (length(menu) > 4)
this.stack[1][5] = args[1];
else
this.stack[1] = {@{@menu, "", "", "", ""}[1..4], args[1]};
endif
elseif (length(menu) > 4)
return menu[5];
else
return 1;
endif
"Last modified Fri Feb  6 08:32:47 1998 CST by Wizard (#2).";
.
#131:37
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Fri Feb  6 08:48:08 1998 CST by Wizard (#2).";
.
#132:0
"Usage: get(site, port, selection)";
"returns a list of strings, or an error if it couldn't connect. Results are cached.";
value = this:get_unparsed(@args);
if (x = "     References from this document:-" in value)
desc = value[1..x - 1];
links = value[x + 1..length(value)];
elseif (x = "     There are no references from this document." in value)
desc = value[1..x - 1];
links = {};
else
desc = value;
links = {};
endif
cnt = 1;
linksdesc = {};
$command_utils:suspend_if_needed(0);
value = {};
for i in (links)
$command_utils:suspend_if_needed(0);
if (i)
if (0)
value = {@value, i[index(i, " ") + 1..length(i)]};
endif
value = {@value, i[index(i, "+") + 1..rindex(i, "+") - 1]};
endif
endfor
return {desc, value};
"Last modified Fri Feb  6 08:33:36 1998 CST by Wizard (#2).";
.
#132:1
"Usage:  get_now(site, port, message)";
"Returns a list of strings, or an error if we couldn't connect.";
if (!this:trusted(caller_perms()))
return E_PERM;
endif
opentime = time();
con = $network:open("mcmuse.mc.maricopa.edu", 90);
opentime = time() - opentime;
if (typeof(con) == ERR)
return con;
endif
notify(con, args[1]);
results = {};
count = this.limit;
"perhaps this isn't necessary, but if a gopher source is slowly spewing things, perhaps we don't want to hang forever -- perhaps this should just fork a process to close the connection instead?";
now = time();
timeout = 30;
end = "^%.$";
if ((length(args) == 4) && (args[4][1] == "2"))
end = "^[2-9]";
endif
while ((((typeof(string = read(con)) == STR) && (!match(string, end))) && ((count = count - 1) > 0)) && ((now + timeout) > (now = time())))
if (string && (string[1] == "."))
string = string[2..length(string)];
endif
results = {@results, string};
endwhile
$network:close(con);
if (opentime > 0)
"This is to keep repeated calls to $network:open to 'slow responding hosts' from totally spamming.";
suspend(0);
endif
return results;
"Last modified Fri Feb  6 08:33:36 1998 CST by Wizard (#2).";
.
#132:2
"Usage: get(site, port, selection)";
"returns a list of strings, or an error if it couldn't connect. Results are cached.";
request = args[1];
while ((index = request in this.cache_requests) && (this.cache_times[index] > time()))
if (typeof(result = this.cache_values[index]) != NUM)
return result;
endif
if ($code_utils:task_valid(result))
"spin, let other process getting same data win, or timeout";
suspend(1);
else
"well, other process crashed, or terminated, or whatever.";
this.cache_times[index] = 0;
endif
endwhile
if (!this:trusted(caller_perms()))
return E_PERM;
endif
while (this.cache_times && (this.cache_times[1] < time()))
$command_utils:suspend_if_needed(0);
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";
endwhile
$command_utils:suspend_if_needed(0);
this:cache_entry(request);
value = this:get_now(@args);
$command_utils:suspend_if_needed(0);
index = this:cache_entry(request);
this.cache_times[index] = time() + ((typeof(value) == ERR) ? 120 | 1800);
this.cache_values[index] = value;
return value;
"Last modified Fri Feb  6 08:33:37 1998 CST by Wizard (#2).";
.
#132:3
if (!caller_perms().wizard)
return E_PERM;
endif
this.cache_values = {};
this.cache_requests = {};
this.cache_times = {};
"Last modified Fri Feb  6 08:33:38 1998 CST by Wizard (#2).";
.
#133:0
"Copyright (C) 2000, Jason Nolan and the University of Toronto";
"Main project server page: lists the vital information about the currently active projects.";
user = args[1];
body = {("<H1 ALIGN=\"CENTER\">" + $network.moo_name) + " Virtual Assignment Server</H1>"};
body = {@body, "<CENTER><TABLE BORDER=1 CELLPADDING=5>"};
body = {@body, "<TR>", "<TH>Project Title</TH>", "<TH>Course</TH>", "<TH>Designer</TH>", "<TH>Participants</TH>", "<TH>Designer</TH>", "</TR>"};
sections = {};
names = {};
for x in (children($assignment))
if (!x.archived)
names = {@names, x.title};
lines = {"<TR STYLE=\"border-top-width: thick\">"};
lines = {@lines, tostr("<TD ALIGN=\"CENTER\"><A HREF=\"/", tonum(x), "/\">", x.title, "</A></TD>")};
if (x.course)
lines = {@lines, ("<TD ALIGN=\"CENTER\">" + x.course) + "</TD>"};
else
lines = {@lines, "<TD>&nbsp;</TD>"};
endif
if (x.show_designer_realname && x.designer)
lines = {@lines, x.designer};
if (x.designer_email)
lines[$] = tostr("<A HREF=\"mailto:", x.designer_email, "\">", lines[$], "</A>");
endif
lines[$] = ("<TD ALIGN=\"CENTER\">" + lines[$]) + "</TD>";
elseif (x.show_designer_mooname)
lines = {@lines, tostr("<TD ALIGN=\"CENTER\"><A HREF=\"/", tonum(x.owner), "/\">", x.owner.name, "</A></TD>")};
else
lines = {@lines, "<TD>&nbsp;</TD>"};
endif
lines = {@lines, tostr("<TD ALIGN=\"CENTER\"><A HREF=\"/", tonum(x), "/student_edit.html\">[Edit]</A></TD>")};
lines = {@lines, tostr("<TD ALIGN=\"CENTER\"><A HREF=\"/", tonum(x), "/admin_edit.html\">[Edit]</A><BR>")};
lines = {@lines, tostr("<A HREF=\"/", tonum(x), "/admin_manage.html\">[Manage]</A></TD>")};
lines = {@lines, "</TR>"};
if (x.desc)
lines = {@lines, "<TR>", ("<TD STYLE=\"border-top-width: 0px\" COLSPAN=5><B>Brief Description:</B> " + x.desc[1]) + "</TD>", "</TR>"};
endif
sections = {@sections, lines};
$command_utils:suspend_if_needed(0);
endif
endfor
sections = $list_utils:sort(sections, names);
body = {@body, @$list_utils:flatten(sections)};
body = {@body, "</TABLE>"};
body = {@body, tostr("<FORM><INPUT TYPE=\"BUTTON\" VALUE=\"Create a New Project\" onClick=\"javascript:document.location.pathname='/", tonum(this), "/project_creation.html';\"></FORM>")};
if (this.uploads_enabled)
body = {@body, tostr("<FORM><INPUT TYPE=\"BUTTON\" VALUE=\"Upload Files\" onClick=\"javascript:document.location.pathname='/", tonum(this), "/upload.html';\"></FORM>")};
body = {@body, tostr("<FORM><INPUT TYPE=\"BUTTON\" VALUE=\"Manage Files\" onClick=\"javascript:document.location.pathname='/", tonum(this), "/manage.html';\"></FORM>")};
endif
body = {@body, "</CENTER>"};
body = this:build(user, body, $network.MOO_name + " Project Listing");
return body;
"Last modified Fri Apr 13 19:09:44 2001 CDT by Wizard (#2).";
.
#133:1
"Copyright (C) 2000, Jason Nolan and the University of Toronto";
"Displays a page which allows certain users to create a new project from scratch.";
user = args[1];
if (!caller_perms().wizard)
return E_PERM;
elseif ((!user.designer) && (!$perm_utils:controls(user, this)))
return {"You are not currently authorized to create a new project. If you think this is incorrect, please contact a member of the wizard staff."};
elseif (!$quota_utils:creation_permitted(user))
return {"You do not have sufficient quota to create a new project. If you believe this message to be in error, please contact one of the wizard staff."};
endif
vars = args[2][1];
vals = args[2][2];
if (vals)
player = user;
newobj = $recycler:_create($assignment, user);
move(newobj, user);
if (vals[1])
newobj.title = vals[1];
$building_utils:set_names(newobj, vals[1]);
endif
if (vals[2])
newobj.course = vals[2];
endif
if (vals[3] && (toint(vals[3]) != 0))
newobj.numsections = vals[3];
endif
if (vals[4])
(typeof(vals[4]) == STR) ? vals[4] = {vals[4]} | 0;
newobj.desc = vals[4];
endif
return {"<HTML><HEAD>", tostr("<META HTTP-EQUIV=\"refresh\" CONTENT=\"0;url=/", tonum(newobj), "/admin_edit.html\">"), "</HEAD></HTML>"};
else
body = {"<H1 ALIGN=\"CENTER\">Create a New Project</H1>"};
body = {@body, "<P>This page will help you to create a new project. Fill in the blanks below with the basic information we need to create your project. <B>Remember, you can always change them later.</B></P>"};
body = {@body, "<HR>"};
body = {@body, "<CENTER>"};
body = {@body, "<FORM METHOD=\"POST\" ENCTYPE=\"multipart/form-data\">"};
body = {@body, "<P>What is the title of this assignment?<BR>"};
body = {@body, "<INPUT TYPE=\"TEXT\" SIZE=75 NAME=\"title\"></P>"};
body = {@body, "<P>What course (if any) is this project associated with?<BR>"};
body = {@body, "<INPUT TYPE=\"TEXT\" SIZE=75 NAME=\"course\"></P>"};
body = {@body, "<P>How many sections would you like to have in your assignment? (Don't worry if you aren't sure what this means yet.)"};
body = {@body, "<INPUT TYPE=\"TEXT\" SIZE=2 VALUE=\"1\" NAME=\"numsections\"></P>"};
body = {@body, "<P>Enter a description of your project. The first line of this description will appear in the main project listing window.<BR>"};
body = {@body, "<TEXTAREA COLS=75 ROWS=10 WRAP=\"soft\" NAME=\"desc\">"};
body = {@body, "</TEXTAREA></P>"};
body = {@body, "<CENTER><INPUT TYPE=\"BUTTON\" VALUE=\"<<< Back\" onClick=\"javascript:history.go(-1);\"><INPUT TYPE=\"SUBMIT\" VALUE=\"Create This Project!\"></CENTER>"};
body = {@body, "</FORM>"};
body = {@body, "</CENTER>"};
body = this:build(user, body, "Create a New Project");
return body;
endif
"Last modified Fri Apr 13 19:10:05 2001 CDT by Wizard (#2).";
.
#133:2
"Copyright (C) 2000, Jason Nolan and the University of Toronto";
"Only allow in $assignments.";
if ($object_utils:isa(args[1], $assignment))
return 1;
else
return 0;
endif
"Last modified Fri Jun  9 15:19:52 2000 CDT by Wizard (#2).";
.
#133:3
"Copyright (C) 2000, Jason Nolan and the University of Toronto";
"Frontend to a cgi-bin script which allows users to upload files to the webserver.";
user = args[1];
username = user.name;
if (!this.uploads_enabled)
return {"Sorry, but this utility is disabled on this MOO."};
elseif ($object_utils:isa(user, $guest))
return {"Sorry, but guests may not upload files."};
endif
"Compute the file and directory limits, possibly customized to the user.";
if ($object_utils:has_property(user, "filelimit") && property_info(user, "filelimit")[1].wizard)
filelimit = user.filelimit;
else
filelimit = this.filelimit;
endif
if ($object_utils:has_property(user, "dirlimit") && property_info(user, "dirlimit")[1].wizard)
dirlimit = user.dirlimit;
else
dirlimit = this.dirlimit;
endif
mooname = $string_utils:lowercase(strsub($network.MOO_name, " ", "_"));
"Start assembling the body of the page.";
body = {("<H3 ALIGN=\"CENTER\">" + $network.MOO_name) + " VASE File Upload</H3>"};
body = {@body, tostr("Welcome to ", $network.MOO_name, "'s file upload service. This utility allows you to upload images, sounds, and other files for use with your ", $network.MOO_name, " projects. Before you begin, please review the rules for this service. ")};
body = {@body, "<font size=\"-1\"><UL>"};
if (filelimit)
body = {@body, ("<LI>Files may not exceed " + tostr(filelimit / 1024)) + " kilobytes in size.</LI>"};
if (dirlimit)
body = {@body, ("<LI>The total of all files uploaded to our server may not exceed " + tostr(dirlimit / 1024)) + " kilobytes in size.</LI>"};
endif
body = {@body, ("<LI>If you think an exception to the above policies is warranted, please contact one of the " + $network.MOO_name) + " administrators directly.</LI>"};
endif
body = {@body, "<LI>For compatibility reasons, images should be in GIF or JPG format, and sounds should be in MIDI or WAV format.</LI>"};
body = {@body, ("<LI>This storage space is for use ONLY with " + $network.MOO_name) + "-related projects. If you need space for your personal webpages, you'll have to look elsewhere.</LI>"};
body = {@body, "<LI>Indecent or illegal material is not allowed.</LI>"};
body = {@body, ("<LI>Observing these rules is a requirement of participation in " + $network.MOO_name) + ".</LI>"};
body = {@body, "</UL></font>"};
body = {@body, tostr("<P>Want to list your files, rename your files, delete your files, or check your quota? Check out the <a href=\"/", tonum(this), "/manage.html\">file management</a> page.</P>")};
body = {@body, "<HR width=75%>"};
"Form method must be spelled in ALL CAPS *POST*, not post.";
body = {@body, tostr("<FORM METHOD=\"POST\" ENCTYPE=\"multipart/form-data\" ACTION=\"http://", $network.site, "/", this.script_path, "\">")};
body = {@body, "<OL><LI> Enter the name you want the file to be stored as. Make sure you use the the correct file suffix, either .jpg, .gif, .mid, or .wav. <INPUT TYPE=\"text\" NAME=\"filename\">"};
body = {@body, "</LI><LI>Click on the browse button to locate a file on your own computer that you would like to upload:"};
body = {@body, ("<INPUT TYPE=\"HIDDEN\" NAME=\"mooname\" VALUE=\"" + mooname) + "\">"};
body = {@body, ("<INPUT TYPE=\"HIDDEN\" NAME=\"user\" VALUE=\"" + tostr(tonum(user))) + "\">"};
body = {@body, ("<INPUT TYPE=\"HIDDEN\" NAME=\"username\" VALUE=\"" + tostr(username)) + "\">"};
body = {@body, "<INPUT TYPE=\"HIDDEN\" NAME=\"do_me\" VALUE=\"upload\">"};
body = {@body, ("<INPUT TYPE=\"HIDDEN\" NAME=\"filelimit\" VALUE=\"" + tostr(filelimit)) + "\">"};
body = {@body, ("<INPUT TYPE=\"HIDDEN\" NAME=\"dirlimit\" VALUE=\"" + tostr(dirlimit)) + "\">"};
body = {@body, ("<INPUT TYPE=\"HIDDEN\" NAME=\"obj_num\" VALUE=\"" + tostr(tonum(this))) + "\">"};
body = {@body, ("<INPUT TYPE=\"HIDDEN\" NAME=\"port\" VALUE=\"" + tostr($network.webport)) + "\">"};
body = {@body, ("<INPUT TYPE=\"HIDDEN\" NAME=\"virtual_host\" VALUE=\"" + $network.site) + "\">"};
body = {@body, ("<INPUT TYPE=\"HIDDEN\" NAME=\"wizmail\" VALUE=\"" + $network.errors_to_address) + "\">"};
body = {@body, "<INPUT TYPE=\"FILE\" NAME=\"file\">"};
body = {@body, "</LI><LI> When you have chosen the name you want, and browsed for the file, click: <INPUT TYPE=\"SUBMIT\" VALUE=\"Upload!\">"};
body = {@body, "</LI></OL><HR width=75%>"};
body = {@body, "This utility is a component of VASE, Copyright 2001 Jason Nolan (jason.nolan@utoronto.ca). Code thanks to Emma, Traveller, Michel, and Jason."};
body = this:build(user, body, $network.MOO_name + " File Upload");
return body;
"Last modified Fri Apr 13 19:10:05 2001 CDT by Wizard (#2).";
.
#133:4
"Copyright (C) 2000, Jason Nolan and the University of Toronto";
"Frontend to a cgi-bin script which allows users to list, rename, and delete their files.";
user = args[1];
username = user.name;
if (!this.uploads_enabled)
return {"Sorry, but this utility is disabled on this MOO."};
elseif ($object_utils:isa(user, $guest))
return {"Sorry, but guests may not mananage files."};
endif
"Compute the file and directory limits, possibly customized to the user.";
if ($object_utils:has_property(user, "filelimit") && property_info(user, "filelimit")[1].wizard)
filelimit = user.filelimit;
else
filelimit = this.filelimit;
endif
if ($object_utils:has_property(user, "dirlimit") && property_info(user, "dirlimit")[1].wizard)
dirlimit = user.dirlimit;
else
dirlimit = this.dirlimit;
endif
mooname = $string_utils:lowercase(strsub($network.MOO_name, " ", "_"));
"Create the standard variables that will be used in every form...";
variables = {};
variables = {@variables, ("<INPUT TYPE=\"HIDDEN\" NAME=\"mooname\" VALUE=\"" + mooname) + "\">"};
variables = {@variables, ("<INPUT TYPE=\"HIDDEN\" NAME=\"user\" VALUE=\"" + tostr(tonum(user))) + "\">"};
variables = {@variables, ("<INPUT TYPE=\"HIDDEN\" NAME=\"username\" VALUE=\"" + tostr(username)) + "\">"};
variables = {@variables, ("<INPUT TYPE=\"HIDDEN\" NAME=\"filelimit\" VALUE=\"" + tostr(filelimit)) + "\">"};
variables = {@variables, ("<INPUT TYPE=\"HIDDEN\" NAME=\"dirlimit\" VALUE=\"" + tostr(dirlimit)) + "\">"};
variables = {@variables, ("<INPUT TYPE=\"HIDDEN\" NAME=\"obj_num\" VALUE=\"" + tostr(tonum($assignment_server))) + "\">"};
variables = {@variables, ("<INPUT TYPE=\"HIDDEN\" NAME=\"port\" VALUE=\"" + tostr($network.webport)) + "\">"};
variables = {@variables, ("<INPUT TYPE=\"HIDDEN\" NAME=\"virtual_host\" VALUE=\"" + tostr($network.site)) + "\">"};
variables = {@variables, ("<INPUT TYPE=\"HIDDEN\" NAME=\"wizmail\" VALUE=\"" + $network.errors_to_address) + "\">"};
"Start assembling the body of the page.";
body = {("<H3 ALIGN=\"CENTER\">" + $network.MOO_name) + " VASE File Management</H3>"};
body = {@body, tostr("Welcome to ", $network.MOO_name, "'s file management service. This utility allows you to manage images, sounds, and other files for use with your ", $network.MOO_name, " projects.  ")};
body = {@body, "<UL><font size=\"-1\">"};
if (filelimit)
body = {@body, ("<LI>Files may not exceed " + tostr(filelimit / 1024)) + " kilobytes in size.</LI>"};
if (dirlimit)
body = {@body, ((("<LI>The total of all files uploaded to our server may not exceed " + tostr(dirlimit / 1024)) + " kilobytes in size. If you need more space, discuss your needs with one of the ") + $network.MOO_name) + " administrators directly.</LI>"};
endif
endif
body = {@body, "<LI>Images should be in GIF or JPG format, and sounds should be in MIDI, WAV or MP3 format.</LI>"};
body = {@body, ("<LI>This storage space is for use ONLY with " + $network.MOO_name) + "-related projects. If you need space for your personal webpages, use other resources.</LI>"};
body = {@body, ("<LI>Indecent or illegal material is not allowed in " + $network.MOO_name) + ".<font></LI>"};
body = {@body, "</UL>"};
body = {@body, "<HR width=75%>"};
body = {@body, "<CENTER>"};
"Start creating the various buttons...";
"Form method must be spelled in ALL CAPS *POST*, not post.";
"Upload _ link to $assignment_server";
if (this.uploads_enabled)
body = {@body, tostr("<FORM NAME=\"UPLOAD\"><INPUT TYPE=\"BUTTON\" VALUE=\"Upload Files\" onClick=\"javascript:document.location.pathname='/", tonum(this), "/upload.html';\"></FORM>")};
endif
"View - do emma.cgi?do_me=view";
body = {@body, ((("<FORM METHOD=\"POST\" ACTION=\"http://" + $network.site) + "/") + this.script_path) + "\">"};
body = {@body, @variables};
body = {@body, "<INPUT TYPE=\"HIDDEN\" NAME=\"do_me\" VALUE=\"view\">"};
body = {@body, tostr("<INPUT TYPE=\"SUBMIT\" VALUE=\"List Files\"></FORM>")};
"Rename - do emma.cgi?do_me=rename";
body = {@body, ((("<FORM METHOD=\"POST\" ACTION=\"http://" + $network.site) + "/") + this.script_path) + "\">"};
body = {@body, @variables};
body = {@body, "<INPUT TYPE=\"HIDDEN\" NAME=\"do_me\" VALUE=\"rename\">"};
body = {@body, tostr("<INPUT TYPE=\"SUBMIT\" VALUE=\"Rename Files\"></FORM>")};
"Delete - do emma.cgi?do_me=delete";
body = {@body, ((("<FORM METHOD=\"POST\" ACTION=\"http://" + $network.site) + "/") + this.script_path) + "\">"};
body = {@body, @variables};
body = {@body, "<INPUT TYPE=\"HIDDEN\" NAME=\"do_me\" VALUE=\"delete\">"};
body = {@body, tostr("<INPUT TYPE=\"SUBMIT\" VALUE=\"Delete Files\"></FORM>")};
"Check Quota - do emma.cgi?do_me=quota";
body = {@body, ((("<FORM METHOD=\"POST\" ACTION=\"http://" + $network.site) + "/") + this.script_path) + "\">"};
body = {@body, @variables};
body = {@body, "<INPUT TYPE=\"HIDDEN\" NAME=\"do_me\" VALUE=\"quota\">"};
body = {@body, tostr("<INPUT TYPE=\"SUBMIT\" VALUE=\"Check Quota\"></FORM>")};
body = {@body, "</CENTER>"};
body = {@body, "<HR width=75%>"};
body = {@body, "This utility is a component of VASE, Copyright 2001 Jason Nolan (jason.nolan@utoronto.ca). Code thanks to Emma, Traveller, Michel and Jason."};
body = this:build(user, body, $network.MOO_name + " File Management");
return body;
"Last modified Fri Apr 13 19:10:05 2001 CDT by Wizard (#2).";
.
#134:0
"Copyright (C) 1998, Jan Rune Holmevik";
if (dobjstr)
text = dobjstr;
else
text = $command_utils:read("your sentence please. It can be as long as you like, but do not hit the return key until you have finished typing what you want,");
endif
if (!this.anonymous)
author = (" (" + player.name) + ")";
text = text + author;
endif
this:set_text({@this.text, text});
player:tell("You add a line to ", this.name);
player.location:announce(player.name, " adds a line to ", this.name);
"Last modified Fri Feb  6 08:40:50 1998 CST by Wizard (#2).";
.
#134:1
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Fri Feb  6 08:48:09 1998 CST by Wizard (#2).";
.
#135:0
"Copyright (C) 1995, Jan Rune Holmevik";
if (!$object_utils:isa(this.location, $room))
return player:tell("You need to drop ", this.name, " in a room before you can use it.");
endif
player:tell("You take a closer look at the ", this.name);
this.location:announce(player.name, " consults ", this.name, ".");
header = "- Room ---------------------------------- Exit name ------------------";
footer = "--------------- Type name of exit or '@go room name' -----------------";
su = $string_utils;
rooms = {};
exits = {};
counter = 0;
line_number = 1;
for exit in (this.location.exits)
$command_utils:suspend_if_needed(0);
exits = {@exits, exit};
rooms = {@rooms, exit.dest.name};
counter = counter + 1;
endfor
sort_list = $list_utils:sort(exits, rooms);
for n in (sort_list)
$command_utils:suspend_if_needed(0);
if (line_number == 1)
player:tell(header);
endif
player:tell("  ", su:left(n.dest.name, 40), su:left(n.name, 27));
counter = counter - 1;
line_number = line_number + 1;
if ((line_number == this.page_length) || (counter == 0))
player:tell(footer);
if (counter > 0)
if (!$command_utils:yes_or_no(tostr("Do you wish to see remaining entries (", counter, ")?")))
player:tell("Okay, listing aborted");
return;
endif
endif
line_number = 1;
endif
endfor
"Last modified Fri Feb  6 08:41:56 1998 CST by Wizard (#2).";
.
#135:1
if (caller_perms().wizard)
pass();
move(this, $tool_box);
endif
"Last modified Fri Feb  6 08:48:08 1998 CST by Wizard (#2).";
.
#136:0
"Copyright (C) 1998, Jan Rune Holmevik";
"This verb allow folks to post a note to the note board. One can either post an existing note object, or create a new note to be posted by typing the word 'new' instead of the name of an existing note. If the object is private, or restricted, only the owner, or registered builders respectively may post to it.";
if (this.private && ((player != this.owner) || player.wizard))
player:tell(this:private_msg());
elseif (this.restricted && (!$object_utils:isa(player, $builder)))
player:tell(this:restricted_msg());
else
if (dobjstr == "new")
note = this:create_note();
if (note == $nothing)
return;
endif
else
note = dobj;
"Check if the specified note is valid";
if ($command_utils:object_match_failed(note, dobjstr))
return;
endif
if (!$object_utils:isa(note, $note))
return player:tell("Sorry, ", note.name, " is not a note object.");
else
"Date stamp note";
note.date = time();
endif
endif
if (((dobjstr == "new") || (player == note.owner)) || player.wizard)
move(note, this);
player:tell(this:post_msg());
if (msg = this:opost_msg())
player.location:announce(player.name, " ", msg);
endif
else
player:tell("Posting failed.");
endif
endif
"Last modified Thu Apr  8 12:27:50 1999 CDT by Wizard (#2).";
.
#136:1
"===========================================================";
"Copyright (C) 1998-2001, Jan Rune Holmevik";
"===========================================================";
note_number = toint(dobjstr);
if (this:valid(note_number))
note = this.contents[note_number];
if (((note.owner == player) || (this.owner == player)) || player.wizard)
note:moveto(player);
player:tell(this:remove_msg());
if (msg = this:oremove_msg())
player.location:announce(player.name, " ", msg);
endif
else
if ($object_utils:isa(player, $guest))
player:tell("Sorry, but guests cannot remove notes. Please talk to the owner of the note, ", note.owner.name, ".");
else
player:tell("You can only remove your own notes.");
endif
endif
else
player:tell("That is not a valid note number.");
endif
"Last modified Mon Sep 17 10:10:14 2001 CDT by Wizard (#2).";
.
#136:2
"Copyright (C) 1998, Jan Rune Holmevik";
if (args[1] <= length(this.contents))
return 1;
else
return 0;
endif
"Last modified Wed Apr 15 13:07:30 1998 CDT by Wizard (#2).";
.
#136:3
"Copyright (C) 1998, Jan Rune Holmevik";
pass();
notes = this.contents;
su = $string_utils;
if (!notes)
player:tell("There are currently no notes on ", this.name);
else
status = this:check_status();
player:tell();
if (status == "private")
player:tell("Only the owner may post notes on this board.");
elseif (status == "restricted")
player:tell("Only registered users may post notes on this board.");
elseif (status == "public")
player:tell("Public posting allowed.");
endif
player:tell();
player:tell("--- Title ------------------------------ Length ---- Author --------- Date ---");
for i in [1..length(notes)]
$command_utils:suspend_if_needed(0);
subject = notes[i].name;
size = length(notes[i].text);
if (!notes[i].author_name)
author = notes[i].owner.name;
else
author = notes[i].author_name;
endif
posted = $time_utils:time_sub("$2/$3/$y", notes[i].date);
if (length(subject) > 37)
subject = subject[1..36];
endif
if (size > 9999)
len = ">9999 lines";
else
len = tostr(size, " lines");
endif
if (length(author) > 17)
author = author[1..16];
endif
player:tell(su:left(i, 4), su:left(subject, 37), su:left(len, 12), su:left(author, 17), su:right(posted, 8));
endfor
player:tell("------------------------------------------------------------------------------");
endif
"Last modified Wed Apr 15 15:08:38 1998 CDT by Wizard (#2).";
.
#136:4
return (msg = this.(verb)) ? $string_utils:pronoun_sub(msg) | "";
"Last modified Wed Apr 15 13:08:16 1998 CDT by Wizard (#2).";
.
#136:5
"Copyright (C) 1998, Jan Rune Holmevik";
"create a new note";
if (caller != this)
return E_PERM;
endif
if ($object_utils:isa(player, $builder))
quota_source = player;
else
quota_source = this.owner;
endif
new = quota_source:_create($note, quota_source);
if (typeof(new) == ERR)
if ($object_utils:isa(player, $builder))
player:notify(">>>   A L E R T   <<<");
player:notify("Your resource quota is exceeded, so I cannot create any new notes for you at this time. Please check to see if you can free up quota by recycling some of the objects you already own, or type '@measure new me' if you have created more than 10 new objects recently. If this does not help and you still need more quota contact one of the administrators.");
else
player:notify(tostr("I'm sorry but no new notes may be posted on ", this.name, " at this time. Please contact the owner ", this.owner.name));
endif
new = $nothing;
else
subject = $command_utils:read("a subject/title for this note");
player:tell(">> Please type in the text you want to appear on this note");
txt = $command_utils:read_lines();
$building_utils:set_names(new, subject);
new:set_text(txt);
endif
return new;
"Last modified Wed Apr 15 13:08:35 1998 CDT by Wizard (#2).";
.
#136:6
"Copyright (C) 1998, Jan Rune Holmevik";
note_number = tonum(dobjstr);
notes = this.contents;
if (this:valid(note_number))
if (!notes[note_number]: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
player:tell("--------------------------------  Begin ---------------------------------------");
player:tell("Author  : ", notes[note_number].author_name);
player:tell("Subject : ", notes[note_number].name);
player:tell("Posted  : ", $time_utils:time_sub("$2/$3/$y $H:$M $Z", notes[note_number].date));
player:tell();
player:tell_lines(notes[note_number].text);
player:tell("--------------------------------   End  ---------------------------------------");
endif
else
player:tell("That is not a valid note number.");
endif
"Last modified Wed Apr 15 13:08:56 1998 CDT by Wizard (#2).";
.
#136:7
"===========================================================";
"Copyright (C) 1998-2001, Jan Rune Holmevik";
"===========================================================";
if ((player == this.owner) || player.wizard)
title = $encore_utils:make_title("Note Board Configuration Menu");
player:tell_lines(title);
player:tell();
player:tell("Current mode: ", mode = this:check_status());
player:tell();
player:tell("1) Private, only the owner may post.");
player:tell("2) Restricted, only registered users may post.");
player:tell("3) Public, anyone may post.");
player:tell();
ans = toint($command_utils:read("Please enter your choice (1-3), or type Q to exit."));
if (ans == 1)
this.private = 1;
this.restricted = 1;
player:tell("Private mode selected.");
elseif (ans == 2)
this.private = 0;
this.restricted = 1;
player:tell("Restricted mode selected.");
elseif (ans == 3)
this.private = 0;
this.restricted = 0;
player:tell("Public mode selected.");
else
player:tell("Exiting...");
endif
else
return player:tell("Sorry, only the owner may configure ", this.name);
endif
"Last modified Sun Sep 30 13:18:00 2001 CDT by Wizard (#2).";
.
#136:8
"Copyright (C) 1998, Jan Rune Holmevik";
status = "";
if (this.private && this.restricted)
status = "Private";
elseif (this.restricted && (!this.private))
status = "Restricted";
elseif ((!this.private) && (!this.restricted))
status = "Public";
endif
return status;
"Last modified Wed Apr 15 13:10:59 1998 CDT by Wizard (#2).";
.
#136:9
"===========================================================";
"Copyright (C) 1995-2001, Jan Rune Holmevik";
"===========================================================";
user = args[1];
html = pass(@args);
contents = {};
if (this.contents)
titles = this.contents;
lengths = authors = dates = {};
for note in (titles)
suspend(0);
lengths = {@lengths, tostr(length(note.text), " lines")};
if (!note.author_name)
authors = {@authors, note.owner.name};
else
authors = {@authors, note.author_name};
endif
dates = {@dates, $time_utils:time_sub("$2/$3/$y $H:$M $Z", note.date)};
endfor
titles = $encore_web_utils:generate_links(user, titles);
titles = {"<B>Title</B>", @titles};
lengths = {"<B>Length</B>", @lengths};
authors = {"<B>Author</B>", @authors};
dates = {"<B>Posted</B>", @dates};
contents = $encore_web_utils:generate_table(user, {titles, lengths, authors, dates}, this, 1);
contents = {"<P><B>Contents:</B><P>", @contents};
else
contents = {@contents, "<P>It is empty."};
endif
html = $list_utils:append(html, contents);
return html;
"Last modified Fri Apr 13 19:05:33 2001 CDT by Wizard (#2).";
.
#136:10
if (caller_perms().wizard)
pass();
this.private = 0;
this.restricted = 1;
this.design = "jan@LinguaMOO";
move(this, $tool_box);
endif
"Last modified Wed Apr 15 15:10:49 1998 CDT by Wizard (#2).";
.
#137: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;
"Last modified Sun Oct  4 19:46:21 1998 CDT by Jan (#138).";
.
#137: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
"Last modified Sun Oct  4 19:46:21 1998 CDT by Jan (#138).";
.
#137:2
{who} = args;
if (caller != this)
raise(E_PERM);
endif
return this:create_session(who);
"Last modified Sun Oct  4 19:46:21 1998 CDT by Jan (#138).";
.
#137:3
{con} = args;
if (caller == con)
this:destroy_session(con);
endif
"Last modified Sun Oct  4 19:46:21 1998 CDT by Jan (#138).";
.
#137: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
"Last modified Sun Oct  4 19:46:21 1998 CDT by Jan (#138).";
.
#137: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;
"Last modified Sun Oct  4 19:46:21 1998 CDT by Jan (#138).";
.
#137: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
"Last modified Sun Oct  4 19:46:21 1998 CDT by Jan (#138).";
.
#137:7
{major, minor} = args;
return tostr(major, ".", minor);
"Last modified Sun Oct  4 19:46:21 1998 CDT by Jan (#138).";
.
#137:8
{who} = args;
return `who.out_of_band_session ! E_PROPNF => $failed_match';
"Last modified Sun Oct  4 19:46:22 1998 CDT by Jan (#138).";
.
#137: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
"Last modified Sun Oct  4 19:46:22 1998 CDT by Jan (#138).";
.
#137: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
"Last modified Sun Oct  4 19:46:22 1998 CDT by Jan (#138).";
.
#137: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
"Last modified Sun Oct  4 19:46:22 1998 CDT by Jan (#138).";
.
#137:12
return this.registry:(verb)(@args);
"Last modified Sun Oct  4 19:46:22 1998 CDT by Jan (#138).";
.
#137:13
{who, @rest} = args;
if (valid(session = this:session_for(who)))
return session:(verb)(@rest);
endif
"Last modified Sun Oct  4 19:46:22 1998 CDT by Jan (#138).";
.
#137:14
return {@pass(@args), this.package, this.session, this.registry, this.parser, $cord};
"Last modified Sun Oct  4 19:46:22 1998 CDT by Jan (#138).";
.
#138:0
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Generate a form for creation data";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, type} = args;
form = {tostr("<INPUT TYPE=hidden NAME=parent VALUE=\"", toint(type), "\">")};
form = {@form, tostr("<INPUT TYPE=text NAME=name VALUE=\"\" SIZE=20> Name of the new object<P>")};
return form;
"Last modified Fri Apr 13 19:01:27 2001 CDT by Wizard (#2).";
.
#138:1
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Assemble the most important properties for quick and easy editing.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
options = {};
if ((!$object_utils:isa(caller, $player)) && (user == this.owner))
options = {"<OPTION VALUE=\"edit_sharing\">Share This Object"};
endif
options = {@options, "<OPTION VALUE=\"edit_name\">Edit Name and Aliases"};
options = {@options, "<OPTION VALUE=\"edit_url\">Edit Multi-Media Content"};
options = {@options, "<OPTION VALUE=\"edit_icon\">Edit Icon"};
options = {@options, "<OPTION VALUE=\"edit_description\">Edit Description"};
options = {@options, "<OPTION VALUE=\"edit_ascii\">Edit ASCII Art"};
options = {@options, "<OPTION VALUE=\"edit_appearance\">Edit Object Appearance"};
return options;
"Last modified Fri Apr 13 19:02:02 2001 CDT by Wizard (#2).";
.
#138:2
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Verb that generates a contextual menu for an object.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, ?object = this, ?call_spec = "", ?return_functions = 1} = args;
contextual_menu = buttons = functions = {};
if ((((user != $no_one) && $object_utils:isa(object, $encore_web_object)) && user.show_contextual_menu) || (caller == $inventory_manager))
base_url = tostr("http://", $network.site, ":", $network.webport, "/");
image_url = $xpress_client.external_baseurl + $xpress_client.icons_folder;
"----------------------------------------------------------------";
"Insert browser back button. Rooms and inventory manager excluded.";
"----------------------------------------------------------------";
if ((!$object_utils:isa(this, $room)) && (caller != $inventory_manager))
buttons = {@buttons, tostr("<A HREF=\"back\" onClick=\"javascript:history.go(-1)\">", $encore_web_utils:get_icon(user, $xpress_client, "back.gif", "Go Back/View Previous"), "</A>")};
endif
"----------------------------------------------------------------";
"Insert contextual help and info button. Exits and basic rooms excluded";
"----------------------------------------------------------------";
if ($object_utils:isa(object, $thing) || $object_utils:isoneof(object, {$classroom, $moderated_room}))
buttons = {@buttons, tostr("<A HREF=\"", base_url, toint(object), "/examine_html\" onClick=\"ContextSensitiveHelp()\" TARGET=\"", $help_browser:get_frame_name("ContextSensitiveHelp"), "\">", $encore_web_utils:get_icon(user, $help_browser, "help.gif", "About this object"), "</A>")};
if (return_functions)
functions = $list_utils:append(functions, $encore_web_utils:javascript_window_open($help_browser, "ContextSensitiveHelp", "", $help_browser.context_help_width, $help_browser.context_help_height));
endif
endif
"----------------------------------------------------------------";
"Insert contextual take/drop button is applicable";
"Does not currently work in Netscape 4.X";
"----------------------------------------------------------------";
if ($encore_web_utils:can_handle(user, object))
buttons = {@buttons, tostr("<A HREF=\"", base_url, toint(this), (object in user.contents) ? "/drop_html" | "/take_html", (call_spec == "") ? "" | tostr("?", call_spec), "\">", (object in user.contents) ? $encore_web_utils:get_icon(user, $xpress_client, tostr(image_url, "drop.gif"), "Drop this object") | $encore_web_utils:get_icon(user, $xpress_client, tostr(image_url, "take.gif"), "Take this object"), "</A>")};
endif
"----------------------------------------------------------------";
"Insert download via email option for note objects";
"----------------------------------------------------------------";
if ((($network.active && ($network.maildrop != "")) && $object_utils:isa(object, $note)) && (!$object_utils:isa(user, $guest)))
buttons = {@buttons, tostr("<A HREF=\"", base_url, toint(object), "/email_html", (call_spec == "") ? "" | tostr("?", call_spec), "\">", $encore_web_utils:get_icon(user, $xpress_client, tostr(image_url, "email.gif"), "Email contents to me"), "</A>")};
endif
"----------------------------------------------------------------";
"Insert contextual Xpress Edit button if user has permission to edit it";
"----------------------------------------------------------------";
if ($encore_web_utils:permission_to_edit(user, object))
buttons = {@buttons, tostr("<A HREF=\"", base_url, "Xpress_Object_Editor/view.html?", toint(object), "/\" TARGET=\"", $Xpress_Object_Editor:get_frame_name("openXpressEdit"), "\" onClick=\"openXpressEdit();\">", $encore_web_utils:get_icon(user, $xpress_client, tostr(image_url, "xpress_edit.gif"), "Edit this object"), "</A>")};
"Add a window function for opening Xpress Edit";
if (return_functions)
functions = $list_utils:append(functions, $encore_web_utils:javascript_window_open($xpress_object_editor, "openXpressEdit", ""));
endif
endif
if (user == object.owner)
"----------------------------------------------------------------";
"Insert lock/unlock icon, Exits excluded.";
"----------------------------------------------------------------";
if (!$object_utils:isa(object, $exit))
buttons = {@buttons, tostr("<A HREF=\"", base_url, toint(this), "/", (object.key == 0) ? "lock_html" | "unlock_html", (call_spec == "") ? "" | tostr("?", call_spec), "\">", $encore_web_utils:get_icon(user, $xpress_client, tostr(image_url, (object.key == 0) ? "unlocked.gif" | "locked.gif"), tostr((object.key == 0) ? "Lock" | "Unlock", " this object")), "</A>")};
endif
endif
if ((((user == object.owner) && (caller == $inventory_manager)) && (!$object_utils:isa(object, $player))) && (!$object_utils:isa(object, $exit)))
"----------------------------------------------------------------";
"Insert Recycle icon for inventory manager. Exits and player objects excluded.";
"----------------------------------------------------------------";
buttons = {@buttons, tostr("<A HREF=\"", base_url, "Xpress_Object_Editor/recycle_object?", toint(object), (call_spec == "") ? "" | tostr("&", call_spec), "\" onClick=\"return confirmDelete();\">", $encore_web_utils:get_icon(user, $xpress_client, tostr(image_url, "recycle.gif"), "Recycle this object"), "</A>")};
endif
if (buttons != {})
functions = $encore_web_utils:insert_javascript(functions);
endif
endif
if (return_functions)
return {functions, buttons};
else
return buttons;
endif
"Last modified Sun Sep 30 12:46:26 2001 CDT by Wizard (#2).";
.
#138:3
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Assembles the web page based on arguments from the calling verb.";
"Accepts the following arguments: user, body html, alternative page title, javascript onload call, target, frame identity, content type, and doctype";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, body, ?title = this.name, ?onload = "", ?target = "", ?frame_identity = "", ?content_type = $httpd.content_type, ?doctype = this.default_doctype, ?style = 1} = args;
if (caller == $xpress_login)
onload = tostr("onLoad=\"", onload, "\"");
else
if (onload)
onload = tostr("onLoad=\"", onload, ";parent.self.focus();\"");
else
onload = "onLoad=\"parent.self.focus();\"";
endif
endif
head = $encore_web_utils:make_head(user, this, title, target, content_type, style);
if (doctype != this.doctype_frameset)
"Do not insert body tags for framesets";
body = $encore_web_utils:make_body(user, this, body, onload, frame_identity);
endif
html = $encore_web_utils:make_page(user, this, $list_utils:append(head, body), doctype);
return html;
"Last modified Fri Apr 13 19:06:33 2001 CDT by Wizard (#2).";
.
#139: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..$]};
"Last modified Sun Oct  4 19:23:39 1998 CDT by Jan (#138).";
.
#139: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};
"Last modified Sun Oct  4 19:23:39 1998 CDT by Jan (#138).";
.
#139: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
"Last modified Sun Oct  4 19:23:39 1998 CDT by Jan (#138).";
.
#140: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";
"Last modified Sun Oct  4 19:27:57 1998 CDT by Jan (#138).";
.
#140:1
"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";
"Last modified Sun Oct  4 19:27:57 1998 CDT by Jan (#138).";
.
#140:2
"Usage:  :initialize_connection()";
"";
{version} = args;
connection = caller;
messages = $list_utils:slice(this.messages_in);
connection:register_handlers(messages);
"Last modified Sun Oct  4 19:27:57 1998 CDT by Jan (#138).";
.
#140:3
{message} = args;
if ($object_utils:has_callable_verb(this, vname = "handle_" + message))
return vname;
else
return 0;
endif
"Last modified Sun Oct  4 19:27:57 1998 CDT by Jan (#138).";
.
#140:4
connection = caller;
return 0;
"Last modified Sun Oct  4 19:27:58 1998 CDT by Jan (#138).";
.
#140:5
"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
"Last modified Sun Oct  4 19:27:58 1998 CDT by Jan (#138).";
.
#140:6
"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
"Last modified Sun Oct  4 19:27:58 1998 CDT by Jan (#138).";
.
#140:7
{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
"Last modified Sun Oct  4 19:27:58 1998 CDT by Jan (#138).";
.
#140:8
"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
"Last modified Sun Oct  4 19:29:05 1998 CDT by Jan (#138).";
.
#141: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);
"Last modified Sun Oct  4 19:30:31 1998 CDT by Jan (#138).";
.
#141: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
"Last modified Sun Oct  4 19:30:31 1998 CDT by Jan (#138).";
.
#141:2
if (caller == this)
{connection, @rest} = args;
connection:end_negotiation();
endif
"Last modified Sun Oct  4 19:30:31 1998 CDT by Jan (#138).";
.
#142:0
if (caller == this)
return tostr("I", this.next_id = this.next_id + 1);
else
raise(E_PERM);
endif
"Last modified Sun Oct  4 19:33:09 1998 CDT by Jan (#138).";
.
#142: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
"Last modified Sun Oct  4 19:33:09 1998 CDT by Jan (#138).";
.
#142:2
cord = caller;
session = cord.session;
this:send_closed(session, tostr(cord.id));
"Last modified Sun Oct  4 19:33:09 1998 CDT by Jan (#138).";
.
#142:3
{session, id, message, @assocs} = args;
if (caller == this)
$cord:mcp_receive(id, message, assocs);
endif
"Last modified Sun Oct  4 19:33:10 1998 CDT by Jan (#138).";
.
#142:4
{session, id, @rest} = args;
if (caller == this)
$cord:mcp_closed(id);
endif
"Last modified Sun Oct  4 19:33:10 1998 CDT by Jan (#138).";
.
#142: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;
"Last modified Sun Oct  4 19:33:10 1998 CDT by Jan (#138).";
.
#142:6
if (caller == $cord)
return pass(@args);
endif
"Last modified Sun Oct  4 19:33:10 1998 CDT by Jan (#138).";
.
#142: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
"Last modified Sun Oct  4 19:33:10 1998 CDT by Jan (#138).";
.
#142: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
"Last modified Sun Oct  4 19:33:10 1998 CDT by Jan (#138).";
.
#143: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};
"Last modified Sun Oct  4 19:34:44 1998 CDT by Jan (#138).";
.
#143: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
"Last modified Sun Oct  4 19:34:45 1998 CDT by Jan (#138).";
.
#143:2
{data_tag, keyword, @rest} = args;
value = argstr[(index(argstr, keyword) + length(keyword)) + 1..$];
keyword = keyword[1..$ - 1];
return {"*", data_tag, keyword, value};
"Last modified Sun Oct  4 19:34:45 1998 CDT by Jan (#138).";
.
#143: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;
"Last modified Sun Oct  4 19:34:45 1998 CDT by Jan (#138).";
.
#143: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;
"Last modified Sun Oct  4 19:34:45 1998 CDT by Jan (#138).";
.
#143:5
datakey = tostr(random(), this.next_datakey);
this.next_datakey = this.next_datakey + 1;
return datakey;
"Last modified Sun Oct  4 19:34:45 1998 CDT by Jan (#138).";
.
#144: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
"Last modified Sun Oct  4 19:37:49 1998 CDT by Jan (#138).";
.
#144: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}};
"Last modified Sun Oct  4 19:37:49 1998 CDT by Jan (#138).";
.
#144: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
"Last modified Sun Oct  4 19:37:49 1998 CDT by Jan (#138).";
.
#144: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
"Last modified Sun Oct  4 19:37:50 1998 CDT by Jan (#138).";
.
#144: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
"Last modified Sun Oct  4 19:37:50 1998 CDT by Jan (#138).";
.
#144: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
"Last modified Sun Oct  4 19:37:50 1998 CDT by Jan (#138).";
.
#144:6
if (caller != $mcp)
raise(E_PERM);
else
this:send("mcp", {{"version", "2.1"}, {"to", "2.1"}});
endif
"Last modified Sun Oct  4 19:37:50 1998 CDT by Jan (#138).";
.
#144: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
"Last modified Sun Oct  4 19:37:50 1998 CDT by Jan (#138).";
.
#144: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
"Last modified Sun Oct  4 19:37:50 1998 CDT by Jan (#138).";
.
#144:9
{package} = args;
if (assoc = $list_utils:assoc(package, this.packages))
return assoc[2];
endif
"Last modified Sun Oct  4 19:37:50 1998 CDT by Jan (#138).";
.
#144: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};
"Last modified Sun Oct  4 19:37:50 1998 CDT by Jan (#138).";
.
#144: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)
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
"Last modified Sun Oct  4 19:37:51 1998 CDT by Jan (#138).";
.
#144: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
"Last modified Sun Oct  4 19:37:51 1998 CDT by Jan (#138).";
.
#144:13
{message} = args;
if (assoc = $list_utils:passoc(message, this.message_handlers[2], this.message_handlers[1]))
return assoc[2];
endif
"Last modified Sun Oct  4 19:37:51 1998 CDT by Jan (#138).";
.
#144:14
return this.connection;
"Last modified Sun Oct  4 19:37:51 1998 CDT by Jan (#138).";
.
#144:15
{package} = args;
return $mcp.registry:package_name(package);
"Last modified Sun Oct  4 19:37:51 1998 CDT by Jan (#138).";
.
#144:16
{prefix, message} = args;
if (message)
message = (prefix + "-") + message;
else
message = prefix;
endif
return message;
"Last modified Sun Oct  4 19:37:51 1998 CDT by Jan (#138).";
.
#144: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
"Last modified Sun Oct  4 19:37:51 1998 CDT by Jan (#138).";
.
#144:18
if (caller == $mcp.negotiate)
this:_signal_package_waiter(0);
endif
"Last modified Sun Oct  4 19:37:51 1998 CDT by Jan (#138).";
.
#144: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
"Last modified Sun Oct  4 19:37:51 1998 CDT by tester (#138).";
.
#144: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
"Last modified Sun Oct  4 19:37:52 1998 CDT by tester (#138).";
.
#144: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
"Last modified Sun Oct  4 19:37:52 1998 CDT by Jan (#138).";
.
#145: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
"Last modified Sun Oct  4 19:39:59 1998 CDT by Jan (#138).";
.
#145: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
"Last modified Sun Oct  4 19:39:59 1998 CDT by Jan (#138).";
.
#145:2
{name} = args;
if (idx = name in this.package_names)
return this.packages[idx];
else
return $failed_match;
endif
"Last modified Sun Oct  4 19:39:59 1998 CDT by Jan (#138).";
.
#145:3
{package} = args;
if (idx = package in this.packages)
return this.package_names[idx];
else
return "";
endif
"Last modified Sun Oct  4 19:39:59 1998 CDT by Jan (#138).";
.
#145:4
return $list_utils:make_alist({this.package_names, this.packages});
"Last modified Sun Oct  4 19:40:00 1998 CDT by Jan (#138).";
.
#145:5
"Usage:  :init_for_module()";
if ((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
"Last modified Tue Jun  1 11:18:51 1999 CDT by Wizard (#2).";
.
#145:6
corenames = this.core_package_names;
corepackages = {};
for name in (corenames)
corepackages = {@corepackages, this:match_package(name)};
endfor
return {@pass(@args), @corepackages};
"Last modified Sun Oct  4 19:40:00 1998 CDT by Jan (#138).";
.
#145: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.name, " 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.name, " 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
"Last modified Sun Oct  4 19:40:00 1998 CDT by Jan (#138).";
.
#146:0
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
result = this:build(user, {}, "Blank");
return result;
"Last modified Fri Apr 13 18:51:29 2001 CDT by Wizard (#2).";
.
#146:1
"Copyright (C) 1999, sindre.sorensen@rasmus.uib.no";
"returns a frame_name that can be used to refer to this frame in javascript";
{?specified_name = ""} = args;
frame_name = ($network.MOO_name + "_") + (specified_name || this.name);
frame_name = $string_utils:subst(frame_name, {{" ", "_"}, {"-", "_"}, {".", "_"}, {",", "_"}, {":", "_"}});
return frame_name;
"Last modified Tue Apr 13 23:38:09 1999 CDT by Wizard (#2).";
.
#147:0
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Reset object to default values.";
"===========================================================";
if (caller_perms().wizard)
pass();
this.java_client_folder = "mootcan/";
this.java_client_archive = "mootcan-012.jar";
this.java_client_filename = "MOOtcan.class";
this.texts_folder = "texts/";
this.stylesheets_folder = "stylesheets/";
this.external_baseurl = "http://localhost/encore/";
this.on = 1;
this.allow_internet_mail = 0;
this.HTTP_09_enabled = 1;
this.web_text_color = "black";
this.web_link_color = "blue";
this.web_vlink_color = "purple";
this.web_alink_color = "red";
this.java_font_sizes = {9, 10, 11, 12, 13, 14};
this.java_client_width = "100%";
this.java_client_height = "100%";
this.java_client_alignment = "top";
this.java_client_font = "Courier";
this.java_client_font_size = "10";
this.window_close_timeout = "3000";
this.guest_names_and_manners = {"In this MOO you can use your own name instead of an anonymous guest name. The name you choose must be unique, however. If it's being used by someone else, you must choose another name. Also, please note that names must not contain any open spaces."};
this.MOTD_Msg = "";
this.MOTD_viewer_list = {};
this.Screen_Sizes = {{"screen.availWidth,screen.availHeight", "Full Screen"}, {"1024,768", "1024 by 768 pixels"}, {"800,600", "800 by 600 pixels"}, {"640,480", "640 by 480 pixels"}};
this.screen_layouts = {{"vertical_layout", "Vertical Orientation"}, {"horizontal_layout", "Horizontal Orientation"}, {"simple_layout", "Chat Area Only"}};
this.Screen_Divisions = {{"30%,70%", "Talk Area 30%, Web Area 70%"}, {"40%,60%", "Talk Area 40%, Web Area 60%"}, {"50%,50%", "Talk Area 50%, Web Area 50%"}, {"60%,40%", "Talk Area 60%, Web Area 40%"}, {"70%,30%", "Talk Area 70%, Web Area 30%"}};
this.themes = {{"Amber", "amber"}, {"Azure", "azure"}, {"Ice", "ice"}, {"Lemon", "lemon"}, {"Lime", "lime"}, {"Magenta", "magenta"}, {"Poppy", "poppy"}, {"Sky", "sky"}, {"Steel", "steel"}, {"Tangerine", "tangerine"}};
this.icon_sizes = {{"Tiny", 16}, {"Small", 24}, {"Normal", 32}, {"Large", 48}, {"Extra Large", 64}};
this.web_background = "background-gray.jpg";
"--------------------------------------------------------";
"Set default icons for other objects in the Xpress system";
"--------------------------------------------------------";
$room.icon = "room.gif";
$bot.icon = "bot.gif";
$builder.icon = "builder.gif";
$classroom.icon = "classroom.gif";
$container.icon = "container.gif";
$exit.icon = "exit.gif";
$guest.icon = "guest.gif";
$lecture.icon = "lecture.gif";
$xpress_moo_mailer.icon = "mail.gif";
$help_browser.icon = "help.gif";
$moderated_room.icon = "moderated_room.gif";
$news.icon = "news.gif";
$note.icon = "note.gif";
$note_board.icon = "noteboard.gif";
$player.icon = "player.gif";
$prog.icon = "programmer.gif";
$slide_projector.icon = "projector.gif";
$recorder.icon = "recorder.gif";
$thing.icon = "thing.gif";
$webpage.icon = "webpage.gif";
$webprojector.icon = "webprojector.gif";
$wiz.icon = "wizard.gif";
endif
"Last modified Sun Sep 30 12:41:35 2001 CDT by Wizard (#2).";
.
#147:1
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Generates the main enCore Xpress client user screen using three frames";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
user = args[1];
base_url = tostr("http://", $network.site, ":", $network.webport, "/Xpress_client/");
menu = tostr(base_url, "menu.html");
java = tostr(base_url, "java.html");
web = tostr(base_url, "web.html");
division = $string_utils:explode(user.Xpress_screen_division, ",");
talk_area = division[1];
web_area = division[2];
html = {tostr("<FRAMESET COLS=\"50%\" FRAMEBORDER=", this.frameborder, ">")};
html = {@html, tostr("<NOFRAMES><BODY>")};
html = {@html, "<H3>Sorry you need a browser that supports frames to use this system</H3>"};
html = {@html, "</BODY></NOFRAMES>"};
html = {@html, tostr("<FRAMESET COLS=\"50%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("   <FRAMESET ROWS=\"75,100%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("      <FRAME SRC=\"", menu, "\" NAME=\"menu_frame\" NORESIZE SCROLLING=\"no\" MARGINWIDTH=\"0\" MARGINHEIGHT=\"0\" FRAMEBORDER=\"", this.frameborder, "\">")};
if (verb == "vertical_layout")
html = {@html, tostr("      <FRAMESET COLS=\"", talk_area, ",", web_area, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("         <FRAME SRC=\"", java, "\" NAME=\"java_frame\" NORESIZE SCROLLING=no MARGINWIDTH=0 MARGINHEIGHT=0 FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("         <FRAME SRC=\"", web, "\" ID=\"web_frame\" NAME=\"web_frame\" NORESIZE MARGINWIDTH=", this.frame_marginwidth, " MARGINHEIGHT=", this.frame_marginheight, " FRAMEBORDER=\"", this.frameborder, "\">")};
else
html = {@html, tostr("      <FRAMESET ROWS=\"", web_area, ",", talk_area, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("         <FRAME SRC=\"", web, "\" NAME=\"web_frame\" NORESIZE SCROLLING=no MARGINWIDTH=0 MARGINHEIGHT=0 FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("         <FRAME SRC=\"", java, "\" ID=\"java_frame\" NAME=\"java_frame\" NORESIZE MARGINWIDTH=", this.frame_marginwidth, " MARGINHEIGHT=", this.frame_marginheight, " FRAMEBORDER=\"", this.frameborder, "\">")};
endif
html = {@html, "      </FRAMESET>"};
html = {@html, "   </FRAMESET>"};
html = {@html, "</FRAMESET>"};
html = {@html, "</FRAMESET>"};
result = this:build(user, html, tostr($httpd.server_name, $core_version), "", "", "", "", this.doctype_frameset);
return result;
"Last modified Fri Apr 13 18:52:08 2001 CDT by Wizard (#2).";
.
#147:2
"===========================================================";
"Copyright (C) 1999, Jan Rune Holmevik";
"Generates the main enCore Xpress client user screen using two frames";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
user = args[1];
base_url = tostr("http://", $network.site, ":", $network.webport, "/Xpress_client/");
menu = tostr(base_url, "menu.html");
java = tostr(base_url, "java.html");
html = {tostr("<FRAMESET COLS=\"50%\" FRAMEBORDER=", this.frameborder, ">")};
html = {@html, tostr("<NOFRAMES><BODY>")};
html = {@html, "<H3>Sorry you need a browser that supports frames to use this system</H3>"};
html = {@html, "</BODY></NOFRAMES>"};
html = {@html, tostr("<FRAMESET COLS=\"50%\" FRAMEBORDER=", this.frameborder, ">")};
html = {@html, tostr("   <FRAMESET ROWS=\"65,100%\" FRAMEBORDER=", this.frameborder, ">")};
html = {@html, tostr("      <FRAME SRC=\"", menu, "\" NAME=\"menu_frame\" NORESIZE SCROLLING=no MARGINWIDTH=0 MARGINHEIGHT=0 FRAMEBORDER=", this.frameborder, ">")};
html = {@html, tostr("         <FRAME SRC=\"", java, "\" NAME=\"java_frame\" NORESIZE SCROLLING=no MARGINWIDTH=0 MARGINHEIGHT=0 FRAMEBORDER=", this.frameborder, ">")};
html = {@html, "</FRAMESET>"};
html = {@html, "</FRAMESET>"};
result = this:build(user, html, tostr($httpd.server_name, $core_version), "", "", "", "", this.doctype_frameset);
return result;
"Last modified Fri Apr 13 18:52:08 2001 CDT by Wizard (#2).";
.
#147:3
"Copyright (C) 1999, Jan Rune Holmevik";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
boot_player(user);
result = this:pre_assemble(user, {}, "Disconnect", "", "");
return result;
"Last modified Thu Apr  8 10:47:35 1999 CDT by Wizard (#2).";
.
#147:4
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"IMPORTANT NOTE: You must not modify this verb in such a way that the copyright notice and the link to the enCore home page and the GNU GPL license is moved or removed from the web display. Removing copyright notices consititutes a violation of the license under which this software is provided to you.";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
user = args[1];
functions = preload = guest_function = quit_function = sound = {};
action = "preLoadImages();";
base_url = tostr($network.site, ":", $network.webport, "/Xpress_client/");
external_baseurl = ((this.external_baseurl + this.themes_folder) + user.xpress_theme) + "/";
"-----------------------------------------------------------";
"Add guest preference window that opens automatically when guests log in";
"-----------------------------------------------------------";
if ($object_utils:isa(user, $guest) && $real_guest_names)
guest_function = $encore_web_utils:javascript_window_open($MOO_info, "guestPreferenceWindow", tostr("http://", base_url, "guest_preferences.html"));
action = action + "guestPreferenceWindow();";
endif
"-----------------------------------------------------------";
"Add possible login alerts";
"-----------------------------------------------------------";
{alerts, alert_func} = this:Xpress_Confunc(user);
action = (action + alert_func) + ";";
quit_function = {"function close_session(){ ", tostr("    if (confirm('Thank you for visiting ", $network.MOO_name, ". Welcome back soon. Please click OK to disconnect from the MOO, or cancel to continue.')) {"), "   parent.close();", "  return true; ", "   }", "return false;", "}"};
swapImage = {"function swapImage(name,image) {", "     document.images[name].src = image;", "}"};
preload = {"function preLoadImages() {"};
{menu, functions, preload, base_url} = user:_web_menu(user, preload);
quit = {tostr(base_url, "xpress_client/quit.html\" onClick=\";swapImage('quit','", external_baseurl, "quit1.jpg'); return close_session()\" onMouseOver=\"swapImage('quit','", external_baseurl, "quit2.jpg')\" onMouseOut=\"swapImage('quit','", external_baseurl, "quit1.jpg')\"><IMG SRC=\"", external_baseurl, "quit1.jpg\" BORDER=0 ALIGN=bottom name=quit ALT=\"Log Out\" TITLE=\"Log Out\"></A>")};
quit = {"<A HREF=\"", @quit};
preload = {@preload, tostr("   quit2 = new Image(); quit2.src = \"", external_baseurl, "quit2.jpg\";")};
preload = {@preload, "}"};
"-----------------------------------------------------------";
"Add mail notification sound";
"-----------------------------------------------------------";
if (user.sound_volume && user.mail_notify_sound)
sound = this:check_moo_mail(user);
endif
menu = $encore_web_utils:center($list_utils:append(menu, quit, sound));
script = $list_utils:append(preload, guest_function, functions, quit_function, swapImage, alerts);
script = $encore_web_utils:insert_JavaScript(script);
body = $list_utils:append(script, menu);
result = this:build(user, body, "Xpress Toolbar", action);
return result;
"Last modified Fri Apr 13 18:51:29 2001 CDT by Wizard (#2).";
.
#147:5
"Copyright (C) 1999-2000, Jan Rune Holmevik";
"Create sound tag if user has new mail and has sound turned on";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
sound = {};
try
new_mail = user:length_all_msgs() - user:length_date_le(user:get_current_message()[2]);
if (new_mail)
sound = $encore_web_utils:make_sound(user, user.mail_notify_sound);
endif
except error (ANY)
"Ignore error in order to avoid breaking login.";
endtry
return sound;
"Last modified Sun Jun 18 08:49:34 2000 CDT by Wizard (#2).";
.
#147:6
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
frame_identity = "myBody";
base_url = tostr("http://", $network.site, "/");
external_baseurl = $xpress_client.external_baseurl + $xpress_client.java_client_folder;
style = 0;
applet_params = {{"MOOname", $network.MOO_name}, {"HostName", $network.site}, {"port", $network.port}, {"font", user.java_font}, {"fontsize", user.java_font_size}, {"localecho", user.java_client_localecho}, {"login", tostr("autoconnect ", user, " ", crypt(user.web_access_code))}};
"Converting the applet_params from MOO-list to javascript-array";
javascript_applet_params = {};
for param in (applet_params)
javascript_applet_params = {@javascript_applet_params, tostr("['", param[1], "', '", param[2], "'],")};
endfor
javascript_applet_params[$] = javascript_applet_params[$][1..$ - 1];
javascript_applet_params = {"var _java_client_param_pairs = [", @javascript_applet_params, "];"};
javascript_vars = {"var _pluginspage = 'http://www.mozilla.org/oji/MRJPlugin.html';"};
javascript_vars = {@javascript_vars, tostr("var _java_client_codebase ='", external_baseurl, "';")};
javascript_vars = {@javascript_vars, tostr("var _java_client_archive ='", this.java_client_archive, "';")};
javascript_vars = {@javascript_vars, tostr("var _java_client_classname='", this.java_client_filename, "';")};
javascript = $list_utils:append(javascript_vars, javascript_applet_params, this.javascript_functions);
javascript = $encore_web_utils:insert_javascript(javascript);
body = javascript;
result = this:build_page(user, body, "MOOtcan", "", "", frame_identity, "", this.default_doctype, style);
return result;
"Last modified Fri Apr 13 18:51:29 2001 CDT by Wizard (#2).";
.
#147:7
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
user = args[1];
style = 0;
body = $list_utils:append({"<HTML>"}, {"<HEAD>"}, {"  <TITLE>Starting Xpress</TITLE>"}, {"</HEAD>"}, {"<BODY BGCOLOR=\"white\">"}, {tostr("<H2>Starting enCore Xpress from server: ", $network.site)}, {tostr("<P></P><P><CENTER><IMG SRC=\"", $xpress_client.external_baseurl, $xpress_client.system_images_folder, "download-indicator.gif", "\"></CENTER></P>")}, {"<P>Please Wait...</P></H2><P>If you are reconnecting and this window is not updated, click on the <I>look button</I> in the Xpress toolbar, or <I>type look</I> in the talk window to update it.</P>"});
result = this:build(user, body, "Downloading Xpress Components", "", "", "", "", this.default_doctype, style);
return result;
"Last modified Fri Apr 13 18:51:48 2001 CDT by Wizard (#2).";
.
#147:8
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
if ($object_utils:isa(user.location, $encore_web_object))
location = user.location;
else
location = user.home;
endif
"Bypass $encore_web_application:pre_assemble here to generate page based on object properties. Modified for enCore 3.0";
result = location:_html(user);
if (!$encore_web_utils:page_assembled(result))
head = $encore_web_utils:make_head(user, location);
body = $encore_web_utils:make_body(user, location, result);
result = $encore_web_utils:make_page(user, location, $list_utils:append(head, body));
endif
return result;
"Last modified Sun Apr 15 10:16:15 2001 CDT by Wizard (#2).";
.
#147:9
"===========================================================";
"Copyright (C) 1999-2001 Jan Rune Holmevik";
"MOO character application form";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
form = script = reason = {};
javascript = real_name = name = email_address = "";
finished = 0;
title = $encore_web_utils:get_title(user, this, tostr($network.MOO_name, " Character Application Form"));
if ({{}, {}} != args[2])
"User is sending a POST request";
application = args[2];
real_name = application[2][1];
name = application[2][2];
email_address = application[2][3];
reason = $list_utils:flatten(application[2][4..$ - 1]);
title = $encore_web_utils:get_title(user, this, tostr($network.MOO_name, " Character Application Form"));
if ((!$player_db:available(name)) && (name != this.name))
"Cannot create new user. Return application form with submitted data intact.";
javascript = tostr("alert('Sorry, the name ", name, " is already being used, or else it is not acceptable. Please choose another name.')");
elseif ($registration_db:find_exact(email_address))
javascript = tostr("alert('It appears that you already have a character in ", $network.moo_name, ". Please talk to an administrator if you need another one.')");
else
$mail_agent:send_message(user, $character_request_list, "Character request", {"Player name: " + name, "Email address: " + email_address, "Real name: " + real_name, "Reason for request: ", @reason});
form = {"<P><P>Thank you. Your character request has been registered. Please allow a few days for processing. In the mean time, please feel free to use the MOO as a guest at any time. Thank you and have a good day."};
finished = 1;
endif
else
"User is sending a GET request";
javascript = "document.application.real_name.focus()";
endif
if (!finished)
validateScript = this.validate_script;
script = $encore_web_utils:insert_JavaScript(validateScript);
form = {tostr("<P><INPUT TYPE=text NAME=\"real_name\" VALUE=\"", real_name, "\" SIZE=30>Your Full Name", "<P><INPUT TYPE=text NAME=\"name\" VALUE=\"", name, "\" SIZE=30>Desired Character Name", "<P><INPUT TYPE=text NAME=\"email_address\" VALUE=\"", email_address, "\" SIZE=30>Your Email address", "<P>Please tell us why you want to become a member of this MOO", "<P><TEXTAREA NAME=reason ROWS=7 COLS=61 WRAP=virtual>")};
for line in (reason)
form = {@form, line};
endfor
form = {@form, tostr("</TEXTAREA>", "<P><INPUT TYPE=submit NAME=Submit VALUE=\"Submit Character Application\">")};
form = $encore_web_utils:append_close_button(form);
form = $encore_web_utils:form(form, "application", "/xpress_client/character_application.html", "return validateForm()");
endif
body = $list_utils:append(script, title, form);
result = this:build(user, body, "Character Application", javascript);
return result;
"Last modified Fri Apr 13 19:06:17 2001 CDT by Wizard (#2).";
.
#147:10
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Web form for setting guest name, gender, and description";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
finished = 0;
form = script = intro = title = {};
{user, data_fields} = args;
javascript = "document.pref.name.focus()";
if (1 < length(data_fields[2]))
"User has submitted data for processing.";
object = toobj(data_fields[2][1]);
name = $string_utils:substitute(data_fields[2][2], {{" ", "_"}});
gender = data_fields[2][3];
description = data_fields[2][4];
if (object != user)
return "403_Forbidden";
elseif ((!$player_db:available(name)) && (name != user.name))
javascript = tostr("alert('Sorry, the name ", name, ", is already being used, or it is not acceptable. Please choose another name.'); document.pref.name.focus()");
else
javascript = "alert('Thank you!'); self.close()";
if (name != user.name)
if ($guest_name_suffix)
name = tostr(name, "_", $guest_name_suffix);
endif
user.location:announce(">> ", user.name, " is now known as ", name, ".");
user:set_name(name);
endif
user:set_gender(gender);
user:set_description(description);
finished = 1;
endif
endif
if (!finished)
"Compile Guest Preference Editor Page";
script = $encore_web_utils:insert_javascript(this.checkName);
title = $encore_web_utils:get_title(user, this, "Guest Preferences");
intro = this.guest_names_and_manners;
user_gender = user.gender;
available_genders = $gender_utils.genders;
form = {@form, tostr("<INPUT TYPE=hidden NAME=user VALUE=\"", toint(user), "\">")};
form = {@form, tostr("<P>Type a name you wish to be known as<P><INPUT TYPE=text NAME=name VALUE=\"\" SIZE=25><P>Please select a gender")};
form = {@form, "<P><SELECT NAME=gender>"};
for option in (available_genders)
if (option == user_gender)
form = {@form, tostr("<OPTION SELECTED> ", option)};
else
form = {@form, tostr("<OPTION> ", option)};
endif
endfor
form = {@form, "</SELECT>"};
descr = user.description;
if (typeof(descr) == STR)
form = {@form, tostr("<P>Please type a description of yourself<BR><TEXTAREA NAME=description ROWS=8 COLS=50 WRAP=virtual>", user.description, "</TEXTAREA><P>")};
else
form = {@form, tostr("<P>If you wish to let others know more about you, you can type a description of yourself below.<P><TEXTAREA NAME=description ROWS=8 COLS=50 WRAP=virtual>", @user.description, "</TEXTAREA><P>")};
endif
form = {@form, "<P><INPUT TYPE=submit NAME=Submit VALUE=\"Save\">"};
form = $encore_web_utils:append_close_button(form);
form = $encore_web_utils:form(form, "pref", "/xpress_client/guest_preferences.html", "return checkName()");
endif
body = $list_utils:append(script, title, intro, form);
result = this:build(user, body, "Guest Preferences", javascript);
return result;
"Last modified Fri Apr 13 19:06:17 2001 CDT by Wizard (#2).";
.
#147:11
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
guide = {};
title = $encore_web_utils:get_title(user, this, $guide.name);
for line in ($guide.text)
guide = {@guide, line};
endfor
guide = $encore_web_utils:detect_urls(user, guide);
guide = $encore_web_utils:insert_line_breaks(guide);
body = $list_utils:append(title, guide);
result = this:build(user, body, $guide.name);
return result;
"Last modified Fri Apr 13 19:06:17 2001 CDT by Wizard (#2).";
.
#147:12
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Initialize enCore Xpress session for user";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
{user, OS} = args;
user.xpress_on = 1;
user.web_access_code = tostr(random());
cookie = (tostr(user) + " ") + crypt(user.web_access_code);
action = "openXpress();";
if (((user.xpress_layout == "vertical_layout") || (user.xpress_layout == "vertical_layout_2")) || (user.xpress_layout == "horizontal_layout"))
user.ts_client = 1;
endif
if ((OS == "MacOS") && (user.xpress_layout == "vertical_layout"))
user.linelen = 50;
endif
if ($object_utils:has_property($player, "ansi"))
user.ansi = 0;
endif
return {user, cookie, action};
"Last modified Fri Apr 13 18:52:23 2001 CDT by Wizard (#2).";
.
#147:13
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Verb that generates various Xpress alerts when players log in. This verb is currently being called from $Xpress_client:menu_html. The verb returns a list of Javascript alert functions, or an empty list.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user} = args;
func_name = "showAlerts()";
alerts = {tostr("function ", func_name, " {")};
if (!$object_utils:isa(user, $guest))
if (msg = this:check_motd(user))
"================================================================";
"Add message of the Day (MOTD)";
"================================================================";
alerts = {@alerts, tostr("   alert('", msg, "');")};
endif
endif
if (user.Xpress_Confunc_msg != {})
"======================================================================";
"Add alert box(es) with individual Xpress confunc message(s)";
"======================================================================";
for line in (user.Xpress_Confunc_msg)
alerts = {@alerts, tostr("   alert('", line, "');")};
endfor
"Reset user's confunc message(s)";
user.Xpress_Confunc_msg = {};
endif
alerts = {@alerts, "}"};
return {alerts, func_name};
"Last modified Mon Sep 17 10:08:37 2001 CDT by Wizard (#2).";
.
#147:14
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Compile overview of Xpress themes";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
user = args[1];
body = $encore_web_utils:get_title(user, this, "Xpress Themes");
for theme in (this.themes)
url = tostr($xpress_client.external_baseurl, $xpress_client.themes_folder, theme[2], "/");
body = {@body, tostr("<IMG SRC=\"", url, "about1.jpg\">   <IMG SRC=\"", url, "theme-background.jpg\">   ", theme[1], "<BR>")};
endfor
body = $encore_web_utils:append_close_button($list_utils:append({"<P></P>"}, body));
result = this:build(user, body, "Xpress Themes");
return result;
"Last modified Fri Apr 13 18:52:23 2001 CDT by Wizard (#2).";
.
#147:15
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Return message of the day if any";
"===========================================================";
user = args[1];
motd = 0;
try
if (this.motd_msg)
if (!(user in this.MOTD_viewer_list))
"================================================================";
"Return message of the Day (MOTD)";
"================================================================";
motd = tostr(this.motd_header, " ", this.motd_msg);
if (!$object_utils:isa(user, $guest))
this.MOTD_viewer_list = {@this.MOTD_viewer_list, user};
endif
endif
endif
except error (ANY)
"An error occurred. Ignore in order not to break login";
endtry
return motd;
"Last modified Mon Sep 17 10:08:53 2001 CDT by Wizard (#2).";
.
#148:0
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Run MOOtcan as a standalone application";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
base_url = tostr("http://", $network.site, "/");
external_baseurl = $xpress_client.external_baseurl + $xpress_client.java_client_folder;
style = 0;
body = {tostr("<APPLET ARCHIVE=\"", $xpress_client.java_client_archive, "\" CODE=\"", $xpress_client.java_client_filename, "\" CODEBASE=\"", external_baseurl, "\" ALT=\"\" WIDTH=\"", this.java_client_width, "\" HEIGHT=\"", this.java_client_height, "\" ALIGN=\"", this.java_client_alignment, "\">")};
body = {@body, tostr("<PARAM NAME=\"MOOname\" VALUE=\"", $network.MOO_name, "\">")};
body = {@body, tostr("<PARAM NAME=\"HostName\" VALUE=\"", $network.site, "\">")};
body = {@body, tostr("<PARAM NAME=\"Port\" VALUE=\"", $network.port, "\">")};
body = {@body, tostr("<PARAM NAME=\"font\" VALUE=\"", user.java_font, "\">")};
body = {@body, tostr("<PARAM NAME=\"fontsize\" VALUE=\"", user.java_font_size, "\">")};
body = {@body, tostr("<PARAM NAME=\"localecho\" VALUE=\"", user.java_client_localecho, "\">")};
body = {@body, "Sorry, you need a browser that supports Java to use this system."};
body = {@body, "</APPLET>"};
result = this:build(user, body, "MOOtcan", "", "", "", "", "", style);
return result;
"Last modified Fri Apr 13 18:51:12 2001 CDT by Wizard (#2).";
.
#149:0
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Perform search on object names, user real names or email addresses in the MOO.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, form_data} = args;
form = search_results = {};
base_url = tostr("http://", $network.site, ":", $network.webport, "/");
title = $encore_web_utils:get_title(user, this, "Xpress Search");
{help_button, help_function} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "Searching");
help_function = $encore_web_utils:insert_javascript(help_function);
if ($anonymous_users)
form = {@form, tostr("<P>Search for name: <INPUT TYPE=text NAME=string VALUE=\"\" SIZE=30> in ", $network.MOO_name, ".")};
else
form = {@form, "<P>Select a search category, then type the string you wish to search for. <P><INPUT TYPE=radio NAME=categories VALUE=1 CHECKED>Object and User Names<P><INPUT TYPE=radio NAME=categories VALUE=2>User Real Names<P><INPUT TYPE=radio NAME=categories VALUE=3>User Email Addresses<P><INPUT TYPE=text NAME=string VALUE=\"\" SIZE=30>"};
endif
form = {@form, "<P><INPUT TYPE=submit NAME=Submit VALUE=\"Search\">"};
form = $encore_web_utils:append_close_button(form);
form = $encore_web_utils:form(form, "search", "/search_engine/search_html");
if ({{}, {}} != form_data)
hits = {};
"Process from data and compile search results.";
if ($anonymous_users)
string = form_data[2][1];
category = "1";
else
string = form_data[2][2];
category = form_data[2][1];
endif
if (category == "1")
search_domain = $root_class;
criteria = "name";
elseif (category == "2")
search_domain = $player;
criteria = "real_name";
else
search_domain = $player;
criteria = "email_address";
endif
for object in ($object_utils:descendants_suspended(search_domain))
$command_utils:suspend_if_needed(1);
if (match(object.(criteria), string))
hits = {@hits, object};
endif
endfor
if (hits)
message = "Your search produced the following results:";
for result in (hits)
$command_utils:suspend_if_needed(1);
if ($object_utils:isa(result, $encore_web_class))
search_results = {@search_results, tostr("<A HREF=\"", base_url, toint(result), "\" TARGET=web_frame>", $encore_web_utils:get_icon(user, result), result.name, " (", result, ")</A><P>")};
else
search_results = {@search_results, tostr("<LI><A HREF=\"", base_url, toint(result), "\" TARGET=web_frame>", result.name, " (", result, ")</A><P>")};
endif
endfor
else
message = "Your search produced no results";
endif
subtitle = $encore_web_utils:get_subtitle(user, this, message);
search_results = $list_utils:append(subtitle, search_results);
endif
body = $list_utils:append(help_function, title, help_button, form, search_results);
result = this:build(user, body, "MOO Search Engine", "document.search.string.focus();", "");
return result;
"Last modified Fri Apr 13 18:57:39 2001 CDT by Wizard (#2).";
.
#150:0
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Show vital MOO statistics plus lisence and credits";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
info = {};
title = $encore_web_utils:get_title(user, this, tostr("About ", $network.MOO_name));
info = {@info, tostr("<P>Founded : ", $time_utils:time_sub("$N $T, $Y.", #2.first_connect_time), "<BR>")};
info = {@info, tostr("Total number of users : ", length(players()), "<BR>")};
info = {@info, tostr("MOO Server: LambdaMOO version ", server_version(), "<BR>")};
info = {@info, tostr("Core: ", $core_name, ", version ", $core_version, "<BR>")};
info = {@info, tostr("Core home page: <A HREF=\"", $core_homepage, "\" TARGET=\"_BLANK\">", $core_homepage, "</A><BR>")};
info = {@info, tostr("Database: ", tonum(max_object()), " objects, ", db_disk_size(), " bytes on disk<BR>")};
info = {@info, tostr("Uptime: ", $time_utils:english_time(time() - $last_restart_time), "<BR>")};
info = {@info, tostr("Last Backup: ", $time_utils:english_time(time() - $last_dump_time), " ago<BR>")};
if ($object_utils:has_property($sysobj, "mcp"))
info = {@info, tostr("MOO Client Protocol : version MCP/", $mcp.version[1], ".", $mcp.version[2], "<BR>")};
endif
info = {@info, tostr("Web server: ", $httpd.server_name, $core_version, " (", $httpd.protocol, ")<BR>")};
info = {@info, tostr("Authentication: ", $httpd.authentication_method, "<BR>")};
info = {@info, tostr("Total Xpress Logins: ", $httpd.total_logins), "<BR>"};
info = {@info, tostr("Total MOO web pages served: ", $httpd.total_pages_served, "<P>")};
license = $string_utils:substitute($sysobj.license[1], $encore_web_utils.html_entity_subs);
license = $encore_web_utils:detect_urls(user, license);
license = $encore_web_utils:insert_line_breaks(license);
license = $encore_web_utils:italics(license);
contrib = $list_utils:append({"<P>"}, $contributors);
body = $list_utils:append(title, info, license, contrib);
body = $encore_web_utils:append_close_button(body, 1, "self");
result = this:build(user, body);
return result;
"Last modified Fri Apr 13 18:56:04 2001 CDT by Wizard (#2).";
.
#152:0
"===========================================================";
"Copyright (C) 1998-2001, Jan Rune Holmevik";
"Display a list of resource categories and bookmarked links";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
result = body = confirmDelete = {};
alert = "";
base_url = tostr("http://", $network.site, ":", $network.webport, "/");
external_baseurl = $xpress_client.external_baseurl + $xpress_client.icons_folder;
title = $encore_web_utils:get_subtitle(user, this, "Bookmarks");
{help_button, help_function} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "Bookmarks");
confirmDelete = {"function confirmDelete() {", "   if (confirm('Are you sure you want to delete this bookmark?')) {", "      return true;", "   }", "   return false;", "}"};
functions = $list_utils:append(help_function, confirmDelete);
functions = $encore_web_utils:insert_javascript(functions);
if ({{}, {}} == data)
requested = 0;
else
requested = data[2][1];
alert = data[1][1];
endif
"-------------------------------------------------------------";
" Manage personal bookmarks";
"-------------------------------------------------------------";
button = {tostr("<INPUT TYPE=\"button\" VALUE=\"Bookmark Current Location\" onClick=\"parent.work.location.href = '", base_url, "Xpress_Navigator/add_bookmark'\">")};
button = $encore_web_utils:form(button);
"Assemble categories and bookmarks";
try
"-------------------------------------------------------------";
" Compile personal bookmarks";
"-------------------------------------------------------------";
bookmarks = {{tostr(user.name, "'s Personal Xpress Bookmarks"), {}}};
bookmarks[1][2] = user.xpress_bookmarks;
bookmarks = $list_utils:append(bookmarks, this.bookmarks);
body = {"<BR>"};
"-------------------------------------------------------------";
" Compile system bookmarks";
"-------------------------------------------------------------";
for n in [1..length(bookmarks)]
if (n == toint(requested))
body = {@body, tostr("<A HREF=\"", base_url, "Xpress_navigator/bookmarks.html?", tostr(n), "\"><IMG SRC=\"", external_baseurl, "list_view2.gif\" BORDER=0 ALIGN=middle> ", bookmarks[n][1], "</A><BR>")};
body = {@body, "<UL>"};
for bookmark in (bookmarks[n][2])
body = {@body, tostr("<A HREF=\"", base_url, toint(bookmark), "\" TARGET=web_frame onClick=\"", tostr("setTimeout('parent.close()',", $xpress_client.window_close_timeout, ");"), "\"><IMG SRC=\"", external_baseurl, "bookmark.gif\" BORDER=0 ALIGN=bottom>", bookmark.name)};
if (requested == "1")
"------------------------------------------------";
" Remove bookmark option";
"------------------------------------------------";
body = {@body, tostr("<A HREF=\"", base_url, "Xpress_navigator/remove_bookmark?", toint(bookmark), "\" onClick=\"return confirmDelete()\">[Remove]</A>")};
endif
body = {@body, "<BR>"};
endfor
body = {@body, "</UL>"};
else
body = {@body, tostr("<A HREF=\"", base_url, "Xpress_navigator/bookmarks.html?", tostr(n), "\"><IMG SRC=\"", external_baseurl, "list_view1.gif\" BORDER=0 ALIGN=middle> ", bookmarks[n][1], "</A><BR>")};
endif
endfor
except error (ANY)
alert = "There is a problem with your Xpress Bookmarks. Please notify a MOO administrator";
endtry
body = $list_utils:append(functions, title, help_button, button, body);
result = this:build(user, body, "Xpress Navigator", $encore_web_utils:make_alert(alert));
return result;
"Last modified Fri Apr 13 18:56:56 2001 CDT by Wizard (#2).";
.
#152:1
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Generates the main Xpress navigator screen";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
user = args[1];
base_url = tostr("http://", $network.site, ":", $network.webport, "/Xpress_Navigator/");
menu_frame = tostr(base_url, "menu.html");
work_frame = tostr(base_url, "bookmarks.html");
html = {tostr("<FRAMESET COLS=\"50%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("<NOFRAMES><BODY>")};
html = {@html, "<H3>Sorry you need a browser that supports frames to use this system</H3>"};
html = {@html, "</BODY></NOFRAMES>"};
html = {@html, tostr("   <FRAMESET ROWS=\"95,82%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("  <FRAME SRC=\"", menu_frame, "\" NAME=menu NORESIZE SCROLLING=no MARGINWIDTH=", this.frame_marginwidth, " MARGINHEIGHT=", this.frame_marginheight, " FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("       <FRAME SRC=\"", work_frame, "\" NAME=work MARGINWIDTH=", this.frame_marginwidth, " MARGINHEIGHT=", this.frame_marginheight, " FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, "   </FRAMESET>"};
html = {@html, "</FRAMESET>"};
result = this:build(user, html, "Xpress Navigator", "", "", "", "", this.doctype_frameset);
return result;
"Last modified Fri Apr 13 18:56:39 2001 CDT by Wizard (#2).";
.
#152:2
"===========================================================";
"Copyright (C) 1999-2000, Jan Rune Holmevik";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
user = args[1];
base_url = tostr("http://", $network.site, ":", $network.webport, "/");
"Assemble menu line";
{help_button, help_function} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "xpress_navigator");
functions = $encore_web_utils:insert_javascript(help_function);
buttons = {tostr("<INPUT TYPE=\"button\" VALUE=\"Bookmarks\" onClick=\"parent.work.location.href = '", base_url, "Xpress_Navigator/bookmarks.html", "'\">")};
if (!$object_utils:isa(user, $guest))
buttons = {@buttons, tostr("<INPUT TYPE=\"button\" VALUE=\"Notebook\" onClick=\"parent.work.location.href = '", base_url, "Xpress_Navigator/Notebook", "'\">")};
buttons = {@buttons, tostr("<INPUT TYPE=\"button\" VALUE=\"Log\" onClick=\"parent.work.location.href = '", base_url, "Xpress_Navigator/MOO-Log", "'\">")};
buttons = {@buttons, tostr("<INPUT TYPE=\"button\" VALUE=\"VASE\" onClick=\"window.open('", base_url, toint($assignment_server), "', '", $string_utils:substitute($network.MOO_name, {{" ", "_"}}), "_OpenVASE', 'width=800,height=600,scrollbars=yes,menubar=yes,toolbar=yes,copyhistory=yes,resizable=yes,location=yes');parent.close()\">")};
endif
title = $encore_web_utils:get_title(user, this, "Xpress Navigator");
menu = $list_utils:append(title, help_button, buttons);
menu = $encore_web_utils:append_close_button(menu);
menu = {"<FORM>", @menu, "</FORM>"};
body = $list_utils:append(functions, menu);
result = this:build(user, body, "Xpress Navigator Menu");
return result;
"Last modified Fri Apr 13 18:56:39 2001 CDT by Wizard (#2).";
.
#152:3
"===========================================================";
"Copyright (C) 1998-2001, Jan Rune Holmevik";
"Display the user's personal notebook";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
msg = status = "";
notebook = {};
if (data != {{}, {}})
"=== User has specified an action ===";
action = data[2][1];
notebook_contents = data[2][2];
if (action == "saveMOO")
"=== Save Notebook in MOO ===";
if (typeof(notebook_contents) == STR)
notebook_contents = {notebook_contents};
endif
user.Xpress_Notebook = notebook_contents;
status = "Your Notebook has been saved";
elseif (action == "email")
"=== Email data to user ===";
user.Xpress_Notebook = notebook_contents;
status = this:save_via_email(user, "Your MOO Notebook", notebook_contents);
elseif (action == "erase")
user.Xpress_Notebook = {};
status = "Your Notebook has been erased";
else
status = "Please choose action";
endif
msg = tostr("alert('", status, "!')");
endif
"=== Build Notebook application web page ===";
"Compile menu";
menu = {tostr("<SELECT onchange=\"return check()\" NAME=\"action\">")};
menu = {@menu, "<OPTION VALUE=\"text/html\">Choose action"};
menu = {@menu, "<OPTION VALUE=\"saveMOO\">Save in MOO"};
menu = {@menu, "<OPTION VALUE=\"email\">Save to Email"};
if (user.Xpress_Notebook != {})
menu = {@menu, "<OPTION VALUE=\"erase\">Erase Notebook"};
endif
menu = {@menu, "</SELECT>"};
notebook = {@notebook, tostr("<P><TEXTAREA NAME=\"NotebookContents\" ROWS=20 COLS=65 WRAP=virtual>")};
contents = user.Xpress_notebook;
if (typeof(contents) == STR)
contents = {contents};
endif
for line in (contents)
suspend(0);
line = $encore_web_utils:substitute_suspended(line, {{"<", "&lt;"}, {">", "&gt;"}});
notebook = {@notebook, line};
endfor
notebook = {@notebook, "</TEXTAREA>"};
form = $list_utils:append(menu, notebook);
form = $encore_web_utils:form(form, "notebook", "/Xpress_Navigator/Notebook/");
title = $encore_web_utils:get_subtitle(user, this, tostr(user.name, "'s Notebook"));
{help_button, help_function} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "Notebook");
check = {"function check(){", "if (document.notebook.action.selectedIndex == 4) { ", "    if (confirm('Do you really want to erase your notebook?')) {", "          	document.notebook.submit();", "          	return true", "   	}", "	return false;", "}", " document.notebook.submit();", "return true; ", "}"};
functions = $list_utils:append(help_function, check);
functions = $encore_web_utils:insert_javascript(functions);
body = $list_utils:append(functions, title, help_button, form);
result = this:pre_assemble(user, body, "Xpress Notebook", msg);
return result;
"Last modified Fri Apr 13 18:56:56 2001 CDT by Wizard (#2).";
.
#152:4
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Display the user's personal MOO Log";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
msg = status = "";
log = {};
if (data != {{}, {}})
"=== User has specified an action ===";
action = data[2][1];
log_contents = data[2][2];
if (action == "stop")
"=== Stop logging ===";
status = user:stop_log(user);
elseif (action == "start")
"=== Start logging ===";
status = user:start_log(user);
elseif (action == "email")
"=== Email data to user ===";
status = this:save_via_email(user, "Your MOO Log", log_contents);
elseif (action == "erase")
user.recording = 0;
user.Xpress_Log = {};
status = "Your log has been erased";
else
status = "Please choose action";
endif
msg = tostr("alert('", status, "!')");
endif
"=== Build Log application web page ===";
"Compile menu";
menu = {tostr("<SELECT onchange=\"return check()\" NAME=\"action\">")};
menu = {@menu, "<OPTION VALUE=\"text/html\">Choose action"};
if (user.recording)
menu = {@menu, "<OPTION VALUE=\"stop\">Stop logging"};
else
menu = {@menu, "<OPTION VALUE=\"start\">Start logging"};
if (user.Xpress_Log != {})
menu = {@menu, "<OPTION VALUE=\"email\">Save to Email"};
menu = {@menu, "<OPTION VALUE=\"erase\">Erase Log"};
endif
endif
menu = {@menu, "</SELECT>"};
log = {@log, tostr("<P><TEXTAREA NAME=\"LogContents\" ROWS=20 COLS=65 WRAP=virtual>")};
for line in (user.xpress_log)
suspend(0);
line = $encore_web_utils:substitute_suspended(line, {{"<", "&lt;"}, {">", "&gt;"}});
log = {@log, line};
endfor
log = {@log, "</TEXTAREA>"};
form = $list_utils:append(menu, log);
form = $encore_web_utils:form(form, "log", "/Xpress_Navigator/MOO-Log/");
if (user.recording)
rec = "(On)";
else
rec = "(Off)";
endif
title = $encore_web_utils:get_subtitle(user, this, tostr(user.name, "'s Log ", rec));
{help_button, help_function} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "Logging");
check = {"function check(){", "if (document.log.action.selectedIndex == 4) { ", "    if (confirm('Do you really want to erase your log?')) {", "          	document.log.submit();", "          	return true", "   	}", "	return false;", "}", " document.log.submit();", "return true; ", "}"};
functions = $list_utils:append(help_function, check);
functions = $encore_web_utils:insert_javascript(functions);
body = $list_utils:append(functions, title, help_button, form);
result = this:pre_assemble(user, body, "Xpress Log", msg);
return result;
"Last modified Fri Apr 13 18:57:14 2001 CDT by Wizard (#2).";
.
#152:5
"Copyright (C) 2000, Jan Rune Holmevik";
"Send the contents of user's log or notebook via email.";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, subject, message} = args;
status = "";
if (typeof(message) == STR)
"Make sure we're not trying to send a string";
message = {message};
endif
if (!$network.active)
status = "Sorry. Network is not active. Cannot save via mail";
elseif (!$xpress_client.allow_internet_mail)
status = "Sorry. This MOO does not allow you to save via email";
else
if (success = $Xpress_MOO_Mailer:sendmail(user, user.email_address, subject, @message))
status = "Sorry, an error occured while trying to save via email. Please notify a MOO administrator";
else
status = "Contents successfully sent to your registered email address " + user.email_address;
endif
endif
return status;
"Last modified Fri Mar 24 21:03:29 2000 CST by Wizard (#2).";
.
#152:6
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Add or remove bookmarks from the users' personal list";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
alert = "";
try
if (verb == "remove_bookmark")
bookmark = toobj(data[2][1]);
user.Xpress_bookmarks = setremove(user.xpress_bookmarks, bookmark);
alert = tostr("Location ", bookmark.name, " removed from your list of personal Xpress bookmarks");
else
if (user.location in user.xpress_bookmarks)
alert = tostr("Location ", user.location.name, " is already in your personal Xpress bookmarks");
else
user.Xpress_bookmarks = setadd(user.xpress_bookmarks, user.location);
alert = tostr("Location ", user.location.name, " added to your list of personal Xpress Bookmarks");
endif
endif
except error (ANY)
alert = "An error occurred while trying to update your bookmarks";
endtry
html = $xpress_navigator:bookmarks_html(user, {{alert}, {"1"}});
return html;
"Last modified Fri Apr 13 18:56:56 2001 CDT by Wizard (#2).";
.
#153:0
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Generates the main help browser using four frames";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
user = args[1];
base_url = tostr("http://", $network.site, ":", $network.webport, "/help_browser/");
default_help_db = toint($encore_help);
help_menu_frame = tostr(base_url, "menu.html");
help_db_frame = tostr(base_url, "help_databases.html");
help_contents_frame = tostr(base_url, "help_contents.html?", default_help_db);
help_view_frame = tostr(base_url, "view.html?", default_help_db, "&Introduction");
html = {tostr("<FRAMESET COLS=\"50%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("<NOFRAMES><BODY>")};
html = {@html, "<H3>Sorry you need a browser that supports frames to use this system</H3>"};
html = {@html, "</BODY></NOFRAMES>"};
html = {@html, tostr("<FRAMESET COLS=\"50%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("   <FRAMESET ROWS=\"95,80%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("      <FRAME SRC=\"", help_menu_frame, "\" NAME=help_menu NORESIZE SCROLLING=no MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("      <FRAMESET COLS=\"30%,70%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("         <FRAME SRC=\"", help_db_frame, "\" NAME=help_db NORESIZE MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("          <FRAMESET ROWS=\"40%,60%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("            <FRAME SRC=\"", help_contents_frame, "\" NAME=help_contents MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("            <FRAME SRC=\"", help_view_frame, "\" NAME=help_view MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, "         </FRAMESET>"};
html = {@html, "      </FRAMESET>"};
html = {@html, "   </FRAMESET>"};
html = {@html, "</FRAMESET>"};
html = {@html, "</FRAMESET>"};
result = this:build(user, html, "MOO Help System", "", "", "", "", this.doctype_frameset);
return result;
"Last modified Fri Apr 13 18:56:04 2001 CDT by Wizard (#2).";
.
#153:1
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
base_url = "/help_browser/menu.html";
title = $encore_web_utils:get_title(user, this, "Help Browser");
{help_button, help_window_function} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "Help_Browser");
help_window_function = $encore_web_utils:insert_javascript(help_window_function);
search_form = {@help_button, "Search for <INPUT TYPE=text NAME=\"search_string\" VALUE=\"\" SIZE=13><INPUT TYPE=submit NAME=Submit VALUE=\"Search\">"};
search_form = $encore_web_utils:append_close_button(search_form);
search_form = $encore_web_utils:form($list_utils:append(title, search_form), "search", base_url);
if ({{}, {}} != args[2])
"User has submitted a search request";
request = args[2][2][1];
relevant_help_dbs = $code_utils:help_db_list(user);
found = 0;
for db in (relevant_help_dbs)
if ($object_utils:has_property(db, request))
help_text = this:view_html(user, {{"", ""}, {toint(db), request}});
return help_text;
break;
endif
endfor
if (!found)
body = {tostr("<I>Sorry, ", request, " was not found</I>")};
result = this:pre_assemble(user, body, "Help System Window");
return result;
endif
endif
body = $list_utils:append(help_window_function, search_form);
result = this:build(user, body, "Help System", "document.search.search_string.focus();", "help_view");
return result;
"Last modified Fri Apr 13 18:56:22 2001 CDT by Wizard (#2).";
.
#153:2
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Compile list of available help databases";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
user = args[1];
links = {"<BASE TARGET=\"help_contents\">"};
base_url = tostr("http://", $network.site, ":", $network.webport, "/help_browser/help_contents.html");
relevant_help_dbs = $code_utils:help_db_list(user);
relevant_help_dbs = setremove(relevant_help_dbs, $help);
title = $encore_web_utils:get_subtitle(user, this, "Help Topics");
for help_db in (relevant_help_dbs)
links = {@links, tostr("<A HREF=", base_url, "?", toint(help_db), "/>", $encore_web_utils:get_icon(user, this), help_db.name, "</A><BR>")};
endfor
body = $list_utils:append(title, links);
result = this:build(user, body, "Help Topics");
return result;
"Last modified Fri Apr 13 18:56:22 2001 CDT by Wizard (#2).";
.
#153:3
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Compile a list of help topics on a given help database";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
user = args[1];
arguments = args[2];
if ({{}, {}} == arguments)
"No arguments were passed";
body = {};
else
help_db = toobj(arguments[2][1]);
relevant_help_dbs = $code_utils:help_db_list(user);
if (help_db in relevant_help_dbs)
"List help topics in selected database";
links = {"<BASE TARGET=\"help_view\">"};
base_url = tostr("http://", $network.site, ":", $network.webport, "/help_browser/view.html");
help_items = properties(help_db);
help_items = $list_utils:sort(help_items);
title = $encore_web_utils:get_subtitle(user, this, tostr(help_db.name));
for item in (help_items)
try
if (help_db.(item)[1] != "*forward*")
links = {@links, tostr("<LI><A HREF=", base_url, "?", toint(help_db), "&", $string_utils:trim(item), ">", item, "</A><BR>")};
endif
except error (ANY)
endtry
endfor
body = $list_utils:append(title, links);
else
"User does not have permission to read requested help database";
return "403_Forbidden";
endif
endif
result = this:build(user, body, "Help Items");
return result;
"Last modified Fri Jul 20 09:04:59 2001 CDT by Wizard (#2).";
.
#153:4
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
body = {};
arguments = args[2];
if ({{}, {}} == arguments)
"No arguments were passed";
body = {};
else
help_db = toobj(arguments[2][1]);
help_item = arguments[2][2];
help_item = $string_utils:trim($string_utils:substitute(help_item, {{"#", ""}}));
relevant_help_dbs = $code_utils:help_db_list(user);
if ((help_db in relevant_help_dbs) || $object_utils:has_property(help_db, "help_msg"))
"User has requested a spesific help item";
if ($object_utils:isa(help_db, $generic_help))
title_str = tostr("Showing Help On: ", $string_utils:substitute(help_item, {{"_", " "}}));
else
title_str = tostr("Showing Help On: ", help_db.name);
endif
title = $encore_web_utils:get_subtitle(user, this, title_str);
try
help_text = $encore_web_utils:get_text_linebreaks(help_db.(help_item));
except error (ANY)
help_text = {tostr("Sorry, Xpress help not available. Type 'help ", help_item, "' in the talk area for help on this item.")};
endtry
body = $list_utils:append(title, help_text);
else
"User does not have permission to read requested help database";
return "403_Forbidden";
endif
endif
result = this:build(user, body, "enCore Help");
return result;
"Last modified Fri Apr 13 18:56:39 2001 CDT by Wizard (#2).";
.
#154:0
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Displays a list of connected users.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
who_listing = players = notice = close = {};
functions = "";
if (connected_players())
who_list = {};
base_url = tostr("http://", $network.site, ":", $network.webport, "/");
{help_button, help_window} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "Who_Browser");
functions = $list_utils:append(help_window, this.move_alert);
functions = $encore_web_utils:insert_javascript(functions);
title = $encore_web_utils:get_title(user, this);
"Compile list of connected users, and sort by idle time";
for person in (connected_players())
$command_utils:suspend_if_needed(0);
if (!($object_utils:has_property(person, "invis") && person.invis))
"Do not show invisible users in the who list.";
players = setadd(players, {person, connected_seconds(person), idle_seconds(person), person.location});
endif
endfor
if (players)
sorted_list = $list_utils:sort_alist(players, 3);
"Put values from sorted list into four collumns";
users = {"<B>Name</B><HR SIZE=1 NOSHADE>"};
connected = {"<B>Connected For</B><HR SIZE=1 NOSHADE>"};
idle = {"<B>Idle Time</B><HR SIZE=1 NOSHADE>"};
location = {"<B>Location</B> (Click to visit)<BR><HR SIZE=1 NOSHADE>"};
activity = {"<B>Activity</B><HR SIZE=1>"};
for n in (sorted_list)
$command_utils:suspend_if_needed(0);
OBJ = toobj(n[1]);
link = tostr("<A HREF=", base_url, toint(OBJ), "/>", $encore_web_utils:get_icon(user, OBJ), "<A HREF=", base_url, toint(OBJ), "/ onClick=\"return setTimeout('self.close()',", $xpress_client.window_close_timeout, ")\">", OBJ.name, "</A>");
if (OBJ.xpress_on)
link = link + tostr("<IMG SRC=\"", $xpress_client.external_baseurl, $xpress_client.icons_folder, "xpress_indicator.gif\" BORDER=\"0\" ALIGN=\"BOTTOM\">");
endif
users = {@users, tostr(link)};
connected = {@connected, tostr($string_utils:from_seconds(connected_seconds(n[1])))};
idle = {@idle, tostr($string_utils:from_seconds(idle_seconds(n[1])))};
if ($object_utils:isa(OBJ.location, $generic_editor) || (OBJ.location.key != 0))
location_link = OBJ.location.name;
else
location_link = tostr("<A HREF=", base_url, toint(OBJ.location), "/ onClick=\"return confirmMove()\">", OBJ.location.name, "</A>");
endif
location = {@location, tostr(location_link)};
activity = {@activity, tostr(OBJ.doing)};
endfor
users = {@users, "<HR SIZE=1 NOSADE>"};
connected = {@connected, "<HR SIZE=1 NOSHADE>"};
idle = {@idle, "<HR SIZE=1 NOSHADE>"};
location = {@location, "<HR SIZE=1 NOSHADE>"};
activity = {@activity, "<HR SIZE=1 NOSHADE>"};
who_listing = $encore_web_utils:generate_table(user, {users, connected, idle, location, activity}, this, "1");
who_listing = {"<BASE TARGET=web_frame>", @who_listing};
who_listing = $encore_web_utils:append_close_button(who_listing);
if (length(sorted_list) == 1)
verb = " is ";
noun = " person ";
else
verb = " are ";
noun = " people ";
endif
notice = {tostr("<P>There ", verb, length(sorted_list), noun, " in ", $network.MOO_name, " on ", $time_utils:time_sub("$D $N $T, $Y, at $O:$M $P, $Z", time())), "</P>"};
else
notice = {tostr("<P>Nobody is connected to ", $network.MOO_name, " at this time.</P>")};
endif
info = $list_utils:append({"<FORM>"}, help_button, {tostr(" <INPUT TYPE=\"BUTTON\" VALUE=\"Refresh Who Browser\" onClick=\"location.href='", base_url, "Who_Browser/main.html'\"><INPUT TYPE=\"button\" VALUE=\"Close Who Browser\" onClick=\"parent.close()\"></FORM>")});
endif
body = $list_utils:append(functions, title, info, notice, who_listing);
result = this:build(user, body, "Who Browser");
return result;
"Last modified Fri Apr 13 18:57:15 2001 CDT by Wizard (#2).";
.
#155:0
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Generates the main MOO mail browser using three frames";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
user = args[1];
base_url = tostr("http://", $network.site, ":", $network.webport, "/Xpress_MOO_Mailer/");
menu_frame = tostr(base_url, "menu.html");
folder_frame = tostr(base_url, "folders.html");
contents_frame = tostr(base_url, "contents.html");
html = {tostr("<FRAMESET COLS=\"50%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("<NOFRAMES><BODY>")};
html = {@html, "<H3>Sorry you need a browser that supports frames to use this system</H3>"};
html = {@html, "</BODY></NOFRAMES>"};
html = {@html, tostr("<FRAMESET COLS=\"50%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("   <FRAMESET ROWS=\"95,82%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("      <FRAME SRC=\"", menu_frame, "\" NAME=menu NORESIZE SCROLLING=no MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("      <FRAMESET COLS=\"30%,70%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("         <FRAME SRC=\"", folder_frame, "\" NAME=folder NORESIZE MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("            <FRAME SRC=\"", contents_frame, "\" NAME=contents MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, "      </FRAMESET>"};
html = {@html, "   </FRAMESET>"};
html = {@html, "</FRAMESET>"};
html = {@html, "</FRAMESET>"};
result = this:build(user, html, "Xpress MOO Mailer", "", "", "", "", this.doctype_frameset);
return result;
"Last modified Fri Apr 13 18:57:56 2001 CDT by Wizard (#2).";
.
#155:1
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
user = args[1];
functions = {};
base_url = tostr("http://", $network.site, ":", $network.webport, "/");
title = $encore_web_utils:get_title(user, this, "Xpress MOO Mailer");
"Assemble menu line";
{help_button, help_function} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "MOO_Mailer");
compose = {"<INPUT TYPE=\"button\" VALUE=\"Compose Mail\" onClick=\"openMailEditor()\">"};
maillists = {"<INPUT TYPE=\"button\" VALUE=\"Mail Lists\" onClick=\"openMailLists()\">"};
menu = $list_utils:append(help_button, compose, maillists);
menu = $encore_web_utils:append_close_button(menu);
menu = $enCore_web_utils:form($list_utils:append(title, menu));
"Assemble JavaScript functions";
mail_editor = $encore_web_utils:javascript_window_open($Xpress_MOO_mailer, "openMailEditor", tostr(base_url, "Xpress_MOO_mailer/compose"), this.browser_window_width, this.browser_window_height);
maillistwindow = $encore_web_utils:javascript_window_open($Xpress_MOO_mailer, "openMailLists", tostr(base_url, "Xpress_MOO_mailer/subscribe"), this.browser_window_width, this.browser_window_height);
functions = $list_utils:append(help_function, mail_editor, maillistwindow);
functions = $encore_web_utils:insert_javascript(functions);
body = $list_utils:append(functions, menu);
result = this:build(user, body);
return result;
"Last modified Fri Apr 13 18:57:56 2001 CDT by Wizard (#2).";
.
#155:2
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Lists all mail folders the user is subscribed to";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, alert} = args;
if (alert == {{}, {}})
alert = "";
endif
base_url = tostr("http://", $network.site, ":", $network.webport, "/Xpress_MOO_mailer/contents.html");
title = $encore_web_utils:get_subtitle(user, this, "Mail Boxes");
folders = {"<BASE TARGET=\"contents\">"};
folders = {@folders, tostr("<A HREF=", base_url, "?", toint(user), ">", $encore_web_utils:get_icon(user, this), "InBox</A><BR>")};
cm = user.current_message;
cm[1..2] = (verb == "@rn") ? {{this, @cm[1..2]}} | {};
for item in (cm)
mail_box = item[1];
if ($mail_agent:is_recipient(mail_box) && (!$object_utils:isa(mail_box, $big_mail_recipient)))
folders = {@folders, tostr("<A HREF=", base_url, "?", toint(mail_box), ">", $encore_web_utils:get_icon(user, this), mail_box.name, "</A><BR>")};
endif
endfor
body = $list_utils:append(title, folders);
result = this:build(user, body, "MOO Mailer Folders", alert);
return result;
"Last modified Fri Apr 13 18:57:56 2001 CDT by Wizard (#2).";
.
#155:3
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Build list of messages in folder. Create quick access navigation menu if required.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data, ?alert = ""} = args;
result = {};
if ({{}, {}} == data)
"No arguments specified, list users' own inbox";
folder_name = "InBox";
requested_folder = user;
folder = user.messages;
else
requested_folder = toobj(data[2][1]);
folder_name = requested_folder.name;
folder = requested_folder.messages;
endif
if (!this:permission_to_read(user, requested_folder))
"User does not have permission to see requested folder";
result = "403_Forbidden";
else
if ($object_utils:isa(requested_folder, $big_mail_recipient))
result = this:build(user, {"Sorry, Xpress does not support Big Mail Recipient folders. Read these folders using the command line verbs @read, and @nn."}, "MOO Mailer Contents", alert);
else
base_url = tostr("http://", $network.site, ":", $network.webport, "/Xpress_MOO_mailer/");
mail_reader = $encore_web_utils:javascript_window_open($Xpress_MOO_mailer, "openMailReader", "", this.browser_window_width, this.browser_window_height);
open_mail_reader = $encore_web_utils:insert_javascript(mail_reader);
title = $encore_web_utils:get_subtitle(user, this, tostr("Mail box: ", folder_name));
subject = from = date = delete = {};
quick_access = {};
messages_to_display = this.messages_to_display;
if (!folder)
table = {"This mail box is empty."};
else
"Determine if number of messages in folder is larger than the specified number of messages to display. Start at newest messages by default";
total_messages = length(folder);
if (length(data[2]) > 1)
"Index has been specified by user via quick access menu, adjust index to specification";
index = toint(data[2][2]);
elseif (total_messages < messages_to_display)
index = 1;
else
"Index not specified. Display newest messages";
index = (total_messages / messages_to_display) * messages_to_display;
endif
if (total_messages > messages_to_display)
"-----------------------------------";
"Build quick access menu";
"-----------------------------------";
finished = 0;
first = menuindex = 1;
if (index > 1)
"Add back button";
if ((index - messages_to_display) < 1)
newindex = 1;
else
newindex = index - messages_to_display;
endif
quick_access = {@quick_access, tostr("<A HREF=\"", base_url, "contents.html?", toint(requested_folder), "&", newindex, "\">[<< prev]</A>")};
endif
while (!finished)
if (first in {index, index + 1})
"We are at current index position. Do not create link";
quick_access = {@quick_access, tostr("<B>", menuindex, "</B>")};
else
quick_access = {@quick_access, tostr("<A HREF=\"", base_url, "contents.html?", toint(requested_folder), "&", first, "\">", menuindex, "</A>")};
endif
first = first + messages_to_display;
menuindex = menuindex + 1;
if (first > total_messages)
finished = 1;
endif
endwhile
if ((index + messages_to_display) <= total_messages)
"Add forward button";
quick_access = {@quick_access, tostr("<A HREF=\"", base_url, "contents.html?", toint(requested_folder), "&", index + this.messages_to_display, "\">[next >>]</A>")};
endif
quick_access = {@quick_access, "<P>"};
endif
"-----------------------------------";
"Compile list of messages to display";
"-----------------------------------";
if ((index + messages_to_display) < total_messages)
last = index + messages_to_display;
else
last = total_messages;
endif
add_delete = 0;
if ((user == requested_folder.owner) || user.wizard)
add_delete = 1;
endif
for msg in [index..last]
suspend(0);
message = folder[msg];
subj = tostr(message[1], ": ", message[2][4]);
sender = message[2][2];
subject = {@subject, tostr("<A HREF=\"", base_url, "view.html?", toint(requested_folder), "&", msg, "\" onClick=openMailReader() TARGET=", $string_utils:substitute($network.MOO_name, {{" ", "_"}}), "_openMailReader>", subj, "</A>")};
from = {@from, sender};
date = {@date, tostr($time_utils:time_sub("$1/$3 $Y", message[2][1]))};
if (add_delete)
delete = {@delete, tostr("<A HREF=\"", base_url, "delete_mail.html?", toint(requested_folder), "&", msg, "\">[Delete]</A>")};
endif
endfor
"Reverse. Make messages appear newest first.";
subject = {"<B>Subject:</B><BR>", @subject};
from = {"<B>From:</B><BR>", @from};
date = {"<B>Date:</B><BR>", @date};
if (add_delete)
delete = {"", @delete};
table = $encore_web_utils:generate_table(user, {subject, from, date, delete}, this, "1");
else
table = $encore_web_utils:generate_table(user, {subject, from, date}, this, "1");
endif
endif
body = $list_utils:append(open_mail_reader, title, quick_access, table);
result = this:build(user, body, "MOO Mailer Contents", alert);
endif
endif
return result;
"Last modified Fri Apr 13 18:58:12 2001 CDT by Wizard (#2).";
.
#155:4
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Displays the contents of a MOO mail message";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
valid_folders = mail_head = mail_message = body = {};
{user, ?requested = ""} = args;
{user, ?requested = {{}, {}}} = args;
base_url = tostr("http://", $network.site, ":", $network.webport, "/");
if ({{}, {}} != requested)
data = requested;
folder = toobj(data[2][1]);
msg_nr = toint(data[2][2]);
cm = user.current_message;
cm[1..2] = (verb == "@rn") ? {{this, @cm[1..2]}} | {};
valid_folders = {user};
for item in (cm)
valid_folders = {@valid_folders, item[1]};
endfor
if (this:permission_to_read(user, folder))
"User has permission to read the requested message";
"Mark message as read";
user:set_current_message(folder, msg_nr, time());
message = folder.messages[msg_nr];
mail_head = {@mail_head, tostr("<P>Date: ", $time_utils:time_sub("$1/$3 $Y, $H:$M $Z", message[2][1]), "<BR>")};
mail_head = {@mail_head, tostr("From: ", message[2][2], "<BR>")};
mail_head = {@mail_head, tostr("To: ", message[2][3], "<BR>")};
mail_head = {@mail_head, tostr("Subject: ", message[2][4])};
mail_message = {"<P>"};
for line in (message[2][6..$])
mail_message = {@mail_message, line};
endfor
mail_message = {@mail_message, "<P>"};
title = $encore_web_utils:get_subtitle(user, this, tostr("Message ", msg_nr, " on ", folder.name));
mail_message = $encore_web_utils:detect_urls(user, mail_message);
mail_message = $encore_web_utils:insert_line_breaks(mail_message);
"Add original message to reply";
"Add reply button";
reply_button = {tostr("<INPUT TYPE=submit NAME=reply VALUE=\"Reply\"><INPUT TYPE=hidden NAME=to VALUE=\"", folder, "\"><INPUT TYPE=hidden NAME=re VALUE=\"", msg_nr, "\"> <INPUT TYPE=checkbox NAME=quote VALUE=no> Quote Original")};
reply_button = $encore_web_utils:append_close_button(reply_button);
reply_button = $encore_web_utils:form(reply_button, "reply", "/Xpress_MOO_mailer/compose");
body = $list_utils:append(title, mail_head, mail_message, reply_button);
else
return "403_Forbidden";
endif
endif
result = this:build(user, body, "MOO Mail Message");
return result;
"Last modified Fri Apr 13 18:58:12 2001 CDT by Wizard (#2).";
.
#155:5
"===========================================================";
"Copyright (C) 1999-2000, Jan Rune Holmevik";
"Deletes a specified MOO mail message and renumbers the folder";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
body = {};
{user, data} = args;
alert = "";
if ({{}, {}} != data)
try
folder = toobj(data[2][1]);
msg_nr = toint(data[2][2]);
if ((folder.owner == user) || user.wizard)
"-----------------------------";
"Delete selected message";
"-----------------------------";
index = 1;
new = {};
old = folder.messages;
"-----------------------------";
"Rebuild folder and renumber messages";
"-----------------------------";
for msg in [1..length(old)]
suspend(0);
if (toint(old[msg][1]) != msg_nr)
new = $list_utils:append(new, {{index, old[msg][2]}});
index = index + 1;
endif
endfor
folder.messages = new;
alert = tostr("alert('Message ", msg_nr, " on ", folder.name, " has been deleted.');window.close();");
else
return "403_Forbidden";
endif
return this:contents_html(user, {{}, {}}, alert);
except error (ANY)
alert = tostr("alert('An error occurred. Message not deleted.');");
endtry
endif
result = this:build(user, body, "MOO Mail Message", alert);
return result;
"Last modified Fri Apr 13 18:58:45 2001 CDT by Wizard (#2).";
.
#155:6
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Process data from web mail editor and send message to recipient(s)";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
"Mail editor web page requested";
{user, ?message_data = {{}, {}}} = args;
to = subject = "";
original = {};
onload = "document.editor.to.focus();";
title = $encore_web_utils:get_title(user, this, "MOO Mail Editor");
if ({{}, {}} != message_data)
folder = toobj(message_data[2][2]);
msg_nr = toint(message_data[2][3]);
message = folder.messages[msg_nr];
if ($object_utils:isa(folder, $mail_agent))
to = "*" + message[2][2];
else
to = message[2][2];
endif
subject = message[2][4];
if (!match(subject, "Re: "))
subject = "Re: " + subject;
onload = "document.editor.message.focus();";
endif
if (message_data[1][$] == "quote")
"Include original mail message";
original = message[2][6..$];
onload = "document.editor.message.focus();";
endif
endif
{help_button, help_window_function} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "Mail_Editor");
help_window_function = $encore_web_utils:insert_javascript(help_window_function);
editor = {tostr("To: <INPUT TYPE=text NAME=to VALUE=\"", to, "\" SIZE=\"30\"> (a list of names separated by spaces)<P>")};
editor = {@editor, tostr("Subject: <INPUT TYPE=text NAME=subject VALUE=\"", subject, "\" SIZE=30><P>")};
editor = {@editor, tostr("<TEXTAREA NAME=message ROWS=19 COLS=75 WRAP=virtual>")};
for line in (original)
newline = ">" + line;
editor = {@editor, newline};
endfor
editor = {@editor, "</TEXTAREA><P>"};
editor = {@editor, "<INPUT TYPE=checkbox NAME=signature VALUE=\"yes\"> Include Signature  "};
editor = {@editor, "<INPUT TYPE=submit NAME=MOOmail VALUE=\"Send MOO Mail\">"};
if (($network.active && $Xpress_client.allow_internet_mail) && (!$anonymous_users))
editor = {@editor, "<INPUT TYPE=submit NAME=email VALUE=\"Send via Internet\">"};
endif
"Modified by Traveller 7/15/01 to add a warning to the Close button, and an explanatory message to the To: field";
"editor = $encore_web_utils:append_close_button(editor);";
editor = {@editor, "<INPUT type=button name=close_window VALUE=\"Close\" onClick=\"if(confirm('This message has not been sent. Are you sure you want to discard it?')) parent.close()\">"};
editor = $encore_web_utils:form(editor, "editor", "/Xpress_MOO_mailer/send_mail");
body = $list_utils:append(help_window_function, title, help_button, editor);
if (caller == this)
return body;
endif
result = this:build(user, body, "MOO Mail Editor", onload);
return result;
"Last modified Sun Sep 30 13:17:20 2001 CDT by Wizard (#2).";
.
#155:7
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Send email and MOO mail from Xpress";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, results} = args;
body = {};
alert = "";
if ({{}, {}} == results)
"No form data supplied";
out = "400_Bad_Request";
else
from = user;
out = to = names = invalids = {};
alert = "";
out = $encore_web_utils:get_page_properties(user, this);
recipients = $string_utils:explode(results[2][1]);
subject = results[2][2];
message = results[2][3];
destination = results[1][$];
if (typeof(message) == STR)
message = {message};
endif
if (results[1][$ - 1] == "signature")
"Include signature";
signature = user.web_mail_signature;
if (typeof(signature) == STR)
message = {@message, signature};
else
message = {@message, @signature};
endif
endif
for recipient in (recipients)
"Check if recipient is a valid mail recipient";
object = $mail_agent:match_recipient_new(recipient);
if (valid(object))
to = {@to, object};
else
invalids = {@invalids, recipient};
endif
endfor
"Compile list of recipient names";
for n in [1..length(to)]
names = {@names, to[n].name};
endfor
if (to)
"Send mail to recipients and return status messages";
if ((((destination == "email") && $network.active) && $xpress_client.allow_internet_mail) && (!$anonymous_users))
origin = {"--", tostr("This email was sent with enCore Xpress ", $core_version, " from ", $network.MOO_name, " at http://", $network.site, ":", $network.webport, "/"), "--"};
message = $list_utils:append(message, origin);
success = 0;
for n in (to)
if ($object_utils:isa(n, $player))
if (result = this:sendmail(user, n.email_address, subject, @message))
alert = tostr("alert('Error sending mail: ", result, "');document.editor.to.focus();\"");
body = this:compose(user);
break;
else
success = 1;
endif
else
alert = tostr("alert('Error sending mail: You cannot send Internet mail to MOO mailing lists.');document.editor.to.focus();\"");
body = this:compose(user, "");
break;
endif
endfor
if (success)
alert = tostr("alert('Mail successfully sent via Internet. Replies to this mail message will go to your Internet email address and not to your MOO mailbox.');parent.focus();self.close();");
body = {};
endif
else
$mail_agent:send_message(from, to, subject, message);
alert = tostr("alert('Mail successfully sent via MOO mail to ", $string_utils:english_list(names), ".');parent.focus();self.close();");
body = {};
endif
else
"Notify user that one or more of the recipients are invalid";
alert = tostr("alert('Error, mail not sent. Recipient was not found in ", $network.MOO_name, ". Please try again, and remember to use player names as mail addresses, not actual email addresses even if you are sending mail via the Internet.');document.editor.to.focus();");
body = this:compose(user);
endif
endif
result = this:build(user, body, "MOO Mail Editor", alert);
return result;
"Last modified Fri Apr 13 18:58:28 2001 CDT by Wizard (#2).";
.
#155:8
"sendmail(from, to, subject, line1, line2, ...)";
"  sends mail to internet address 'to', with given subject.";
"This is a modified version of $network:sendmail to allow the Xpress web mail system to send internet email.";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
recipient = args[2];
subject = args[3];
mooname = $network.MOO_name;
mooinfo = tostr(mooname, " (http://", $network.site, ":", $network.webport, "/)");
if (reason = $network:invalid_email_address(to = recipient))
return reason;
endif
return $network:raw_sendmail(to, "Date: " + ctime(), "From: " + $network:return_address_for(user), "To: " + to, "Subject: " + subject, "Reply-to: " + user.email_address, "Errors-to: " + $network.errors_to_address, "Content-Type: text/plain", "X-Mail-Agent: " + mooinfo, @args[4..$]);
"Last modified Fri Apr 13 18:58:28 2001 CDT by Wizard (#2).";
.
#155:9
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Form for subscribing and unsubscribing from MOO mail lists";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, subscriptions} = args;
alert = "";
body = {};
if ({{}, {}} != subscriptions)
"User is submitting data";
old_lists = new_lists = {};
for n in (subscriptions[2][1..$ - 1])
new_lists = {@new_lists, toobj(n)};
endfor
for n in (user.current_message[3..$])
old_lists = {@old_lists, n[1]};
endfor
"Add new lists";
for new in (new_lists)
found = 0;
for old in (old_lists)
if (new == old)
found = 1;
break;
endif
endfor
if (!found)
user:make_current_message(new);
endif
endfor
"Remove unsubscribed lists";
for old in (old_lists)
found = 0;
for new in (new_lists)
if (old == new)
found = 1;
break;
endif
endfor
if (!found)
old:delete_notify(user);
user:kill_current_message(old);
endif
endfor
alert = "alert('Your mailing list subscriptions have been updated!')";
result = this:folders_html(user, alert);
return result;
else
form = form1 = form2 = mail_box = {};
title = $encore_web_utils:get_title(user, this, "MOO Mailing Lists");
{help_button, help_window_function} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "Mailing_Lists");
help_window_function = $encore_web_utils:insert_javascript(help_window_function);
intro = {tostr("The following mailing lists are available in ", $network.MOO_name, ". Click the checkboxes to subscribe or unsubscribe from the  lists as desired, then click the Update Subscriptions button.<P>")};
cm = user.current_message;
cm[1..2] = (verb == "@rn") ? {{this, @cm[1..2]}} | {};
for item in (cm)
mail_box = {@mail_box, item[1]};
endfor
for LIST in ($mail_agent.contents)
"Cycle through available mailing lists";
if (LIST:is_usable_by(user) && LIST:is_readable_by(user))
if (LIST in mail_box)
form1 = {@form1, tostr("<P><INPUT TYPE=checkbox NAME=list VALUE=\"", LIST, "\" CHECKED>", LIST.name, ": ")};
else
form1 = {@form1, tostr("<P><INPUT TYPE=checkbox NAME=list VALUE=\"", LIST, "\">", LIST.name, ": ")};
endif
if (typeof(LIST.description) == 4)
for line in (LIST.description)
form2 = {@form2, line};
endfor
else
form2 = {@form2, LIST.description};
endif
endif
endfor
form1 = {"List Subscriptions<HR SIZE=\"1\" NOSHADE>", @form1, "<HR SIZE=\"1\" NOSHADE>"};
form2 = {"List Description<HR SIZE=\"1\" NOSHADE>", @form2, "<HR SIZE=\"1\" NOSHADE>"};
form = $encore_web_utils:generate_table(user, {form1, form2}, this, "1");
form = {@form, tostr("<P><INPUT TYPE=submit NAME=Submit VALUE=\"Update Subscriptions\">")};
form = $encore_web_utils:append_close_button(form);
form = $encore_web_utils:form(form, "lists", "/Xpress_MOO_mailer/subscribe", tostr("setTimeout('self.close()',", $xpress_client.window_close_timeout, ");"), "folder");
body = $list_utils:append(help_window_function, title, help_button, intro, form);
endif
result = this:build(user, body, "MOO Mailing Lists", alert);
return result;
"Last modified Fri Apr 13 18:58:45 2001 CDT by Wizard (#2).";
.
#155:10
"Verb that checks to see if user has permission to see requested folder or mail message. Returns true (1) if user has permission, else returns false (0)";
{user, folder} = args;
cm = user.current_message;
cm[1..2] = (verb == "@rn") ? {{this, @cm[1..2]}} | {};
valid_folders = {user};
for item in (cm)
valid_folders = {@valid_folders, item[1]};
endfor
if (folder in valid_folders)
return 1;
else
return 0;
endif
"Last modified Fri Mar 24 21:04:37 2000 CST by Wizard (#2).";
.
#156:0
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Generates the main Object Editor screen";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
user = args[1];
base_url = tostr("http://", $network.site, ":", $network.webport, "/Xpress_Object_Editor/");
menu_frame = tostr(base_url, "menu.html");
list_frame = tostr(base_url, "blank.html");
editor_frame = tostr(base_url, "blank.html");
html = {"<FRAMESET COLS=\"50%\" FRAMEBORDER=\"1\">"};
html = {@html, tostr("<NOFRAMES><BODY>")};
html = {@html, "<H3>Sorry you need a browser that supports frames to use this system</H3>"};
html = {@html, "</BODY></NOFRAMES>"};
html = {@html, tostr("<FRAMESET COLS=\"50%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("   <FRAMESET ROWS=\"95,82%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("      <FRAME SRC=\"", menu_frame, "\" NAME=prop_editor_menu NORESIZE SCROLLING=no MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("      <FRAMESET COLS=\"70%,30%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("         <FRAME SRC=\"", editor_frame, "\" NAME=\"view\" NORESIZE MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("            <FRAME SRC=\"", list_frame, "\" NAME=\"list\" MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, "      </FRAMESET>"};
html = {@html, "   </FRAMESET>"};
html = {@html, "</FRAMESET>"};
html = {@html, "</FRAMESET>"};
result = this:build(user, html, "Xpress Object Editor", "", "", "", "", this.doctype_frameset);
return result;
"Last modified Fri Apr 13 19:00:18 2001 CDT by Wizard (#2).";
.
#156:1
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
menu = buttons = {};
base_url = tostr("http://", $network.site, ":", $network.webport, "/Xpress_Object_Editor/");
{help_button, help_window_function} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "Object_Editor");
help_window_function = $encore_web_utils:insert_javascript(help_window_function);
buttons = {tostr("Object: <INPUT TYPE=text NAME=\"search_string\" VALUE=\"\" SIZE=15><INPUT TYPE=submit NAME=Submit VALUE=\"View\"><INPUT TYPE=\"button\" VALUE=\"My Objects\" onClick=\"parent.list.location.href = '", base_url, "list_objects_html", "'\"><INPUT TYPE=\"button\" VALUE=\"Create New Object\" onClick=\"parent.list.location.href = '", base_url, "classes_html", "'\">")};
title = $encore_web_utils:get_title(user, this, "Xpress Object Editor");
menu = $list_utils:append(title, help_button, buttons);
menu = $encore_web_utils:append_close_button(menu);
menu = $encore_web_utils:form(menu, "view", "/Xpress_Object_Editor/view_html", "", "view");
body = $list_utils:append(help_window_function, menu);
result = this:build(user, body, "Xpress Object Editor", "document.search.search_string.focus();", "properties");
return result;
"Last modified Fri Apr 13 19:00:18 2001 CDT by Wizard (#2).";
.
#156:2
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"User wants to edit an object. Check if object exists, is reachable and editable by user.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, form_data, ?alert = ""} = args;
body = data = {};
if (form_data != {{}, {}})
"User has asked for an object to view";
requested = form_data[2][1];
if (valid(object = $string_utils:match_object(requested, user.location, user)) || valid(object = $string_utils:match_object(tostr("#", requested), user.location, user)))
if ($encore_web_utils:permission_to_edit(user, object))
body = this:editor_html(user, object);
else
alert = tostr("alert('Sorry, you may only work with objects that you own. The object, ", object.name, " belongs to ", object.owner.name, ".')");
endif
else
alert = "alert('Could not find an object by that name. You must either hold an object, or be in the same room as the object in order to refer to it by name.')";
endif
endif
result = this:build(user, body, "Xpress Edit", alert);
return result;
"Last modified Fri Apr 13 19:01:46 2001 CDT by Wizard (#2).";
.
#156:3
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
body = exits = {};
alert = "";
app = vrb = 0;
if (data != {{}, {}})
object = toobj(data[2][1]);
try
app = toobj(data[2][2]);
vrb = tostr(data[2][3]);
except error (ANY)
endtry
name = $string_utils:capitalize(object.name);
if ((user != object.owner) && (!user.wizard))
alert = "alert('Permission denied: Only the owner of this object may recycle it.');";
elseif (is_player(object))
alert = "alert('Permission denied: Player objects cannot be recycled.');";
else
if ($object_utils:isa(object, $room))
exits = $list_utils:append(object.exits, object.entrances);
endif
result = user:_recycle(object);
if (typeof(result) == ERR)
alert = result;
else
alert = tostr("Object ", name, " (", object, ") recycled.");
if (exits)
"----------------------------------------------";
"The object was a room so we need to clean up various things";
"----------------------------------------------";
for exit in (exits)
user:_recycle(exit);
endfor
alert = alert + " Exits and entrances cleaned up and recycled.";
if (object == user.home)
user.home = $player_start;
alert = tostr(alert, " ", $player_start.name, " is your new home.");
endif
endif
endif
endif
endif
if ((app != 0) && (vrb != 0))
result = this:build(user, body = app:(vrb)(user), app.name, $encore_web_utils:make_alert(alert));
else
result = this:build(user, body, "Xpress Recycler", $encore_web_utils:make_alert(alert) + ";parent.close();");
endif
return result;
"Last modified Fri Apr 13 19:01:46 2001 CDT by Wizard (#2).";
.
#156:4
"Copyright (C) 1999, Jan Rune Holmevik";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, requested} = args;
body = {};
javascript = "";
if ({{}, {}} != requested)
data = requested;
destination = toobj(data[2][1]);
origin = user.location;
user:tell("You move to ", destination.name);
for person in (setremove(origin.contents, user))
person:tell(user.name, " leaves for ", destination.name);
endfor
user.update_web_display = 1;
user:moveto(destination);
dest = user.location.contents;
for person in (setremove(dest, user))
person:tell(user.name, " arrives from ", origin.name);
endfor
javascript = "parent.close()";
endif
result = this:pre_assemble(user, body, "Move", javascript, "");
return result;
"Last modified Thu Apr  8 12:07:25 1999 CDT by Wizard (#2).";
.
#156:5
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Lists a user's objects";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
body = objects = {};
base_url = tostr("http://", $network.site, ":", $network.webport, "/Xpress_Object_Editor/view_html");
title = $encore_web_utils:get_subtitle(user, this, "My Objects");
for object in (user.owned_objects)
"Compile list of objects the user owns";
if ($object_utils:isa(object, $encore_web_class))
if (object.icon != "")
objects = {@objects, tostr("<A HREF=\"", base_url, "?", toint(object), "\">", $encore_web_utils:get_icon(user, object), object.name, " (", object, ")</A><BR>")};
else
objects = {@objects, tostr("<LI><A HREF=\"", base_url, "?", toint(object), "\">", object.name, "</A><BR>")};
endif
endif
endfor
body = $list_utils:append(title, objects);
result = this:build(user, body, "My Objects", "", "view");
return result;
"Last modified Fri Apr 13 19:01:00 2001 CDT by Wizard (#2).";
.
#156:6
"===========================================================";
"Copyright (C) 1998-2001, Jan Rune Holmevik";
"Display a list of generics sorted in categories";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
result = body = {};
alert = "";
base_url = tostr("http://", $network.site, ":", $network.webport, "/");
external_baseurl = $xpress_client.external_baseurl + $xpress_client.icons_folder;
title = $encore_web_utils:get_subtitle(user, this, "Classes");
{help_button, help_function} = $encore_web_utils:insert_context_sensitive_help(user, $builder_help, "Xpress_Generics");
functions = $encore_web_utils:insert_javascript(help_function);
if ({{}, {}} == data)
requested = 0;
else
requested = data[2][1];
endif
try
body = {"<P>"};
generics = this.generics;
for n in [1..length(generics)]
if (n == toint(requested))
body = {@body, tostr("<A HREF=\"", base_url, "Xpress_object_editor/classes.html?", tostr(n), "\"><IMG SRC=\"", external_baseurl, "list_view2.gif\" BORDER=0 ALIGN=middle> ", generics[n][1], "</A><BR>")};
body = {@body, "<UL>"};
for object in (generics[n][2])
body = {@body, tostr("<A HREF=\"", base_url, "xpress_object_editor/create.html?", toint(object), "\" TARGET=\"view\">", $encore_web_utils:get_icon(user, object), object.name)};
body = {@body, "<BR>"};
endfor
body = {@body, "</UL>"};
else
body = {@body, tostr("<A HREF=\"", base_url, "Xpress_object_editor/classes_html?", tostr(n), "\"><IMG SRC=\"", external_baseurl, "list_view1.gif\" BORDER=0 ALIGN=middle> ", generics[n][1], "</A><BR>")};
endif
endfor
except error (ANY)
alert = "alert('There is a problem with the Xpress Class Browser. Please notify a MOO administrator!')";
endtry
body = $list_utils:append(functions, title, help_button, body);
result = this:build(user, body, "Xpress Class Browser", alert);
return result;
"Last modified Fri Apr 13 19:01:00 2001 CDT by Wizard (#2).";
.
#156:7
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Handle web creation of MOO objects";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, requested, ?alert = ""} = args;
body = form = title = {};
if ({{}, {}} != requested)
if ($object_utils:isa(user, $builder))
data = requested;
object = toobj(data[2][1]);
if (1 == length(data[2]))
"User wants to create new object";
title = $encore_web_utils:get_subtitle(user, this, tostr("Creating new ", $string_utils:substitute(object.name, {{"generic", ""}})));
creation_form = object:creation_form_html(user, object);
creation_form = {@creation_form, "<P><INPUT TYPE=submit NAME=Submit VALUE=\"Create\">"};
creation_form = $encore_web_utils:form(creation_form, "create", "/Xpress_Object_Editor/create_new_object", "return validate()");
alert = alert + "document.create.name.focus();";
body = $list_utils:append(title, creation_form);
endif
else
"User is not a builder but has requested this verb anyway.";
return "403_Forbidden";
endif
endif
result = this:build(user, body, "Create Object", alert);
return result;
"Last modified Fri Apr 13 19:01:00 2001 CDT by Wizard (#2).";
.
#156:8
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Perform permission checks and creates new objects and rooms";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
alert = sethome = "";
body = {};
base_url = tostr("http://", $network.site, ":", $network.webport, "/");
if (data == {{}, {}})
return "400_Bad_Request";
elseif (!$object_utils:isa(user, $builder))
return "403_Forbidden";
else
"-------------------------------------";
"Check that the user has provided necessary data";
"-------------------------------------";
parent = toobj(data[2][1]);
name = $encore_web_utils:decode_url(data[2][2]);
if (name == "")
vrb = "create_html";
object = parent;
alert = "You must name the object you wish to create. Please try again.";
else
"-------------------------------------";
"Attempt to create new object";
"-------------------------------------";
new_object = user:_create(parent, user);
if (typeof(new_object) == ERR)
"Modified by Traveller 7/15/01 to prevent a traceback when over quota...";
vrb = "create_html";
object = parent;
alert = "Quota limit exceeded, cannot create a new object at this time. Please recycle some of your objects, or contact a wizard to get more quota.";
else
destination = user;
for f in ($string_utils:char_list(user:build_option("create_flags") || ""))
new_object.(f) = 1;
endfor
$building_utils:set_names(new_object, name);
$byte_quota_utils:object_size(new_object);
if ($object_utils:isa(parent, $room))
"-------------------------------------";
"User has created a room";
"-------------------------------------";
destination = $nowhere;
connect_room = data[2][3];
if (connect_room == "1")
"-------------------------------------";
"User wants to connect her new room";
"-------------------------------------";
origin = toobj(data[2][4]);
entrance_name = tostr("To_", $string_utils:substitute(new_object.name, {{" ", "_"}}));
exit_name = tostr("To_", $string_utils:substitute(origin.name, {{" ", "_"}}));
do_recreate = !user:build_option("bi_create");
{exit, alert} = this:make_exit(user, entrance_name, origin, new_object, do_recreate, $exit);
if ((alert == "") && (exit != ""))
{exit, alert} = this:make_exit(user, exit_name, new_object, origin, do_recreate, $exit);
endif
endif
"-------------------------------------";
"Make room user's home if it's her first room";
"-------------------------------------";
if (((user.home == $player.home) && $object_utils:isa(new_object, $room)) && new_object:accept_for_abode(user))
user.home = new_object;
sethome = tostr(" ", new_object.name, " is now your new MOO home. You will start here each time you connect. You can get to your home by typing the command HOME at anytime, anywhere in the MOO.");
endif
endif
`move(new_object, destination) ! ANY';
alert = tostr("New object named ", new_object.name, " created.");
vrb = "view_html";
object = new_object;
endif
endif
endif
result = this:(vrb)(user, {{}, {tostr(object)}}, $encore_web_utils:make_alert(alert + sethome));
return result;
"Last modified Sun Sep 30 13:17:44 2001 CDT by Wizard (#2).";
.
#156:9
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Creates new exit objects";
"===========================================================";
if (caller != this)
return E_PERM;
endif
set_task_perms(caller_perms());
alert = "";
{user, spec, source, dest, ?use_recycler, ?exit_kind = $exit} = args;
exit = user:_create(exit_kind, user);
if (typeof(exit) == ERR)
alert = {"Cannot create new exit at this time. Either you are out of quota, or you have created more than 10 new objects recently. Please recycle some of your objects, or contact a MOO administrator to get more quota."};
else
for f in ($string_utils:char_list(user:build_option("create_flags") || ""))
exit.(f) = 1;
endfor
"Since builder can only use this system to dig between their own rooms, one should never run into trouble with dest_ok below";
$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);
$byte_quota_utils:object_size(exit);
return {exit, alert};
endif
return {exit, alert};
"Last modified Fri Apr 13 19:01:46 2001 CDT by Wizard (#2).";
.
#156:10
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"===========================================================";
if (caller_perms().wizard)
pass();
this.generics = {{"Rooms", {$room, $classroom, $moderated_room, $webpage}}, {"Basic Objects", {$thing, $container, $note}}, {"Educational Objects", {$recorder, $bot, $slide_projector, $webprojector, $recitable_note, $lecture, $note_board}}};
endif
"Last modified Fri Apr 13 19:00:18 2001 CDT by Wizard (#2).";
.
#156:11
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Form for editing sharing of objects.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
owners = object.shared_owners;
{help_button, help_window} = $encore_web_utils:insert_context_sensitive_help(user, $builder_help, "Xpress_Object_Sharing");
help_window = $encore_web_utils:insert_javascript(help_window);
form = $list_utils:append(help_button, help_window);
form = {@form, tostr("Enter object numbers for users you wish to share this object with. Enter only one user per line.<P><TEXTAREA NAME=shared_owners ROWS=5 COLS=10 WRAP=virtual>")};
if (typeof(owners) == LIST)
for owner in (owners)
form = {@form, tostr(owner)};
endfor
else
form = {@form, owners};
endif
form = {@form, "</TEXTAREA>"};
return form;
"Last modified Fri Apr 13 19:00:43 2001 CDT by Wizard (#2).";
.
#156:12
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Allows a user to share objects with others. This verb performs error handling and updates shared owners.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
if (data == "")
return "400_Bad_Request";
else
object = toobj(data[2][1]);
data = (typeof(data[2][2]) == LIST) ? data[2][2] | {data[2][2]};
alert = "";
owners = {};
if ((data == {}) || (data == {""}))
object.shared_owners = {};
alert = "All shared owners removed.";
else
for item in (data)
item = toobj(item);
"-----------------------------------------------------";
"  Perform error handling";
"-----------------------------------------------------";
if (!$object_utils:isa(toobj(item), $builder))
alert = tostr(item.name, " (", item, ") is not a builder and may not share your object. Please try again");
break;
else
owners = {@owners, item};
endif
endfor
if (owners)
alert = $xpress_program_editor:save_property_value(user, object, "shared_owners", owners);
endif
endif
endif
result = $xpress_object_editor:view_html(user, {{}, {tostr(object)}}, $encore_web_utils:make_alert(alert));
return result;
"Last modified Fri Apr 13 19:00:43 2001 CDT by Wizard (#2).";
.
#156:13
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Build actions and editable properties for an object";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
edit_action = 0;
stats = buttons = menu = form = {};
if (typeof(data) == LIST)
"------------------------------------------------------------";
" User has selected an editing option";
"------------------------------------------------------------";
object = toobj(data[2][1]);
edit_action = data[2][2];
else
object = toobj(data);
endif
base_url = tostr("http://", $network.site, ":", $network.webport, "/");
confirmDelete = {"function confirmDelete() {", tostr("   if (confirm('", $xpress_program_editor:delete_warning(object), " Are you sure you want to permanently recycle this object? This action cannot be undone!')) {"), tostr("       location.href = '", base_url, "Xpress_object_editor/recycle_object?", toint(object), "';"), "       return true;", "   }", "   return false;", "}"};
functions = $encore_web_utils:insert_javascript(confirmDelete);
"----------------------------------------------------------------";
" Build display header";
"----------------------------------------------------------------";
title = $encore_web_utils:get_subtitle(user, this, tostr("Editing ", object.name, " (", object, ")"));
"----------------------------------------------------------------";
" Object statistics";
"----------------------------------------------------------------";
try
stats = {@stats, tostr("<P>", $encore_web_utils:get_icon(user, object))};
stats = {@stats, tostr("<BR><B>Owner:</B> ", object.owner.name, " (", object.owner, ")")};
if (object.location == $nowhere)
stats = {@stats, "<BR><B>Location:</B> Limbo."};
else
stats = {@stats, tostr("<BR><B>Location:</B> ", object.location.name, " (", object.location, ")")};
endif
if ($object_utils:isa(object, $exit))
stats = {@stats, tostr("<BR><B>Origin:</B> ", object.source.name, " (", object.source, ") <BR><B>Destination:</B> ", object.dest.name, " (", object.dest, ")")};
endif
if ($quota_utils.byte_based && $object_utils:has_property(object, "object_size"))
stats = {@stats, tostr("<BR><B>Size:</B> ", $string_utils:group_number(object.object_size[1]), " bytes at ", user:ctime(object.object_size[2]))};
endif
stats = {@stats, tostr("<BR><B>Viewed:</B> ", object.hits, " times by Xpress Users.<P>")};
except error (ANY)
endtry
"----------------------------------------------------------------";
" Build actions and editing options for this object";
"----------------------------------------------------------------";
buttons = {};
if (((!$object_utils:isa(object, $player)) && (user == object.owner)) || user.wizard)
buttons = {@buttons, tostr("<INPUT TYPE=\"button\" VALUE=\"Recycle This Object\" onClick=\"return confirmDelete()\">")};
endif
if ($object_utils:isa(object, $room))
buttons = {@buttons, tostr("<INPUT TYPE=\"button\" VALUE=\"Go to Room\" onClick=\"location.href = '", base_url, "Xpress_Object_Editor/move_to_room?", toint(object), "'\">")};
endif
buttons = $encore_web_utils:append_close_button(buttons);
buttons = $encore_web_utils:form(buttons);
"----------------------------------------------------------------";
" Add context sensitive help";
"----------------------------------------------------------------";
{help_button, help_window} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "Quick_Editing");
help_window = $encore_web_utils:insert_javascript(help_window);
help = $list_utils:append(help_button, help_window);
"----------------------------------------------------------------";
" Generate list of most important editable properties";
"----------------------------------------------------------------";
hidden = {tostr("<INPUT TYPE=hidden NAME=object VALUE=\"", toint(object), "\">")};
properties = object:xpress_edit(user);
properties = {"<SELECT NAME=\"Edit_Options\" onChange=this.form.submit()><OPTION VALUE=\"0\">Select Edit Option", @properties, "</SELECT>"};
prop_list = $list_utils:append(hidden, properties);
prop_list = $encore_web_utils:form(prop_list, "edit", "/Xpress_Object_Editor/Editor.html");
menu = $encore_web_utils:generate_table(user, {prop_list, buttons}, this);
"----------------------------------------------------------------";
" Add editing form if requested";
"----------------------------------------------------------------";
if (edit_action)
form = $list_utils:append(form, this:(edit_action)(user, object));
form = {@form, "<P><INPUT TYPE=submit NAME=Submit VALUE=\"Save Changes\">"};
form = $list_utils:append(hidden, form);
if (edit_action == "edit_password")
form = $encore_web_utils:form(form, "Change Password", "/Xpress_Object_Editor/change_password");
elseif (edit_action == "edit_sharing")
form = $encore_web_utils:form(form, "Share Object", "/Xpress_Object_Editor/share_object");
else
form = $encore_web_utils:form(form, "Xpress_Edit", "/Xpress_Object_Editor/save_property");
endif
form = $list_utils:append({"<HR>"}, form, {"<HR>"});
endif
body = $list_utils:append(functions, title, stats, help, menu, form);
return body;
"Last modified Fri Jul 20 09:03:35 2001 CDT by Wizard (#2).";
.
#156:14
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Save properties.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
if (data == {{}, {}})
return "400_Bad_Request";
else
object = toobj(data[2][1]);
result = this:set_property(user, object, data);
endif
return this:view_html(user, {{}, {tostr(object)}}, result);
"Last modified Fri Apr 13 19:02:02 2001 CDT by Wizard (#2).";
.
#156:15
"===========================================================";
"Modifications copyright (C) 2001, Jan Rune Holmevik";
"Verb for changing object names. This is essensially an Xpress version of $player:@rename";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object, new_name} = args;
old_name = object.name;
old_aliases = object.aliases;
if (e = $building_utils:set_names(object, new_name))
if (strcmp(object.name, old_name) == 0)
name_message = tostr("Name of ", object, " (", old_name, ") is unchanged");
else
name_message = tostr("Name of object ", object, " changed to ", object.name, " ");
endif
aliases = $string_utils:english_list(object.aliases);
if (object.aliases == old_aliases)
alias_message = tostr(".  Aliases are unchanged (", aliases, ").");
else
alias_message = tostr(", with aliases ", aliases, ".");
endif
status = name_message + alias_message;
elseif (e == E_INVARG)
status = "That particular name change not allowed. ";
if (object == user)
status = status + tostr($player_db:why_bad_name(user, new_name));
endif
elseif (e == E_NACC)
status = "Oops.  You can't update that name right now. Try again in a few minutes.";
elseif (e == E_ARGS)
status = tostr("Sorry, name too long.  Maximum number of characters in a name:  ", $login.max_player_name);
elseif (e == 0)
status = "Name and aliases remain unchanged.";
else
status = tostr(e);
endif
return status;
"Last modified Fri Apr 13 19:02:02 2001 CDT by Wizard (#2).";
.
#156:16
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Allows user to change password in Xpress";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
javascript = "";
if (data == "")
return "400_Bad_Request";
else
form_data = data;
who = form_data[2][1];
old = form_data[2][3];
new = form_data[2][4];
if (toobj(who) != user)
return "403_Forbidden";
else
if (user.password != crypt(old, user.password))
javascript = "alert('The password you typed does not match your old password. Please try again and remember that passwords are case sensitive which means that the words PASSWORD and password are two completely different passwords.')";
else
user.password = crypt(new);
user.last_password_time = time();
javascript = "alert('Your password has been changed. Please keep it secure and do not let anyone see it.')";
endif
endif
endif
result = $xpress_object_editor:view_html(user, {{}, {tostr(user)}}, javascript);
return result;
"Last modified Fri Apr 13 19:03:03 2001 CDT by Wizard (#2).";
.
#156:17
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Update properties";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object, data} = args;
body = {};
if ($encore_web_utils:permission_to_edit(user, object))
try
"User has permission to edit this object";
saved_properties = name_change = gender_change = alert = "";
"Cycle through all data fields minus the first which is always the object number, and the last which is persumed to be the submit/save button data";
for field in [2..length(data[$]) - 1]
property = data[1][field];
new_value = data[2][field];
"----------------------------------------------------------------";
"Update property value. Certain properties must be treated as special cases.";
"----------------------------------------------------------------";
if (property == "name")
name_change = this:rename(user, object, new_value);
elseif (is_player(object) && (property == "gender"))
object:set_gender(new_value);
gender_change = "Gender set to " + new_value;
else
saved_properties = saved_properties + $xpress_program_editor:save_property_value(user, object, property, new_value);
endif
if ($object_utils:isa(object, $news_item))
"-------------------------------------------------------------";
"Object is a $news_item. Notify users of update";
"-------------------------------------------------------------";
$news:notify_Xpress_change(object);
endif
endfor
"-----------------------------------------------------";
"      Generate alert to user";
"-----------------------------------------------------";
if (name_change != "")
alert = alert + tostr("alert('", name_change, "');");
endif
if (gender_change != "")
alert = alert + tostr("alert('", gender_change, "');");
endif
if (saved_properties != "")
alert = alert + tostr("alert('", saved_properties, "');");
endif
except error (ANY)
alert = "alert('There is a problem with the Xpress Object Editor. Please notify a MOO administrator!');";
endtry
else
return "403_Forbidden";
endif
return alert;
"Last modified Fri Apr 13 19:02:02 2001 CDT by Wizard (#2).";
.
#156:18
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
name = object.name;
for alias in (object.aliases)
if (alias != object.name)
name = (name + ", ") + alias;
endif
endfor
form = {tostr("Name and Aliases: <P><INPUT TYPE=text NAME=name VALUE=\"", name, "\" SIZE=40>")};
return form;
"Last modified Fri Apr 13 19:02:19 2001 CDT by Wizard (#2).";
.
#156:19
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
form = {};
form = {@form, tostr("<P>Audio URL: <INPUT TYPE=text NAME=audio_url VALUE=\"", object.audio_url, "\" SIZE=60></P>")};
form = {@form, tostr("<P>Visual URL: <INPUT TYPE=text NAME=url_address VALUE=\"", object.url_address, "\" SIZE=60></P>")};
form = {@form, tostr("<P>Visual Height: <INPUT TYPE=text NAME=web_height VALUE=\"", object.web_height, "\" SIZE=6> Visual Width: <INPUT TYPE=text NAME=web_width VALUE=\"", object.web_width, "\" SIZE=6> Pixels</P>")};
form = {@form, tostr("<P>Visual Content Alignment: <SELECT NAME=\"Media_Content_Alignment\">")};
for pos in ($xpress_client.web_alignment_options)
if (pos[2] == object.Media_Content_Alignment)
form = {@form, tostr("<OPTION VALUE=", pos[2], " SELECTED>", pos[1])};
else
form = {@form, tostr("<OPTION VALUE=", pos[2], ">", pos[1])};
endif
endfor
form = {@form, "</SELECT></P>"};
return form;
"Last modified Sun Sep 30 12:47:44 2001 CDT by Wizard (#2).";
.
#156:20
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
form = {tostr("Icon: <INPUT TYPE=text NAME=icon VALUE=\"", object.icon, "\" SIZE=70>")};
return form;
"Last modified Fri Apr 13 19:02:19 2001 CDT by Wizard (#2).";
.
#156:21
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
descr = object.description;
form = {tostr("Description: <BR><TEXTAREA NAME=description ROWS=10 COLS=70 WRAP=virtual>")};
if (typeof(descr) == STR)
form = {@form, object.description};
else
form = {@form, @object.description};
endif
form = {@form, "</TEXTAREA>"};
form = {@form, tostr("<P>Text Alignment: <SELECT NAME=\"Text_Alignment\">")};
for align in ($xpress_client.web_alignment_options)
if (align[2] == object.Text_Alignment)
form = {@form, tostr("<OPTION VALUE=", align[2], " SELECTED>", align[1])};
else
form = {@form, tostr("<OPTION VALUE=", align[2], ">", align[1])};
endif
endfor
form = {@form, "</SELECT></P>"};
return form;
"Last modified Sun Sep 30 12:47:44 2001 CDT by Wizard (#2).";
.
#156:22
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
form = {tostr("ASCII Drawing/Art: <BR><TEXTAREA NAME=drawing ROWS=10 COLS=70 WRAP=virtual>")};
drawing = object.drawing;
if (typeof(drawing) == STR)
form = {@form, object.drawing};
else
for line in (object.drawing)
form = {@form, line};
endfor
endif
form = {@form, "</TEXTAREA><BR>"};
form = {@form, "Display ASCII graphics in Xpress?"};
if (object.display_ascii)
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"1\" name=\"display_ascii\" CHECKED> Yes <INPUT TYPE=\"radio\" value=\"0\" name=\"display_ascii\"> No <P>")};
else
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"1\" name=\"display_ascii\"> Yes <INPUT TYPE=\"radio\" value=\"0\" name=\"display_ascii\" CHECKED> No")};
endif
return form;
"Last modified Fri Apr 13 19:02:47 2001 CDT by Wizard (#2).";
.
#156:23
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
form = {};
if ($encore_web_object.use_external_stylesheet)
form = {@form, "This MOO uses external style sheets for object layout. Appearance cannot be modified by users.<P>"};
else
form = {@form, "<P>Font: <SELECT NAME=\"web_font\">"};
for font in ($xpress_client.web_fonts)
if (font == object.web_font)
form = {@form, tostr("<OPTION VALUE=", font, " SELECTED>", font)};
else
form = {@form, tostr("<OPTION VALUE=", font, ">", font)};
endif
endfor
form = {@form, "</SELECT>  Size: <SELECT NAME=\"web_font_size\">"};
for size in ($xpress_client.web_font_sizes)
if (size == object.web_font_size)
form = {@form, tostr("<OPTION VALUE=", size, " SELECTED>", size)};
else
form = {@form, tostr("<OPTION VALUE=", size, ">", size)};
endif
endfor
form = {@form, "</SELECT>  Color: <SELECT NAME=\"web_text_color\">"};
for color in ($xpress_client.web_colors)
if (color == object.web_text_color)
form = {@form, tostr("<OPTION VALUE=", color, " SELECTED>", color)};
else
form = {@form, tostr("<OPTION VALUE=", color, ">", color)};
endif
endfor
form = {@form, "</SELECT><P>Link: <SELECT NAME=\"web_link_color\">"};
for color in ($xpress_client.web_colors)
if (color == object.web_link_color)
form = {@form, tostr("<OPTION VALUE=", color, " SELECTED>", color)};
else
form = {@form, tostr("<OPTION VALUE=", color, ">", color)};
endif
endfor
form = {@form, "</SELECT><P>Visited Link: <SELECT NAME=\"web_vlink_color\">"};
for color in ($xpress_client.web_colors)
if (color == object.web_vlink_color)
form = {@form, tostr("<OPTION VALUE=", color, " SELECTED>", color)};
else
form = {@form, tostr("<OPTION VALUE=", color, ">", color)};
endif
endfor
form = {@form, "</SELECT><P>Active Link: <SELECT NAME=\"web_alink_color\">"};
for color in ($xpress_client.web_colors)
if (color == object.web_alink_color)
form = {@form, tostr("<OPTION VALUE=", color, " SELECTED>", color)};
else
form = {@form, tostr("<OPTION VALUE=", color, ">", color)};
endif
endfor
form = {@form, "</SELECT><P>Background Color: <SELECT NAME=\"web_bgcolor\">"};
for color in ($xpress_client.web_colors)
if (color == object.web_bgcolor)
form = {@form, tostr("<OPTION VALUE=", color, " SELECTED>", color)};
else
form = {@form, tostr("<OPTION VALUE=", color, ">", color)};
endif
endfor
form = {@form, "</SELECT>"};
form = {@form, tostr("<P>Background Image URL:<P><INPUT TYPE=text NAME=web_background VALUE=\"", object.web_background, "\" SIZE=70><P>")};
endif
return form;
"Last modified Fri Apr 13 19:02:47 2001 CDT by Wizard (#2).";
.
#156:24
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
form = {};
form = {@form, tostr("<P>Note Text: </P><TEXTAREA NAME=text ROWS=10 COLS=70 WRAP=virtual>")};
if (typeof(object.text) == STR)
form = {@form, object.text};
else
for line in (object.text)
form = {@form, line};
endfor
endif
form = {@form, "</TEXTAREA>"};
form = {@form, tostr("<P>Text Alignment: <SELECT NAME=\"Note_Text_Alignment\">")};
for align in ($xpress_client.web_alignment_options)
if (align[2] == object.Note_Text_Alignment)
form = {@form, tostr("<OPTION VALUE=", align[2], " SELECTED>", align[1])};
else
form = {@form, tostr("<OPTION VALUE=", align[2], ">", align[1])};
endif
endfor
form = {@form, "</SELECT></P>"};
form = {@form, "<P>Encrypt note: "};
if (object.encryption_key != 0)
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"", user, "\" name=\"encryption_key\" CHECKED> Yes <INPUT TYPE=\"radio\" value=\"0\" name=\"encryption_key\"> No <P>")};
else
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"", user, "\" name=\"encryption_key\"> Yes <INPUT TYPE=\"radio\" value=\"0\" name=\"encryption_key\" CHECKED> No")};
endif
return form;
"Last modified Sun Sep 30 12:48:18 2001 CDT by Wizard (#2).";
.
#156:25
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
form = {};
form = {@form, tostr("<P>Lecture Text: </P><TEXTAREA NAME=text ROWS=10 COLS=70 WRAP=virtual>")};
if (typeof(object.text) == STR)
form = {@form, object.text};
else
for line in (object.text)
form = {@form, line};
endfor
endif
form = {@form, "</TEXTAREA>"};
return form;
"Last modified Fri Apr 13 19:03:03 2001 CDT by Wizard (#2).";
.
#156:26
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
{password_help_button, help_window_function} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "Password");
help_window_function = $encore_web_utils:insert_javascript(help_window_function);
password = {tostr("<INPUT TYPE=hidden NAME=object VALUE=\"", toint(object), "\"><A NAME=password></A><B>Password:</B><P>")};
password = $list_utils:append(password_help_button, help_window_function, password);
password = {@password, tostr("Old Password: <INPUT TYPE=password NAME=\"old_password\" VALUE=\"\" SIZE=10> New Password: <INPUT TYPE=password NAME=\"new_password\" VALUE=\"\" SIZE=10><P>")};
return password;
"Last modified Fri Apr 13 19:03:03 2001 CDT by Wizard (#2).";
.
#156:28
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
form = {"Gender: <SELECT NAME=\"gender\">"};
user_gender = user.gender;
available_genders = $gender_utils.genders;
for option in (available_genders)
if (option == user_gender)
form = {@form, tostr("<OPTION SELECTED> ", option)};
else
form = {@form, tostr("<OPTION> ", option)};
endif
endfor
form = {@form, "</SELECT>"};
return form;
"Last modified Fri Apr 13 19:03:03 2001 CDT by Wizard (#2).";
.
#156:29
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
form = {"<B>User information :</B>"};
form = {@form, tostr("<P>Home Page URL: <INPUT TYPE=\"text\" NAME=homepage VALUE=\"", object.homepage, "\" SIZE=40><P>")};
form = {@form, tostr("<P>MOO activity message: <INPUT TYPE=\"text\" NAME=\"doing\" VALUE=\"", object.doing, "\" SIZE=40><P>")};
profile = object.interests_msg;
form = {@form, tostr("Additional personal and professional information: <BR><TEXTAREA NAME=interests_msg ROWS=10 COLS=70 WRAP=virtual>")};
if (typeof(profile) == STR)
form = {@form, profile};
else
form = {@form, @profile};
endif
form = {@form, "</TEXTAREA><P>"};
signature = object.web_mail_signature;
form = {@form, "MOO Mail Signature:<BR><TEXTAREA NAME=web_mail_signature ROWS=2 COLS=70 WRAP=virtual>"};
if (typeof(signature) == STR)
form = {@form, signature};
else
form = {@form, @signature};
endif
form = {@form, "</TEXTAREA><P>"};
return form;
"Last modified Fri Apr 13 19:03:03 2001 CDT by Wizard (#2).";
.
#156:30
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
viewThemes = $encore_web_utils:javascript_window_open($help_browser, "viewThemes", tostr("http://", $network.site, ":", $network.webport, "/Xpress_Client/view_themes"), 300, 500);
viewThemes = $encore_web_utils:insert_javascript(viewThemes);
form = {"<P><B>Xpress Theme Settings</B></P>"};
form = {@form, "<P>Current theme: <SELECT NAME=\"xpress_theme\">"};
for theme in ($xpress_client.themes)
if (theme[2] == object.xpress_theme)
form = {@form, tostr("<OPTION VALUE=", theme[2], " SELECTED>", theme[1])};
else
form = {@form, tostr("<OPTION VALUE=", theme[2], ">", theme[1])};
endif
endfor
form = {@form, "</SELECT>"};
form = {@form, $encore_web_utils:get_icon(user, $help_browser, "help.gif", "View Xpress Themes", "viewThemes()")};
form = {@form, "<P>(Changes take effect when you reconnect)</P>"};
form = {@form, "<P>Xpress Icon Size: <SELECT NAME=\"icon_size\">"};
for size in ($xpress_client.icon_sizes)
if (size[2] == object.icon_size)
form = {@form, tostr("<OPTION VALUE=", size[2], " SELECTED>", size[1])};
else
form = {@form, tostr("<OPTION VALUE=", size[2], ">", size[1])};
endif
endfor
form = {@form, "</SELECT>"};
form = {@form, "<P>Show Contextual Menu: "};
if (user.show_contextual_menu)
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"1\" name=\"show_contextual_menu\" CHECKED> Yes <INPUT TYPE=\"radio\" value=\"0\" name=\"show_contextual_menu\"> No <P>")};
else
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"1\" name=\"show_contextual_menu\"> Yes <INPUT TYPE=\"radio\" value=\"0\" name=\"show_contextual_menu\" CHECKED> No")};
endif
if (!object.use_external_stylesheet)
"------------------------------------------------";
"Add option for overriding object specific styles";
"------------------------------------------------";
form = {@form, "<P>Override object specific styles: "};
if (user.override_object_styles)
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"1\" name=\"override_object_styles\" CHECKED> Yes <INPUT TYPE=\"radio\" value=\"0\" name=\"override_object_styles\"> No <P>")};
else
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"1\" name=\"override_object_styles\"> Yes <INPUT TYPE=\"radio\" value=\"0\" name=\"override_object_styles\" CHECKED> No")};
endif
form = {@form, "<P>When the override styles option is on, you will use your own appearance settings to view MOO content. Select 'Edit Object Appearance' from the editing options above to modify these settings.</P>"};
endif
form = $list_utils:append(viewThemes, form);
return form;
"Last modified Fri Apr 13 19:03:20 2001 CDT by Wizard (#2).";
.
#156:31
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
object = $xpress_client;
{layout_help_button, layout_help_window} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "Xpress_Screen_Layout");
layout_help_window = $encore_web_utils:insert_javascript(layout_help_window);
form = {tostr("<B>Xpress Screen Settings:</B>")};
form = $list_utils:append(layout_help_button, layout_help_window, form);
form = {@form, "<P>Screen Size: "};
form = {@form, "<SELECT NAME=Xpress_Screen_Size>"};
for size in (object.screen_sizes)
if (size[1] == user.xpress_screen_size)
form = {@form, tostr("<OPTION VALUE=\"", size[1], "\" SELECTED>", size[2])};
else
form = {@form, tostr("<OPTION VALUE=\"", size[1], "\">", size[2])};
endif
endfor
form = {@form, "</SELECT><P>Layout: "};
form = {@form, "<SELECT NAME=Xpress_layout>"};
for layout in (object.screen_layouts)
if (layout[1] == user.xpress_layout)
form = {@form, tostr("<OPTION VALUE=\"", layout[1], "\" SELECTED>", layout[2])};
else
form = {@form, tostr("<OPTION VALUE=\"", layout[1], "\">", layout[2])};
endif
endfor
form = {@form, "</SELECT><P>Screen Division: "};
form = {@form, "<SELECT NAME=Xpress_Screen_Division>"};
for division in (object.screen_divisions)
if (division[1] == user.xpress_screen_division)
form = {@form, tostr("<OPTION VALUE=\"", division[1], "\" SELECTED>", division[2])};
else
form = {@form, tostr("<OPTION VALUE=\"", division[1], "\">", division[2])};
endif
endfor
form = {@form, "</SELECT><P>Changes take effect when you reconnect"};
return form;
"Last modified Fri Apr 13 19:03:21 2001 CDT by Wizard (#2).";
.
#156:32
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
form = {"<B>Talk Area Settings:</B><P>Talk Area Font : <SELECT NAME=\"java_font\">"};
for font in ($xpress_client.java_fonts)
if (font == user.java_font)
form = {@form, tostr("<OPTION VALUE=", font, " SELECTED>", font)};
else
form = {@form, tostr("<OPTION VALUE=", font, ">", font)};
endif
endfor
form = {@form, "</SELECT>  Size: <SELECT NAME=\"java_font_size\">"};
for size in ($xpress_client.java_font_sizes)
if (size == user.java_font_size)
form = {@form, tostr("<OPTION VALUE=", size, " SELECTED>", size)};
else
form = {@form, tostr("<OPTION VALUE=", size, ">", size)};
endif
endfor
form = {@form, "</SELECT>"};
form = {@form, " Local Echo: <SELECT NAME=\"java_client_localecho\">"};
echo = user.java_client_localecho;
if (echo == "true")
form = {@form, tostr("<OPTION VALUE=\"True\" SELECTED>On<OPTION VALUE=\"False\">Off")};
else
form = {@form, tostr("<OPTION VALUE=\"True\">On<OPTION VALUE=\"False\" SELECTED>Off")};
endif
form = {@form, "</SELECT><P>Changes take effect when you reconnect"};
return form;
"Last modified Fri Apr 13 19:03:21 2001 CDT by Wizard (#2).";
.
#156:33
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
form = {"<B>Xpress Sound Settings:</B><P>Sound Volume:"};
form = {@form, "<SELECT NAME=sound_volume>"};
for option in ($xpress_client.sound_volumes)
if (option[1] == user.sound_volume)
form = {@form, tostr("<OPTION VALUE=\"", option[1], "\" SELECTED>", option[2])};
else
form = {@form, tostr("<OPTION VALUE=\"", option[1], "\">", option[2])};
endif
endfor
form = {@form, "</SELECT> Mail Notification:"};
form = {@form, "<SELECT NAME=mail_notify_sound>"};
for option in ($xpress_client.mail_notification_sounds)
if (option[1] == user.mail_notify_sound)
form = {@form, tostr("<OPTION VALUE=\"", option[1], "\" SELECTED>", option[2])};
else
form = {@form, tostr("<OPTION VALUE=\"", option[1], "\">", option[2])};
endif
endfor
form = {@form, "</SELECT>"};
return form;
"Last modified Fri Apr 13 19:03:21 2001 CDT by Wizard (#2).";
.
#156:34
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
{help_button, help_window} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "Player_Message_Editing");
help_window = $encore_web_utils:insert_javascript(help_window);
form = $list_utils:append(help_button, help_window);
form = {@form, tostr("<B>Player Page and Teleport Messages:</B><P>")};
form = {@form, tostr("<P>Printed when your page has been successfully received:<BR><INPUT TYPE=text NAME=page_echo_msg VALUE=\"", object.page_echo_msg, "\" SIZE=70><P>")};
form = {@form, tostr("Printed to people you page:<BR><INPUT TYPE=text NAME=page_origin_msg VALUE=\"", object.page_origin_msg, "\" SIZE=70><P>")};
form = {@form, tostr("Printed to people who page you when you are not connected:<BR><INPUT TYPE=text NAME=page_absent_msg VALUE=\"", object.page_absent_msg, "\" SIZE=70><P>")};
form = {@form, tostr("Printed to other people when you leave a room:<BR><INPUT TYPE=text NAME=odepart_msg VALUE=\"", object.odepart_msg, "\" SIZE=70><P>")};
form = {@form, tostr("Printed to other people when you enter a room:<BR><INPUT TYPE=text NAME=oarrive_msg VALUE=\"", object.oarrive_msg, "\" SIZE=70><P>")};
return form;
"Last modified Fri Apr 13 19:03:37 2001 CDT by Wizard (#2).";
.
#156:35
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
{help_button, help_window} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "Exit_Message_Editing");
help_window = $encore_web_utils:insert_javascript(help_window);
form = $list_utils:append(help_button, help_window);
form = {@form, "<B>Exit Options</B><P>"};
form = {@form, "<P>Exit visible: "};
if (object.obvious)
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"1\" name=\"obvious\" CHECKED> Yes <INPUT TYPE=\"radio\" value=\"0\" name=\"obvious\"> No <P>")};
else
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"1\" name=\"obvious\"> Yes <INPUT TYPE=\"radio\" value=\"0\" name=\"obvious\" CHECKED> No")};
endif
form = {@form, "<P>Several kinds of messages can be set on an exit object. They are printed to various audiences at certain times whenever an attempt is made to go through an exit. Creative exit messages will certainly make your MOO creations more intersting for others to explore.</P>"};
form = {@form, tostr("Printed to the player just before they go through the exit:<BR><INPUT TYPE=text NAME=leave_msg VALUE=\"", object.leave_msg, "\" SIZE=70><P>")};
form = {@form, tostr("Printed to others when a player leaves the room through the exit.:<BR><INPUT TYPE=text NAME=oleave_msg VALUE=\"", object.oleave_msg, "\" SIZE=70><P>")};
form = {@form, tostr("Printed to others in the destination room when a player comes through the exit:<BR><INPUT TYPE=text NAME=oarrive_msg VALUE=\"", object.oarrive_msg, "\" SIZE=70><P>")};
return form;
"Last modified Fri Apr 13 19:03:37 2001 CDT by Wizard (#2).";
.
#156:36
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
form = {};
form = {@form, "<B>Web Projector Slides:</B>"};
form = {@form, "<P>Target: "};
if (object.display_in_xpress)
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"1\" name=\"display_in_xpress\" CHECKED> Xpress Client <INPUT TYPE=\"radio\" value=\"0\" name=\"display_in_xpress\"> New Browser Window <P>")};
else
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"1\" name=\"display_in_xpress\"> Xpress Client <INPUT TYPE=\"radio\" value=\"0\" name=\"display_in_xpress\" CHECKED> New Browser Window")};
endif
form = {@form, "<P>Type the URLs for the web pages you wish to display in the text area below. Type only one URL per. line. If you choose Xpress client as target, please make sure your pages does not contain javascript code that will take over the whole client window and disconnect users.<P>"};
if (typeof(object.slides) == STR)
form = {@form, tostr("Slides: <P><TEXTAREA NAME=slides ROWS=5 COLS=50 WRAP=virtual>", object.text, "</TEXTAREA><P>")};
else
form = {@form, tostr("Slides: <P><TEXTAREA NAME=slides ROWS=5 COLS=50 WRAP=virtual>")};
for line in (object.slides)
form = {@form, line};
endfor
form = {@form, "</TEXTAREA><P>"};
endif
return form;
"Last modified Fri Apr 13 19:03:37 2001 CDT by Wizard (#2).";
.
#157:0
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Generates the main program editor screen";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
user = args[1];
base_url = tostr("http://", $network.site, ":", $network.webport, "/Xpress_Program_Editor/");
menu_frame = tostr(base_url, "menu.html");
editor_frame = tostr(base_url, "blank.html");
view1_frame = tostr(base_url, "blank.html");
view2_frame = tostr(base_url, "blank.html");
html = {tostr("<FRAMESET COLS=\"50%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("<NOFRAMES><BODY>")};
html = {@html, "<H3>Sorry you need a browser that supports frames to use this system</H3>"};
html = {@html, "</BODY></NOFRAMES>"};
html = {@html, tostr("<FRAMESET COLS=\"50%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("   <FRAMESET ROWS=\"95,82%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("      <FRAME SRC=\"", menu_frame, "\" NAME=\"menu\" NORESIZE SCROLLING=\"no\" MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("      <FRAMESET COLS=\"70%,30%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("         <FRAME SRC=\"", editor_frame, "\" NAME=\"editor\" NORESIZE MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("         <FRAMESET ROWS=\"50%,50%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("            <FRAME SRC=\"", view1_frame, "\" NAME=\"view1\" MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("            <FRAME SRC=\"", view2_frame, "\" NAME=\"view2\" MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, "         </FRAMESET>"};
html = {@html, "      </FRAMESET>"};
html = {@html, "   </FRAMESET>"};
html = {@html, "</FRAMESET>"};
html = {@html, "</FRAMESET>"};
result = this:build(user, html, "Xpress Program Editor", "", "", "", "", this.doctype_frameset);
return result;
"Last modified Fri Apr 13 18:53:07 2001 CDT by Wizard (#2).";
.
#157:1
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
functions = buttons = body = {};
base_url = tostr("http://", $network.site, ":", $network.webport, "/Xpress_Program_Editor/");
title = $encore_web_utils:get_title(user, this, "Xpress Program Editor");
{help_button, help_function} = $encore_web_utils:insert_context_sensitive_help(user, $prog_help, "Xpress_Program_Editor");
openManual = $encore_web_utils:javascript_window_open(this, "openManual", tostr($xpress_client.external_baseurl, $xpress_client.texts_folder, this.programmers_manual), "600", "500", "yes");
functions = $list_utils:append(help_function, openManual);
functions = $encore_web_utils:insert_javascript(functions);
buttons = {@help_button, tostr("Object: <INPUT TYPE=text NAME=\"search_string\" VALUE=\"\" SIZE=20><INPUT TYPE=submit NAME=Submit VALUE=\"View\"><INPUT TYPE=\"button\" VALUE=\"My Objects\" onClick=\"parent.view1.location.href = '", base_url, "list_objects", "'\"><INPUT TYPE=\"button\" VALUE=\"Programmers Manual\" onClick=\"openManual()\">")};
buttons = $encore_web_utils:append_close_button(buttons);
body = $encore_web_utils:form($list_utils:append(title, buttons), "search", "/Xpress_Program_Editor/editor", "", "editor");
body = $list_utils:append(functions, body);
result = this:build(user, body, "Xpress Program Editor", "document.search.search_string.focus();");
return result;
"Last modified Fri Apr 13 18:53:07 2001 CDT by Wizard (#2).";
.
#157:2
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, form_data} = args;
body = vrbs = {};
alert = "";
if ({{}, {}} != form_data)
if (user.programmer == 0)
return "403_Forbidden";
endif
base_url = tostr("http://", $network.site, ":", $network.webport, "/");
object = toobj(form_data[2][1]);
if (valid(object))
"Valid object found";
title = $encore_web_utils:get_subtitle(user, this, tostr("Verbs on ", object.name));
"----------------------------------------------";
"Compile list of verbs on an object";
"----------------------------------------------";
if ($server_options.support_numeric_verbname_strings)
index = 0;
else
index = 1;
endif
verb_list = verbs(object);
for vrb_number in [1..length(verb_list)]
vrb_name = verb_list[vrb_number];
if (match(verb_info(object, vrb_number)[2], "r") || (verb_info(object, vrb_number)[1] == user))
vrbs = {@vrbs, tostr("<LI><A HREF=\"", base_url, "Xpress_Program_Editor/editor?", toint(object), ":", index, "\">", vrb_name, "</A><BR>")};
else
vrbs = {@vrbs, tostr("<LI>", vrb_name, " ** unreadable **")};
endif
index = index + 1;
endfor
if (!vrbs)
alert = "There are no verbs defined directly on this object. Please select a super class to see verbs inherited by this object.";
endif
body = $list_utils:append(title, vrbs);
else
alert = tostr("Sorry, no object named ", object_name, " was found. Unless you refer to an object directly by it's object number, you can only work with objects that are in your possession, or in the same room as you are.");
endif
endif
result = this:build(user, body, "Verb List", "", "editor");
return result;
"Last modified Fri Jul 20 09:04:58 2001 CDT by Wizard (#2).";
.
#157:3
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Create new verb via web";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
body = {};
alert = "";
verb_number = 0;
if ({{}, {}} != data)
object = toobj(data[2][1]);
if ((user.programmer && (user == object.owner)) || user.wizard)
try
new_verb = add_verb(object, {user, this.default_verb_permissions, this.default_new_verb_name}, this.default_verb_arguments);
verb_number = $code_utils:find_last_verb_named(object, this.default_new_verb_name);
alert = tostr("New verb created.");
except error (ANY)
alert = tostr(error[2]);
endtry
endif
endif
body = this:editor(user, {{}, {tostr(object, ":", this.default_new_verb_name)}}, alert);
return body;
"Last modified Fri Apr 13 18:54:52 2001 CDT by Wizard (#2).";
.
#157:4
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik and sindre.sorensen@rasmus.uib.no";
"===========================================================";
if (caller != this)
return E_PERM;
endif
{user, object, requested_verb} = args;
body = submit = {};
alert = continue_message = "";
if (slot = user in this.temporary_items[1])
"------------------------------------------------";
"User has unfinished business. Return verb in progress";
"------------------------------------------------";
continue_message = " (Last compile failed)";
{object, verb_number, code} = this.temporary_items[2][slot];
else
code = "";
try
"----------------------------------------------------------";
"Find correct verb number";
"----------------------------------------------------------";
if ($server_options.support_numeric_verbname_strings && (requested_verb == "0"))
"Added for compatibility with older, non-enCore based MOOs";
verb_number = tostr(0);
else
if (!(verb_number = $code_utils:toint(requested_verb)))
"If we are given a verb_name instead of a verb_number, we'll try to find it";
verb_number = $code_utils:find_last_verb_named(object, requested_verb);
endif
error = verb_info(object, verb_number);
endif
except error (ANY)
alert = "Sorry, the verb you requested either does not exist or is out of range";
endtry
endif
"----------------------------------------------------------";
"String conversion added for compatibility with older";
"non-enCore based MOOs. Jan, 07/20/01";
"----------------------------------------------------------";
if ($server_options.support_numeric_verbname_strings)
verb_number = tostr(verb_number);
endif
if ((user.programmer && match(verb_info(object, verb_number)[2], "r")) || (user == verb_info(object, verb_number)[1]))
"User is programmer and verb is readable, or user is owner of verb";
checkverbname = {"function checkVerbName() {", tostr("   if (document.editor.name.value == \"\" || document.editor.name.value == \"", this.default_new_verb_name, "\") {"), "   alert('You must give your verb a name before you can compile it !');", "   document.editor.name.focus();", "document.editor.name.select();", "   return false; ", "}", "return true;", "}"};
confirmDelete = {"function confirmDelete() {", tostr("   if (confirm('", this:delete_warning(object, requested_verb), " Are you sure you want to delete this verb?')) {"), tostr("      location.href = '/Xpress_Program_Editor/delete_verb?", toint(object), "&", verb_number, "';"), "      return true;", "   }", "   return false;", "}"};
functions = $list_utils:append(checkverbname, confirmDelete);
functions = $encore_web_utils:insert_javascript(functions);
arguments = verb_args(object, verb_number);
permissions = verb_info(object, verb_number);
if (!code)
code = verb_code(object, verb_number);
endif
"----------------------------------------------";
"Create editor web page based on verb values";
"----------------------------------------------";
OBJ = {tostr("<INPUT TYPE=hidden NAME=obj VALUE=\"", object, "\"><INPUT TYPE=hidden NAME=verb_number VALUE=\"", verb_number, "\"><INPUT TYPE=hidden NAME=long_old_verb_name VALUE=\"", permissions[$], "\">")};
name = {tostr("Name: <INPUT TYPE=text NAME=name VALUE=\"", permissions[$], "\" SIZE=20><P>")};
owner = {tostr("Owner: ", permissions[1].name, " (", permissions[1], ")<P>")};
title = $encore_web_utils:get_subtitle(user, this, tostr("Editing verb \"", permissions[$], "\" on ", object.name, "  ", continue_message));
r = match(verb_info(object, verb_number)[2], "r") ? "r" | "";
x = match(verb_info(object, verb_number)[2], "x") ? "x" | "";
d = match(verb_info(object, verb_number)[2], "d") ? "d" | "";
perm_list = {"Permissions:<BR>"};
if (r)
perm_list = {@perm_list, tostr("<INPUT TYPE=checkbox NAME=Read VALUE=\"", r, "\" CHECKED>Read<BR>")};
else
perm_list = {@perm_list, tostr("<INPUT TYPE=checkbox NAME=Read VALUE=\"", r, "\">Read<BR>")};
endif
if (x)
perm_list = {@perm_list, tostr("<INPUT TYPE=checkbox NAME=Execute VALUE=\"", x, "\" CHECKED>Execute<BR>")};
else
perm_list = {@perm_list, tostr("<INPUT TYPE=checkbox NAME=Execute VALUE=\"", x, "\">Execute<BR>")};
endif
if (d)
perm_list = {@perm_list, tostr("<INPUT TYPE=checkbox NAME=Debug VALUE=\"", d, "\" CHECKED>Debug<BR>")};
else
perm_list = {@perm_list, tostr("<INPUT TYPE=checkbox NAME=Debug VALUE=\"", d, "\">Debug<BR>")};
endif
direct_object = {"Arguments:<BR>"};
direct_object = {@direct_object, "<SELECT NAME=dobj>"};
for option in ({"this", "none", "any"})
if (option == arguments[1])
direct_object = {@direct_object, tostr("<OPTION VALUE=\"", option, "\" SELECTED>", option)};
else
direct_object = {@direct_object, tostr("<OPTION VALUE=\"", option, "\">", option)};
endif
endfor
direct_object = {@direct_object, "</SELECT>Direct Object<BR>"};
preps = $list_utils:append({"this", "none", "any"}, $code_utils:prepositions());
preposition = {"<SELECT NAME=preposition>"};
for option in (preps)
if (option == arguments[2])
preposition = {@preposition, tostr("<OPTION VALUE=\"", option, "\" SELECTED>", option)};
else
preposition = {@preposition, tostr("<OPTION VALUE=\"", option, "\">", option)};
endif
endfor
preposition = {@preposition, "</SELECT>Prep<BR>"};
indirect_object = {"<SELECT NAME=iobj>"};
for option in ({"this", "none", "any"})
if (option == arguments[3])
indirect_object = {@indirect_object, tostr("<OPTION VALUE=\"", option, "\" SELECTED>", option)};
else
indirect_object = {@indirect_object, tostr("<OPTION VALUE=\"", option, "\">", option)};
endif
endfor
indirect_object = {@indirect_object, "</SELECT>Indirect Object<BR>"};
arg_list = $list_utils:append(direct_object, preposition, indirect_object);
perm_list = $list_utils:append(name, perm_list);
arg_list = $list_utils:append(owner, arg_list);
verb_properties = $encore_web_utils:generate_table(user, {perm_list, arg_list}, this, "0");
verb_code = {"<P><TEXTAREA NAME=verb_code ROWS=17 COLS=72 WRAP=virtual>"};
indent_width = length(tostr(length(code)));
for line_number in [1..length(code)]
line = ($string_utils:right(tostr(line_number), indent_width) + ": ") + code[line_number];
"Added by Jan 05/16/99 to fix verb form layout";
line = $encore_web_utils:substitute_suspended(line, {{"<", "&lt;"}, {">", "&gt;"}});
verb_code = {@verb_code, line};
endfor
verb_code = {@verb_code, "</TEXTAREA>"};
if ((user == object.owner) || user.wizard)
submit = {"<P><INPUT TYPE=submit NAME=Submit VALUE=\"Compile Verb\">"};
submit = {@submit, "<INPUT TYPE=\"button\" VALUE=\"Delete This Verb\" onClick=\"return confirmDelete()\">"};
endif
editor = $list_utils:append(OBJ, verb_properties, verb_code, submit);
editor = $encore_web_utils:form(editor, "editor", "/Xpress_Program_Editor/compiler.html", "return checkVerbName()", "view2");
cancel_button = {"<INPUT TYPE=submit NAME=Cancel VALUE=\"Cancel Editing\">"};
cancel_user = {tostr("<INPUT TYPE=hidden NAME=user VALUE=\"", user, "\">")};
cancel_form = $encore_web_utils:form({@cancel_button, @cancel_user}, "cancel", "/Xpress_Program_Editor/cancel.html");
if (!continue_message)
cancel_form = {""};
endif
editor = $list_utils:append(title, editor, cancel_form);
body = $list_utils:append(functions, body, editor);
else
alert = "That verb is not readable by you.";
endif
return {body, alert};
"Last modified Fri Jul 20 09:04:18 2001 CDT by Wizard (#2).";
.
#157:5
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik and sindre.sorensen@rasmus.uib.no";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, form_data} = args;
function = sound = name_change = arg_result = perm_result = compile_result = {};
r = x = d = javascript = sound_file = "";
status = {"<B>Status:</B> Ready"};
title = $encore_web_utils:get_subtitle(user, this, "Verb Compiler");
if ({{}, {}} != form_data)
"User wants to compile a verb";
data = form_data;
object = toobj(data[2][1]);
verb_number = tostr(data[2][2]);
long_old_verb_name = data[2][3];
long_verb_name = data[2][4];
short_verb_name = strsub($string_utils:explode(long_verb_name)[1], "*", "");
"----------------------------------------------------------";
"String conversion added for compatibility with older";
"non-enCore based MOOs. Jan, 07/20/01";
"----------------------------------------------------------";
if ($server_options.support_numeric_verbname_strings == 0)
verb_number = toint(verb_number);
endif
if (long_verb_name != verb_info(object, verb_number)[$])
"User wants to change the verb name";
try
info = verb_info(object, verb_number);
try
result = set_verb_info(object, verb_number, listset(info, long_verb_name, 3));
name_change = {"Verb name changed.<BR>"};
except e (ANY)
endtry
"Ignore exceptions here";
except e (ANY)
endtry
endif
if ((user == object.owner) || user.wizard)
for n in [5..length(data[1]) - 1]
if (data[1][n] == "Read")
r = "r";
elseif (data[1][n] == "Execute")
x = "x";
elseif (data[1][n] == "Debug")
d = "d";
elseif (data[1][n] == "dobj")
dobj = data[2][n];
elseif (data[1][n] == "preposition")
"Modified the way prepositions are determined to fix problems with preps of more than one word. Jan Nov 22, 2000";
"preposition = $string_utils:explode(data[2][n])[1]";
preposition = data[2][n];
elseif (data[1][n] == "iobj")
iobj = data[2][n];
elseif (data[1][n] == "verb_code")
verb_code = data[2][n];
if (typeof(verb_code) == STR)
verb_code = {verb_code};
endif
endif
endfor
info = verb_info(object, short_verb_name);
"Set verb permissions";
perms = $string_utils:trim((r + x) + d);
if (info[2] != perms)
try
info[2] = perms = $perm_utils:apply(info[2], perms);
result = set_verb_info(object, verb_number, info);
perm_result = {tostr("Verb permissions set to ", perms, ".<BR>")};
except error (ANY)
perm_result = {error[2]};
endtry
endif
"Set verb arguments";
arguments = verb_args(object, strsub(short_verb_name, "*", ""));
if (arguments != {dobj, preposition, iobj})
try
result = set_verb_args(object, verb_number, {dobj, preposition, iobj});
arg_result = {"Verb arguments changed.<BR>"};
except error (ANY)
arg_result = {error[2]};
endtry
endif
"Compile code";
insert_help = 0;
try
for line_number in [1..length(verb_code)]
embedded_line_number = match(verb_code[line_number], "^[ 0-9]*:[ ]*");
if (embedded_line_number)
verb_code[line_number] = verb_code[line_number][embedded_line_number[2] + 1..$];
endif
endfor
if (result = set_verb_code(object, verb_number, verb_code))
for line in (result)
compile_result = {@compile_result, tostr(line, "<BR>")};
endfor
compile_result = {@compile_result, tostr(length(result), " error(s).<BR>")};
compile_result = {@compile_result, "Verb not programmed.<BR>"};
if (slot = user in this.temporary_items[1])
this.temporary_items[2][slot] = {object, verb_number, verb_code};
else
this.temporary_items[1] = {@this.temporary_items[1], user};
this.temporary_items[2] = {@this.temporary_items[2], {object, verb_number, verb_code}};
endif
sound_file = "compile_failed.wav";
insert_help = 1;
else
compile_result = {"0 errors.", "Verb programmed.<BR>"};
if (slot = user in this.temporary_items[1])
this.temporary_items[1] = listdelete(this.temporary_items[1], slot);
this.temporary_items[2] = listdelete(this.temporary_items[2], slot);
endif
sound_file = "compile_success.wav";
this:update_last_modified(user, object, short_verb_name);
endif
except error (ANY)
if (typeof(error[2]) == 4)
for line in (error[2])
compile_result = {@compile_result, line};
endfor
else
compile_result = {@compile_result, error[2]};
endif
compile_result = {@compile_result, "Verb not programmed."};
sound_file = "compile_failed.wav";
insert_help = 1;
endtry
if (insert_help)
{help_button, help_function} = $encore_web_utils:insert_context_sensitive_help(user, $prog_help, "Debugging");
function = $encore_web_utils:insert_javascript(help_function);
compile_result = $list_utils:append(function, help_button, compile_result);
endif
if (user.sound_volume)
sound = $encore_web_utils:make_sound(user, sound_file);
endif
else
javascript = "alert('Permission denied. You can only program verbs on objects that you own.')";
endif
status = $list_utils:append(name_change, arg_result, perm_result, compile_result);
endif
body = $list_utils:append(title, sound, status);
javascript = tostr(javascript, "parent.edit.location.href='edit.html?", toint(object), "&", verb_number, "'");
result = this:build(user, body, "Xpress Compiler", javascript);
return result;
"Last modified Fri Jul 20 09:04:38 2001 CDT by Wizard (#2).";
.
#157:6
"Copyright (C) 1999, sindre.sorensen@rasmus.uib.no";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, form_data} = args;
body = {};
javascript = "";
user = toobj(user);
javascript = "alert('Permission denied: Only the owner of the verb may edit it.')";
slot = user in this.temporary_items[1];
if (slot)
this.temporary_items[1] = listdelete(this.temporary_items[1], slot);
this.temporary_items[2] = listdelete(this.temporary_items[2], slot);
javascript = tostr("alert('Editing Canceled')");
endif
result = this:pre_assemble(user, body, "Cancel Editing", javascript, "");
return result;
"Last modified Thu Apr  8 12:10:43 1999 CDT by Wizard (#2).";
.
#157:7
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Display the Xpress Program Editor main screen";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data, ?alert = ""} = args;
error_msg = onload = "";
body = editor = {};
try
if (data != {{}, {}})
"---------------------------------------------------------";
"User has submitted a request. Find object and verb or";
"property if submitted";
"---------------------------------------------------------";
{object, vrb, property, alert2} = this:parse_args(user, tostr(data[2][1]));
if (valid(object))
"------------------------------------------------------";
"Valid object found.";
"------------------------------------------------------";
if (vrb != "")
"---------------------------------------------------";
"Verb specified. Diplay verb editor";
"---------------------------------------------------";
{editor, error_msg} = this:edit_verb(user, object, vrb);
elseif (property != "")
"---------------------------------------------------";
"Property specified. Display property editor";
"---------------------------------------------------";
{editor, error_msg} = this:edit_property(user, object, property);
else
"---------------------------------------------------";
"Object only specified. Display object editor";
"---------------------------------------------------";
{editor, error_msg, onload} = this:edit_object(user, object);
endif
if (error_msg)
alert2 = (alert2 + " ") + error_msg;
endif
body = $list_utils:append(body, editor);
endif
endif
except error (ANY)
alert = tostr("There is a problem with the Xpress Program Editor. Please try again later or notify a MOO administrator. Error: ", error[2]);
endtry
alert = alert + alert2;
alert = $encore_web_utils:make_alert(alert);
if (onload)
alert = alert + onload;
endif
result = this:build(user, body, "Xpress Program Editor", alert);
return result;
"Last modified Fri Apr 13 18:53:23 2001 CDT by Wizard (#2).";
.
#157:8
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Parse argument string and find object, and verb or property names if specified.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, arguments} = args;
object = vrb = property = alert = "";
"-----------------------------------------------------------";
"Check if user has specified a property";
"-----------------------------------------------------------";
if (index(arguments, ".") && (spec = $code_utils:parse_propref(arguments)))
if (valid(object = user:my_match_object($string_utils:trim(spec[1]))) || valid(object = user:my_match_object($string_utils:trim(tostr("#", spec[1])))))
if ($object_utils:has_property(object, spec[2]))
if (spec[2] in $code_utils.builtin_props)
alert = tostr("Sorry, ", spec[2], " is a built-in property and cannot be edited in Xpress. Please use the command line instead.");
else
property = spec[2];
endif
else
alert = "Property not found";
endif
endif
"-----------------------------------------------------------";
"Check if user has specified a verb";
"-----------------------------------------------------------";
elseif (spec = $code_utils:parse_verbref(arguments))
if (valid(object = user:my_match_object($string_utils:trim(spec[1]))) || valid(object = user:my_match_object($string_utils:trim(tostr("#", spec[1])))))
if (toint(spec[2]))
"We got a verb number";
vrb_info = verb_info(object, toint(spec[2]));
vrb = tostr(spec[2]);
"Following lines commented out for changes in version 3.0.1";
"We're now always referencing verbs by number instead of name";
"vrb = $string_utils:explode(vrb_info[$])[1]";
"vrb = $string_utils:substitute(vrb, {{\"*\", \"\"}})";
else
if (!(hv = $object_utils:has_verb(object, spec[2])))
alert = "That object does not define that verb.";
elseif (hv[1] != object)
alert = tostr("Object ", object, " does not define that verb, but its ancestor ", hv[1], " does.");
object = hv[1];
vrb = spec[2];
else
vrb = spec[2];
endif
endif
endif
else
"-----------------------------------------------------------";
"Check if user has specified a valid object";
"-----------------------------------------------------------";
if (valid(object = user:my_match_object($string_utils:trim(arguments))) || valid(object = user:my_match_object($string_utils:trim(tostr("#", arguments)))))
endif
endif
"-----------------------------------------------------------";
"Can't find object. Handle errors.";
"-----------------------------------------------------------";
if (object == $nothing)
alert = "You must give the name of some object.";
elseif (object == $failed_match)
alert = tostr("Object not found. You must either hold the object of be in the same location as the object");
elseif (object == $ambiguous_match)
alert = tostr("I do not know which ", arguments, " you mean.");
elseif (!valid(object))
alert = tostr(object, " does not exist.");
endif
return {object, vrb, property, alert};
"Last modified Fri Jul 20 09:03:56 2001 CDT by Wizard (#2).";
.
#157:9
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Lists data and flags on an object";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object} = args;
body = classes = location = size = name = owner = flags = functions = form = {};
alert = onload = r = f = "";
if ((object.r || (user == object.owner)) || user.wizard)
onload = "verbs_and_properties();";
base_url = tostr("http://", $network.site, ":", $network.webport, "/Xpress_Program_Editor/");
icon = {$encore_web_utils:get_icon(user, object)};
title = $encore_web_utils:get_subtitle(user, this, tostr("Viewing ", object.name, " (", object, ")"));
vrb_and_prop = {"function verbs_and_properties() {", tostr("  parent.view1.location.href = '", base_url, "view_verbs?", toint(object), "'"), tostr("  parent.view2.location.href = '", base_url, "view_properties?", toint(object), "'"), "}"};
functions = $encore_web_utils:insert_javascript(vrb_and_prop);
"----------------------------------------------";
"Compile list of super classes";
"----------------------------------------------";
classes = {@classes, "Name: "};
classes = {@classes, "<SELECT onchange=this.form.submit() NAME=classes>"};
super = $list_utils:reverse($object_utils:ancestors(object));
for class in (super)
if (length(class.name) > 30)
class_name = class.name[1..29] + "..";
else
class_name = class.name;
endif
classes = {@classes, tostr("<OPTION VALUE=", toint(class), ">", class_name)};
endfor
if (length(object.name) > 30)
object_name = object.name[1..29] + "..";
else
object_name = object.name;
endif
classes = {@classes, tostr("<OPTION VALUE=", toint(object), " SELECTED> ", object_name)};
classes = {@classes, "</SELECT>"};
top_menu = $encore_web_utils:form($list_utils:append(classes), "Object Menu", "/Xpress_Program_Editor/Editor");
owner = {tostr("Owner: ", object.owner.name, " (", object.owner, ")<P>")};
if (object.location == $nowhere)
location = {"Location: Limbo<P>"};
else
location = {tostr("Location: ", object.location.name, " (", object.location, ")<P>")};
endif
if ($quota_utils.byte_based && $object_utils:has_property(object, "object_size"))
$quota_utils:object_size(object);
obj_size = tostr($string_utils:group_number(object.object_size[1]), " bytes at ", user:ctime(object.object_size[2]));
else
obj_size = "Unknown";
endif
size = {tostr("Object size: ", obj_size, "<P>")};
"----------------------------------------------";
"Add editing options if user is owner or wiz";
"----------------------------------------------";
r = object.r ? "r" | "";
f = object.f ? "f" | "";
if ((user == object.owner) || user.wizard)
form = {@form, tostr("<INPUT TYPE=\"hidden\" NAME=\"object\" VALUE=\"", toint(object), "\">")};
"-------------------------------------------------";
"Object flags";
"-------------------------------------------------";
flags = {@flags, "Flags: "};
if (r)
flags = {@flags, tostr("<INPUT TYPE=\"checkbox\" NAME=\"Read\" VALUE=\"", r, "\" CHECKED>Read ")};
else
flags = {@flags, tostr("<INPUT TYPE=\"checkbox\" NAME=\"Read\" VALUE=\"", r, "\">Read ")};
endif
if (f)
flags = {@flags, tostr("<INPUT TYPE=\"checkbox\" NAME=\"Fertile\" VALUE=\"", f, "\" CHECKED>Fertile<P>")};
else
flags = {@flags, tostr("<INPUT TYPE=\"checkbox\" NAME=\"Fertile\" VALUE=\"", f, "\">Fertile<P>")};
endif
form = $list_utils:append(form, flags);
else
"----------------------------------------------";
"Add non-editable object information";
"----------------------------------------------";
form = {@form, tostr("Flags: ", $string_utils:trim(r + f))};
endif
"----------------------------------------------";
"Add save and create buttons if user is wiz or owner";
"----------------------------------------------";
if ((user == object.owner) || user.wizard)
form = {@form, "<INPUT TYPE=submit NAME=\"Save\" VALUE=\"Save Object\">"};
form = {@form, tostr("<INPUT TYPE=\"button\" VALUE=\"New Verb\" onClick=\"location.href = '/Xpress_Program_Editor/create_new_verb?", toint(object), "'\"><INPUT TYPE=\"button\" VALUE=\"New Property\" onClick=\"location.href = '/Xpress_Program_Editor/create_new_property?", toint(object), "'\">")};
form = $encore_web_utils:form(form, "editor", "/Xpress_Program_Editor/set_object", "return check()");
endif
body = $list_utils:append(functions, title, icon, top_menu, owner, location, size, form);
else
alert = "That object is not readable by you. ";
endif
return {body, alert, onload};
"Last modified Fri Apr 13 18:53:40 2001 CDT by Wizard (#2).";
.
#157:10
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Set object flags via Xpress";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
body = {};
alert = r = f = "";
if ({{}, {}} != data)
"-------------------------------------------";
"Decode data and assign values";
"-------------------------------------------";
object = toobj(data[2][1]);
if ((user == object.owner) || user.wizard)
for n in [2..length(data[1]) - 1]
if (data[1][n] == "Read")
r = "r";
elseif (data[1][n] == "Fertile")
f = "f";
endif
endfor
try
object.r = r;
object.f = f;
alert = tostr("Object flags set to ", r + f);
except error (ANY)
alert = tostr("Could not set object flags. Reason: ", error[2], ".");
endtry
else
alert = "Permission denied";
endif
endif
body = this:editor(user, {{}, {object}}, alert);
return body;
"Last modified Fri Apr 13 18:53:40 2001 CDT by Wizard (#2).";
.
#157:11
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Lists a user's objects";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
body = objects = {};
base_url = tostr("http://", $network.site, ":", $network.webport, "/Xpress_Program_Editor/Editor");
title = $encore_web_utils:get_subtitle(user, this, "My Objects");
for object in (user.owned_objects)
"Compile list of objects the user owns";
if ($object_utils:isa(object, $encore_web_class))
if (object.icon != "")
objects = {@objects, tostr("<A HREF=\"", base_url, "?", toint(object), "\">", $encore_web_utils:get_icon(user, object), object.name, " (", object, ")</A><BR>")};
else
objects = {@objects, tostr("<LI><A HREF=\"", base_url, "?", toint(object), "\">", object.name, "</A><BR>")};
endif
endif
endfor
body = $list_utils:append(title, objects);
result = this:build(user, body, "Objects", "", "editor");
return result;
"Last modified Fri Apr 13 18:54:01 2001 CDT by Wizard (#2).";
.
#157:12
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Lists web editable properties on an object";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
body = properties = {};
alert = "";
if ({{}, {}} != data)
"User has asked for an object to edit";
object = toobj(data[2][1]);
if (valid(object))
"----------------------------------------------------";
"Compile a list properties on the requested object";
"----------------------------------------------------";
title = $encore_web_utils:get_subtitle(user, this, tostr("Properties on ", object.name));
base_url = tostr("http://", $network.site, ":", $network.webport, "/Xpress_Program_Editor/editor");
for property in ($object_utils:all_properties_suspended(object))
prop_perms = property_info(object, property);
if ((match(prop_perms[2], "r") || (user == prop_perms[1])) || user.wizard)
"Show only readable properties";
properties = {@properties, tostr("<LI><A HREF=\"", base_url, "?", toint(object), ".", property, "\">", property, "</A><BR>")};
else
properties = {@properties, tostr("<LI>", property, " ** unreadable **")};
endif
endfor
body = $list_utils:append(title, properties);
else
alert = "Could not find an object by that name. You must either hold an object, or be in the same room as the object in order to refer to it by name.";
endif
endif
result = this:build(user, body, "Properties", $encore_web_utils:make_alert(alert), "editor");
return result;
"Last modified Fri Apr 13 18:54:01 2001 CDT by Wizard (#2).";
.
#157:13
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Checks of a verb or property is defined on a core object or if the object has sub classes prints delete warning to user.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
object = args[1];
alert = "";
"-------------------------------------------------";
"Check if object is system object";
"-------------------------------------------------";
if (object == #0)
alert = "WARNING: You are working with the System Object. Unless you know exactly what you are doing, you are STRONGLY ADVICED NOT TO PROCEED, as this action may create severe problems with the MOO!";
"-------------------------------------------------";
"Check if object is a super class";
"-------------------------------------------------";
elseif ($object_utils:descendants_suspended(object))
alert = "WARNING: You are working with an object that has sub classes. If you proceed these sub classes will be affected!";
else
"-------------------------------------------------";
"Check if object is defined on core";
"-------------------------------------------------";
coreprops = properties(#0);
for prop in (coreprops)
if (#0.(prop) == object)
alert = "WARNING: You are working with a core object. Unless you know exactly what you are doing, you are STRONGLY ADVICED NOT TO PROCEED, as this action may create severe problems with the MOO!";
endif
endfor
endif
return alert;
"Last modified Fri Apr 13 18:54:18 2001 CDT by Wizard (#2).";
.
#157:14
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress property editor screen";
"===========================================================";
if (caller != this)
return E_PERM;
endif
{user, object, property} = args;
body = form = type = flags = name = owner = {};
alert = datatype = coreCheck = "";
prop_info = property_info(object, property);
if (((user.programmer && match(prop_info[2], "r")) || (user == prop_info[1])) || user.wizard)
"-------------------------------------------------";
"Generate edit form for the requested property";
"-------------------------------------------------";
confirmDelete = {"function confirmDelete() {", tostr("   if (confirm('", this:delete_warning(object, property), " Are you sure you want to delete this property?')) {"), tostr("      location.href = '/Xpress_Program_Editor/delete_property?", toint(object), "&", property, "';"), "      return true;", "   }", "   return false;", "}"};
checkPropName = {"function checkPropertyName() {", tostr("   if (document.editor.name.value == \"\" || document.editor.name.value == \"", this.default_new_property_name, "\") {"), "   alert('You must give your property a name before you can save it');", "   document.editor.name.focus();", "document.editor.name.select();", "   return false; ", "}", "return true;", "}"};
functions = $list_utils:append(confirmDelete, checkPropName);
confirmDelete = $encore_web_utils:insert_javascript(functions);
base_url = tostr("http://", $network.site, ":", $network.webport, "/Xpress_Program_Editor/set_property");
title = $encore_web_utils:get_subtitle(user, this, tostr("Editing property \"", property, "\" on ", object.name));
form = {@form, tostr("<INPUT TYPE=\"hidden\" NAME=\"object\" VALUE=\"", toint(object), "\">")};
form = {@form, tostr("<INPUT TYPE=\"hidden\" NAME=\"oldName\" VALUE=\"", property, "\">")};
owner = {tostr("Owner: ", prop_info[1].name, " (", prop_info[1], ")<P>")};
data = object.(property);
if (typeof(data) == OBJ)
datatype = "Object";
elseif (typeof(data) == INT)
datatype = "Integer";
elseif (typeof(data) == STR)
datatype = "String";
elseif (typeof(data) == LIST)
datatype = "List";
elseif (typeof(data) == FLOAT)
datatype = "Floating-point number";
elseif (typeof(data) == ERR)
datatype = "Error code";
else
datatype = "Unknown";
endif
type = {tostr("Data Type: ", datatype, "<P>")};
size = {tostr("Size: ", $quota_utils:value_bytes(object.(property)), " bytes.<P>")};
if (($object_utils:defines_property(object, property) && (user == prop_info[1])) || user.wizard)
"-------------------------------------------------";
"Property is defined on the object, and user has permission to edit. add editing options";
"-------------------------------------------------";
name = {tostr("Name: <INPUT TYPE=text NAME=name VALUE=\"", property, "\" SIZE=20><P>")};
"-------------------------------------------------";
"Edit property permissions";
"-------------------------------------------------";
r = match(prop_info[2], "r") ? "r" | "";
c = match(prop_info[2], "c") ? "c" | "";
flags = {@flags, "Permissions: "};
if (r)
flags = {@flags, tostr("<INPUT TYPE=\"checkbox\" NAME=\"Read\" VALUE=\"", r, "\" CHECKED>Read ")};
else
flags = {@flags, tostr("<INPUT TYPE=\"checkbox\" NAME=\"Read\" VALUE=\"", r, "\">Read ")};
endif
if (c)
flags = {@flags, tostr("<INPUT TYPE=\"checkbox\" NAME=\"Change\" VALUE=\"", c, "\" CHECKED>Change")};
else
flags = {@flags, tostr("<INPUT TYPE=\"checkbox\" NAME=\"Change\" VALUE=\"", c, "\">Change")};
endif
else
"-------------------------------------------------";
"Object does not define property. Add non-editable property information";
"-------------------------------------------------";
name = {tostr("Inherited property: ", property, "<P>")};
flags = {@flags, tostr("Permissions: ", prop_info[2], "<P>")};
endif
form = $list_utils:append(form, name, owner, type, size, flags);
form = {@form, tostr("<P><TEXTAREA NAME=\"value\" ROWS=10 COLS=70 WRAP=virtual>")};
try
"-------------------------------------------------";
"Format property value. Lists of lists must be treated as special cases";
"-------------------------------------------------";
if (typeof(data) == LIST)
if ((data != {}) && (typeof(data[$]) == LIST))
form = {@form, tostr($string_utils:print(data), "</TEXTAREA><P>")};
else
if (data != {})
for line in [1..length(data)]
if (line == length(data))
form = {@form, tostr(data[line], "</TEXTAREA><P>")};
else
form = {@form, tostr(data[line])};
endif
endfor
else
form = {@form, "</TEXTAREA><P>"};
endif
endif
else
form = {@form, tostr(data, "</TEXTAREA><P>")};
endif
except error (ANY)
"-------------------------------------------------";
"Default to formatted string if errors occur";
"-------------------------------------------------";
form = {@form, tostr($string_utils:print(data), "</TEXTAREA><P>")};
endtry
if ((user == prop_info[1]) || user.wizard)
form = {@form, "<INPUT TYPE=submit NAME=\"Save\" VALUE=\"Save Property\">"};
if ($object_utils:defines_property(object, property))
form = {@form, "<INPUT TYPE=\"button\" VALUE=\"Delete This Property\" onClick=\"return confirmDelete()\">"};
endif
endif
form = $encore_web_utils:form(form, "editor", "/Xpress_Program_Editor/set_property", "return checkPropertyName()");
onload = tostr("document.edit_property.", property, ".focus()");
body = $list_utils:append(confirmDelete, title, form);
else
alert = "That property is not readable by you.";
endif
return {body, alert};
"Last modified Fri Apr 13 18:54:19 2001 CDT by Wizard (#2).";
.
#157:15
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Set property values via Xpress";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
body = {};
alert = property = r = c = value = "";
if ({{}, {}} != data)
"-------------------------------------------";
"Decode data and assign values";
"-------------------------------------------";
object = toobj(data[2][1]);
if (length(data[2]) > 4)
oldName = tostr(data[2][2]);
name = tostr(data[2][3]);
for n in [4..length(data[1]) - 1]
if (data[1][n] == "Read")
r = "r";
elseif (data[1][n] == "Change")
c = "c";
elseif (data[1][n] == "Value")
value = data[2][n];
endif
endfor
"--------------------------------";
"Set property permissions";
"--------------------------------";
perms = $string_utils:trim(r + c);
info = property_info(object, oldName);
oldperms = info[2];
if ((oldperms != perms) || (name != oldName))
try
info[2] = perms = $perm_utils:apply(info[2], perms);
if (name != oldName)
set_property_info(object, oldName, {@info, name});
alert = tostr("Property name changed. Permissions set to: ", perms, ".");
else
set_property_info(object, oldName, info);
alert = tostr("Property permissions set to: ", perms, ".");
endif
except error (ANY)
if (name != oldName)
alert = tostr("Could not rename property. Reason: ", error[2], ". The property name may already exist on the object or one of its sub classes, or, name change is not permitted.");
else
alert = tostr("Could not change property permissions. Reason: ", error[2], ".");
endif
name = oldName;
endtry
endif
else
name = tostr(data[2][2]);
value = data[2][3];
endif
"-------------------------------------------";
"Save property value";
"-------------------------------------------";
alert = alert + this:save_property_value(user, object, name, value);
property = "." + name;
endif
body = this:editor(user, {{}, {tostr(object, property)}}, alert);
return body;
"Last modified Fri Apr 13 18:54:36 2001 CDT by Wizard (#2).";
.
#157:16
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Save property values via Xpress";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object, property, value} = args;
alert = "";
value = this:coerce(value);
if (value != object.(property))
try
object.(property) = value;
alert = tostr(" Property ", property, " saved.");
except error (ANY)
alert = " Could not save property: " + tostr(error[2]);
endtry
endif
return alert;
"Last modified Fri Apr 13 18:54:36 2001 CDT by Wizard (#2).";
.
#157:17
"===========================================================";
"Copyright Amy Bruckman";
"Verb from $macmoose_utils. Moved here to avoid dependencies";
"===========================================================";
"Syntax: coerce(argument)";
"Last modified by jaime (#2) Mon Mar 11 14:35:53 1996 EST.";
set_task_perms(caller_perms());
if (typeof(args[1]) != STR)
return args[1];
"This function is designed to convert strings to other types.";
"If the input is not a string, return it as is.";
endif
"Force the argument to be a type other than string, if possible.";
string = args[1];
if (!length(string))
return string;
endif
first = string[1];
if ((first == "#") && ((result = $code_utils:toobj(string)) != E_TYPE))
return result;
elseif ((0 && (first == "{")) && (result = this:coerce_list_string(string)))
return result;
endif
result = $code_utils:eval_d(tostr("return ", args[1], ";"));
"It would be nice if this called eval_env; right now it doesn't";
if (((args[1] == "") || (result[1] == 0)) || ((typeof(result[2]) == ERR) && (!(args[1] in this.error_codes))))
return args[1];
"Can't coerce it into anything; return the original";
else
return result[2];
endif
"Amy 12/93";
"Amy  7/95: Fixed bug in how lists are handled";
"Last modified Fri Apr 13 18:54:36 2001 CDT by Wizard (#2).";
.
#157:18
"===========================================================";
"Copyright Amy Bruckman";
"Verb from $macmoose_utils. Moved here to avoid dependencies";
"===========================================================";
"Copied from Parse Utilities (#89):coerce_list by Hacker ($hacker) Mon Mar 20 18:43:20 1995 EST";
"Syntax: coerce_list(string <text, list as string>)";
"Convert list as a string to list of possibly other data types";
"Recursively goes through sub-lists.";
"Last modified by jaime (#2) Mon Mar 11 14:35:53 1996 EST.";
text = args[1];
"Chop off opening {";
open = match(text, "^ *{");
text = (open && (length(text) > open[2])) ? text[open[2] + 1..length(text)] | "";
"Chop off closing }";
end = rmatch(text, "} *$");
text = (end && (end[1] > 1)) ? text[1..end[1] - 1] | "";
text = this:tokenize(text);
result = {};
for item_number in [1..length(text)]
$command_utils:suspend_if_needed(0);
if (!(text[item_number] == ","))
if (match(text[item_number], "^{.*}$"))
result = {@result, this:coerce_list(text[item_number])};
else
result = {@result, this:coerce(text[item_number])};
endif
endif
endfor
return result;
"Amy 10/94";
"Amy  5/95: Made remove commas in middle of seq; they were getting added as elements to the list";
"Last modified Fri Apr 13 18:54:52 2001 CDT by Wizard (#2).";
.
#157:19
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Delete a property via Xpress";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
body = {};
alert = "";
object = toobj(data[2][1]);
property = tostr(data[2][2]);
if ((user == object.owner) || user.wizard)
try
result = delete_property(object, property);
alert = "Property deleted.";
except (E_PROPNF)
alert = "That object does not define that property.";
except res (ANY)
alert = tostr(res[2]);
endtry
else
alert = "Permission Denied.";
endif
body = this:editor(user, {{}, {tostr(object)}}, alert);
return body;
"Last modified Fri Apr 13 18:54:52 2001 CDT by Wizard (#2).";
.
#157:20
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Create a property via Xpress";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
body = {};
alert = property = "";
if ({{}, {}} != data)
object = toobj(data[2][1]);
if ((user.programmer && (user == object.owner)) || user.wizard)
if (!$quota_utils:property_addition_permitted(user))
alert = "Property addition not permitted because quota exceeded.";
else
try
name = this.default_new_property_name;
value = this.default_property_value;
perms = this.default_property_permissions;
add_property(object, name, value, {user, perms});
alert = tostr("New property added with value ", toliteral(object.(name)), ".");
property = "." + name;
except (E_INVARG)
if ($object_utils:has_property(object, name))
alert = tostr("Property ", object, ".", name, " already exists.");
property = "." + name;
else
alert = "Property is already defined on one or more descendents.";
endif
except error (ANY)
alert = tostr(error[2]);
endtry
endif
else
alert = "Permission denied.";
endif
endif
body = this:editor(user, {{}, {tostr(object, property)}}, alert);
return body;
"Last modified Fri Apr 13 18:54:52 2001 CDT by Wizard (#2).";
.
#157:21
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Delete a verb using the Xpress Program Editor";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
body = {};
alert = "";
object = toobj(data[2][1]);
verb_number = data[2][2];
"----------------------------------------------------------";
"String conversion added for compatibility with older";
"non-enCore based MOOs. Jan, 07/20/01";
"----------------------------------------------------------";
if (!$server_options.support_numeric_verbname_strings)
verb_number = toint(verb_number);
endif
if ((user == object.owner) || user.wizard)
try
delete_verb(object, verb_number);
alert = "Verb deleted.";
except error (ANY)
alert = tostr(error[2]);
endtry
else
alert = "Permission Denied.";
endif
body = this:editor(user, {{}, {tostr(object)}}, alert);
return body;
"Last modified Fri Jul 20 09:04:58 2001 CDT by Wizard (#2).";
.
#157:22
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Time stamp code changes. Based on $code_utils:update_last_modified";
"===========================================================";
if (caller != this)
return E_PERM;
endif
{user, object, verbname} = args;
oldcode = code = verb_code(object, verbname);
while (((code && (last = code[length(code)])) && (length(last) >= 15)) && (last[1..15] == "\"Last modified "))
code = code[1..length(code) - 1];
endwhile
code = {@code, tostr("\"Last modified ", ctime(), " by ", user.name, " (", user, ").\";")};
set_verb_code(object, verbname, code);
return oldcode == verb_code(object, verbname);
"Last modified Fri Apr 13 18:55:49 2001 CDT by Wizard (#2).";
.
#158:0
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Generates the main MOO administration module screen";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
user = args[1];
if (!user.wizard)
return "403_Forbidden";
endif
base_url = tostr("http://", $network.site, ":", $network.webport, "/administration_module/");
menu_frame = tostr(base_url, "menu.html");
work_frame = tostr(base_url, "blank.html");
html = {"<FRAMESET COLS=\"50%\" FRAMEBORDER=\"1\">"};
html = {@html, tostr("<NOFRAMES><BODY>")};
html = {@html, "<H3>Sorry you need a browser that supports frames to use this system</H3>"};
html = {@html, "</BODY></NOFRAMES>"};
html = {@html, tostr("   <FRAMESET ROWS=\"95,82%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("  <FRAME SRC=\"", menu_frame, "\" NAME=\"menu\" NORESIZE SCROLLING=no MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("       <FRAME SRC=\"", work_frame, "\" NAME=\"work\" MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, "   </FRAMESET>"};
html = {@html, "</FRAMESET>"};
result = this:build(user, html, "Xpress MOO Administration", "", "", "", "", this.doctype_frameset);
return result;
"Last modified Fri Apr 13 19:06:51 2001 CDT by Wizard (#2).";
.
#158:1
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
user = args[1];
if (!user.wizard)
return "403_Forbidden";
endif
base_url = tostr("http://", $network.site, ":", $network.webport, "/administration_module/");
"Assemble menu line";
{help_button, help_function} = $encore_web_utils:insert_context_sensitive_help(user, $wiz_help, "enCore_MOO_administration");
accounts_button = {tostr("<INPUT TYPE=\"button\" VALUE=\"Accounts\" onClick=\"parent.work.location.href = '", base_url, "view_html", "'\">")};
xpress_button = {tostr("<INPUT TYPE=\"button\" VALUE=\"Bookmarks\" onClick=\"parent.work.location.href = '", base_url, "edit_xpress_bookmarks_html", "'\">")};
generics_button = {tostr("<INPUT TYPE=\"button\" VALUE=\"Generics\" onClick=\"parent.work.location.href = '", base_url, "edit_generics_html", "'\">")};
motd_button = {tostr("<INPUT TYPE=\"button\" VALUE=\"MOTD\" onClick=\"parent.work.location.href = '", base_url, "edit_motd_html", "'\">")};
core_pref_button = {tostr("<INPUT TYPE=\"button\" VALUE=\"enCore Settings\" onClick=\"parent.work.location.href = '", base_url, "core_settings", "'\">")};
functions = $encore_web_utils:insert_javascript(help_function);
title = $encore_web_utils:get_title(user, this, "Xpress MOO Administration");
menu = $list_utils:append(title, help_button, accounts_button, xpress_button, generics_button, motd_button, core_pref_button);
menu = $encore_web_utils:append_close_button(menu);
menu = {"<FORM>", @menu, "</FORM>"};
body = $list_utils:append(functions, menu);
result = this:build(user, body, "MOO Administration Menu");
return result;
"Last modified Fri Apr 13 18:59:03 2001 CDT by Wizard (#2).";
.
#158:2
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
if (!user.wizard)
return "403_Forbidden";
endif
action = data[2];
if (action == {"update"})
OBJ = data[1];
player_class = parent(OBJ);
plyr_name = OBJ.name;
email = OBJ.email_address;
real_name = OBJ.real_name;
msg = "Change data for this account by editing the fields below.";
elseif (action == {"batch"})
if (data[1] == {""})
player_class = $player_class;
msg = "Select a character class and enter data for the new accounts below. Use one line for each new account. Separate name, email address and real name with open spaces. Example: <I>Cindy cindy@someplace.com Cynthia Haynes</I>";
account_data = {};
else
player_class = toobj(data[1][1]);
msg = ("The following name(s) is/are already in use: <B>" + data[1][2]) + "</B><BR>Please choose new name(s) and try again.";
account_data = data[1][3];
endif
elseif (action == {"report"})
player_class = $player_class;
msg = "Account Creation Report:";
account_data = data[1][3];
else
msg = "Select a character class and enter name, email address and real name for the new account to be created below. If you are creating a new guest account, you only need to fill in the name field.";
player_class = $player_class;
plyr_name = email = real_name = "";
endif
body = search = part1 = part2 = {};
confirmDelete = {"function confirmAction() {", "   if (confirm('Are you sure you want to do this?')) {", "      return true;", "   }", "   return false;", "}"};
base_url = tostr("http://", $network.site, ":", $network.webport, "/administration_module/");
title = $encore_web_utils:get_subtitle(user, this, "Character Account Administration");
{help_button, help_function} = $encore_web_utils:insert_context_sensitive_help(user, $wiz_help, "enCore_Account_Creation");
functions = $encore_web_utils:insert_javascript($list_utils:append(help_function, confirmDelete));
menu_buttons = {tostr("<INPUT TYPE=\"button\" VALUE=\"Single Account\" onClick=\"parent.work.location.href = '", base_url, "view_html", "'\"><INPUT TYPE=\"button\" VALUE=\"Multiple Accounts\" onClick=\"parent.work.location.href = '", base_url, "view_html?batch", "'\"> Update Account: <INPUT TYPE=text NAME=\"name\" VALUE=\"\" SIZE=15><INPUT TYPE=submit NAME=Submit VALUE=\"Find\">")};
menu = $list_utils:append(help_button, menu_buttons);
menu = $encore_web_utils:form(menu, "search", "/administration_module/find_player");
"=================================";
"== Set up player class section ==";
"=================================";
part1 = {tostr("<INPUT TYPE=hidden NAME=name VALUE=\"", OBJ, "\">")};
player_classes = {$guest, $frand_class, $builder, $prog, $wiz};
part1 = {@part1, "Character Class: <P>"};
for class in (player_classes)
class_name = $string_utils:trim($string_utils:substitute(class.name, {{"Generic", ""}}));
class_name = $string_utils:capitalize(class_name);
if (class == player_class)
part1 = {@part1, tostr("<INPUT TYPE=radio NAME=class VALUE=\"", toint(class), "\" CHECKED>", class_name, "<P>")};
else
part1 = {@part1, tostr("<INPUT TYPE=radio NAME=class VALUE=\"", toint(class), "\">", class_name, "<P>")};
endif
endfor
if ((action == {"batch"}) || (action == {"report"}))
"==============================================";
"== Form for creating a batch of characters  ==";
"==============================================";
part2 = {@part2, tostr(msg, "<P><TEXTAREA NAME=account_data ROWS=10 COLS=70 WRAP=virtual>")};
for n in (account_data)
part2 = {@part2, n};
endfor
part2 = {@part2, "</TEXTAREA><P>"};
if (action == {"report"})
if ($network.active)
part2 = {@part2, "<P><INPUT TYPE=submit NAME=Submit VALUE=\"Email Me Report\">"};
else
part2 = {@part2, "<P><INPUT TYPE=submit NAME=Submit VALUE=\"MOOmail Me Report\">"};
endif
vrb = "mail_report";
else
part2 = {@part2, "<P><INPUT TYPE=submit NAME=Submit VALUE=\"Create Multiple Accounts\">"};
vrb = "batch_make_player";
endif
else
"============================================================";
"== Form for making one character, or updating a character ==";
"============================================================";
part2 = {@part2, tostr(msg, "<P><INPUT TYPE=text NAME=\"name\" VALUE=\"", plyr_name, "\" SIZE=30>Name<P><INPUT TYPE=text NAME=\"email_address\" VALUE=\"", email, "\" SIZE=30>Email Address<P><INPUT TYPE=text NAME=\"real_name\" VALUE=\"", real_name, "\" SIZE=30>Real Name<P>")};
if (action == {"update"})
part2 = {@part2, "<INPUT TYPE=submit NAME=Submit VALUE=\"Update Account \"><INPUT TYPE=submit NAME=Submit VALUE=\"Delete Account\">"};
vrb = "update";
else
part2 = {@part2, "<INPUT TYPE=submit NAME=Submit VALUE=\"Create New Account\">"};
vrb = "create";
endif
endif
form = $encore_web_utils:generate_table(user, {part1, part2}, this);
form = $encore_web_utils:form(form, "character", tostr("/administration_module/", vrb), "return confirmAction()");
body = $list_utils:append(functions, title, menu, form);
return body;
"Last modified Fri Apr 13 18:59:03 2001 CDT by Wizard (#2).";
.
#158:3
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Find a character and return character data";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
alert = "";
if (!user.wizard)
return "403_Forbidden";
endif
body = {};
alert = result = "";
if (data != {{}, {}})
info = data[2];
person = toobj(info[1]);
class = toobj(info[2]);
name = info[3];
email = info[4];
real_name = info[5];
action = info[$];
if (action == "Delete Account")
result = this:do_purge(user, person);
else
if (action == "Create New Account")
"Create a new character";
if (class == $guest)
result = this:make_guest(user, class, name);
else
result = this:make_player(user, class, name, email, real_name);
endif
else
result = this:update_player(user, person, class, name, email, real_name);
endif
endif
if (result)
alert = $string_utils:substitute(result, {{"'", ""}, {"\"", ""}});
else
alert = "No changes made";
endif
endif
body = this:view_html(user, {{}, {}});
result = this:build(user, body, "Character Administration", $encore_web_utils:make_alert(alert));
return result;
"Last modified Fri Apr 13 18:59:21 2001 CDT by Wizard (#2).";
.
#158:4
"Copyright (C) 1999, Jan Rune Holmevik";
"Toads and recycles a player and their objects in the purge queue.";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, form_data} = args;
object = form_data;
if (!user.wizard)
return "403_Forbidden";
endif
msg = "error";
if (is_player(object))
if (((email = object.email_address) && (current = $registration_db:find_exact(email))) && (ind = $list_utils:iassoc(object, current)))
current[ind] = {@current[ind], "User account deleted"};
$registration_db:insert(email, current);
endif
$wiz_utils:unset_player(object);
total = bytes = 0;
for x in ({@setremove(object.owned_objects, object), object})
if (x.owner == object)
$command_utils:suspend_if_needed(0);
if ($object_utils:has_property(x, "object_size"))
bytes = bytes + x.object_size[1];
endif
$recycler:_recycle(x);
total = total + 1;
endif
endfor
msg = tostr("Character deleted.  ", total, " objects recycled for a total of ", $string_utils:group_number(bytes), " bytes.");
else
msg = "That object is not a valid player";
endif
return msg;
"Last modified Fri Apr 23 15:42:42 1999 CDT by Wizard (#2).";
.
#158:5
"Copyright (C) 1999, Jan Rune Holmevik";
"Create a $guest character";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, class, name} = args;
if (!user.wizard)
return "403_Forbidden";
endif
body = {};
msg = "";
if ((class == $guest) && (name != ""))
i = length(children(class));
while (!$player_db:available(guestnum = tostr("Guest", i = i + 1)))
endwhile
guestname = name + "_Guest";
guests = children($guest);
if (length(guests) == 0)
guestaliases = {guestname, adj = name, "Guest"};
else
guestaliases = {guestname, adj = name, guestnum};
endif
if ($player_db.frozen)
msg = "Sorry, the player db is frozen, so no players can be made right now.  Please try again in a few minutes.";
elseif (!$player_db:available(guestname))
msg = tostr("\"", guestname, "\" is not an available name.");
elseif (!$player_db:available(adj))
msg = tostr("\"", adj, "\" is not an available name.");
else
new = $quota_utils:bi_create(class, $hacker);
new:set_name(guestname);
new:set_aliases(guestaliases);
new.default_name = guestname;
new.default_aliases = guestaliases;
if (!(e = $wiz_utils:set_player(new, 1)))
msg = tostr("Unable to make ", new.name, " (", new, ") a player.", e);
else
msg = tostr("Guest: ", new.name, " (", new, ") made.");
new.default_description = {"By definition, guests appear nondescript."};
new.description = new.default_description;
new.last_connect_time = $maxint;
new.last_disconnect_time = time();
new.password = 0;
new.size_quota = new.size_quota;
new:set_gender(new.default_gender);
move(new, $player_start);
endif
endif
endif
return msg;
"Last modified Thu Apr  8 12:11:48 1999 CDT by Wizard (#2).";
.
#158:6
"Copyright (C) 1999, Jan Rune Holmevik";
"Create a character";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, class, name, email, ?real_name = ""} = args;
if (!user.wizard)
return "403_Forbidden";
endif
body = {};
msg = "";
if ((name != "") && (email != ""))
if ($player_db:available(name))
new = create(class, $nothing);
new.name = name;
new.real_name = real_name;
new.aliases = {name};
new.password = crypt(password = $wiz_utils:random_password(5));
new.last_password_time = time();
new.last_connect_time = $maxint;
new.last_disconnect_time = time();
$quota_utils:initialize_quota(new);
if (!(ERR = $wiz_utils:set_player(new)))
msg = tostr("An error, ", ERR, " occurred while trying to make ", new, " a player. The database is probably inconsistent.");
else
new.email_address = email;
$registration_db:add(new, email, real_name);
move(new, $player_start);
if (class == $prog)
new.programmer = 1;
elseif (class == $wiz)
new.programmer = 1;
new.wizard = 1;
endif
$mail_agent:send_message(user, $new_player_log, tostr(name, " (", new, ")"), tostr(email, real_name ? " " + real_name | ""));
if (real_name)
real_name = ("Real name: " + real_name) + ". ";
endif
msg = tostr("New account created. User ID: ", name, " Password: ", password, " ", real_name, "Type: ", class.name, ". ");
if ($network.active)
if ((result = $wiz_utils:send_new_player_mail(tostr("From ", user.name, "@", $network.moo_name, ":"), name, email, new, password, user)) == 0)
msg = msg + tostr("Password sent successfully via email to ", email, ".");
else
msg = msg + tostr("Cannot send password via email. Reason: ", result);
endif
else
msg = msg + tostr("Password was not sent via email because the network is not active.");
endif
endif
else
msg = "Sorry, that name is already in use. Please select a different name and try again";
endif
else
msg = "You are required to fill in both name and email address";
endif
return msg;
"Last modified Sun May 16 17:35:14 1999 CDT by Wizard (#2).";
.
#158:7
"Copyright (C) 1999, Jan Rune Holmevik";
"Update player data";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, person, class, name, email, ?real_name = ""} = args;
if (!user.wizard)
return "403_Forbidden";
endif
body = {};
msg = "";
if ((name != "") && (email != ""))
"Update data for character";
old_class = parent(person);
if (old_class != class)
if (person == #2)
return "Trust me, you really do not want to dewiz #2 since that will render your MOO inoperable! Are you not grateful that Xpress is looking after you so well ;-)";
elseif (class == $guest)
return "For security reasons you are not permitted to change a former character to a guest character! Create a new guest character instead.";
elseif (old_class == $guest)
return "For security resons, a former guest character cannot be made into a regular player character. Create a new player character instead.";
endif
if (typeof(result = user:_chparent(person, class)) != ERR)
if (old_class == $wiz)
person.wizard = 0;
endif
if ((old_class == $prog) && (class != $wiz))
person.programmer = 0;
endif
if (class == $prog)
person.programmer = 1;
elseif (class == $wiz)
person.programmer = 1;
person.wizard = 1;
endif
msg = tostr("Character type changed to ", class.name, ". ");
else
msg = "Character type could not be changed. ";
endif
endif
if (person.name != name)
if (person:set_name(name))
msg = msg + "Name changed. ";
else
msg = msg + "Name could not be changed because it is already in use by someone else. Please select a different name. ";
endif
endif
if (person.email_address != email)
person.email_address = email;
msg = msg + "Email Address Changed. ";
endif
if (person.real_name != real_name)
person.real_name = real_name;
msg = msg + "Real Name Changed. ";
endif
else
msg = "You are required to fill in both name and email address";
endif
return msg;
"Last modified Thu Apr  8 12:12:21 1999 CDT by Wizard (#2).";
.
#158:8
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Find a character and return character data";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
if (!user.wizard)
return "403_Forbidden";
endif
alert = "";
if (data)
name = data[2][1];
person = $string_utils:match_player(name);
if (person == $failed_match)
alert = tostr("alert('Sorry, no users by that name was found. Please try again');document.search.name.focus();");
body = this:view_html(user, {{}, {}});
else
body = this:view_html(user, {person, {"update"}});
endif
endif
result = this:build(user, body, "Character Administration", alert);
return result;
"Last modified Fri Apr 13 18:59:21 2001 CDT by Wizard (#2).";
.
#158:9
"Copyright (C) 1995-98, Jan Rune Holmevik";
"Update preferences for #2 plus MOO contact email addresses";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
if (!user.wizard)
return "403_Forbidden";
endif
msg = "";
if (data != "")
info = data[2];
name = info[2];
email = info[3];
password = info[4];
if (name != #2.real_name)
#2.real_name = name;
msg = "New name set. ";
endif
if (email != #2.email_address)
$network.postmaster = $network.reply_address = $network.errors_to_address = $network.usual_postmaster = $network.password_postmaster = $network.envelope_from = email;
#2.email_address = email;
msg = msg + "New email address registered. ";
endif
if (password != #2.password)
#2.password = crypt(tostr(password));
#2.last_password_time = time();
msg = msg + "New password for wizard #2 set.";
endif
endif
if (msg != "")
javascript = tostr("alert('", msg, "')");
else
javascript = "alert('No changes made.')";
endif
body = this:core_preferences_html(user, {{}, {}});
result = this:pre_assemble(user, body, "enCore Preferences", javascript);
return result;
"Last modified Thu Apr  8 12:12:22 1999 CDT by Wizard (#2).";
.
#158:10
"Copyright (C) 1999, Jan Rune Holmevik";
"Mail batch creation report.";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
if (!user.wizard)
return "403_Forbidden";
endif
report = data[2][$ - 1];
action = data[2][$];
if ($network.active)
$xpress_moo_mailer:sendmail(user, user.email_address, "Account Creation Report", @report);
msg = tostr("alert('Account creation report sent to you via email')");
else
$mail_agent:send_message(user, user, "", report);
msg = tostr("alert('Account creation report sent to you via MOOmail')");
endif
body = this:view_html(user, {{}, {}});
result = this:pre_assemble(user, body, "Character Administration", msg);
return result;
"Last modified Sun May 16 17:34:11 1999 CDT by Wizard (#2).";
.
#158:11
"Copyright (C) 1999, Jan Rune Holmevik";
"Create several characters at once.";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
if (!user.wizard)
return "403_Forbidden";
endif
body = names = email_addresses = real_names = {};
bad_names = "";
class = toobj(data[2][2]);
account_data = data[2][3];
if (typeof(account_data) == STR)
account_data = {account_data};
endif
"=== Organize data ===";
for line in (account_data)
$command_utils:suspend_if_needed(1);
parts = $string_utils:explode(line);
try
names = {@names, parts[1]};
if (class != $guest)
email_addresses = {@email_addresses, parts[2]};
real_names = {@real_names, $string_utils:from_list(parts[3..$], " ")};
endif
except error (ANY)
body = this:view_html(user, {{class, "", account_data}, {"batch"}});
msg = "alert('You need to fill both names and valid email addresses. Real names are optional. If you are creating new guest characters, you only need to fill in the names.')";
result = this:pre_assemble(user, body, "Character Administration", msg);
return result;
endtry
endfor
"=== Check that names are available ===";
for name in (names)
if (!$player_db:available(name))
bad_names = (bad_names + name) + " ";
endif
endfor
if (bad_names)
return this:view_html(user, {{class, bad_names, account_data}, {"batch"}});
else
result = {tostr("Account Creation Report: ", $network.MOO_name)};
result = {@result, tostr("Date: ", $time_utils:time_sub("$2/$T/$Y", time()), $time_utils:time_sub(" $H:$M:$S, $Z", time()))};
result = {@result, tostr("Created by: ", user.name)};
result = {@result, ""};
for n in [1..length(names)]
$command_utils:suspend_if_needed(1);
if (class == $guest)
result = {@result, this:make_guest(user, class, names[n])};
else
result = {@result, this:make_player(user, class, names[n], email_addresses[n], real_names[n])};
endif
result = {@result, ""};
endfor
endif
return this:view_html(user, {{$player_class, "", result}, {"report"}});
"Last modified Sun May 16 17:34:25 1999 CDT by Wizard (#2).";
.
#158:12
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
if (!user.wizard)
return "403_Forbidden";
endif
msg = data[1];
bookmark_source = $xpress_navigator.bookmarks;
category = "";
bookmarks = {};
index = 0;
if (data[2] != {})
index = toint(data[2][1]);
if (index > 0)
category = bookmark_source[index][1];
bookmarks = bookmark_source[index][2];
endif
endif
title = $encore_web_utils:get_subtitle(user, this, "Xpress Bookmark Editor");
{help_button, help_function} = $encore_web_utils:insert_context_sensitive_help(user, $wiz_help, "Xpress_Bookmark_Editor");
functions = $encore_web_utils:insert_javascript(help_function);
menu = {"<SELECT onchange=this.form.submit() NAME=form>"};
menu = {@menu, "<OPTION VALUE=0>Edit Bookmarks"};
menu = {@menu, "<OPTION VALUE=0>Create New Category"};
for n in [1..length(bookmark_source)]
menu = {@menu, tostr("<OPTION VALUE=", n, ">", bookmark_source[n][1])};
endfor
menu = {@menu, "</SELECT>"};
menu = $list_utils:append(help_button, menu);
menu = $encore_web_utils:form(menu, "menu", "/administration_module/edit_xpress_bookmarks_html");
edit_form = {"<INPUT TYPE=hidden NAME=\"index\" VALUE=\"", tostr(index), "\">"};
edit_form = {@edit_form, tostr("<P>Bookmark Category<BR><INPUT TYPE=text NAME=\"Bookmark Categories\" VALUE=\"", category, "\" SIZE=30>")};
edit_form = {@edit_form, tostr("<P>Bookmarks. <BR><TEXTAREA NAME=\"Bookmarks\" ROWS=10 COLS=30 WRAP=virtual>")};
for item in (bookmarks)
edit_form = {@edit_form, tostr(item)};
endfor
edit_form = {@edit_form, "</TEXTAREA><P>"};
if (index == 0)
edit_form = {@edit_form, tostr("<INPUT TYPE=\"submit\" VALUE=\"Save New Bookmarks\">")};
edit_form = $encore_web_utils:form(edit_form, "Bookmarks", "/administration_module/create_bookmark");
else
edit_form = {@edit_form, tostr("<INPUT TYPE=\"submit\" VALUE=\"Update Bookmarks\"><INPUT TYPE=\"button\" VALUE=\"Delete This Bookmark Category\" onClick=\"location.href = 'http://", $network.site, ":", $network.webport, "/administration_module/delete_bookmark?", tostr(index), "'\"><INPUT TYPE=\"button\" VALUE=\"Cancel\" onClick=\"location.href = 'http://", $network.site, ":", $network.webport, "/administration_module/edit_xpress_bookmarks_html'\">")};
edit_form = $encore_web_utils:form(edit_form, "Bookmarks", "/administration_module/update_bookmark");
endif
body = $list_utils:append(functions, title, menu, edit_form);
result = this:build(user, body, "Xpress Navigator Editor", msg);
return result;
"Last modified Fri Apr 13 18:58:45 2001 CDT by Wizard (#2).";
.
#158:13
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
if (!user.wizard)
return "403_Forbidden";
endif
try
msg = err1 = err2 = "";
if (verb == "delete_bookmark")
index = toint(data[2][1]);
msg = tostr("Bookmark category: ", $xpress_navigator.bookmarks[index][1], " deleted");
$xpress_navigator.bookmarks = listdelete($xpress_navigator.bookmarks, index);
else
bookmarks = {};
index = toint(data[2][1][2]);
category = data[2][2];
objects = data[2][3];
if (typeof(data[2][3]) != LIST)
objects = {objects};
endif
for n in (objects)
bookmark = toobj(n);
if ((bookmark == #0) || (toint(bookmark) > toint(max_object())))
err1 = ";alert('One or more bookmarks not added due to invalid object number(s). Do not use object names when entering bookmarks!')";
elseif (!$object_utils:isa(bookmark, $encore_web_class))
err2 = ";alert('One or more bookmarks not added because they cannot be viewed in Xpress!')";
else
bookmarks = {@bookmarks, bookmark};
endif
endfor
if (verb == "create_bookmark")
$xpress_navigator.bookmarks = listappend($xpress_navigator.bookmarks, {category, bookmarks});
msg = tostr("New category named: ", category, " added to Xpress Navigator bookmarks");
else
$xpress_navigator.bookmarks = listdelete($xpress_navigator.bookmarks, index);
$xpress_navigator.bookmarks = listinsert($xpress_navigator.bookmarks, {category, bookmarks}, index);
msg = tostr("Bookmark category: ", category, " updated");
endif
endif
except error (ANY)
msg = "An error occurred, please try again";
endtry
alert = (tostr("alert('", msg, "!')") + err1) + err2;
body = this:edit_xpress_bookmarks_html(user, {alert, {}});
return body;
"Last modified Fri Apr 13 18:59:03 2001 CDT by Wizard (#2).";
.
#158:14
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Compose and edit message of the day to be displayed at login.";
"===========================================================";
{user, data} = args;
if (!user.wizard)
return "403_Forbidden";
endif
base_url = "/administration_module/edit_motd_html";
alert = text = "";
{help_button, help_function} = $encore_web_utils:insert_context_sensitive_help(user, $wiz_help, "Xpress_MOTD_Editor");
functions = $encore_web_utils:insert_javascript(help_function);
title = $encore_web_utils:get_subtitle(user, this, "Xpress Message of the Day Editor");
if (data != {{}, {}})
"-------------------------------------------------------------";
"Arguments specified, update properties before generating form";
"-------------------------------------------------------------";
action = data[1][2];
text = $string_utils:subst(data[2][1], {{"\"", ""}, {"'", ""}});
if (action == "clear")
$xpress_client.motd_msg = "";
$xpress_client.MOTD_viewer_list = {};
alert = "Message of the Day Cleared";
else
if ($xpress_client.motd_msg == text)
alert = "No changes made";
else
$xpress_client.motd_msg = text;
$xpress_client.MOTD_viewer_list = {};
alert = "Message of the Day Updated";
endif
endif
endif
"-------------------------------------------------------------";
"Generate form";
"-------------------------------------------------------------";
body = {tostr("<P>", $network.MOO_name, " Message of the Day:<P><INPUT TYPE=\"text\" name=\"MOTD_msg\" size=\"60\" maxlength=\"255\" VALUE=\"", $xpress_client.motd_msg, "\">")};
body = {@body, tostr("<P><INPUT TYPE=\"submit\" NAME=\"update\" VALUE=\"Update\">")};
if ($xpress_client.motd_msg != "")
body = {@body, tostr("<INPUT TYPE=\"submit\" NAME=\"clear\" VALUE=\"Clear\">")};
endif
body = $encore_web_utils:form(body, "motd_form", base_url);
body = $list_utils:append(functions, title, help_button, body);
result = this:pre_assemble(user, body, "Message of the Day Editor", $encore_web_utils:make_alert(alert));
return result;
"Last modified Fri Apr 13 18:59:21 2001 CDT by Wizard (#2).";
.
#158:15
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Form for editing and managing generics";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
if (!user.wizard)
return "403_Forbidden";
endif
msg = data[1];
generics_source = $xpress_object_editor.generics;
category = "";
generics = {};
index = 0;
if (data[2] != {})
index = toint(data[2][1]);
if (index > 0)
category = generics_source[index][1];
generics = generics_source[index][2];
endif
endif
title = $encore_web_utils:get_subtitle(user, this, "Xpress Generics Editor");
{help_button, help_function} = $encore_web_utils:insert_context_sensitive_help(user, $wiz_help, "Xpress_Generics_Editor");
functions = $encore_web_utils:insert_javascript(help_function);
menu = {"<SELECT onchange=this.form.submit() NAME=form>"};
menu = {@menu, "<OPTION VALUE=0>Edit Generics"};
menu = {@menu, "<OPTION VALUE=0>Create New Generics Category"};
for n in [1..length(generics_source)]
menu = {@menu, tostr("<OPTION VALUE=", n, ">", generics_source[n][1])};
endfor
menu = {@menu, "</SELECT>"};
menu = $list_utils:append(help_button, menu);
menu = $encore_web_utils:form(menu, "menu", "/administration_module/edit_generics_html");
edit_form = {"<INPUT TYPE=hidden NAME=\"index\" VALUE=\"", tostr(index), "\">"};
edit_form = {@edit_form, tostr("<P>Generics Category<BR><INPUT TYPE=text NAME=\"Generics Categories\" VALUE=\"", category, "\" SIZE=30>")};
edit_form = {@edit_form, tostr("<P>Generics. <BR><TEXTAREA NAME=\"Generics\" ROWS=10 COLS=30 WRAP=virtual>")};
for item in (generics)
edit_form = {@edit_form, tostr(item)};
endfor
edit_form = {@edit_form, "</TEXTAREA><P>"};
if (index == 0)
edit_form = {@edit_form, tostr("<INPUT TYPE=\"submit\" VALUE=\"Save New Generics\">")};
edit_form = $encore_web_utils:form(edit_form, "Generics", "/administration_module/create_generics");
else
edit_form = {@edit_form, tostr("<INPUT TYPE=\"submit\" VALUE=\"Update Generics\"><INPUT TYPE=\"button\" VALUE=\"Delete This Generics Category\" onClick=\"location.href = 'http://", $network.site, ":", $network.webport, "/administration_module/delete_generics?", tostr(index), "'\"><INPUT TYPE=\"button\" VALUE=\"Cancel\" onClick=\"location.href = 'http://", $network.site, ":", $network.webport, "/administration_module/edit_generics_html'\">")};
edit_form = $encore_web_utils:form(edit_form, "Generics", "/administration_module/update_generics");
endif
body = $list_utils:append(functions, title, menu, edit_form);
result = this:pre_assemble(user, body, "Xpress Generics Editor", msg);
return result;
"Last modified Fri Apr 13 18:59:40 2001 CDT by Wizard (#2).";
.
#158:16
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
if (!user.wizard)
return "403_Forbidden";
endif
try
msg = err1 = "";
if (verb == "delete_generics")
index = toint(data[2][1]);
msg = tostr("Generics Category: ", $xpress_object_editor.generics[index][1], " deleted");
$xpress_object_editor.generics = listdelete($xpress_object_editor.generics, index);
else
generics = {};
index = toint(data[2][1][2]);
category = data[2][2];
objects = data[2][3];
if (typeof(data[2][3]) != LIST)
objects = {objects};
endif
for n in (objects)
object = toobj(n);
if (!object.f)
err1 = ";alert('One or more objects cannot be added to generics. Please make sure all objects are set to fertile')";
else
generics = {@generics, object};
endif
endfor
if (verb == "create_generics")
$xpress_object_editor.generics = listappend($xpress_object_editor.generics, {category, generics});
msg = tostr("New category named: ", category, " added to Generics");
else
$xpress_object_editor.generics = listdelete($xpress_object_editor.generics, index);
$xpress_object_editor.generics = listinsert($xpress_object_editor.generics, {category, generics}, index);
msg = tostr("Generics Category: ", category, " Updated");
endif
endif
except error (ANY)
msg = "An error occurred, please try again";
endtry
alert = tostr("alert('", msg, "!')") + err1;
body = this:edit_generics_html(user, {alert, {}});
return body;
"Last modified Fri Apr 13 19:00:18 2001 CDT by Wizard (#2).";
.
#158:17
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Assemble the most important system settings for easy editing.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data, ?javascript = ""} = args;
title = menu = form = {};
"----------------------------------------------------------------";
" Build display header";
"----------------------------------------------------------------";
title = $encore_web_utils:get_subtitle(user, this, tostr($network.MOO_name, " System Settings"));
"----------------------------------------------------------------";
"Build list of available core settings.";
"----------------------------------------------------------------";
menu = {"<SELECT NAME=\"Edit_Options\" onChange=this.form.submit()><OPTION VALUE=\"0\">Edit enCore System Settings"};
menu = {@menu, "<OPTION VALUE=\"edit_network\">Network Settings"};
menu = {@menu, "<OPTION VALUE=\"edit_login\">Login Settings"};
menu = {@menu, "<OPTION VALUE=\"edit_user_system_settings\">User System Settings"};
menu = {@menu, "<OPTION VALUE=\"edit_telnet_welcome_screen\">Telnet Welcome Screen"};
menu = {@menu, "<OPTION VALUE=\"edit_xpress_login\">Xpress Welcome Screen"};
menu = {@menu, "<OPTION VALUE=\"edit_xpress_settings\">Xpress System Settings"};
menu = {@menu, "<OPTION VALUE=\"edit_xpress_user_settings\">Xpress User Settings"};
menu = {@menu, "<OPTION VALUE=\"edit_xpress_object_layout\">Xpress Object Layout"};
menu = {@menu, "<OPTION VALUE=\"edit_xpress_application_layout\">Xpress Application Layout"};
menu = {@menu, "</SELECT>"};
menu = $encore_web_utils:form(menu, "edit", "/Administration_Module/Core_Settings");
if (data != {{}, {}})
edit_action = data[2][1];
"------------------------------------------------------------";
" User has selected an editing option. Add correct form";
"------------------------------------------------------------";
form = this:(edit_action)(user);
form = {@form, "<P><INPUT TYPE=submit NAME=Submit VALUE=\"Save Changes\">"};
form = $encore_web_utils:form(form, "System_Settings", "/Administration_Module/save_settings");
endif
body = $list_utils:append(title, menu, form);
result = this:build(user, body, "Xpress Edit", javascript);
return result;
"Last modified Mon Sep 17 10:07:03 2001 CDT by Wizard (#2).";
.
#158:18
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Save system settings.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
if (data == {{}, {}})
return "400_Bad_Request";
else
object = toobj(data[2][1]);
result = $xpress_object_editor:set_property(user, object, data);
endif
return this:core_settings(user, {{}, {}}, result);
"Last modified Fri Apr 13 19:07:07 2001 CDT by Wizard (#2).";
.
#158:19
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user} = args;
object = $network;
form = {tostr("<INPUT TYPE=hidden NAME=object VALUE=\"", toint(object), "\">")};
form = {@form, "<P><B>MOO Network Preferences:</B><P>"};
if ($network.active)
form = {@form, tostr("Network: <INPUT TYPE=radio NAME=active VALUE=\"1\" CHECKED> On <INPUT TYPE=radio NAME=active VALUE=\"0\"> Off")};
else
form = {@form, tostr("Network: <INPUT TYPE=radio NAME=active VALUE=\"1\"> On <INPUT TYPE=radio NAME=active VALUE=\"0\" CHECKED> Off")};
endif
form = {@form, tostr("<P>MOO Name: <INPUT TYPE=text NAME=\"moo_name\" VALUE=\"", $network.MOO_name, "\" SIZE=30><P>Domain Name: <INPUT TYPE=text NAME=site VALUE=\"", $network.site, "\" SIZE=30><P>Mail Server: <INPUT TYPE=text NAME=maildrop VALUE=\"", $network.maildrop, "\" SIZE=30><P>Telnet Port <INPUT TYPE=text NAME=port VALUE=\"", $network.port, "\" SIZE=5 MAXLENGTH=4> Web Port <INPUT TYPE=text NAME=webport VALUE=\"", $network.webport, "\" SIZE=5 MAXLENGTH=4><P>")};
return form;
"Last modified Fri Apr 13 19:07:07 2001 CDT by Wizard (#2).";
.
#158:20
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user} = args;
object = #0;
form = {tostr("<INPUT TYPE=hidden NAME=object VALUE=\"", toint(object), "\">")};
form = {@form, tostr("<B>Name and Anonymity Settings:</B><P>")};
form = {@form, "<P>Anonymity. Hide Real Names and Email Addresses :"};
if ($anonymous_users)
form = {@form, "<INPUT TYPE=radio NAME=\"anonymous_users\" VALUE=1 CHECKED> Yes <INPUT TYPE=radio NAME=\"anonymous_users\" VALUE=0> No"};
else
form = {@form, "<INPUT TYPE=radio NAME=\"anonymous_users\" VALUE=1> Yes <INPUT TYPE=radio NAME=\"anonymous_users\" VALUE=0 CHECKED> No"};
endif
form = {@form, "<P>Allow Guests to Use Real Names: "};
if ($real_guest_names)
form = {@form, "<INPUT TYPE=radio NAME=\"real_guest_names\" VALUE=1 CHECKED> Yes <INPUT TYPE=radio NAME=\"real_guest_names\" VALUE=0> No"};
else
form = {@form, "<INPUT TYPE=radio NAME=\"real_guest_names\" VALUE=1> Yes <INPUT TYPE=radio NAME=\"real_guest_names\" VALUE=0 CHECKED> No"};
endif
if ($real_guest_names)
form = {@form, tostr("<P>Guest Name Suffix : <INPUT TYPE=text NAME=\"guest_name_suffix\" VALUE=\"", $guest_name_suffix, "\" SIZE=10 MAXLENGTH=10> For no suffix, leave blank.")};
endif
return form;
"Last modified Fri Apr 13 19:07:07 2001 CDT by Wizard (#2).";
.
#158:21
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user} = args;
object = $login;
form = {tostr("<INPUT TYPE=hidden NAME=object VALUE=\"", object, "\"><B>MOO Telnet Login Screen:</B>")};
form = {@form, tostr("<P><TEXTAREA NAME=\"welcome_message\" ROWS=24 COLS=79 WRAP=virtual>")};
if (typeof($login.welcome_message) == STR)
form = {@form, $login.welcome_message};
else
form = {@form, @$login.welcome_message};
endif
form = {@form, "</TEXTAREA>"};
return form;
"Last modified Fri Apr 13 19:07:29 2001 CDT by Wizard (#2).";
.
#158:22
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user} = args;
object = $xpress_client;
form = {tostr("<INPUT TYPE=hidden NAME=object VALUE=\"", object, "\"><A NAME=xpress></A><B>Xpress System Settings:</B><P>Xpress Client ")};
if (object.on)
form = {@form, "<INPUT TYPE=radio NAME=\"on\" VALUE=1 CHECKED> On <INPUT TYPE=radio NAME=\"on\" VALUE=\"0\"> Off"};
else
form = {@form, "<INPUT TYPE=radio NAME=\"on\" VALUE=1> On <INPUT TYPE=radio NAME=\"on\" VALUE=\"0\" CHECKED> Off"};
endif
form = {@form, "<P>Enable anonymous HTTP requests: "};
if (object.HTTP_09_enabled)
form = {@form, "<INPUT TYPE=radio NAME=\"http_09_enabled\" VALUE=1 CHECKED> Yes <INPUT TYPE=radio NAME=\"http_09_enabled\" VALUE=\"0\"> No"};
else
form = {@form, "<INPUT TYPE=radio NAME=\"http_09_enabled\" VALUE=1> Yes <INPUT TYPE=radio NAME=\"http_09_enabled\" VALUE=\"0\" CHECKED> No"};
endif
form = {@form, "<P>When this option is enabled, users can browse the MOO without having to log in. To use editors and other web applications, however, the user must be logged in."};
form = {@form, "<P>Allow Xpress MOO Mailer to send via Internet: "};
if ($xpress_client.allow_internet_mail)
form = {@form, "<INPUT TYPE=radio NAME=\"allow_internet_mail\" VALUE=1 CHECKED> Yes <INPUT TYPE=radio NAME=\"allow_internet_mail\" VALUE=0> No"};
else
form = {@form, "<INPUT TYPE=radio NAME=\"allow_internet_mail\" VALUE=1> Yes <INPUT TYPE=radio NAME=\"allow_internet_mail\" VALUE=0 CHECKED> No"};
endif
form = {@form, tostr("<P>External Base URL: <INPUT TYPE=text NAME=\"external_baseurl\" VALUE=\"", object.external_baseurl, "\" SIZE=50>")};
return form;
"Last modified Fri Apr 13 19:07:29 2001 CDT by Wizard (#2).";
.
#158:23
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user} = args;
object = $xpress_login;
form = {tostr("<INPUT TYPE=hidden NAME=object VALUE=\"", object, "\"><A NAME=xpress></A><B>Login Area:</B><P>")};
form = {@form, "Font <SELECT NAME=\"web_text_color\">"};
for color in ($xpress_client.web_colors)
if (color == object.web_text_color)
form = {@form, tostr("<OPTION VALUE=", color, " SELECTED>", color)};
else
form = {@form, tostr("<OPTION VALUE=", color, ">", color)};
endif
endfor
form = {@form, "</SELECT>"};
form = {@form, " Link: <SELECT NAME=\"web_link_color\">"};
for color in ($xpress_client.web_colors)
if (color == object.web_link_color)
form = {@form, tostr("<OPTION VALUE=", color, " SELECTED>", color)};
else
form = {@form, tostr("<OPTION VALUE=", color, ">", color)};
endif
endfor
form = {@form, "</SELECT> Visited Link: <SELECT NAME=\"web_vlink_color\">"};
for color in ($xpress_client.web_colors)
if (color == object.web_vlink_color)
form = {@form, tostr("<OPTION VALUE=", color, " SELECTED>", color)};
else
form = {@form, tostr("<OPTION VALUE=", color, ">", color)};
endif
endfor
form = {@form, "</SELECT>"};
form = {@form, "<P>Background <SELECT NAME=\"web_bgcolor\">"};
for color in ($xpress_client.web_colors)
if (color == object.web_bgcolor)
form = {@form, tostr("<OPTION VALUE=", color, " SELECTED>", color)};
else
form = {@form, tostr("<OPTION VALUE=", color, ">", color)};
endif
endfor
form = {@form, "</SELECT></P>"};
form = {@form, "<P><B>Welcome screen HTML:</B><P>Enter HTML code for your MOO welcome screen below. The settings above only affect the login area itself, so your code must define both content and layout of the welcome screen.</P>"};
form = {@form, tostr("<TEXTAREA NAME=\"welcome_screen\" ROWS=15 COLS=79 WRAP=virtual>")};
if (typeof(object.welcome_screen) == STR)
form = {@form, object.welcome_screen};
else
form = {@form, @object.welcome_screen};
endif
form = {@form, "</TEXTAREA>"};
return form;
"Last modified Fri Apr 13 19:07:29 2001 CDT by Wizard (#2).";
.
#158:24
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user} = args;
object = $player;
form = {tostr("<INPUT TYPE=hidden NAME=object VALUE=\"", object, "\"><A NAME=xpress></A><B>Default Xpress User Settings:</B>")};
form = {@form, "<P>Default Xpress theme: <SELECT NAME=\"xpress_theme\">"};
for theme in ($xpress_client.themes)
if (theme[2] == object.xpress_theme)
form = {@form, tostr("<OPTION VALUE=", theme[2], " SELECTED>", theme[1])};
else
form = {@form, tostr("<OPTION VALUE=", theme[2], ">", theme[1])};
endif
endfor
form = {@form, "</SELECT"};
form = {@form, "<P>Icon Size: <SELECT NAME=\"icon_size\">"};
for size in ($xpress_client.icon_sizes)
if (size[2] == object.icon_size)
form = {@form, tostr("<OPTION VALUE=", size[2], " SELECTED>", size[1])};
else
form = {@form, tostr("<OPTION VALUE=", size[2], ">", size[1])};
endif
endfor
form = {@form, "</SELECT>"};
form = {@form, "<P>Show Contextual Menu: "};
if (object.show_contextual_menu)
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"1\" name=\"show_contextual_menu\" CHECKED> Yes <INPUT TYPE=\"radio\" value=\"0\" name=\"show_contextual_menu\"> No <P>")};
else
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"1\" name=\"show_contextual_menu\"> Yes <INPUT TYPE=\"radio\" value=\"0\" name=\"show_contextual_menu\" CHECKED> No")};
endif
form = {@form, "<P>Screen Size: "};
form = {@form, "<SELECT NAME=Xpress_Screen_Size>"};
for size in ($xpress_client.screen_sizes)
if (size[1] == object.xpress_screen_size)
form = {@form, tostr("<OPTION VALUE=\"", size[1], "\" SELECTED>", size[2])};
else
form = {@form, tostr("<OPTION VALUE=\"", size[1], "\">", size[2])};
endif
endfor
form = {@form, "</SELECT></P><P>Screen Layout: "};
form = {@form, "<SELECT NAME=\"Xpress_layout\">"};
for option in ($xpress_client.screen_layouts)
if (option[1] == object.xpress_layout)
form = {@form, tostr("<OPTION VALUE=\"", option[1], "\" SELECTED>", option[2])};
else
form = {@form, tostr("<OPTION VALUE=\"", option[1], "\">", option[2])};
endif
endfor
form = {@form, "</SELECT>"};
form = {@form, "</P><P>Screen Division: "};
form = {@form, "<SELECT NAME=Xpress_Screen_Division>"};
for division in ($xpress_client.screen_divisions)
if (division[1] == object.xpress_screen_division)
form = {@form, tostr("<OPTION VALUE=\"", division[1], "\" SELECTED>", division[2])};
else
form = {@form, tostr("<OPTION VALUE=\"", division[1], "\">", division[2])};
endif
endfor
form = {@form, "</SELECT></P><P>Talk Area Font : <SELECT NAME=\"java_font\">"};
for font in ($xpress_client.java_fonts)
if (font == object.java_font)
form = {@form, tostr("<OPTION VALUE=", font, " SELECTED>", font)};
else
form = {@form, tostr("<OPTION VALUE=", font, ">", font)};
endif
endfor
form = {@form, "</SELECT>  Size: <SELECT NAME=\"java_font_size\">"};
for size in ($xpress_client.java_font_sizes)
if (size == object.java_font_size)
form = {@form, tostr("<OPTION VALUE=", size, " SELECTED>", size)};
else
form = {@form, tostr("<OPTION VALUE=", size, ">", size)};
endif
endfor
form = {@form, "</SELECT>"};
form = {@form, " Local Echo: <SELECT NAME=\"java_client_localecho\">"};
echo = object.java_client_localecho;
if (echo == "true")
form = {@form, tostr("<OPTION VALUE=\"True\" SELECTED>On<OPTION VALUE=\"False\">Off")};
else
form = {@form, tostr("<OPTION VALUE=\"True\">On<OPTION VALUE=\"False\" SELECTED>Off")};
endif
form = {@form, "</SELECT></P><P>Sound Volume:"};
form = {@form, "<SELECT NAME=sound_volume>"};
for option in ($xpress_client.sound_volumes)
if (option[1] == object.sound_volume)
form = {@form, tostr("<OPTION VALUE=\"", option[1], "\" SELECTED>", option[2])};
else
form = {@form, tostr("<OPTION VALUE=\"", option[1], "\">", option[2])};
endif
endfor
form = {@form, "</SELECT> Mail Notification:"};
form = {@form, "<SELECT NAME=mail_notify_sound>"};
for option in ($xpress_client.mail_notification_sounds)
if (option[1] == object.mail_notify_sound)
form = {@form, tostr("<OPTION VALUE=\"", option[1], "\" SELECTED>", option[2])};
else
form = {@form, tostr("<OPTION VALUE=\"", option[1], "\">", option[2])};
endif
endfor
form = {@form, "</SELECT>"};
form = {@form, "</P><P>Note: Users must have an appropirate plug-in installed and set to handle WAV files in order to hear sounds.</P>"};
return form;
"Last modified Fri Apr 13 19:07:48 2001 CDT by Wizard (#2).";
.
#158:25
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
form = this:layout_form(user, $encore_web_object, "Default Web Object Layout");
return form;
"Last modified Fri Apr 13 19:07:48 2001 CDT by Wizard (#2).";
.
#158:26
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
form = this:layout_form(user, $encore_web_application, "Default Web Application Layout");
return form;
"Last modified Fri Apr 13 19:07:48 2001 CDT by Wizard (#2).";
.
#158:27
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form for default web object and application layout.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, object, ?heading = "Xpress Default Layout Form"} = args;
form = {tostr("<INPUT TYPE=hidden NAME=object VALUE=\"", object, "\"><B>", heading, "</B>")};
form = {@form, "<P>Use external style sheet: "};
if (object.use_external_stylesheet)
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"1\" name=\"use_external_stylesheet\" CHECKED> Yes <INPUT TYPE=\"radio\" value=\"0\" name=\"use_external_stylesheet\"> No <P>")};
else
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"1\" name=\"use_external_stylesheet\"> Yes <INPUT TYPE=\"radio\" value=\"0\" name=\"use_external_stylesheet\" CHECKED> No")};
endif
form = {@form, tostr("<P>External style sheet: <INPUT TYPE=text NAME=external_stylesheet VALUE=\"", object.external_stylesheet, "\" SIZE=40><P>")};
form = {@form, "Title Color: <SELECT NAME=\"title_font_color\">"};
for color in ($xpress_client.web_colors)
if (color == object.title_font_color)
form = {@form, tostr("<OPTION VALUE=", color, " SELECTED>", color)};
else
form = {@form, tostr("<OPTION VALUE=", color, ">", color)};
endif
endfor
form = {@form, "</SELECT>"};
form = {@form, " Title Size: <SELECT NAME=\"title_font_size\">"};
for size in ($xpress_client.web_font_sizes)
if (size == object.title_font_size)
form = {@form, tostr("<OPTION VALUE=", size, " SELECTED>", size)};
else
form = {@form, tostr("<OPTION VALUE=", size, ">", size)};
endif
endfor
form = {@form, "</SELECT> Subtitle Size: <SELECT NAME=\"subtitle_font_size\">"};
for size in ($xpress_client.web_font_sizes)
if (size == object.subtitle_font_size)
form = {@form, tostr("<OPTION VALUE=", size, " SELECTED>", size)};
else
form = {@form, tostr("<OPTION VALUE=", size, ">", size)};
endif
endfor
form = {@form, "</SELECT>"};
form = {@form, "<P>Font: <SELECT NAME=\"web_font\">"};
for font in ($xpress_client.web_fonts)
if (font == object.web_font)
form = {@form, tostr("<OPTION VALUE=", font, " SELECTED>", font)};
else
form = {@form, tostr("<OPTION VALUE=", font, ">", font)};
endif
endfor
form = {@form, "</SELECT>  Size: <SELECT NAME=\"web_font_size\">"};
for size in ($xpress_client.web_font_sizes)
if (size == object.web_font_size)
form = {@form, tostr("<OPTION VALUE=", size, " SELECTED>", size)};
else
form = {@form, tostr("<OPTION VALUE=", size, ">", size)};
endif
endfor
form = {@form, "</SELECT>  Color: <SELECT NAME=\"web_text_color\">"};
for color in ($xpress_client.web_colors)
if (color == object.web_text_color)
form = {@form, tostr("<OPTION VALUE=", color, " SELECTED>", color)};
else
form = {@form, tostr("<OPTION VALUE=", color, ">", color)};
endif
endfor
form = {@form, "</SELECT><P>Link: <SELECT NAME=\"web_link_color\">"};
for color in ($xpress_client.web_colors)
if (color == object.web_link_color)
form = {@form, tostr("<OPTION VALUE=", color, " SELECTED>", color)};
else
form = {@form, tostr("<OPTION VALUE=", color, ">", color)};
endif
endfor
form = {@form, "</SELECT> Visited Link: <SELECT NAME=\"web_vlink_color\">"};
for color in ($xpress_client.web_colors)
if (color == object.web_vlink_color)
form = {@form, tostr("<OPTION VALUE=", color, " SELECTED>", color)};
else
form = {@form, tostr("<OPTION VALUE=", color, ">", color)};
endif
endfor
form = {@form, "</SELECT> Active Link: <SELECT NAME=\"web_alink_color\">"};
for color in ($xpress_client.web_colors)
if (color == object.web_alink_color)
form = {@form, tostr("<OPTION VALUE=", color, " SELECTED>", color)};
else
form = {@form, tostr("<OPTION VALUE=", color, ">", color)};
endif
endfor
form = {@form, "</SELECT><P>Underline links: "};
if (object.underline_links)
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"1\" name=\"underline_links\" CHECKED> Yes <INPUT TYPE=\"radio\" value=\"0\" name=\"underline_links\"> No <P>")};
else
form = {@form, tostr("<INPUT TYPE=\"radio\" value=\"1\" name=\"underline_links\"> Yes <INPUT TYPE=\"radio\" value=\"0\" name=\"underline_links\" CHECKED> No")};
endif
form = {@form, "</SELECT><P>Background Color: <SELECT NAME=\"web_bgcolor\">"};
for color in ($xpress_client.web_colors)
if (color == object.web_bgcolor)
form = {@form, tostr("<OPTION VALUE=", color, " SELECTED>", color)};
else
form = {@form, tostr("<OPTION VALUE=", color, ">", color)};
endif
endfor
form = {@form, "</SELECT>"};
form = {@form, tostr("<P>Background Image URL: <INPUT TYPE=text NAME=web_background VALUE=\"", object.web_background, "\" SIZE=40><P>")};
return form;
"Last modified Fri Apr 13 19:07:48 2001 CDT by Wizard (#2).";
.
#158:28
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Xpress Edit form.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user} = args;
object = $login;
form = {tostr("<INPUT TYPE=hidden NAME=object VALUE=\"", toint(object), "\">")};
form = {@form, tostr("<B>Login Settings:</B><P>")};
form = {@form, "<P>Automatic Character Creation :"};
if ($login.create_enabled)
form = {@form, "<INPUT TYPE=radio NAME=\"create_enabled\" VALUE=1 CHECKED> On <INPUT TYPE=radio NAME=\"create_enabled\" VALUE=0> Off"};
else
form = {@form, "<INPUT TYPE=radio NAME=\"create_enabled\" VALUE=1> On <INPUT TYPE=radio NAME=\"create_enabled\" VALUE=0 CHECKED> Off"};
endif
form = {@form, "<P>If enabled, automatic character creation will be available from the login page.</P>"};
return form;
"Last modified Mon Sep 17 10:07:17 2001 CDT by Wizard (#2).";
.
#159:0
"Copyright (C) 2000, Jason Nolan and the University of Toronto";
"First page of the project editing process, allows user to set the general information about the project.";
user = args[1];
if (!caller_perms().wizard)
return E_PERM;
elseif (!$perm_utils:controls(user, this))
return {"You are not authorized to edit this project."};
endif
vars = args[2][1];
vals = args[2][2];
props = {"title", "subtitle", "course", "designer", "designer_email", "desc", "criteria", "references", "bibliography", "url", "logo", "web_bgcolor", "web_text_color", "web_link_color", "web_background", "show_designer_realname", "show_designer_mooname", "show_participant_realname", "show_participant_mooname", "numsections"};
if (vars)
"A form has been submitted...";
for x in [1..length(vars)]
"Commented out to make verb work with enCore, Jan 06/09/00";
"vars[x] = $string_utils:to_value(vars[x])[2]";
if ((typeof(this.(vars[x])) == STR) && (vals[x] == {}))
vals[x] = "";
elseif (typeof(this.(vars[x])) == NUM)
if (vals[x] == "on")
vals[x] = 1;
else
vals[x] = 0;
endif
elseif ((typeof(this.(vars[x])) == LIST) && (typeof(vals[x]) == STR))
vals[x] = {vals[x]};
endif
this.(vars[x]) = vals[x];
if (vars[x] == "title")
$building_utils:set_names(this, vals[x]);
endif
$command_utils:suspend_if_needed(0);
endfor
"Coerce all submitted variables into the proper datatype";
if (leftovers = $set_utils:difference(props, vars))
for x in [1..length(leftovers)]
this.(leftovers[x]) = 0;
endfor
endif
"Blank checkboxes aren't submitted at all, so notice this fact";
if (this.students)
"Changes to the layout are now locked out";
return {"<HTML><HEAD>", "<META HTTP-EQUIV=\"refresh\" CONTENT=\"0;url=admin_edit3.html\">", "</HEAD></HTML>"};
else
return {"<HTML><HEAD>", "<META HTTP-EQUIV=\"refresh\" CONTENT=\"0;url=admin_edit2.html\">", "</HEAD></HTML>"};
endif
else
core = this.admin_edit_layout;
next = 1;
for prop in (props)
core = this:find_insert(core, prop, this.(prop), next);
next = core[2] + 1;
core = core[1];
endfor
"Loop through the props, substituting them into the template, being careful not to check the same line twice";
body = core;
result = $encore_web_application:pre_assemble(user, body, this.title + " (Admin Edit)");
return result;
endif
"Last modified Fri Jun  9 15:20:42 2000 CDT by Wizard (#2).";
.
#159:1
"Copyright (C) 2000, Jason Nolan and the University of Toronto";
":find_insert(LIST text, STR search, STR replacement[, NUM begin[, STR value]])";
"Search through text line by line, beginning on line begin (defaults to 1). When search is found, append VALUE=\"replacement\", or value, if given.";
"Returns {LIST newtext, NUM linefound}";
{text, what, with, ?begin = 1, ?value = ""} = args;
for l in [begin..length(text)]
"The following code is a Godel sentence for the human Turing machine. Do not attempt to understand it. I certainly don't.";
if (i = index(text[l], what, 1))
i = (text[l][i + length(what)] == "\"") ? i + length(what) | ((i + length(what)) - 1);
if (typeof(with) == STR)
text[l] = ((text[l][1..i] + " ") + ((value != "") ? value | (("VALUE=\"" + with) + "\""))) + text[l][i + 1..$];
elseif ((typeof(with) == NUM) && (with != 0))
text[l] = ((text[l][1..i] + " ") + ((value != "") ? value | "CHECKED")) + text[l][i + 1..$];
elseif (typeof(with) == LIST)
text = {@text[1..l], @with, @text[l + 1..$]};
l = l + length(with);
endif
break;
endif
$command_utils:suspend_if_needed(0);
endfor
return {text, l};
"Last modified Fri Jun  9 15:20:43 2000 CDT by Wizard (#2).";
.
#159:2
"Copyright (C) 2000, Jason Nolan and the University of Toronto";
"Second page of the project editing process. Allows the user to control the projject's layout. Skipped if this.students is nonempty.";
user = args[1];
if (!caller_perms().wizard)
return E_PERM;
elseif (!$perm_utils:controls(user, this))
return {"You are not authorized to edit this project."};
elseif (this.students)
return {"You cannot make changes to the project layout once students have begun working on it."};
endif
vars = args[2][1];
vals = args[2][2];
len = toint(this.numsections);
if (vars)
"A form has been submitted...";
format = {};
for s in [1..len]
section = {};
for i in [1..8]
val = vals[((s - 1) * 8) + i];
if (val != "Choose one")
section = {@section, val};
endif
endfor
"Loop through the submitted elements, ignoring those that were left blank";
if (section != {})
format = {@format, section};
endif
$command_utils:suspend_if_needed(0);
endfor
this.format = format;
return {"<HTML><HEAD>", "<META HTTP-EQUIV=\"refresh\" CONTENT=\"0;url=admin_edit3.html\">", "</HEAD></HTML>"};
else
body = {("<CENTER><H1>" + $network.moo_name) + " Project Editor</H1>", "<FORM METHOD=\"POST\" ENCTYPE=\"multipart/form-data\">"};
body = {@body, tostr("<INPUT TYPE=\"BUTTON\" VALUE=\"Show Available Help\" onClick=\"javascript:window.open('/", tonum(this), "/admin_edit2_help.html', 'help', 'directories=no,location=no,menubar=no,personalbar=no,resizable=yes,scrollbars=yes,status=no,titlebar=yes,toolbar=no')\">")};
for s in [1..len]
body = {@body, tostr("<H2>Section ", s, ":</H2>")};
body = {@body, "<TABLE BORDER=1 CELLPADDING=5><TR>"};
for i in [1..8]
body = {@body, tostr("<TD>", $string_utils:capitalize($string_utils:english_ordinal(i)), " Item:<BR>")};
select = this:find_insert(this.admin_edit2_layout, "SELECT", "", 1, tostr("NAME=\"", s, ".", i, "\""))[1];
if (`this.format[s][i] ! ANY => 0')
select = this:find_insert(select, this.format[s][i], "", 1, "SELECTED")[1];
endif
body = {@body, @select, "</TD>"};
"Loop through the number of sections, substituting in the saved value if it exists";
if (i == 4)
body = {@body, "</TR><TR>"};
endif
$command_utils:suspend_if_needed(0);
endfor
body = {@body, "</TR></TABLE>"};
$command_utils:suspend_if_needed(0);
endfor
body = {@body, "<P></P>"};
body = {@body, "<INPUT TYPE=\"BUTTON\" VALUE=\"<<< Back\" onClick=\"javascript:document.location='admin_edit.html';\"><INPUT TYPE=\"RESET\" VALUE=\"Revert to Saved\"><INPUT TYPE=\"Submit\" VALUE=\"Save and Continue >>>\">"};
body = {@body, "</FORM></CENTER>"};
result = $encore_web_application:pre_assemble(user, body, this.title + " (Admin Edit)");
return result;
endif
"Last modified Fri Jun  9 15:20:43 2000 CDT by Wizard (#2).";
.
#159:3
"Copyright (C) 2000, Jason Nolan and the University of Toronto";
"Third page of the project editing process. Allows users to give directions for each section of the project.";
user = args[1];
if (!caller_perms().wizard)
return E_PERM;
elseif (!$perm_utils:controls(user, this))
return {"You are not authorized to edit this project."};
endif
vars = args[2][1];
vals = args[2][2];
len = length(this.format);
if (vars)
"A form has been submitted...";
directions = {};
for s in [1..len]
vals[s] = (typeof(vals[s]) == STR) ? {vals[s]} | vals[s];
directions = {@directions, vals[s]};
endfor
"Coerce them into the proper datatype if necessary, and append";
this.directions = directions;
return {"<HTML><HEAD>", tostr("<META HTTP-EQUIV=\"refresh\" CONTENT=\"0;url=/", tonum(this), "/\">"), "</HTML></HEAD>"};
else
body = {("<CENTER><H1>" + $network.moo_name) + " Project Editor</H1>"};
body = {@body, "<FORM METHOD=\"POST\" ENCTYPE=\"multipart/form-data\">"};
body = {@body, tostr("<INPUT TYPE=\"BUTTON\" VALUE=\"Show Available Help\" onClick=\"javascript:window.open('/", tonum(this), "/admin_edit3_help.html', 'help', 'directories=no,location=no,menubar=no,personalbar=no,resizable=yes,scrollbars=yes,status=no,titlebar=yes,toolbar=no')\">")};
for s in [1..len]
body = {@body, ("<H2>Directions for Section " + tostr(s)) + ":</H2>"};
body = {@body, ("<TEXTAREA COLS=75 ROWS=10 WRAP=\"soft\" NAME=\"" + tostr(s)) + "\">"};
if (`this.directions[s] ! ANY => 0')
body = {@body, @this.directions[s]};
endif
body = {@body, "</TEXTAREA>"};
endfor
"Loop through the number of sections, inserting a saved value where necessary";
body = {@body, "<P></P>"};
if (this.students)
body = {@body, "<INPUT TYPE=\"BUTTON\" VALUE=\"<<< Back\" onClick=\"javascript:document.location='admin_edit.html';\">"};
else
body = {@body, "<INPUT TYPE=\"BUTTON\" VALUE=\"<<< Back\" onClick=\"javascript:document.location='admin_edit2.html';\">"};
endif
if (len)
body[$] = body[$] + "<INPUT TYPE=\"RESET\" VALUE=\"Revert to Saved\"><INPUT TYPE=\"SUBMIT\" VALUE=\"Save and View >>>\">";
else
body = {@body, "<P>Sorry, but you may not enter directions until there is at least one section in your project. Please go back and create a section.</P>"};
endif
body = {@body, "</FORM></CENTER>"};
result = $encore_web_application:pre_assemble(user, body, this.title + " (Admin Edit)");
return result;
endif
"Last modified Fri Apr 13 19:10:21 2001 CDT by Wizard (#2).";
.
#159:4
"Copyright (C) 2000, Jason Nolan and the University of Toronto";
"Main index page for the project: lists the description and has links to all the various interactive functions.";
"I added target=\"_blank\" tags, 3 of em... 1.25.01";
user = args[1];
body = {"<HTML>", "<HEAD>"};
body = {@body, tostr("<TITLE>", this.title, " (", $network.moo_name, ")</TITLE>")};
body = {@body, "</HEAD>"};
body = {@body, $encore_web_utils:get_page_properties(this, this)[1]};
body = {@body, ("<FONT FACE=\"" + this.web_font) + "\">"};
body = {@body, @this:header_info()};
if (this.desc)
desc = this.desc;
desc[1] = "<B>Project Description:</B> " + desc[1];
desc = $encore_web_utils:detect_urls(user, desc, 1);
desc = $encore_web_utils:insert_line_breaks(desc);
desc = {@desc, "<P>"};
body = {@body, @desc};
endif
if (this.criteria)
criteria = this.criteria;
criteria[1] = "<B>Evaluation Criteria:</B> " + criteria[1];
criteria = $encore_web_utils:detect_urls(user, criteria);
criteria = $encore_web_utils:insert_line_breaks(criteria);
criteria = {@criteria, "<P>"};
body = {@body, @criteria};
endif
if (this.references)
references = this.references;
references = $encore_web_utils:detect_urls(user, references);
for r in [1..length(references)]
references[r] = ("<LI>" + references[r]) + "</LI>";
endfor
references = {"<B>Suggested Online References:</B><UL>", @references, "</UL>"};
body = {@body, @references};
endif
if (this.bibliography)
bibliography = this.bibliography;
bibliography = $encore_web_utils:detect_urls(user, bibliography);
for b in [1..length(bibliography)]
bibliography[b] = ("<LI>" + bibliography[b]) + "</LI>";
endfor
bibliography = {"<B>Suggested Bibliography:</B><UL>", @bibliography, "</UL>"};
body = {@body, @bibliography};
endif
if (this.url)
body = {@body, tostr("<B>For further information:</B> <A HREF=\"", this.url, "\" TARGET=\"_blank\">", this.url, "</A>")};
endif
body = {@body, "<P>"};
if ((this.handed_in && this.public) && (this.show_participant_realname || this.show_participant_mooname))
body = {@body, "<HR>"};
body = {@body, "<H3 ALIGN=\"CENTER\">Projects Handed In</H3>"};
body = {@body, "<UL>"};
for item in (this.handed_in)
if (this.show_participant_realname && this.(tostr(item))[1])
body = {@body, tostr("<LI><A HREF=\"/", tonum(this), "/view.html?", tonum(item), "\">", this.(tostr(item))[1], "</A></LI>")};
else
body = {@body, tostr("<LI><A HREF=\"/", tonum(this), "/view.html?", tonum(item), "\">", item.name, "</A></LI>")};
endif
endfor
body = {@body, "</UL>"};
endif
body = {@body, "<HR><CENTER><B>"};
body = {@body, "<A HREF=\"student_edit.html\" target=\"_blank\">I would like to work on this project.</A>"};
if ($perm_utils:controls(user, this))
body = {@body, "<P><A HREF=\"admin_edit.html\" target=\"_blank\">I am this project's designer and would like to edit it.</A>"};
body = {@body, "<P><A HREF=\"admin_manage.html\" target=\"_blank\">I am this project's designer and would like to manage it.</A>"};
endif
body = {@body, "</B></CENTER><HR>"};
body = {@body, tostr("<P ALIGN=\"CENTER\"><B><A HREF=\"javascript:window.close()\">Close this window</A></B></P>")};
body = {@body, "</FONT>"};
body = {@body, "</BODY>", "</HTML>"};
return body;
"Last modified Fri Apr 13 19:10:21 2001 CDT by Wizard (#2).";
.
#159:5
"Copyright (C) 2000, Jason Nolan and the University of Toronto";
"Returns header data on the project that should appear on all pages.";
body = {"<CENTER>"};
if (this.logo)
body = {@body, tostr("<IMG ALT=\"Project Logo\" SRC=\"", this.logo, "\" ALIGN=\"RIGHT\">")};
endif
if (this.title)
body = {@body, ("<H1>" + this.title) + "</H1>"};
endif
if (this.subtitle)
body = {@body, ("<H3><I>" + this.subtitle) + "</I></H3>"};
endif
if (this.course)
body = {@body, ("<B>" + this.course) + "</B><BR>"};
endif
if (this.designer && this.show_designer_realname)
body = {@body, this.designer};
if (this.designer_email)
body[$] = tostr("<A HREF=\"mailto:", this.designer_email, "\">", body[$], "</A>");
endif
body[$] = "Designed by " + body[$];
endif
if (this.show_designer_mooname)
body[$] = tostr(body[$], " (<A HREF=\"/", tonum(this.owner), "/\">", this.owner.name, "</A>)");
endif
body[$] = body[$] + "<P>";
body = {@body, "</CENTER>"};
return body;
"Last modified Fri Jun  9 15:21:00 2000 CDT by Wizard (#2).";
.
#159:6
"Copyright (C) 2000, Jason Nolan and the University of Toronto";
"Project editing page for students. Creates form fields according to the project's layout and also allows users to hand the project in.";
user = args[1];
if (!caller_perms().wizard)
return E_PERM;
elseif ($object_utils:isa(user, $guest))
return {"Guests are not allowed to work on projects. Please log in with a full user account."};
elseif ((!$list_utils:flatten(this.format)) || (!this.directions))
return {"This project has not yet been fully designed, and therefore cannot yet be worked on."};
elseif (this.archived)
return {"This project has been archived; therefore, you may not make further changes."};
elseif (user in this.handed_in)
return {"You have turned in this project, and therefore cannot edit it further."};
endif
"Only allow logged in users to work on the project if it has a layout, it's not archived, and e hasn't already turned it in.";
vars = args[2][1];
vals = args[2][2];
if (vars)
"A form has been submitted...";
student = {};
vals[1] = (vals[1] == {}) ? "" | vals[1];
vals[2] = (vals[2] == {}) ? "" | vals[2];
student = {vals[1], vals[2]};
vars = vars[3..$];
vals = vals[3..$];
if (vars[$] == "hand_in_assignment")
finished_flag = 1;
vars = vars[1..$ - 1];
vals = vals[1..$ - 1];
else
finished_flag = 0;
endif
if (vars[$] == "make_readable")
readable_flag = vals[$] == "on";
vars = vars[1..$];
vals = vals[1..$];
else
readable_flag = 0;
endif
for s in [1..length(this.format)]
student = {@student, {}};
for i in [1..length(this.format[s])]
idx = tostr(s, ".", i) in vars;
if ((this.format[s][i] in {"title", "subtitle", "image", "url", "audio"}) && (vals[idx] == {}))
vals[idx] = "";
elseif ((this.format[s][i] in {"paragraph", "inset_paragraph", "bulleted_list", "numbered_list"}) && (typeof(vals[idx]) == STR))
vals[idx] = {vals[idx]};
endif
student[s + 2] = {@student[s + 2], vals[idx]};
endfor
$command_utils:suspend_if_needed(0);
endfor
if (!(user in this.students))
"If this is their first time editing, we need to create a property so there's somewhere to put the data";
add_property(this, tostr(user), {}, {$assignment.owner, ""});
this.students = {@this.students, user};
endif
this.(tostr(user)) = student;
if (readable_flag && (!(user in this.readable)))
this.readable = setadd(this.readable, user);
this:notify_readable(user);
endif
if (finished_flag)
this.handed_in = {@this.handed_in, user};
this.handed_in_times = {@this.handed_in_times, time()};
this:notify_handin(user);
return {"<HTML><HEAD>", ("<META HTTP-EQUIV=\"refresh\" CONTENT=\"0;url=/" + tostr(tonum(this))) + "/\">", "</HEAD></HTML>"};
else
return {"<HTML><HEAD>", "<META HTTP-EQUIV=\"refresh\" CONTENT=\"0;url=view.html\">", "</HEAD></HTML>"};
endif
else
body = {"<HTML>", "<HEAD>"};
body = {@body, tostr("<TITLE>", this.title, " (Student Edit) (", $network.moo_name, ")</TITLE>")};
body = {@body, "</HEAD>"};
body = {@body, $encore_web_utils:get_page_properties(this, this)[1]};
body = {@body, ("<FONT FACE=\"" + this.web_font) + "\">"};
body = {@body, @this:header_info()};
body = {@body, "<FORM METHOD=\"POST\" ENCTYPE=\"multipart/form-data\">"};
body = {@body, "<CENTER>"};
body = {@body, tostr("<INPUT TYPE=\"BUTTON\" VALUE=\"Show Available Help\" onClick=\"javascript:window.open('/", tonum(this), "/student_edit_help.html', 'help', 'directories=no,location=no,menubar=no,personalbar=no,resizable=yes,scrollbars=yes,status=no,titlebar=yes,toolbar=no')\">")};
body = {@body, "<TABLE BORDER=0 CELLPADDING=5>"};
body = {@body, "<TR><TD>Participant Name(s):</TD><TD><INPUT TYPE=\"TEXT\" SIZE=75 NAME=\"participant\"></TD></TR>"};
body = {@body, "<TR><TD>Participant Email:</TD><TD><INPUT TYPE=\"TEXT\" SIZE=75 NAME=\"participant_email\"></TD></TR>"};
body = {@body, "</TABLE>"};
if (user in this.students)
body = this:find_insert(body, "participant", this.(tostr(user))[1])[1];
body = this:find_insert(body, "participant_email", this.(tostr(user))[2])[1];
endif
body = {@body, "<TABLE BORDER=1 CELLPADDING=2>"};
for s in [1..length(this.format)]
directions = this.directions[s];
if (directions)
directions[1] = "<B>Directions:</B> " + directions[1];
directions = $encore_web_utils:insert_line_breaks(directions);
endif
body = {@body, "<TR><TD COLSPAN=2>", ("<CENTER><H3>Section " + tostr(s)) + "</H3></CENTER>", @directions, "</TD></TR>"};
for i in [1..length(this.format[s])]
name = $string_utils:explode(this.format[s][i], "_");
if (name[1] == "url")
name = "URL";
else
name[1] = $string_utils:capitalize(name[1]);
`name[2] = $string_utils:capitalize(name[2]) ! ANY => 0';
name = $string_utils:from_list(name, " ");
"Parse the internal representation of the datatype into something pretty";
endif
if (this.format[s][i] in {"title", "subtitle", "image", "url", "audio"})
item = {"<TR>", ("<TD VALIGN=\"TOP\" width=\"20%\">" + name) + ":</TD>", tostr("<TD><INPUT TYPE=\"TEXT\" SIZE=75 NAME=\"", s, ".", i, "\"></TD>"), "</TR>"};
elseif (this.format[s][i] in {"paragraph", "inset_paragraph", "bulleted_list", "numbered_list"})
name = (name == "Paragraph") ? "Paragraph(s)" | name;
item = {"<TR>", ("<TD VALIGN=\"TOP\">" + name) + ":</TD>", tostr("<TD><TEXTAREA COLS=120 ROWS=20 WRAP=\"soft\" NAME=\"", s, ".", i, "\">"), "</TEXTAREA></TR>"};
endif
if (user in this.students)
item = this:find_insert(item, tostr(s, ".", i), this.(tostr(user))[s + 2][i])[1];
"Paste in the saved value, if any...";
endif
body = {@body, @item};
$command_utils:suspend_if_needed(0);
endfor
endfor
body = {@body, "</TABLE>"};
body = {@body, "<P></P>"};
if (user in this.readable)
body = {@body, "<INPUT TYPE=\"CHECKBOX\" NAME=\"make_readable\" CHECKED>"};
else
body = {@body, "<P><INPUT TYPE=\"CHECKBOX\" NAME=\"make_readable\">"};
endif
body = {@body, "Allow this project's designer to view and comment on your work before it is handed in.</P>"};
body = {@body, "<INPUT TYPE=\"BUTTON\" VALUE=\"<<< Back\" onClick=\"javascript:history.go(-1);\"><INPUT TYPE=\"RESET\" VALUE=\"Revert to Saved\"><INPUT TYPE=\"SUBMIT\" VALUE=\"Save and View >>>\">"};
body = {@body, "<H5 STYLE=\"color: Red\"><INPUT TYPE=\"SUBMIT\" NAME=\"hand_in_assignment\" VALUE=\"Save and Hand In Project\" onClick=\"javascript:return confirm('Are you SURE you want to do this?');\">"};
body = {@body, "<BR>WARNING: Once you hand this project in, you will not be able to edit it further!</H5>"};
body = {@body, "</FORM>", "</CENTER>", "</FONT>", "</BODY>", "</HTML>"};
return body;
endif
"Last modified Fri Apr 13 19:10:38 2001 CDT by Wizard (#2).";
.
#159:7
"Copyright (C) 2000, Jason Nolan and the University of Toronto";
"Displays the specified student's saved data, according to the project's layout. Only the student eirself can see this page until it has been handed in.";
if (!caller_perms().wizard)
return E_PERM;
elseif (args[2][2])
request = toobj(args[2][2][1]);
else
request = $nothing;
endif
"Before the project is handed in, only allow the student to see eir own work and ignore any arguments passed to the verb. After being handed in, allow arguments but default to the student.";
"Version 3: Added an exception allowing users to raise a readable flag, which allows the designer to see the user's work early.";
if (($recycler:valid(request) && (request != args[1])) && ((request in this.handed_in) || ($perm_utils:controls(args[1], this) && (request in this.readable))))
user = request;
elseif (($recycler:valid(request) && (request != args[1])) && ((!(request in this.handed_in)) || (!this.public)))
return {"Sorry, but you are not currently authorized to view this project."};
elseif (($recycler:valid(request) && (request == args[1])) && (!(request in this.students)))
return {"You have not yet started work on this project."};
else
user = args[1];
endif
body = {"<HTML>", "<HEAD>"};
body = {@body, tostr("<TITLE>", this.title, " (", $network.moo_name, ")</TITLE>")};
body = {@body, "</HEAD>"};
body = {@body, $encore_web_utils:get_page_properties(this, this)[1]};
body = {@body, ("<FONT FACE=\"" + this.web_font) + "\">"};
body = {@body, @this:header_info()};
if (this.show_participant_realname || this.show_participant_mooname)
body = {@body, ""};
if (this.(tostr(user))[1] && this.show_participant_realname)
"If we're at the end, insert the written by data";
body[$] = this.(tostr(user))[1];
if (this.(tostr(user))[2])
body[$] = tostr("<A HREF=\"mailto:", this.(tostr(user))[2], "\">", body[$], "</A>");
endif
endif
if (this.show_participant_mooname)
body[$] = tostr(body[$], " (<A HREF=\"/", tonum(user), "/\">", user.name, "</A>)");
endif
body[$] = tostr("<P ALIGN=\"CENTER\">Submitted by: ", body[$], "</P>");
endif
body = {@body, "<HR>"};
for s in [1..length(this.format)]
for i in [1..length(this.format[s])]
type = this.format[s][i];
value = this.(tostr(user))[s + 2][i];
if (value)
if (type == "title")
body = {@body, ("<H2 ALIGN=\"CENTER\">" + value) + "</H2>"};
elseif (type == "subtitle")
body = {@body, ("<H4 ALIGN=\"CENTER\"><I>" + value) + "</I></H4>"};
elseif (type == "url")
body = {@body, tostr("<B>Link:</B> <A TARGET=\"_blank\" HREF=\"", value, "\">", value, "</A>")};
elseif (type == "image")
body = {@body, tostr("<P ALIGN=\"CENTER\"><IMG SRC=\"", value, "\"></P>")};
elseif (type == "paragraph")
value = $encore_web_utils:detect_urls(user, value, 1);
value = $encore_web_utils:insert_line_breaks(value);
value[1] = "<P>" + value[1];
value[$] = value[$][1..$ - 4] + "</P>";
body = {@body, @value};
elseif (type == "inset_paragraph")
value = $encore_web_utils:detect_urls(user, value);
value = $encore_web_utils:insert_line_breaks(value);
value[1] = "<BLOCKQUOTE>" + value[1];
value[$] = value[$][1..$ - 4] + "</BLOCKQUOTE>";
body = {@body, @value};
elseif (type == "bulleted_list")
value = $encore_web_utils:detect_urls(user, value);
for item in [1..length(value)]
value[item] = ("<LI>" + value[item]) + "</LI>";
endfor
value = {"<UL>", @value, "</UL>"};
body = {@body, @value};
elseif (type == "numbered_list")
value = $encore_web_utils:detect_urls(user, value);
for item in [1..length(value)]
value[item] = ("<LI>" + value[item]) + "</LI>";
endfor
value = {"<OL>", @value, "</OL>"};
body = {@body, @value};
elseif (type == "audio")
value = ("<P ALIGN=\"CENTER\"><EMBED SRC=\"" + value) + "\" ALIGN=ABSMIDDLE WIDTH=144 HEIGHT=60 AUTOSTART=FALSE LOOP=FALSE PLAYCOUNT=0></EMBED></P>";
body = {@body, value};
endif
endif
$command_utils:suspend_if_needed(0);
endfor
body = {@body, "<HR>"};
$command_utils:suspend_if_needed(0);
endfor
if (((user == args[1]) && (caller != this)) && $list_utils:iassoc(user, this.evaluations))
body = {@body, tostr("<P ALIGN=\"CENTER\"><B><A HREF=\"/", tonum(this), "/evaluation.html?", tonum(user), "\">See the designer's evaluation of my project.</A></B></P>")};
elseif (($perm_utils:controls(args[1], this) && (caller != this)) && ((user in this.readable) || (user in this.handed_in)))
body = {@body, tostr("<P ALIGN=\"CENTER\"><B><A HREF=\"/", tonum(this), "/evaluation.html?", tonum(user), "\">I am the designer and would like to evaluate this project.</A></B></P>")};
endif
body = {@body, tostr("<P ALIGN=\"CENTER\"><B><A HREF=\"/", tonum(this), "/\">Back to ", this.title, " main menu.</A></B></P><HR>")};
body = {@body, "</FONT>", "</BODY>", "</HTML>"};
return body;
"Last modified Fri Apr 13 19:10:56 2001 CDT by Wizard (#2).";
.
#159:8
"Copyright (C) 2000, Jason Nolan and the University of Toronto";
"Gives assorted project management options to the designer, including un-handing in, deleting student data, archiving the project, and deleting the project.";
user = args[1];
if (!caller_perms().wizard)
return E_PERM;
elseif (!$perm_utils:controls(user, this))
return {"You are not authorized to manage this project."};
endif
vars = args[2][1];
vals = args[2][2];
if (vars)
this.public = 0;
this.archived = 0;
this.notifications = 0;
for v in (vars)
"    v = $string_utils:to_value(v)[2];";
if (v == "delete_project")
$recycler:_recycle(this);
break;
elseif (v[1..3] == "del")
who = toobj(v[4..$]);
if (who in this.handed_in)
this.handed_in_times = listdelete(this.handed_in_times, who in this.handed_in);
endif
this.handed_in = setremove(this.handed_in, who);
this.students = setremove(this.students, who);
this.readable = setremove(this.readable, who);
delete_property(this, tostr(who));
elseif (v[1..4] == "undo")
who = toobj(v[5..$]);
this.handed_in_times = listdelete(this.handed_in_times, who in this.handed_in);
this.handed_in = setremove(this.handed_in, who);
elseif (v == "public_project")
this.public = 1;
elseif (v == "archive_project")
this.archived = 1;
elseif (v == "notify_project")
this.notifications = 1;
endif
$command_utils:suspend_if_needed(0);
endfor
return $assignment_server:_html(user, {{}, {}});
else
body = {("<H1 ALIGN=\"CENTER\">" + $network.moo_name) + " Project Management</H1>"};
body = {@body, "<FORM METHOD=\"POST\" ENCTYPE=\"multipart/form-data\">"};
body = {@body, "<CENTER>"};
body = {@body, tostr("<INPUT TYPE=\"BUTTON\" VALUE=\"Show Available Help\" onClick=\"javascript:window.open('/", tonum(this), "/admin_manage_help.html', 'help', 'directories=no,location=no,menubar=no,personalbar=no,resizable=yes,scrollbars=yes,status=no,titlebar=yes,toolbar=no')\"><P>")};
body = {@body, "<HR>"};
if (this.students && (this.show_participant_realname || this.show_participant_mooname))
body = {@body, "<TABLE BORDER=1 CELLPADDING=5 COLS=2>"};
ready = $list_utils:remove_duplicates({@this.handed_in, @this.readable});
if (ready)
body = {@body, "<TR><TD VALIGN=\"TOP\" COLSPAN=\"2\">"};
body = {@body, "<H3 ALIGN=\"CENTER\">Ready for Evaluation</H3>"};
body = {@body, "<UL>"};
for item in (ready)
if (item in this.handed_in)
body = {@body, tostr("<LI><A HREF=\"/", tonum(this), "/evaluation.html?", tonum(item), "\">", item.name, "</A> (Handed In)</LI>")};
else
body = {@body, tostr("<LI><A HREF=\"/", tonum(this), "/evaluation.html?", tonum(item), "\">", item.name, "</A> (Marked viewable by participant)</LI>")};
endif
$command_utils:suspend_if_needed(0);
endfor
body = {@body, "</UL>"};
body = {@body, "</TD></TR>"};
endif
body = {@body, "<TR><TD VALIGN=\"TOP\">"};
body = {@body, "<H3 ALIGN=\"CENTER\">Undo Hand In</H3>"};
for item in (this.handed_in)
timestamp = ctime(this.handed_in_times[item in this.handed_in]);
timestamp = timestamp[1..10] + timestamp[20..24];
if (this.show_participant_realname && this.(tostr(item))[1])
body = {@body, tostr("<INPUT TYPE=\"CHECKBOX\" NAME=\"undo", tonum(item), "\"> <A HREF=\"view.html?", tonum(item), "\">", this.(tostr(item))[1], "</A> (handed in ", timestamp, ")<BR>")};
else
body = {@body, tostr("<INPUT TYPE=\"CHECKBOX\" NAME=\"undo", tonum(item), "\"> <A HREF=\"view.html?", tonum(item), "\">", item.name, "</A> (handed in ", timestamp, ")<BR>")};
endif
endfor
body = {@body, "</TD><TD VALIGN=\"TOP\">"};
body = {@body, "<H3 ALIGN=\"CENTER\">Delete Participant Data</H3>"};
for item in (this.students)
if (this.show_participant_realname && this.(tostr(item))[1])
body = {@body, tostr("<INPUT TYPE=\"CHECKBOX\" NAME=\"del", tonum(item), "\"> ", this.(tostr(item))[1], "<BR>")};
else
body = {@body, tostr("<INPUT TYPE=\"CHECKBOX\" NAME=\"del", tonum(item), "\"> ", item.name, "<BR>")};
endif
endfor
body = {@body, "</TR><TR><TD COLSPAN=2 ALIGN=\"CENTER\">"};
body = {@body, ("<INPUT TYPE=\"CHECKBOX\" NAME=\"notify_project\" " + (this.notifications ? "CHECKED" | "")) + "> <b>Send Notifications:</b> If checked, notifications will be e-mailed to you when a participant requests help or hands their work in, and to the participant when you evaluate their work.<p>"};
body = {@body, ("<INPUT TYPE=\"CHECKBOX\" NAME=\"public_project\" " + (this.public ? "CHECKED" | "")) + "> <b>Public:</b> If checked, anyone may view completed projects after they have been handed in.<p>"};
body = {@body, ("<INPUT TYPE=\"CHECKBOX\" NAME=\"archive_project\" " + (this.archived ? "CHECKED" | "")) + "> <b>Archived:</b> If checked, the project will be removed from the listing and may not be altered.<p>"};
body = {@body, "<INPUT TYPE=\"SUBMIT\" NAME=\"make_changes\" VALUE=\"Make These Changes\">"};
body = {@body, "</TD></TR></TABLE>"};
body = {@body, "<P></P>"};
endif
body = {@body, "<INPUT TYPE=\"SUBMIT\" NAME=\"delete_project\" VALUE=\"Delete This Project\" onClick=\"javascript:return confirm('Are you SURE you want to do this?');\">"};
body = {@body, "<H5 STYLE=\"color: Red\">WARNING: This will permanently and irrevocably delete this project and all participant data associated with it.</H5>"};
body = {@body, "<HR>"};
body = {@body, "<INPUT TYPE=\"BUTTON\" VALUE=\"<<< Back\" onClick=\"javascript:history.go(-1);\">"};
body = {@body, "</CENTER></FORM>"};
body = $encore_web_application:pre_assemble(user, body, $network.moo_name + " Project Management");
return body;
endif
"Last modified Fri Apr 13 19:10:56 2001 CDT by Wizard (#2).";
.
#159:9
"Copyright (C) 2000, Jason Nolan and the University of Toronto";
"Returns the text of the assorted system help files. Edit this verb as approproate to however you're storing your files.";
return $assignment_help.(verb);
"Last modified Fri Jun  9 15:21:36 2000 CDT by Wizard (#2).";
.
#159:10
"Copyright (C) 2000, Jason Nolan and the University of Toronto";
"This page allows designers to make comments on a participant's project, and participants to view them. It only becomes available once the participant has marked the project readable or handed in.";
if (!caller_perms().wizard)
return E_PERM;
elseif (args[2][2])
request = toobj(args[2][2][1]);
else
request = $nothing;
endif
if (($recycler:valid(request) && ((request in this.handed_in) || (request in this.readable))) && ((request == args[1]) || $perm_utils:controls(args[1], this)))
user = request;
elseif ((args[1] in this.readable) || (args[1] in this.handed_in))
user = args[1];
else
return {"You are not allowed to view this page at this time."};
endif
"Users may see their own page; the owner (designer) may see anyone's pages. Nobody can see anything until the requested user has either made their assignment viewable or handed it in.";
if (length(args[2][1]) > 1)
"A form has been submitted...";
args[2][1] = args[2][1][2..$];
args[2][2] = args[2][2][2..$];
comments = {};
"Grab the submitted values, converting as necessary...";
for c in (args[2][2])
if (typeof(c) == STR)
comments = {@comments, {c}};
else
comments = {@comments, c};
endif
endfor
i = $list_utils:iassoc(user, this.evaluations);
if (i)
this.evaluations[i][2] = comments;
else
this.evaluations = setadd(this.evaluations, {user, comments});
endif
this:notify_evaluation(user);
return {"<HTML><HEAD>", ("<META HTTP-EQUIV=\"refresh\" CONTENT=\"0;url=/" + tostr(tonum(this))) + "/\">", "</HEAD></HTML>"};
else
data = $list_utils:assoc(user, this.evaluations);
if (data)
data = data[2];
endif
"Retrieve the user's existing evaluation data, if any...";
oldhtml = this:view_html(args[1], {{}, {tostr(tonum(user))}});
html = oldhtml[1..("<HR>" in oldhtml) - 1];
html = {@html, "<FORM METHOD=\"POST\" ENCTYPE=\"multipart/form-data\">"};
html = {@html, ("<INPUT TYPE=\"HIDDEN\" NAME=\"user\" VALUE=\"" + tostr(tonum(user))) + "\">"};
html = {@html, "<CENTER>"};
if (request == args[1])
html = {@html, tostr("<INPUT TYPE=\"BUTTON\" VALUE=\"Show Available Help\" onClick=\"javascript:window.open('/", tonum(this), "/evaluation_help.html', 'help', 'directories=no,location=no,menubar=no,personalbar=no,resizable=yes,scrollbars=yes,status=no,titlebar=yes,toolbar=no')\">")};
else
html = {@html, tostr("<INPUT TYPE=\"BUTTON\" VALUE=\"Show Available Help\" onClick=\"javascript:window.open('/", tonum(this), "/evaluation2_help.html', 'help', 'directories=no,location=no,menubar=no,personalbar=no,resizable=yes,scrollbars=yes,status=no,titlebar=yes,toolbar=no')\">")};
endif
html = {@html, "</CENTER>"};
html = {@html, "<HR>"};
oldhtml = oldhtml[("<HR>" in oldhtml) + 1..$];
"Begin splicing in places for the designer to comment... note that the designer will get textareas, while the participant will get static text...";
counter = 0;
while (i = "<HR>" in oldhtml)
counter = counter + 1;
html = {@html, @oldhtml[1..("<HR>" in oldhtml) - 1]};
html = {@html, "<CENTER><TABLE BORDER=10 CELLPADDING=5 BORDERCOLOR=Red>"};
html = {@html, "<TR><TD>"};
html = {@html, "<P><B>Designer's Comments:</B></P>"};
if (user == args[1])
if (data)
data[counter] = $encore_web_utils:detect_urls(user, data[counter], 1);
data[counter] = $encore_web_utils:insert_line_breaks(data[counter]);
html = {@html, @data[counter]};
endif
elseif ($perm_utils:controls(args[1], this))
html = {@html, ("<TEXTAREA COLS=75 ROWS=5 WRAP=\"soft\" NAME=\"" + tostr(counter)) + "\">"};
if (data)
html = {@html, @data[counter]};
endif
html = {@html, "</TEXTAREA>"};
endif
html = {@html, "</TD></TR>"};
html = {@html, "</TABLE></CENTER>"};
html = {@html, "<HR>"};
oldhtml = oldhtml[("<HR>" in oldhtml) + 1..$];
if (length(oldhtml) < 5)
break;
endif
endwhile
if ($perm_utils:controls(args[1], this) && (user != args[1]))
html = {@html, "<CENTER><INPUT TYPE=\"SUBMIT\" VALUE=\"Save These Comments\"></CENTER>"};
endif
html = {@html, "</FORM>"};
html = {@html, @oldhtml};
return html;
endif
"Last modified Fri Apr 13 19:11:13 2001 CDT by Wizard (#2).";
.
#159:11
"Copyright (C) 2000, Jason Nolan and the University of Toronto";
"This is a utility verb which sends assorted email notifications when a player marks a project readable, hands it in, or the designer makes comments. It sends directly to the email addresses listed on the project itself.";
if (caller != this)
return E_PERM;
elseif (!this.notifications)
return;
endif
user = args[1];
which = verb[8..$];
if (which in {"readable", "handin"})
if (!this.designer_email)
return;
else
address = this.designer_email;
endif
"If we won't be able to send out the email, bail...";
"Otherwise remember the correct email address...";
if (this.(tostr(user))[1] && this.show_participant_realname)
name = this.(tostr(user))[1];
if (this.(tostr(user))[2])
name = ((name + " (") + this.(tostr(user))[2]) + ")";
endif
elseif (this.show_participant_mooname)
name = user.name;
else
name = "(name withheld)";
endif
if (which == "readable")
subject = "Readable Project";
message = {tostr("This is an automated message to notify you that ", name, " has marked his/her work as being readable on your project, ", this.name, ". This means that you may view what they have done so far, and make comments on it if you wish. To do so, please go to the address below.")};
elseif (which == "handin")
message = {tostr("This is an automated message to notify you that ", name, " has handed in his/her work on your project, ", this.name, ", and may not make further changes. You may, if you wish, evaluate his/her work by going to the address below.")};
subject = "Project Handin";
endif
elseif (which == "evaluation")
if (!this.(tostr(user))[2])
return;
else
address = this.(tostr(user))[2];
endif
"If we aren't going to be able to send the email, bail...";
"Otherwise remember the correct address...";
if (this.designer && this.show_designer_realname)
name = this.designer;
if (this.designer_email)
name = tostr(name, " (", this.designer_email, ")");
endif
elseif (this.show_designer_mooname)
name = this.owner.name;
else
name = "(name withheld)";
endif
message = {tostr("This is an automated message to notify you that your work on ", this.name, " has been commented/evaluated by its designer, ", name, ". To view what he/she said, please go to the address below.")};
subject = "Project Evaluated";
endif
message = {@message, ""};
message = {@message, tostr("http://", $network.site, ":", $network.webport, "/", tonum(this), "/evaluation.html?", tonum(user))};
message = {@message, tostr("(Note that you must log in to the ", $network.MOO_name, " system FIRST by going to: http://", $network.site, ":", $network.webport, "/ )")};
message = {@message, ""};
message = {@message, "If you have any further questions, please consult one of the wizard staff directly. Thank you."};
player = user;
return $network:sendmail(address, subject, @message);
"Last modified Fri Apr 13 19:11:30 2001 CDT by Wizard (#2).";
.
#162:0
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Reset object to default values.";
"===========================================================";
if (caller_perms().wizard)
pass();
this.web_bgcolor = "SlateGray";
this.web_text_color = "Black";
this.web_link_color = "Black";
this.web_vlink_color = "Black";
this.web_alink_color = "Silver";
this.welcome_screen = {"<HTML>", "<HEAD>", "  <TITLE>Welcome to enCore</TITLE>", "  <STYLE TYPE=\"text/css\" MEDIA=\"screen\">", "    <!--", "    BODY, P { color: Black; font-family: Sans-Serif; font-size: 14px; background-color: White;  }", "    H1 { font-size: 200%; }", "    A { font-family: Sans-Serif; font-size: 14px; }", "    A:link { color: Blue }", "    A:visited { color: Purple }", "    A:hover { color: Red }", "    -->", "  </STYLE>", "</HEAD>", "<BODY>", tostr("<H1>Welcome to enCore version ", $core_version, "</H1>"), "<P>For more information on how to use, and get the most out of your new educational MOO, take a look at our edited collection of essays <A HREF=\"http://www.press.umich.edu/titles/09665.html\" TARGET=_BLANK><I>High Wired: On the Design, Use, and Theory of Educational MOOs</I></A> available from University of Michigan Press, and our student text book, <A HREF=\"http://vig.abacon.com/catalog/abbooks/0,2371,0205271146,00.html\" TARGET=_BLANK><I>MOOniversity: A Guide to Online Learning Environments</I></A>, from Allyn And Bacon.</P><P>Don't forget to subscribe to the enCore administrator's email list. Please go to <A HREF=\"http://listar.utdallas.edu/\" TARGET=\"_BLANK\">http://listar.utdallas.edu</A> to subscribe.</P><P>Below are a few links to enCore-related web sites.</P><UL><LI><A HREF=\"http://lingua.utdallas.edu/encore/gpl.html\" TARGET=_BLANK>Read the GNU General Public License</A></LI><LI><A HREF=\"http://lingua.utdallas.edu/encore\" TARGET=_BLANK>Visit the <I>High Wired</I> enCore web site</A></LI><LI><A HREF=\"http://lingua.utdallas.edu:7000/\" TARGET=_BLANK>Visit Lingua MOO (The Mothership)</A></LI></UL><P>Good luck with your new educational MOO</P><P><A HREF=\"http://wwwpub.utdallas.edu/~cynthiah/\" TARGET=_BLANK>Cynthia Haynes</A> and <A HREF=\"http://lingua.utdallas.edu/jan/\" TARGET=_BLANK>Jan Rune Holmevik</A></P><P>Coordinators enCore Open Source MOO Project.</P>", "</BODY>", "</HTML>"};
endif
"Last modified Fri Jul 20 09:05:12 2001 CDT by Wizard (#2).";
.
#162:1
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Generates the main enCore Xpress Login Screen";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
user = args[1];
base_url = tostr("http://", $network.site, ":", $network.webport, "/Xpress_Login/");
login = tostr(base_url, "login");
welcome = tostr(base_url, "welcome_screen");
html = {tostr("<FRAMESET  COLS=\"100%\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("<NOFRAMES><BODY>")};
html = {@html, "<H3>Sorry you need a browser that supports frames to use this system</H3>"};
html = {@html, "</BODY></NOFRAMES>"};
html = {@html, tostr("<FRAMESET COLS=\"50%\" FRAMEBORDER=\"", this.frameborder, "\" NAME=main>")};
html = {@html, tostr("      <FRAMESET COLS=\"200,100%\" FRAMEBORDER=\"", this.frameborder, "\" SCROLLING=\"auto\">")};
html = {@html, tostr("         <FRAME SRC=\"", login, "\" NAME=\"login\" NORESIZE MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, tostr("         <FRAME SRC=\"", welcome, "\" NAME=\"welcome\" MARGINWIDTH=\"", this.frame_marginwidth, "\" MARGINHEIGHT=\"", this.frame_marginheight, "\" FRAMEBORDER=\"", this.frameborder, "\">")};
html = {@html, "      </FRAMESET>"};
html = {@html, "   </FRAMESET>"};
html = {@html, "</FRAMESET>"};
html = this:pre_assemble(user, html, "Login", "", "", "", "", this.doctype_frameset);
return html;
"Last modified Fri Apr 13 18:50:51 2001 CDT by Wizard (#2).";
.
#162:2
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Generates a a customized welcome page for this MOO.";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
html = {};
if (this.welcome_screen)
for line in (this.welcome_screen)
suspend(0);
html = {@html, line};
endfor
endif
return html;
"Last modified Fri Apr 13 18:51:12 2001 CDT by Wizard (#2).";
.
#162:3
"===========================================================";
"Copyright (C) 1999-2001, Jan Rune Holmevik";
"Generates a login page for connecting to the MOO from the web";
"IMPORTANT NOTE: You must not modify this verb in such a way that the copyright notice and the link to the license is moved or removed from the web display. If you want to add your own copyright notice, you should insert this below current copyright notices. Removing copyright notices consititutes a violation of the license under which this software is provided to you.";
"===========================================================";
if (caller != $httpd)
return E_PERM;
endif
{user, action} = args;
body = login = {};
base_url = tostr("http://", $network.site, ":", $network.webport, "/");
if (typeof(action) == LIST)
action = "";
endif
if (match(action, "Permission Denied"))
"Return a blank page";
body = {};
else
action = "document.useridForm.useridField.focus();document.useridForm.useridField.select();" + action;
if (!children($guest))
default_login_name = "";
else
default_login_name = "Guest";
endif
checkClient = this.checkClient;
submit_hack = $encore_web_utils:insert_javascript(this.submit_hack);
"---------------------------------------------------------";
"Set up the main Xpress Client Screen based on users' preferences";
"---------------------------------------------------------";
screen_size = $string_utils:explode(user.Xpress_screen_size, ",");
width = screen_size[1];
height = screen_size[2];
client_window = $encore_web_utils:javascript_window_open($xpress_client, "openXpress", tostr(base_url, "Xpress_client/", user.Xpress_layout, "/"), width, height);
mootcan_standalone_url = tostr(base_url, "mootcan/standalone.html");
mootcan_script = $encore_web_utils:javascript_window_open($mootcan, "openMOOtcan", mootcan_standalone_url);
browse = $encore_web_utils:javascript_window_open($Xpress_login, "openBrowseMode", tostr(base_url, toint($player_start)));
request = $encore_web_utils:javascript_window_open($Xpress_login, "openRequestForm", tostr(base_url, "Xpress_Login/character_application_html"));
script = $list_utils:append(client_window, mootcan_script, browse, request);
script = $encore_web_utils:insert_JavaScript(script);
login = {@login, tostr("<H3>enCore Xpress ", $core_version, "</H3><P>Copyright &copy; 1997-2001 enCore Open Source Project, All Rights Reserved</P><P>This program is free software and comes WITHOUT ANY WARRANTY! <A HREF=\"", $core_homepage, "gpl.html\" TARGET=_BLANK>See license</A> for details.</P>")};
useridForm = {tostr("<FORM NAME=\"useridForm\" onSubmit='return submit1()'><TABLE BORDER=0><TR><TD><P ALIGN=right>User ID</TD><TD><INPUT CLASS=\"login\" TYPE=text NAME=\"useridField\" VALUE=\"", default_login_name, "\" SIZE=10></TD></TR></FORM>")};
passwordForm = {tostr("<FORM NAME=\"passwordForm\" ACTION=\"", base_url, "\" METHOD=POST ENCTYPE=multipart/form-data onSubmit='return (submit2() && checkClient())'><TR><TD><P><P ALIGN=right>Password</TD><TD><INPUT  NAME=\"useridField\" TYPE=\"HIDDEN\"><INPUT CLASS=\"login\" TYPE=password NAME=\"passwordField\" VALUE=\"\" SIZE=10></TD></TR></TABLE><P><INPUT TYPE=hidden NAME=OS VALUE=\"\"><INPUT TYPE=submit NAME=xpress VALUE=\"Log in\"></FORM><P>")};
login = $list_utils:append(login, useridForm, passwordForm);
login = {@login, tostr("<P>[<A HREF=\"", $core_homepage, "xpress_system_requirements.html\" TARGET =\"_BLANK\">System Requirements</A>]</P><HR>")};
scripted_open_mootcan = {"document.write('<FORM><INPUT TYPE=button NAME=mootcan VALUE=\"Open MOOtcan\" onClick=\"return openMOOtcan()\"></FORM>')"};
noscript_open_mootcan = {tostr("<FORM ACTION=\"", mootcan_standalone_url, "\" TARGET=\"java_frame\"><INPUT TYPE=submit NAME=mootcan VALUE=\"Open MOOtcan\"></FORM>")};
login = $list_utils:append(login, $encore_web_utils:insert_javascript(scripted_open_mootcan, noscript_open_mootcan));
if ($xpress_client.http_09_enabled)
login = {@login, "<P><FORM> <INPUT TYPE=button NAME=browse VALUE=\"Browse the MOO\" onClick=\"return openBrowseMode()\"></FORM></P>"};
endif
if ($login.create_enabled)
login = {@login, "<P><FORM> <INPUT TYPE=button NAME=request VALUE=\"Create Account\" onClick=\"return openRequestForm()\"></FORM></P>"};
endif
login = $list_utils:append(login, {"</TD></TD><TD VALIGN=top WIDTH=\"80%\">"});
end_html = {"</TD></TR>"};
body = $list_utils:append(submit_hack, checkClient, script, login, end_html);
body = $encore_web_utils:center(body);
endif
result = this:build(user, body, "Login", action);
return result;
"Last modified Mon Sep 17 10:07:46 2001 CDT by Wizard (#2).";
.
#162:4
"===========================================================";
"Copyright (C) 1999-2001 Jan Rune Holmevik";
"MOO character application form";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
form = script = reason = {};
javascript = mail_alert = real_name = name = email_address = "";
finished = 0;
title = $encore_web_utils:get_title(user, this, tostr($network.MOO_name, " Character Application Form"));
if ($login.create_enabled)
if ({{}, {}} != args[2])
"User is sending a POST request";
application = args[2];
real_name = application[2][1];
name = application[2][2];
email_address = application[2][3];
title = $encore_web_utils:get_title(user, this, tostr($network.MOO_name, " Character Application Form"));
if ((!$player_db:available(name)) && (name != this.name))
"Cannot create new user. Return application form with submitted data intact.";
javascript = tostr("alert('Sorry, the name ", name, " is already being used, or else it is not acceptable. Please choose another name.')");
elseif ($registration_db:find_exact(email_address))
javascript = tostr("alert('It appears that you already have a character in ", $network.moo_name, ". Please talk to an administrator if you need another one.')");
else
"----------------------------------------------";
"Create new character";
"----------------------------------------------";
{new_player, password} = $wiz_utils:make_player(name, email_address, real_name);
$mail_agent:send_message(this.owner, $new_player_log, tostr(name, " (", new_player, ")"), {email_address, tostr("Account ", new_player.name, " automatically created")});
if ($network.active)
$wiz_utils:send_new_player_mail(tostr("Someone requested a character on ", $network.moo_name, " for email address ", email_address, "."), name, email_address, new_player.name, password);
mail_alert = tostr("Your password has been mailed to your email address: ", email_address, ".");
endif
javascript = tostr("alert('New account named ", new_player.name, " created. Your password is: ", password, ". The password is case sensitive. Please keep it confidential.", mail_alert, "');self.close()");
finished = 1;
endif
else
"User is sending a GET request";
javascript = "document.application.real_name.focus()";
endif
if (!finished)
validateScript = this.validate_script;
script = $encore_web_utils:insert_JavaScript(validateScript);
form = {tostr("<P><INPUT TYPE=text NAME=\"real_name\" VALUE=\"", real_name, "\" SIZE=30>Your Full Name", "<P><INPUT TYPE=text NAME=\"name\" VALUE=\"", name, "\" SIZE=30>Desired Character Name", "<P><INPUT TYPE=text NAME=\"email_address\" VALUE=\"", email_address, "\" SIZE=30>Your Email address")};
for line in (reason)
form = {@form, line};
endfor
form = {@form, tostr("</TEXTAREA>", "<P><INPUT TYPE=submit NAME=Submit VALUE=\"Submit Character Application\">")};
form = $encore_web_utils:append_close_button(form);
form = $encore_web_utils:form(form, "application", "/xpress_login/character_application.html", "return validateForm()");
endif
body = $list_utils:append(script, title, form);
result = this:build(user, body, "Character Application", javascript);
else
result = "403_Forbidden";
endif
return result;
"Last modified Mon Sep 17 10:08:04 2001 CDT by Wizard (#2).";
.
#163:0
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"View a player's inventory via Xpress.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, data} = args;
try
index = toint(data[2][2]);
except error (ANY)
index = 1;
endtry
body = this:make_inventory(user, index);
html = this:build(user, body, this.name);
return html;
"Last modified Fri Apr 13 19:08:02 2001 CDT by Wizard (#2).";
.
#163:1
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"View a player's inventory via Xpress.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, ?index = 1} = args;
body = object_menu = {};
try
url = tostr("http://", $network.site, ":", $network.webport, "/");
target = "web_frame";
use_exit_name = 1;
call_spec = tostr(toint(this), "&make_inventory");
title = $encore_web_utils:get_title(user, this, tostr("Inventory for ", user.name));
{help_button, help_function} = $encore_web_utils:insert_context_sensitive_help(user, $encore_help, "Inventory_Manager");
help_button = {"<FORM>", @help_button, tostr(" <INPUT TYPE=\"BUTTON\" VALUE=\"Refresh inventory\" onClick=\"location.href='", url, "Inventory_Manager/view_inventory?", toint(user), "';\"><INPUT TYPE=\"BUTTON\" VALUE=\"Close inventory\" onClick=\"parent.close();\"></FORM>")};
edit_window = $encore_web_utils:javascript_window_open($xpress_object_editor, "openXpressEdit", "");
confirmDelete = {"function confirmDelete() {", tostr("   if (confirm('Are you sure you want to permanently recycle this object? This action cannot be undone!')) {"), "       return true;", "   }", "   return false;", "}"};
functions = $list_utils:append(confirmDelete, help_function, edit_window);
functions = $encore_web_utils:insert_javascript(functions);
stuff = {};
if (user.owned_objects != {})
"-----------------------------------------";
"Compile list of owned objects";
"-----------------------------------------";
if (length(user.owned_objects) > this.max_view)
object_menu = this:make_object_menu(user, index);
endif
links = locations = actions = {};
if ((index + this.max_view) > length(user.owned_objects))
last = length(user.owned_objects);
else
last = index + this.max_view;
endif
for item in ($list_utils:reverse(user.owned_objects)[index..last])
$command_utils:suspend_if_needed(0);
action = "";
links = {@links, tostr($encore_web_utils:generate_links(user, {item}, target, use_exit_name)[1], " (", item, ")")};
locations = {@locations, (item.location == $nowhere) ? "Nowhere" | tostr(item.location.name, " (", item.location, ")")};
"-----------------------------------------";
"Add contextual menu for object if relevant";
"-----------------------------------------";
if ($object_utils:has_callable_verb(item, "make_contextual_menu"))
contextual_menu = item:make_contextual_menu(user, item, call_spec, return_functions = 0);
for button in (contextual_menu)
action = action + button;
endfor
endif
actions = {@actions, action};
endfor
links = {"<B>Object</B>", @links};
locations = {"<B>Location</B>", @locations};
actions = {"<B>Actions</B>", @actions};
stuff = $encore_web_utils:generate_table(user, {links, locations, actions}, this, 1);
endif
"-----------------------------------------";
"Add quota information";
"-----------------------------------------";
quota_info = this:display_quota(user);
body = $list_utils:append(functions, title, help_button, quota_info, object_menu, stuff);
except error (ANY)
endtry
return body;
"Last modified Fri Apr 13 19:08:35 2001 CDT by Wizard (#2).";
.
#163:2
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Visualize a user's quota usage.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
"-----------------------------------------";
"Measure the user's quota usage";
"-----------------------------------------";
$quota_utils:summarize_one_user(user);
{quota, usage, timestamp, unmeasured} = user.size_quota;
url = $xpress_client.external_baseurl + $xpress_client.system_images_folder;
used = (usage * 100) / quota;
free = quota - usage;
if (free < 0)
free = 0;
endif
"------------------------------------------------";
"Determine what quota chart to show";
"------------------------------------------------";
if (used < 13)
chart = "quota_0.gif";
elseif ((used >= 13) && (used < 25))
chart = "quota_12_5.gif";
elseif ((used >= 25) && (used < 38))
chart = "quota_25.gif";
elseif ((used >= 38) && (used < 50))
chart = "quota_37_5.gif";
elseif ((used >= 50) && (used < 63))
chart = "quota_50.gif";
elseif ((used >= 63) && (used < 75))
chart = "quota_62_5.gif";
elseif ((used >= 75) && (used < 88))
chart = "quota_75.gif";
elseif ((used >= 88) && (used < 100))
chart = "quota_87_5.gif";
else
chart = "quota_100.gif";
endif
quota_chart = {tostr("<P>Quota usage: <IMG SRC=\"", url, chart, "\"> <IMG SRC=\"", url, "quota_free.gif", "\"> ", $string_utils:group_number(free), " bytes free. <IMG SRC=\"", url, "quota_used.gif", "\"> ", $string_utils:group_number(usage), " bytes used. Total objects: ", length(user.owned_objects), ".<P>")};
return quota_chart;
"Last modified Fri Apr 13 19:08:35 2001 CDT by Wizard (#2).";
.
#163:3
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Break down long object listings into chuncks in order to reduce lag and load times.";
"===========================================================";
if (!caller_perms().wizard)
return E_PERM;
endif
{user, index} = args;
finished = 0;
first = menuindex = 1;
quick_access = {};
total_objects = length(user.owned_objects);
base_url = tostr("http://", $network.site, ":", $network.webport, "/Inventory_Manager/view_inventory?", toint(user), "&");
if (index > 1)
"Add back button";
if ((index - this.max_view) < 1)
newindex = 1;
else
newindex = index - this.max_view;
endif
quick_access = {@quick_access, tostr("<A HREF=\"", base_url, newindex, "\">[<< prev]</A>")};
endif
while (!finished)
if (first in {index, index + 1})
"We are at current index position. Do not create link";
quick_access = {@quick_access, tostr("<B>", menuindex, "</B>")};
else
quick_access = {@quick_access, tostr("<A HREF=\"", base_url, first, "\">", menuindex, "</A>")};
endif
first = first + this.max_view;
menuindex = menuindex + 1;
if (first > total_objects)
finished = 1;
endif
endwhile
if ((index + this.max_view) <= total_objects)
"Add forward button";
quick_access = {@quick_access, tostr("<A HREF=\"", base_url, index + this.max_view, "\">[next >>]</A>")};
endif
return {"<P><CENTER>", @quick_access, "</CENTER></P>"};
"Last modified Fri Apr 13 19:08:35 2001 CDT by Wizard (#2).";
.
#163:4
"===========================================================";
"Copyright (C) 2001, Jan Rune Holmevik";
"Reset object to default values.";
"===========================================================";
if (caller_perms().wizard)
pass();
this.max_view = 8;
this.table_border = "1";
endif
"Last modified Fri Apr 13 19:08:35 2001 CDT by Wizard (#2).";
.
0 clocks
0 queued tasks
0 suspended tasks
1 active connections with listeners
2 0
