** LambdaMOO Database, Format Version 4 **
309
3125
0
10
2
71
36
38
100
101
113
115
125
139
#0
The System Object

24
2
-1
-1
-1
1
-1
45
13
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
165
-1
user_disconnected user_client_disconnected
2
165
-1
checkpoint_started
2
165
-1
checkpoint_finished
2
165
-1
handle_uncaught_error
2
173
-1
handle_task_timeout
2
173
-1
ignore_timeout
2
173
-1
do_command
2
173
-1
bf_toliteral toliteral
2
173
-1
243
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
free_list
max_seconds
max_ticks
hacker
generic_db
shutdown_message
shutdown_time
no_one
player_db
player_class
gender_utils
trig_utils
time_utils
editor_help
mail_recipient
mail_agent
mail_editor
note_editor
verb_editor
generic_editor
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
list_options
error
newt_log
toad_log
site_log
housekeeper
network
generic_biglist_home
feature
local
character
tangible
detailed
gendered
puppet
zone
utility
exit_utils
heart
robot
nomad
furniture
rpg
skilled
combatant
weapon
everyone
wieldable
skill
they
someone
quota_utils
clothing
door
quota
banker
english
ahab
reactor
value_editor
food
drink
consumable
sys
body_area
trust_db
root
collective
core_utils
geomancer
char
integrated
gamemaster
build_options
prog_options
emu
server_options
ranged
scrooge
gm
armour
pc
npc
alt_player_db
combat_options
debug
gender
packrat
event_object
event
privacy_options
hidden_loc
hidden_home
rpg_exit
rpg_room
restricted_player
byte_quota_utils
dump_started
dump_finished
output_basket
vr_core
lock_interface
lockable
lock_dummy
executor
ammo
terrain_room
terrain_zone
traceback_log
log_errors
word_game
word_game_options
public
table
skill_db
type
justice
nobody
ignore_timeout
default_gender
fists
pose_feature
gibber_feature
crystal
corpse
combat_reactor
attribute
humanoid_body_area
nowhere
pc_heart
npc_heart
combat_message_set
shotgun
chainsaw
sledge_hammer
shotgun_shells
reaping_feature
heaven
hell
martial_law
replicator
portable_room
default_zone
dictionary
bioscanner
reroll_log
afterlife
builder_feature
bulletin_board
located_room
campground
natural_weapon
reg_db
taskmaster
commands_processed
builder_help
communicator
comm_channel_server
comm_channel
species_feature
shared_player
species
effect
scheduler
core_history
character_editor
uncoded
channel
channel_feature
medkit
gps
pec
computer
computer_device
transputty
notion
effigy
species_human
crosswarp
noweapon
liquid
blood
252
1
4
2
5
1
10
2
1
1
11
2
1
1
12
2
1
0
955494926
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
32
2
5
1
33
2
5
1
35
36
1
4
0
2
5
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
1
6
2
5
1
40
2
1
1
26
2
5
1
41
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
4
1
1
2
2
5
1
53
2
5
0
3600
2
5
1
54
2
5
1
55
2
5
1
6
2
5
1
56
2
5
1
57
2
5
1
58
2
5
1
59
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
60
2
5
1
9
2
5
1
8
2
5
1
5
2
5
1
7
2
5
1
3
2
5
1
61
2
5
1
1
2
5
1
62
2
5
1
63
2
5
1
64
2
5
1
65
2
5
1
75
2
5
1
67
2
5
0
2147483647
2
5
0
-2147483648
2
5
1
68
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
78
2
1
1
79
2
1
1
80
2
1
1
76
2
5
1
81
2
1
1
82
2
1
1
83
2
1
1
84
2
1
1
86
2
1
1
87
2
1
1
88
2
1
1
89
2
5
1
90
2
1
1
91
2
1
1
92
2
1
1
93
2
1
1
38
2
1
1
94
2
1
1
95
2
1
1
96
2
1
1
97
2
1
1
34
2
1
1
98
2
1
1
99
2
1
1
100
2
1
1
101
2
1
1
102
2
1
1
103
2
1
1
104
2
1
1
105
2
1
1
106
2
1
1
107
2
1
1
108
2
1
1
0
2
1
1
109
2
1
1
110
2
1
1
1
2
1
1
111
2
1
1
112
2
5
1
113
2
1
1
78
2
1
1
114
2
1
1
115
2
1
1
116
2
1
1
66
2
1
1
117
2
1
1
118
2
5
1
119
2
5
1
101
2
1
1
115
2
1
1
120
2
1
1
6
2
1
1
103
2
1
1
121
2
1
1
122
36
1
1
123
36
1
1
124
36
1
1
125
2
1
1
77
2
1
1
126
2
1
1
127
2
1
1
128
2
1
1
130
2
1
1
131
2
1
1
132
2
1
1
133
2
1
1
34
2
1
0
955490189
36
1
0
955475815
36
1
1
134
2
1
1
135
2
1
1
136
2
1
1
137
2
1
1
138
2
1
1
139
2
1
1
140
2
1
1
141
2
1
1
142
2
1
1
143
2
1
0
1
2
1
1
144
2
1
1
145
2
1
1
-1
2
1
1
146
2
1
1
147
2
1
1
148
2
1
1
149
2
1
1
38
36
1
0
1217539547
2
1
1
150
36
1
1
151
115
1
1
152
36
1
1
153
36
1
1
154
115
1
1
155
115
1
1
156
115
1
1
157
115
1
1
158
36
1
1
-1
36
1
1
159
2
1
1
160
2
1
1
161
115
1
1
162
2
1
1
163
2
1
1
164
2
1
1
165
2
1
1
166
2
1
1
167
2
1
1
168
2
1
0
0
2
1
1
169
2
1
1
170
2
1
1
171
2
1
1
172
2
1
1
173
2
1
1
174
2
1
1
175
2
1
1
129
2
1
1
176
2
1
1
177
2
1
1
178
2
1
1
179
2
1
1
16
2
1
1
180
2
1
0
73
36
1
1
181
2
1
1
182
2
1
1
183
2
1
1
184
2
1
1
185
2
1
1
186
2
1
1
187
2
1
1
188
2
1
1
189
2
1
4
2
4
3
2
Ghostwheel
2
1.8.0r5
0
955495067
4
3
2
LambdaMOO
2
1.7.1
0
744357778
2
1
1
190
2
1
1
191
2
1
1
192
2
1
1
193
2
1
1
194
2
1
1
195
2
1
1
196
2
1
1
197
2
1
1
198
2
1
1
199
2
1
1
200
2
1
1
201
2
1
1
202
2
1
1
203
2
1
1
204
2
1
1
205
2
1
1
206
2
1
0
0
2
4
4
3
2
The
2
Known
2
Universe
2
5
2
The known universe.
2
5
4
2
0
16916
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#1
Root Class

152
2
-1
-1
-1
-1
0
-1
70
initialize
2
165
-1
recycle
2
165
-1
set_name
2
165
-1
title
2
165
-1
titlec
2
165
-1
set_aliases
2
165
-1
match match_contents
2
173
-1
match_object match_env*ironment my_match_object
2
173
-1
set_description
2
173
-1
description
2
173
-1
look_self
2
173
-1
exam*ine
2
37
-1
notify
2
165
-1
tell
2
173
-1
tell_lines
2
173
-1
accept
2
173
-1
moveto
2
173
-1
eject eject_nice eject_basic
2
165
-1
is_unlocked_for
2
173
-1
huh
2
173
-1
set_message
2
165
-1
do_examine
2
165
-1
examine_key
2
173
-1
examine_names
2
173
-1
examine_desc
2
173
-1
examine_contents
2
173
-1
examine_verbs
2
165
-1
get_message
2
165
-1
room_announce*_all_but
2
165
-1
init_for_core
2
173
-1
contents
36
173
-1
examine_verb_ok
2
165
-1
is_listening
2
173
-1
hidden_verbs
2
165
-1
examine_owner
2
173
-1
announce*_all_but
2
173
-1
tell_lines_suspended
2
173
-1
is_readable_by
2
173
-1
is_writable_by
2
173
-1
is_controllable_by
2
173
-1
acceptable
2
173
-1
announce_message*s
2
165
-1
matches
2
165
-1
room zone
2
173
-1
name
2
173
-1
namec
2
165
-1
definite
2
173
-1
use_article
2
173
-1
id
2
173
-1
dname*c iname*c dtitle*c ititle*c
2
165
-1
tell_description
2
173
-1
is_local_to
2
165
-1
hear_event_*
2
165
-1
is_enclosing
2
173
-1
match_pool env*ironment
2
173
-1
matching_contents
2
173
-1
announce_to
2
165
-1
attached_contents
2
173
-1
process_notified_text
2
165
-1
is_bonded_to
2
173
-1
is_unique
2
173
-1
spawned_name
36
173
-1
spawned_aliases
36
173
-1
sub_name*c
36
173
-1
v_size
36
165
-1
has_message
2
165
-1
is_valid_name
36
173
-1
grammar_sub g_sub
36
173
-1
set_ic
115
173
-1
notify_b*inary
2
173
-1
9
key
aliases
description
object_size
namec
definite
use_article
help_msg
ic
9
0
0
2
4
4
0
2
5
2

2
5
4
2
0
40830
0
955495071
100
1
2

2
5
0
0
2
5
0
1
2
5
2

2
5
0
0
115
1
#2
God

7
2
61
-1
-1
56
-1
-1
0
0
193
2

2
5
2
%n @newts %d (%[#d])
2
5
1
-1
2
5
2

2
5
2
The reality surrounding you collects into logical arrangements, washing your mind with a new enlightenment.  Your perception of all things real has forever been altered.  [You're a programmer.]
2
5
2
Have a nice life...
2
5
2
%n @toads %d (%[#d])
2
5
1
-1
2
4
4
0
36
1
0
1
2
5
0
0
2
5
2
me = player; here = player.location;
2
5
0
48
2
5
0
0
2
1
4
20
4
4
2
#37:clearall (this == #25), line 16: Type mismatch
0
955495070
2
25081
4
3
4
6
1
2
2
notify_lines
1
2
1
6
1
2
0
4
4
6
1
112
2
mcd_initialize_core
1
2
1
112
1
2
0
19
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
53
4
4
2
... called from #25:load (this == #25), line 10
0
955495070
2
25081
4
3
4
6
1
2
2
notify_lines
1
2
1
6
1
2
0
4
4
6
1
112
2
mcd_initialize_core
1
2
1
112
1
2
0
19
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
53
4
4
2
... called from #25:init_for_core (this == #25), line 3
0
955495070
2
25081
4
3
4
6
1
2
2
notify_lines
1
2
1
6
1
2
0
4
4
6
1
112
2
mcd_initialize_core
1
2
1
112
1
2
0
19
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
53
4
4
2
... called from #112:mcd_initialize_core (this == #112), line 17
0
955495070
2
25081
4
3
4
6
1
2
2
notify_lines
1
2
1
6
1
2
0
4
4
6
1
112
2
mcd_initialize_core
1
2
1
112
1
2
0
19
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
53
4
4
2
(End of traceback)
0
955495070
2
25081
4
3
4
6
1
2
2
notify_lines
1
2
1
6
1
2
0
4
4
6
1
112
2
mcd_initialize_core
1
2
1
112
1
2
0
19
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
53
4
4
2
*** Generic Garbage Object <#63> has no :init_for_core verb. ***
0
955495070
2
25081
4
2
4
6
1
112
2
mcd_initialize_core
1
2
1
112
1
2
0
22
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
53
4
4
2
#51:isa (this == #51), line 5: Type mismatch
0
955495071
2
25081
4
3
4
6
1
2
2
notify_lines
1
2
1
6
1
2
0
4
4
6
1
112
2
mcd_initialize_core
1
2
1
112
1
2
0
19
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
53
4
4
2
... called from #190:kill_session (this == #190), line 6
0
955495071
2
25081
4
3
4
6
1
2
2
notify_lines
1
2
1
6
1
2
0
4
4
6
1
112
2
mcd_initialize_core
1
2
1
112
1
2
0
19
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
53
4
4
2
... called from #190:kill_all_sessions (this == #190), line 12
0
955495071
2
25081
4
3
4
6
1
2
2
notify_lines
1
2
1
6
1
2
0
4
4
6
1
112
2
mcd_initialize_core
1
2
1
112
1
2
0
19
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
53
4
4
2
... called from #49:init_for_core (this == #190), line 4
0
955495071
2
25081
4
3
4
6
1
2
2
notify_lines
1
2
1
6
1
2
0
4
4
6
1
112
2
mcd_initialize_core
1
2
1
112
1
2
0
19
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
53
4
4
2
... called from #190:init_for_core (this == #190), line 4
0
955495071
2
25081
4
3
4
6
1
2
2
notify_lines
1
2
1
6
1
2
0
4
4
6
1
112
2
mcd_initialize_core
1
2
1
112
1
2
0
19
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
53
4
4
2
... called from #112:mcd_initialize_core (this == #112), line 17
0
955495071
2
25081
4
3
4
6
1
2
2
notify_lines
1
2
1
6
1
2
0
4
4
6
1
112
2
mcd_initialize_core
1
2
1
112
1
2
0
19
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
53
4
4
2
(End of traceback)
0
955495071
2
25081
4
3
4
6
1
2
2
notify_lines
1
2
1
6
1
2
0
4
4
6
1
112
2
mcd_initialize_core
1
2
1
112
1
2
0
19
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
53
4
4
2
Cleaning up extraction residue ...
0
955495071
2
25081
4
1
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
55
4
4
2
Beginning initialize_owned:  Tue Apr 11 19:17:51 2000 EDT
0
955495071
2
25081
4
3
4
6
1
24
2
initialize_owned
1
2
1
24
1
2
0
4
4
6
1
112
2
mcd_finalize
1
2
1
112
1
2
0
11
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
57
4
4
2
Done adding, beginning verification pass.
0
955495071
2
25081
4
3
4
6
1
24
2
initialize_owned
1
2
1
24
1
2
0
14
4
6
1
112
2
mcd_finalize
1
2
1
112
1
2
0
11
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
57
4
4
2
Finished:  Tue Apr 11 19:17:51 2000 EDT
0
955495071
2
25081
4
3
4
6
1
24
2
initialize_owned
1
2
1
24
1
2
0
16
4
6
1
112
2
mcd_finalize
1
2
1
112
1
2
0
11
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
57
4
4
2
Core database extraction is complete.  Type @shutdown to save it.
0
955495071
2
25081
4
1
4
6
1
2
2
make-core-database
1
2
1
58
1
2
0
58
4
4
2
=> 0
0
955495220
2
shutdown()
4
1
4
6
1
2
2
eval
1
2
1
57
1
2
0
18
4
4
2
[used 4 ticks, 0 seconds.]
0
955495220
2
shutdown()
4
1
4
6
1
2
2
eval
1
2
1
57
1
2
0
20
2
0
0
1
2
5
4
0
36
0
5
36
1
5
2
0
4
0
2
5
5
2
0
0
0
2
1
0
0
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
0
0
2
5
4
114
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
42
1
51
1
52
1
53
1
55
1
56
1
57
1
60
1
61
1
69
1
72
1
75
1
76
1
78
1
79
1
80
1
81
1
82
1
83
1
84
1
85
1
86
1
89
1
90
1
96
1
97
1
102
1
103
1
104
1
106
1
107
1
108
1
110
1
112
1
114
1
116
1
117
1
118
1
121
1
123
1
127
1
129
1
131
1
132
1
133
1
143
1
154
1
159
1
160
1
161
1
162
1
164
1
165
1
166
1
168
1
169
1
170
1
174
1
177
1
178
1
179
1
180
1
183
1
185
1
186
1
187
1
196
1
197
1
198
1
202
1
203
1
211
1
212
1
215
1
216
1
217
1
218
1
219
1
220
1
221
1
222
1
223
1
224
1
225
1
226
1
227
1
265
1
267
1
268
1
269
1
270
1
271
1
272
1
295
1
296
1
303
2
1
0
1
2
4
4
0
2
0
5
2
0
4
0
2
4
0
10
2
4
2
%N %<is> not currently logged in.  However, your page has been saved for %o.  Use `@unpage %N` to delete it.
2
5
2
*** You sense %n %<is> trying to send you an OOC message.
2
5
2
%N %<has> received your page: "$text"
2
5
4
0
2
5
4
0
2
1
4
2
0
0
0
0
2
4
4
0
2
4
0
0
2
1
0
117
100
0
0
0
2
0
4
0
2
5
0
2147483647
2
1
4
0
115
1
2

2
5
1
159
2
5
4
4
0
10000000
0
1447995
0
955412150
0
0
100
1
2
eval shutdown()
2
0
0
0
2
5
0
0
2
1
1
-2
36
1
4
0
2
5
4
0
115
1
4
0
36
0
2
%N %<is> not accepting pages at this time.
2
5
4
0
115
1
0
0
115
1
0
0
36
0
0
50
115
1
4
2
1
2
0
945478019
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
4
0
115
1
5
115
1
5
115
1
5
115
1
5
115
1
2

2
5
2

2
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
2
%[Dppc] natural armour absorbs all the damage.
2
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
2
%N %<wakes> from %p voluntary sleep, rubbing %p eyes and yawning with well-rested satisfaction.
2
5
2
You jump into a deep sleep.  You may use commands prefixed by '@' or type `wake up` to return to consciousness.
2
5
2
%N %<goes> to sleep.  %S %<is> now dead to the world.
2
5
5
115
1
5
115
1
1
206
115
1
5
115
1
0
66
115
1
0
67
115
1
0
59
115
1
0
72
115
1
0
58
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
38
115
1
0
0
115
1
0
60
115
1
0
60
115
1
0
60
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
100
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
1
2
5
4
0
2
5
1
61
2
5
0
0
2
5
0
79
36
1
2
CST
2
5
0
2
2
5
2
%S %<is> carrying %c.
2
5
2

2
5
4
19
1
349
1
350
1
351
1
352
1
353
1
354
1
355
1
358
1
359
1
360
1
361
1
362
1
363
1
364
1
365
1
368
1
369
1
370
1
371
2
1
4
2
1
360
1
361
115
1
2

2
5
4
0
2
1
0
0
2
1
4
19
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
2
1
0
0
2
1
4
19
2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2
4
4
5
2
shoulders
2
arms
2
hands
2
legs
2
feet
2
5
4
5
2

2

2

2

2

2
4
0
2
2
5
4
0
115
0
0
0
115
1
4
0
36
0
1
-1
115
1
1
2789
2
5
2

2
5
2

2
5
2
%N is carrying %t around on %p shoulders.
2
5
2

2
5
2

2
5
2

2
5
2

2
5
2

2
5
2

2
5
0
40
36
1
2

2
5
2

2
5
4
0
115
1
0
0
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
0
4
2
5
4
0
2
5
0
0
2
4
4
2
2
Wizard
2
God
2
1
2

2
5
4
2
0
12053
0
955495071
100
1
2

2
5
0
0
2
5
0
0
2
5
2

2
5
0
0
115
1
#3
generic room

152
2
-1
-1
-1
80
49
79
70
confunc
2
173
-1
disfunc
2
165
-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 @entrances
2
5
-1
look_self
2
173
-1
acceptable
2
165
-1
add_entrance
2
165
-1
bless_for_entry
2
173
-1
go
2
93
-2
announce_all
2
173
-1
announce_all_but
2
165
-1
enterfunc
2
165
-1
exitfunc
36
173
-1
remove_exit
2
165
-1
remove_entrance
2
173
-1
@add-exit
2
17
-1
@add-entrance
2
17
-1
recycle
2
165
-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
2
25
-1
ejection_msg oejection_msg victim_ejection_msg
36
173
-1
accept_for_abode
2
173
-1
@resident*s
2
25
-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
2
165
-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
165
-1
tell_exits
2
165
-1
find_seat
2
173
-1
broadcast_event_*
2
173
-1
audience_for_sound audience_for_speech
2
165
-1
audience_for_motion
2
173
-1
identify_exit identify_entrance
2
173
-1
exitfunc
2
165
-1
audience_for_announce
2
165
-1
light_level
2
173
-1
furniture_huh
2
173
-1
free_entry
2
173
-1
disfunc_leave_msg disfunc_arrive_msg
2
165
-1
send_user_home
2
165
-1
default_yanked_msg default_oyanked_msg
2
173
-1
announce_yanked_messages
2
165
-1
matching_contents
2
173
-1
audience_for_event
2
165
-1
occupants
2
173
-1
set_zone_coordinates
2
173
-1
get_integration_msg
2
165
-1
jump_code
2
173
-1
check_jump_code
2
173
-1
exits_blocked_by
36
173
-1
check_exiting_object
2
165
-1
ititle_list*c
36
173
-1
tell_title
2
173
-1
is_controllable_by
113
173
-1
take_exit
2
173
-1
will_link_to
36
173
-1
init_for_core
2
173
-1
is_resident
36
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
default_yanked_msg
default_oyanked_msg
zone_coordinates
30
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
%D is unceremoniously expelled from %i by %n.
2
5
4
0
2
5
1
115
2
5
4
0
2
4
1
-1
2
5
0
0
2
5
4
0
2
4
0
0
2
5
0
2
2
5
2

2
5
2
%[ddnamec] %<d:snaps> out of sight, as if yanked away by some invisible chain.
2
5
0
0
113
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
1
2
generic room
2
5
5
2
5
4
2
0
45942
0
955495071
100
1
5
2
5
5
2
5
0
0
2
5
5
2
5
5
115
1
#4
generic builder

144
2
-1
-1
-1
6
57
133
46
@create
2
81
-2
@recreate
2
89
13
@dig
2
81
-2
@auditDB
2
89
-2
@count
2
25
-1
@countDB
2
25
-1
@sort-owned*-objects
2
9
-1
@add-owned
2
25
-1
@verify-owned
2
9
-1
@newmess*age
2
81
-2
@unmess*age
2
81
-2
_messagify
2
173
-1
@realm
2
93
-2
realm2
2
173
-1
classes_2
2
173
-1
_create
2
173
-1
@chparent
2
81
1
@check-chp*arent
2
89
1
@set*-property-value
2
89
1
option_packages
2
173
-1
@spawn
2
81
-2
@zone*s
2
89
-2
_create(oq)
2
173
-1
@chain
36
29
-1
@unchain
36
21
-1
@chained
2
17
-1
@chparent(presafe)
2
81
1
@lock
2
89
0
@s*how
2
25
-1
@d*isplay
2
25
-1
@disown @disinherit
2
89
-2
@who
2
85
-2
@pros*pectus pros*pectus
2
85
-2
@prop*erty
2
81
-2
@rmprop*erty
2
81
-2
@code
2
81
-2
@list
2
81
-2
parse_display_args
2
173
-1
@value-edit @vedit
2
25
-1
@chmod
2
81
-2
@remember
36
25
-1
@forget
36
25
-1
@known
36
9
-1
@dump
2
81
-2
@quick-edit @qedit
2
81
-2
death unconsciousness
115
173
-1
2
recreate_enabled
known_objects
178
0
1
2
5
4
0
36
0
5
36
1
5
2
0
5
2
5
5
2
0
5
2
1
0
0
2
5
5
2
5
5
36
1
5
36
1
4
0
36
0
5
36
1
5
2
5
4
0
2
1
5
2
4
4
0
2
0
5
2
0
4
0
2
4
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
4
4
0
2
4
5
2
1
0
0
100
0
5
2
0
5
2
5
5
2
1
4
0
115
1
5
2
5
1
159
2
5
5
100
1
5
2
0
5
2
5
5
2
1
5
36
1
5
2
5
5
115
1
4
0
36
0
5
2
5
5
115
1
5
115
1
5
36
0
5
115
1
5
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
2
5
5
115
1
5
115
1
1
206
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
1
61
2
5
5
2
5
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
115
1
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
4
5
2
5
5
2
4
5
2
5
5
115
0
5
115
1
5
36
0
5
115
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
5
2
4
5
2
4
5
36
1
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
62175
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#5
generic thing

152
2
-1
-1
-1
79
8
76
15
get take
2
45
-1
drop
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
examine_key
2
173
-1
is_airborne
2
165
-1
throw toss pitch
2
101
-2
terminate_throw
2
173
-1
catch
2
37
-1
dodge
2
37
-1
look_place_msg
2
173
-1
do_throw
2
165
-1
maybe_yank_chain
2
173
-1
is_chained*_to
36
165
-1
tell_description
2
173
-1
14
drop_failed_msg
drop_succeeded_msg
odrop_failed_msg
odrop_succeeded_msg
otake_succeeded_msg
otake_failed_msg
take_succeeded_msg
take_failed_msg
thrown
catch_msg
ocatch_msg
dodge_msg
ododge_msg
chained
42
2
You can't seem to drop %[tdname] here.
2
5
2
You drop %[tdname].
2
5
2
%N %<tries> to drop %[tiname] but fails!
2
5
2
%N %<drops> %[tiname].
2
5
2
%N %<picks> up %[tiname].
2
5
2

2
5
2
You take %[tdname].
2
5
2
You can't pick up %[tdname].
2
5
0
0
2
1
2

2
5
2
%N %<catches> %[tdname].
2
5
2

2
5
2
%N %<dodges> %[tdname].  %[tpsc] %<t:sails> past %o and %<t:arcs> to the ground.
2
5
1
-1
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
generic thing
2
thing
2
5
5
2
5
4
2
0
10815
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#6
generic player

152
2
-1
-1
-1
92
38
81
197
init_for_core
2
173
-1
confunc
2
173
-1
disfunc
2
173
-1
initialize
2
173
-1
recycle
2
173
-1
my_huh
2
165
-1
last_huh
2
165
-1
notify_lines
2
173
-1
linesplit
2
173
-1
@more
2
25
-1
@wrap
36
1
-2
@linelen*gth
36
17
-1
@pagelen*gth
2
17
-1
tell
2
173
-1
gag_p
2
173
-1
set_gaglist
2
173
-1
@gag
2
89
-2
@listgag @gaglist
2
5
-1
@ungag
2
29
-1
whodunnit
2
173
-1
@sw*eep
2
9
-1
receive_page
2
173
-1
@sethome
2
9
-1
@eject
2
89
5
@wizards
2
29
-1
?* help info*rmation @help
2
85
-2
news
2
93
-2
mail_forward mail_notify
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 display_seq_headers display_seq_full messages_in_seq list_rmm new_message_num length_num_le length_date_le length_date_gt length_all_msgs exists_num_eq rm_message_seq undo_rmm expunge_rmm renumber
2
173
-1
msg_summary_line
36
173
-1
msg_text
2
173
-1
notify_mail
2
173
-1
__fix
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
85
-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
173
-1
@answer @repl*y
2
173
-1
@forward
2
173
-1
@gripe
2
173
-1
@typo @bug @suggest*ion @idea @comment
2
173
-1
@skip
2
89
-2
@subscribe @unsubscribed
2
25
-1
mail_catch_up
2
173
-1
@rn check_mail_lists @subscribed
2
5
-1
list_option
2
173
-1
set_name
2
173
-1
set_aliases
2
173
-1
@rename
2
81
1
@addalias
2
81
1
@rmalias
2
81
5
@desc*ribe
2
89
13
@mess*ages
2
81
-2
@password
2
89
-2
@last-c*onnection
2
29
-1
@gender*s
2
25
-1
set_brief
2
173
-1
@mode
2
81
-2
exam*ine @exam*ine
2
25
-1
add_feature
36
165
-1
remove_feature
36
165
-1
@add-feature @addfeature
2
25
-1
@remove-feature @rmfeature
2
25
-1
@features*!
2
93
-2
@memory
36
9
-1
@version
36
9
-1
@uptime
36
9
-1
@quit QUIT
2
9
-1
examine_commands_ok
2
173
-1
is_listening
2
165
-1
moveto
2
173
-1
announce*_all_but
2
165
-1
linewrap
36
173
-1
@set-note-string @set-note-text
2
25
-1
ownership_quota
2
173
-1
tell_lines
2
173
-1
set_pagelength
2
165
-1
@unsub*scribe
2
89
-2
@registerme @register
2
73
13
tag_msg
2
165
-1
WHO @who
2
13
-1
'*
36
81
-2
+*
36
81
-2
moo-whisper wh*isper
2
157
1
@quota
2
17
-1
name
2
173
-1
owned_objects
2
173
-1
@detail
2
81
-2
@undetail
2
17
-1
@details
2
89
-2
@unlock
2
17
-1
@name
2
89
12
@chown
2
89
1
local-who little-who
2
85
-2
@mood
2
89
-2
@ansi
2
89
-2
match_option_package
2
173
-1
option_packages
2
173
-1
set_option
2
173
-1
get_option*s
2
173
-1
@option*
2
93
-2
notify
2
165
-1
process_notified_text
2
165
-1
namec
2
173
-1
is_unique
36
173
-1
@audit
2
89
-2
object_audit_string
2
173
-1
audit_object_category
2
173
-1
set_mail_forward
2
173
-1
@request*!
2
25
-1
@alternates @alts
2
89
-2
is_writable_by
2
173
-1
@roll*! @reroll*!
115
89
-2
@time @date
36
13
-1
puppet_huh
2
173
-1
p*age @p*age
2
85
-2
@beep
2
89
-2
game @game
115
29
-1
@hide @unhide
2
9
-1
has_message
2
173
-1
birth_effects
115
173
-1
stat_agility stat_dexterity stat_endurance stat_quickness stat_strength stat_willpower
115
173
-1
set_sheet
115
165
-1
@lag
2
9
-1
@edit @notedit @note-edit
36
173
-1
@recall
36
29
-1
unpage @unpage
36
29
-1
finger_line_info
2
173
-1
public_loc*ation
36
173
-1
public_email
2
173
-1
public_connect_site
2
165
-1
public_home
36
173
-1
@finger finger
2
89
-2
parse_page_args
36
173
-1
character_points
115
173
-1
send_self_netmail
2
173
-1
@netforw*ard
2
93
-2
page_echo_msg page_absent_msg page_origin_msg page_refused_msg
36
165
-1
@unread
2
89
-2
character_sheet
115
173
-1
@quota(oq)
2
17
-1
@measure
2
89
-2
local_who
2
5
-1
apply_death_penalty
115
173
-1
@last-checkpoint @lc @next-checkpoint @checkpoint-info
36
13
-1
matches
115
173
-1
penalty(pk)
115
173
-1
registration_nag
2
173
-1
@recycle
2
25
-1
_recycle
2
173
-1
@size
36
93
11
@dump-garbage
2
9
-1
_chparent
2
165
-1
acceptable
115
173
-1
virtual_age
36
173
-1
skill
115
29
-1
remove_puppet
115
173
-1
add_puppet
115
173
-1
has_gagged
36
173
-1
get_option(pretable)
2
173
-1
set_option(pretable)
2
173
-1
format_for_netforward
2
173
-1
my_match_player
36
173
-1
public_who
36
173
-1
set_stat mod_stat
115
173
-1
crystal_worth
115
173
-1
death_effects
115
173
-1
check_saved_pages
2
173
-1
handle_uncaught_error
2
173
-1
handle_task_timeout
2
173
-1
news_catch_up
2
173
-1
announce_mood
115
173
-1
should_be_moody
115
173
-1
should_suffer_insanity suffer_insanity
115
173
-1
should_suffer_fatigue suffer_fatigue
115
173
-1
maybe_increase_potential
115
173
-1
pulse
115
173
-1
@advantage*s
115
89
-2
finger_header
2
173
-1
pnc(pre-charsets)
2
165
-1
mp*age
36
81
-2
@@sendmail
2
89
-2
@chargen @charedit @character-edit @char-edit
2
89
-2
47
features
previous_connection
mail_lists
email_address
last_disconnect_time
help
more_msg
linetask
linesleft
linebuffer
pagelen
_mail_task
owned_objects
current_folder
all_connect_places
last_connect_place
messages_going
lines
page_absent_msg
page_origin_msg
page_echo_msg
mail_notify
mail_forward
current_message
messages
last_connect_time
ownership_quota
password
gaglist
first_connect_time
puppets
tag_msg
heart
size_quota
last_command
ansi
alt_info
last_paged
plan
sheet
saved_pages
page_refused_msg
pk
last_reroll
options
cp_base
last_edited_by
176
4
0
36
1
0
0
2
0
4
0
2
5
2

2
0
0
0
2
1
0
0
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
0
0
2
5
4
0
2
1
0
1
2
4
4
0
2
0
2
?
2
0
4
0
2
4
0
10
2
4
2
%N %<is> not currently logged in.  However, your page has been saved for %o.  Use `@unpage %N` to delete it.
2
5
2
*** You sense %n %<is> trying to send you an OOC message.
2
5
2
%N %<has> received your page: "$text"
2
5
4
0
2
5
4
0
2
1
4
2
0
0
0
0
2
4
4
0
2
4
0
0
2
1
0
0
100
0
2
impossible password to type
2
0
4
0
2
5
0
2147483647
2
1
4
0
115
1
2

2
5
1
159
2
5
4
4
0
0
0
0
0
0
0
0
100
1
2

2
0
0
0
2
5
0
0
2
1
1
-2
36
1
4
0
2
5
4
0
115
1
4
0
36
0
2
%N %<is> not accepting pages at this time.
2
5
4
0
115
1
0
0
115
1
0
0
36
0
0
50
115
1
4
2
1
2
0
945478019
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
2
5
5
115
1
5
115
1
1
206
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
0
100
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
1
61
2
5
5
2
5
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
115
1
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
4
5
2
5
5
2
4
5
2
5
5
115
0
5
115
1
5
36
0
5
115
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
5
2
4
5
2
4
5
36
1
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
209948
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#7
generic exit

152
2
-1
-1
-1
137
131
-1
33
invoke
2
165
-1
move
2
173
-1
recycle
2
165
-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
oarrive_msg oleave_msg leave_msg arrive_msg nogo_msg onogo_msg
2
173
-1
obvious
2
173
-1
otherside
2
165
-1
transparency
2
173
-1
sound_permeability
2
173
-1
hear_event_speech
2
165
-1
hear_event_sound
2
165
-1
hear_event_motion
2
165
-1
from_name
2
173
-1
invoke_for_walker
2
173
-1
is_unlocked_for_walker
2
173
-1
look_self
2
165
-1
dest
2
173
-1
dname*c
2
165
-1
is_writable_by
113
173
-1
clearance
36
173
-1
set_clearance
36
173
-1
too_big_msg otoo_big_msg
36
173
-1
check_clearance
36
173
-1
is_unlocked_for
36
173
-1
tell_stats
36
173
-1
oname*c odname*c oiname*c otitle*c odtitle*c oititle*c
36
173
-1
set_dest set_source
2
173
-1
initialize
2
165
-1
17
obvious
source
dest
nogo_msg
onogo_msg
arrive_msg
oarrive_msg
oleave_msg
leave_msg
otherside
transparency
sound_permeability
clearance
self_too_big_msg
oself_too_big_msg
too_big_msg
otoo_big_msg
29
0
1
2
5
1
-1
2
5
1
-1
2
5
0
0
2
5
0
0
2
5
0
0
2
5
0
0
2
5
0
0
2
5
0
0
2
5
1
-1
2
1
0
1
2
5
0
-4
2
5
0
50
36
1
2
You can't fit through that exit; you're too big.
2
5
2
%N %<tries> to squeeze %r through %[tdname], but %s won't fit.
2
5
2
You can't fit %[ddname] through that exit; %[dps] %<d:is> too big.
2
5
2
%N %<tries> to squeeze %[diname] through %[tdname], but %[ddname] %<d:is> too big.
2
5
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
1
2
generic exit
2
5
5
2
5
4
2
0
19622
0
955495071
100
1
5
2
5
5
2
5
0
1
2
5
5
2
5
5
115
1
#8
generic container

152
2
-1
-1
-1
5
169
9
17
put insert cache store
2
157
3
remove take get retrieve
2
157
5
acceptable
2
173
-1
open
2
45
-1
@lock_for_open
2
97
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
165
-1
@opacity
2
97
12
set_opaque
2
165
-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
165
-1
do_auto_close
36
173
-1
do_auto_open
36
173
-1
do_lock
36
173
-1
examine_contents
2
173
-1
20
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
ctype
62
2
%N %<closes> %[diname].
2
5
2
You close %[ddname].
2
5
2
%N %<opens> %[diname].
2
5
2
You open %[ddname].
2
5
2

2
5
2
You can't put %[ddname] in that.
2
5
0
1
2
1
0
0
2
1
2

2
5
2
%N %<removes> %[diname] from %[iiname].
2
5
2
You can't remove that.
2
5
2
You remove %[diname] from %[idname].
2
5
2
%N %<puts> %[diname] in %[iiname].
2
5
2
You put %[ddname] in %[idname].
2
5
2

2
5
2
You can't open that; maybe it's locked.
2
5
2
It is empty.
2
5
0
1
2
1
0
0
2
5
0
3
2
5
5
2
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
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
generic container
2
container
2
5
5
2
5
4
2
0
12774
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#9
generic note

152
2
-1
-1
-1
5
53
62
12
r*ead
2
45
-1
er*ase
2
45
-1
wr*ite
2
157
4
del*ete rem*ove
2
153
5
encrypt
2
97
0
decrypt
2
33
-1
text
2
173
-1
is_readable_by
2
173
-1
set_text
2
173
-1
is_writable_by
2
173
-1
mailme
2
41
-1
look_self
2
173
-1
4
writers
encryption_key
text
auto_read
46
4
0
2
5
0
0
2
4
4
0
2
4
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
1
5
2
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
generic note
2
note
2
5
2
There appears to be some writing on the note ...
2
5
4
2
0
7596
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
0
1
115
1
#10
Login Commands

16
2
-1
-1
-1
1
-1
67
32
? h*elp @h*elp
2
93
-1
w*ho @w*ho
2
93
-1
co*nnect @co*nnect
2
93
-1
cr*eate @cr*eate
2
89
-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
165
-1
check_for_shutdown
2
173
-1
check_player_db
2
173
-1
_match_player
2
165
-1
notify
2
165
-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
165
-1
blacklist_add graylist_add redlist_add spooflist_add
2
173
-1
blacklist_remove graylist_remove redlist_remove spooflist_remove
2
173
-1
listname
2
173
-1
record_connection
2
173
-1
spooflisted
2
173
-1
user_created user_connected user_disconnected user_client_disconnected user_approached
2
165
-1
welcome_message
2
165
-1
we*lcome
2
93
-1
connected
2
173
-1
sample_lag
2
173
-1
current_lag
2
173
-1
special_connected
2
165
-1
do_lag_sample
2
173
-1
attempt_shared_login
2
173
-1
23
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
login_listeners
alt_welcome_messages
connected
last_lag_sample
lag_samples
lag_sample_interval
extra_login_msg
special_connected
lag_task
32
4
5
2
Welcome to the GhostCore database.
2

2
Type 'connect god' to log in.
2

2
You will probably want to change this text, which is stored in $login.welcome_message.
2
5
2
Your character is temporarily hosed.
2
5
2
Character creation is disabled.
2
5
2

2
5
0
1
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
4
1
1
2
2
1
4
15
2
    ;;;                 ;;;;;;;;;
2
     ;;;       ;       ;;;    ;;;;;;;;;;;;;   ,,    ,,,,,, ,,    , ,,,,
2
    ;;;        ;;    ;;  ;;;      ;;;;   ;;;;;;;;   ,,   , ,,    , ,,  ,
2
     ;;;  ;;;  ;;;  ; ;;   ;;;   ;;;    ;;;   ,,    ,,   , ,,    , ,,   ,
2
    ;;; ;;;;  ;;;; ;;;;;    ;;;  ;;;   ;;;    ,,    ,,   , ,,,   , ,,   ,
2
      ;;;; ;;;; ;; ;  ;;   ;;;; ;;;   ;;;;;;; ,,    ,,   , ,, ,  , ,,   ,
2
      ;;;  ;;;  ;; ;  ;; ;;;;    ;;; ;;;      ,,    ,,,,,, ,,  , , ,,   ,
2
                 ;;;   ;;;;    ;;;; ;;;       ,,    ,,   , ,,   ,, ,,  ,
2
                   ;;;;;           ;;;;;;;;   ,,,,,,,,   , ,,    , ,,,,
2
 
2
         `connect <player> <password>' to connect to your character.
2
 
2
    `connect guest' and type `help @request' to find out how to register.
2
 
2
       `WHO' or `@who' at any time to get a list of connected players.
2
1
4
0
2
0
0
955494957
2
1
4
4
0
0
0
0
0
4724
0
0
2
1
0
15
2
1
2

2
5
4
0
2
1
0
1036373182
36
1
0
0
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
24827
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#11
Player Last_huh Verbs

16
2
-1
-1
-1
1
-1
10
5
@*
2
165
-1
give hand
2
173
-1
get take
2
173
-1
drop throw
2
173
-1
call_social_verb
2
173
-1
1
social_verbs
10
4
25
2
comfort
2
cry
2
wink
2
yawn
2
wave
2
cackle
2
smile
2
laugh
2
giggle
2
snore
2
grin
2
blush
2
poke
2
shrug
2
cringe
2
smirk
2
nod
2
sigh
2
chuckle
2
hug
2
kiss
2
bow
2
point
2
frown
2
sneer
36
1
0
0
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
6093
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#12
Guest Log

0
2
-1
-1
-1
1
-1
11
3
enter
2
173
-1
last
2
157
4
init_for_core
2
173
-1
1
connections
10
4
0
2
0
0
0
2
4
4
1
2
Guest Log
2
5
5
2
5
4
2
0
2502
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#13
Generic BigList Utilities

144
36
-1
-1
-1
83
-1
72
25
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
2
about
maxfanout
11
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
0
0
36
4
4
2
2
ghblu
2
biglist_utils
36
1
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
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#14
Generic Large-Capacity Mail Recipient

144
36
-1
-1
-1
44
17
70
40
_genprop
36
173
-1
_make
2
173
-1
_kill
2
165
-1
_get
36
165
-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
165
-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
4
summary_uses_body
_mgr
mowner
_genprop
30
0
0
36
5
1
13
36
5
1
36
36
1
2

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

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

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

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

2
    ;;something.foo= #MR:messages();
2
    @rmm 1-$ from #MR
2
    @unrmm expunge
2
    @chparent #MR to $big_mail_recipient
2
    ;#MR:receive_batch(@something.foo);
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#15
The Body Bag

16
2
-1
-1
-1
1
-1
73
3
acceptable
2
173
-1
confunc
2
173
-1
who_location_msg
36
165
-1
0
9
0
0
2
4
4
1
2
The Body Bag
2
5
5
2
5
4
2
0
1610
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#16
Registration Database

0
36
-1
-1
-1
37
-1
121
6
find* _only
36
173
-1
add
36
173
-1
init_for_core
36
173
-1
suspicious_address
2
173
-1
suspicious_userid
2
173
-1
load
2
173
-1
2
registrar
new_player_mail
14
1
2
36
5
4
0
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
200005
0
927471300
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#17
Player-Creation-Log

0
36
45
-1
29
14
-1
-1
4
display_seq_headers
2
173
-1
msg_summary_line
2
173
-1
init_for_core
2
173
-1
is_usable_by
36
165
-1
1
autoregistration_player
31
1
2
36
5
0
1
36
5
1
13
36
5
1
36
36
1
2

36
1
4
0
36
5
0
0
36
1
4
0
36
0
0
1
36
5
2
%n (%#) can't send to moderated list %t (%[#t]) directly.
36
5
4
0
36
5
0
1
36
5
4
1
1
2
36
1
4
2
1
2
1
17
36
1
0
2592000
36
5
0
0
36
1
4
0
36
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
0
36
4
4
3
2
Player-Creation-Log
2
Player_Creation_Log
2
PCL
36
1
2
Log of player creations.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#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
0
9
0
0
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
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#19
Core Utility Help

16
36
-1
-1
-1
30
-1
181
3
find_topics
36
165
-1
get_topic
36
173
-1
dump_topic
2
173
-1
31
$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
41
4
70
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 availabe to non-connected players
2

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

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

2
CUSTOMIZATIONS
2

2
  .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
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
19
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
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-resolution  -- how mail forwarding/notification works, in gory detail
2
receiving-mail   -- what :receive_message should do
2
mail-format      -- format of transmitted messages
2
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
69
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 - 1..4 for four different styles of .contents lists
2

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

2
l*ook
2

2

2
(5)  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
(6)  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
76
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
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
5
36
5
0
0
36
4
4
1
2
Core Utility Help
36
5
2
Help database for LambdaCore utility objects and generics.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#20
string utilities

24
2
-1
-1
-1
83
-1
21
75
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 match_player match_player_or_object
2
173
-1
match
36
165
-1
match_str*ing
36
173
-1
match_object
36
173
-1
find_prefix
36
173
-1
index_d*elimited
36
165
-1
is_numeric
36
173
-1
ordinal
36
173
-1
group_number
36
173
-1
english_number
2
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
165
-1
pronoun_sub_secure
36
173
-1
pronoun_quote
36
173
-1
alt_pronoun_sub
38
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
165
-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
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
pluralize pluralise
2
165
-1
unbreak
2
165
-1
chop
2
173
-1
aliases_from_name
2
165
-1
title_list*c name_list*c dname_list*c iname_list*c dtitle_list*c ititle_list*c
2
173
-1
format
36
173
-1
lines_to_list enlist
2
173
-1
lines_to_string
2
173
-1
english_encrypt
2
173
-1
smart_columnize smart_columnise
36
173
-1
quoted_english_list
36
173
-1
to_bytes byte_str*ing
36
173
-1
strhash
2
173
-1
abbreviated_value
36
173
-1
_abbreviated_value
36
173
-1
quoted_list
36
173
-1
8
digits
ascii
alphabet
use_article_a
use_article_an
spaces
ps_verbs
ps_props
17
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
4
53
2
ps
2
po
2
pp
2
ititlec
2
dtitle
2
ddesc_noun
2
desc_adj
2
dname
2
dnamec
2
pr
2
inamec
2
gender_noun
2
pq
2
titlec
2
title
2
outside_dname
2
iname
2
ititle
2
dtitlec
2
callname
2
space
2
psc
2
ppc
2
quickness
2
blockers
2
namec
2
title_msg
2
callnum
2
outside_name
2
desc_noun
2
time
2
i
2
l
2
d
2
odname
2
odnamec
2
poc
2
eng_time_left
2
natural_weapon
2
page_origin_msg
2
page_echo_msg
2
page_refused_msg
2
tag_msg
2
watched_msg
2
help_msg
2
wielding
2
nice
2
who_location_msg
2
connected_seconds
2
idle_seconds
2
description
2
sleep
2
cdesc
38
0
4
23
2
color_msg
2
color
2
nickname
2
place_msg
2
plumage_msg
2
demeanor_msg
2
eyes_msg
2
setting
2
remaining_portions
2
metal
2
card_holder_msg
2
plan
2
sleep_msg
2
osleep_msg
2
odeath_msg
2
oabsorbed_hit_msg
2
aliases
2
users
2
help_msg
2
tag_msg
2
location
2
stone_msg
2
shape_msg
38
0
0
0
2
4
4
2
2
string
2
utils
2
5
4
66
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

2
:english_number(42)          => "forty-two"
2
:english_ordinal(42)         => "forty-second"
2
:ordinal(42)                 => "42nd"
2
:group_number(42135 [,sep])  => "42,135"
2

2
    Type checking:
2

2
:is_numeric   (string) => return true if string is composed entirely of digits
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

2
    Matching:
2

2
:match_string (string, pattern, options)       => * wildcard matching
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
: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

2
    Pretty printing:
2

2
:space         (n/string[,filler])     => n spaces
2
:left          (string,width[,filler]) => left justified string in field 
2
:right         (string,width[,filler]) => right justified string in field
2
:center/re     (string,width[,filler]) => centered string in field
2
:columnize/se  (list,n[,width])        => list of strings in n columns
2

2
    Substitutions
2

2
:substitute (string,subst_list [,case])   -- general substitutions.
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

2
    A useful property:
2

2
.alphabet                    => "abcdefghijklmnopqrstuvwxyz"
2
5
4
2
0
70494
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#21
building utilities

24
2
-1
-1
-1
83
-1
42
6
make_exit
2
173
-1
set_names
2
165
-1
recreate
2
173
-1
parse_names
2
173
-1
size_string
2
173
-1
set_spawned_names
2
173
-1
0
9
0
0
2
4
4
2
2
building
2
utils
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
4
2
0
6595
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#22
Programmer Help

16
2
-1
-1
-1
30
-1
19
2
errors
2
173
-1
prepositions
2
165
-1
56
@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
@edit
@display
@dbsize
@copy
@chparent
@chmod
@args
.program
@clearproperty
@disown
@disinherit
@displayoptions
@display-options
@add-feature
@remove-feature
features
examine
mail
#
event-names
events
event-system
summary

66
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
36
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
        RETURN ;
2
        RETURN expression ;
2
The return statement evalautes the expression, if any, and returns the resulting value (or 0 if there is no expression) to the verb that called the current one.  Execution of the current verb is immediately terminated.
2

2
        FORK ( 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
17
2
The table below gives the relative precedence of all of the MOO operators; operators on higher lines in the table have higher precedence and those on the same line have identical precedence:
2

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

2
Thus, the horrendous expression
2

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

2
would be grouped as follows:
2

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

2
statements -- the syntax and semantics of the various kinds of MOO statements
2
expressions -- the same for the various kinds of MOO expressions
2
functions -- a list of the primitive functions available to MOO programs
36
1
4
93
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
eval(string)              -- parsing and executing strings as MOO code
2
notify(player, string)    -- sending text to a player's terminal
2
read()                    -- reading a line of input from the player (*)
2
output_delimiters(player) -- return {prefix,suffix} set by PREFIX/SUFFIX cmds
2

2
typeof(value)      -- determining the data type of a value
2
 tostr(value, ...) -- converting any set of values into a string
2
 tonum(value)      -- converting any non-list value into a number
2
 toobj(value)      -- converting any non-list value into an object
2
length(value)      -- returns the length of a string or list
2

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
min(n1, n2, ...) -- minimum of n1,n2,...
2
max(n1, n2, ...) -- maximum of n1,n2,...
2
abs(n)           -- absolute value of n
2
sqrt(n)          -- square root of n, rounded down
2
random(n)        -- random integer between 1 and n inclusive
2
 time()          -- current time in seconds since midnight GMT, 1 Jan 70
2
ctime([time])    -- time (or current time) converted to a human-readable string
2
 index(str1, str2 [, case-matters]) -- index of first str2 in str1
2
rindex(str1, str2 [, case-matters]) -- index of last  str2 in str1
2
strcmp(str1, str2) -- case-sensitive string comparison
2
strsub(subject, what, with [, case-matters]) -- substitution in a string
2
crypt(string [, salt]) -- one-way string encryption
2
 match(str1, str2 [, case-matters]) -- match first 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
   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

2
        is_player(object) -- testing whether or not object is a player
2
          players()       -- a list of all players, active or not
2
connected_players()       -- a list of all currently-connected players
2
     idle_seconds(player) -- seconds since given player typed anything
2
connected_seconds(player) -- seconds given player has been logged in
2
    boot_player(player)        -- disconnect player from the MOO immediately(*)
2
set_player_flag(player, value) -- set/clear player bit; boot player if clear(*)
2
connection_name(player)   -- a server-assigned name for player's connection
2
open_network_connection(@args) -- open a connection to another network site
2

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

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

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

2
        number
2
        # number
2
        # - number
2
        "character string"
2
        error-name
2
Literal expressions return the obvious values: numbers, 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, NUM
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 number between 1 and the length of S, inclusive.  The Nth element of S is returned.  The elements of a string are themselves one-character strings.
2

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

2
        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

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

2
        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
The method for disambiguating the meaning of a complex MOO expression in the absence of sufficient parentheses is described in 'help precedence'.
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 player property .eval_time to 1, 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
5
2
Syntax:  @setenv <environment string>
2

2
Defines the environment for eval (property player.eval_env).  See "help eval"
2
for more information.  This is mostly useful when one's .eval_env is broken
2
and prevents one from using eval to reset it.
36
1
4
7
2
Syntax:  @rmverb <object>:<verb-name>
2
         @rmverb <object>:<verb-name>  <dobj> <prep> <iobj>
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
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
29
2
Usage:  @prospectus player [from number] [to number]
2

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

2
Example:
2
   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
        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
17
2
Syntax:  @program <object>:<verb-name>
2
         @program <object>:<verb-name> <dobj> <preposition> <iobj>
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
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
18
2
Syntax:  @list <object>:<verb>
2
         @list <object>:<verb> [with|without parentheses|numbers]
2
         @list <object>:<verb> <dobj> <prep> <iobj>
2

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

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

2
The 3rd form of the verb lists the verb matching the given dobj/prep/iobj specification if such exists.  
2

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

2
The 2nd and 3rd forms can be combined, e.g.,
2

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

2
which would be useful if `frobule' had more than one `burfle' verb and we wanted to see the one having `this' `in front of' `any' as its respective dobj/prep/iobj specifiers.
36
1
4
24
2
Syntax:  @kill task_id
2
         @kill [object]:[verb]
2
         @kill soon [number-of-seconds]
2
         @kill all
2
         @kill %trailing_id
2

2

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

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

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

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

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

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

2
Example:
2
  @forked
2
  1359083655  Sep 16 21:45:00 1991  yduJ          #5803:heartbeat (10) [#68]
2
  @kill %655
2
  Killed:   task 1359083655, verb #5803:heartbeat, line 10, this==#68
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
22
2
Syntax:  @forked
2

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

2
Queue ID:
2
   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.
36
1
4
3
2
Syntax:  @edit <object>:<verb-name> [<dobj> [<prep> [<iobj>]]]
2

2
Enters the MOO Verb Editor for the named verb on the named object.  See 'help editors' for more detail.
2
5
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
11
2
Syntax:  @copy <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
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
25
2
Syntax:  @chmod <object> <object-permissions>
2
         @chmod <object>.<prop-name> <property-permissions>
2
         @chmod <object>:<verb-name> <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
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 Writeable in addition to any current bits:
2
  @chmod table +w
2
5
4
5
2
Syntax:  @args <object>:<verb-name> <dobj>
2
         @args <object>:<verb-name> <dobj> <prep>
2
         @args <object>:<verb-name> <dobj> <prep> <iobj>
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'.
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
event-types
2
1
4
2
2
*forward*
2
event-system
2
1
4
18
2
An event is defined as "something that takes place".  Many things take place in the MOO environment.  People say things, do things by emoting, pick things up, leave the room, enter the room, etcetera.
2
 
2
Without some protocol for communicating these events, programmers must resort to time and resource consuming hacks into :tell verbs, parsing out text heard by objects to determine if some event has taken place.
2
 
2
An "event system", in the MOO paradigm, is this protocol.
2
 
2
The event generation system here consists of the following verbs.  The asterisk (*) should be replaced with the name of the event.  See `help event-types' for a current list of events.
2
 
2
$room:broadcast_event_*      -- Broadcast an event to the room.
2
$room:audience_for_*   -- A list of objects listening to the given event type.
2
 
2
At the core of event generation, there's still a lot of string hacking.  Emotes must be hacked to decide if they are motion, speech, sound, or a combination of any of the three.  It's a tricky deal.
2
 
2
If you're writing a VR verb which you feel should generate an event, then please do so.  The format is:
2
 
2
this.location:broadcast_<event-type>(@event-args);
2
 
2
Again, see `help event-types' for more info on specific events.  Feel free to ask another programmer or to look at the appropriate :audience_for_* verbs on your location.  They usually contain comment lines with arglists.
2
1
4
2
2
building      -- extending the MOO
2
programming   -- writing code in the MOO programming language
2
5
4
7
2
*pass*
2
summary
2

2
building      -- extending the MOO
2
programming   -- writing code in the MOO programming language
2

2
Type `help <topic>` for information on a particular topic.
2
5
5
2
5
0
0
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
72134
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#23
Wizard Help

16
36
-1
-1
-1
30
-1
22
0
34
@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
@grant
redlist
@blacklist
@graylist
@redlist
@make-guest
44
4
7
2
Syntax:  @guests [<n>]
2

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

2
Alternate:  @guests now
2

2
Prints out in @who format all connected guests.  In place of the location field is the current connect site.
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
25
2
*subst*
2
Syntax:  @newt <player> [commentary]
2

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

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

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

2
  @newt Twit  did real annoying things.
2

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

2
@newt  [%[$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.
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
         @grep <pattern>
2
         @egrep <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
26
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

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', or `gray'.
2

2
One may either specify a domain name (e.g., "baz.edu") or a numeric IP address (e.g., "36.0.23.17").  Domain names match all hosts underneath that domain, so, e.g., puting "baz.edu" on a list effectively adds "x.bax.edu" for all x as well.  
2
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 <number> [<reason>]
2

2
This second and more interesting form of the verb changes a player's quota to the given number.  Mail will be sent to $quota_log; the message will include the <reason> if such is given.
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
13
2
Syntax:  @chown <object>            [to] <owner>
2
         @chown <object>.<propname> [to] <owner>
2
         @chown <object>:<verbname> [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.
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
3
2
Syntax:  @grant <object> to <player>
2

2
Ownership of the object changes as in @chown, i.e., .owner and all c properties change hands while the previous owner's and the new owner's quotas are adjusted.  In addition all verbs and !c properties owned by the previous owner change ownership as well.  Finally, for !c properties, instances on descendant objects change ownership as when @chowning the properties individually.
36
5
4
2
2
*forward*
2
blacklist
36
5
4
14
2
Syntax:  @redlist   [<domain or subnet> [commentary]]
2
         @blacklist [<domain or subnet> [commentary]]
2
         @graylist  [<domain or subnet> [commentary]]
2

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

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

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

2
See `help blacklist' for a description of the functions of these <color>lists.
36
5
4
2
2
*forward*
2
@blacklist
36
5
4
2
2
*forward*
2
@blacklist
36
5
4
7
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
See also `help @make-player'.
36
5
5
36
5
0
0
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
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#24
Wizard Utilities

16
2
-1
-1
-1
83
-1
13
35
set_programmer
2
173
-1
set_player
2
173
-1
set_owner
2
173
-1
set_property_owner
2
165
-1
unset_player
2
173
-1
set_property_flags
2
165
-1
_set_property_flags
2
173
-1
random_password
2
173
-1
queued_tasks
2
165
-1
player_cmd_perms
2
173
-1
isnewt
2
165
-1
newt_confunc
2
169
-1
initialize_owned
2
173
-1
verify_owned_objects
2
173
-1
connected_wizards
36
165
-1
all_wizards
36
165
-1
rename_all_instances
2
165
-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
93
-2
starting_quota
2
165
-1
checkpoint_started_msg checkpoint_failed_msg checkpoint_finished_msg
2
165
-1
set_player
2
165
-1
check_password
2
173
-1
announcements_for
2
173
-1
grant_object
2
173
-1
is_builder
2
173
-1
8
default_programmer_quota
default_player_quota
missed_help_strings
missed_help_counters
checkpoint_started_msg
checkpoint_failed_msg
checkpoint_finished_msg
disclaimer
17
0
28
2
5
0
14
2
5
4
0
2
1
4
0
2
1
2
!!! A checkpoint has begun.  During the next few minutes, you will experience slow response time (AKA lag).  Be patient, grasshopper.
2
5
2
!!! OH SWEET JESUS--The checkpoint has failed!  All work since the last checkpoint may be lost.  See '@last-checkpoint' for when that was.
2
5
2
!!! The checkpoint has finished.  After a suspenseful $elapsed, the world is saved once more.
2
5
4
0
2
1
0
0
2
4
4
1
2
Wizard Utilities
2
5
4
36
2
Wizard Utilities
2
----------------
2
The following functions are substitutes for various server builtins.
2
Anytime one feel tempted to use one of the expressions on the right,
2
use the corresponding one on the left instead.  This will take care
2
of various things that the server (for whatever reason) does not handle.
2

2
:set_programmer(object)             object.programmer = 1;
2
    chparent object to $prog
2
    send mail to $prog_log
2

2
:set_player(object[,nochown])       set_player_flag(object,1);
2
    set player flag, 
2
    add name/aliases to $player_db,
2
    and maybe do a self chown.
2

2
:unset_player(object[,newowner])    set_player_flag(object,0);
2
    unset player flag,
2
    remove name/aliases from $player_db
2
    chown to newowner if given
2

2
:set_owner(object, newowner)        object.owner = newowner;
2
    change ownership on object
2
    change ownership on all +c properties
2
    juggle .ownership_quotas
2

2
:set_property_owner(object, property, newowner)
2
    change owner on a given property
2
    if this is a -c property, we change the owner on all descendants
2
    for which this is also a -c property.
2
    Polite protest if property is +c and newowner != object.owner.
2

2
:set_property_flags(object, property, flags)
2
    change the permissions on a given property and propagate these to 
2
    *all descendants*.  property ownership is changed on descendants 
2
    where necessary.
2
5
4
2
0
36536
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#25
Site DB

0
36
-1
-1
-1
37
-1
16
7
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
cleanup
36
41
-1
add(blech)
2
173
-1
1
domain
13
2
bga.com
36
1
5
36
1
4
4
2

2
ewsclfjarnimpd21Utgyhvbkz453o8x9q760
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
2670029
0
900614078
100
1
4
2
0
4837
0
920650538
36
5
5
36
5
5
36
5
5
36
5
5
115
1
0
0
1
0
#26
Math Utilities

16
36
-1
-1
-1
83
-1
51
39
sin
36
173
-1
cos
36
173
-1
tan
36
173
-1
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
arctan
36
173
-1
div
36
173
-1
mod
36
173
-1
aexp
36
173
-1
random
36
173
-1
random_range
36
173
-1
is_prime
36
173
-1
AND
36
173
-1
OR
36
173
-1
XOR
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
exp
36
173
-1
norm
36
173
-1
sum
36
173
-1
to_percent*age
2
173
-1
from_percent*age
2
173
-1
precision
36
173
-1
rint
36
173
-1
4
base_alphabet
tangents
factor
taylor
13
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
0
0
36
4
4
4
2
Math Utilities
2
Math_Utils
2
trigonometric utilites
2
trig_utils
36
5
4
47
2
Trigonometric/Exponential functions:
2
  sin(a),cos(a),tan(a) -- returns 10000*(the value of the corresponding
2
       trigonometric function) angle a is in degrees.
2
  arctan([x,]y) -- returns arctan(y/x) in degrees in the range -179..180.
2
       x defaults to 10000.  Quadrant is that of (x,y).
2
  exp(x[,n]) -- calculates e^x with an nth order taylor polynomial
2

2
Statistical functions:
2
  combinations(n,r) -- returns the number of combinations given n objects
2
       taken r at a time.
2
  permutations(n,r) -- returns the number of permutations possible given
2
       n objects taken r at a time.
2

2
Number decomposition:
2
  div(n,d) -- correct version of / (handles negative numbers correctly)
2
  mod(n,d) -- correct version of % (handles negative numbers correctly)
2
  divmod(n,d) -- {div(n,d),mod(n,d)}
2
  parts(n,q[,i]) -- returns a list of two elements {integer,decimal fraction}
2

2
Other math functions:
2
  sqrt(x)      -- returns the largest integer n <= the square root of x
2
  pow(x,n)     -- returns x^n
2
  factorial(x) -- returns x!
2

2
Series:
2
  fibonacci(n) -- returns the 1st n fibonacci numbers in a list
2
  geometric(x,n) -- returns the value of the nth order geometric series at x
2

2
Integer Properties:
2
  gcd(a,b) -- find the greatest common divisor of the two numbers
2
  lcm(a,b) -- find the least common multiple of the two numbers
2
  are_relatively_prime(a,b) -- return 1 if a and b are relatively prime
2
  is_prime(n) -- returns 1 if the number is a prime and 0 otherwise
2
  
2
Miscellaneous:
2
  random(n) -- returns a random number from 0..n if n > 0 or n..0 if n < 0
2
  random_range(n[,mean]) -- returns a random number from mean - n..mean + n
2
       with mean defaulting to 0
2
  simpson({a,b},{f(a),f((a+b)/2),f(b)}) -- returns the numerical
2
      approximation of an integral using simpson's rule
2

2
Bitwise Arithmetic:
2
  AND(x,y) -- returns x AND y
2
  OR(x,y) -- returns x OR y
2
  XOR(x,y) -- returns x XOR y (XOR is the exclusive-or function)
2
  NOT(x) -- returns the complement of x
2
      All bitwise manipulation is of 32-bit values.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#27
Set Utilities

16
36
-1
-1
-1
83
-1
24
7
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
0
9
0
0
36
4
4
2
2
Set Utilities
2
set_utilities
36
5
4
16
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

2
 diff*erence(set1, set2, ..., setn)
2
        => result of removing all elements of sets 2..n from set 1.
2
 exclusive_or(set, set, set, ...)
2
        => all elements that are contained in exactly one of the sets
2

2
 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
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#28
Builtin Function Help

16
36
-1
-1
-1
30
-1
23
1
init_for_core
2
173
-1
81
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()
91
4
20
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 returned.  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.
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 (num <x>)  => num
2

2
Returns the square root of <x>.  If <x> is negative, then `E_INVARG' is returned.
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 returned.  If <is-error> is provided and true, then <message> is marked in the server log as an error.
36
5
4
31
2
Syntax:  pass (<arg>,...)
2

2
Often, it is useful for a child object to define a verb that *augments*
2
the behavior of a verb on its parent object.  For example, the root object 
2
(an ancestor of every other object) defines a :description() verb that 
2
simply returns the value of `this.description'; 
2
this verb is used by the implementation of the `look' command.  
2
In many cases, a programmer would like the description of some object to
2
include some non-constant part; for example, a sentence about whether or not
2
the object was `awake' or `sleeping'.  This sentence should be added onto the
2
end of the normal description.  The programmer would like to have a means of
2
calling the normal `description' verb and then appending the sentence onto the
2
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
2
defined on the parent of the object that defines the current verb.  The
2
arguments given to the called verb are the ones given to pass() and the
2
returned value of the called verb is returned from the call to pass().
2
The initial value of `this' in the called verb is the same as in the
2
calling verb.
2

2
Thus, in the example above, the child-object's :description() verb might
2
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
2
result a sentence whose content is computed based on the value of a property on
2
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
52
2
Syntax:  open_network_connection (<value>, ...)   => obj
2

2
Establishes a network connection to the place specified by the arguments and
2
pretends that a new, normal player connection has been established from there.
2
The new connection, as usual, will not be logged in initially and will have a
2
negative object number associated with it for use with `read()',
2
`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
2
option was not used in building the server, then `E_PERM' is returned.  If
2
the network connection cannot be made for some reason, then other errors will
2
be returned, depending upon the particular network implementation in use.
2

2
For the BSD UNIX network implementation (the only publicly-available one as of
2
this writing), there must be two arguments, a string naming a host (possibly
2
using the numeric Internet syntax) and a number specifying a TCP port.  If a
2
connection cannot be made because the host does not exist, the port does not
2
exist, the host is not reachable or refused the connection, `E_INVARG' is
2
returned.  If the connection cannot be made for other reasons, including
2
resource limitations, then `E_QUOTA' is returned.
2

2
It is worth mentioning a couple of tricky points concerning the use of this
2
function.
2

2
Since the server treats the new connection like any other normal
2
player connection, it will naturally try to parse any input from that
2
connection as commands in the usual way.  To prevent this treatment, it is
2
necessary to ensure that some task is always suspended using `read()' on
2
the connection whenever the server considers a line of input from it.  That
2
way, the line of input will be given to that task instead of being parsed as a
2
command.  The only reliable way to ensure this is for the task that opens the
2
connection to enter an infinite loop reading from the connection.  One possible
2
structure for such a task is as follows:
2

2
    conn = open_network_connection(...);
2
    read(conn);
2
    while (1)
2
      line = read(conn);
2
      fork (0)
2
        this:handle_input(line);
2
      endfork
2
    endwhile
2

2
The first call to `read()' in this example is to discard the null line of
2
input always automatically supplied by the server for new connections; for more
2
details, see the discussion of `#0:do_login_command' in the section on
2
server commands and database assumptions.
2

2
The second fine point to be considered is that, unless the new connection
2
eventually `logs in' in the usual way for players, the server will impose its
2
usual five-minute timeout on it, shutting down the connection unless new input
2
arrives at least once every five minutes.
36
5
4
15
2
Syntax:  connection_name (obj <player>)   => str
2

2
Returns a network-specific string identifying the connection being used by the
2
given player.  If the programmer is not a wizard and not <player>, then
2
`E_PERM' is returned.  If <player> is not currently connected, then
2
`E_INVARG' is returned.
2

2
For the BSD UNIX network implementation (the only publicly-available one as of
2
this writing), the string has the form
2

2
    "<number> from <host>"
2

2
where <number> is a remarkably uninteresting internal server index and
2
<host> is either the name or decimal TCP address of the host from which the
2
player is connected.
36
5
4
5
2
Syntax:  shutdown (str <message>)   => none
2

2
Requests that the server shut itself down at its next opportunity.  Before
2
doing so, the given <message> is printed to all connected players.  If the
2
programmer is not a wizard, then `E_PERM' is returned.
36
5
4
7
2
Syntax:  dump_database ()   => none
2

2
Requests that the server checkpoint the database at its next opportunity.  It
2
is not normally necessary to call this function; the server automatically
2
checkpoints the database at regular intervals; see the chapter on server
2
assumptions about the database for details.  If the programmer is not a wizard,
2
then `E_PERM' is returned.
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
10
2
Syntax:  reset_max_object ()   => none
2

2
The server's idea of the highest object number ever used is changed to be the
2
highest object number of a currently-existing object, thus allowing reuse of
2
any higher numbers that refer to now-recycled objects.  If the programmer is
2
not a wizard, then `E_PERM' is returned.
2

2
This operation is intended for use in making new versions of the LambdaCore
2
database from the then-current LambdaMOO database, and other similar
2
situations.  Its use requires great care.
36
5
4
19
2
Syntax:  renumber (obj <object>)   => obj
2

2
The object number of the object currently numbered <object> is changed to
2
be the least nonnegative object number not currently in use and the new object
2
number is returned.  If <object> is not valid, then `E_INVARG' is
2
returned.  If the programmer is not a wizard, then `E_PERM' is returned.
2
If there are no unused nonnegative object numbers less than <object>, then
2
<object> is returned and no changes take place.
2

2
The references to <object> in the parent/children and location/contents
2
hierarchies are updated to use the new object number, and any verbs, properties
2
and/or objects owned by <object> are also changed to be owned by the new
2
object number.  The latter operation can be quite time consuming if the
2
database is large.  No other changes to the database are performed; in
2
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
2
database from the then-current LambdaMOO database, and other similar
2
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
8
2
Syntax:  output_delimiters (obj <player>)   => list
2

2
Returns a list of two strings, the current "output prefix" and "output
2
suffix" for <player>.  If <player> does not have an active network
2
connection, then `E_INVARG' is returned.  If either string is currently
2
undefined, the value `""' is used instead.  See the discussion of the
2
`PREFIX' and `SUFFIX' commands in the next chapter for more
2
information about the output prefix and suffix.
36
5
4
22
2
Syntax:  callers ()   => list
2

2
Returns information on each of the verbs currently waiting to resume execution
2
in the current task.  When one verb calls another verb, execution of the caller
2
is temporarily suspended, pending the called verb returning a value.  At any
2
given time, there could be several such pending verbs: the one that called the
2
currently executing verb, the verb that called that one, and so on.  The result
2
of `callers()' is a list, each element of which gives information about
2
one pending verb in the following format:
2

2
    {<this>, <verb-name>, <programmer>, <verb-loc>, <player>}
2

2
where <this> is the initial value of the variable `this' in that verb,
2
<verb-name> is the name used to invoke that verb,  <programmer> is
2
the player with whose permissions that verb is running, <verb-loc> is the
2
object on which that verb is defined, and <player> is the initial value of
2
the variable `player' in that verb.
2

2
The first element of the list returned by `callers()' gives information on
2
the verb that called the currently-executing verb, the second element describes
2
the verb that called that one, and so on.  The last element of the list
2
describes the first verb called in this task.
36
5
4
6
2
Syntax:  kill_task (num <task-id>)   => none
2

2
Removes the task with the given <task-id> from the queue of waiting tasks.
2
If the programmer is not the owner of that task and not a wizard, then
2
`E_PERM' is returned.  If there is no task on the queue with the given
2
<task-id>, then `E_INVARG' is returned.
36
5
4
25
2
Syntax:  queued_tasks ()   => list
2

2
Returns information on each of the forked, suspended or reading tasks owned by
2
the programmer (or, if the programmer is a wizard, all queued tasks).  The
2
returned value is a list of lists, each of which encodes certain information
2
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,
2
<start-time> is the time after which this task will begin execution (in
2
`time()' format), <ticks> is the number of ticks this task will have
2
when it starts (always 20,000 now), <clock-id> is a number whose value is
2
no longer interesting, <programmer> is the permissions with which this task
2
will begin execution (and also the player who "owns" this task),
2
<verb-loc> is the object on which the verb that forked this task was
2
defined at the time, <verb-name> is that name of that verb, <line> is
2
the number of the first line of the code in that verb that this task will
2
execute, and <this> is the value of the variable `this' in that verb.
2
For reading tasks, <start-time> is `-1'.
2

2
The <ticks> and <clock-id> fields are now obsolete and are retained
2
only for backward-compatibility reasons.  They may disappear in a future
2
version of the server.
36
5
4
46
2
Syntax:  read ([obj <player>])   => str
2

2
This function suspends the current task, waiting for a line of input from the
2
given <player> (which defaults to the player that typed the command that
2
initiated the current task), and resumes when such a line is received,
2
returning that line as a string.  Upon resumption, the task is given a full
2
quota of ticks and seconds.  `Read()' may only be called by a wizard, and,
2
unless <player> is given explicitly, only in a command task that has never
2
been suspended by a call to `suspend()'.  Otherwise, `E_PERM' is
2
returned.  If the given `player' is not currently connected and has no
2
pending lines of input, `read()' returns `E_INVARG'.
2

2
These restrictions on the use of `read()' without an explicit argument
2
preserve the following simple invariant: if input is being read from a player,
2
it is for the task started by the last command that player typed.  This
2
invariant adds responsibility to the programmer, however.  If your program
2
calls another verb before doing a `read()', then that verb must also not
2
suspend.  As an example, consider the following, which refers to the verbs
2
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
2
programmer's permissions.  However, the second `read()' in
2
`#0:read_twice_B' always fails and returns `E_PERM'.  This is because
2
the task was suspended, even though `#0:read_twice_B' did not do the
2
actual suspending.  Hence, if you want to read some input (by using
2
`read()' directly or by calling other verbs which do the reading), you
2
must make sure that the task is not suspended before the reading.  This
2
includes making sure that any verbs you call, directly or indirectly, also do
2
not suspend.
2

2
It is possible to call `read()' many times in the same command task, so
2
long as the task does not call `suspend()' or an explicit argument is
2
given.  The best use for `read()' with an explicit argument is in
2
conjunction with `open_network_connection()', if it is enabled.
36
5
4
61
2
Syntax:  suspend (num <seconds>)   => none
2

2
Suspends the current task, and resumes it after at least <seconds> seconds.
2
When the task is resumed, it will have a full quota of ticks and seconds.  This
2
function is useful for programs that run for a long time or require a lot of
2
ticks.  If <seconds> is negative, then `E_INVARG' is returned.
2

2
In some sense, this function forks the `rest' of the executing task.  However,
2
there is a major difference between the use of `suspend(<seconds>)'
2
and the use of the `fork (<seconds>)'.  The `fork' statement
2
creates a new task (a "forked task") while the currently-running task still
2
goes on to completion, but a `suspend()' suspends the currently-running
2
task (thus making it into a "suspended task").  This difference may be best
2
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
2
assign 1 to `#0.prop', call `#0:callee_A', fork a new task, return to
2
`#0:caller_A', and assign 2 to `#0.prop', ending this task.  Five
2
seconds later, if the forked task had not been killed, then it would begin to
2
run; it would assign 3 to `#0.prop' and then stop.  So, the final value of
2
`#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
2
`#0:callee_A'.  This task would assign 1 to `#0.prop', call
2
`#0:callee_B', and suspend.  Five seconds later, if the suspended task had
2
not been killed, then it would resume; it would assign 3 to `#0.prop',
2
return to `#0:caller', and assign 2 to `#0.prop', ending the task.
2
So, the final value of `#0.prop' (i.e., the value after more than 5
2
seconds) would be 2.
2

2
A suspended task, like a forked task, can be described by the
2
`queued_tasks()' function and killed by the `kill_task()' function.
2
Suspending a task does not change its task id.  A task can be suspended again
2
and again by successive calls to `suspend()'.
2

2
Once `suspend()' has been used in a particular task, then the
2
`read()' function will always return `E_PERM' in that task.  For more
2
details, see the description of `read()' below.
36
5
4
5
2
Syntax:  task_id ()   => num
2

2
Returns the numeric identifier for the currently-executing task.  Such numbers
2
are randomly selected for each task and can therefore safely be used in
2
circumstances where unpredictability is required.
36
5
4
2
2
*forward*
2
ticks_left()
36
5
4
7
2
Syntax:  ticks_left ()   => num
2
       seconds_left ()   => num
2

2
These two functions return the number of ticks or seconds (respectively) left
2
to the current task before it will be forcibly terminated.  These are useful,
2
for example, in deciding when to fork another task to continue a long-lived
2
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
10
2
Syntax:  set_task_perms (obj <player>)   => none
2

2
Changes the permissions with which the currently-executing verb is running to
2
be those of <player>.  If <player> is not a valid player object, then
2
`E_INVARG' is returned.  If the programmer is neither <player> nor a
2
wizard, then `E_PERM' is returned.
2

2
Note: This does not change the owner of the currently-running verb, only the
2
permissions of this particular invocation.  It is used in verbs owned by
2
wizards to make themselves run with lesser (usually non-wizard) permissions.
36
5
4
32
2
Syntax:  eval (str <string>)   => list
2

2
The MOO-code compiler processes <string> as if it were to be the program
2
associated with some verb and, if no errors are found, that fictional verb is
2
invoked.  If the programmer is not, in fact, a programmer, then `E_PERM'
2
is returned.  The normal result of calling `eval()' is a two element list.
2
The first element is true if there were no compilation errors and false
2
otherwise.  The second element is either the result returned from the fictional
2
verb (if there were no compilation errors) or a list of the compiler's error
2
messages (otherwise).
2

2
When the fictional verb is invoked, the various built-in variables have values
2
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
2
`d' permissions bit were on.
2

2
    eval("return 3 + 4;")   =>   {1, 7}
36
5
4
15
2
Syntax:  boot_player (obj <player>)   => none
2

2
Immediately terminates any currently-active connection to the given
2
<player>.  If the programmer is not either a wizard or the same as
2
<player>, then `E_PERM' is returned.  If there is no currently-active
2
connection to <player>, then this function does nothing.
2

2
If there was a currently-active connection, then the following two verb calls
2
are made before the connection is closed:
2

2
    <player>:disfunc()
2
    <player>.location:disfunc(<player>)
2

2
It is not an error if either of these verbs do not exist; the corresponding
2
call is simply skipped.
36
5
4
6
2
Syntax:  notify (obj <player>, str <string>)   => none
2

2
Outputs <string> (on a line by itself) to the user connected to the given
2
<player>.  If the programmer is not <player> or a wizard, then
2
`E_PERM' is returned.  If there is no currently-active connection to
2
<player>, then this function does nothing.
36
5
4
2
2
*forward*
2
connected_seconds()
36
5
4
7
2
Syntax:  connected_seconds (obj <player>)   => num
2
              idle_seconds (obj <player>)   => num
2

2
These functions return the number of seconds that the currently-active
2
connection to <player> has existed and been idle, respectively.  If
2
<player> is not the object number of a player object with a
2
currently-active connection, then `E_INVARG' is returned.
36
5
4
4
2
Syntax:  connected_players ()   => list
2

2
Returns a list of the object numbers of those player objects with
2
currently-active connections.
36
5
4
20
2
Syntax:  set_player_flag (obj <object>, <value>)   => none
2

2
Confers or removes the ``player object'' status of the given <object>,
2
depending upon the truth value of <value>.  If <object> is not valid,
2
`E_INVARG' is returned.  If the programmer is not a wizard, then
2
`E_PERM' is returned.
2

2
If <value> is true, then <object> gains (or keeps) ``player object''
2
status: it will be an element of the list returned by `players()', the
2
expression `is_player(<object>)' will return true, and users can
2
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
2
object'' status: it will not be an element of the list returned by
2
`players()', the expression `is_player(<object>)' will return
2
false, and users cannot connect to <object> by name when they log into the
2
server.  In addition, if a user is connected to <object> at the time that
2
it loses ``player object'' status, then that connection is immediately broken,
2
just as if `boot_player(<object>)' had been called (see the
2
description of `boot_player()' below).
36
5
4
4
2
Syntax:  is_player (obj <object>)   => num
2

2
Returns a true value if the given <object> is a player object and a false
2
value otherwise.  If <object> is not valid, `E_INVARG' is returned.
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
25
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
2
the verb named <verb-name> on <object>.  The program is represented as
2
a list of strings, one for each line of the program; this is the kind of value
2
returned by `verb_code()' and expected as the third argument to
2
`set_verb_code()'.  For `verb_code()', the expressions in the
2
returned code are usually written with the minimum-necessary parenthesization;
2
if <full-paren> is true, then all expressions are fully parenthesized.
2
Also for `verb_code()', the lines in the returned code are usually not
2
indented at all; if <indent> is true, each line is indented to better show
2
the nesting of statements.
2

2
If <object> is not valid, then `E_INVARG' is returned.  If
2
<object> does not define a verb named <verb-name>, then `E_VERBNF'
2
is returned.  If the programmer does not have read (write) permission on the
2
verb in question, then `verb_code()' (`set_verb_code()') returns
2
`E_PERM'.  If the programmer is not, in fact, a programmer, then
2
`E_PERM' is returned.
2

2
For `set_verb_code()', the result is a list of strings, the error messages
2
generated by the MOO-code compiler during processing of <code>.  If the
2
list is non-empty, then `set_verb_code()' did not install <code>; the
2
program associated with the verb in question is unchanged.
36
5
4
7
2
Syntax:  delete_verb (obj <object>, str <verb-name>)   => none
2

2
Removes the verb named <verb-name> from the given <object>.  If
2
<object> is not valid, then `E_INVARG' is returned.  If the programmer
2
does not have write permission on <object>, then `E_PERM' is returned.
2
If <object> does not define a verb named <verb-name>, then
2
`E_VERBNF' is returned.
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 returned.  If <object> does not define a verb named <verb-name>, then `E_VERBNF' is returned.  If the programmer does not have read (write) permission on the verb in question, then `verb_args()' (`set_verb_args()') returns `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()' returns `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
19
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
2
name(s) for the verb named <verb-name> on the given <object>.  If
2
<object> is not valid, then `E_INVARG' is returned.  If <object>
2
does not define a verb named <verb-name>, then `E_VERBNF' is returned.
2
If the programmer does not have read (write) permission on the verb in
2
question, then `verb_info()' (`set_verb_info()') returns
2
`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
2
characters from the set `r', `w', `x', and `d', and
2
<names> is a string.  This is the kind of value returned by
2
`verb_info()' and expected as the third argument to
2
`set_verb_info()'; the latter function returns `E_INVARG' if
2
<owner> is not valid or <perms> contains any illegal characters.
36
5
4
6
2
Syntax:  verbs (obj <object>)   => list
2

2
Returns a list of the names of the verbs defined directly on the given
2
<object>, not inherited from its parent.  If <object> is not valid,
2
then `E_INVARG' is returned.  If the programmer does not have read
2
permission on <object>, then `E_PERM' is returned.
36
5
4
8
2
Syntax:  delete_property (obj <object>, str <prop-name>)   => none
2

2
Removes the property named <prop-name> from the given <object> and all
2
of its descendants.  If <object> is not valid, then `E_INVARG' is
2
returned.  If the programmer does not have write permission on <object>,
2
then `E_PERM' is returned.  If <object> does not directly define a
2
property named <prop-name> (as opposed to inheriting one from its parent),
2
then `E_PROPNF' is returned.
36
5
4
12
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
2
descendants; the property is named <prop-name>, its initial value is
2
<value>, and its owner and initial permission bits are given by <info>
2
in the same format as is returned by `property_info()'.
2
If <object> is not valid or <object> already has a property named
2
<prop-name> or <info> does not specify a legitimate owner and
2
permission bits, then `E_INVARG' is retuned.  If the programmer does not
2
have write permission on <object> or if the owner specified by <info>
2
is not the programmer and the programmer is not a wizard, then `E_PERM' is
2
returned.
36
5
4
2
2
*forward*
2
property_info()
36
5
4
18
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
2
for the property named <prop-name> on the given <object>.  If
2
<object> is not valid, then `E_INVARG' is returned.  If <object>
2
has no non-built-in property named <prop-name>, then `E_PROPNF' is
2
returned.  If the programmer does not have read (write) permission on the
2
property in question, then `property_info()' (`set_property_info()')
2
returns `E_PERM'.  Property info has the following form:
2

2
    {<owner>, <perms>}
2

2
where <owner> is an object and <perms> is a string containing only
2
characters from the set `r', `w', and `c'.  This is the kind of
2
value returned by `property_info()' and expected as the third argument to
2
`set_property_info()'; the latter function returns `E_INVARG' if
2
<owner> is not valid or <perms> contains any illegal characters.
36
5
4
6
2
Syntax:  properties (obj <object>)   => list
2

2
Returns a list of the names of the properties defined directly on the given
2
<object>, not inherited from its parent.  If <object> is not valid,
2
then `E_INVARG' is returned.  If the programmer does not have read
2
permission on <object>, then `E_PERM' is returned.
36
5
4
41
2
Syntax:  move (obj <what>, obj <where>)   => none
2

2
Changes <what>'s location to be <where>.  This is a complex process
2
because a number of permissions checks and notifications must be performed.
2
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
2
object or `#-1' (denoting a location of 'nowhere'); otherwise
2
`E_INVARG' is returned.  The programmer must be either the owner of
2
<what> or a wizard; otherwise, `E_PERM' is returned.
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
2
false value and the programmer is not a wizard, then <where> is
2
considered to have refused entrance to <what>; `move()' returns
2
`E_NACC'.  If <where> does not define an `accept' verb, then it
2
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
2
hierarchy (i.e., <what> would contain itself, even indirectly), then
2
`E_RECMOVE' is returned instead.
2

2
The `location' property of <what> is changed to be <where>, and
2
the `contents' properties of the old and new locations are modified
2
appropriately.  Let <old-where> be the location of <what> before it was
2
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>
2
does not define a verb named `exitfunc'.  Finally, if <where> and
2
<what> are still valid objects, and <where> is still the location of
2
<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
2
<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
14
2
Syntax:  recycle (obj <object>)   => none
2

2
The given <object> is destroyed, irrevocably.  The programmer must either
2
own <object> or be a wizard; otherwise, `E_PERM' is returned.  If
2
<object> is not valid, then `E_INVARG' is returned.  The children of
2
<object> are reparented to the parent of <object>.  Before <object>
2
is recycled, each object in its contents is moved to `#-1' (implying a
2
call to <object>'s `exitfunc' verb, if any) and then <object>'s
2
`recycle' verb, if any, is called with no arguments.
2

2
After <object> is recycled, if the owner of the former object has a
2
property named `ownership_quota' and the value of that property is a
2
number, then `recycle()' treats that value as a "quota" and increments
2
it by one, storing the result back into the `ownership_quota' property.
36
5
4
2
2
*forward*
2
parent()
36
5
4
5
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>,
2
respectively.  If <object> is not valid, then `E_INVARG' is returned.
36
5
4
8
2
Syntax:  valid (obj <object>)   => num
2

2
Returns a non-zero number (i.e., a true value) if <object> is a valid
2
object (one that has been created and not yet recycled) and zero (i.e., a false
2
value) otherwise.
2

2
    valid(#0)    =>   1
2
    valid(#-1)   =>   0
36
5
4
27
2
Syntax:  chparent (obj <object>, obj <new-parent>)   => none
2

2
Changes the parent of <object> to be <new-parent>.  If the programmer
2
is neither a wizard or the owner of <object>, or if <new-parent> is not
2
fertile (i.e., its `f' bit is not set) and the programmer is neither the
2
owner of <new-parent> nor a wizard, then `E_PERM' is returned.  If
2
<object> is not valid or if <object> or one of its descendants defines
2
a property with the same name as one defined either on <new-parent> or on
2
one of its ancestors, then `E_INVARG' is returned.
2

2
Changing an object's parent can have the effect of removing some properties
2
from and adding some other properties to that object and all of its descendants
2
(i.e., its children and its children's children, etc.).  Let <common> be
2
the nearest ancestor that <object> and <new-parent> have in common
2
before the parent of <object> is changed.  Then all properties defined by
2
ancestors of <object> under <common> (that is, those ancestors of
2
<object> that are in turn descendants of <common>) are removed from
2
<object> and all of its descendants.  All properties defined by
2
<new-parent> or its ancestors under <common> are added to <object>
2
and all of its descendants.  As with `create()', the newly-added
2
properties are given the same permission bits as they have on <new-parent>,
2
the owner of each added property is either the owner of the object it's added
2
to (if the `c' permissions bit is set) or the owner of that property on
2
<new-parent>, and the value of each added property is "clear"; see the
2
description of the built-in function `clear_property()' for details.  All
2
properties that are not removed or added in the reparenting process are 
2
completely unchanged.
36
5
4
45
2
Syntax:  create (obj <parent> [, obj <owner>])   => obj
2

2
Creates and returns a new object whose parent is <parent> and whose owner
2
is as described below.  Either the given <parent> object must be fertile
2
(i.e., its `f' bit must be set) or else the programmer must own
2
<parent> or be a wizard; otherwise `E_PERM' is returned.
2
`E_PERM' is also returned if <owner> is provided and not the same as 
2
the programmer, unless the programmer is a wizard.  After the new object is
2
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
2
yet been used for a created object.  Note that no object number is ever reused,
2
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
2
provided), the new object itself (if <owner> was given as `#-1'), or
2
<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
2
<parent>.  These properties have the same permission bits as on
2
<parent>.  If the `c' permissions bit is set, then the owner of the
2
property on the new object is the same as the owner of the new object itself;
2
otherwise, the owner of the property on the new object is the same as that on
2
<parent>.  The initial value of every inherited property is "clear";
2
see the description of the built-in function `clear_property()' for
2
details.
2

2

2
If the intended owner of the new object has a property named
2
`ownership_quota' and the value of that property is a number, then
2
`create()' treats that value as a "quota".  If the quota is less than
2
or equal to zero, then the quota is considered to be exhausted and
2
`create()' returns `E_QUOTA' instead of creating an object.
2
Otherwise, the quota is decremented and stored back into the
2
`ownership_quota' property as a part of the creation of the new object.
36
5
4
2
2
*forward*
2
setadd()
36
5
4
16
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
2
appropriate.  `setadd()' only adds <value> if it is not already an
2
element of <list>; <list> is thus treated as a mathematical set.
2
<value> is added at the end of the resulting list, if at all.  Similarly,
2
`setremove()' returns a list identical to <list> if <value> is not
2
an element.  If <value> appears more than once in <list>, only the
2
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>, num <index>)   => list
2

2
Returns a copy of <list> with the <index>th element replaced by
2
<value>.  If <index> is not in the range
2
`[1..length(<list>)]', then `E_RANGE' is returned.
2

2
    x = {"foo", "bar", "baz"};
2
    listset(x, "mumble", 2)   =>   {"foo", "mumble", "baz"}
36
5
4
8
2
Syntax:  listdelete (list <list>, num <index>)   => list
2

2
Returns a copy of <list> with the <index>th element removed.  If
2
<index> is not in the range `[1..length(<list>)]', then
2
`E_RANGE' is returned.
2

2
    x = {"foo", "bar", "baz"};
2
    listdelete(x, 2)   =>   {"foo", "baz"}
36
5
4
27
2
Syntax:  listinsert (list <list>, <value> [, num <index>])   => list
2
         listappend (list <list>, <value> [, num <index>])   => list
2

2
These functions return a copy of <list> with <value> added as a new
2
element.  `listinsert()' and `listappend()' add <value> before
2
and after (respectively) the existing element with the given <index>, if
2
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>
2
at the end of the list and `listinsert()' adds it at the beginning; this
2
usage is discouraged, however, since the same intent can be more clearly
2
expressed using the list-construction expression, as shown in the examples
2
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
8
2
Syntax:  strcmp (str <str1>, str <str2>)   => num
2

2
Performs a case-sensitive comparison of the two argument strings.  If
2
<str1> is lexicographically less than <str2>, the
2
`strcmp()' returns a negative number.  If the two strings are
2
identical, `strcmp()' returns zero.  Otherwise, `strcmp()'
2
returns a positive number.  The ASCII character ordering is used for the
2
comparison.
36
5
4
2
2
*forward*
2
index()
36
5
4
15
2
Syntax:  index (str <str1>, str <str2> [, <case-matters>])   => num
2
        rindex (str <str1>, str <str2> [, <case-matters>])   => num
2

2
The function `index()' (`rindex()') returns the index of the first
2
character of the first (last) occurrence of <str2> in <str1>, or zero
2
if <str2> does not occur in <str1> at all.  By default the search for
2
an occurrence of <str2> is done while ignoring the upper/lower case
2
distinction.  If <case-matters> is provided and true, then case is treated
2
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
12
2
Syntax:  strsub (str <subject>, str <what>, str <with> [, <case-matters>])   => str
2

2
Replaces all occurrences in <subject> of <what> with <with>,
2
performing string substitution.  The occurrences are found from left to right
2
and all substitutions happen simultaneously.  By default, occurrences of
2
<what> are searched for while ignoring the upper/lower case distinction.
2
If <case-matters> is provided and true, then case is treated as significant
2
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>)   => num
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
17
2
Syntax:  ctime ([num <time>])   => str
2

2
Interprets <time> as a time, using the same representation as given in the
2
description of `time()', and converts it into a 28-character,
2
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
2
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
2
computer on which the MOO server is running.
36
5
4
4
2
Syntax:  time ()   => num
2

2
Returns the current time, represented as the number of seconds that have
2
elapsed since midnight on 1 January 1970, Greenwich Mean Time.
36
5
4
4
2
Syntax:  random (num <mod>)   => num
2

2
<mod> must be a positive number; otherwise, `E_INVARG' is returned.  A
2
number is chosen randomly from the range `[1..<mod>]' and returned.
36
5
4
4
2
Syntax:  abs (num <x>)   => num
2

2
Returns the absolute value of <x>.  If <x> is negative, then the result
2
is `-<x>'; otherwise, the result is <x>.
36
5
4
2
2
*forward*
2
min()
36
5
4
6
2
Syntax:  min (num <x>, ...)   => num
2
         max (num <x>, ...)   => num
2

2
These two functions return the smallest or largest of their arguments,
2
respectively.  All of the arguments must be numbers; otherwise `E_TYPE' is
2
returned.
36
5
4
10
2
Syntax:  toobj (<value>)   => obj
2

2
Converts the given MOO value into an object number and returns that object
2
number.  The conversions are very similar to those for `tonum()' except
2
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
14
2
Syntax:  tonum (<value>)   => num
2

2
Converts the given MOO value into a number and returns that number.  Object
2
numbers are converted into the equivalent numbers, strings are parsed as the
2
decimal encoding of a number, and errors are converted into numbers obeying the
2
same ordering (with respect to `<=' as the errors themselves.
2
`tonum()' returns `E_TYPE' if <value> is a list.  If <value>
2
is a string but the string does not contain a syntactically-correct number,
2
then `tonum()' returns 0.
2

2
    tonum(#34)         =>   34
2
    tonum("34")        =>   34
2
    tonum(" - 34  ")   =>  -34
2
    tonum(E_TYPE)      =>    1
36
5
4
15
2
Syntax:  tostr (<value>, ...)   => str
2

2
Converts all of the given MOO values into strings and returns the concatenation
2
of the results.
2

2
    tostr(17)                  =>   "17"
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
2
strings; all lists, including the empty list, are converted into the string
2
`"{list}"'.
36
5
4
14
2
Syntax:  typeof (<value>)   => num
2

2
Takes any MOO value and returns a number representing the type of <value>.
2
The result is the same as the initial value of one of these built-in variables:
2
`NUM', `STR', `LIST', `OBJ', or `ERR'.  Thus, one
2
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
15
2
Syntax:  clear_property (obj <object>, str <prop-name>)  => none
2
      is_clear_property (obj <object>, str <prop-name>)  => boolean
2

2
These two functions test for clear and set to clear, respectively, the property
2
named <prop-name> on the given <object>.  If <object> is not valid,
2
then `E_INVARG' is returned.  If <object> has no non-built-in property
2
named <prop-name>, then `E_PROPNF' is returned.  If the programmer
2
does not have read (write) permission on the property in question, then
2
`is_clear_property()' (`clear_property()') returns `E_PERM'.
2
If a property is clear, then when the value of that property is queried the
2
value of the parent's property of the same name is returned.  If the parent's
2
property is clear, then the parent's parent's value is examined, and so on.
2
If <object> is the definer of the property <prop-name>, as opposed to
2
an inheritor of the property, then `clear_property()' returns
2
`E_INVARG'.
36
5
4
2
2
*forward*
2
clear_property()
36
5
5
36
5
0
0
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
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#29
New-Prog-Log

0
36
45
-1
33
44
-1
14
7
init_for_core
2
173
-1
receive_message
36
173
-1
display_seq_headers display_seq_full
36
173
-1
from_msg_seq
36
173
-1
to_msg_seq
36
173
-1
%to_msg_seq subject_msg_seq
2
173
-1
%from_msg_seq
2
173
-1
0
26
4
0
36
5
0
0
36
1
4
0
36
0
0
1
36
5
2
%n (%#) can't send to moderated list %t (%[#t]) directly.
36
5
4
0
36
5
0
1
36
5
4
1
1
2
36
1
4
2
1
2
1
29
36
1
0
2592000
36
5
0
0
36
1
4
0
36
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
0
36
4
4
3
2
New-Prog-Log
2
New_Prog_Log
2
NPL
36
1
2
Record of who's been made a @programmer.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#30
Generic Help Database

144
36
-1
-1
-1
1
59
15
10
find_topics
2
165
-1
get_topic
2
165
-1
sort_topics
36
173
-1
columnize
36
173
-1
forward pass
36
165
-1
subst
36
173
-1
index
36
173
-1
initialize
2
173
-1
verbdoc
2
173
-1
dump_topic
2
165
-1
1
index
10
4
0
36
5
0
0
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
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#31
Generic Guest

16
36
-1
-1
-1
6
-1
4
23
@password
2
93
-2
boot
2
173
-1
disfunc
2
165
-1
defer
2
173
-1
mail_catch_up news_catch_up registration_nag
2
173
-1
create
36
89
-2
eject
36
173
-1
log
36
173
-1
confunc
2
173
-1
log_disconnect
2
165
-1
@last-c*onnection
2
29
-1
connection_name_hash
2
173
-1
my_huh
2
173
-1
@read
36
89
-2
set_current_folder
36
173
-1
@request*-character @register
2
89
11
init_for_core
2
173
-1
set_name set_aliases
2
173
-1
set_message
2
173
-1
description
2
173
-1
reset_stats
2
173
-1
public_connect_site
2
165
-1
attempt_action
115
173
-1
6
default_gender
default_description
request
extra_confunc_msg
settable_messages
settable_properties
182
2
neuter
36
1
2
A nameless wanderer of the great InterNet wasteland.
36
1
0
0
2
0
2

36
5
4
2
2
tag
2
title
36
5
4
17
2
paranoid
2
lines
2
responsible
2
linelen
2
linebuffer
2
brief
2
gaglist
2
rooms
2
pagelen
2
current_message
2
current_folder
2
messages
2
request
2
ic
2
following
2
ansi
2
saved_pages
36
5
5
36
1
5
2
0
5
36
5
2

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
36
5
5
2
1
5
36
4
5
2
0
5
2
0
5
36
4
0
30
36
4
5
36
5
5
36
5
5
36
5
5
36
5
2
%t (%[#t]) is a guest character.
2
1
5
36
4
5
36
4
5
2
1
0
0
100
0
0
0
2
0
5
36
5
5
2
1
5
115
1
5
36
5
5
36
5
5
100
1
5
2
0
5
36
5
5
2
1
5
36
1
5
36
5
5
115
1
5
36
0
5
36
5
5
115
1
5
115
1
5
36
0
5
115
1
5
115
0
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
5
36
5
5
115
1
4
2
1
-1
0
0
115
1
5
115
1
5
115
1
0
0
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
5
36
5
5
36
5
5
36
1
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
115
1
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
36
4
5
36
5
5
36
4
5
36
5
5
115
0
5
115
1
5
36
0
5
115
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
1
5
36
5
5
36
5
5
115
1
5
115
1
5
36
4
5
36
4
5
36
1
5
36
5
5
36
5
5
36
4
4
1
2
Generic Guest
2
1
2
A nameless wanderer of the great InterNet wasteland.
36
5
4
2
0
18025
0
950843368
100
1
5
36
5
5
36
5
0
0
36
5
5
36
5
5
115
1
#32
sequence utilities

16
36
-1
-1
-1
83
-1
27
18
add remove
36
173
-1
contains
36
173
-1
complement
36
173
-1
union intersection
36
173
-1
tostr
36
173
-1
for
2
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
contract
2
173
-1
0
9
0
0
36
4
4
3
2
sequence utilities
2
seq_utils
2
squ
36
5
4
35
2
A sequence is a set of integers (*)
2
This package supplies the following verbs:
2

2
  :add      (seq,f,t)  => seq with [f..t] interval added
2
  :remove   (seq,f,t)  => seq with [f..t] interval removed
2
  :range    (f,t)      => sequence corresponding to [f..t]
2
  {}                   => empty sequence
2
  :contains (seq,n)    => n in seq
2
  :size     (seq)      => number of elements in seq
2
  :first    (seq)      => first integer in seq or E_NONE
2
  :firstn   (seq,n)    => first n integers in seq (as a sequence)
2
  :last     (seq)      => last integer in seq  or E_NONE
2
  :lastn    (seq,n)    => last n integers in seq (as a sequence)
2

2
  :complement(seq)       => [-2147483648..2147483647] - seq
2
  :union    (seq,seq,...) 
2
  :intersect(seq,seq,...) 
2

2
  :extract(seq,array)           => array[@seq]
2
  :for([n,]seq,obj,verb,@args)  => for s in (seq) obj:verb(s,@args); endfor
2

2
  :tolist(seq)            => list corresponding to seq
2
  :tostr(seq)             => contents of seq as a string
2
  :from_list(list)        => sequence corresponding to list
2
  :from_sorted_list(list) => sequence corresponding to list (assumed sorted)
2
  :from_string(string)    => sequence corresponding to string
2

2
For boolean expressions, note that
2
  the representation of the empty sequence is {} (boolean FALSE) and
2
  all non-empty sequences are represented as nonempty lists (boolean TRUE).
2

2
The representation used works better than the usual list implementation for sets consisting of long uninterrupted ranges of integers.  
2
For sparse sets of integers the representation is decidedly non-optimal (though it never takes more than double the space of the usual list representation).
2

2
(*) Actually what this package implements is sets of integers-mod-2^32, but this assumes the underlying machine on which the server runs has 32-bit integers.  If not, you need to change this.maxneg to be the largest negative ("smallest"?) integer available.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#33
Quota-Log

0
36
45
-1
60
44
-1
29
1
init_for_core
2
173
-1
0
26
4
0
36
5
0
0
36
1
4
0
36
0
0
1
36
5
2
%n (%#) can't send to moderated list %t (%[#t]) directly.
36
5
4
0
36
5
0
1
36
5
4
1
1
2
36
1
4
2
1
2
1
33
36
1
0
2592000
36
5
0
0
36
1
4
0
36
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
0
36
4
4
3
2
Quota-Log
2
Quota_Log
2
QL
36
1
2
Record of whose quota has been messed with and why.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#34
Quota Utilities

16
100
-1
-1
-1
1
-1
161
35
initialize_quota
100
173
-1
init_for_core
2
173
-1
adjust_quota_for_programmer
100
173
-1
bi_create
2
173
-1
enable_create
2
173
-1
disable_create
100
173
-1
parse_create_args
100
173
-1
creation_permitted verb_addition_permitted property_addition_permitted
100
173
-1
all_characters
100
173
-1
display_quota
100
173
-1
get_quota
100
173
-1
charge_quota
100
173
-1
reimburse_quota
100
173
-1
set_quota
100
173
-1
get_size_quota
100
173
-1
display_quota_summary
100
173
-1
quota_remaining
100
173
-1
preliminary_reimburse_quota
100
173
-1
value_bytes
2
173
-1
object_bytes object_size
2
165
-1
do_summary
100
157
0
summarize_one_user
100
173
-1
recent_object_bytes
2
165
-1
can_peek
100
173
-1
can_touch
100
173
-1
do_breakdown
2
173
-1
object_overhead_bytes
100
173
-1
property_overhead_bytes
2
173
-1
verb_overhead_bytes
2
173
-1
bi_value_size bi_object_size
2
165
-1
indb_object_size
2
173
-1
indb_value_size
2
173
-1
measure_user
2
165
-1
measure_all_users
2
165
-1
measurement_task
2
173
-1
11
default_quota
large_negative_number
max_unmeasured
unmeasured_multiplier
working
cycle_days
task_time_limit
byte_based
exempted
unmeasured_exempt
measurement_task
20
4
4
0
50000
0
0
0
0
0
1
100
5
0
-10000
100
5
0
10
100
5
0
100
100
5
1
980
100
5
0
1
100
5
0
86400
100
5
0
1
100
5
4
3
1
25
1
16
1
39
100
5
4
4
1
36
1
125
1
113
1
101
100
1
0
1973668199
2
1
0
0
100
4
4
1
2
Quota Utilities
100
5
4
27
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
See help @measure and help @quota for the command line verbs.
100
5
4
2
0
26033
0
955495071
100
1
5
100
5
5
100
5
5
100
5
5
100
5
5
115
1
#35
you

16
36
-1
-1
-1
76
-1
78
3
say_action
36
165
-1
fixpos
36
173
-1
init_for_core
2
173
-1
1
conjugations
30
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
1
238
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
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
1
2
you
36
5
4
1
2
an object useful for pronoun substitution
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
0
0
36
5
5
36
5
5
115
1
#36
Hacker

19
36
-1
-1
-1
295
-1
71
0
0
185
4
0
36
1
0
1
36
5
0
0
36
5
5
36
5
0
48
36
5
0
0
2
1
4
0
2
0
5
36
5
5
36
0
5
36
1
5
2
0
5
36
5
5
2
0
5
2
1
0
0
36
5
5
36
5
5
36
1
5
36
1
4
0
36
0
5
36
1
5
36
5
4
107
1
13
1
14
1
16
1
17
1
18
1
19
1
23
1
25
1
26
1
27
1
28
1
29
1
30
1
31
1
32
1
33
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
54
1
58
1
59
1
62
1
63
1
64
1
65
1
66
1
67
1
68
1
70
1
71
1
73
1
74
1
77
1
98
1
99
1
105
1
119
1
120
1
124
1
126
1
128
1
130
1
134
1
136
1
137
1
138
1
139
1
144
1
145
1
146
1
148
1
150
1
152
1
153
1
156
1
163
1
167
1
171
1
172
1
176
1
181
1
182
1
184
1
189
1
192
1
193
1
205
1
207
1
208
1
210
1
213
1
214
1
229
1
230
1
231
1
232
1
233
1
234
1
235
1
236
1
237
1
238
1
239
1
297
1
298
1
299
1
300
1
301
1
302
1
304
1
305
1
306
1
307
1
308
2
1
1
18105
36
4
4
0
2
0
5
2
0
4
0
36
4
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
4
2
0
0
0
0
36
4
4
0
36
4
0
2147483647
2
1
0
15590
100
0
5
2
0
5
36
5
0
0
2
1
4
0
115
1
5
36
5
1
159
36
5
4
4
0
10000000
0
6889018
0
0
0
98953969
100
1
5
2
0
5
36
5
5
2
1
5
36
1
5
36
5
5
115
1
4
0
36
0
5
36
5
5
115
1
5
115
1
4
3
0
1219694
4
2
1
4744
1
66
4
2
4
1
4
2
2
hide_loc
0
0
4
1
2
thisonly
36
0
5
115
1
5
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
5
36
5
5
115
1
5
115
1
1
206
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
0
0
115
1
0
0
115
1
0
0
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
1
-1
36
5
5
36
5
5
36
1
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
115
1
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
36
4
5
36
5
5
36
4
5
36
5
5
115
0
5
115
1
5
36
0
5
115
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
1
5
36
5
5
36
5
5
115
1
5
115
1
5
36
4
5
36
4
5
36
1
5
36
5
5
36
5
5
36
4
4
1
2
Hacker
2
1
2
You see a player who should type '@describe me as ...'.
36
5
4
2
0
7370
0
950843373
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#37
Generic Database

144
36
-1
-1
-1
1
39
30
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
 
12
2
r
36
5
0
4
36
1
4
4
2

2

4
0
4
0
36
0
0
0
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
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#38
Nobody

19
36
-1
-1
-1
6
-1
31
4
eval
2
165
-1
moveto
36
173
-1
eval_d
2
165
-1
call_verb
2
173
-1
0
176
5
36
1
5
2
0
5
36
5
5
2
0
0
2147483647
2
1
0
0
36
5
5
36
5
5
36
1
5
36
1
4
0
36
0
5
36
1
5
36
5
4
0
2
1
5
36
4
4
0
2
0
5
2
0
4
0
36
4
5
36
4
5
36
5
5
36
5
2
... no one out there to see it.
36
5
5
36
5
4
1
1
-1
2
1
5
36
4
4
0
36
4
0
2147483647
2
1
0
0
100
0
5
2
0
5
36
5
0
0
2
1
4
0
115
1
5
36
5
1
159
36
5
4
4
0
10000000
0
0
0
955495071
0
0
100
1
5
2
0
5
36
5
5
2
1
5
36
1
5
36
5
5
115
1
4
0
36
0
5
36
5
5
115
1
5
115
1
4
3
0
1219694
4
1
1
4744
4
1
4
1
4
2
2
hide_loc
0
0
36
0
5
115
1
5
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
5
36
5
5
115
1
5
115
1
1
206
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
0
0
115
1
0
0
115
1
0
0
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
1
-1
36
5
5
36
5
5
36
1
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
115
1
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
36
4
5
36
5
5
36
4
5
36
5
5
115
0
5
115
1
5
36
0
5
115
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
1
5
36
5
5
36
5
5
115
1
5
115
1
5
36
4
5
36
4
5
36
1
5
36
5
5
36
5
5
36
4
4
2
2
Everyone
2
Everyman
2
1
2
... He never even got to go to Disneyland.
36
5
4
2
0
8008
0
950843368
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#39
Player Database

16
36
-1
-1
-1
37
-1
25
5
load
36
173
-1
check
36
45
-1
init_for_core
36
173
-1
available
36
173
-1
suspend_restart
36
173
-1
10
stupid_names
frozen
reserved
 H
 E
 G
 ev
 n
 b
 Ex
22
4
27
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
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

2
vx
4
0
4
0
36
1
4
4
2

2

4
4
2
GM
2
GameMaster
2
Geomancer
2
God
4
4
1
115
1
115
1
113
1
2
36
1
4
4
2
ery
2

4
2
2
Everyman
2
Everyone
4
2
1
38
1
38
36
1
4
4
2
o
2

4
2
2
nosnorb
2
Nobody
4
2
1
115
1
38
36
1
4
4
2

2

4
2
2
bronson
2
Banker
4
2
1
115
1
101
36
1
4
4
2
ecut
2

4
2
2
Executor
2
executioner_bird
4
2
1
139
1
115
36
1
5
36
5
5
36
1
4
4
2

2
HEGnb
4
5
2
PackRat
2
osbornn
2
Scrooge
2
Quota
2
Wizard
4
5
1
125
1
115
1
101
1
100
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
250707
0
941729004
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#40
gender utilities

16
36
-1
-1
-1
83
-1
32
5
set
2
165
-1
get_conj*ugation
36
165
-1
_verb_plural
36
173
-1
_verb_singular
36
165
-1
match_gender
36
173
-1
0
9
0
0
36
4
4
1
2
Gender_Utilities
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
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#41
time utilities

16
36
-1
-1
-1
83
-1
40
21
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
abbreviated_time abbr_time short_time
2
173
-1
time ctime
2
173
-1
timezone_offset
2
173
-1
_timezone_index
2
173
-1
ctime_gmt
36
173
-1
18
monthlens
timezones
stsd
ctcd
ct
corr
dayabbrs
days
months
monthabbrs
zones
time_units
hour
day
month
year
gmt_offset
week
27
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
25
4
0
4
0
4
2
2
AuEST
2
HST
4
1
2
AuCST
4
2
2
AuWST
2
PST
4
2
2
MST
2
PDT
4
2
2
CST
2
MDT
4
2
2
EST
2
CDT
4
2
2
AST
2
EDT
4
0
4
0
4
1
2
WET
4
2
2
GMT
2
GMT
4
1
2
WET
4
0
4
0
4
2
2
AST
2
EDT
4
2
2
EST
2
CDT
4
2
2
CST
2
MDT
4
2
2
MST
2
PDT
4
2
2
PST
2
AuWST
4
1
2
AuCST
4
2
2
HST
2
AuEST
4
0
4
0
36
5
4
7
4
4
0
31536000
2
year
2
years
2
yrs
4
5
0
2678400
2
month
2
months
2
mnths
2
mo
4
5
0
604800
2
week
2
weeks
2
wk
2
wks
4
5
0
86400
2
day
2
days
2
dy
2
dys
4
5
0
3600
2
hour
2
hours
2
hr
2
hrs
4
5
0
60
2
minute
2
minutes
2
min
2
mins
4
5
0
1
2
second
2
seconds
2
sec
2
secs
36
5
0
3600
36
1
0
86400
36
1
0
2592000
36
1
0
31536000
36
1
2
-0400
36
1
0
604800
36
1
0
0
36
4
4
2
2
time utilities
2
time
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, 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
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#42
permissions utilities

24
2
-1
-1
-1
83
-1
58
4
controls
2
173
-1
apply
36
173
-1
caller
2
173
-1
controls_prop controls_property
2
165
-1
0
9
0
0
2
4
5
2
5
5
2
5
4
2
0
2262
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#43
Editor Help

16
36
-1
-1
-1
30
-1
28
0
43
summary
edit-index
emote
say
delete
insert
view
depublish
perish
unpublish
publish
mode
enter
quit
unsubscribe
subscribe
reply-to
moo
find
edit
compile
save
showlists
subject
to
also-to
who
print
send
list
next
prev
subst
join
fill
move
copy
what
abort
done
pause
ranges
cc
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
5
36
5
0
0
36
4
4
1
2
Editor Help
36
5
0
0
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#44
Generic Mail Recipient

144
36
-1
-1
-1
1
33
37
38
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
165
-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 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
36
173
-1
length_date_gt
36
173
-1
rm_message_seq
36
173
-1
undo_rmm expunge_rmm renumber
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
36
173
-1
mail_names
36
173
-1
expire_old_messages
2
173
-1
moveto
36
173
-1
netmail_message_seq
2
173
-1
maybe_expire_old_messages
2
173
-1
do_expiration_notification
2
173
-1
get_expired_seq_desc
2
173
-1
fix_future_dates
2
173
-1
17
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
expire_half_msgs
expire_half_size
expire_half_days
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
0
1
36
5
4
0
36
1
2
%t (%[#t]) is a generic recipient.
36
1
0
2592000
36
5
0
0
36
1
4
0
36
0
0
1
36
5
0
0
36
5
0
0
36
5
0
0
36
5
0
0
36
5
0
0
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
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#45
Mail Distribution Center

16
36
-1
17
-1
1
-1
44
47
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
accept
36
173
-1
check_names
36
173
-1
match
36
173
-1
match_recipient
36
173
-1
match_failed
36
173
-1
make_message
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
165
-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
__convert_new
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
165
-1
expire_mail
2
13
-1
expire_mail_lists
2
173
-1
expiration_daemon
2
173
-1
name
36
173
-1
4
options
reserved_patterns
total_recipients
expire_mail_task
13
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
2
4
2
2
^Petition:%|^Ballot:%|^P:%|^B:
1
33842
4
2
2
^Dispute:%|^D:
1
34809
36
1
4
10
0
107140
0
6992
0
742
0
269
0
167
0
74
0
55
0
41
0
38
0
156
36
5
0
668671696
36
1
0
0
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
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#46
Mail Room

16
36
-1
-1
-1
49
-1
190
24
working_on
36
165
-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
to*:
36
89
-2
also*-to: cc*:
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
55
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
11
2
w*hat
2
subj*ect
2
to
2
also-to
2
reply-to
2
showlists,unsubscribe
2
who
2
pri*nt
2
send
2
abort
2
q*uit,done,pause
36
5
1
43
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
0
0
36
5
5
36
5
5
36
5
5
36
5
4
0
36
5
0
1
36
5
4
0
36
4
1
-1
36
5
0
386586018
36
5
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
0
0
113
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
1
2
Mail Room
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#47
Note Editor

16
36
-1
-1
-1
49
105
46
14
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
165
-1
set_note_text
2
165
-1
note_match_failed
36
173
-1
w*hat
36
9
-1
mode
36
25
-1
local_editing_info
36
173
-1
list_line
2
165
-1
lis*t view
2
81
-2
set_*
36
165
-1
2
strmode
objects
53
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
6
2
w*hat
2
mode
2
e*dit
2
save
2
abort
2
q*uit,done,pause
36
5
1
43
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
0
0
36
5
5
36
5
5
36
5
5
36
5
4
0
36
5
0
1
36
5
4
0
36
4
1
-1
36
5
0
413966915
36
5
4
0
36
4
5
36
5
5
36
5
5
36
5
5
36
5
0
0
113
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
Note Editor
2
nedit
36
5
4
0
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#48
Verb Editor

16
36
-1
-1
-1
49
-1
47
11
e*dit
36
25
-1
com*pile
36
73
-2
working_on
36
173
-1
init_session
36
173
-1
parse_invoke
36
173
-1
fetch_verb_code
2
165
-1
set_verb_code
2
165
-1
local_editing_info
2
173
-1
verb_name
2
165
-1
verb_args
2
165
-1
comment
36
89
-2
2
objects
verbnames
53
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
5
2
w*hat
2
e*dit
2
com*pile
2
abort
2
q*uit,done,pause
36
5
1
43
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
0
0
36
5
5
36
5
5
36
5
5
36
5
4
0
36
5
0
1
36
5
4
0
36
4
1
-1
36
5
0
347573887
36
5
4
0
36
4
5
36
5
5
36
5
5
36
5
5
36
5
0
0
113
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
4
2
Verb Editor
2
vedit
2
verbedit
2
verb edit
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#49
Generic Editor

144
36
-1
-1
-1
3
48
132
69
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
93
-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 exit
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
165
-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
accept
36
173
-1
enterfunc
36
173
-1
exitfunc
36
173
-1
@flush
36
105
-2
@stateprop
36
153
11
@rmstateprop
36
153
5
initialize
36
173
-1
init_for_core
2
173
-1
set_stateprops
36
165
-1
description
36
173
-1
commands_info
36
173
-1
match_object
36
173
-1
who_location_msg
36
165
-1
nothing_loaded_msg no_text_msg change_msg no_change_msg no_littering_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
tell_exits
2
173
-1
return_msg depart_msg
2
173
-1
dumb_join_lines
36
173
-1
subst_regexp
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
51
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
3
2
w*hat
2
abort
2
q*uit,done,pause
36
5
1
43
36
5
2
There are no lines of text.
36
5
4
15
4
2
2
say
2
<text>
4
2
2
emote
2
<text>
4
2
2
lis*t
2
[<range>] [nonum]
4
2
2
ins*ert
2
[<ins>] ["<text>]
4
2
2
n*ext,p*rev
2
[n] ["<text>]
4
2
2
del*ete
2
[<range>]
4
2
2
f*ind
2
/<str>[/[c][<range>]]
4
2
2
s*ubst
2
/<str1>/<str2>[/[g][c][<range>]]
4
2
2
m*ove,c*opy
2
[<range>] to <ins>
4
2
2
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

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
0
0
36
5
5
36
5
5
36
5
5
36
5
4
0
36
5
0
1
36
5
4
0
36
4
1
-1
36
5
0
1260718963
36
5
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
0
0
113
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
3
2
Generic Editor
2
gedit
2
edit
36
5
4
0
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#50
matching utilities

16
36
-1
-1
-1
83
-1
52
16
match_environment match_object
36
173
-1
match_nth
36
173
-1
match_verb
2
173
-1
match_list
36
165
-1
parse_ordinal_ref*erence
36
173
-1
parse_possessive_ref*erence
36
165
-1
match
36
165
-1
find_verb
2
165
-1
match_player
36
173
-1
match_room
36
165
-1
room_match_failed
2
173
-1
object_match_failed
2
165
-1
player_match_result player_match_failed
2
173
-1
match_player_or_object
36
173
-1
literal_object
36
173
-1
ppr_norx
36
165
-1
3
ordn
ordw
ordinal_regexp
12
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
0
0
36
4
4
1
2
matching utilities
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#51
object utilities

16
2
-1
-1
-1
83
-1
41
26
has_property
2
165
-1
all_properties
2
165
-1
has_verb
2
165
-1
has_callable_verb
2
165
-1
all_verbs
2
165
-1
match_verb
2
165
-1
isa
36
173
-1
ancestors
36
173
-1
descendants descendents
36
173
-1
descendants_suspended descendents_suspended
2
173
-1
ordered_descendants
36
173
-1
branches
36
173
-1
branches_suspended
2
173
-1
leaves
36
173
-1
leaves_suspended
2
173
-1
contains
36
173
-1
all_contents
36
173
-1
findable_properties
2
173
-1
owned_properties
2
173
-1
property_conflicts
2
165
-1
descendants_with_property_suspended
2
165
-1
locations
2
173
-1
all_properties_suspended
2
165
-1
connected
36
165
-1
has_message
2
165
-1
isany
36
173
-1
0
9
0
0
2
4
4
1
2
object utilities
2
5
4
26
2
These routines are useful for finding out information about individual objects.
2

2
Examining everything an object has defined on it:
2
  all_verbs          (object) => like it says
2
  all_properties     (object) => likewise
2
  findable_properties(object) => tests to see if caller can "find" them
2
  owned_properties   (object[, owner]) => tests for ownership
2

2
Investigating inheritance:
2
  ancestors(object[,object...]) => all ancestors
2
  descendants      (object)     => all descendants
2
  ordered_descendants(object)   => descendants, in a different order
2
  leaves           (object)     => descendants with no children
2
  branches         (object)     => descendants with children 
2
  isa        (object,class) => true iff object is a descendant of class (or ==)
2

2
Considering containment:
2
  contains      (obj1, obj2) => Does obj1 contain obj2 (nested)?
2
  all_contents      (object) => return all the (nested) contents of object
2

2
Verifying verbs and properties:
2
  has_property(object,pname) => false/true   according as object.(pname) exists
2
  has_verb    (object,vname) => false/{#obj} according as object:(vname) exists
2
  has_callable_verb          => same, but verb must be callable from a program
2
  match_verb  (object,vname) => false/{location, newvname}
2
                               (identify location and usable name of verb)
2
5
4
2
0
16124
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#52
lock utilities

16
2
-1
-1
-1
83
-1
26
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
4
player
input_index
input_length
input_string
13
1
5288
2
5
0
3
2
5
0
2
2
5
2
me
2
5
0
0
2
4
4
1
2
lock utilities
2
5
5
2
5
4
2
0
7782
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#53
generic letter

144
2
-1
-1
-1
9
-1
-1
3
burn
2
41
-1
burn_succeeded_msg oburn_succeeded_msg burn_failed_msg oburn_failed_msg
2
173
-1
do_burn
2
165
-1
4
oburn_succeeded_msg
oburn_failed_msg
burn_failed_msg
burn_succeeded_msg
50
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
36
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
1
5
2
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
36
4
4
2
2
generic letter
2
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
2866
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#54
list utilities

16
36
-1
-1
-1
83
-1
50
35
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
165
-1
iassoc
36
165
-1
iassoc_suspended
2
165
-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
random_element
2
173
-1
exclusive_list
2
173
-1
contains_only_these_types
36
173
-1
assoc_suspended
2
173
-1
merge
36
173
-1
1
nonstring_tell_lines
10
4
282
4
4
4
5
1
54875
2
tell_lines
1
2487
1
6
1
54875
4
5
1
48928
2
secure
1
30246
1
29557
1
54875
4
5
1
48928
2
arm
1
30246
1
29557
1
54875
4
5
1
48928
2
press
1
30246
1
29557
1
54875
4
4
4
5
1
46432
2
tell_lines
1
2487
1
6
1
46432
4
5
1
-1
2

1
46432
1
-1
1
46432
4
5
1
46432
2
eval_cmd_string
1
46432
1
217
1
46432
4
5
1
46432
2
eval
1
46432
1
217
1
46432
4
4
4
5
1
52775
2
tell_lines
1
2487
1
6
1
52775
4
5
1
-1
2

1
52775
1
-1
1
52775
4
5
1
52775
2
eval_cmd_string
1
52775
1
217
1
52775
4
5
1
52775
2
eval
1
52775
1
217
1
52775
4
3
4
5
1
30246
2
tell_lines
1
2487
1
6
1
30246
4
5
1
41114
2
look_self
1
53118
1
53931
1
30246
4
5
1
41114
2
look
1
4292
1
11825
1
30246
4
2
4
5
1
52775
2
tell_lines
1
2487
1
6
1
52775
4
5
1
52775
2
confunc
1
52775
1
52775
1
52775
4
4
4
5
1
1449
2
tell_lines
1
2487
1
6
1
1449
4
5
1
-1
2

1
1449
1
-1
1
1449
4
5
1
1449
2
eval_cmd_string
1
1449
1
217
1
1449
4
5
1
1449
2
eval
1
1449
1
217
1
1449
4
4
4
5
1
51902
2
tell_lines
1
2487
1
6
1
51902
4
5
1
53151
2
secure
1
30246
1
29557
1
51902
4
5
1
53151
2
arm
1
30246
1
29557
1
51902
4
5
1
53151
2
press
1
30246
1
29557
1
51902
4
6
4
5
1
56294
2
tell_lines
1
2487
1
6
1
8270
4
5
1
56294
2
receive_page
1
2487
1
6
1
8270
4
5
1
56294
2
receive_page
1
47
1
3133
1
8270
4
5
1
56294
2
receive_page
1
2487
1
7069
1
8270
4
5
1
56294
2
page
1
24442
1
5803
1
8270
4
5
1
8270
2
call
1
56294
1
7680
1
8270
4
6
4
5
1
56429
2
tell_lines
1
2487
1
6
1
56429
4
5
1
36284
2
@dox
1
19845
1
36284
1
56429
4
5
1
56429
2
my_huh
1
56429
1
6
1
56429
4
5
1
56429
2
my_huh
1
78
1
7069
1
56429
4
5
1
219
2
do_huh
1
56429
1
219
1
56429
4
5
1
26477
2
@dox
1
56429
1
1
1
56429
4
2
4
5
1
56824
2
tell_lines
1
2487
1
6
1
56824
4
5
1
36826
2
secure
1
30246
1
29557
1
56824
4
4
4
5
1
52407
2
tell_lines
1
2487
1
6
1
52407
4
5
1
187
2
secure
1
30246
1
29557
1
52407
4
5
1
187
2
arm
1
30246
1
29557
1
52407
4
5
1
187
2
press
1
30246
1
29557
1
52407
4
8
4
5
1
48961
2
tell_lines
1
2487
1
6
1
48961
4
5
1
36284
2
@inspect
1
19845
1
36284
1
48961
4
5
1
48961
2
my_huh
1
48961
1
6
1
48961
4
5
1
48961
2
my_huh
1
78
1
7069
1
48961
4
5
1
48961
2
my_huh
1
24442
1
26026
1
48961
4
5
1
48961
2
my_huh
1
57140
1
49900
1
48961
4
5
1
219
2
do_huh
1
48961
1
219
1
48961
4
5
1
39357
2
@inspect
1
48961
1
1
1
48961
4
4
4
5
1
50290
2
tell_lines
1
2487
1
6
1
50290
4
5
1
30744
2
secure
1
30246
1
29557
1
50290
4
5
1
30744
2
arm
1
30246
1
29557
1
50290
4
5
1
30744
2
press
1
30246
1
29557
1
50290
4
7
4
5
1
54099
2
tell_lines
1
2487
1
6
1
54099
4
5
1
30054
2
secure
1
30246
1
29557
1
54099
4
5
1
30054
2
arm
1
30246
1
29557
1
54099
4
5
1
30054
2
say_parse
1
30246
1
29557
1
54099
4
5
1
30054
2
announce
1
30246
1
29557
1
54099
4
5
1
54099
2
say
1
57140
1
49900
1
54099
4
5
1
54099
2
say
1
19845
1
5409
1
54099
4
9
4
5
1
53471
2
tell_lines
1
2487
1
6
1
53471
4
5
1
53914
2
secure
1
30246
1
29557
1
53471
4
5
1
53914
2
arm
1
30246
1
29557
1
53471
4
5
1
53914
2
say_parse
1
30246
1
29557
1
53471
4
5
1
53914
2
announce
1
30246
1
29557
1
53471
4
5
1
53914
2
say
1
2
1
3
1
53471
4
5
1
53914
2
say
1
48961
1
39357
1
53471
4
5
1
53471
2
say
1
57140
1
49900
1
53471
4
5
1
53471
2
say
1
19845
1
5409
1
53471
4
6
4
5
1
51626
2
tell_lines
1
2487
1
6
1
52739
4
5
1
51626
2
receive_page
1
2487
1
6
1
52739
4
5
1
51626
2
receive_page
1
47
1
3133
1
52739
4
5
1
51626
2
receive_page
1
2487
1
7069
1
52739
4
5
1
51626
2
page
1
24442
1
5803
1
52739
4
5
1
52739
2
call
1
56294
1
7680
1
52739
4
4
4
5
1
19845
2
tell_lines
1
2487
1
6
1
19845
4
5
1
-1
2

1
19845
1
-1
1
19845
4
5
1
19845
2
eval_cmd_string
1
19845
1
217
1
19845
4
5
1
19845
2
eval
1
19845
1
19845
1
19845
4
9
4
5
1
50459
2
tell_lines
1
2487
1
6
1
50459
4
5
1
54488
2
look_self
1
50459
1
54488
1
50459
4
5
1
54488
2
cell_enterfunc
1
19845
1
53993
1
50459
4
5
1
54488
2
cell_enterfunc
1
19845
1
35120
1
50459
4
5
1
54488
2
cell_enterfunc
1
19845
1
7970
1
50459
4
5
1
54488
2
cell_enterfunc
1
50459
1
54488
1
50459
4
5
1
54488
2
place_object
1
19845
1
53993
1
50459
4
5
1
54488
2
sw
1
19845
1
53993
1
50459
4
5
1
54488
2
sw
1
19845
1
5804
1
50459
4
9
4
5
1
50459
2
tell_lines
1
2487
1
6
1
50459
4
5
1
54488
2
look_self
1
50459
1
54488
1
50459
4
5
1
54488
2
cell_enterfunc
1
19845
1
53993
1
50459
4
5
1
54488
2
cell_enterfunc
1
19845
1
35120
1
50459
4
5
1
54488
2
cell_enterfunc
1
19845
1
7970
1
50459
4
5
1
54488
2
cell_enterfunc
1
50459
1
54488
1
50459
4
5
1
54488
2
place_object
1
19845
1
53993
1
50459
4
5
1
54488
2
w
1
19845
1
53993
1
50459
4
5
1
54488
2
w
1
19845
1
5804
1
50459
4
11
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
22566
2
look_self
1
15
1
6792
1
50636
4
5
1
22566
2
enterfunc
1
2
1
3
1
50636
4
5
1
22566
2
enterfunc
1
2693
1
258
1
50636
4
5
1
22566
2
enterfunc
1
50636
1
22566
1
50636
4
5
1
50636
2
moveto
1
50636
1
1
1
50636
4
5
1
50636
2
moveto
1
47
1
6
1
50636
4
5
1
50636
2
moveto
1
47
1
3133
1
50636
4
5
1
50636
2
moveto
1
78
1
7069
1
50636
4
5
1
50636
2
moveto
1
3685
1
8855
1
50636
4
5
1
5443
2
done
1
6336
1
5400
1
50636
4
7
4
5
1
34107
2
tell_lines
1
2487
1
6
1
34107
4
5
1
7944
2
secure
1
30246
1
29557
1
34107
4
5
1
7944
2
arm
1
30246
1
29557
1
34107
4
5
1
7944
2
say_parse
1
30246
1
29557
1
34107
4
5
1
7944
2
announce
1
30246
1
29557
1
34107
4
5
1
7944
2
say
1
2
1
3
1
34107
4
5
1
34107
2
say
1
31783
1
4803
1
34107
4
2
4
5
1
48961
2
tell_lines
1
2487
1
6
1
48961
4
5
1
2934
2
secure
1
30246
1
29557
1
48961
4
6
4
5
1
56824
2
tell_lines
1
2487
1
6
1
56824
4
5
1
36826
2
secure
1
30246
1
29557
1
56824
4
5
1
36826
2
arm
1
30246
1
29557
1
56824
4
5
1
36826
2
say_parse
1
30246
1
29557
1
56824
4
5
1
36826
2
announce
1
30246
1
29557
1
56824
4
5
1
56824
2
say
1
57140
1
49900
1
56824
4
8
4
5
1
48961
2
tell_lines
1
2487
1
6
1
48961
4
5
1
49519
2
secure
1
30246
1
29557
1
48961
4
5
1
49519
2
arm
1
30246
1
29557
1
48961
4
5
1
49519
2
say_parse
1
30246
1
29557
1
48961
4
5
1
49519
2
announce
1
30246
1
29557
1
48961
4
5
1
49519
2
announce
1
48961
1
39357
1
48961
4
5
1
48961
2
say
1
57140
1
49900
1
48961
4
5
1
48961
2
say
1
19845
1
5409
1
48961
4
7
4
5
1
56872
2
tell_lines
1
2487
1
6
1
56872
4
5
1
39439
2
secure
1
30246
1
29557
1
56872
4
5
1
39439
2
arm
1
30246
1
29557
1
56872
4
5
1
39439
2
say_parse
1
30246
1
29557
1
56872
4
5
1
39439
2
announce
1
30246
1
29557
1
56872
4
5
1
39439
2
say
1
2
1
3
1
56872
4
5
1
56872
2
say
1
31783
1
4803
1
56872
4
4
4
5
1
57014
2
tell_lines
1
2487
1
6
1
57014
4
5
1
55869
2
secure
1
30246
1
29557
1
57014
4
5
1
55869
2
arm
1
30246
1
29557
1
57014
4
5
1
55869
2
press
1
30246
1
29557
1
57014
4
3
4
5
1
52775
2
tell_lines
1
2487
1
6
1
52775
4
5
1
25465
2
look_self
1
2612
1
13131
1
52775
4
5
1
5443
2
l
1
2
1
3
1
52775
4
6
4
5
1
56819
2
tell_lines
1
2487
1
6
1
56819
4
5
1
52619
2
secure
1
30246
1
29557
1
56819
4
5
1
52619
2
arm
1
30246
1
29557
1
56819
4
5
1
52619
2
say_parse
1
30246
1
29557
1
56819
4
5
1
52619
2
announce
1
30246
1
29557
1
56819
4
5
1
56819
2
say
1
57140
1
49900
1
56819
4
6
4
5
1
56244
2
tell_lines
1
2487
1
6
1
56244
4
5
1
22110
2
secure
1
30246
1
29557
1
56244
4
5
1
22110
2
arm
1
30246
1
29557
1
56244
4
5
1
22110
2
say_parse
1
30246
1
29557
1
56244
4
5
1
22110
2
announce
1
30246
1
29557
1
56244
4
5
1
22110
2
say
1
2
1
3
1
56244
4
9
4
5
1
56429
2
tell_lines
1
2487
1
6
1
43852
4
5
1
56429
2
receive_page
1
2487
1
6
1
43852
4
5
1
56429
2
receive_page
1
47
1
3133
1
43852
4
5
1
56429
2
receive_page
1
2487
1
7069
1
43852
4
5
1
56429
2
page
1
24442
1
5803
1
43852
4
5
1
43852
2
call
1
41057
1
7680
1
43852
4
5
1
43852
2
do
1
78
1
7680
1
56429
4
5
1
43852
2
do
1
41057
1
7608
1
56429
4
5
1
43852
2
command
1
78
1
7680
1
56429
4
4
4
5
1
56815
2
tell_lines
1
2487
1
6
1
56815
4
5
1
54413
2
secure
1
30246
1
29557
1
56815
4
5
1
54413
2
arm
1
30246
1
29557
1
56815
4
5
1
54413
2
press
1
30246
1
29557
1
56815
4
6
4
5
1
57122
2
tell_lines
1
2487
1
6
1
57122
4
5
1
31891
2
secure
1
30246
1
29557
1
57122
4
5
1
31891
2
arm
1
30246
1
29557
1
57122
4
5
1
31891
2
say_parse
1
30246
1
29557
1
57122
4
5
1
31891
2
announce
1
30246
1
29557
1
57122
4
5
1
31891
2
say
1
2
1
3
1
57122
4
7
4
5
1
56815
2
tell_lines
1
2487
1
6
1
56815
4
5
1
54413
2
secure
1
30246
1
29557
1
56815
4
5
1
54413
2
arm
1
30246
1
29557
1
56815
4
5
1
54413
2
say_parse
1
30246
1
29557
1
56815
4
5
1
54413
2
announce
1
30246
1
29557
1
56815
4
5
1
56815
2
say
1
57140
1
49900
1
56815
4
5
1
56815
2
say
1
19845
1
5409
1
56815
4
9
4
5
1
57464
2
tell_lines
1
2487
1
6
1
53121
4
5
1
57464
2
receive_page
1
2487
1
6
1
53121
4
5
1
57464
2
receive_page
1
47
1
3133
1
53121
4
5
1
57464
2
receive_page
1
2487
1
7069
1
53121
4
5
1
57464
2
page
1
24442
1
5803
1
53121
4
5
1
53121
2
call
1
41057
1
7680
1
53121
4
5
1
53121
2
do
1
78
1
7680
1
57464
4
5
1
53121
2
do
1
41057
1
7608
1
57464
4
5
1
53121
2
command
1
78
1
7680
1
57464
4
4
4
5
1
35203
2
tell_lines
1
2487
1
6
1
35203
4
5
1
6946
2
secure
1
30246
1
29557
1
35203
4
5
1
6946
2
arm
1
30246
1
29557
1
35203
4
5
1
6946
2
press
1
30246
1
29557
1
35203
4
5
4
5
1
24436
2
tell_lines
1
2487
1
6
1
24436
4
5
1
6712
2
key
1
30246
1
6712
1
24436
4
5
1
-1
2

1
24436
1
-1
1
24436
4
5
1
24436
2
eval_cmd_string
1
24436
1
217
1
24436
4
5
1
24436
2
eval
1
24436
1
217
1
24436
4
2
4
5
1
24436
2
tell_lines
1
2487
1
6
1
24436
4
5
1
6686
2
key
1
30246
1
6686
1
24436
4
4
4
5
1
2957
2
tell_lines
1
2487
1
6
1
2957
4
5
1
-1
2

1
2957
1
-1
1
2957
4
5
1
2957
2
eval_cmd_string
1
2957
1
217
1
2957
4
5
1
2957
2
eval
1
2957
1
217
1
2957
4
7
4
5
1
50624
2
tell_lines
1
2487
1
6
1
50624
4
5
1
40295
2
secure
1
30246
1
29557
1
50624
4
5
1
40295
2
arm
1
30246
1
29557
1
50624
4
5
1
40295
2
say_parse
1
30246
1
29557
1
50624
4
5
1
40295
2
announce
1
30246
1
29557
1
50624
4
5
1
40295
2
say
1
2
1
3
1
50624
4
5
1
50624
2
say
1
31783
1
4803
1
50624
4
4
4
5
1
53465
2
tell_lines
1
2487
1
6
1
53465
4
5
1
5008
2
secure
1
30246
1
29557
1
53465
4
5
1
5008
2
arm
1
30246
1
29557
1
53465
4
5
1
5008
2
press
1
30246
1
29557
1
53465
4
2
4
5
1
50459
2
tell_lines
1
2487
1
6
1
50459
4
5
1
9200
2
about
1
50459
1
9200
1
50459
4
4
4
5
1
46432
2
tell_lines
1
2487
1
6
1
46432
4
5
1
11853
2
secure
1
30246
1
29557
1
46432
4
5
1
11853
2
arm
1
30246
1
29557
1
46432
4
5
1
11853
2
press
1
30246
1
29557
1
46432
4
8
4
5
1
56968
2
tell_lines
1
2487
1
6
1
56968
4
5
1
36284
2
@dox
1
19845
1
36284
1
56968
4
5
1
56968
2
my_huh
1
56968
1
6
1
56968
4
5
1
56968
2
my_huh
1
78
1
7069
1
56968
4
5
1
56968
2
my_huh
1
24442
1
26026
1
56968
4
5
1
56968
2
my_huh
1
57140
1
49900
1
56968
4
5
1
219
2
do_huh
1
56968
1
219
1
56968
4
5
1
56977
2
@dox
1
56968
1
1
1
56968
4
7
4
5
1
54837
2
tell_lines
1
2487
1
6
1
54837
4
5
1
38607
2
secure
1
30246
1
29557
1
54837
4
5
1
38607
2
arm
1
30246
1
29557
1
54837
4
5
1
38607
2
say_parse
1
30246
1
29557
1
54837
4
5
1
38607
2
announce
1
30246
1
29557
1
54837
4
5
1
38607
2
say
1
2
1
3
1
54837
4
5
1
54837
2
say
1
31783
1
4803
1
54837
4
7
4
5
1
53702
2
tell_lines
1
2487
1
6
1
53702
4
5
1
42348
2
secure
1
30246
1
29557
1
53702
4
5
1
42348
2
arm
1
30246
1
29557
1
53702
4
5
1
42348
2
say_parse
1
30246
1
29557
1
53702
4
5
1
42348
2
announce
1
30246
1
29557
1
53702
4
5
1
42348
2
announce
1
48961
1
39357
1
53702
4
5
1
53702
2
say
1
57140
1
49900
1
53702
4
4
4
5
1
34107
2
tell_lines
1
2487
1
6
1
34107
4
5
1
7944
2
secure
1
30246
1
29557
1
34107
4
5
1
7944
2
arm
1
30246
1
29557
1
34107
4
5
1
7944
2
press
1
30246
1
29557
1
34107
4
4
4
5
1
50459
2
tell_lines
1
2487
1
6
1
50459
4
5
1
-1
2

1
50459
1
-1
1
50459
4
5
1
50459
2
eval_cmd_string
1
50459
1
217
1
50459
4
5
1
50459
2
eval
1
50459
1
217
1
50459
4
4
4
5
1
49397
2
tell_lines
1
2487
1
6
1
50459
4
5
1
-1
2

1
50459
1
-1
1
50459
4
5
1
50459
2
eval_cmd_string
1
50459
1
217
1
50459
4
5
1
50459
2
eval
1
50459
1
217
1
50459
4
2
4
5
1
30246
2
tell_lines
1
2487
1
6
1
30246
4
5
1
30246
2
debug
1
30246
1
30246
1
30246
4
2
4
5
1
54699
2
tell_lines
1
2487
1
6
1
54699
4
5
1
54699
2
sgrep
1
54699
1
54699
1
54699
4
7
4
5
1
57421
2
tell_lines
1
2487
1
6
1
57421
4
5
1
51426
2
secure
1
49551
1
29557
1
57421
4
5
1
51426
2
arm
1
49551
1
29557
1
57421
4
5
1
51426
2
say_parse
1
49551
1
29557
1
57421
4
5
1
51426
2
announce
1
49551
1
29557
1
57421
4
5
1
57421
2
say
1
57140
1
49900
1
57421
4
5
1
57421
2
say
1
19845
1
5409
1
57421
4
2
4
5
1
56815
2
tell_lines
1
2487
1
6
1
56815
4
5
1
56516
2
secure
1
49551
1
29557
1
56815
4
6
4
5
1
55164
2
tell_lines
1
2487
1
6
1
55164
4
5
1
54023
2
secure
1
49551
1
29557
1
55164
4
5
1
54023
2
arm
1
49551
1
29557
1
55164
4
5
1
54023
2
say_parse
1
49551
1
29557
1
55164
4
5
1
54023
2
announce
1
49551
1
29557
1
55164
4
5
1
55164
2
say
1
57140
1
49900
1
55164
4
4
4
5
1
53502
2
tell_lines
1
2487
1
6
1
53502
4
5
1
53245
2
secure
1
49551
1
29557
1
53502
4
5
1
53245
2
arm
1
49551
1
29557
1
53502
4
5
1
53245
2
press
1
49551
1
29557
1
53502
4
4
4
5
1
53464
2
tell_lines
1
2487
1
6
1
53464
4
5
1
30362
2
secure
1
49551
1
29557
1
53464
4
5
1
30362
2
arm
1
49551
1
29557
1
53464
4
5
1
30362
2
press
1
49551
1
29557
1
53464
4
7
4
5
1
50064
2
tell_lines
1
2487
1
6
1
50064
4
5
1
22709
2
secure
1
49551
1
29557
1
50064
4
5
1
22709
2
arm
1
49551
1
29557
1
50064
4
5
1
22709
2
say_parse
1
49551
1
29557
1
50064
4
5
1
22709
2
announce
1
49551
1
29557
1
50064
4
5
1
22709
2
announce
1
48961
1
39357
1
50064
4
5
1
50064
2
say
1
57140
1
49900
1
50064
4
7
4
5
1
34107
2
tell_lines
1
2487
1
6
1
34107
4
5
1
7944
2
secure
1
49551
1
29557
1
34107
4
5
1
7944
2
arm
1
49551
1
29557
1
34107
4
5
1
7944
2
say_parse
1
49551
1
29557
1
34107
4
5
1
7944
2
announce
1
49551
1
29557
1
34107
4
5
1
7944
2
say
1
2
1
3
1
34107
4
5
1
34107
2
say
1
31783
1
4803
1
34107
4
3
4
5
1
30246
2
tell_lines
1
2487
1
6
1
30246
4
5
1
53622
2
look_self
1
15
1
6792
1
30246
4
5
1
53622
2
look
1
2693
1
258
1
30246
4
4
4
5
1
50290
2
tell_lines
1
2487
1
6
1
50290
4
5
1
53622
2
look_self
1
15
1
6792
1
50290
4
5
1
27391
2
l
1
2
1
3
1
50290
4
5
1
27391
2
l
1
4290
1
6145
1
50290
4
4
4
5
1
54377
2
tell_lines
1
2487
1
6
1
54377
4
5
1
53646
2
secure
1
49551
1
29557
1
54377
4
5
1
53646
2
arm
1
49551
1
29557
1
54377
4
5
1
53646
2
press
1
49551
1
29557
1
54377
4
4
4
5
1
30246
2
tell_lines
1
2487
1
6
1
30246
4
5
1
53756
2
event_enterfunc
1
30246
1
53756
1
30246
4
5
1
54318
2
invoke
1
30246
1
54318
1
30246
4
5
1
53622
2
d
1
7003
1
16020
1
30246
4
8
4
5
1
46102
2
tell_lines
1
2487
1
6
1
46102
4
5
1
26811
2
look_self
1
46102
1
26811
1
46102
4
5
1
26811
2
cell_enterfunc
1
19845
1
53993
1
46102
4
5
1
26811
2
cell_enterfunc
1
19845
1
35120
1
46102
4
5
1
26811
2
cell_enterfunc
1
19845
1
7970
1
46102
4
5
1
26811
2
place_object
1
19845
1
53993
1
46102
4
5
1
26811
2
n
1
19845
1
53993
1
46102
4
5
1
26811
2
n
1
19845
1
5804
1
46102
4
16
4
5
1
46102
2
tell_lines
1
2487
1
6
1
46102
4
5
1
26811
2
look_self
1
46102
1
26811
1
46102
4
5
1
26811
2
cell_enterfunc
1
19845
1
53993
1
46102
4
5
1
26811
2
cell_enterfunc
1
19845
1
35120
1
46102
4
5
1
26811
2
cell_enterfunc
1
19845
1
7970
1
46102
4
5
1
26811
2
place_object
1
19845
1
53993
1
46102
4
5
1
26811
2
enterfunc
1
19845
1
53993
1
46102
4
5
1
26811
2
enterfunc
1
19845
1
7970
1
46102
4
5
1
46102
2
moveto
1
46102
1
1
1
46102
4
5
1
46102
2
moveto
1
47
1
6
1
46102
4
5
1
46102
2
moveto
1
47
1
3133
1
46102
4
5
1
46102
2
moveto
1
78
1
7069
1
46102
4
5
1
46102
2
moveto
1
3685
1
8855
1
46102
4
5
1
46102
2
teleport
1
47
1
3133
1
46102
4
5
1
46102
2
teleport
1
3920
1
33337
1
46102
4
5
1
46102
2
@go
1
47
1
3133
1
46102
4
7
4
5
1
56872
2
tell_lines
1
2487
1
6
1
56872
4
5
1
39439
2
secure
1
49551
1
29557
1
56872
4
5
1
39439
2
arm
1
49551
1
29557
1
56872
4
5
1
39439
2
say_parse
1
49551
1
29557
1
56872
4
5
1
39439
2
announce
1
49551
1
29557
1
56872
4
5
1
39439
2
say
1
2
1
3
1
56872
4
5
1
56872
2
say
1
31783
1
4803
1
56872
4
2
4
5
1
53456
2
tell_lines
1
2487
1
6
1
53456
4
5
1
35807
2
secure
1
30246
1
16017
1
53456
4
4
4
5
1
2487
2
tell_lines
1
2487
1
6
1
2487
4
5
1
-1
2

1
2487
1
-1
1
2487
4
5
1
2487
2
eval_cmd_string
1
2487
1
217
1
2487
4
5
1
2487
2
eval
1
2487
1
217
1
2487
4
4
4
5
1
50432
2
tell_lines
1
2487
1
6
1
50432
4
5
1
39785
2
secure
1
49551
1
29557
1
50432
4
5
1
39785
2
arm
1
49551
1
29557
1
50432
4
5
1
39785
2
press
1
49551
1
29557
1
50432
4
2
4
5
1
55689
2
tell_lines
1
2487
1
6
1
55689
4
5
1
39285
2
secure
1
49551
1
29557
1
55689
4
6
4
5
1
56289
2
tell_lines
1
2487
1
6
1
56289
4
5
1
44585
2
secure
1
49551
1
29557
1
56289
4
5
1
44585
2
arm
1
49551
1
29557
1
56289
4
5
1
44585
2
say_parse
1
49551
1
29557
1
56289
4
5
1
44585
2
announce
1
49551
1
29557
1
56289
4
5
1
56289
2
say
1
57140
1
49900
1
56289
4
3
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
50636
2
look_self
1
15
1
7069
1
50636
4
5
1
22929
2
look
1
2693
1
258
1
50636
4
4
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
50636
2
look_self
1
15
1
7069
1
50636
4
5
1
52044
2
look
1
2
1
3
1
50636
4
5
1
52044
2
look
1
4290
1
6145
1
50636
4
4
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
50636
2
look_self
1
15
1
7069
1
50636
4
5
1
52044
2
look
1
2
1
3
1
50636
4
5
1
52044
2
look
1
4290
1
6145
1
50636
4
8
4
5
1
50051
2
tell_lines
1
2487
1
6
1
50051
4
5
1
18807
2
@peruse
1
31783
1
18807
1
50051
4
5
1
50051
2
my_huh
1
50051
1
6
1
50051
4
5
1
50051
2
my_huh
1
78
1
7069
1
50051
4
5
1
50051
2
my_huh
1
24442
1
26026
1
50051
4
5
1
50051
2
my_huh
1
57140
1
49900
1
50051
4
5
1
219
2
do_huh
1
50051
1
219
1
50051
4
5
1
20242
2
@peruse
1
50051
1
1
1
50051
4
4
4
5
1
53562
2
tell_lines
1
2487
1
6
1
53562
4
5
1
50636
2
look_self
1
15
1
7069
1
53562
4
5
1
29038
2
special_look
1
31783
1
9805
1
53562
4
5
1
29038
2
look
1
31783
1
9805
1
53562
4
4
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
50636
2
look_self
1
15
1
7069
1
50636
4
5
1
29038
2
special_look
1
31783
1
9805
1
50636
4
5
1
29038
2
look
1
31783
1
9805
1
50636
4
4
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
50636
2
look_self
1
15
1
7069
1
50636
4
5
1
52044
2
look
1
2
1
3
1
50636
4
5
1
52044
2
look
1
4290
1
6145
1
50636
4
4
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
50636
2
look_self
1
15
1
7069
1
50636
4
5
1
29038
2
special_look
1
31783
1
9805
1
50636
4
5
1
29038
2
look
1
31783
1
9805
1
50636
4
4
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
50636
2
look_self
1
15
1
7069
1
50636
4
5
1
29038
2
special_look
1
31783
1
9805
1
50636
4
5
1
29038
2
look
1
31783
1
9805
1
50636
4
4
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
50636
2
look_self
1
15
1
7069
1
50636
4
5
1
52044
2
look
1
2
1
3
1
50636
4
5
1
52044
2
look
1
4290
1
6145
1
50636
4
7
4
5
1
54837
2
tell_lines
1
2487
1
6
1
54837
4
5
1
38607
2
secure
1
49551
1
29557
1
54837
4
5
1
38607
2
arm
1
49551
1
29557
1
54837
4
5
1
38607
2
say_parse
1
49551
1
29557
1
54837
4
5
1
38607
2
announce
1
49551
1
29557
1
54837
4
5
1
38607
2
say
1
2
1
3
1
54837
4
5
1
54837
2
say
1
31783
1
4803
1
54837
4
4
4
5
1
56819
2
tell_lines
1
2487
1
6
1
56819
4
5
1
48003
2
secure
1
49551
1
29557
1
56819
4
5
1
48003
2
arm
1
49551
1
29557
1
56819
4
5
1
48003
2
press
1
49551
1
29557
1
56819
4
2
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
50636
2
@refusals
1
50636
1
50636
1
50636
4
2
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
50636
2
@refusals
1
50636
1
50636
1
50636
4
2
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
50636
2
@refusals
1
50636
1
50636
1
50636
4
2
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
50636
2
@refusals
1
50636
1
50636
1
50636
4
2
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
50636
2
@refusals
1
50636
1
50636
1
50636
4
8
4
5
1
49551
2
tell_lines
1
2487
1
6
1
49551
4
5
1
29557
2
_darkness
1
49551
1
29557
1
49551
4
5
1
19233
2
int_desc
1
49551
1
29557
1
49551
4
5
1
19233
2
description
1
49551
1
29557
1
49551
4
5
1
19233
2
sitting_description
1
31783
1
9805
1
49551
4
5
1
19233
2
look_self
1
31783
1
9805
1
49551
4
5
1
19233
2
look
1
31783
1
9805
1
49551
4
5
1
19233
2
look
1
49551
1
29557
1
49551
4
7
4
5
1
56872
2
tell_lines
1
2487
1
6
1
56872
4
5
1
39439
2
secure
1
49551
1
29557
1
56872
4
5
1
39439
2
arm
1
49551
1
29557
1
56872
4
5
1
39439
2
say_parse
1
49551
1
29557
1
56872
4
5
1
39439
2
announce
1
49551
1
29557
1
56872
4
5
1
39439
2
say
1
2
1
3
1
56872
4
5
1
56872
2
say
1
31783
1
4803
1
56872
4
7
4
5
1
53592
2
tell_lines
1
2487
1
6
1
53592
4
5
1
35434
2
secure
1
49551
1
29557
1
53592
4
5
1
35434
2
arm
1
49551
1
29557
1
53592
4
5
1
35434
2
say_parse
1
49551
1
29557
1
53592
4
5
1
35434
2
announce
1
49551
1
29557
1
53592
4
5
1
53592
2
say
1
57140
1
49900
1
53592
4
5
1
53592
2
say
1
19845
1
5409
1
53592
4
10
4
5
1
53592
2
tell_lines
1
2487
1
6
1
53592
4
5
1
16553
2
secure
1
30246
1
16017
1
53592
4
5
1
16553
2
arm
1
30246
1
16017
1
53592
4
5
1
16553
2
say_parse
1
30246
1
16017
1
53592
4
5
1
16553
2
announce_all
1
30246
1
16017
1
53592
4
5
1
16553
2
tell
1
4290
1
6145
1
53592
4
5
1
35434
2
announce
1
31783
1
9805
1
53592
4
5
1
35434
2
announce
1
49551
1
29557
1
53592
4
5
1
53592
2
say
1
57140
1
49900
1
53592
4
5
1
53592
2
say
1
19845
1
5409
1
53592
4
7
4
5
1
53592
2
tell_lines
1
2487
1
6
1
53592
4
5
1
35434
2
secure
1
49551
1
29557
1
53592
4
5
1
35434
2
arm
1
49551
1
29557
1
53592
4
5
1
35434
2
say_parse
1
49551
1
29557
1
53592
4
5
1
35434
2
announce
1
49551
1
29557
1
53592
4
5
1
53592
2
say
1
57140
1
49900
1
53592
4
5
1
53592
2
say
1
19845
1
5409
1
53592
4
2
4
5
1
53592
2
tell_lines
1
2487
1
6
1
53592
4
5
1
35434
2
secure
1
49551
1
29557
1
53592
4
6
4
5
1
57810
2
tell_lines
1
2487
1
6
1
57810
4
5
1
25980
2
secure
1
49551
1
29557
1
57810
4
5
1
25980
2
arm
1
49551
1
29557
1
57810
4
5
1
25980
2
say_parse
1
49551
1
29557
1
57810
4
5
1
25980
2
announce
1
49551
1
29557
1
57810
4
5
1
25980
2
say
1
2
1
3
1
57810
4
4
4
5
1
51011
2
tell_lines
1
2487
1
6
1
51011
4
5
1
23829
2
secure
1
49551
1
29557
1
51011
4
5
1
23829
2
arm
1
49551
1
29557
1
51011
4
5
1
23829
2
press
1
49551
1
29557
1
51011
4
6
4
5
1
57490
2
tell_lines
1
2487
1
6
1
57490
4
5
1
40548
2
secure
1
49551
1
29557
1
57490
4
5
1
40548
2
arm
1
49551
1
29557
1
57490
4
5
1
40548
2
say_parse
1
49551
1
29557
1
57490
4
5
1
40548
2
announce
1
49551
1
29557
1
57490
4
5
1
57490
2
say
1
57140
1
49900
1
57490
4
7
4
5
1
49397
2
tell_lines
1
2487
1
6
1
49397
4
5
1
7832
2
secure
1
49551
1
29557
1
49397
4
5
1
7832
2
arm
1
49551
1
29557
1
49397
4
5
1
7832
2
say_parse
1
49551
1
29557
1
49397
4
5
1
7832
2
announce
1
49551
1
29557
1
49397
4
5
1
49397
2
say
1
57140
1
49900
1
49397
4
5
1
49397
2
say
1
19845
1
5409
1
49397
4
8
4
5
1
50823
2
tell_lines
1
2487
1
6
1
50823
4
5
1
36284
2
@inspect
1
19845
1
36284
1
50823
4
5
1
50823
2
my_huh
1
50823
1
6
1
50823
4
5
1
50823
2
my_huh
1
78
1
7069
1
50823
4
5
1
50823
2
my_huh
1
24442
1
26026
1
50823
4
5
1
50823
2
my_huh
1
57140
1
49900
1
50823
4
5
1
219
2
do_huh
1
50823
1
219
1
50823
4
5
1
17
2
@inspect
1
50823
1
1
1
50823
4
4
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
50636
2
look_self
1
2
1
1
1
50636
4
5
1
50636
2
look_self
1
2
1
6
1
50636
4
5
1
5443
2
look
1
2
1
3
1
50636
4
7
4
5
1
50204
2
tell_lines
1
2487
1
6
1
50204
4
5
1
25972
2
secure
1
49551
1
29557
1
50204
4
5
1
25972
2
arm
1
49551
1
29557
1
50204
4
5
1
25972
2
say_parse
1
49551
1
29557
1
50204
4
5
1
25972
2
announce
1
49551
1
29557
1
50204
4
5
1
25972
2
say
1
2
1
3
1
50204
4
5
1
50204
2
say
1
31783
1
4803
1
50204
4
7
4
5
1
54837
2
tell_lines
1
2487
1
6
1
54837
4
5
1
38607
2
secure
1
49551
1
29557
1
54837
4
5
1
38607
2
arm
1
49551
1
29557
1
54837
4
5
1
38607
2
say_parse
1
49551
1
29557
1
54837
4
5
1
38607
2
announce
1
49551
1
29557
1
54837
4
5
1
38607
2
say
1
2
1
3
1
54837
4
5
1
54837
2
say
1
31783
1
4803
1
54837
4
7
4
5
1
50204
2
tell_lines
1
2487
1
6
1
50204
4
5
1
25972
2
secure
1
49551
1
29557
1
50204
4
5
1
25972
2
arm
1
49551
1
29557
1
50204
4
5
1
25972
2
say_parse
1
49551
1
29557
1
50204
4
5
1
25972
2
announce
1
49551
1
29557
1
50204
4
5
1
25972
2
say
1
2
1
3
1
50204
4
5
1
50204
2
say
1
31783
1
4803
1
50204
4
6
4
5
1
51261
2
tell_lines
1
2487
1
6
1
51261
4
5
1
51261
2
look_self
1
15
1
7069
1
51261
4
5
1
51261
2
look_self
1
57140
1
49900
1
51261
4
5
1
30711
2
special_look
1
31783
1
9805
1
51261
4
5
1
30711
2
l
1
31783
1
9805
1
51261
4
5
1
30711
2
l
1
49551
1
29557
1
51261
4
6
4
5
1
51261
2
tell_lines
1
2487
1
6
1
51261
4
5
1
51261
2
look_self
1
15
1
7069
1
51261
4
5
1
51261
2
look_self
1
57140
1
49900
1
51261
4
5
1
30711
2
special_look
1
31783
1
9805
1
51261
4
5
1
30711
2
l
1
31783
1
9805
1
51261
4
5
1
30711
2
l
1
49551
1
29557
1
51261
4
6
4
5
1
51261
2
tell_lines
1
2487
1
6
1
51261
4
5
1
51261
2
look_self
1
15
1
7069
1
51261
4
5
1
51261
2
look_self
1
57140
1
49900
1
51261
4
5
1
30711
2
special_look
1
31783
1
9805
1
51261
4
5
1
30711
2
l
1
31783
1
9805
1
51261
4
5
1
30711
2
l
1
49551
1
29557
1
51261
4
6
4
5
1
57861
2
tell_lines
1
2487
1
6
1
57861
4
5
1
51261
2
look_self
1
15
1
7069
1
57861
4
5
1
51261
2
look_self
1
57140
1
49900
1
57861
4
5
1
30711
2
special_look
1
31783
1
9805
1
57861
4
5
1
30711
2
l
1
31783
1
9805
1
57861
4
5
1
30711
2
l
1
49551
1
29557
1
57861
4
6
4
5
1
51261
2
tell_lines
1
2487
1
6
1
51261
4
5
1
51261
2
look_self
1
15
1
7069
1
51261
4
5
1
51261
2
look_self
1
57140
1
49900
1
51261
4
5
1
30711
2
special_look
1
31783
1
9805
1
51261
4
5
1
30711
2
l
1
31783
1
9805
1
51261
4
5
1
30711
2
l
1
49551
1
29557
1
51261
4
4
4
5
1
51261
2
tell_lines
1
2487
1
6
1
51261
4
5
1
51261
2
look_self
1
15
1
7069
1
51261
4
5
1
51261
2
look_self
1
57140
1
49900
1
51261
4
5
1
57659
2
l
1
2
1
3
1
51261
4
4
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
51261
2
look_self
1
15
1
7069
1
50636
4
5
1
51261
2
look_self
1
57140
1
49900
1
50636
4
5
1
57659
2
look
1
2
1
3
1
50636
4
4
4
5
1
51261
2
tell_lines
1
2487
1
6
1
51261
4
5
1
51261
2
look_self
1
15
1
7069
1
51261
4
5
1
51261
2
look_self
1
57140
1
49900
1
51261
4
5
1
57659
2
l
1
2
1
3
1
51261
4
4
4
5
1
50453
2
tell_lines
1
2487
1
6
1
50453
4
5
1
51261
2
look_self
1
15
1
7069
1
50453
4
5
1
51261
2
look_self
1
57140
1
49900
1
50453
4
5
1
57659
2
l
1
2
1
3
1
50453
4
4
4
5
1
50453
2
tell_lines
1
2487
1
6
1
50453
4
5
1
51261
2
look_self
1
15
1
7069
1
50453
4
5
1
51261
2
look_self
1
57140
1
49900
1
50453
4
5
1
57659
2
l
1
2
1
3
1
50453
4
4
4
5
1
51261
2
tell_lines
1
2487
1
6
1
51261
4
5
1
51261
2
look_self
1
15
1
7069
1
51261
4
5
1
51261
2
look_self
1
57140
1
49900
1
51261
4
5
1
57659
2
l
1
2
1
3
1
51261
4
4
4
5
1
51261
2
tell_lines
1
2487
1
6
1
51261
4
5
1
51261
2
look_self
1
15
1
7069
1
51261
4
5
1
51261
2
look_self
1
57140
1
49900
1
51261
4
5
1
57659
2
l
1
2
1
3
1
51261
4
4
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
51261
2
look_self
1
15
1
7069
1
50636
4
5
1
51261
2
look_self
1
57140
1
49900
1
50636
4
5
1
57659
2
look
1
2
1
3
1
50636
4
4
4
5
1
51261
2
tell_lines
1
2487
1
6
1
51261
4
5
1
51261
2
look_self
1
15
1
7069
1
51261
4
5
1
51261
2
look_self
1
57140
1
49900
1
51261
4
5
1
57659
2
l
1
2
1
3
1
51261
4
4
4
5
1
50636
2
tell_lines
1
2487
1
6
1
50636
4
5
1
51261
2
look_self
1
15
1
7069
1
50636
4
5
1
51261
2
look_self
1
57140
1
49900
1
50636
4
5
1
57659
2
look
1
2
1
3
1
50636
4
4
4
5
1
51261
2
tell_lines
1
2487
1
6
1
51261
4
5
1
51261
2
look_self
1
15
1
7069
1
51261
4
5
1
51261
2
look_self
1
57140
1
49900
1
51261
4
5
1
57659
2
l
1
2
1
3
1
51261
4
6
4
5
1
57871
2
tell_lines
1
2487
1
6
1
57871
4
5
1
25913
2
secure
1
49551
1
29557
1
57871
4
5
1
25913
2
arm
1
49551
1
29557
1
57871
4
5
1
25913
2
say_parse
1
49551
1
29557
1
57871
4
5
1
25913
2
announce
1
49551
1
29557
1
57871
4
5
1
25913
2
say
1
2
1
3
1
57871
4
6
4
5
1
57871
2
tell_lines
1
2487
1
6
1
57871
4
5
1
25913
2
secure
1
49551
1
29557
1
57871
4
5
1
25913
2
arm
1
49551
1
29557
1
57871
4
5
1
25913
2
say_parse
1
49551
1
29557
1
57871
4
5
1
25913
2
announce
1
49551
1
29557
1
57871
4
5
1
25913
2
say
1
2
1
3
1
57871
4
8
4
5
1
54875
2
tell_lines
1
2487
1
6
1
54875
4
5
1
18807
2
@peruse
1
31783
1
18807
1
54875
4
5
1
54875
2
my_huh
1
54875
1
6
1
54875
4
5
1
54875
2
my_huh
1
78
1
7069
1
54875
4
5
1
54875
2
my_huh
1
24442
1
26026
1
54875
4
5
1
54875
2
my_huh
1
57140
1
49900
1
54875
4
5
1
219
2
do_huh
1
54875
1
219
1
54875
4
5
1
17
2
@peruse
1
54875
1
1
1
54875
4
8
4
5
1
53484
2
tell_lines
1
2487
1
6
1
53484
4
5
1
39091
2
secure
1
49551
1
29557
1
53484
4
5
1
39091
2
arm
1
49551
1
29557
1
53484
4
5
1
39091
2
say_parse
1
49551
1
29557
1
53484
4
5
1
39091
2
announce
1
49551
1
29557
1
53484
4
5
1
39091
2
announce
1
48961
1
39357
1
53484
4
5
1
53484
2
say
1
57140
1
49900
1
53484
4
5
1
53484
2
say
1
19845
1
5409
1
53484
4
8
4
5
1
53484
2
tell_lines
1
2487
1
6
1
53484
4
5
1
36284
2
@dox
1
19845
1
36284
1
53484
4
5
1
53484
2
my_huh
1
53484
1
6
1
53484
4
5
1
53484
2
my_huh
1
78
1
7069
1
53484
4
5
1
53484
2
my_huh
1
24442
1
26026
1
53484
4
5
1
53484
2
my_huh
1
57140
1
49900
1
53484
4
5
1
219
2
do_huh
1
53484
1
219
1
53484
4
5
1
39091
2
@dox
1
53484
1
1
1
53484
4
7
4
5
1
57421
2
tell_lines
1
2487
1
6
1
57421
4
5
1
52633
2
secure
1
49551
1
29557
1
57421
4
5
1
52633
2
arm
1
49551
1
29557
1
57421
4
5
1
52633
2
say_parse
1
49551
1
29557
1
57421
4
5
1
52633
2
announce
1
49551
1
29557
1
57421
4
5
1
57421
2
say
1
57140
1
49900
1
57421
4
5
1
57421
2
say
1
19845
1
5409
1
57421
4
2
4
5
1
56673
2
tell_lines
1
2487
1
6
1
56673
4
5
1
26640
2
secure
1
30246
1
16017
1
56673
4
6
4
5
1
57919
2
tell_lines
1
2487
1
6
1
57919
4
5
1
20584
2
secure
1
49551
1
29557
1
57919
4
5
1
20584
2
arm
1
49551
1
29557
1
57919
4
5
1
20584
2
say_parse
1
49551
1
29557
1
57919
4
5
1
20584
2
announce
1
49551
1
29557
1
57919
4
5
1
20584
2
say
1
2
1
3
1
57919
4
2
4
5
1
57280
2
tell_lines
1
2487
1
6
1
57280
4
5
1
57280
2
who_has_quota
1
57280
1
21493
1
57280
4
2
4
5
1
57280
2
tell_lines
1
2487
1
6
1
57280
4
5
1
57280
2
who_has_quota
1
57280
1
21493
1
57280
4
4
4
5
1
57510
2
tell_lines
1
2487
1
6
1
57510
4
5
1
53657
2
secure
1
49551
1
29557
1
57510
4
5
1
53657
2
arm
1
49551
1
29557
1
57510
4
5
1
53657
2
press
1
49551
1
29557
1
57510
4
4
4
5
1
57014
2
tell_lines
1
2487
1
6
1
57014
4
5
1
55869
2
secure
1
49551
1
29557
1
57014
4
5
1
55869
2
arm
1
49551
1
29557
1
57014
4
5
1
55869
2
press
1
49551
1
29557
1
57014
4
7
4
5
1
53702
2
tell_lines
1
2487
1
6
1
53702
4
5
1
42348
2
secure
1
49551
1
29557
1
53702
4
5
1
42348
2
arm
1
49551
1
29557
1
53702
4
5
1
42348
2
say_parse
1
49551
1
29557
1
53702
4
5
1
42348
2
announce
1
49551
1
29557
1
53702
4
5
1
42348
2
announce
1
48961
1
39357
1
53702
4
5
1
53702
2
say
1
57140
1
49900
1
53702
4
8
4
5
1
53484
2
tell_lines
1
2487
1
6
1
53484
4
5
1
36284
2
@dox
1
19845
1
36284
1
53484
4
5
1
53484
2
my_huh
1
53484
1
6
1
53484
4
5
1
53484
2
my_huh
1
78
1
7069
1
53484
4
5
1
53484
2
my_huh
1
24442
1
26026
1
53484
4
5
1
53484
2
my_huh
1
57140
1
49900
1
53484
4
5
1
219
2
do_huh
1
53484
1
219
1
53484
4
5
1
39091
2
@dox
1
53484
1
1
1
53484
4
7
4
5
1
57882
2
tell_lines
1
2487
1
6
1
57882
4
5
1
57882
2
receive_page
1
2487
1
6
1
57882
4
5
1
57882
2
receive_page
1
47
1
3133
1
57882
4
5
1
57882
2
receive_page
1
2487
1
7069
1
57882
4
5
1
-1
2

1
57882
1
-1
1
57882
4
5
1
57882
2
eval_cmd_string
1
57882
1
217
1
57882
4
5
1
57882
2
eval
1
57882
1
217
1
57882
4
7
4
5
1
57882
2
tell_lines
1
2487
1
6
1
57882
4
5
1
57882
2
receive_page
1
2487
1
6
1
57882
4
5
1
57882
2
receive_page
1
47
1
3133
1
57882
4
5
1
57882
2
receive_page
1
2487
1
7069
1
57882
4
5
1
-1
2

1
57882
1
-1
1
57882
4
5
1
57882
2
eval_cmd_string
1
57882
1
217
1
57882
4
5
1
57882
2
eval
1
57882
1
217
1
57882
4
4
4
5
1
13412
2
tell_lines
1
2487
1
6
1
13412
4
5
1
42109
2
do_events
1
13412
1
42109
1
13412
4
5
1
42109
2
schedule_event
1
13412
1
42109
1
13412
4
5
1
42109
2
start
1
13412
1
42109
1
13412
4
7
4
5
1
49551
2
tell_lines
1
2487
1
6
1
55988
4
5
1
24380
2
enterfunc
1
49551
1
29557
1
55988
4
5
1
55988
2
moveto
1
55988
1
1
1
55988
4
5
1
55988
2
moveto
1
47
1
6
1
55988
4
5
1
55988
2
moveto
1
47
1
3133
1
55988
4
5
1
33355
2
leave
1
4290
1
6145
1
55988
4
5
1
33355
2
leave
1
24436
1
26152
1
55988
4
12
4
5
1
49551
2
tell_lines
1
2487
1
6
1
56720
4
5
1
13507
2
enterfunc
1
49551
1
29557
1
56720
4
5
1
13507
2
enterfunc
1
50053
1
13507
1
56720
4
5
1
56720
2
moveto
1
56720
1
1
1
56720
4
5
1
56720
2
moveto
1
47
1
6
1
56720
4
5
1
56720
2
moveto
1
47
1
3133
1
56720
4
5
1
56720
2
moveto
1
78
1
7069
1
56720
4
5
1
56720
2
moveto
1
3685
1
8855
1
56720
4
5
1
56720
2
moveto
1
57140
1
49900
1
56720
4
5
1
56720
2
moveto
1
19845
1
5409
1
56720
4
5
1
11001
2
out
1
4290
1
6145
1
56720
4
5
1
11001
2
out
1
24436
1
26152
1
56720
4
7
4
5
1
49551
2
tell_lines
1
2487
1
6
1
55988
4
5
1
24380
2
enterfunc
1
49551
1
29557
1
55988
4
5
1
55988
2
moveto
1
55988
1
1
1
55988
4
5
1
55988
2
moveto
1
47
1
6
1
55988
4
5
1
55988
2
moveto
1
47
1
3133
1
55988
4
5
1
56183
2
leave
1
4290
1
6145
1
55988
4
5
1
56183
2
leave
1
24436
1
26152
1
55988
4
8
4
5
1
49551
2
tell_lines
1
2487
1
6
1
55988
4
5
1
45676
2
enterfunc
1
49551
1
29557
1
55988
4
5
1
55988
2
moveto
1
55988
1
1
1
55988
4
5
1
55988
2
moveto
1
47
1
6
1
55988
4
5
1
55988
2
moveto
1
47
1
3133
1
55988
4
5
1
22213
2
move
1
2
1
7
1
55988
4
5
1
22213
2
invoke
1
2
1
7
1
55988
4
5
1
24380
2
e
1
2
1
3
1
55988
4
10
4
5
1
49551
2
tell_lines
1
2487
1
6
1
49551
4
5
1
13507
2
enterfunc
1
49551
1
29557
1
49551
4
5
1
13507
2
enterfunc
1
50053
1
13507
1
49551
4
5
1
49551
2
moveto
1
49551
1
1
1
49551
4
5
1
49551
2
moveto
1
47
1
6
1
49551
4
5
1
49551
2
moveto
1
47
1
3133
1
49551
4
5
1
49551
2
moveto
1
78
1
7069
1
49551
4
5
1
49551
2
moveto
1
3685
1
8855
1
49551
4
5
1
49551
2
moveto
1
57140
1
49900
1
49551
4
5
1
5443
2
q
1
6336
1
5400
1
49551
4
8
4
5
1
49551
2
tell_lines
1
2487
1
6
1
55988
4
5
1
24380
2
enterfunc
1
49551
1
29557
1
55988
4
5
1
55988
2
moveto
1
55988
1
1
1
55988
4
5
1
55988
2
moveto
1
47
1
6
1
55988
4
5
1
55988
2
moveto
1
47
1
3133
1
55988
4
5
1
31277
2
move
1
2
1
7
1
55988
4
5
1
31277
2
invoke
1
2
1
7
1
55988
4
5
1
45676
2
w
1
2
1
3
1
55988
4
9
4
5
1
49551
2
tell_lines
1
2487
1
6
1
55988
4
5
1
13507
2
enterfunc
1
49551
1
29557
1
55988
4
5
1
13507
2
enterfunc
1
50053
1
13507
1
55988
4
5
1
55988
2
moveto
1
55988
1
1
1
55988
4
5
1
55988
2
moveto
1
47
1
6
1
55988
4
5
1
55988
2
moveto
1
47
1
3133
1
55988
4
5
1
54185
2
move
1
2
1
7
1
55988
4
5
1
54185
2
invoke
1
2
1
7
1
55988
4
5
1
24380
2
n
1
2
1
3
1
55988
4
8
4
5
1
49551
2
tell_lines
1
2487
1
6
1
55988
4
5
1
13507
2
enterfunc
1
49551
1
29557
1
55988
4
5
1
13507
2
enterfunc
1
50053
1
13507
1
55988
4
5
1
56183
2
moveto
1
55988
1
1
1
55988
4
5
1
56183
2
moveto
1
6349
1
3
1
55988
4
5
1
56183
2
moveto
1
4290
1
6145
1
55988
4
5
1
56183
2
moveto
1
24436
1
26152
1
55988
4
5
1
56183
2
drop
1
4290
1
6145
1
55988
4
8
4
5
1
49551
2
tell_lines
1
2487
1
6
1
55988
4
5
1
13507
2
enterfunc
1
49551
1
29557
1
55988
4
5
1
13507
2
enterfunc
1
50053
1
13507
1
55988
4
5
1
55988
2
moveto
1
55988
1
1
1
55988
4
5
1
55988
2
moveto
1
47
1
6
1
55988
4
5
1
55988
2
moveto
1
47
1
3133
1
55988
4
5
1
56183
2
leave
1
4290
1
6145
1
55988
4
5
1
56183
2
leave
1
24436
1
26152
1
55988
4
10
4
5
1
49551
2
tell_lines
1
2487
1
6
1
56310
4
5
1
23829
2
enterfunc
1
49551
1
29557
1
56310
4
5
1
56310
2
moveto
1
56310
1
1
1
56310
4
5
1
56310
2
moveto
1
47
1
6
1
56310
4
5
1
56310
2
moveto
1
47
1
3133
1
56310
4
5
1
56310
2
moveto
1
78
1
7069
1
56310
4
5
1
56310
2
moveto
1
3685
1
8855
1
56310
4
5
1
56310
2
teleport
1
47
1
3133
1
56310
4
5
1
56310
2
teleport
1
3920
1
33337
1
56310
4
5
1
56310
2
@join
1
47
1
3133
1
56310
4
10
4
5
1
49551
2
tell_lines
1
2487
1
6
1
49551
4
5
1
13507
2
enterfunc
1
49551
1
29557
1
49551
4
5
1
13507
2
enterfunc
1
50053
1
13507
1
49551
4
5
1
49551
2
moveto
1
49551
1
1
1
49551
4
5
1
49551
2
moveto
1
47
1
6
1
49551
4
5
1
49551
2
moveto
1
47
1
3133
1
49551
4
5
1
49551
2
moveto
1
78
1
7069
1
49551
4
5
1
49551
2
moveto
1
3685
1
8855
1
49551
4
5
1
49551
2
moveto
1
57140
1
49900
1
49551
4
5
1
5443
2
q
1
6336
1
5400
1
49551
4
12
4
5
1
49551
2
tell_lines
1
2487
1
6
1
54787
4
5
1
13507
2
enterfunc
1
49551
1
29557
1
54787
4
5
1
13507
2
enterfunc
1
50053
1
13507
1
54787
4
5
1
54787
2
moveto
1
54787
1
1
1
54787
4
5
1
54787
2
moveto
1
47
1
6
1
54787
4
5
1
54787
2
moveto
1
47
1
3133
1
54787
4
5
1
54787
2
moveto
1
78
1
7069
1
54787
4
5
1
54787
2
moveto
1
3685
1
8855
1
54787
4
5
1
54787
2
moveto
1
57140
1
49900
1
54787
4
5
1
54787
2
moveto
1
19845
1
5409
1
54787
4
5
1
54560
2
exit
1
4290
1
6145
1
54787
4
5
1
54560
2
exit
1
24436
1
26152
1
54787
4
10
4
5
1
49551
2
tell_lines
1
2487
1
6
1
50204
4
5
1
25972
2
enterfunc
1
49551
1
29557
1
50204
4
5
1
50204
2
moveto
1
50204
1
1
1
50204
4
5
1
50204
2
moveto
1
47
1
6
1
50204
4
5
1
50204
2
moveto
1
47
1
3133
1
50204
4
5
1
50204
2
moveto
1
78
1
7069
1
50204
4
5
1
50204
2
moveto
1
3685
1
8855
1
50204
4
5
1
50204
2
moveto
1
50204
1
50204
1
50204
4
5
1
5746
2
done
1
6336
1
5400
1
50204
4
5
1
5746
2
send
1
6349
1
5746
1
50204
4
10
4
5
1
49551
2
tell_lines
1
2487
1
6
1
50176
4
5
1
51186
2
enterfunc
1
49551
1
29557
1
50176
4
5
1
50176
2
moveto
1
50176
1
1
1
50176
4
5
1
50176
2
moveto
1
47
1
6
1
50176
4
5
1
50176
2
moveto
1
47
1
3133
1
50176
4
5
1
50176
2
moveto
1
78
1
7069
1
50176
4
5
1
50176
2
moveto
1
3685
1
8855
1
50176
4
5
1
50176
2
moveto
1
57140
1
49900
1
50176
4
5
1
50176
2
moveto
1
50176
1
50176
1
50176
4
5
1
50176
2
home
1
57140
1
49900
1
50176
4
11
4
5
1
49551
2
tell_lines
1
2487
1
6
1
54099
4
5
1
30054
2
enterfunc
1
49551
1
29557
1
54099
4
5
1
54099
2
moveto
1
54099
1
1
1
54099
4
5
1
54099
2
moveto
1
47
1
6
1
54099
4
5
1
54099
2
moveto
1
47
1
3133
1
54099
4
5
1
54099
2
moveto
1
78
1
7069
1
54099
4
5
1
54099
2
moveto
1
3685
1
8855
1
54099
4
5
1
54099
2
moveto
1
57140
1
49900
1
54099
4
5
1
54099
2
moveto
1
19845
1
5409
1
54099
4
5
1
54099
2
moveto
1
54099
1
54099
1
54099
4
5
1
54099
2
home
1
57140
1
49900
1
54099
4
9
4
5
1
49551
2
tell_lines
1
2487
1
6
1
54787
4
5
1
35428
2
enterfunc
1
49551
1
29557
1
54787
4
5
1
54560
2
moveto
1
54787
1
1
1
54787
4
5
1
54560
2
moveto
1
6349
1
3
1
54787
4
5
1
54560
2
moveto
1
4290
1
6145
1
54787
4
5
1
54560
2
moveto
1
24436
1
26152
1
54787
4
5
1
54560
2
warp_to
1
24436
1
1452
1
54787
4
5
1
54560
2
warp_to
1
24436
1
26152
1
54787
4
5
1
54560
2
engage
1
24436
1
26152
1
54787
4
7
4
5
1
49551
2
tell_lines
1
2487
1
6
1
57807
4
5
1
53397
2
enterfunc
1
49551
1
29557
1
57807
4
5
1
57807
2
moveto
1
57807
1
1
1
57807
4
5
1
57807
2
moveto
1
47
1
6
1
57807
4
5
1
57807
2
moveto
1
47
1
3133
1
57807
4
5
1
57807
2
teleport
1
47
1
3133
1
57807
4
5
1
57807
2
@join
1
47
1
3133
1
57807
4
10
4
5
1
49551
2
tell_lines
1
2487
1
6
1
49551
4
5
1
13507
2
enterfunc
1
49551
1
29557
1
49551
4
5
1
13507
2
enterfunc
1
50053
1
13507
1
49551
4
5
1
33355
2
moveto
1
30246
1
1
1
49551
4
5
1
33355
2
moveto
1
6349
1
3
1
49551
4
5
1
33355
2
moveto
1
4290
1
6145
1
49551
4
5
1
33355
2
moveto
1
24436
1
26152
1
49551
4
5
1
49551
2
teleport
1
47
1
3133
1
49551
4
5
1
49551
2
teleport
1
3920
1
33337
1
49551
4
5
1
49551
2
@move
1
47
1
3133
1
49551
4
11
4
5
1
49551
2
tell_lines
1
2487
1
6
1
54787
4
5
1
35428
2
enterfunc
1
49551
1
29557
1
54787
4
5
1
54787
2
moveto
1
54787
1
1
1
54787
4
5
1
54787
2
moveto
1
47
1
6
1
54787
4
5
1
54787
2
moveto
1
47
1
3133
1
54787
4
5
1
54787
2
moveto
1
78
1
7069
1
54787
4
5
1
54787
2
moveto
1
3685
1
8855
1
54787
4
5
1
54787
2
moveto
1
57140
1
49900
1
54787
4
5
1
54787
2
moveto
1
19845
1
5409
1
54787
4
5
1
54560
2
exit
1
4290
1
6145
1
54787
4
5
1
54560
2
exit
1
24436
1
26152
1
54787
4
8
4
5
1
49551
2
tell_lines
1
2487
1
6
1
50176
4
5
1
45676
2
enterfunc
1
49551
1
29557
1
50176
4
5
1
18438
2
moveto
1
50176
1
1
1
50176
4
5
1
18438
2
moveto
1
6349
1
3
1
50176
4
5
1
18438
2
moveto
1
4290
1
6145
1
50176
4
5
1
18438
2
moveto
1
24436
1
26152
1
50176
4
5
1
13507
2
do_remove
1
50053
1
13507
1
50176
4
5
1
13507
2
do_remove
1
50053
1
13507
1
50176
4
2
4
5
1
30246
2
tell_lines
1
2487
1
6
1
30246
4
5
1
5987
2
list
1
30246
1
5987
1
30246
4
2
4
5
1
30246
2
tell_lines
1
2487
1
6
1
30246
4
5
1
5987
2
list
1
30246
1
5987
1
30246
4
6
4
5
1
55164
2
tell_lines
1
2487
1
6
1
55164
4
5
1
54023
2
secure
1
49551
1
29557
1
55164
4
5
1
54023
2
arm
1
49551
1
29557
1
55164
4
5
1
54023
2
say_parse
1
49551
1
29557
1
55164
4
5
1
54023
2
announce
1
49551
1
29557
1
55164
4
5
1
55164
2
say
1
57140
1
49900
1
55164
4
4
4
5
1
53502
2
tell_lines
1
2487
1
6
1
53502
4
5
1
53245
2
secure
1
49551
1
29557
1
53502
4
5
1
53245
2
arm
1
49551
1
29557
1
53502
4
5
1
53245
2
press
1
49551
1
29557
1
53502
4
4
4
5
1
50176
2
tell_lines
1
2487
1
6
1
50176
4
5
1
13507
2
secure
1
49551
1
29557
1
50176
4
5
1
13507
2
arm
1
49551
1
29557
1
50176
4
5
1
13507
2
press
1
49551
1
29557
1
50176
4
8
4
5
1
51261
2
tell_lines
1
2487
1
6
1
51261
4
5
1
18807
2
@peruse
1
33119
1
18807
1
51261
4
5
1
51261
2
my_huh
1
51261
1
6
1
51261
4
5
1
51261
2
my_huh
1
78
1
7069
1
51261
4
5
1
51261
2
my_huh
1
24442
1
26026
1
51261
4
5
1
51261
2
my_huh
1
57140
1
49900
1
51261
4
5
1
219
2
do_huh
1
51261
1
219
1
51261
4
5
1
57659
2
@peruse
1
51261
1
1
1
51261
4
7
4
5
1
46470
2
tell_lines
1
2487
1
6
1
46470
4
5
1
47192
2
secure
1
49551
1
29557
1
46470
4
5
1
47192
2
arm
1
49551
1
29557
1
46470
4
5
1
47192
2
say_parse
1
49551
1
29557
1
46470
4
5
1
47192
2
announce
1
49551
1
29557
1
46470
4
5
1
47192
2
say
1
2
1
3
1
46470
4
5
1
46470
2
say
1
31783
1
4803
1
46470
4
4
4
5
1
56629
2
tell_lines
1
2487
1
6
1
56629
4
5
1
30286
2
secure
1
49551
1
29557
1
56629
4
5
1
30286
2
arm
1
49551
1
29557
1
56629
4
5
1
30286
2
press
1
49551
1
29557
1
56629
4
4
4
5
1
56629
2
tell_lines
1
2487
1
6
1
56629
4
5
1
30286
2
secure
1
49551
1
29557
1
56629
4
5
1
30286
2
arm
1
49551
1
29557
1
56629
4
5
1
30286
2
press
1
49551
1
29557
1
56629
4
4
4
5
1
56629
2
tell_lines
1
2487
1
6
1
56629
4
5
1
30286
2
secure
1
49551
1
29557
1
56629
4
5
1
30286
2
arm
1
49551
1
29557
1
56629
4
5
1
30286
2
press
1
49551
1
29557
1
56629
4
4
4
5
1
56629
2
tell_lines
1
2487
1
6
1
56629
4
5
1
30286
2
secure
1
49551
1
29557
1
56629
4
5
1
30286
2
arm
1
49551
1
29557
1
56629
4
5
1
30286
2
press
1
49551
1
29557
1
56629
4
7
4
5
1
51902
2
tell_lines
1
2487
1
6
1
51902
4
5
1
53151
2
secure
1
49551
1
29557
1
51902
4
5
1
53151
2
arm
1
49551
1
29557
1
51902
4
5
1
53151
2
say_parse
1
49551
1
29557
1
51902
4
5
1
53151
2
announce
1
49551
1
29557
1
51902
4
5
1
53151
2
say
1
2
1
3
1
51902
4
5
1
51902
2
say
1
31783
1
4803
1
51902
4
2
4
5
1
56651
2
tell_lines
1
2487
1
6
1
56651
4
5
1
56651
2
@peruse
1
31783
1
4803
1
56651
4
7
4
5
1
34107
2
tell_lines
1
2487
1
6
1
34107
4
5
1
7944
2
secure
1
49551
1
29557
1
34107
4
5
1
7944
2
arm
1
49551
1
29557
1
34107
4
5
1
7944
2
say_parse
1
49551
1
29557
1
34107
4
5
1
7944
2
announce
1
49551
1
29557
1
34107
4
5
1
7944
2
say
1
2
1
3
1
34107
4
5
1
34107
2
say
1
31783
1
4803
1
34107
4
4
4
5
1
57810
2
tell_lines
1
2487
1
6
1
57810
4
5
1
25980
2
secure
1
49551
1
29557
1
57810
4
5
1
25980
2
arm
1
49551
1
29557
1
57810
4
5
1
25980
2
press
1
49551
1
29557
1
57810
4
7
4
5
1
50198
2
tell_lines
1
2487
1
6
1
50198
4
5
1
42772
2
secure
1
49551
1
29557
1
50198
4
5
1
42772
2
arm
1
49551
1
29557
1
50198
4
5
1
42772
2
say_parse
1
49551
1
29557
1
50198
4
5
1
42772
2
announce
1
49551
1
29557
1
50198
4
5
1
42772
2
say
1
2
1
3
1
50198
4
5
1
50198
2
say
1
31783
1
4803
1
50198
4
16
4
5
1
30246
2
tell_lines
1
2487
1
6
1
30246
4
5
1
5987
2
secure_take
1
30246
1
5987
1
30246
4
5
1
5987
2
exitfunc
1
30246
1
5987
1
30246
4
5
1
13393
2
moveto
1
2693
1
1
1
30246
4
5
1
13393
2
moveto
1
2
1
5
1
30246
4
5
1
13393
2
moveto
1
2693
1
517
1
30246
4
5
1
8085
2
buy_item
1
50459
1
8085
1
30246
4
5
1
8085
2
Request_Sale
1
50459
1
8085
1
30246
4
5
1
8085
2
rx_verb
1
19845
1
6475
1
30246
4
5
1
8085
2
rx_list
1
19845
1
6475
1
30246
4
5
1
8085
2
parse_say
1
19845
1
6475
1
30246
4
5
1
8085
2
notify_say
1
19845
1
6475
1
30246
4
5
1
8085
2
tell
1
19845
1
6475
1
30246
4
5
1
5987
2
announce
1
2
1
3
1
30246
4
5
1
5987
2
announce
1
2693
1
258
1
30246
4
5
1
30246
2
say
1
57140
1
49900
1
30246
4
4
4
5
1
51626
2
tell_lines
1
2487
1
6
1
51626
4
5
1
41034
2
secure
1
49551
1
29557
1
51626
4
5
1
41034
2
arm
1
49551
1
29557
1
51626
4
5
1
41034
2
press
1
49551
1
29557
1
51626
4
8
4
5
1
50224
2
tell_lines
1
2487
1
6
1
50224
4
5
1
18807
2
@peruse
1
33119
1
18807
1
50224
4
5
1
50224
2
my_huh
1
50224
1
6
1
50224
4
5
1
50224
2
my_huh
1
78
1
7069
1
50224
4
5
1
50224
2
my_huh
1
24442
1
26026
1
50224
4
5
1
50224
2
my_huh
1
57140
1
49900
1
50224
4
5
1
219
2
do_huh
1
50224
1
219
1
50224
4
5
1
51400
2
@peruse
1
50224
1
1
1
50224
4
8
4
5
1
49397
2
tell_lines
1
2487
1
6
1
49397
4
5
1
37404
2
@peruse
1
19845
1
37404
1
49397
4
5
1
49397
2
my_huh
1
49397
1
6
1
49397
4
5
1
49397
2
my_huh
1
78
1
7069
1
49397
4
5
1
49397
2
my_huh
1
24442
1
26026
1
49397
4
5
1
49397
2
my_huh
1
57140
1
49900
1
49397
4
5
1
219
2
do_huh
1
49397
1
219
1
49397
4
5
1
27861
2
@peruse
1
49397
1
1
1
49397
4
11
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
50367
2
moveto
1
50367
1
50367
1
50367
4
5
1
5443
2
done
1
6336
1
5400
1
50367
4
3
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
l
1
2693
1
258
1
50367
4
11
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
50367
2
moveto
1
50367
1
50367
1
50367
4
5
1
5443
2
done
1
6336
1
5400
1
50367
4
11
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
50367
2
moveto
1
50367
1
50367
1
50367
4
5
1
5443
2
done
1
6336
1
5400
1
50367
4
3
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
l
1
2693
1
258
1
50367
4
3
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
l
1
2693
1
258
1
50367
4
3
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
l
1
2693
1
258
1
50367
4
3
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
l
1
2693
1
258
1
50367
4
3
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
l
1
2693
1
258
1
50367
4
2
4
5
1
53466
2
tell_lines
1
2487
1
6
1
53466
4
5
1
53466
2
@peruse
1
31783
1
4803
1
53466
4
4
4
5
1
50290
2
tell_lines
1
2487
1
6
1
50290
4
5
1
41318
2
secure
1
49551
1
29557
1
50290
4
5
1
41318
2
arm
1
49551
1
29557
1
50290
4
5
1
41318
2
press
1
49551
1
29557
1
50290
4
7
4
5
1
57531
2
tell_lines
1
2487
1
6
1
57531
4
5
1
53249
2
secure
1
49551
1
29557
1
57531
4
5
1
53249
2
arm
1
49551
1
29557
1
57531
4
5
1
53249
2
say_parse
1
49551
1
29557
1
57531
4
5
1
53249
2
announce
1
49551
1
29557
1
57531
4
5
1
53249
2
say
1
2
1
3
1
57531
4
5
1
57531
2
say
1
31783
1
4803
1
57531
4
6
4
5
1
56607
2
tell_lines
1
2487
1
6
1
56607
4
5
1
54479
2
secure
1
49551
1
29557
1
56607
4
5
1
54479
2
arm
1
49551
1
29557
1
56607
4
5
1
54479
2
say_parse
1
49551
1
29557
1
56607
4
5
1
54479
2
announce
1
49551
1
29557
1
56607
4
5
1
56607
2
say
1
57140
1
49900
1
56607
4
2
4
5
1
57140
2
tell_lines
1
2487
1
6
1
41057
4
5
1
41057
2
@fedit
1
41057
1
41057
1
41057
4
8
4
5
1
57421
2
tell_lines
1
2487
1
6
1
57421
4
5
1
18807
2
@peruse
1
33119
1
18807
1
57421
4
5
1
57421
2
my_huh
1
57421
1
6
1
57421
4
5
1
57421
2
my_huh
1
78
1
7069
1
57421
4
5
1
57421
2
my_huh
1
24442
1
26026
1
57421
4
5
1
57421
2
my_huh
1
57140
1
49900
1
57421
4
5
1
219
2
do_huh
1
57421
1
219
1
57421
4
5
1
43787
2
@peruse
1
57421
1
1
1
57421
4
8
4
5
1
57135
2
tell_lines
1
2487
1
6
1
57135
4
5
1
18807
2
@peruse
1
33119
1
18807
1
57135
4
5
1
57135
2
my_huh
1
57135
1
6
1
57135
4
5
1
57135
2
my_huh
1
78
1
7069
1
57135
4
5
1
57135
2
my_huh
1
24442
1
26026
1
57135
4
5
1
57135
2
my_huh
1
57140
1
49900
1
57135
4
5
1
219
2
do_huh
1
57135
1
219
1
57135
4
5
1
53512
2
@peruse
1
57135
1
1
1
57135
4
2
4
5
1
50266
2
tell_lines
1
2487
1
6
1
50266
4
5
1
50266
2
@peruse
1
31783
1
4803
1
50266
4
2
4
5
1
56361
2
tell_lines
1
2487
1
6
1
56361
4
5
1
50484
2
print
1
2487
1
5400
1
56361
4
2
4
5
1
56361
2
tell_lines
1
2487
1
6
1
56361
4
5
1
50484
2
print
1
2487
1
5400
1
56361
4
4
4
5
1
53465
2
tell_lines
1
2487
1
6
1
53465
4
5
1
5008
2
secure
1
49551
1
29557
1
53465
4
5
1
5008
2
arm
1
49551
1
29557
1
53465
4
5
1
5008
2
press
1
49551
1
29557
1
53465
4
4
4
5
1
57810
2
tell_lines
1
2487
1
6
1
57810
4
5
1
25980
2
secure
1
49551
1
29557
1
57810
4
5
1
25980
2
arm
1
49551
1
29557
1
57810
4
5
1
25980
2
press
1
49551
1
29557
1
57810
4
13
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
50367
2
moveto
1
50367
1
50367
1
50367
4
5
1
50367
2
teleport
1
47
1
3133
1
50367
4
5
1
50367
2
teleport
1
3920
1
33337
1
50367
4
5
1
50367
2
@go
1
47
1
3133
1
50367
4
3
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
confunc
1
2
1
3
1
50367
4
11
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
50367
2
moveto
1
50367
1
50367
1
50367
4
5
1
5443
2
done
1
6336
1
5400
1
50367
4
11
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
50367
2
moveto
1
50367
1
50367
1
50367
4
5
1
5443
2
done
1
6336
1
5400
1
50367
4
11
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
50367
2
moveto
1
50367
1
50367
1
50367
4
5
1
5443
2
done
1
6336
1
5400
1
50367
4
11
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
50367
2
moveto
1
50367
1
50367
1
50367
4
5
1
5443
2
done
1
6336
1
5400
1
50367
4
11
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
50367
2
moveto
1
50367
1
50367
1
50367
4
5
1
5443
2
done
1
6336
1
5400
1
50367
4
11
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
50367
2
moveto
1
50367
1
50367
1
50367
4
5
1
5443
2
done
1
6336
1
5400
1
50367
4
11
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
50367
2
moveto
1
50367
1
50367
1
50367
4
5
1
5443
2
done
1
6336
1
5400
1
50367
4
7
4
5
1
56872
2
tell_lines
1
2487
1
6
1
56872
4
5
1
39439
2
secure
1
49551
1
29557
1
56872
4
5
1
39439
2
arm
1
49551
1
29557
1
56872
4
5
1
39439
2
say_parse
1
49551
1
29557
1
56872
4
5
1
39439
2
announce
1
49551
1
29557
1
56872
4
5
1
56872
2
say
1
57140
1
49900
1
56872
4
5
1
56872
2
say
1
19845
1
5409
1
56872
4
8
4
5
1
53484
2
tell_lines
1
2487
1
6
1
53484
4
5
1
36284
2
@inspect
1
19845
1
36284
1
53484
4
5
1
53484
2
my_huh
1
53484
1
6
1
53484
4
5
1
53484
2
my_huh
1
78
1
7069
1
53484
4
5
1
53484
2
my_huh
1
24442
1
26026
1
53484
4
5
1
53484
2
my_huh
1
57140
1
49900
1
53484
4
5
1
219
2
do_huh
1
53484
1
219
1
53484
4
5
1
42023
2
@inspect
1
53484
1
1
1
53484
4
8
4
5
1
53484
2
tell_lines
1
2487
1
6
1
53484
4
5
1
36284
2
@dox
1
19845
1
36284
1
53484
4
5
1
53484
2
my_huh
1
53484
1
6
1
53484
4
5
1
53484
2
my_huh
1
78
1
7069
1
53484
4
5
1
53484
2
my_huh
1
24442
1
26026
1
53484
4
5
1
53484
2
my_huh
1
57140
1
49900
1
53484
4
5
1
219
2
do_huh
1
53484
1
219
1
53484
4
5
1
43542
2
@dox
1
53484
1
1
1
53484
4
7
4
5
1
26343
2
tell_lines
1
2487
1
6
1
26343
4
5
1
18807
2
@peruse
1
33119
1
18807
1
26343
4
5
1
26343
2
my_huh
1
26343
1
6
1
26343
4
5
1
26343
2
my_huh
1
78
1
7069
1
26343
4
5
1
26343
2
my_huh
1
24442
1
26026
1
26343
4
5
1
219
2
do_huh
1
26343
1
219
1
26343
4
5
1
38937
2
@peruse
1
26343
1
1
1
26343
4
6
4
5
1
23276
2
tell_lines
1
2487
1
6
1
23276
4
5
1
40878
2
secure
1
49551
1
29557
1
23276
4
5
1
40878
2
arm
1
49551
1
29557
1
23276
4
5
1
40878
2
say_parse
1
49551
1
29557
1
23276
4
5
1
40878
2
announce
1
49551
1
29557
1
23276
4
5
1
23276
2
say
1
57140
1
49900
1
23276
4
4
4
5
1
57042
2
tell_lines
1
2487
1
6
1
57042
4
5
1
46611
2
secure
1
49551
1
29557
1
57042
4
5
1
46611
2
arm
1
49551
1
29557
1
57042
4
5
1
46611
2
press
1
49551
1
29557
1
57042
4
8
4
5
1
56263
2
tell_lines
1
2487
1
6
1
56263
4
5
1
18807
2
@peruse
1
33119
1
18807
1
56263
4
5
1
56263
2
my_huh
1
56263
1
6
1
56263
4
5
1
56263
2
my_huh
1
78
1
7069
1
56263
4
5
1
56263
2
my_huh
1
24442
1
26026
1
56263
4
5
1
56263
2
my_huh
1
57140
1
49900
1
56263
4
5
1
219
2
do_huh
1
56263
1
219
1
56263
4
5
1
55879
2
@peruse
1
56263
1
1
1
56263
4
7
4
5
1
56872
2
tell_lines
1
2487
1
6
1
56872
4
5
1
39439
2
secure
1
49551
1
29557
1
56872
4
5
1
39439
2
arm
1
49551
1
29557
1
56872
4
5
1
39439
2
say_parse
1
49551
1
29557
1
56872
4
5
1
39439
2
announce
1
49551
1
29557
1
56872
4
5
1
56872
2
say
1
57140
1
49900
1
56872
4
5
1
56872
2
say
1
19845
1
5409
1
56872
4
2
4
5
1
54875
2
tell_lines
1
2487
1
6
1
54875
4
5
1
54875
2
@peruse
1
31783
1
4803
1
54875
4
2
4
5
1
37781
2
tell_lines
1
2487
1
6
1
37781
4
5
1
37781
2
@peruse
1
37781
1
37781
1
37781
4
7
4
5
1
57531
2
tell_lines
1
2487
1
6
1
57531
4
5
1
53249
2
secure
1
49551
1
29557
1
57531
4
5
1
53249
2
arm
1
49551
1
29557
1
57531
4
5
1
53249
2
say_parse
1
49551
1
29557
1
57531
4
5
1
53249
2
announce
1
49551
1
29557
1
57531
4
5
1
53249
2
say
1
2
1
3
1
57531
4
5
1
57531
2
say
1
31783
1
4803
1
57531
4
4
4
5
1
57990
2
tell_lines
1
2487
1
6
1
57990
4
5
1
13610
2
secure
1
49551
1
29557
1
57990
4
5
1
13610
2
arm
1
49551
1
29557
1
57990
4
5
1
13610
2
press
1
49551
1
29557
1
57990
4
6
4
5
1
57998
2
tell_lines
1
2487
1
6
1
57998
4
5
1
57998
2
look_self
1
2
1
1
1
57998
4
5
1
57998
2
look_self
1
2
1
6
1
57998
4
5
1
31788
2
special_look
1
31783
1
9805
1
57998
4
5
1
31788
2
l
1
31783
1
9805
1
57998
4
5
1
31788
2
l
1
49551
1
29557
1
57998
4
2
4
5
1
50464
2
tell_lines
1
2487
1
6
1
50464
4
5
1
50464
2
@peruse
1
31783
1
4803
1
50464
4
8
4
5
1
39670
2
tell_lines
1
2487
1
6
1
39670
4
5
1
5023
2
spamhelp
1
37636
1
5023
1
39670
4
5
1
39670
2
my_huh
1
39670
1
6
1
39670
4
5
1
39670
2
my_huh
1
78
1
7069
1
39670
4
5
1
39670
2
my_huh
1
24442
1
26026
1
39670
4
5
1
39670
2
my_huh
1
57140
1
49900
1
39670
4
5
1
219
2
do_huh
1
39670
1
219
1
39670
4
5
1
5173
2
spamhelp
1
39670
1
1
1
39670
4
6
4
5
1
57413
2
tell_lines
1
2487
1
6
1
57413
4
5
1
50866
2
secure
1
49551
1
29557
1
57413
4
5
1
50866
2
arm
1
49551
1
29557
1
57413
4
5
1
50866
2
say_parse
1
49551
1
29557
1
57413
4
5
1
50866
2
announce
1
49551
1
29557
1
57413
4
5
1
50866
2
say
1
2
1
3
1
57413
4
8
4
5
1
57421
2
tell_lines
1
2487
1
6
1
57421
4
5
1
18807
2
@peruse
1
33119
1
18807
1
57421
4
5
1
57421
2
my_huh
1
57421
1
6
1
57421
4
5
1
57421
2
my_huh
1
78
1
7069
1
57421
4
5
1
57421
2
my_huh
1
24442
1
26026
1
57421
4
5
1
57421
2
my_huh
1
57140
1
49900
1
57421
4
5
1
219
2
do_huh
1
57421
1
219
1
57421
4
5
1
40840
2
@peruse
1
57421
1
1
1
57421
4
13
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
50367
2
moveto
1
50367
1
50367
1
50367
4
5
1
50367
2
teleport
1
47
1
3133
1
50367
4
5
1
50367
2
teleport
1
3920
1
33337
1
50367
4
5
1
50367
2
@go
1
47
1
3133
1
50367
4
9
4
5
1
50634
2
tell_lines
1
2487
1
6
1
50634
4
5
1
27804
2
secure
1
30246
1
16017
1
50634
4
5
1
27804
2
arm
1
30246
1
16017
1
50634
4
5
1
27804
2
say_parse
1
30246
1
16017
1
50634
4
5
1
27804
2
announce_all
1
30246
1
16017
1
50634
4
5
1
27804
2
tell
1
4290
1
6145
1
50634
4
5
1
50986
2
announce
1
31783
1
9805
1
50634
4
5
1
50986
2
announce
1
49551
1
29557
1
50634
4
5
1
50634
2
say
1
57140
1
49900
1
50634
4
6
4
5
1
55707
2
tell_lines
1
2487
1
6
1
55707
4
5
1
36264
2
secure
1
49551
1
29557
1
55707
4
5
1
36264
2
arm
1
49551
1
29557
1
55707
4
5
1
36264
2
say_parse
1
49551
1
29557
1
55707
4
5
1
36264
2
announce
1
49551
1
29557
1
55707
4
5
1
55707
2
say
1
57140
1
49900
1
55707
4
6
4
5
1
57998
2
tell_lines
1
2487
1
6
1
57998
4
5
1
31788
2
secure
1
49551
1
29557
1
57998
4
5
1
31788
2
arm
1
49551
1
29557
1
57998
4
5
1
31788
2
say_parse
1
49551
1
29557
1
57998
4
5
1
31788
2
announce
1
49551
1
29557
1
57998
4
5
1
31788
2
say
1
2
1
3
1
57998
4
15
4
5
1
56998
2
tell_lines
1
2487
1
6
1
56998
4
5
1
32361
2
look_self
1
15
1
6792
1
56998
4
5
1
32361
2
enterfunc
1
2
1
3
1
56998
4
5
1
32361
2
enterfunc
1
2693
1
258
1
56998
4
5
1
56998
2
moveto
1
56998
1
1
1
56998
4
5
1
56998
2
moveto
1
47
1
6
1
56998
4
5
1
56998
2
moveto
1
47
1
3133
1
56998
4
5
1
56998
2
moveto
1
78
1
7069
1
56998
4
5
1
56998
2
moveto
1
3685
1
8855
1
56998
4
5
1
56998
2
moveto
1
56998
1
56998
1
56998
4
5
1
26217
2
move
1
2
1
7
1
56998
4
5
1
26217
2
move
1
50367
1
26217
1
56998
4
5
1
26217
2
invoke
1
2
1
7
1
56998
4
5
1
26217
2
invoke
1
50367
1
26217
1
56998
4
5
1
35514
2
d
1
2
1
3
1
56998
4
3
4
5
1
56998
2
tell_lines
1
2487
1
6
1
56998
4
5
1
32361
2
look_self
1
15
1
6792
1
56998
4
5
1
32361
2
look
1
2693
1
258
1
56998
4
10
4
5
1
54918
2
tell_lines
1
2487
1
6
1
54918
4
5
1
24737
2
look_self
1
2
1
1
1
54918
4
5
1
24737
2
look_self
1
2
1
3
1
54918
4
5
1
24737
2
enterfunc
1
2
1
3
1
54918
4
5
1
54918
2
moveto
1
54918
1
1
1
54918
4
5
1
54918
2
moveto
1
47
1
6
1
54918
4
5
1
54918
2
moveto
1
47
1
3133
1
54918
4
5
1
43463
2
move
1
2
1
7
1
54918
4
5
1
43463
2
invoke
1
2
1
7
1
54918
4
5
1
47200
2
s
1
2
1
3
1
54918
4
7
4
5
1
56872
2
tell_lines
1
2487
1
6
1
56872
4
5
1
39439
2
secure
1
49551
1
29557
1
56872
4
5
1
39439
2
arm
1
49551
1
29557
1
56872
4
5
1
39439
2
say_parse
1
49551
1
29557
1
56872
4
5
1
39439
2
announce
1
49551
1
29557
1
56872
4
5
1
56872
2
say
1
57140
1
49900
1
56872
4
5
1
56872
2
say
1
19845
1
5409
1
56872
4
4
4
5
1
58173
2
tell_lines
1
2487
1
6
1
58173
4
5
1
41398
2
secure
1
49551
1
29557
1
58173
4
5
1
41398
2
arm
1
49551
1
29557
1
58173
4
5
1
41398
2
press
1
49551
1
29557
1
58173
4
15
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
50367
2
moveto
1
50367
1
50367
1
50367
4
5
1
26217
2
move
1
2
1
7
1
50367
4
5
1
26217
2
move
1
50367
1
26217
1
50367
4
5
1
26217
2
invoke
1
2
1
7
1
50367
4
5
1
26217
2
invoke
1
50367
1
26217
1
50367
4
5
1
35514
2
d
1
2
1
3
1
50367
4
4
4
5
1
57990
2
tell_lines
1
2487
1
6
1
57990
4
5
1
52032
2
secure
1
49551
1
29557
1
57990
4
5
1
52032
2
arm
1
49551
1
29557
1
57990
4
5
1
52032
2
press
1
49551
1
29557
1
57990
4
4
4
5
1
56310
2
tell_lines
1
2487
1
6
1
56310
4
5
1
56232
2
secure
1
49551
1
29557
1
56310
4
5
1
56232
2
arm
1
49551
1
29557
1
56310
4
5
1
56232
2
press
1
49551
1
29557
1
56310
4
4
4
5
1
56311
2
tell_lines
1
2487
1
6
1
56311
4
5
1
45919
2
secure
1
49551
1
29557
1
56311
4
5
1
45919
2
arm
1
49551
1
29557
1
56311
4
5
1
45919
2
press
1
49551
1
29557
1
56311
4
7
4
5
1
52687
2
tell_lines
1
2487
1
6
1
52687
4
5
1
11461
2
secure
1
49551
1
29557
1
52687
4
5
1
11461
2
arm
1
49551
1
29557
1
52687
4
5
1
11461
2
say_parse
1
49551
1
29557
1
52687
4
5
1
11461
2
announce
1
49551
1
29557
1
52687
4
5
1
11461
2
say
1
2
1
3
1
52687
4
5
1
52687
2
say
1
31783
1
4803
1
52687
4
4
4
5
1
57892
2
tell_lines
1
2487
1
6
1
57892
4
5
1
22838
2
secure
1
49551
1
29557
1
57892
4
5
1
22838
2
arm
1
49551
1
29557
1
57892
4
5
1
22838
2
press
1
49551
1
29557
1
57892
4
7
4
5
1
57531
2
tell_lines
1
2487
1
6
1
57531
4
5
1
53249
2
secure
1
49551
1
29557
1
57531
4
5
1
53249
2
arm
1
49551
1
29557
1
57531
4
5
1
53249
2
say_parse
1
49551
1
29557
1
57531
4
5
1
53249
2
announce
1
49551
1
29557
1
57531
4
5
1
53249
2
say
1
2
1
3
1
57531
4
5
1
57531
2
say
1
31783
1
4803
1
57531
4
7
4
5
1
57531
2
tell_lines
1
2487
1
6
1
57531
4
5
1
53249
2
secure
1
49551
1
29557
1
57531
4
5
1
53249
2
arm
1
49551
1
29557
1
57531
4
5
1
53249
2
say_parse
1
49551
1
29557
1
57531
4
5
1
53249
2
announce
1
49551
1
29557
1
57531
4
5
1
53249
2
say
1
2
1
3
1
57531
4
5
1
57531
2
say
1
31783
1
4803
1
57531
4
4
4
5
1
58091
2
tell_lines
1
2487
1
6
1
58091
4
5
1
13455
2
secure
1
49551
1
29557
1
58091
4
5
1
13455
2
arm
1
49551
1
29557
1
58091
4
5
1
13455
2
press
1
49551
1
29557
1
58091
4
4
4
5
1
58173
2
tell_lines
1
2487
1
6
1
58173
4
5
1
41398
2
secure
1
49551
1
29557
1
58173
4
5
1
41398
2
arm
1
49551
1
29557
1
58173
4
5
1
41398
2
press
1
49551
1
29557
1
58173
4
5
4
5
1
57998
2
tell_lines
1
2487
1
6
1
57998
4
5
1
57998
2
look_self
1
15
1
7069
1
57998
4
5
1
31788
2
special_look
1
31783
1
9805
1
57998
4
5
1
31788
2
l
1
31783
1
9805
1
57998
4
5
1
31788
2
l
1
49551
1
29557
1
57998
4
7
4
5
1
56263
2
tell_lines
1
2487
1
6
1
56263
4
5
1
55879
2
secure
1
49551
1
29557
1
56263
4
5
1
55879
2
arm
1
49551
1
29557
1
56263
4
5
1
55879
2
say_parse
1
49551
1
29557
1
56263
4
5
1
55879
2
announce
1
49551
1
29557
1
56263
4
5
1
56263
2
say
1
57140
1
49900
1
56263
4
5
1
56263
2
say
1
19845
1
5409
1
56263
4
14
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
26217
2
move
1
2
1
7
1
50367
4
5
1
26217
2
move
1
50367
1
26217
1
50367
4
5
1
26217
2
invoke
1
2
1
7
1
50367
4
5
1
26217
2
invoke
1
50367
1
26217
1
50367
4
5
1
35514
2
d
1
2
1
3
1
50367
4
14
4
5
1
29209
2
tell_lines
1
2487
1
6
1
29209
4
5
1
32361
2
look_self
1
15
1
6792
1
29209
4
5
1
32361
2
enterfunc
1
2
1
3
1
29209
4
5
1
32361
2
enterfunc
1
2693
1
258
1
29209
4
5
1
29209
2
moveto
1
29209
1
1
1
29209
4
5
1
29209
2
moveto
1
47
1
6
1
29209
4
5
1
29209
2
moveto
1
47
1
3133
1
29209
4
5
1
29209
2
moveto
1
78
1
7069
1
29209
4
5
1
29209
2
moveto
1
3685
1
8855
1
29209
4
5
1
26217
2
move
1
2
1
7
1
29209
4
5
1
26217
2
move
1
50367
1
26217
1
29209
4
5
1
26217
2
invoke
1
2
1
7
1
29209
4
5
1
26217
2
invoke
1
50367
1
26217
1
29209
4
5
1
35514
2
d
1
2
1
3
1
29209
4
4
4
5
1
56310
2
tell_lines
1
2487
1
6
1
56310
4
5
1
56232
2
secure
1
49551
1
29557
1
56310
4
5
1
56232
2
arm
1
49551
1
29557
1
56310
4
5
1
56232
2
press
1
49551
1
29557
1
56310
4
4
4
5
1
35404
2
tell_lines
1
2487
1
6
1
35404
4
5
1
34623
2
secure
1
49551
1
29557
1
35404
4
5
1
34623
2
arm
1
49551
1
29557
1
35404
4
5
1
34623
2
press
1
49551
1
29557
1
35404
4
4
4
5
1
56819
2
tell_lines
1
2487
1
6
1
56819
4
5
1
53010
2
secure
1
49551
1
29557
1
56819
4
5
1
53010
2
arm
1
49551
1
29557
1
56819
4
5
1
53010
2
press
1
49551
1
29557
1
56819
4
7
4
5
1
57531
2
tell_lines
1
2487
1
6
1
57531
4
5
1
53249
2
secure
1
49551
1
29557
1
57531
4
5
1
53249
2
arm
1
49551
1
29557
1
57531
4
5
1
53249
2
say_parse
1
49551
1
29557
1
57531
4
5
1
53249
2
announce
1
49551
1
29557
1
57531
4
5
1
53249
2
say
1
2
1
3
1
57531
4
5
1
57531
2
say
1
31783
1
4803
1
57531
4
7
4
5
1
57531
2
tell_lines
1
2487
1
6
1
57531
4
5
1
53249
2
secure
1
49551
1
29557
1
57531
4
5
1
53249
2
arm
1
49551
1
29557
1
57531
4
5
1
53249
2
say_parse
1
49551
1
29557
1
57531
4
5
1
53249
2
announce
1
49551
1
29557
1
57531
4
5
1
53249
2
say
1
2
1
3
1
57531
4
5
1
57531
2
say
1
31783
1
4803
1
57531
4
6
4
5
1
57998
2
tell_lines
1
2487
1
6
1
57998
4
5
1
31788
2
secure
1
49551
1
29557
1
57998
4
5
1
31788
2
arm
1
49551
1
29557
1
57998
4
5
1
31788
2
say_parse
1
49551
1
29557
1
57998
4
5
1
31788
2
announce
1
49551
1
29557
1
57998
4
5
1
31788
2
say
1
2
1
3
1
57998
4
4
4
5
1
55718
2
tell_lines
1
2487
1
6
1
55718
4
5
1
-1
2

1
55718
1
-1
1
55718
4
5
1
55718
2
eval_cmd_string
1
55718
1
217
1
55718
4
5
1
55718
2
eval
1
55718
1
217
1
55718
4
4
4
5
1
55718
2
tell_lines
1
2487
1
6
1
55718
4
5
1
-1
2

1
55718
1
-1
1
55718
4
5
1
55718
2
eval_cmd_string
1
55718
1
217
1
55718
4
5
1
55718
2
eval
1
55718
1
217
1
55718
4
2
4
5
1
58217
2
tell_lines
1
2487
1
6
1
58217
4
5
1
16175
2
check
1
19845
1
53431
1
58217
4
7
4
5
1
57956
2
tell_lines
1
2487
1
6
1
57956
4
5
1
45245
2
secure
1
49551
1
29557
1
57956
4
5
1
45245
2
arm
1
49551
1
29557
1
57956
4
5
1
45245
2
say_parse
1
49551
1
29557
1
57956
4
5
1
45245
2
announce
1
49551
1
29557
1
57956
4
5
1
45245
2
say
1
2
1
3
1
57956
4
5
1
57956
2
say
1
24436
1
10068
1
57956
4
7
4
5
1
57956
2
tell_lines
1
2487
1
6
1
57956
4
5
1
45245
2
secure
1
49551
1
29557
1
57956
4
5
1
45245
2
arm
1
49551
1
29557
1
57956
4
5
1
45245
2
say_parse
1
49551
1
29557
1
57956
4
5
1
45245
2
announce
1
49551
1
29557
1
57956
4
5
1
45245
2
say
1
2
1
3
1
57956
4
5
1
57956
2
say
1
24436
1
10068
1
57956
4
6
4
5
1
30246
2
tell_lines
1
2487
1
6
1
30246
4
5
1
25476
2
secure
1
49551
1
29557
1
30246
4
5
1
25476
2
arm
1
49551
1
29557
1
30246
4
5
1
25476
2
say_parse
1
49551
1
29557
1
30246
4
5
1
25476
2
announce
1
49551
1
29557
1
30246
4
5
1
30246
2
say
1
57140
1
49900
1
30246
4
2
4
5
1
48961
2
tell_lines
1
2487
1
6
1
48961
4
5
1
6776
2
@roomo
1
48961
1
6776
1
48961
4
2
4
5
1
48961
2
tell_lines
1
2487
1
6
1
48961
4
5
1
6776
2
@roomo
1
48961
1
6776
1
48961
4
2
4
5
1
48961
2
tell_lines
1
2487
1
6
1
48961
4
5
1
6776
2
@roomo
1
48961
1
6776
1
48961
4
4
4
5
1
58172
2
tell_lines
1
2487
1
6
1
58172
4
5
1
56555
2
secure
1
49551
1
29557
1
58172
4
5
1
56555
2
arm
1
49551
1
29557
1
58172
4
5
1
56555
2
press
1
49551
1
29557
1
58172
4
7
4
5
1
34107
2
tell_lines
1
2487
1
6
1
34107
4
5
1
58228
2
@antisocial
1
34107
1
58228
1
34107
4
5
1
34107
2
my_huh
1
34107
1
6
1
34107
4
5
1
34107
2
my_huh
1
78
1
7069
1
34107
4
5
1
34107
2
my_huh
1
24442
1
26026
1
34107
4
5
1
219
2
do_huh
1
34107
1
219
1
34107
4
5
1
7944
2
@antisocial
1
34107
1
1
1
34107
4
4
4
5
1
53502
2
tell_lines
1
2487
1
6
1
53502
4
5
1
53245
2
secure
1
49551
1
29557
1
53502
4
5
1
53245
2
arm
1
49551
1
29557
1
53502
4
5
1
53245
2
press
1
49551
1
29557
1
53502
4
6
4
5
1
57867
2
tell_lines
1
2487
1
6
1
57867
4
5
1
36302
2
secure
1
49551
1
29557
1
57867
4
5
1
36302
2
arm
1
49551
1
29557
1
57867
4
5
1
36302
2
say_parse
1
49551
1
29557
1
57867
4
5
1
36302
2
announce
1
49551
1
29557
1
57867
4
5
1
36302
2
say
1
2
1
3
1
57867
4
6
4
5
1
57919
2
tell_lines
1
2487
1
6
1
57919
4
5
1
20584
2
secure
1
49551
1
29557
1
57919
4
5
1
20584
2
arm
1
49551
1
29557
1
57919
4
5
1
20584
2
say_parse
1
49551
1
29557
1
57919
4
5
1
20584
2
announce
1
49551
1
29557
1
57919
4
5
1
20584
2
say
1
2
1
3
1
57919
4
4
4
5
1
58173
2
tell_lines
1
2487
1
6
1
58173
4
5
1
41398
2
secure
1
49551
1
29557
1
58173
4
5
1
41398
2
arm
1
49551
1
29557
1
58173
4
5
1
41398
2
press
1
49551
1
29557
1
58173
4
4
4
5
1
58173
2
tell_lines
1
2487
1
6
1
58173
4
5
1
41398
2
secure
1
49551
1
29557
1
58173
4
5
1
41398
2
arm
1
49551
1
29557
1
58173
4
5
1
41398
2
press
1
49551
1
29557
1
58173
4
14
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
26217
2
move
1
2
1
7
1
50367
4
5
1
26217
2
move
1
50367
1
26217
1
50367
4
5
1
26217
2
invoke
1
2
1
7
1
50367
4
5
1
26217
2
invoke
1
50367
1
26217
1
50367
4
5
1
35514
2
d
1
2
1
3
1
50367
4
14
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
26217
2
move
1
2
1
7
1
50367
4
5
1
26217
2
move
1
50367
1
26217
1
50367
4
5
1
26217
2
invoke
1
2
1
7
1
50367
4
5
1
26217
2
invoke
1
50367
1
26217
1
50367
4
5
1
35514
2
d
1
2
1
3
1
50367
4
15
4
5
1
56939
2
tell_lines
1
2487
1
6
1
56939
4
5
1
32361
2
look_self
1
15
1
6792
1
56939
4
5
1
32361
2
enterfunc
1
2
1
3
1
56939
4
5
1
32361
2
enterfunc
1
2693
1
258
1
56939
4
5
1
56939
2
moveto
1
56939
1
1
1
56939
4
5
1
56939
2
moveto
1
47
1
6
1
56939
4
5
1
56939
2
moveto
1
47
1
3133
1
56939
4
5
1
56939
2
moveto
1
78
1
7069
1
56939
4
5
1
56939
2
moveto
1
3685
1
8855
1
56939
4
5
1
56939
2
moveto
1
57140
1
49900
1
56939
4
5
1
26217
2
move
1
2
1
7
1
56939
4
5
1
26217
2
move
1
50367
1
26217
1
56939
4
5
1
26217
2
invoke
1
2
1
7
1
56939
4
5
1
26217
2
invoke
1
50367
1
26217
1
56939
4
5
1
35514
2
d
1
2
1
3
1
56939
4
3
4
5
1
56939
2
tell_lines
1
2487
1
6
1
56939
4
5
1
32361
2
look_self
1
15
1
6792
1
56939
4
5
1
32361
2
l
1
2693
1
258
1
56939
4
3
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
l
1
2693
1
258
1
50367
4
14
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
26217
2
move
1
2
1
7
1
50367
4
5
1
26217
2
move
1
50367
1
26217
1
50367
4
5
1
26217
2
invoke
1
2
1
7
1
50367
4
5
1
26217
2
invoke
1
50367
1
26217
1
50367
4
5
1
35514
2
d
1
2
1
3
1
50367
4
15
4
5
1
56939
2
tell_lines
1
2487
1
6
1
56939
4
5
1
32361
2
look_self
1
15
1
6792
1
56939
4
5
1
32361
2
enterfunc
1
2
1
3
1
56939
4
5
1
32361
2
enterfunc
1
2693
1
258
1
56939
4
5
1
56939
2
moveto
1
56939
1
1
1
56939
4
5
1
56939
2
moveto
1
47
1
6
1
56939
4
5
1
56939
2
moveto
1
47
1
3133
1
56939
4
5
1
56939
2
moveto
1
78
1
7069
1
56939
4
5
1
56939
2
moveto
1
3685
1
8855
1
56939
4
5
1
56939
2
moveto
1
57140
1
49900
1
56939
4
5
1
26217
2
move
1
2
1
7
1
56939
4
5
1
26217
2
move
1
50367
1
26217
1
56939
4
5
1
26217
2
invoke
1
2
1
7
1
56939
4
5
1
26217
2
invoke
1
50367
1
26217
1
56939
4
5
1
35514
2
d
1
2
1
3
1
56939
4
3
4
5
1
56939
2
tell_lines
1
2487
1
6
1
56939
4
5
1
32361
2
look_self
1
15
1
6792
1
56939
4
5
1
32361
2
l
1
2693
1
258
1
56939
4
14
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
26217
2
move
1
2
1
7
1
50367
4
5
1
26217
2
move
1
50367
1
26217
1
50367
4
5
1
26217
2
invoke
1
2
1
7
1
50367
4
5
1
26217
2
invoke
1
50367
1
26217
1
50367
4
5
1
35514
2
d
1
2
1
3
1
50367
4
10
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
5443
2
done
1
6336
1
5400
1
50367
4
14
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
26217
2
move
1
2
1
7
1
50367
4
5
1
26217
2
move
1
50367
1
26217
1
50367
4
5
1
26217
2
invoke
1
2
1
7
1
50367
4
5
1
26217
2
invoke
1
50367
1
26217
1
50367
4
5
1
35514
2
d
1
2
1
3
1
50367
4
10
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
5443
2
done
1
6336
1
5400
1
50367
4
3
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
l
1
2693
1
258
1
50367
4
14
4
5
1
50367
2
tell_lines
1
2487
1
6
1
50367
4
5
1
32361
2
look_self
1
15
1
6792
1
50367
4
5
1
32361
2
enterfunc
1
2
1
3
1
50367
4
5
1
32361
2
enterfunc
1
2693
1
258
1
50367
4
5
1
50367
2
moveto
1
50367
1
1
1
50367
4
5
1
50367
2
moveto
1
47
1
6
1
50367
4
5
1
50367
2
moveto
1
47
1
3133
1
50367
4
5
1
50367
2
moveto
1
78
1
7069
1
50367
4
5
1
50367
2
moveto
1
3685
1
8855
1
50367
4
5
1
26217
2
move
1
2
1
7
1
50367
4
5
1
26217
2
move
1
50367
1
26217
1
50367
4
5
1
26217
2
invoke
1
2
1
7
1
50367
4
5
1
26217
2
invoke
1
50367
1
26217
1
50367
4
5
1
35514
2
down
1
2
1
3
1
50367
4
4
4
5
1
53502
2
tell_lines
1
2487
1
6
1
53502
4
5
1
10202
2
secure
1
49551
1
29557
1
53502
4
5
1
10202
2
arm
1
49551
1
29557
1
53502
4
5
1
10202
2
press
1
49551
1
29557
1
53502
4
6
4
5
1
57919
2
tell_lines
1
2487
1
6
1
57919
4
5
1
20584
2
secure
1
49551
1
29557
1
57919
4
5
1
20584
2
arm
1
49551
1
29557
1
57919
4
5
1
20584
2
say_parse
1
49551
1
29557
1
57919
4
5
1
20584
2
announce
1
49551
1
29557
1
57919
4
5
1
20584
2
say
1
2
1
3
1
57919
36
1
0
0
36
4
4
1
2
list_utilities
36
5
4
31
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

2
make              (n[,e])        => list of n copies of e
2
range             (m,n)          => {m,m+1,...,n}
2

2
arrayset   (list,val,i[,j,k...]) => array modified so that list[i][j][k]==val
2

2
-- Mapping functions (take a list and do something to each element):
2

2
map_prop ({o...},prop)              => list of o.(prop)            for all o
2
map_verb ({o...},verb[,args)        => list of o:(verb)(@args)     for all o
2
map_arg  ([n,]obj,verb,{a...},args) => list of obj:(verb)(a,@args) for all a
2

2
-- Association list functions --
2

2
An association list (alist) is a list of pairs (2-element lists), though the following functions have been generalized for lists of n-tuples (n-element lists).  In each case i defaults to 1.
2

2
assoc        (targ,alist[,i]) => 1st tuple in alist whose i-th element is targ
2
iassoc       (targ,alist[,i]) => index of same.
2
assoc_prefix (targ,alist[,i]) => ... whose i-th element has targ as a prefix
2
iassoc_prefix(targ,alist[,i]) => index of same.
2
slice             (alist[,i]) => list of i-th elements
2
sort_alist        (alist[,i]) => alist sorted on i-th elements.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#55
command utilities

16
2
-1
-1
-1
83
-1
54
20
object_match_failed player_match_failed player_match_result
2
173
-1
read
2
165
-1
read_lines
2
165
-1
yes_or_no
2
165
-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
suspend_database_add
2
173
-1
suspend_database_remove
2
173
-1
suspend_database_cleanup
2
173
-1
task_info
2
173
-1
suspend_database_display
2
173
-1
suspend_database_info
2
173
-1
standard_features
2
173
-1
init_for_core
2
173
-1
include_with_core
2
173
-1
5
suspend_database
char_features
player_features
gm_features
prog_features
14
4
0
2
0
4
3
1
207
1
152
1
208
2
1
4
6
1
85
1
210
1
211
1
213
1
214
1
193
2
1
4
3
1
212
1
215
1
216
2
1
4
1
1
129
2
1
0
0
2
4
5
2
5
4
31
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

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
4
2
0
18243
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#56
generic wizard

16
2
-1
-1
-1
57
2
186
38
@chown
2
81
-2
@grant @grants* @transfer
2
89
1
@programmer
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
81
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*! @make-user*! @make-char*acter!
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
$wiz:paranoid_moveto
2
165
-1
@newt
2
17
-1
@unnewt @denewt @get-better
2
81
-2
@register
2
89
-2
@new-password @newpassword
2
89
-2
@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
2
13
-1
@blacklist @graylist @redlist @unblacklist @ungraylist @unredlist @spooflist @unspooflist
2
89
-2
@corify*!
2
89
13
@make-guest
2
25
-1
@mail-new-player
2
25
-1
@programmer(oq)
2
25
-1
@quota(oq)
2
81
12
@zap
2
25
-1
init_for_core
2
173
-1
make-core-database
2
25
-1
@reserve-name
2
25
-1
@finger finger
2
9
-1
@copyo*bject
2
89
-2
@unalt
2
25
-1
8
newt_victim_msg
newt_msg
public_identity
programmer_msg
programmer_victim_msg
toad_victim_msg
toad_msg
mail_identity
193
2

2
5
2
%n @newts %d (%[#d])
2
5
1
-1
2
5
2

2
5
2
The reality surrounding you collects into logical arrangements, washing your mind with a new enlightenment.  Your perception of all things real has forever been altered.  [You're a programmer.]
2
5
2
Have a nice life...
2
5
2
%n @toads %d (%[#d])
2
5
1
-1
2
4
4
0
36
1
0
1
2
5
0
0
2
5
5
2
5
0
48
2
5
0
0
2
1
4
0
2
0
5
2
5
5
36
0
5
36
1
5
2
0
5
2
5
5
2
0
5
2
1
1
23
2
5
5
2
5
5
36
1
5
36
1
4
0
36
0
5
36
1
5
2
5
4
0
2
1
5
2
4
4
0
2
0
5
2
0
4
0
2
4
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
4
4
0
2
4
5
2
1
5
100
0
2
really impossible password to type
2
0
5
2
5
5
2
1
4
0
115
1
5
2
5
1
159
2
5
5
100
1
5
2
0
5
2
5
5
2
1
5
36
1
5
2
5
5
115
1
4
0
36
0
5
2
5
5
115
1
5
115
1
5
36
0
5
115
1
5
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
2
5
5
115
1
5
115
1
1
206
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
1
61
2
5
5
2
5
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
115
1
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
4
5
2
5
5
2
4
5
2
5
5
115
0
5
115
1
5
36
0
5
115
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
5
2
4
5
2
4
5
36
1
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
54273
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#57
generic programmer

144
2
-1
-1
-1
4
56
-1
29
@args
2
81
-2
eval*-d eval? eval-d?
2
81
-2
@verb
2
81
-2
@rmverb
2
17
-1
@forked
2
17
-1
@kill
2
17
-1
@edit
36
89
-2
@copy @copy-x @copy-move
2
81
1
_kill_task_message
2
173
-1
@prog*ram
2
81
-2
@setenv
2
81
-2
@db*size
2
9
-1
@gethelp
2
85
-2
@prospectusDB
2
85
-2
@grep @egrep
2
81
-2
@check-p*roperty
2
17
-1
set_eval_env
2
165
-1
@clearp*roperty @clprop*erty
2
17
-1
eval_cmd_string
2
173
-1
#*
2
89
-2
eval_value_to_string
2
173
-1
@ps @que*ued
2
13
-1
option_packages
2
173
-1
@sbk
2
89
-2
notify
2
165
-1
@resp*onsible @eresp*onsible
2
89
-2
@stream-edit*! @sed*it! @sed!
2
81
-2
@prop(oq)
2
81
-2
init_for_core
2
173
-1
7
eval_subs
eval_time
eval_ticks
eval_env
sbk_lines
sbk_off
sbk
185
4
0
36
1
0
1
2
5
0
0
2
5
2
me = player; here = player.location;
2
5
0
48
2
5
0
0
2
1
4
0
2
0
5
2
5
5
36
0
5
36
1
5
2
0
5
2
5
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
4
0
36
0
5
36
1
5
2
5
4
0
2
1
5
2
4
4
0
2
0
5
2
0
4
0
2
4
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
4
4
0
2
4
5
2
1
0
0
100
0
5
2
0
5
2
5
5
2
1
4
0
115
1
5
2
5
1
159
2
5
5
100
1
5
2
0
5
2
5
5
2
1
5
36
1
5
2
5
5
115
1
4
0
36
0
5
2
5
5
115
1
5
115
1
5
36
0
5
115
1
5
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
2
5
5
115
1
5
115
1
1
206
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
1
61
2
5
5
2
5
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
115
1
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
4
5
2
5
5
2
4
5
2
5
5
115
0
5
115
1
5
36
0
5
115
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
5
2
4
5
2
4
5
36
1
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
46241
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#58
code utilities

16
36
-1
-1
-1
83
-1
55
58
eval_d
2
85
-2
1
2
165
-1
tonum toint
36
173
-1
toobj
36
173
-1
toerr
36
173
-1
error_name
36
173
-1
show_object
2
165
-1
show_property
2
165
-1
show_verbdef
2
165
-1
explain_verb_syntax
2
173
-1
verb_p*erms verb_permi*ssions
2
173
-1
verb_loc*ation
36
173
-1
verb_doc*umentation
2
165
-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
165
-1
_fix_preps
36
165
1
find_verb_named
2
165
-1
find_last_verb_named
2
165
-1
find_callable_verb_named
2
165
-1
find_verbs_containing
2
173
-1
_find_verbs_containing
2
165
-1
find_verbs_matching
2
173
-1
_find_verbs_matching
2
165
-1
_grep_verb_code
2
165
-1
_egrep_verb_code
2
165
-1
_parse_audit_args
36
173
-1
help_db_list
2
165
-1
help_db_search
36
165
-1
corify_object
36
165
-1
substitute
2
173
-1
inside_quotes
2
173
-1
verb_or_property
2
165
-1
task_valid
2
165
-1
task_owner
2
173
-1
argstr
2
173
-1
verbname_match
2
173
-1
show_who_listing
2
165
-1
_egrep_verb_code_all
2
173
-1
_grep_verb_code_all
2
173
-1
_grep_verb_code_all
36
173
-1
verb_usage
2
165
-1
args_with_quotes
2
173
-1
verb_id
2
173
-1
call_verb
2
165
-1
get_prop*erty_value
2
165
-1
notify
2
165
-1
is_secure_verbcall
2
165
-1
has_message
2
165
-1
display_line_verb
2
173
-1
display_line_prop*erty
2
173
-1
type_name
36
173
-1
dflag_on
2
173
-1
10
prepositions
_version
_multi_preps
_other_preps_n
_other_preps
_short_preps
_all_preps
builtin_props
error_names
error_list
19
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
2
1.8.0r5
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
15
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
36
5
4
15
3
0
3
1
3
2
3
3
3
4
3
5
3
6
3
7
3
8
3
9
3
10
3
11
3
12
3
13
3
14
36
5
0
0
36
4
4
2
2
code
2
utils
36
5
4
45
2
parse_propref("foo.bar")  => {"foo","bar"} (or 0 if arg. isn't a property ref.)
2
parse_verbref("foo:bar")  => {"foo","bar"} (or 0 if arg. isn't a verb ref.)
2
parse_argspec("any","in","front","of","this","baz"...)
2
                          => {{"any", "in front of", "this"},{"baz"...}} 
2
                                           (or string if args don't parse)
2

2
tonum(string)           => number (or E_TYPE if string is not a number)
2
toobj(string)           => object (or E_TYPE if string is not an object)
2
toerr(number or string) => error value (or 1 if out of range or unrecognized)
2
error_name(error value) => name of error (e.g., error_name(E_PERM) => "E_PERM")
2

2
verb_perms()    => the current task_perms (as set by set_task_perms()).
2
verb_location() => the object where the current verb is defined.
2
verb_documentation([object,verbname]) => documentation at beginning of
2
           verb code, if any -- default is the calling verb
2

2
   Preposition routines
2

2
prepositions()     => full list of prepostions
2
full_prep ("in")   => "in/inside/into"
2
short_prep("into") => "in"
2
short_prep("in/inside/into") => "in"
2
get_prep  ("off", "of", "the", "table") => {"off of", "the", "table"}
2

2
   Verb routines
2

2
verbname_match (fullname,name) => can `name' be used to call `fullname'
2
find_verb_named          (object,name[,n]) => verb number or -1 if not found
2
find_callable_verb_named (object,name[,n]) => verb number or -1 if not found
2
find_verbs_containing (pattern[,object|objlist])
2

2
   Verbs that do the actual dirty work for @show:
2

2
show_object  (object)
2
show_property(object,propname)
2
show_verbdef (object,verbname)
2

2
   Dirty work for explain_syntax
2

2
explain_verb_syntax(thisname,verbname,@verbargs)
2

2
   A random but useful verb
2

2
verb_or_property(object,name[,@args]) => result of verb or property call,
2
                                         or E_PROPNF
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#59
Help Database

16
36
-1
-1
-1
30
-1
43
9
player_quota
2
173
-1
prog_quota
2
173
-1
get_topic
2
165
-1
find_topics
2
165
-1
full_index
36
173
-1
index_list
36
165
-1
wizard_list
2
165
-1
dump_topic
36
173
-1
trustable_actions
2
173
-1
210
@locations
@uptime
@mailoptions
@sort-owned
@verify-owned
@add-owned
@mail-options
wizard-list
@wrap
full-index
 index
gen-index
mail-forwarding
@pagelength
@more
programming
@forward
@subscribe
@rn
@unsubscribe
@skip
negative_quota
zombie-messages
message-sequences
common_quota
@recreate
@linelength
room-messages
@unrmmail
@gaglist
::
@comment
@remove-entrance
@remove-exit
@parents
@contents

spoofing
privacy
@realm
@resident
@examine
security
@sweep
@paranoid
@check
@reply
@eject
@quit
whereis
@suggest
@idea
@bug
@typo
@renumber
@notedit
editors
@prev
@unlock_for_open
@lock_for_open
@opacity
container-messages
@memory
"
:
@lastlog
@version
miscellaneous
insert
information
?
put
remove
burn
letters
decrypt
encrypt
delete
erase
write
read
examine
hand
key-representation
keys
@unlock
@lock
locking
thing-messages
throw
take
@messages
pronouns
exit-messages
messages
descriptions
@describe
@add-entrance
@add-exit
topology
@classes
@audit
@count
@quota
@create
tinymud
@next
@answer
@rmmail
@read
@send
mail
@gripe
creation
@mail
@listgag
@ungag
@gag
go
@password
@sethome
introduction
give
news
gagging
@dig
@move
inventory
@entrances
@exits
@gender
@recycle
@rename
containers
notes
look
drop
get
manipulation
help
rooms
movement
home
communication
say
whisper
page
emote
building
players
summary
@edit-options
@editoptions
@add-feature
@remove-feature
@features
features
@dump
me
@rmalias
@addalias
commands
 name
 alias
@setprop
@set
@peek
@subscribed
@request-character
player-names
details
integration
following
theme
premise
realtime
sponsor
subscription
access
@undetail
@details
@detail
trust
trustable-actions
trusting
clothing
clothing-messages
nudity
mood
character
ooc
_character
combat
WHO
ansi
stage
_game_help_core
builder-advice
page-details
building-advice
customizing
size
speech
staff-ethics
@reroll
sheet
death
staff-rules
replication
chargen
220
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
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
2
2
*forward*
2
@mail-options
36
1
4
3
2
Syntax:  @sort-owned
2

2
Sorts your .owned_objects property so @audit shows up sorted.  See help @audit for more information.
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
52
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      @reply's start out with a blank message body
2
 +include      @reply's start with original message included
2
 -all          @reply's go to sender only
2
 +all          @reply's go to sender and all original recipients
2
 -nosubject    @send forces you to provide a Subject: line
2
 +nosubject    allow entering the mail editor without giving a subject line
2
 -enter        start each mail editing session in the usual command mode.
2
 +enter        start each mail editing session with an implicit `enter' command
2
 -expert       novice mail user (various annoying messages will be printed)
2
 +expert       expert mail user (suppress printing of annoying messages)
2
 -sticky       each mail command applies by default to one's own collection
2
 +sticky       each mail command applies by default to the same message 
2
                 collection that the previous successful command did
2

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.
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
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
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
3
2
Syntax:  @forward <msg> [on *<collection>] to <recipient> [<recipient>...]
2

2
Takes the indicated message in your (or some other) message collection, creates a new message whose body is the original message (both headers and body) and sends it on to the given recipients.
36
5
4
23
2
Syntax:  @subscribe *<collection>
2
         @subscribe
2

2
The first form of this command does two things:
2

2
(1) it sets up a current message and a last-read-time for the given mail 
2
    collection so that when next you log in, you will be informed about new 
2
    mail that has appeared there.  Note that this happens automatically
2
    whenever you @read messages on a given collection, so if this much is 
2
    all you care about, you don't need to use this command; just do, e.g.,
2
             @read last on *<collection>
2

2
(2) adds you to the .mail_notify list for that collection, so that you will
2
    be notified *immediately* whenever new mail is sent there.
2

2
You can only @subscribe to collections that are readable by you.
2
The second form of the command gives a list of collections available to 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
10
2
*subst*
2
We recently reduced the initial quota for new players to %[$help:player_quota()].
2
Those of you who were around before this are covered by a grandfather clause
2
i.e., you get to keep your over-quota objects.  However this means that 
2
your quota for creating new objects may now be *more than* used up, i.e.,
2
negative.  In particular, this means that if you recycle an object, 
2
you will not be able to @create one in its place.
2

2
You can, however, re-use your over-quota objects by using the @recreate 
2
command instead (see `help recreate').
36
5
4
2
2
*forward*
2
@unrmmail
36
5
4
45
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
  last      the final message if any (`$' is a synonym for `last')
2

2
You may use as many of these at once as sanity permits, e.g.,
2

2
  @mail cur 1..5 last
2

2
which will display the header for your current message, your messages in the range 1..5, and your last message.  Though some of these ranges may overlap, the header for any given message is only shown once in any event.
2

2
In addition, there are other message-sequence arguments that act as filters
2
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
 subject:<string>            messages with <string> in the subject
2
 body:<string>               messages with <string> in the body (SLOW!!!)
2
 first:<number>              the first <number> messages
2
 last:<number>               the last <number> messages
2

2
<date>  is either a weekday, an 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 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
5
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
5
2
Usage: @recreate <object> as <parent> named <name spec>
2

2
This is a combination of @create and @chparent.  It takes an existing object, completely strips it of any verbs, properties, and values for inherited properties.  This object is then reshaped into a child of the parent specified, as though @create had been called, but retaining the same object number as the original.
2

2
The <parent> and <name spec> arguments are as in @create.
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 LambdaMOO 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 LambdaMOO'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
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:  @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
@listgag
36
5
4
2
2
*forward*
2
emote
36
5
4
2
2
*forward*
2
@typo
36
5
4
3
2
Syntax:  @remove-entrance <entrance>
2

2
Remove the specified entrance from the current entrances list of the room.  Entrance may be either the name or object number of an entrance to this room.
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
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
4
2
*forward*
2
summary
2

2
Type `help <topic>` for information on a particular topic.
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 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 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
17
2
Syntax: @realm [owners] [from root] [missing descendants]
2

2
Displays a part of the parenting tree of objects on the MOO, i.e. all
2
the descendants of a given object owned by particular players.
2

2
owners:      a list of players whose objects you are interested in. If
2
             one of these is * or !, @realm will display objects only if
2
             they belong to players you didn't mention. (defaults to you)
2
root:        the object which is an ancestor of all the objects you are
2
             interested in. (defaults to $root_class).
2
descendants: a list of objects (descendants of root) which you are not
2
             interested in. Neither they nor their descendants will be
2
             displayed.
2

2
@realm also displays the objects which are ancestors of root, and, if it
2
is not redundant, the owners of all objects. @realm has a habit of
2
running out of ticks if you try displaying large chunks of the MOO.
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
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
2
2
*forward*
2
@answer
36
5
4
10
2
Syntax: @eject <object>
2
        @eject <object> from <place>
2

2
This command is used to remove unwanted objects from places you own.  Players thus removed are unceremoniously dumped in their homes (unless that's this room, in which case they are dumped in the default player starting place).  Other kinds of objects are checked for a .home property and sent there if possible, otherwise they get thrown into #-1.  Unlike @move, @eject does *not* check to see if the object wants to be moved, and with the destination being what it is, there is no question of the destination refusing the move, either.  Generally, you should only resort to @eject if @move doesn't work.
2

2
The first form of the command removes the object from the current room.  The second form removes the object from the specified place (which, in most cases, you'll have to specify as an object number).  In either case, this command only works if you own the room/entity from which the object is being ejected.
2

2
"@eject ... from me" suffices to get rid of some unwanted object in your inventory.
2

2
See 'help room-messages' for a list of messages one can set regarding the @eject command.
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
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
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:  @prev [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.
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
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
5
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
@lc      -- information on the last (and next) checkpoint
2
@uptime  -- how long the MOO has been up without a reboot
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
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
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
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
8
2
Syntax: @messages <object>
2
        @messages <object> from <parent>
2
 
2
List all of the messages that can be set on the named object and their current values.
2
 
2
The second form restricts messages listed to those inherited from the given parent.
2
 
2
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
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
19
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
2
clothing-messages -- the messages on clothing
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
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
8
2
The topology of the MOO universe is determined by the rooms that exist and the exits that connect them.  Several commands are available for creating and discovering the topology of the MOO.  Help on them is available under the following topics:
2

2
@dig -- creating new rooms and exits
2
@add-exit -- adding other players' exits from your rooms
2
@add-entrance -- adding other player's entrances to your rooms
2
@exits -- listing all of the conventional exits from your rooms
2
@entrances -- listing all of the conventional entrances to your rooms
2
@resident -- listing or changing the residents of your rooms
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
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
1
4
8
2

2
One very significant difference between this MOO and many others you will explore is that we do not allow free use of the @create command.  The power of creation is the power to change the reality itself, and in a virtual world such as ours, this power must be limited.
2

2
You may still create objects, and in many cases they'll be owned by you as with the @create command elsewhere.  To do this, you have to find "replicators" which will create objects chosen from their menus.
2

2
As with any object, type `examine replicator` when you find one to see a list of its commands.  There are many replicators scattered through the world, each providing a different menu of items.
2

2
You can start with the replicator in the Real/Time Lounge, just up and north of the Underground.
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 parcftp.xerox.com as pub/MOO/contrib/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
3
2
Syntax:  @next [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.
36
1
4
13
2
Syntax:  @answer [<message-number>] [sender] [all] [include] [noinclude]
2

2
Synonym: @reply
2

2
Prepares for you to compose a MOO email message to the players who either received or composed the indicated message from your collection.  The usual editor is invoked (see `help editors' for details).  The subject line for the new message will be initialized from that of the indicated message.  If you leave off the message number, the reply will be to your current message, if that exists.  In fact you may give a general message-sequence (see `help message-sequences') argument here, provided that it indicates only a single message (e.g., `@answer last:1')
2

2
If there is a Reply-to: field in the message you are @answer'ing, its contents will be used to initialize the To: line of your reply.  Otherwise, a To: line is determined depending on whether you specified `sender' or `all' in the command line (or your .mail_options).
2

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

2
`sender', `all', `include', and `noinclude' can each be abbreviated (e.g., `@answer i').
2

2
Defaults are `sender' and `noinclude', but you can change this by setting your .mail-options (see `help mail-options').  
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
33
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

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
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
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
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
4
2
Syntax:  @listgag
2
         @gaglist
2

2
Shows you a list of the players and objects currently on your 'gag list'.  You don't see any messages that result from actions initiated by the players or objects on this list.  In particular, you will not hear them if they try to speak, emote, or whisper to you.  See 'help gagging' for an explanation of gagging in general.
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
7
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
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
16
2
MOO is a virtual reality in which people explore the evolving dreamworld around them.  There is no `goal' except discovery, knowledge, and fun.
2
 
2
Most commands have the form of simple English sentences:
2
    <verb>
2
    <verb>  <direct object>
2
    <verb>  <direct object>  <preposition>  <indirect object>
2
 
2
Don't use English articles (e.g. 'a', 'an', or 'the') in your commands; the MOO probably 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 people in the same room as you
2
WHO       -- showing which people 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
3
2
Syntax:  news
2

2
Read the latest edition of the LambdaMOO Times, which carries articles concerning recent changes to the MOO server or to the main public classes, or whatever the wizards feel like writing about.
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
27
2
Syntax:  @dig "<new-room-name>"
2
         @dig <exit-spec> to "<new-room-name>"
2
         @dig <exit-spec> to <old-room-object-number>
2
          @dig <exit-spec> to <old-room-object-number> with <exit-parent>
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
The 'with <exit-parent>' option allows you to specify the exit to which you would like the exits to be parented to.  For example, you can use '$door' to tell the command that you would like the exits to and from to be doors.
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
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
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
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
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
20
2
Syntax: @rename <object>        to [name-and-alias],<alias>,...,<alias>
2
        @rename <object>        to [name]:<alias>,...,<alias>
2
        @rename <object>:<verb> to <new-verb-name>
2

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

2
Munchkin changes his dog's aliases:
2
  @rename #4237 to ,Rover,dog,Rover the Wonder Dog
2
The name remains the same--we still see 'Rover the Wonder Dog'--but now any of 'Rover', 'dog', or 'Rover the Wonder Dog' can be used to refer to him.  This can help reduce confusion.
2

2
The third 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.
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
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
18
2
 
2
Syntax: look
2
        look <object>
2
        look <first, second, etc> <object>
2
        look <someone>'s <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'.   See 'help @describe' for how to set an object's description.
2
 
2
The third form allows you to look at one of several objects with the same name.
2
 
2
The last two forms show you the description of an object contained in another object, including those carried by another player.
2
 
2
In addition, the fourth syntax will allow you to look at `details' of another object.  See 'help details' for more information.
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
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
14
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 special teleportation.
2

2
Almost all areas permit the use of the 'home' command to teleport you to your designated home (see 'help home' for more details), and the 'start' command to take you to the origin point for all players.
2

2
Other movement topics that may interest you include:
2

2
following      -- Following someone around.
2
walk           -- Walking automatically to a person or place.
2
join           -- Walking to a person.
2
start          -- Completely lost?  Use this to return to the starting point.
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
18
2
There are several commands available to allow you to communicate with your fellow MOO comrades.  Help is available on the following communication- related topics:
2

2
say      -- talking to the other characters in the room
2
emote pose
2
         -- non-verbal communication with others in the same room
2
murmur whisper
2
         -- talking (semi-)privately to someone in the same room
2
gibber   -- gibber in a readable code
2
stage    -- directed speech, thought bubbles, etc
2
page     -- yelling to someone anywhere in the MOO
2
news     -- reading the wizards' most recent set of general announcements
2

2
@gripe   -- sending complaints to the wizards
2
@typo @bug @idea @suggest
2
         -- sending complaints/ideas to the owner of the current room
2

2
mail     -- the MOO email system
2
security -- the facilities for detecting forged messages and eavesdropping.
36
1
4
21
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!"
2
 
2
You can change the way you say something by setting your @speech message.
2
 
2
                       @my speech is "%N %<growls>, \"$text\""
2
Would result in:       John_Doe growls, "Whatever he said."
2
 
2
"$text" subs to whatever you said.  You can go back to the old style of saying things by clearing the message via `@my speech is ""'.
2
 
2
See `help pronouns' for more information on pronoun substitution in messages.
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
15
2
        Syntax:
2
 page <user> with <message>
2
 page <user> <message>
2
 page <user>=<message>
2
 '<user> <message>
2

2
Page is used to send OOC messages, emotes, or simple "pings" to a user or users.  The general formats above cover most MUD syntax.
2

2
You can send to multiple users by delimiting their names with spaces.  In some cases, you may have to surround all the names with quotes (").
2

2
You can send an emote by using ":" or "::" as the first character of the message, OR by using the shorthand `+user emote`, or `++user emote`.
2

2
With the shortcuts "'" and "+", leaving out the "user" arg will respond to the last person you paged or emoted.
2

2
See `help page-details` for examples and even MORE info on paging.
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
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
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
24
2

2
             Help is available on the following general topics:
2
 
2
intro         -- what's going on here and some basic commands
2
 
2
theme         -- where the heck am I?
2
access        -- information on Internet access from our sponsor
2

2
character     -- What do "IC" and "OOC" mean?
2
chargen       -- Molding your character's stats
2
sheet         -- How to compose your character "sheet"
2
combat        -- Skip the crap--How do I KILL KILL KILL?
2
 
2
players       -- setting characteristics of yourself
2
clothing      -- clothing and nudity
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
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
36
5
4
21
2
Syntax:  @edit-option
2
         @edit-option <option>
2

2
Synonym:  @editoption
2

2
The edit options customize the behavior of the various editors (mail editor, verb editor, etc...) to your particular taste.  The first form of this command displays all of your edit options.  The second form displays just that one option, one of the flags listed below.
2

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

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

2
These respectively set and reset the specified flag
2

2
-quiet_insert    insert (") and append (:) echo back the line numbers
2
+quiet_insert    insert (") and append (:) produce no output
2
-eval_subs       (VERB EDITOR) ignore .eval_subs when compiling verbs
2
+eval_subs       (VERB EDITOR) apply .eval_subs to verbs being compiled
2
-local           Use in-MOO text editors.
2
+local           Ship text to client for local editing.
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
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
1
2
The MOO helps those who help themselves.  Try starting with `help summary'.
36
5
4
7
2
Syntax: @rmalias <alias>[,...,<alias>] from <object>
2
        @rmalias <alias>[,...,<alias>] from <object>:<verb>
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.
36
5
4
22
2
Syntax: @addalias <alias>[,...,<alias>] to <object>
2
        @addalias <alias>[,...,<alias>] to <object>:<verb>
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.
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.
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
2
2
*forward*
2
 name
36
5
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
@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 new messages.
36
5
4
11
2
Usage:  @request <player-name> for <email-address>
2

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

2
Usage:  @request <player-name>
2

2
If you already have a character, and wish another for role-playing or other purposes, you can `@request` one from your primary character and have it instantly created.
2

2
These 'alternate characters' are usually limited to two per player.
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
15
2
Details are attributes of an object.  They describe parts of an object, or features of a room or person, that do not merit a `real' object.
2
 
2
Look at details with one of the following syntax:
2
 
2
look at object's detail
2
look detail on object
2
look detail                    -- (for room details only)
2
 
2
Be careful with your use of details.  Details should only be features or sub-parts of an object.  If you say a potted plant is in the room, go ahead and @create a potted plant.
2
 
2
However, if you just want to add a wart on your nose, you don't need to make a wart object (or a nose, for that matter).  It's simple as:
2
 
2
@detail my schnoz,nose,beak,wart as "A horrid warted thing between his eyes."
2
 
2
See also: @detail, @undetail, and @details.
36
5
4
60
2
Integration, in the MOO sense, is the way objects fit into their containing object's appearance.  By default, objects are listed in a rather bland way:
2
 
2
|Lobby
2
|A spacious room with all the amenities.
2
|You see map and chair here.
2
 
2
Boring, eh?  Let's say you want the map to be a framed wall-map.
2
 
2
>@look_place map is "A map of the complex is on the north wall, framed in oak and protected by glass."
2
 
2
Now we'll see:
2
 
2
|Lobby
2
|A spacious room with all the amenities.  A map of the complex is on the north wall, framed in oak and protected by glass.
2
|You see chair here.
2
 
2
But we still have that chair.  Some generic objects have their own `look_place_msg' verbs to handle special behaviour.  $furniture is one of these.  So-- let's @chparent that chair to $furniture...
2
 
2
|Lobby
2
|A spacious room with all the amenities.  A map of the complex is on the north wall, framed in oak and protected by glass.  Chair is invitingly empty.
2
 
2
Voila!  All this goddamned documenting is making me tired, so I'm gonna sit down.
2
 
2
|Lobby
2
|A spacious room with all the amenities.  A map of the complex is on the north wall, framed in oak and protected by glass.  Quinn is sprawled out on the chair, legs over the arm.
2
 
2
oH my god!  Did you see that?  Heh.  Anyway, there's an example of how objects can integrate into a room.  There's another type of integration-- that of objects into the character carrying them.
2
 
2
I like that chair so much I'm gonna take it with me.
2
 
2
|Quinn
2
|One sexy son of a bitch.
2
|He is carrying chair.
2
 
2
I'm carrying a chair?  Hrm.  True enough, but when someone carries a chair around it's pretty obvious.  Let's spice things up.
2
 
2
>@look_person chair is "For some reason, this moron is carrying a chair around."
2
 
2
|Quinn
2
|One sexy son of a bitch.  For some reason, this moron is carrying a chair around.
2
 
2
Cool, eh?  Now let's review:
2
 
2
look_place     -- Shown when object is integrating into a room.
2
look_person    -- Shown when object is integrating into a person.
2
 
2
Pronoun substitutions are performed as follows:
2
 
2
look_place_msg  --
2
                       %N -- Object                    %D -- Player
2
 
2
look_person_msg --
2
                       %N -- Person                    %T -- Object
2
                       %L -- Person's location         %D -- Player
2
 
2
See `help pronouns' for more information on pronoun substitutions.
2
 
2
In most cases, integration such as the above will do fine.  However, in some cases you may desire to program more complex integration into your objects.
2
 
2
Programmers can find further help with `help me|here:integrators', or by @listing $furniture:look_place_msg and other :look_place_msgs.
36
5
4
7
2
Following another character helps you to easily stay with em when e leaves the room through a traditional exit.  Keep in mind that teleportation and other super-natural means of exit will probably cause you to lose em.
2
 
2
You may find the `following' (heh--i'm such a punster) verbs useful:
2
 
2
following              -- Show who you're following.
2
follow <character>     -- Follow the given character.
2
abandon <character>    -- Stop following the given character.
2
1
4
17
2
Nuclear armageddon, biogenetic holocaust, meteorological catastrophe, magnetic shift, deep impact, environmental devolution.  The nature of the Cataclysm is lost to history.  Something happened, and the world, as they knew it, was gone.
2

2
Ironically, the powermongers who had (according to some scenarios) caused the Cataclysm comprised the majority of the survivors.  Stripped of their mindless consumer flock, these evil shepherds retreated deeper into their vaults as the fires of the Cataclysm cleansed the Earth above.
2

2
Through the turmoil of the Cataclysm, those without shelter, those few who survived the initial apocalypse, carved their futures from the bones of civilization.  Lone survivors confronted, conflicted, united, raised families, settled into communities.  Slowly, the Earth was healing.
2

2
As the pioneer revenants of the Wasteland rebuilt, the "worms" crawled upward. They did not stray far from their vaults, building cities around their entrances.  They had retained networks of communication with vaults around the world, and began to rebuild commerce between them.  Cities rose from the ashes.
2

2
Initially, the cities were heavily guarded against "visits" from without.  Their leaders were certain anyone surviving the Cataclysm "naked" must surely be some kind of monster.  All prejudices are grounded in some fear of the truth, of course.  There were "monsters" in this new world.  Many of the outsiders bore progeny "monstrous" by old world standards.  Something had been mixed with the stuff of humans -- something of gods and demons.
2

2
The cities were not without their own monstrosities.  While those outside were borne of nature, the strangeness within the vaults was borne of human lust and greed.  Entire races were bred for the purpose of serving their masters.  But for their chains, these races were not entirely unlike some of the new peoples in the Wastelands.
2

2
Eventually, the two worlds mingled.  Trade is vigorous, but the rulers of the cities are careful not to let too much of their technology into the hands of the "savages".  Remarkably, they have made very few attempts to colonize beyond their walls.  On the contrary, they have taken steps to control their own population's growth, perhaps heeding at least one lesson taught by the Cataclysm.
2

2
Contrary to the sterile society of the cities, the outlands are teeming with life.  By definition, the Wasteland is "sparse" (to say the least) but there exist many oases of wildly varied civilization, many unaware of anything beyond their own boundaries.
2

2
Technology, myth, magic.  It is a new world of wonder, and it is yours to explore.
36
5
4
2
2
*subst*
2
%;#3833:text()
36
5
4
3
2
------------------------------------------------------------------------------
2
The Ghost MOO is sponsored by the real-life Real/Time Communications in Austin, Texas.  If you're interested in dialup access or Internet services, please visit them at http://www.realtime.net/.
2
------------------------------------------------------------------------------
36
5
4
2
2
*forward*
2
realtime
36
5
4
2
2
*forward*
2
realtime
36
5
4
2
2
*forward*
2
realtime
36
5
4
3
2
*verbdoc*
2
$player
2
@undetail
36
5
4
3
2
*verbdoc*
2
$player
2
@details
36
5
4
3
2
*verbdoc*
2
$player
2
@detail
36
5
4
10
2
Certain actions in this VR require some kind of consent in order to be performed.  For instance, you wouldn't want just anyone taking off your clothes, or feeding you some gooey potion.
2

2
Consent is given with the 'trust' system.  You may trust people to perform individual actions, or put complete trust in them, allowing them to do EVERY trustable thing to you.
2

2
See 'help @trust'            for info on trusting/untrusting folk.
2

2
    'help @trusted'          for info on how to list who you trust.
2

2
and 'help trustable-actions' for a complete list of actions which
2
                              require trust and their descriptions.
36
5
4
2
2
*subst*
2
%;this:trustable_actions()
36
5
4
2
2
*forward*
2
trust
36
5
4
30
2

2
Clothing covers your naughty bits, protects you from RPG damage, and gives you that snazzy continental flair.
2

2
(You should create some clothing so that you can follow the instructions given below to customize it.  I'll use a duster as an example.)
2

2
The first thing you should do to any item you create is describe it.  This is as easy as:
2

2
  @describe duster as "A long riding coat of tough cowboy material, no
2
   doubt worn by some tough cowboy on those tough roundups of old."
2

2
Before wearing your new piece of clothing, you'll have to set the areas on which it fits:
2

2
  @coverage duster is arms,shoulders,chest,abdomen,groin,legs
2

2
When worn, the duster will cover every area except head, throat, hands and feet.  See 'help @coverage' for more information.
2

2
The most important message (see 'help messages' if you don't know what a MOO "message" is) to be set on your clothing is 'worn'.  This text will integrate into your description when you are wearing the clothing.
2

2
  @worn duster is "A long khaki canvas coat trimmed in leather hangs from
2
   %p shoulders to %p ankles, protecting %o from harsh desert storms."
2

2
(The "%p" and "%o" are called "pronoun subs" and you can learn more about them by reading 'help pronouns')
2

2
Now all you have to do is 'wear duster' and Voila!--You are clothed!  You can remove the clothing with 'remove duster'.
2

2
If someone has trusted you to strip them (see 'help trust'), you can strip the duster from them by typing 'strip duster from My_Love_Slave'.
2

2
(There are more messages attached to clothing.  See 'help clothing-messages' for a full list of their names and functions.)
2

2
Clothing (and nudity--see 'help nudity') is one of the most...interesting aspects of our MOO.  You will benefit from learning how to use them.  Win friends and influence people with your way mod duds!
2
1
4
39
2

2
Clothing has several messages which enhance its virtuality.  All messages are displayed to the room as well as all actors.
2

2
(If you are not familiar with pronoun substitution on MOO, you should review 'help pronouns' now.  References to "player sub", "T-sub", "D-sub", and so on are referring to pronoun substitutions.)
2

2
-- worn --
2

2
This is the message integrated into your description when the clothing is worn.  The player sub is for the wearer of the clothing, and the T- sub is for the clothing.
2

2
(T-sub will ALWAYS refer to the clothing.)
2

2
If you would like your clothing to look different on a man than on a woman, set either the "male" or "female" messages.
2

2
If a message of the same gender as the wearer is defined, that message will be shown instead of the default "worn" message.  For example:
2

2
@worn   t-shirt is "%S wears a tight t-shirt."
2
@male   t-shirt is "His tight t-shirt shows off some massive pecs."
2
@female t-shirt is "Her bountiful bosoms bounce bouyantly within the tight
2
 fabric of her t-shirt."
2

2
-- wear --
2

2
Shown when the clothing is put on.
2

2
-- remove --
2

2
Shown when the clothing is removed.
2

2
-- tease --
2

2
This message is displayed when you 'tease clothing'.  You (the teaser) are represented with the player sub.
2

2
-- strip --
2

2
This message is shown when someone (using the player sub) strips a piece of clothing from someone else (using the D-sub).
2

2
-- dress --
2

2
This is printed when someone (player sub) dresses someone else (D-sub).
2
1
4
23
2

2
Ah, Nudity!  Beautiful nudity!
2

2
Our MOO has an extensive system of nakedness.  You may describe individual body parts or groups of them.
2

2
(By default (to avoid newbie embarassment), your nude messages are left blank.  You are encouraged to describe these areas and then create clothing to cover them (see 'help clothing').)
2

2
Nudity messages are functionally just like any other message.  Each has the prefix of  "naked_".  You set them in the format:
2

2
  @<message-name> <object> is <new-message>
2

2
For example:
2

2
  @naked_throat Me is "Her throat is pale white, unblemished save for two
2
   tiny marks over the jugular."
2

2
You could cover this with a scarf (again, see 'help clothing') if you didn't want those little marks to be seen.
2

2
(Alternately, you could use the @my syntax to set a message.  For example: @my naked_throat is "A small purple bruise is visible just under his jaw.")
2

2
Issuing the '@nudity' command will display all the parts you have described, and all those you have not, in addition to the grouped areas you may want to use as shortcuts to describing individual areas.
2

2
Remember: Your body is nothing to be ashamed of!  Get nekkid!
2
1
4
5
2
It's easy to forget something mentioned once in a description, but with mood you can remind players of their surroundings.  A mood message on an object is periodically announced to its containing room.
2

2
Not all objects can have mood.  All players, things, and rooms do.  Pronoun substitution takes place as normal, but with a random character in the room as %N.
2

2
See 'help @mood' for details on how to set, list, and edit an object's mood.
2
1
4
17
2
The roots of role-playing are not in mindless hacking and slashing, but moreso in developing and playing a character.  We hold strong to these roots.
2

2
An RPG is nothing but a bloodbath if it lacks characters with feeling and motives--depth.  You are encouraged to create such a persona.  When you're playing that persona, you are considered to be "In Character" (IC).
2

2
Of course, sometimes you just wanna be yourself.  Perhaps you want to suggest something to a GameMaster or developer, or just relax your role and talk about RL (real life) for a while.  This is being "Out of Character" (OOC).
2

2
"Page" (or ', or + for page-emote: See respective help for details) allows you to communicate a single private OOC message to another player.  You can use the `ooc` command to say or emote something out of character to the room.
2

2
'@ic' switches you into IC mode.  '@ooc' switches you into OOC mode.  You start out in OOC mode, since you'll probably have a lot of mundane technical questions at first.  "(OOC)" will be appended to your title when you are in this mode.
2

2
See 'help @ic' for more details on the @ic/@ooc commands.  There are several options to assist you in slipping between the two modes.
2

2
You can choose to be strictly IC at all times, or just connect as "yourself" and chat without regards to the storyline.  But you should ALWAYS respect those who are OOC.  If you must communicate an OOC message to someone who is IC, please page them.
2

2
It is very annoying to have someone start talking OOC whilst folks are in the middle of a heavy role-playing session.  Imagine Tom Brokaw walking onto the bridge of the Enterprise in the middle of Star Trek and delivering the nightly news!
2

2
Above all, respect the interests of others.  And don't be offended if someone says something very rude or "EVIL"; unless they're OOC.  ;)
2
1
4
2
2
*forward*
2
character
2
1
4
17
2
The roots of role-playing are not in mindless hacking and slashing, but moreso in developing and playing a character.  We hold strong to these roots.
2

2
An RPG is nothing but a bloodbath if it lacks characters with feeling and motives--depth.  You are encouraged to create such a persona.  When you're playing that persona, you are considered to be "In Character" (IC).
2

2
Of course, sometimes you just wanna be yourself.  Perhaps you want to suggest something to a GameMaster or developer, or just relax your role and talk about RL (real life) for a while.  This is being "Out of Character" (OOC).
2

2
"Page" (or ', or + for page-emote: See respective help for details) allows you to communicate a single private OOC message to another player.  You can use the `ooc` command to say or emote something out of character to the room.
2

2
'@ic' switches you into IC mode.  '@ooc' switches you into OOC mode.  You start out in OOC mode, since you'll probably have a lot of mundane technical questions at first.  "(OOC)" will be appended to your title when you are in this mode.
2

2
See 'help @ic' for more details on the @ic/@ooc commands.  There are several options to assist you in slipping between the two modes.
2

2
You can choose to be strictly IC at all times, or just connect as "yourself" and chat without regards to the storyline.  But you should ALWAYS respect those who are OOC.  If you must communicate an OOC message to someone who is IC, please page them.
2

2
It is very annoying to have someone start talking OOC whilst folks are in the middle of a heavy role-playing session.  Imagine Tom Brokaw walking onto the bridge of the Enterprise in the middle of Star Trek and delivering the nightly news!
2

2
Above all, respect the interests of others.  And don't be offended if someone says something very rude or "EVIL"; unless they're OOC.  ;)
2
1
4
27
2
Combat here is handled in a style akin to the old dice-and-paper RPGs.  You declare the action you wish to take this round, initiative is determined by the combat machine, and then everyone acts in turn.
2

2
The following actions may be declared:
2

2
wield           -- Ready an item.
2
sheathe         -- Unequip an item.
2

2
kill            -- Attack someone with something.
2
stun            -- Attempt a non-lethal attack.
2
ambush          -- Wait to attack one matching a given description (name).
2

2
guard           -- Attempt to defend another character.
2
block           -- Block escape via a certain exit.  (`block exit_name`)
2
flee            -- Get the Hell out!
2
dodge           -- Dodge an attacker.
2
revive          -- Attempt to bring someone to consciousness.
2
heal            -- Attempt to heal someone.
2

2
Help is available on most of those commands.
2

2
You may use `actions` to display the actions you have queued up, and `cancel` to cancel the last action declared.
2

2
If you want an action to be moved to the front of your queue, use the `rush` command followed by its args.  ie: `rush attack fred`.
2

2
Remember that it is just as rude to attack someone who is OOC as it is to talk RL politics with someone who is IC.  Please respect this.
2

2
Also, keep in mind that not ALL things are sword fodder.  Some who seem to be users may be robots, and vice versa.  And some are just plain nice folks.  Don't be a sadistic killer.  Unless, of course, it's your IC persona. ;)
2
1
4
3
2
*verbdoc*
2
$player
2
WHO
36
5
4
22
2

2
Our MOO allows for certain ANSI codes to be integrated into text.  These include colour, alternate print styles, and graphics characters.
2

2
For details on activating the MOO-end of the emulation, and an overview of the special characters used, see `help @ansi`.
2

2
Typing `@options display` will show you related options.  For example:
2

2
        @options display +bold_room
2

2
Would show all room titles in bold print.
2

2
        @options display bold_room="~~!~~G%N~0"
2

2
Would show all room titles in bold green print.  ("~!~GYour_Location~0")
2

2
        @options display !bold_room
2

2
Would return room titles to normal print.
2

2
You can use the ANSI tokens in all your text, but please don't abuse the privelege by flashing screenfuls of text, or leaving off the normalizing token (~~0), possibly messing up someone's display.
2

2
See the documentation of your communications package for how to activate ANSI on your end.
36
1
4
16
2

2
There are various "stage" talking commands available which allow you
2
 to perform directed speech and other actions which assist in
2
 communicating more effectively.
2

2
Here are a few commands, and the text resulting from their usage by
2
 the fictional 'Iggy'.
2

2
        !Someone farts.         Someone farts.  --Iggy
2
        `fred Did you fart?     Iggy [to Fred]: Did you fart?
2
        -lulu Fred farted.      Iggy [to Lulu]: Fred farted.
2
        <Fart-Free              Iggy <- Fart-Free
2
        (Unlike Fred            (Unlike Fred)
2
        [the fart festers       [the fart festers]
2

2
Remember that Iggy is not real.  Do not try to find him.
36
1
1
3754
115
1
4
105
2
The following are basic tips for builders.  "Builder" should be taken as meaning anyone who can even @describe something.  Heed them well.
2

2
               [For those using ANSI, examples are in green.]
2

2
                         ~!"One Line == One Paragraph"~0
2

2
Not everyone has the same screen width as you.  Don't assume that since your screen stops at 80 characters, the whole Net stops there, too.
2

2
Keep typing until you reach the end of a paragraph, and THEN press return.  This aids greatly in formatting your text.  Take a look at the following two paragraphs:~G
2

2
My name is Billy and I really like to play golf.  Unfortunately, out
2
here in the Wasteland, everywhere is a sand trap.  So I use my nine
2
iron to bash the Hell out of cainids.
2
Just kidding.  I am really a nice guy.  Unless you're a mutt.  Then
2
watch out!~0
2

2
Notice how everything is flush to left.  Our friend Billy typed each line, and then RETURN.  Still don't understand?  Then here's what he typed in the MOO editor:~G
2

2
"My name is Billy and I really like to play golf.  Unfortunately, out
2
"here in the Wasteland, everywhere is a sand trap.  So I use my nine
2
"iron to bash the Hell out of cainids.
2
"Just kidding.  I am really a nice guy.  Unless you're a mutt.  Then
2
"watch out!~0
2

2
He broke his lines.
2

2
Now, let's say I told Billy to read this document, and slapped him around a little:~G
2

2
My name is Billy and I really like to play golf.  Unfortunately, out
2
 here in the Wasteland, everywhere is a sand trap.  So I use my nine
2
 iron to bash the Hell out of cainids.
2
Just kidding.  I am really a nice guy.  Unless you're a mutt.  Then
2
 watch out!~0
2

2
See the difference?  Each line after the first is indented by one space.  Looks a lot better, huh?  Billy didn't do that manually.  Billy typed the following in the MOO editor:~G
2

2
"My name is Billy and I really like to play golf.  Unfortunately, out here in the Wasteland, everywhere is a sand trap.  So I use my nine iron to bash the Hell out of cainids.
2
"Just kidding.  I am really a nice guy.  Unless you're a mutt.  Then watch out.~0
2

2
In descriptions, don't do the formatting.  Leave that to the MOO.  All you need to remember is: one line is one paragraph.
2

2
                        ~!"Describe Everything!"~0
2

2
Now that you know HOW you should describe something, you should do it liberally.  There is nothing so bad as looking at something, aching to learn more about it, and reading "You see nothing special."
2

2
Describe all your items.  It doesn't have to be a book-length text; that's often as bad as not describing it.  But at least give us a clue what it looks like.
2

2
Imagine having it in your RL hands, and describe what you see.
2

2
                            ~!"Proper Names"~0
2

2
First, a few naming DOs and DON'Ts.
2

2
DON'T include articles in your names.  These are added automatically. Your "iron sword" is not "the iron sword".  It's "iron sword".  "The" is useless.  Articles aren't reality--they're grammar.
2

2
DON'T overload the name.  If you have a special sword, describe it as such.  Don't name it "rusty iron sword glowing an eerie green".  If you think it should be glowing, @describe it as glowing.  But name it "rusty iron sword".
2

2
DO be concise.  Try to keep your name as short as possible, without being ambiguous.  "Sword" is a little too brief, "long sword" is better, "iron long sword" is just about right, and "rusty long sword" is good.  "Big mean
2
 looking rusty long sword" is a bit too much.
2

2
DON'T use cryptic names.  If you have a pet name "Waldo" for your iron sword, don't name it "Waldo".  People can't know that just by looking at the sword.  Use `dub iron sword as Waldo` instead, and maybe describe it
2
 as having "Waldo" etched into the blade.
2

2
DON'T use misleading names.  If you are caught naming your dagger as "vicious chainsaw", expect to get spanked.  Hard.  This is an abuse of your creative freedom, and it will not be tolerated.
2

2

2
                            ~!"Sane Aliasing"~0
2

2
MOO uses a powerful system of object "aliases"--names by which an object can be referred.
2

2
For example, I have a "ripped black shirt".  If it had no aliases, I could refer to it only as "ripped black shirt", or perhaps "ripped black" or maybe even just "ripped", depending on what else was around me.
2

2
I could not, however, refer to it as "black shirt", or even the obvious "shirt".  To do that, I'd need to ALIAS it.  There are several ways of doing this...~G
2

2
               @rename clothing to ripped black shirt,black shirt,shirt
2
               @addalias black shirt,shirt to ripped black shirt~0
2

2
If you mess up and typo the wrong alias, you can remove it.  For example:~G
2

2
               @rmalias shit from black shirt
2
               @addalias shirt to black shirt~0
2

2
You can `examine object` to check an object's aliases.
2

2
                          ~!"Message Your Exits!"~0
2

2
There are a lot of messages on exits.  Do a `@messages exit` to see the whole damn list.  You don't need to set ALL of them, but you should set at least the following:~G
2

2
               @oleave exit is "Someone leaves the room."
2
               @leave exit is "You leave the room."
2
               @oarrive exit is "Someone comes into the room."~0
2

2
Not the exact text above, of course.  @oleave is shown to the room the person is leaving, @leave is shown to the person just before e leaves the room, and @oarrive is shown to the destination after e arrives there.
2

2
                               ~!"Synopsis"~0
2

2
Building in a VR is the construction of reality, and nobody wants to live in a shoebox of cardboard cutouts.  Give your creations depths and realism by making using words to evoke images of their RL counterparts.
2

2
Or to make the fantastic SEEM real.
2

2
Here, words are power.  If you use them well, you enhance your realism-- you strengthen the reality of your virtual persona.
2

2
Be real.
2

2
                                                --Quinn
36
1
4
50
2
*subst*
2
This help doc contains detailed information on the "page" command, including examples, customization, and offline page storage.  See `help page` for the lightweight lowdown.
2

2
        Examples:
2
Munchkin types:
2
  A     page Frebble with "Where are you?"
2
  B     'frebble Oh snuggle boy!
2
  C     page bette :hugs.
2
  D     + smiles.  "Hello, my darling!"
2
  E     p frebble bette=You're the only one I love!
2
Frebble sees:
2
  A     You sense that Munchkin is looking for you in the Kitchen.
2
        He pages, "Where are you?"
2
  B     Munchkin pages, "Oh snuggle boy!"
2
  D     Munchkin pages, "You're the only one I love!"
2
Bette sees:
2
  C     (from Munchkin's LoveHut) Munchkin hugs.
2
  D     (from Munchkin's LoveHut) Munchkin smiles.  "Hello, my darling!"
2
  E     Munchkin pages, "You're the only one I love!"
2
Munchkin sees:
2
  A     Frebble leaps with JOY upon receiving your page.
2
  B     Frebble leaps with JOY upon receiving your page.
2
  C     (to Bette) Munchkin hugs.
2
  D     (to Bette) Munchkin smiles.  "Hello, my darling!"
2
  E     Your message has been received by Frebble and Bette.
2

2
        Customizing pages:
2
Page refers to the following messages on the players involved (see `help messages`):
2

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

2
@my page_echo is "%[$player.page_echo_msg]"
2
  Determines the response received by anyone who pages you.  $text is replaced with the message that was paged.
2

2
@my page_absent is "%[$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.
2

2
You should only change these messages if you want to add to the Virtual Reality feel of the MOO for your character.  You should _always_ include either "%N" or your name in the message.
2

2
        Absentee Pages:
2
If the person you paged is not connected, your page will be stored on their character so that they may view it when they return.  This is useful when you know they'll be right back.  You can keep on paging.
2

2
The user will be informed of the pages at login, and can type `@recall` to view them.
2

2
Sometimes you page the WRONG person.  For example, Munchkin may have mispaged a feminine stroke to Frebble the hapless cuckold.  In these situations, you can `@unpage user` and remove all of your saved pages to that user.
2

2
This doesn't work with mispages to CONNECTED users, of course.  Sorry, but we can't change the past.
36
1
4
2
2
*forward*
2
builder-advice
36
1
4
36
2

2
Unlike many MOOs with an RPG, we allow users a certain amount of creative freedom with their objects.  You may freely rename, describe, and message your personal items to give them character.
2

2
We are not, however, oblivious to the inevitability of abuse.  No doubt some smart-ass will `@rename dagger to SUPER NUCLEAR CHAINSAW` or set his integration messages so he appears as punctuation in the room description.
2

2
When you replicate a quarterstaff, you replicate a quarterstaff.  It is not a katana, or a shotgun.  There is much you can do to customize the object, and much you should avoid.
2

2
Instead of...                       Do this...
2

2
@rename staff to THE DEATHWAND      dub staff as "Deathwand"
2

2
@describe staff as "Glowing with    @desc staff as "The humble staff of an
2
 the power of whimsical carnage,     apprentice magician.  The word 'Deathwand'
2
 this staff projects fireballs       has been etched with great care into the
2
 at the will of its owner, Fred."    gnarled wood."
2

2
@wielding staff is "Fred grasps     @wielding staff is "%N %<clutches> %p
2
 his mighty staff, glaring at all    staff with white knuckles, projecting
2
 who would oppose him.  DEATHWAND    %p mystic concentration into the
2
 glows with power, and you shudder   mundane wood."
2
 in terror, knowing it could reduce
2
 you to so much bone and ash."
2

2
You see, that staff isn't a magic wand.  It's just a staff.  Now, if you someday come across a magic wand and are able to customize it, then that would be fine.  Embellish all you like.
2

2
Here are some general tips:
2

2
o   When available, use `dub` rather than @rename.  The *name* of your object should be as generic as possible.  One can't tell with a simple glance that you like to call your dagger "little shaver".
2

2
o   Never use articles in names.  Don't rename something to "the staff"; just use "staff".  Articles are handled automatically by the MOO.
2

2
o   Whenever using names and pronouns other than second person ("you", "your", etc), always use substitution keys.  Remember that others may wield your weapon or wear your clothing.  See `help pronouns`.
2

2
Above all, remember that @rename, @describe, and the ability to set messages aren't provided so you can dupe your fellow users.  They're given as a privilege, and can be taken away if abused.
2

2
If you value your creative freedom here, use it responsibly.
36
1
4
22
2
Object size is recorded in the v_size ("virtual size") property.  It's all relative with no direct correlation to real metrics.
2

2
Exits and other portals have a "clearance" property which should be the largest size allowed through the exit.
2

2
Here's a list of the size categories.
2

2
0       Neglig: Lint
2
5       Tiny-1: Pebble, watch battery, paper clip
2
10      Tiny-2: Small arms round, roach
2
15      Tiny-3: Long arms or shotgun round
2
20      Smal-1: Mouse, wrist comm, drinking glass
2
25      Smal-2: Rat, flashlight, pistol, bioscanner, GPS
2
30      Smal-3: Cat or small dog, sawed-off shotgun, backpack, portable PC
2
35      Medi-1: Wolf, small humanoid, tricycle, rifle, swords and staves
2
45      Medi-2: Tiger, average-large size human, mounted weaponry
2
50      Medi-3: Adult lion, motorcycle, average door size
2
55      Larg-1: Horse, small ATV, rowboat
2
60      Larg-2: Hippo, full-size car, motorboat
2
65      Larg-3: Elephant, tractor, tank, field artillery, helicopter
2
70      Huge-1: Dragon, railroad car, ICBM, satellite, houseboat
2
75      Huge-2: Bomber, yacht, space shuttle
2
100     Huge-3: Starship, aircraft carrier, small island, space station
115
1
4
2
2
*forward*
2
say
36
1
4
2
2
*forward*
2
staff-rules
36
1
4
7
2
@reroll [character]
2

2
The @reroll command resets your game stats (or that of the given character) to their starting levels.
2

2
If your last reroll was more than a minute ago, @reroll will ask for confirmation before proceeding.  Using  @reroll! bypasses this confirmation.
2

2
Note that rerolling will reset your character's age and clear your character sheet.  You're effectively starting over.
115
1
4
53
2

2
Your "sheet" is the online equivalent of a character sheet in a RL dice and paper role playing game.  It should contain all pertinent game-related information about your character.  You don't have to create a sheet if you're just here to casually role-play and/or hack and slash.  You will need one if you're going to be involved in any plots.  Creating a character sheet tells others you're serious about role-playing.  If you took the time to write up a sheet, you're probably going to be around for more than a "one-night stand".
2

2
Here are the steps for editing your sheet:
2

2
(1) @edit me.sheet
2

2
This will move you to the MOO's editor -- a quiet place where everything you "say" is appended to the data you're editing.  Type `help` there for help on using the MOO's editor, or just `look` to see a list of commands.
2

2
(2) Start editing.
2

2
Here's a sample sheet:
2

2
[01] Level 0 - Forgery
2
[02] Level 1 - Jeweler
2
[04] Level 2 - Oculist
2
[04] Level 2 - Poet
2
[08] Level 3 - Musician
2
[RP] Flamboyant Bisexual
2

2
Each line begins with brackets which contain the character point (cp) cost of that attribute.  To find out how many character points you possess, type `finger me`.
2

2
Ones mastery of each attribute is measured by ones 'level' of competence.  Here are the various skill levels and their cp cost:
2

2
(0) Apprentice:    1 cp
2
        Basic knowledge, an enthusiast.  After your initial sheet edit, all skills must start at this level.
2
(1) Journeyman:    2 cp
2
        An accomplished amateur.
2
(2) Professional:  4 cp
2
        Good enough to make a living.
2
-----No starting skill may be higher than level 2.-----
2
(3) Teacher:       8 cp
2
        Sought after for your knowledge.
2
(4) Master:       16 cp
2
        Known by anyone in the same field with at least a level 1.
2
(5) Guru:         32 cp
2
        Known by anyone not living in a cave.
2

2
Except for unusual circumstances, players cannot start with more than level 2 in any skill.  After you've played for a while, you'll gain more character points, proportional to your game age.  With those extra points, you can achieve further levels.
2

2
Once you've started the game, after your initial sheet edit, all new skills must begin at Zero-level and progress from there!
2

2
While all levels beyond 2 require some IC justification, level 4 and above require a player to have significantly demonstrated excellence in his field to merit the fame afforded by those levels.  This involves at least a written explanation of ones achievements, submitted to the *requests list.
2

2
You may include any type of skill in your sheet, but if a coded version of that skill exists, it will always take precedence.  This generally means that you should not include combat, medical, or "magical" skills in your sheet.  Try to keep them to interpersonal and pure knowledge skills.
2

2
An "RP" attribute is merely a way to describe your character's general attitude.  It has no function in the game other than to give a "heads up" to those you encounter.  It sometimes makes up for the gaps left in virtual role-playing, making it clear how you're trying to present yourself.
2

2
(3) Save
2

2
Typing `save` within the editor will save your sheet.  Type `done` to return to the game world.
2

2
Once your sheet is complete, send a note to *requests (see `help mail` if you don't know how) asking that it be reviewed.  A staff member will eventually get around to giving it their stamp of approval, or suggest changes you should make.
115
1
4
3
2
What happens when you die?  Your "self" (composed of body and "soul") is yanked from this plane, leaving a crystalline residue--the portion of your essence lost in transit to the afterlife.  Traumatic bodily damage such as gaping wounds and severed limbs usually translates into mental damage when you and your "container" are thrust back into the physical world, but the body itself may retain diseases or other chronic destructive elements.
2

2
When do the dead stay dead?  When your entire body is disintegrated at the atomic level, you will not come back.  Luckily for you, only something as powerful as a ground-zero nuclear detonation can completely disintegrate a body.   Even burning someone to ashes or melting him into a pile of goo will not destroy the ethereal cohesion of his self.
115
1
4
17
2
Staff members exist to feed the imaginations of players; their sole reward is the appreciation of their creations.
2

2
With rare exception, what you create should be public: potentially enjoyed by and available to everyone.  Be very careful of favoring (or even appearing to favor) your friends.  A staff member must be particularly objective when dealing with those with whom he is involved in real life.  A true friend would not endanger your powers by requesting that you abuse your duty.
2

2
Show courtesy and respect to your fellow players and staff members.  If you're in a bad mood, don't login.
2

2
Under no circumstances should a staff member log or monitor the conversations of any other player without their explicit permission.  Devices intended for such activities are forbidden.  Monitoring required for debugging purposes should be performed via children of $puppet.  Violation of player privacy may result in complete expulsion from the MOO (ie. @toading).
2

2
All activities must be posted to *changes.  These activities include but are not limited to: fulfilled *requests, stat changes on any objects, creation of new generics, or introduction of any object into the game.  Your posts to *changes reflect your activity as a staff member.  If there are no posts, you're not doing anything and can expect to be removed for inactivity.
2

2
An area is not judged by its difficulty or the treasures it yields.  A monster is not the sum of its attributes and skills.  An item is not valued by the damage it does.  Innovation within the theme is appreciated.  Strive to contribute to the mythos by embellishing new cultures and denizens.
2

2
Always present project ideas to the *staff (nee *gms) list before pursuing them.  Do not invest time and effort in a project which may not be allowed.  In particular, (a) plots or ideas which directly effect the theme and (b) items which reveal game statistics or mechanics should be thoroughly discussed and approved before any building or coding is begun.
2

2
Any special items created for players must have an IC justification.  A master sword cannot be given to a player unless he has some acquaintance (perhaps indirect) with a master swordmaker.  Any compensation (eg. crystals) for an item must be destroyed or randomly dispersed by the staff member handling the request.  The staff member should retain ownership of weapons, armour, and other items which should be lootable upon the recipient's death.  Rooms and their associated exits may be chowned to the player.  As with all staff activities, the transaction should be noted on *changes.
2

2
Use common sense.  If something doesn't seem perfectly ethical, ask others.  If it does seem ethical, but it isn't, you probably shouldn't be here.
36
1
4
11
2
Replicators `create' a desired object by combining part of the essence of the operator with ambient particles.  The desire of the operator is matched to a stored template which guides the creation.  While functionally equivalent, each replicated object is unique in that it is the manifestation of the peculiar desire of the operator.
2

2
Anything can be replicated, but the quality of the replication is a function of the resolution of the template, the knowledge and willpower (force of desire) of the operator, and the current availability of ambient particles.  Often after a replication, one will feel a static charge as the required particles are `stolen' from the environment.  The manufacture of replicators is carefully controlled to ensure particulate capture operates within safe parameters.
2

2
While food and drink can be replicated, long-term sustenance on replicated nourishment alone has proved problematic.  As real as replicated objects may seem, they are still very much a manifestation of their creator's desire.  The end effect, while giving a `feeling' of having eaten, is only slightly more nourishing than eating dirt while imagining a feast.
2

2
A creator is as aware of his replicated object as he is a part of his own body; he knows its location and condition at all times.  Furthermore, it can both be summoned to and be willed out of existence by its creator at any time.  Caveat emptor.
2

2
Each person is limited in the amount of matter he may replicate.  When this limit is reached, more matter may be created only by destroying items previously replicated.
2

2
Some replicators have been modified to draw from a central source of desire (often artificial or forced), or to directly accept refined particulate (often `crystals').  The objects produced by these modified replicators may or may not be tied to the operator in the manner described above.
115
1
4
17
2
To generate your character's initial stats, you must go to the Personal Enhancement Center ("PEC").  To do so, first go out-of-character.  Enter an IC-neutral location (or a room all by yourself), then type the following:
2

2
     @go OOC
2

2
Next, teleport to the PEC:
2

2
     @go PEC
2

2
Finally, enter the character editor with the following command:
2

2
     @chargen
2

2
Type `look' to see the commands available to you.  Inside the character editor, you have the ability to shape your character into the type you want to play.  You'll probably want to concentrate on increasing your skills with your available `potential points'.  Potential is accumulated through time spent online and in-character.  You can use it in the character editor to improve your skills and attributes.
2

2
As a newbie, you're allowed to use the `trade' command to swap points from skill to skill, or attribute to attribute, or (at a lower ratio) between skills and attributes.  You won't be able to do this after using `save' to save the stats, so take advantage of it while you can!  (You can @reroll to clear yourself back to "newbie" status.)
2

2
Once you're finished, type `save', then `quit' to return to the PEC.  Now you're ready to take on the world!
36
1
5
36
5
0
0
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
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
4
2
2
*forward*
2
character
115
1
#60
News

16
2
45
-1
70
44
-1
143
21
description
2
173
-1
is_writable_by
2
173
-1
rm_message_seq
2
173
-1
undo_rmm
2
173
-1
expunge_rmm
2
173
-1
set_current_news
2
173
-1
add_current_news
2
173
-1
rm_current_news
2
173
-1
news_display_seq_full
2
173
-1
touch
2
45
-1
@add-news
2
157
1
@remove-news
2
157
5
@set-news
2
105
1
_parse
2
173
-1
init_for_core
2
173
-1
add_news
2
173
-1
rm_news
2
173
-1
@list-news
2
141
4
@clear-news
2
45
-1
to_text
2
165
-1
check
2
173
-1
3
current_news
last_news_time
current_news_going
29
4
2
0
76
0
95
2
5
0
0
2
5
4
0
2
5
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
0
1
2
5
4
0
36
1
4
0
36
1
0
2592000
2
5
0
0
36
1
4
0
36
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
News
36
1
2
It's the current issue of the News, dated %d.
2
5
4
2
0
12700
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#61
Genesis

0
2
-1
2
-1
296
-1
-1
0
0
53
5
36
1
5
2
5
5
2
5
5
2
5
5
36
1
5
36
1
5
2
5
5
36
1
5
2
5
5
2
5
5
36
1
4
0
36
1
5
36
0
5
36
0
5
36
1
5
36
1
5
36
1
5
2
5
5
2
5
5
2
5
0
1397152052
115
1
4
0
115
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
4
0
2
4
5
2
5
5
2
5
4
0
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
113
1
5
2
4
5
2
4
5
36
1
5
2
5
5
2
5
5
2
4
4
0
2
5
2
In the beginning, God (#2) was thrust into Heaven and Earth, otherwise known as Genesis (#61).
2
5
5
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#62
Recycling Center

16
36
-1
-1
-1
5
-1
74
13
_recreate
2
173
-1
_recycle
2
173
-1
_create
2
165
-1
addhist
2
173
-1
show*-history
2
37
-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
trusts_to_create_for
36
173
-1
_create_for
2
173
-1
4
orphans
announce_removal_msg
nhist
history
46
4
0
36
1
2

36
5
0
50
36
0
4
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
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
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#63
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
1
aliases
1
4
1
2
garbage
2
1
#64
Mail Options

16
36
-1
-1
-1
67
-1
65
10
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
9
show_include
show_all
show_nosubject
show_expert
show_enter
type_manymsgs
type_@mail
type_replyto
show_netmail
23
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
2
2
Receive personal MOO-mail here in the MOO.
2
Forward MOO-mail directly to my e-mail address.
36
1
4
10
2
include
2
all
2
nosubject
2
expert
2
enter
2
sticky
2
@mail
2
manymsgs
2
replyto
2
netmail
36
1
2
!include!noinclude!all!sender!nosubject!expert!enter!sticky!@mail!manymsgs!replyto!netmail!
36
1
4
2
2
noinclude
2
sender
36
1
5
36
5
2
mail_options
36
5
0
0
36
4
4
1
2
Mail Options
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#65
Edit Options

16
36
-1
-1
-1
67
-1
66
0
3
show_quiet_insert
show_eval_subs
show_local
17
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
3
2
quiet_insert
2
eval_subs
2
local
36
1
2
!quiet_insert!eval_subs!local!
36
1
5
36
1
0
20
36
5
2
edit_options
36
5
0
0
36
4
4
1
2
Edit Options
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#66
Programmer Options

16
36
-1
-1
-1
67
-1
68
0
7
show_blank_tnt
show_shortprep
show_thisonly
default_mail_tbks
show_mail_tbks
show_darkonly
show_no_verbdup
21
4
2
2
@d will treat `this none this' verbs like the others.
2
@d will blank out the args on `this none this' verbs.
36
5
4
2
2
@d will display prepositions in full.
2
@d will use short forms of prepositions.
36
5
4
2
2
@d:. will show ancestor properties/verbs if none on this.
2
@d:. will not show ancestor properties/verbs.
36
5
0
0
36
1
4
2
2
Don't mail me tracebacks occuring in my verbs.
2
Send me mail when my verbs produce a traceback.
36
1
4
2
2
@d, shows ALL ancestor properties.
2
@d, shows only opaque ancestor properties.
36
1
4
2
2
Allow @verb to create verbs with aliases of existing verbs.
2
Forbid @verb from creating verbs with aliases of existing verbs.
36
1
4
5
2
blank_tnt
2
shortprep
2
thisonly
2
darkonly
2
no_verbdup
36
1
2
!blank_tnt!shortprep!thisonly!darkonly!no_verbdup!
36
1
4
0
36
1
5
36
5
2
prog_options
36
5
0
0
36
4
4
3
2
Programmer Options
2
Programming Options
2
Prog Options
36
5
2
Various options useful to programmers.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#67
Generic Option Package

144
36
-1
-1
-1
1
64
69
13
get*_option_value
36
173
-1
set*_option_value
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
short_name
2
173
-1
get_default
36
173
-1
5
names
_namelist
extras
namewidth
storage_prop
14
4
0
36
1
2
!
36
1
4
0
36
1
0
15
36
5
2

36
5
0
0
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
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#68
List Options

0
36
-1
-1
-1
67
-1
116
2
actual
36
173
-1
show
36
173
-1
2
show_parentheses
show_nonumbers
16
4
2
2
Parenthesize expressions in listings only as needed.
2
Fully parenthesize expressions in listings.
36
5
4
2
2
Include line numbers.
2
Omit line numbers.
36
5
4
2
2
parentheses
2
nonumbers
36
1
2
!parentheses!nonumbers!numbers!
36
1
4
1
2
numbers
36
1
0
20
36
5
5
36
5
0
0
36
4
4
1
2
List Options
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#69
Error Generator

16
2
-1
-1
-1
1
-1
82
19
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
format_traceback full_traceback
36
173
-1
short_traceback
36
173
-1
1
names
10
4
15
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
5
0
0
2
4
4
1
2
Error Generator
2
5
4
3
2
Object to automatically generate errors.
2

2
raise(error) actually raises the error.
2
5
4
2
0
7270
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#70
Site-Locks

0
36
45
-1
143
44
-1
60
1
init_for_core
2
173
-1
0
26
4
0
36
5
0
0
36
1
4
0
36
0
0
1
36
5
2
%n (%#) can't send to moderated list %t (%[#t]) directly.
36
5
4
0
36
5
0
1
36
5
4
1
1
2
36
1
4
2
1
2
1
70
36
1
0
2592000
36
5
0
0
36
1
4
0
36
0
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
0
36
4
4
1
2
Site-Locks
36
1
2
Notes on annoying sites.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#71
housekeeper

19
36
-1
-1
-1
295
-1
100
20
look_self
36
173
-1
cleanup
36
165
-1
replace
36
173
-1
cleanup_list
36
21
-1
add_cleanup
36
93
-2
remove_cleanup
36
29
-1
controls
36
173
-1
continuous
36
165
-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
accept
2
173
-1
11
recycle_bins
owners
cleaning
litter
eschews
public_places
task
requestors
destination
clean
testing
196
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
0
1
36
5
0
0
36
5
2
here=player.location;me=player
36
5
0
48
36
5
0
0
2
1
4
0
2
0
5
36
5
5
36
0
5
36
1
5
2
0
5
36
5
5
2
0
0
0
2
1
0
0
36
5
5
36
5
5
36
1
5
36
1
4
0
36
0
5
36
1
0
276800495
36
5
4
0
2
1
1
36830
36
4
4
0
2
0
5
2
0
4
0
36
4
0
0
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
5
36
5
5
36
5
5
36
5
5
2
1
4
2
0
2
0
0
36
4
4
0
36
4
0
2147483647
2
1
0
21
100
0
2
Impossible password to type
2
0
5
36
5
0
0
2
1
4
0
115
1
5
36
5
1
159
36
5
4
4
0
10000000
0
0
0
955495071
0
0
100
1
5
2
0
5
36
5
5
2
1
5
36
1
5
36
5
5
115
1
4
0
36
0
5
36
5
5
115
1
5
115
1
4
3
0
1219694
4
2
1
4744
1
66
4
2
4
1
4
2
2
hide_loc
0
0
4
1
2
thisonly
36
0
5
115
1
5
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
5
36
5
5
115
1
5
115
1
1
206
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
0
0
115
1
0
0
115
1
0
0
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
1
61
36
5
5
36
5
5
36
1
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
115
1
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
36
4
5
36
5
5
36
4
5
36
5
5
115
0
5
115
1
5
36
0
5
115
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
1
5
36
5
5
36
5
5
115
1
5
115
1
5
36
4
5
36
4
5
36
1
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
19228
0
950843373
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#72
Network Utilities

16
2
-1
-1
-1
83
-1
84
11
parse_address
2
173
-1
local_domain
2
173
-1
open
2
165
-1
close
2
165
-1
sendmail
2
29
-1
trust
2
173
-1
init_for_core
2
173
-1
raw_sendmail
2
21
-1
invalid_email_address
2
173
-1
invalid_hostname
2
173
-1
email_will_fail
2
173
-1
15
site
large_domains
open_connections
connect_connections_to
postmaster
port
MOO_name
valid_host_regexp
maildrop
trusts
reply_address
active
valid_email_regexp
invalid_userids
debugging
24
2
yoursite
2
1
4
0
2
1
4
0
2
1
4
0
2
0
2
postmastername@yourhost
2
5
0
7777
2
5
2
YourMOO
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
0
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
0
0
2
4
4
1
2
Network Utilities
2
5
4
73
2
Utilities for dealing with network connections
2
---------------
2
Creating & tracking hosts:
2

2
:open(host, port [, connect-connection-to]) => {connection, object}
2
    open a network connection (using open_network_connection), optionally
2
    allows for it to be connected to another object.
2
    (see #0:do_login_command for details).
2

2
:close(connection)
2
     closes the connection & cleans up data
2

2
------------------
2
Parsing network things:
2

2
:invalid_email_address(email)
2
     return "" or string saying why 'email' is invalid.
2
     uses .valid_email_regexp
2

2
:invalid_hostname(host)
2
     return "" or string saying why 'host' doesn't look
2
     like a valid internet host name
2

2
:local_domain(host)
2
     returns the 'important' part of a host name, e.g.
2
     golden.parc.xerox.com => parc.xerox.com
2

2
-------------------
2
Sending mail
2

2
:sendmail(to, subject, @lines)
2
     send mail to the email address 'to' with indicated subject.
2
     header fields like 'from', 'date', etc. are filled in.
2
     lines can start with additional header lines.
2

2
:raw_sendmail(to, @lines)
2
     used by :sendmail. Send mail to given user at host, just
2
     as specified, no error checking.
2

2
================================================================
2
Parameters:
2

2
.active If 0, disabled sending of mail.
2

2
.site   Where does this MOO run?
2
        (Maybe MOOnet will use it later).
2

2
.port   The network port this MOO listens on.
2

2
.large_domains 
2
        A list of sites where more than 2 levels of host name are
2
        significant, e.g., if you want 'parc.xerox.com' to be
2
        different than 'cinops.xerox.com', put "xerox.com" as an
2
        element in .large_domains.
2

2
.postmaster
2
        Email address to which problems with MOO mail should
2
        go. This should be a real email address that someone reads.
2

2
.maildrop
2
        Hostname to connect to for dropping off mail. Usually can
2
        just be "localhost".
2

2
.reply_address
2
        If a MOO character sends email, where does a reply go?
2
        Inserted in 'From:' for mail from characters without
2
        registration addresses.        
2

2
.trusts
2
        List of (non-wizard) programmers who can call
2
        :open, :sendmail, :close
2

2
                
2
5
4
2
0
13648
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#73
Generic BigList Resident

144
36
-1
-1
-1
1
-1
18
6
_make
2
173
-1
_kill
2
173
-1
_get
36
173
-1
_put
36
173
-1
_genprop
36
173
-1
_ord
36
173
-1
3
_genprop
mowner
_mgr
12
2

36
5
1
4292
36
5
1
5533
36
5
0
0
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
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#74
Generic Feature Object

144
36
-1
-1
-1
5
85
89
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
165
-1
hidden_verbs
36
173
-1
set_feature_verbs
36
165
-1
initialize
36
165
-1
init_for_core
2
173
-1
display_msg
36
173
-1
4
warehouse
feature_verbs
feature_ok
display_msg
46
1
-1
36
1
4
0
36
1
0
1
36
1
2

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
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
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
0
0
0
100
1
5
36
5
5
36
5
5
36
5
4
0
36
5
5
115
1
#75
Display Options

16
2
-1
-1
-1
67
-1
122
5
show_bold_rooms show_bold_exits
2
173
-1
parse_bold_rooms parse_bold_exits
2
173
-1
parse_charset
2
173
-1
show_charset
2
173
-1
default_charset
36
173
-1
16
show_bold_rooms
show_bold_exits
type_bold_rooms
type_bold_exits
show_exit_sentence
default_exit_sentence
show_full_owned
show_brief_pages
show_brief_rooms
show_npc_titles
default_rpg_titles
show_rpg_titles
show_short_tbks
show_brief_finger
type_charset
show_color
30
4
2
2
Show room titles in normal text.
2
Show room titles in BOLD text.
2
5
4
2
2
Show exit names in room descriptions in normal text.
2
Show exit names in room descriptions in BOLD text.
2
5
4
2
0
2
0
0
2
1
4
2
0
2
0
0
2
1
4
2
2
List room exits one line at a time.
2
Show room exits as an english sentence.
36
1
0
1
36
1
4
2
2
Use abbreviated audit display with @owned.
2
Display location and object size with @owned.
36
1
4
2
2
Show full page origin, echo, absent and refusal messages.
2
Show the short default versions of page messages.
36
1
4
2
2
Upon entering a room, show the full description.
2
Upon entering a room, show only its title and contents.
36
1
4
2
2
Don't show NPC titles.
2
Show NPC titles.
36
1
0
1
36
1
4
2
2
Don't show RPG stats in character titles.
2
Show various RPG stats in character titles.
36
1
4
2
2
Show full error and timeout tracebacks.
2
Show abbreviated one-line traceback and timeout messages.
36
1
4
2
2
@finger by default shows sheet, plan, etc.
2
@finger by default shows only header information.
36
1
4
1
0
2
2
1
4
2
2
Color text disabled.
2
Color text enabled.
2
1
4
12
2
bold_rooms
2
bold_exits
2
exit_sentence
2
full_owned
2
brief_pages
2
brief_rooms
2
npc_titles
2
rpg_titles
2
short_tbks
2
brief_finger
2
charset
2
color
36
1
2
!bold_rooms!bold_exits!exit_sentence!full_owned!brief_pages!brief_rooms!npc_titles!rpg_titles!short_tbks!brief_finger!charset!color!
36
1
4
0
36
1
5
2
5
2
display_options
2
5
0
0
2
4
4
4
2
Display Options
2
Ansi Options
2
Emulation Options
2
Emu Options
2
5
2
Options for customizing your display using ANSI (and other) emulation.
2
5
4
2
0
5438
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#76
generic gendered object

144
2
-1
-1
-1
79
35
-1
8
set_gender
2
173
-1
@gen*der
2
105
12
verb_sub
2
165
-1
pqc pq ppc pp prc pr poc po psc ps
36
165
-1
gender_adj gender_noun
36
165
-1
gender_tokenize
36
173
-1
gender_obj*ect
36
173
-1
init_for_core
2
173
-1
1
gender
29
1
150
2
5
5
2
5
5
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
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
1
2
generic gendered object
2
5
2
An object with some kind of gender distinction.  Maybe a wee-wee?
2
5
4
2
0
4975
0
955495071
100
1
5
2
5
5
2
5
0
0
2
5
5
2
5
5
115
1
#77
Generic Event Object

16
36
-1
-1
-1
1
-1
135
10
Assign_Args
36
173
-1
Fetch_Object
36
173
-1
Spawn
36
173
-1
Find
36
173
-1
New
36
173
-1
Set_Custom_Format
36
173
-1
Reset
36
173
-1
Origin Subject Dobj Iobj Other
36
165
-1
Text_For
36
173
-1
Sub_Template
36
173
-1
12
type
args
actors
task_id
template
phrase
intensity
arg_names
custom_format
compiled_text
actor_names
type_str
21
1
-1
36
5
4
0
36
1
1
-1
36
1
0
0
36
1
2

36
1
2

36
1
0
0
36
1
4
4
2
actors
2
phrase
2
intensity
2
template
36
1
4
0
36
1
2

36
1
4
5
2
origin
2
subject
2
dobj
2
iobj
2
other
36
1
2

36
5
0
0
36
4
4
2
2
Generic Event Object
2
event object
36
5
2
A package of information compiled by the $event broadcaster and shuttled off to a group of receivers for their response.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#78
generic character

144
2
-1
-1
-1
76
91
96
106
ctime time
2
173
-1
set_home
2
165
-1
set_linelength
2
173
-1
linelen
36
173
-1
tell_contents
2
165
-1
i inv*entory
2
13
-1
acceptable
2
173
-1
mu*rmur wh*isper
2
93
-2
carrying_msg
36
173
-1
resolve
2
165
-1
reroll
2
173
-1
random_body_area
2
173
-1
body_areas
2
173
-1
match_body_area
2
173
-1
hands
2
173
-1
hands_free
2
165
-1
set_hands
2
173
-1
speech_msg
2
173
-1
say
2
85
-2
give hand
2
93
1
go_home
2
173
-1
hear_event_exit
2
165
-1
follow
2
37
-1
following
2
13
-1
set_body_areas
2
165
-1
my_match_room
2
173
-1
walk
2
173
-1
_walk
2
173
-1
find_path
2
165
-1
move_by_exits
2
173
-1
join
2
173
-1
stop
2
85
-2
get take
2
29
-1
drop
2
29
-1
get_message
2
165
-1
set_message
2
165
-1
naked_*
2
173
-1
clothed_description
2
165
-1
@nudity
2
25
-1
_match_body_area_exactly
2
173
-1
is_controllable_by
2
165
-1
trusts
2
173
-1
is_trustable_action
2
173
-1
add_trusted_object
2
173
-1
remove_trusted_object
2
173
-1
parse_trust_args
2
165
-1
@trust @untrust
2
93
-2
@trusted
2
13
-1
my_call_verb
2
173
-1
clothing
2
173
-1
trustable_actions
2
173
-1
strip
2
157
5
match_clothing
2
165
-1
connected_seconds
2
173
-1
idle_seconds
2
173
-1
namec
2
173
-1
is_holding
2
173
-1
is_enclosing
2
173
-1
l*ook smell taste feel listen sniff touch
2
85
-2
emote
2
93
-2
get_integration_msg
2
165
-1
gib*ber
36
93
-2
available_exits
2
173
-1
move_through
2
165
-1
get_option*s
2
173
-1
@ic*! @ooc*!
115
93
-2
disfunc
2
173
-1
grab swipe take
2
157
5
set_my
2
173
-1
description
2
173
-1
ooc*
115
93
-2
abandon lose ditch unfollow
2
21
-1
tt*
115
93
-2
sub_name*c
36
173
-1
last_huh
2
165
-1
_last_pose _last_.*
2
93
-2
@ways
115
13
-1
carry
2
93
-2
eject_character
2
173
-1
features
36
165
-1
who_location_msg
36
173
-1
sight_level
115
173
-1
receive_event
36
173
-1
tell
115
173
-1
cpr
2
165
-1
look_place_msg
2
173
-1
provoke_mood
115
165
-1
do_behave
115
173
-1
pulse
115
165
-1
take_random_exit
115
173
-1
is_carryable*_by
115
173
-1
announce_carry_success announce_carry_failure announce_drop_success
115
173
-1
do_grab_from
115
173
-1
hear_event_speech hear_event_sound hear_event_motion
115
173
-1
emptyhanded_msg
36
173
-1
filter_speech
36
173
-1
stop_carrying
2
173
-1
title
36
173
-1
title_msg
36
173
-1
set_gm_notes
115
173
-1
get_gm_notes
115
173
-1
init_for_core
2
173
-1
examine_contents
36
173
-1
get_trustees
2
173
-1
set_species
115
173
-1
is_wearing
115
173
-1
23
verb_subs
home
brief
linelen
timezone_msg
ctype
carrying_msg
emptyhanded_msg
body_areas
hands
speech_msg
following
forked_follow
clothing
walking
nudity
grouped_body_areas
grouped_body_areas_nudity
clothing_integration_style
gm_notes
criminal
trustees
species
52
4
0
2
5
1
-1
2
5
0
0
2
5
0
79
2
1
2
CST
2
5
0
2
2
5
2
%S %<is> carrying %c.
36
5
2

36
5
4
19
1
273
1
274
1
275
1
276
1
277
1
278
1
279
1
280
1
281
1
282
1
283
1
284
1
285
1
286
1
287
1
288
1
289
1
290
1
291
2
1
4
2
1
282
1
283
115
1
2

2
5
4
0
2
1
0
0
2
1
4
19
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
4
0
2
1
0
0
2
1
4
19
2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2
4
4
5
2
shoulders
2
arms
2
hands
2
legs
2
feet
2
5
4
5
2

2

2

2

2

2
4
0
2
2
5
4
0
115
0
0
0
115
1
4
0
36
0
1
-1
115
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
1
2
generic character
2
5
2
My god what potential this man has!
2
5
4
2
0
83556
0
955495071
100
1
5
2
5
5
2
5
0
0
2
5
5
2
5
0
0
115
1
#79
generic tangible object

144
2
-1
-1
-1
80
5
-1
25
owatched_msg watched_msg
2
173
-1
look_self
2
165
-1
look_place_msg
2
173
-1
look_person_msg
2
173
-1
description
2
173
-1
smell_self
2
173
-1
taste_self
2
173
-1
feel_self
2
173
-1
hear_self
2
173
-1
aroma_msg taste_msg texture_msg sound_msg
2
173
-1
is_bonded_to
2
165
-1
set_v_size
36
173
-1
attempt_pickup
36
173
-1
init_for_core
2
173
-1
new_effect_id
115
173
-1
add_effect
115
173
-1
remove_effect
115
173
-1
integrated_description
115
173
-1
get_effect_integration
115
173
-1
pulse
115
173
-1
enterfunc
115
173
-1
exitfunc
115
173
-1
update_bulk
115
173
-1
dispatch_event_*
2
173
-1
is_affected_by
115
173
-1
14
title_msg
look_place_msg
look_person_msg
watched_msg
owatched_msg
aroma_msg
taste_msg
texture_msg
sound_msg
v_size
olocked_down_msg
locked_down_msg
effects
v_bulk
28
2

2
5
2

2
5
2

2
5
2

2
5
2

2
5
2

2
5
2

2
5
2

2
5
2

2
5
0
25
36
1
2

2
5
2

2
5
4
0
115
1
0
0
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
generic tangible object
2
tangible
2
5
2
Something you can see, feel, hear, taste, maybe even sniff.  Anyway, it's tangible.
2
5
4
2
0
13780
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#80
generic detailed object

144
2
-1
-1
-1
137
3
7
7
get_detail
2
173
-1
match_detail
2
173
-1
set_detail
2
173
-1
add_detail
2
173
-1
remove_detail
2
173
-1
detail_names detail_descs
2
173
-1
init_for_core
2
173
-1
2
detail_names
detail_descs
14
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
generic detailed object
2
detailed object
2
5
2
An object which is perhaps more than meets the eye.
2
5
4
2
0
3979
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#81
generic puppet

144
2
-1
-1
-1
92
87
201
32
mon*itor @mon*itor
115
45
-1
ign*ore @ign*ore
115
45
-1
add_monitor
115
173
-1
del_monitor
115
173
-1
notify
2
173
-1
com*mand @com*mand
115
101
1
do
2
173
-1
find_verb
2
173
-1
sweep_msg
115
173
-1
call
2
173
-1
monitor_ok command_ok
115
173
-1
initialize
115
173
-1
parse_command
2
173
-1
call_as_puppet
2
173
-1
monitor_prefix_msg
115
173
-1
is_controllable_by is_writable_by is_readable_by
115
173
-1
is_shaper shapers
115
173
-1
recycle
115
173
-1
go_home
2
173
-1
do_scene
2
165
-1
hear_event_attack hear_event_heal
115
165
-1
@set-home
2
109
1
idle_seconds
115
173
-1
connected_seconds
115
173
-1
is_listening
115
173
-1
is_unique
2
173
-1
behave
115
173
-1
hear_event_*
115
173
-1
take_exit move
115
173
-1
crystal_worth
115
173
-1
add_shaper
115
173
-1
remove_shaper
115
173
-1
4
monitors
monitor_prefix_msg
shapers
last_do_time
133
4
0
115
1
2
(%N) 
115
5
4
0
115
1
0
0
115
1
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
2
5
5
115
1
5
115
1
1
206
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
1
14531
2
5
5
2
5
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
115
1
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
4
5
2
5
5
2
4
5
2
5
5
115
0
5
115
1
5
36
0
5
115
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
5
2
4
5
2
4
5
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
generic puppet
2
puppet
2
5
2
A character over whom you could have total control.  BWAHAHA.
2
5
4
2
0
24133
0
2085978499
100
1
5
2
5
5
2
5
5
2
5
5
2
5
0
1
115
1
#82
generic zone

144
2
-1
-1
-1
1
171
83
7
acceptable
2
173
-1
match_room
2
173
-1
free_entry
2
173
-1
disfunc_leave_msg disfunc_arrive_msg
2
165
-1
local_objects
2
173
-1
is_writable_by
2
173
-1
init_for_core
2
173
-1
3
free_entry
disfunc_leave_msg
disfunc_arrive_msg
12
1
115
2
5
2
The resolution of %n fades.  With a hiss of static %p disconnected body implodes into a single point of light, then blinks out of existence.
2
5
2
You feel the presence of %n--%p disconnected self a static haze around you.
2
5
0
0
2
4
4
2
2
generic zone
2
zone
2
5
2
A zone is a group of rooms with attributes peculiar or their virtual geographic location.  Heh.  That's about it right now, bub. Eases room matching/mapping for :walk and such.
2
5
4
2
0
3775
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#83
utility class

16
2
-1
-1
-1
1
20
145
0
0
9
0
0
2
4
4
1
2
utility class
2
5
2
A common parent for all the many $*_utils.
2
5
4
2
0
319
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#84
exit utilities

16
2
-1
-1
-1
83
-1
90
5
oleave_msg
2
173
-1
match_direction
2
173
-1
oarrive_msg
2
173
-1
otherside_name
2
173
-1
from_name
2
173
-1
6
source_names
source_aliases
dest_names
source_messages
dest_messages
from_names
15
4
12
2
north
2
northeast
2
east
2
southeast
2
south
2
southwest
2
west
2
northwest
2
up
2
down
2
in
2
out
2
1
4
12
4
2
2
north
2
n
4
2
2
northeast
2
ne
4
2
2
east
2
e
4
2
2
southeast
2
se
4
2
2
south
2
s
4
2
2
southwest
2
sw
4
2
2
west
2
w
4
2
2
northwest
2
nw
4
2
2
up
2
u
4
2
2
down
2
d
4
2
2
in
2
enter
4
2
2
out
2
exit
2
1
4
12
2
south
2
southwest
2
west
2
northwest
2
north
2
northeast
2
east
2
southeast
2
down
2
up
2
out
2
in
2
1
4
12
2
%N %<goes> north.
2
%N %<goes> northeast.
2
%N %<goes> east.
2
%N %<goes> southeast.
2
%N %<goes> south.
2
%N %<goes> southwest.
2
%N %<goes> west.
2
%N %<goes> northwest.
2
%N %<goes> up.
2
%N %<goes> down.
2
%N %<goes> in.
2
%N %<exits>.
2
1
4
12
2
%N %<arrives> from the south.
2
%N %<arrives> from the southwest.
2
%N %<arrives> from the west.
2
%N %<arrives> from the northwest.
2
%N %<arrives> from the north.
2
%N %<arrives> from the northeast.
2
%N %<arrives> from the east.
2
%N %<arrives> from the southeast.
2
%N %<arrives> from below.
2
%N %<arrives> from above.
2
%N %<comes> in.
2
%N %<comes> out.
2
1
4
12
2
the north
2
the northeast
2
the east
2
the southeast
2
the south
2
the southwest
2
the west
2
the northwest
2
above
2
below
2
inside
2
outside
2
1
0
0
2
4
4
1
2
exit utilities
2
5
2
A pretty lame attempt at some standard directional utility methods.
2
5
4
2
0
5582
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#85
Mail Feature Object

16
2
-1
-1
-1
74
-1
129
3
@nn
2
5
-1
@refile @copym*ail
2
93
-2
@read-all-new*-mail @ranm
2
29
-1
0
46
1
-1
36
1
4
2
2
@nn
2
@refile
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
1
5
2
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
4
2
Mail Feature Object
2
MailFO
2
Mail FO
2
is_player_feature
2
5
2
Commands to make your MOO-mailing easier.
2
5
4
2
0
5779
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#86
generic heart

16
2
-1
-1
-1
1
160
104
17
is_writable_by is_controllable_by
2
173
-1
pump
2
173
-1
heartbeat
2
165
-1
remove
2
173
-1
add
2
173
-1
full
2
173
-1
transplant
2
165
-1
spawn_heart
2
165
-1
initialize
2
173
-1
call
2
165
-1
call_next
2
165
-1
aorta
2
165
-1
set_heart
2
165
-1
valid_object
2
173
-1
should_bypass
115
173
-1
shuffle
2
173
-1
init_for_core
2
173
-1
8
heartbeat_task
queue
interval
max
root
recycler
current
last_call_time
17
0
0
2
1
4
0
2
1
0
180
2
5
0
180
2
5
1
86
2
5
1
62
2
5
1
-1
2
1
0
0
2
1
0
0
2
4
4
2
2
generic heart
2
heart
2
5
4
2
2
A throbbing mass of taut muscle, sending pulses of life out through arteries and into the many appendages of the MOO.
2
Actually, it's just a simple task scheduler.
2
5
4
2
0
11276
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#87
generic automaton

144
115
-1
-1
-1
81
88
-1
7
_start
115
173
-1
_stop
115
173
-1
@start @stop
115
41
-1
running
115
173
-1
connected_seconds
115
173
-1
do_behave
115
165
-1
init_for_core
2
173
-1
4
heart
running
last_start_time
last_stop_time
137
1
160
115
5
0
0
115
1
0
0
115
1
0
0
115
1
4
0
115
1
5
115
5
5
115
1
0
794444592
115
1
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
5
5
115
5
5
115
1
5
115
1
1
206
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
1
5
115
5
5
2
1
5
2
1
5
2
1
5
2
1
5
115
4
5
115
5
5
115
4
5
115
5
5
115
0
5
115
1
5
36
0
5
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
5
115
4
5
115
4
5
36
1
5
115
5
5
115
5
0
0
115
4
4
3
2
generic automaton
2
automaton
2
robot
115
5
4
4
2
"It's alive!  ALIVE!  oH dear god now I know how it feels to be god!"
2
-Baron Victor vonFrankenstein
2
 
2
A puppet which receives pulses from the universal $heart, and performs actions when tweaked by same.
115
5
4
2
0
5450
0
2085978633
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#88
generic wanderer

144
115
-1
-1
-1
87
103
-1
13
will_wander
115
173
-1
@wander-chance
115
109
12
get_exits
115
165
-1
ok_exit
115
165
-1
choose_exit
115
173
-1
move take_exit
115
173
-1
wander
115
173
-1
set_wander_chance set_forbidden_exits set_forbidden_rooms set_allowable_zones set_forbidden_terrain
115
173
-1
do_flee
115
173
-1
behave
115
173
-1
my_oleave_msg my_oarrive_msg
36
173
-1
ok_destination_terrain
115
173
-1
take_random_exit
115
173
-1
7
wander_chance
forbidden_exits
forbidden_rooms
allowable_zones
my_oleave_msg
my_oarrive_msg
forbidden_terrain
144
0
50
115
1
4
0
115
1
4
0
115
1
4
0
115
1
2

115
5
2

115
5
4
0
115
1
1
160
115
5
0
0
115
1
0
0
115
1
0
0
115
1
4
0
115
1
5
115
5
5
115
1
0
794444601
115
1
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
5
5
115
5
5
115
1
5
115
1
1
206
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
0
0
115
1
0
0
115
1
0
0
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
1
5
115
5
5
2
1
5
2
1
5
2
1
5
2
1
5
115
4
5
115
5
5
115
4
5
115
5
5
115
0
5
115
1
5
36
0
5
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
5
115
4
5
115
4
5
36
1
5
115
5
5
115
5
0
0
115
4
4
3
2
generic wanderer
2
wanderer
2
nomad
115
5
2
This fella can't seem to stand still.
115
5
4
2
0
7960
0
2085978633
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#89
generic furniture

144
2
-1
-1
-1
5
-1
93
32
sit_from_standing_msg sit_from_reclining_msg recline_from_standing_msg recline_from_sitting_msg rise_from_sitting_msg rise_from_reclining_msg
2
173
-1
osit_from_standing_msg osit_from_reclining_msg orecline_from_standing_msg orecline_from_sitting_msg orise_from_sitting_msg orise_from_reclining_msg
2
173
-1
no_room_msg ono_room_msg push_msg opush_msg
2
173
-1
sitting reclining
2
173
-1
look_place_msg look_msg
2
173
-1
sitting_msg reclining_msg
2
173
-1
space
2
173
-1
sit rest
2
157
-2
recline lie sleep
2
157
-2
stand rise
2
157
-2
add_sitting add_reclining
2
173
-1
remove_sitting remove_reclining
2
173
-1
empty_msg
2
173
-1
look_self
2
173
-1
push shove
2
157
5
acceptable
2
173
-1
bless_for_placement
2
173
-1
place put lay set
2
149
4
remove retrieve
2
149
5
placed_on_msg
2
173
-1
occupied_msg
2
165
-1
put_msg oput_msg remove_msg oremove_msg
2
173
-1
occupants
2
173
-1
is_enclosing
2
173
-1
announce*_all_but
2
165
-1
hear_event_sit hear_event_recline hear_event_stand
2
165
-1
announce_to
2
173
-1
attached_contents
2
173
-1
hear_event_exit
2
165
-1
tt_prefix_msg
36
173
-1
is_occupied_by
36
173
-1
place_person
2
173
-1
35
space
sitting
reclining
sitting_msg
reclining_msg
empty_msg
they
compatible_integrating_room
osit_from_standing_msg
osit_from_reclining_msg
orecline_from_standing_msg
orecline_from_sitting_msg
orise_from_sitting_msg
orise_from_reclining_msg
ono_room_msg
sit_from_standing_msg
sit_from_reclining_msg
recline_from_standing_msg
recline_from_sitting_msg
rise_from_sitting_msg
rise_from_reclining_msg
no_room_msg
sitting_alone_msg
reclining_alone_msg
opush_msg
push_msg
put_msg
oput_msg
remove_msg
oremove_msg
place_task
placed_on_msg
tt_prefix_msg
oplace_person_msg
place_person_msg
77
0
1
2
5
4
0
2
1
4
0
2
1
2
%(ititlec) %<is> sitting together on %[tdtitle].
2
5
2
%(ititlec) %<is> lying together on %[tdtitle].
2
5
2
%[tdtitlec] is invitingly empty.
2
5
1
24556
2
5
1
179
2
5
2
%N %<sits> down on %[tdname].
2
5
2
%N %<sits> up on %[tdname].
2
5
2
%N %<flops> down on %[tdname].
2
5
2
%N %<lies/lie> back on %[tdname].
2
5
2
%N %<rises> from %[tdname].
2
5
2
%N %<rises> from %[tdname].
2
5
2
%N %<tries> to sit on %[tdname], but there isn't enough room.
2
5
2

2
5
2

2
5
2

2
5
2

2
5
2

2
5
2

2
5
2

2
5
2
%(ititlec) %<is> %<sitting> on %[tdtitle].
2
5
2
%(ititlec) %<is> %<sprawled> out on %[tdtitle].
2
5
2
%N %<pushes> %[diname] off %[tdname].
2
5
2

2
5
2

2
5
2
%N %<places> %[diname] on %[tdname].
2
5
2

2
5
2
%N %<removes> %[diname] from %[tdname].
2
5
4
2
0
0
1
-1
2
1
2
You see %[dititle] on %[tdtitle].
2
5
2
Here at %[tdname], 
2
5
2
%N gently %<places> %[diname] on %[tdname].
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
5
2
5
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
generic furniture
2
furniture
2
5
2

2
5
4
2
0
22391
0
955495071
100
1
5
2
5
5
2
5
5
2
5
4
47
2
==== Verbs one would expect on furniture:
2
 
2
sit on furniture               -- Sit down on the furniture.
2
lie on furniture               -- Lie down on the furniture.
2
stand from furniture            -- Git yo lazy ass up.
2
 
2
push someone from furniture    -- Push some poor sap from the furniture.
2
 
2
To set how many folks the furniture may hold:
2
       @set furniture.space to <how-many-people>
2
 
2
==== The following messages describe how the furniture appears:
2
 
2
@sitting
2
@reclining -- Shown when several are sitting/reclining on the furniture.
2
                 %N is sub'd with a list of their names.  Be sure to
2
                 enclose any verbs in your message in %<verb> for
2
                 proper conjugation.
2
 
2
@sitting_alone
2
@reclining_alone -- Shown when only one person is sitting/reclining.
2
                       Sub'd normally.
2
 
2
@empty -- Shown when nobody's on the furniture.
2
 
2
==== The following messages describe actions on the furniture:
2
 
2
@osit_from_standing    -- Sitting from a standing position.
2
@osit_from_reclining   -- Sitting up from a reclining position.
2
@orecline_from_standing        -- Reclining from a standing position.
2
@orecline_from_sitting -- Recline from a sitting position.
2
@orise_from_sitting    -- Stand up while sitting.
2
@orise_from_reclining  -- Stand up while reclining.
2
 
2
@ono_room              -- When there isn't any room left.
2
 
2
@opush                 -- When pushing someone off the furniture.
2
                               %d is the person being pushed.
2
 
2
The above messages are shown to everyone in the room, conjugated
2
 through $you:say_action.  If a message without the leading 'o' is
2
 set, that message is shown to the player, and the 'o'-message is
2
 printed to the room.
2
 
2
==== Integration
2
 
2
The furniture will handle integration for everyone sitting/reclining upon it, removing them from the contents list.
2
5
5
115
1
#90
RPG Utilities

16
2
-1
-1
-1
83
-1
102
44
roll
2
165
-1
result
2
173
-1
match_skill match_stat*istic match_att*ribute
2
173
-1
is_char*acter
2
173
-1
trusted
2
173
-1
prod_hearts
2
165
-1
spawn
2
165
-1
att_bonus
115
173
-1
parse_reroll_args
115
173
-1
standard_tell_stats
115
173
-1
build_stat_line
115
173
-1
is_combat_action
115
173
-1
is_rushed_task
115
173
-1
add_rushed_task
115
173
-1
delete_rushed_task
115
173
-1
character_match_failed
115
173
-1
match_character
115
173
-1
is_violent_action
115
173
-1
maybe_prod_hearts
115
173
-1
match_global_loc*ation
115
173
-1
is_global_loc*ation
115
173
-1
global_locations
115
173
-1
faux_pedest
2
165
-1
char_stat_line
115
173
-1
delete_stat
2
165
-1
wts_code
115
173
-1
ic_ctime
115
173
-1
ic_time
115
173
-1
log_stat_change
2
172
-1
user_desig*nation
115
173
-1
init_for_core
2
173
-1
is_npc
115
173
-1
is_ic_neutral_location
115
173
-1
forbid_strict_ic_entry
115
173
-1
maybe_forbid_ic_switch
115
173
-1
notify
36
173
-1
stat_match_failed
115
173
-1
_ic_to_ooc_ctime
115
173
-1
ic_ctime_diff
115
173
-1
allow_player_stat_change_by
115
173
-1
is_newbie
115
173
-1
_indent_skill
115
173
-1
indented_skill_sheet
115
173
-1
trusts_to_spawn
36
173
-1
49
skills
atts
monitors
senses
skin_tones
races
eye_colors
hair_colors
hair_lengths
hair_styles
build_levels
height_levels
all_hearts
last_heart_update
update_interval
last_heart_called
corpse
crystal
basic_combat_reactor
atts_regexp
jump_seed
actions
rushed_task
att_object
reroll_dist_pts
reroll_hard_pts
reroll_base_att
reroll_high_att
violent_actions
bypass
global_locations
max_queue_length
idle_safety_threshold
disconnect_safety_threshold
last_death_penalty_change
YEAR_OFFSET
BASE_TIME
TIME_RATE
month_data
stat_changes
gibber_threshold
strict_ic
npc_are_immortal
npc_are_invulnerable
neutral_location_title_tag_msg
pot_rate
proposed_advantages
forgive_forget_time
trusted_spawners
58
1
147
2
1
4
6
2
agility
2
dexterity
2
endurance
2
quickness
2
strength
2
willpower
2
1
4
3
2
injury
2
insanity
2
fatigue
2
1
4
3
2
sight
2
hearing
2
smell
2
1
4
5
2
pale
2
light
2
average
2
swarthy
2
dark
2
5
4
12
2
American
2
Asian
2
Indonesian
2
Native American
2
Aborigine
2
European
2
African
2
Slavik
2
Nordic
2
Arab
2
Celtic
2
Latino
2
5
4
6
2
pink
2
blue
2
green
2
hazel
2
brown
2
black
2
5
4
7
2
bleached
2
white
2
grey
2
blonde
2
red
2
brown
2
black
2
5
4
5
2
glabrous
2
bald
2
short
2
shoulder-length
2
long
2
5
4
9
2
straight
2
wavy
2
curly
2
permed
2
spiked
2
mohawk
2
page-boy
2
balding
2
receding
2
5
4
5
2
thin
2
slender
2
average
2
fat
2
obese
2
5
4
5
2
dwarvish
2
short
2
average
2
tall
2
giant
2
5
4
2
1
160
1
159
2
1
0
0
2
1
0
86400
2
1
1
-1
2
1
1
155
2
1
1
154
2
1
1
156
115
1
4
6
2
agility%|agl%|Ag
2
dexterity%|dex%|Dx
2
endurance%|end%|Ed%|constitution%|con%|co
2
quickness%|qu%|quick%|speed%|spd%|sp
2
strength%|st%|str
2
willpower%|will%|wil%|wp
115
1
2
69
115
1
4
20
2
attack
2
kill
2
stun
2
knockout
2
dodge
2
flee
2
heal
2
revive
2
wield
2
ready
2
sheathe
2
unwield
2
sling
2
shoot
2
glare
2
block
2
unblock
2
defend
2
guard
2
eat
115
1
0
0
115
1
1
157
115
1
0
100
115
1
0
30
115
1
0
35
115
1
0
75
115
1
4
2
2
attack
2
shoot
115
1
0
0
115
1
4
2
1
61
1
196
115
1
0
10
115
1
0
300
115
1
0
120
115
1
0
812993272
115
1
0
658
115
1
0
752149389
115
1
9
3
115
1
4
12
4
3
2
Jan
2
Alpha
0
31
4
3
2
Feb
2
Beta
0
29
4
3
2
Mar
2
Gamma
0
31
4
3
2
Apr
2
Delta
0
30
4
3
2
May
2
Epsilon
0
31
4
3
2
Jun
2
Zeta
0
30
4
3
2
Jul
2
Eta
0
31
4
3
2
Aug
2
Theta
0
31
4
3
2
Sep
2
Iota
0
30
4
3
2
Oct
2
Kappa
0
31
4
3
2
Nov
2
Lambda
0
30
4
3
2
Dec
2
Mu
0
31
115
1
4
0
2
0
0
50
115
1
0
1
115
1
0
0
115
1
0
0
115
1
2
 (An IC-Neutral Location)
2
5
4
2
0
300
9
0.01000000000000000021
115
1
4
129
4
5
2
Reputation
2
This advantage can be a two-edged sword.  When you buy it, it means the character is a person of some renown.  Those with appropriate IC background (especially a Reputation with similarities to yours!) may choose to RP recognizing you by sight, and even know your name.  This doesn't always mean you know them.  But if you buy the Advantage, it means you're  visually distinct, and have achieved noteworthy status in your field. You should specify precisely what's common knowledge about you in your .plan . (This Advantage can retroactively give your character a history.)
1
2
0
908403227
0
5
4
5
2
Poet
2
Proficiency in writing free verse of any sort.  May include lyricist.
1
2
0
908404016
0
0
4
5
2
MOOsex avoidance
2
The fine art of avoiding the wily Sinner's idiotic attempts at seduc..er, natural charm. I won't add any more fake advantages - I promise.
1
14172
0
908404587
0
5
4
5
2
Martial Arts
2
Let us have a coded skill in basic martial arts. Coded kicks, strikes. Many have asked for this. Make it so weapons cannot be wielded if in a MA mode. Have the ability to disarm with a kick or a punch, but lost the ability to make any other attack for that 'round'
1
5288
0
908405696
0
20
4
5
2
Foreign language proficiency. Ability
2
speak, read, write, understand any language other then English.
1
24173
0
908464708
0
0
4
5
2
Occult Knowledge
2
General knowledge used by either mages or not mages, useful for information about certain potions, hexes, and curses.
1
10660
0
908469980
0
0
4
5
2
Magic Theory
2
The understanding of the very essence of magick.
1
10660
0
908470117
0
0
4
5
2
Necromancy Lore
2
The understanding of using the magical energies of death.
1
10660
0
908470117
0
0
4
5
2
Energy Manipulation (Spirit)
2
Using the energy of a mages body, or their enemy to create a magical effect. As well as percieving, controlling and creation of the same energy.
1
10660
0
908470117
0
0
4
5
2
Black Magick Lore
2
Otherwise known as Sympathetic Magick, used by voodoo priests predominately.
1
10660
0
908470117
0
0
4
5
2
Translocation Lore
2
The magical art of understanding gate magic. higher circles such as seekers may only produce a small effects the size of a pouch. The magic grows as the circle lowers, for a magister can now effectively store weapon caches such as bladed or staves in these pocket dimensions. A magus as well can open entire gates to pass through himself, or with others.
1
10660
0
908470117
0
0
4
5
2
Enchantment Lore
2
The use of magerunes, wards, and glyphs. wards to prevent, glyphs to empower.
1
10660
0
908470118
0
0
4
5
2
Ritual Magick Lore
2
The use of magic when concerning ceremonies. Be it for sacrafice, or using groups of mages to preform a effect.
1
10660
0
908470118
0
0
4
5
2
Conjuring Lore
2
Creation of objects from thin air using magic, predominately non living objects, though some rumors speak of magi creating animals from thin air.
1
10660
0
908470118
0
0
4
5
2
Blood Magick Lore
2
Use of blood in magical effects, be it from soulbonding to converting blood into magical energy.
1
10660
0
908470118
0
0
4
5
2
Spell Weaving
2
Using a mages knowledge to formulate magic that is not normally exercised.
1
10660
0
908470118
0
0
4
5
2
Alchemy Lore
2
The making of potions and other concoctions.
1
10660
0
908470118
0
0
4
5
2
Illusion Lore
2
The art of making magical illusions from thin air.
1
10660
0
908470118
0
0
4
5
2
Stealth
2
Hiding in shadows, the mundane ability to stay away from others perceptions.
1
10660
0
908470543
0
0
4
5
2
Acting
2
The ability to act and pretend to be who your not.
1
10660
0
908470543
0
0
4
5
2
Ettiquette
2
Common knowledge of procedures of higher class living
1
10660
0
908470544
0
0
4
5
2
Singing
2
Be it eulogy, or happy songs you sing.
1
10660
0
908470544
0
0
4
5
2
Calligraphy
2
Artistic writing of one form or another.
1
10660
0
908470544
0
0
4
5
2
Linguistics
2
The study of languages.
1
10660
0
908470544
0
0
4
5
2
Acrobatics
2
Preformance of showing off ones gymnastic ability.
1
10660
0
908470544
0
0
4
5
2
Chandler
2
The creation of candles, from wax, or fat from.... animals.
1
10660
0
908470544
0
0
4
5
2
Taxidermy
2
The fine art of preserving one's kills/family pets/relatives for later amusement. Not THAT kind of amusement. Moose heads, bears, and maybe even people.
1
14172
0
908471152
0
0
4
5
2
Herbalism
2
Knowledge of various herbs and natural plants, and what they are useful for. From healing and cooking herbs to natural poisons.
1
14172
0
908471258
0
0
4
5
2
Toxicology
2
Knowledge of various poisons, from cobra venom to belladonna, and their uses.
1
14172
0
908471315
0
0
4
5
2
Appearance
2
Appearance is something anyone can do in their description, however, someone actually purchasing appearance has done so for a reason, this is like reputation and can be just as much as a double edged sword. Appearance does not mean physical beauty, it can mean the character is horridly ugly, mysterious, angelic, innocent, repulsive, etc. By purchasing appearance one allows others to immediately react, much like reputation does.
1
3791
0
908476378
0
5
4
5
2
Divination
2
The ability to read someone's fortune through some medium, be it Weirdan, runes, tarot cards, tea leaves, etc.
1
3791
0
908477387
0
0
4
5
2
Psychology
2
Someone with this skill has an intimate knowledge of how the mind *may* work. In higher levels, said person may be able to counsel and aid people with psychological disorders.
1
3791
0
908477415
0
0
4
5
2
Painting
2
The ability to create artwork via the medium of paints.
1
3791
0
908477533
0
0
4
5
2
Torture
2
Someone with this skill knows several torture techniques and how to extract information, or just cause exquisite pain while managing to keep the victim alive for more.
1
3791
0
908477855
0
0
4
5
2
Dance
2
Someone with this ability can, well, dance. Specialties can be made, such as Dance (Exotic).
1
3791
0
908478308
0
0
4
5
2
Instrument
2
Someone with this skill may play one or more instruments with some kind of profficiency. Instruments the character knows should be tacked on the end in ()'s. Such as Instrument (Oboe).
1
3791
0
908478378
0
0
4
5
2
Elemental Magic
2
Earth, Fire, Wind, Water. The control, use, and creation of the elements.
1
16038
0
908493600
0
0
4
5
2
Spellsinging
2
The use of instrutments, music, or any form of singing to create magic.
1
16038
0
908493652
0
0
4
5
2
Spellsinging Rituals
2
ritual based spellsinging, some might call it orchestra magic.
1
16038
0
908493659
0
0
4
5
2
Spellsinging Conjuring
2
Creation of some form from magic, Oh my how'd that brick wall appear over my head suddenly.
1
16038
0
908493659
0
0
4
5
2
Spellsinging Summoning
2
Summoning of musical spirits, I've heard of invoking the muses but this is rediculous.
1
16038
0
908493659
0
0
4
5
2
Spellsinging Enchantment
2
The enchanting of objects using music, often used to create silence in rooms, or the opposite of keening screams.
1
16038
0
908493659
0
0
4
5
2
Hedge Magick
2
Less conventional then real magic, this is the use of homemade magic. Perhaps considered more mudane belief is still powerful enough to make it the nemesis of black magic.
1
16038
0
908493659
0
0
4
5
2
Magic Lore
2
The study of, magic. Very deep.
1
16038
0
908493660
0
0
4
5
2
Philosophy Lore
2
The pursuit of wisdom, the eternal question of why.
1
16038
0
908493660
0
0
4
5
2
Spell Myths/Legends
2
Study of Myths and Legends
1
16038
0
908493660
0
0
4
5
2
Cataclysm Lore
2
Study of things that happened before the Cataclysm
1
16038
0
908493660
0
0
4
5
2
Spellsinging Lore
2
Study of Spellsinging, it's history and general knowledge.
1
16038
0
908493660
0
0
4
5
2
Streetwise
2
Knowledge of urban areas, how to survive and find ones way around.
1
16038
0
908493660
0
0
4
5
2
Survival
2
Ability to learn how to survive, be it in forests, underwater, or wasteland.
1
16038
0
908493660
0
0
4
5
2
Animal Handling
2
Care for animals, be it horse or dragon.
1
16038
0
908493660
0
0
4
5
2
Hunting
2
The ability to search for food, and set traps.
1
16038
0
908493660
0
0
4
5
2
Pet/Fighter Can be a follower, warder, or even Seahound. THis
2
an old one not really used anymore, but it does still exist.
1
9972
0
908493834
0
10
4
5
2
Espionage
2
The art of spying, being sneaky, and stealing information.
1
9972
0
908494378
0
0
4
5
2
Ambidextrous
2
The skill of using both hands in combat at the same time.
1
9972
0
908494378
0
0
4
5
2
Tracking
2
The skill of following an animal, or person's tracks. Be it underwater, or on wasteland.
1
9972
0
908494378
0
0
4
5
2
Combat Strategy
2
Learning a fighters methods by watching them during battle.
1
9972
0
908494378
0
0
4
5
2
Time Sense
2
The ability to judge the time of day no matter where one is by instincts.
1
9972
0
908494378
0
0
4
5
2
Demonology
2
The study of old demon names, as well as their personalities and power.
1
23970
0
908495102
0
0
4
5
2
Summoning
2
The magical ability to summon otherworldly beings.
1
23970
0
908495102
0
0
4
5
2
Life Magick
2
The use of magic to heal, be it the mind, or the body.
1
23970
0
908495102
0
0
4
5
2
Procurement
2
The commonly misplaced idea that gypsies find things. In otherwords, pickpocketing.
1
23970
0
908495102
0
0
4
5
2
Ancient Languages
2
The study of ancient languages
1
11884
0
908495449
0
0
4
5
2
Stage Magic
2
The preformance of mudane magic, pulling rabbits out of hats, card tricks, etc.
1
11884
0
908495449
0
0
4
5
2
Chemistry
2
the study of substances's structure and proposition.
1
11884
0
908495449
0
0
4
5
2
Street Brawling
2
The ability to to fight with fisticuffs.
1
11884
0
908495449
0
0
4
5
2
Heraldry
2
The study of family seals, emblems and creation of ones as well.
1
11884
0
908495449
0
0
4
5
2
Climbing
2
The ability to climb, be it walls, mountainsides or ladders.
1
24528
0
908495682
0
0
4
5
2
Lockpicking
2
The ability to pick locks.
1
24528
0
908495682
0
0
4
5
2
Reading & Writing
2
The ability to read and write.
1
24528
0
908495682
0
0
4
5
2
Navigation
2
The ability to judge direction and orientation from well-known landmarks or devices including compasses, the sun/moon, GPS, maps, foilage/rocks indigenous to certain levels of elevation, etc.
1
13170
0
908499010
0
0
4
5
2
Tailor
2
The ability to mend, alter and 'assemble' clothing.
1
13170
0
908499086
0
0
4
5
2
Leather working/tanning
2
The ability to work with leather, to fashion clothes, armor, saddles...just about anything.
1
13170
0
908499121
0
0
4
5
2
Midwife
2
Knowledge of the birthing process, care for the newborn.
1
13170
0
908499436
0
0
4
5
2
Ride
2
Ability to ride a certain class of animal (horse, dragon, etc.)
1
13170
0
908499514
0
0
4
5
2
Diplomacy
2
A skill for use in diplomatic relations. It also means you know how to behave yourself so as not to tick off another party.
1
3791
0
908515784
0
0
4
5
2
Lyrical Composition
2
The ability to compose song lyrics.
1
3791
0
908515846
0
0
4
5
2
Musical Composition
2
The ability to compose and read music.
1
3791
0
908515883
0
0
4
5
2
Literature
2
The knowledge of a certain type of literature, such as Ancient History or Fairy tales.
1
3791
0
908515937
0
0
4
5
2
Holistic Medicine
2
Much like herbalism, only it's a basic knowledge of medicinal plants as well as old wives' tales of remedies for common everyday illness and such. The individual can choose a variety of specializations from Healing Herbs/Poultices to Hallucinogens.
1
3791
0
908516031
0
0
4
5
2
Theology
2
Knowledge of a specific religion or group of religions such as Christianity or Celtic Paganism.
1
3791
0
908516113
0
0
4
5
2
Meditation
2
The ability to sit still for hours on end, meditating on the goals of life. Or something like that.
1
3791
0
908516227
0
0
4
5
2
Disguise
2
The ability to disguise or conceal one's self to appear like someone other then his/her original visage. The art of deception on a physical level.
1
3791
0
908516304
0
0
4
5
2
Mimicry
2
The ability to imitate other sounds, from animal cries to human voices.
1
3791
0
908516340
0
0
4
5
2
Pain Tolerance
2
The ability to shrug off pain, not necessarily physical, as that's described in the coded ability of 'shock', but rather, it takes an awful lot to torture something out of you.
1
3791
0
908516393
0
0
4
5
2
Swimming
2
The ability to swim in somewhat shallow waters. Not the ability Submariners have.
1
3791
0
908516458
0
0
4
5
2
Blacksmithing
2
The art of forging various types of metal into swords, horse shoes, or anything mental.
1
24462
0
908553254
0
0
4
5
2
Riding
2
The skill of riding horses, dragon,s or whatever mountable animal.
1
24462
0
908553254
0
0
4
5
2
Investigation
2
The skill of being able to recognize ones surroundings.
1
24462
0
908553254
0
0
4
5
2
Oration
2
The ability to give speeches
1
24462
0
908553254
0
0
4
5
2
Blade Mastery
2
An art of sword fighting for highly developed swordsmen.
1
24462
0
908553254
0
0
4
5
2
Biblical Scripture
2
Quoting biblical scripture, what fun.
1
24462
0
908553255
0
0
4
5
2
Chivalry
2
The system and spirit of belief for the medieval knights.
1
24462
0
908553257
0
0
4
5
2
Artificing
2
The creation of magical items.
1
23970
0
908554618
0
0
4
5
2
Spell Mimicry
2
Copying of one spellcraft in order to reproduce a counter effect to nullify it.
1
23970
0
908554618
0
0
4
5
2
Scarification
2
The use of magic to scar a persons body, similar to runes but with a different useage of symbols.
1
23970
0
908554618
0
0
4
5
2
Patteran Kumpania Sign Language (Exclusive Skill)
2
Sigh language used by gypsies.
1
23970
0
908554618
0
0
4
5
2
Romany (Exclusive Skill)
2
Language used by gypsies.
1
23970
0
908554621
0
0
4
5
2
Body Art
2
Tattooing/Body peircing. You know how to do it.
1
3791
0
908559069
0
0
4
5
2
Blind Fighting
2
The ability to fight with one's eyes closed, or the ability to fight when blinded. You have an extremely good spatial ability.
1
3791
0
908559294
0
0
4
5
2
Cooking
2
The ability to whip up a variety of meals from whatever your nimble little fingers can find.
1
3791
0
908559333
0
0
4
5
2
Biology
2
You have an extensive knowledge of the way life affects life within these cataclysmic times.
1
3791
0
908559500
0
0
4
5
2
Botany
2
You have an eye for plants, and unlike Herbalism (which is not as scientific based) you have established knowledge of a vast array of plant life and what each plant has been proven to provide.
1
3791
0
908559615
0
0
4
5
2
Astronomy
2
You can identify most constellations on sight, know the alignment of the planets and can find your way by watching the stars at night.
1
3791
0
908559660
0
0
4
5
2
Zoology
2
You have extensive knowledge of the animals and beasts that have arisen since the cataclysm and have studied them scientifically.
1
3791
0
908559716
0
0
4
5
2
Area Knowledge
2
You have specific and intimate knowledge of a certain area, such as Ghostwheel Plains or the Wasteland.
1
3791
0
908559762
0
0
4
5
2
History
2
You're educated and you have learned the past mistakes humans have made. Anything from more recent history to Ancient history to Cataclysmic history.
1
3791
0
908559820
0
0
4
5
2
Military Science
2
You're a skilled military tactician and this knowledge allows you to know how to lead an army of men, what you will need to supply them with, how to deploy them and the best method of cutting off your enemy.
1
3791
0
908559914
0
0
4
5
2
Naturalist
2
You have a deep and intimate knowledge of the wooded areas and can survive there for many months if not years without harm to yourself. You can read natural signs, and even accurately predict the weather by watching the patterns and feeling the pressure in the air.
1
3791
0
908559979
0
0
4
5
2
Brewing/Distilling
2
You know how to create alcoholic beverages and age them or not age them as the case may be.
1
3791
0
908560169
0
0
4
5
2
Camouflage
2
You know how to hide yourself, someone else or something you wish to remain unseen by another party. This is not disguise, it's the art of blending in.
1
3791
0
908560215
0
0
4
5
2
Carpentry
2
The ability to work with wood, to make houses or furniture.
1
3791
0
908560270
0
0
4
5
2
Masonry
2
The ability to work with stone to create walls and/or houses.
1
3791
0
908560318
0
0
4
5
2
Sculpting
2
The ability to create works of art from stone or wood or other 3D material.
1
3791
0
908560445
0
0
4
5
2
Philosophy
2
This skill is the ability to debate rationally the morals of life, for or against.
1
3791
0
908561273
0
0
4
5
2
Politics
2
The ability to assess the local political scene and use it to your advantage.
1
3791
0
908595813
0
0
4
5
2
Cosmetics
2
The art of make-up and hairstyling.
1
3791
0
908595849
0
0
4
5
2
Pilot Skill
2
The ability to operate some kind of vehicle, be it a helicoptor, a motorcycle or a boat. Specialty should be denoted.
1
3791
0
908596078
0
0
4
5
2
Demolitions
2
The knowledge of explosives, timers, how to use them and how to keep yourself from getting killed in the process.
1
3791
0
908596187
0
0
4
5
2
Forgery
2
The ability to forge signatures or created phony documents.
1
3791
0
908596281
0
0
4
5
2
Weapons Care
2
More then just knowing how to swing a sword or wield a crowbar, this means you have intimate knowledge of your weapon of choice, including cleaning and care to keep the weapon in prime condition.
1
3791
0
908596340
0
0
4
5
2
Surgery
2
The ability to do surgery on another person without harming them. Too much.
1
3791
0
908596423
0
0
4
5
2
Photography
2
Another form of artistic expression. Someone with this skill is profficient with a camera and some film.
1
3791
0
908596529
0
0
4
5
2
Combat knowledge
2
Choose between various forms such as aerial, land, sea, underground, hand to hand, weapons, etc. This skill lets people know you know something about combat, but not necessarily meaning it's true depending on level.
1
20348
0
908932860
0
0
4
5
2
Citadel
2
This advantage allows you to have a Citadel. ** Note: All citadel requests must go through a GM in order to have this skill added. You will pay the normal amount per room and exit crystal-wise, as well as for any furniture.
1
20348
0
908932985
0
10
4
5
2
Falconry
2
Uncoded skill to control a raptor puppet (as in hawk or falcon, NOT like a mini-dinosaur).
1
24173
0
909061304
0
0
4
5
2
Dream Interpretation (Non-Magical)
2
The ability to decipher dreams and reveal insights to others who are confused by the odd symbolism of dreams. This does not include magical or prophetical dreams.
1
3791
0
910037719
0
0
4
5
2
Dream Interpretation (Prophetical)
2
The ability to decipher dreams of a magical nature, normally those that give a glimpse of the future, or of a possible future event.
1
3791
0
910037785
0
0
4
5
2
Sympathetic Magick
2
Sympathetic magick is not a mind magick, as one might assume from its title. It is the ability to perform magicks using symbolism to represent the desired affect. (Such as donning a cainid mask during a ritual to give good luck to a hunting party.)
1
3791
0
910382928
0
0
115
1
0
259200
115
1
4
0
2
1
0
0
2
4
4
2
2
RPG Utilities
2
RPG
2
5
2
Various verbs useful in skill resolution and other RPG tasks.
2
5
4
2
0
56260
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#91
generic skilled character

144
115
-1
-1
-1
78
92
-1
18
resolve
115
173
-1
reroll
115
173
-1
is_stat
115
173
-1
stat_*
115
173
-1
penalty
115
173
-1
match_skill match_stat*istic match_att*ribute
115
173
-1
total
115
165
-1
initialize
115
173
-1
all_skills
115
173
-1
set_stat_*
2
173
-1
get_stat
115
173
-1
set_stat
115
173
-1
mod_stat
115
173
-1
maybe_reset_stats
115
173
-1
set_pot*ential
115
173
-1
tell_stats
115
173
-1
filter_speech
36
173
-1
should_improve
115
173
-1
40
agility
dexterity
endurance
quickness
strength
fatigue
injury
insanity
Melee
unarmed
armed
Shock
Medic
willpower
perception
sight
hearing
smell
Ranged
Lockpick
Dodge
pot
Bladed
Blunt
MechTech
ElecTech
Focus
Bite
Claw
Flail
Brawl
Throwing
Shooting
Whip
Javelin
Trident
Discus
Archery
Sling
should_improve
92
0
66
115
1
0
67
115
1
0
59
115
1
0
72
115
1
0
58
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
38
115
1
0
0
115
1
0
60
115
1
0
60
115
1
0
60
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
100
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
0
115
1
0
1
115
5
5
115
5
1
-1
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
4
19
1
273
1
274
1
275
1
276
1
277
1
278
1
279
1
280
1
281
1
282
1
283
1
284
1
285
1
286
1
287
1
288
1
289
1
290
1
291
2
1
4
2
1
282
1
283
115
1
5
115
5
5
2
1
5
2
1
5
2
1
5
2
1
4
19
2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

115
4
5
115
5
5
115
4
5
115
5
5
115
0
5
115
1
5
36
0
5
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
4
0
115
4
4
0
115
4
1
-1
36
1
5
115
5
5
115
5
0
0
115
4
4
2
2
generic skilled character
2
skilled
115
5
2
A character with great potential as an adventurer.
115
5
4
2
0
15289
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#92
generic combatant

144
115
-1
-1
-1
91
6
-1
118
status result
115
173
-1
current_weapon weapon
115
173
-1
wielding holding weapons_wielded
115
173
-1
attacking dodging
115
173
-1
add_action
115
173
-1
remove_action
115
173
-1
cancel_action
115
173
-1
act
2
165
-1
cleanup
115
165
-1
do_attack do_shoot
115
173
-1
do_defense
115
173
-1
do_flee
115
173
-1
do_dodge
115
173
-1
cleanup_dodge
115
173
-1
receive_damage receive_injury
115
173
-1
dodge
115
93
-2
add_attacker
115
165
-1
remove_attacker
115
165
-1
attack kill stun knockout
115
93
-2
flee
115
93
-2
look_self
115
173
-1
tell_health
115
173
-1
slowness
115
173
-1
wield ready
115
85
-2
sheath*e unwield sling
115
21
-1
exitfunc
115
173
-1
natural_weapon*s
115
173
-1
heal
115
93
-2
death
115
165
-1
actions
115
13
-1
health ht
115
29
-1
cancel
115
29
-1
eq*uipped
115
21
-1
co*ndition sc*ore
115
21
-1
nice
115
173
-1
cleanup_attack
115
173
-1
unconsciousness
115
173
-1
unconscious
115
165
-1
do_revive
115
165
-1
revive
115
29
-1
set_status set_result set_wielding set_natural_weapon set_defense_bonus set_slowness set_nice set_current_weapon set_natural_protection
115
173
-1
defense_bonus
115
173
-1
queue_action
2
173
-1
do_heal
115
165
-1
mod_damage
115
165
-1
quickness
115
173
-1
random_weapon
115
173
-1
absorb_damage
115
165
-1
attempt_action
2
173
-1
is_natural_weapon
115
165
-1
do_wield
115
173
-1
do_unwield
115
173
-1
is_listening
115
173
-1
is_wielding is_readied
115
173
-1
get_afterlife
115
173
-1
afterlife
115
173
-1
birth
115
173
-1
deposit_remains
115
173
-1
is_stat
115
173
-1
stat_quickness
115
173
-1
stat_agility
115
173
-1
stat_dexterity
115
173
-1
alt_unconsciousness alt_death
115
173
-1
birth_effects
115
173
-1
death_effects
115
173
-1
sleep
115
13
-1
wake
115
29
-1
disfunc
115
173
-1
asterisk
115
165
-1
crystal_worth
115
173
-1
death_pending
115
173
-1
maybe_queue_action
2
173
-1
receive_fatigue
115
173
-1
receive_insanity
115
173
-1
wake_up
2
165
-1
queue rush
2
93
-2
attempt_queue
115
173
-1
attempt_queue
115
173
-1
ambush
115
29
-1
hear_event_entrance
115
173
-1
in_combat
115
173
-1
add_defender remove_defender
115
173
-1
guard defend
115
29
-1
do_guard
115
173
-1
clear_corpse
115
173
-1
possible_targets
115
165
-1
my_match_target
115
173
-1
target_match_failed
115
173
-1
my_match_wielding
115
173
-1
wielding_match_failed
115
173
-1
tell_equipped
115
173
-1
is_possible_target
115
173
-1
queue_length
115
173
-1
armour_worn clothing_worn
115
173
-1
resolve_null_target
115
165
-1
do_metabolism
115
173
-1
update_damage_monitors
115
165
-1
do_behave
115
173
-1
unarmed_msg
115
173
-1
forgive
115
29
-1
tell
115
173
-1
init_for_core
115
173
-1
all_attackers
115
173
-1
multiple_attacker_penalty
115
173
-1
penalty
115
173
-1
should_be_moody
115
173
-1
should_suffer_insanity suffer_insanity
115
173
-1
should_suffer_fatigue suffer_fatigue
115
173
-1
do_attack(prefloat)
115
173
-1
do_defense(prefloat)
115
165
-1
attempt_guard
115
173
-1
attempt_parry
115
173
-1
attempt_dodge
115
173
-1
start_defending
115
173
-1
stop_defending
115
173
-1
melee_damage_bonus
115
173
-1
is_mortally_wounded
115
173
-1
update_bleeding
115
173
-1
37
natural_weapon
slowness
status
result
wielding
attacking
dodging
queue
pending_actions
death_msg
odeath_msg
nice
unconscious
defense_bonus
current_weapon
natural_protection
afterlife
last_death_time
corpse
upper_mobility
lower_mobility
total_deaths
last_attacked_by
oabsorbed_hit_msg
total_kills
total_power
dt
last_healed_by
ambushing
defenders
owake_up_msg
sleep_msg
osleep_msg
defending
last_killed_by
blood
bleeding
129
1
151
115
1
0
0
115
1
2

115
1
0
0
115
1
4
0
115
1
4
0
115
1
4
0
115
1
4
0
115
1
4
0
115
1
2

115
5
2

115
5
0
0
115
1
0
0
115
1
0
0
115
1
1
151
115
1
0
0
115
1
1
-1
115
1
0
0
115
1
1
-1
115
1
0
100
115
1
0
100
115
1
0
0
115
1
4
2
1
-1
0
0
115
1
2
%[Dppc] natural armour absorbs all the damage.
115
5
0
0
115
1
0
1
115
1
0
0
115
1
4
3
1
-1
0
0
0
0
115
1
2

115
1
4
0
115
1
2
%N %<wakes> from %p voluntary sleep, rubbing %p eyes and yawning with well-rested satisfaction.
115
5
2
You jump into a deep sleep.  You may use commands prefixed by '@' or type `wake up` to return to consciousness.
115
5
2
%N %<goes> to sleep.  %S %<is> now dead to the world.
115
5
4
0
115
1
4
2
0
0
0
0
115
1
1
206
115
1
0
0
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
1
5
115
5
5
2
1
5
2
1
5
2
1
5
2
1
5
115
4
5
115
5
5
115
4
5
115
5
5
115
0
5
115
1
5
36
0
5
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
5
115
4
5
115
4
5
36
1
5
115
5
5
115
5
0
0
115
4
4
2
2
generic combatant
2
combatant
115
5
5
115
5
4
2
0
89944
0
2085978498
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#93
generic weapon

144
115
-1
-1
-1
5
179
183
22
skill slowness lethality penetration
115
173
-1
announce_swing announce_miss announce_dodge announce_hit announce_knockout announce_kill
115
165
-1
random_hit_location
115
173
-1
critical_miss
115
173
-1
critical_hit
115
173
-1
announce_parry
115
165
-1
attempt_attack attempt_parry
115
173
-1
finished_attack
115
173
-1
combat_messages
115
173
-1
damage
115
173
-1
exertion
115
173
-1
tell_stats
115
173
-1
set_stat
115
173
-1
mod_stat
115
173
-1
init_for_core
2
173
-1
include_with_core
2
173
-1
finished_parry
115
173
-1
attack_mod
115
173
-1
dodge_mod
115
173
-1
parry_with
115
173
-1
parry_against
115
173
-1
melee_damage_bonus
115
173
-1
12
attack_mod
dodge_mod
parry_with
parry_against
damage
slowness
combat_messages
skill
penetration
lethality
exertion
damage_type
54
0
0
115
1
0
0
115
1
0
-5
115
1
0
0
115
1
0
0
115
1
0
0
115
1
1
161
115
1
1
240
115
1
0
50
115
1
0
25
115
1
0
1
115
1
2

115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
4
0
115
4
4
0
115
4
1
-1
36
1
5
115
5
5
115
5
0
0
115
4
4
2
2
generic weapon
2
weapon
115
5
2
A menacing implement of death and dismemberment.
115
5
4
2
0
15821
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#94
generic wieldable weapon

144
115
-1
-1
-1
93
119
204
8
wielded
115
165
-1
look_person_msg
115
173
-1
hands_required
115
173
-1
christen dub entitle
2
109
13
title_msg
115
165
-1
announce_mood
115
173
-1
attempt_wield
115
173
-1
attempt_unwield
115
173
-1
8
wield_msg
owield_msg
sheathe_msg
osheathe_msg
wielded_msg
sheathed_msg
hands_required
omood_idle_msg
62
2

115
5
2
%N %<wields> %p %t.
115
5
2

115
5
2
%N %<sheathes> %p %t.
115
5
2
%S %<is> wielding %p %t.
115
5
2

115
5
0
1
115
1
2
%N %<clutches> %p %t nervously, ready for action.
115
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
1
161
115
1
1
240
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
4
0
115
4
4
0
115
4
1
-1
36
1
5
115
5
5
115
5
0
0
115
4
4
4
2
generic wieldable weapon
2
wielded
2
wieldable weapon
2
weapon
115
5
2
A weapon with a handle; not an extension of yourself.  More than just damage and balance.  Get a good grip and CLOBBER somebody.
115
5
4
2
0
5885
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#95
generic skill

16
115
-1
-1
-1
1
240
147
25
att_bonus_for
115
165
-1
unskilled
115
173
-1
total
115
173
-1
resolve
115
173
-1
improve
115
173
-1
improved_msg macrospar_msg
115
173
-1
should_improve
115
173
-1
tell_stats
115
173
-1
penalty
115
173
-1
disallow_boost_by
115
173
-1
recycle
2
165
-1
initialize
115
173
-1
boost_cost
115
173
-1
average_rank
115
165
-1
compile_census
115
173
-1
raw_rank_for
115
173
-1
current_rank_for
115
173
-1
death_adjustment_for
115
173
-1
moveto
115
173
-1
improved_recently
115
173
-1
record_improvement
115
173
-1
_total_100
115
165
-1
skill_bonus_for
115
165
-1
total_skill_bonus_for
115
173
-1
starting_rank_for
115
173
-1
19
property
attributes
learn
unskilled
improve_range
pot
improved_msg
max_boost_rank
macrospar_msg
boost_cost_pct
census
average_range
wait
mean
trickle_chance
census_interval
base_average
last_boosted
starting_rank
28
2

115
1
4
0
115
5
0
5
115
5
0
0
115
5
4
2
0
-25
0
50
115
5
0
2
115
5
2

115
5
0
-1
115
5
2

115
5
9
1
115
1
4
4
0
0
0
0
0
0
0
0
115
1
4
2
0
100
0
900
115
5
0
3600
115
5
0
1
115
5
0
110
115
5
0
259200
115
5
0
50
115
1
4
0
115
1
0
-1
115
5
0
0
115
4
4
2
2
generic skill
2
skill
115
5
2
Something which you could be using to improve yourself.
115
5
4
2
0
21043
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#96
they

16
2
-1
-1
-1
76
-1
97
1
init_for_core
2
173
-1
0
29
1
235
2
5
5
2
5
5
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
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
1
2
they
2
5
5
2
5
4
2
0
949
0
955495071
100
1
5
2
5
5
2
5
0
0
2
5
5
2
5
5
115
1
#97
someone

16
2
-1
-1
-1
76
-1
-1
1
init_for_core
2
173
-1
0
29
1
150
2
5
5
2
5
5
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
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
1
2
someone
2
5
5
2
5
4
2
0
956
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#98
generic clothing

144
36
-1
-1
-1
5
120
111
47
tease
36
45
-1
wear
36
45
-1
worn_msg
36
165
-1
wear_msg remove_msg dress_msg strip_msg tease_msg oremove_msg otease_msg ostrip_msg odress_msg owear_msg
36
173
-1
rem*ove
36
45
-1
do_remove
36
173
-1
do_wear
36
173
-1
get_clothing_object
36
173
-1
caller_ok
36
173
-1
moveto
36
173
-1
recycle
36
173
-1
worn
36
173
-1
get_message
36
173
-1
is_configurable_by
36
173
-1
get_body_parts
36
173
-1
arrange_clothing
36
173
-1
tuck_in
36
173
-1
acceptable
36
165
-1
where
36
173
-1
visible
36
173
-1
covered_by
36
173
-1
add_to
2
173
-1
remove_from
2
173
-1
@coverage
36
105
-2
carried_msg look_person_msg
36
173
-1
appendage_check
36
173
-1
dress
36
149
3
set_worn_on
36
173
-1
set_name set_aliases set_description set_message
36
173
-1
get_coverage
36
165
-1
is_readable_by
36
173
-1
tell_coverage
36
173
-1
match_body_part
36
173
-1
is_wearable_by
36
173
-1
absorption deflection max_damage
115
173
-1
mod_damage
115
165
-1
absorb_damage
115
173
-1
announce_clang
115
165
-1
set_absorption set_max_damage set_deflection
115
173
-1
tell_stats
115
173
-1
unconditional_remove
36
173
-1
worn_by
36
173
-1
will_reveal
36
173
-1
@reveal
36
157
8
maybe_announce_clang
115
173
-1
maybe_announce_deflect maybe_announce_absorb
115
173
-1
init_for_core
2
173
-1
22
worn
move_task
wear_msg
remove_msg
dress_msg
worn_msg
male_msg
female_msg
strip_msg
tease_msg
body_parts
worn_on
root
absorption
max_damage
owear_msg
oremove_msg
odress_msg
ostrip_msg
otease_msg
revealing
deflection
64
1
-1
36
1
0
0
36
1
2

36
4
2

36
4
2

36
4
2
%N %<is> wearing %[tititle].
36
4
2

36
4
2

36
4
2

36
4
2

36
4
4
7
2
head
2
chest
2
arms
2
hands
2
groin
2
legs
2
feet
36
1
4
0
36
1
1
98
36
5
0
5
115
1
0
10
115
1
2
%N %<puts> on %p %t.
36
4
2
%N %<takes> off %p %t.
36
4
2
%N %<dresses/dress> %d in %[tiname].
36
4
2
%N %<strips> %[tdname] from %d.
36
4
2
%N %<runs> %p hands seductively over %p %t.
36
4
0
0
36
1
0
1
115
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
generic clothing
2
clothing
36
5
2
A very nice, albeit nondescript, garment.  Many patches and loose stitches give it a well-worn look.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
4
1
2
Type `help clothing` for detailed information on how to customize your clothing.
36
5
5
115
1
#99
generic door

144
36
-1
-1
-1
131
-1
-1
23
sound_permeability transparency
36
173
-1
closed_sound_permeability closed_transparency
36
173
-1
is_opened is_locked
36
173
-1
set_opened set_locked
36
165
-1
is_key
36
173
-1
is_lockable_by is_unlockable_by
36
173
-1
user_has_key
36
173
-1
knock_msg oknock_msg knock_sound_msg already_opened_msg locked_msg olocked_msg open_msg oopen_msg already_locked_msg lock_msg olock_msg cannot_lock_msg bad_key already_unlocked_msg unlock_msg ounlock_msg closed_msg oclosed_msg opened_append_msg closed_append_msg not_lockable_msg already_closed_msg close_msg oclose_msg cannot_unlock_msg bad_key_msg
36
173
-1
knock
36
157
4
open
36
45
-1
close
36
45
-1
lock
36
109
-2
unlock
36
109
-2
invoke
36
173
-1
look_self
36
173
-1
invoke_for_walker
36
165
-1
is_unlocked_for
36
165
-1
pick_attempt_action
115
173
-1
pick_failure_action
115
173
-1
pick_success_action
115
173
-1
set_pickable
115
173
-1
@add-key
36
157
1
@remove-key
36
157
5
39
keys
lockable
opened
locked
closed_sound_permeability
closed_transparency
knock_msg
oknock_msg
knock_sound_msg
already_opened_msg
locked_msg
olocked_msg
open_msg
oopen_msg
already_locked_msg
lock_msg
olock_msg
cannot_lock_msg
bad_key
already_unlocked_msg
unlock_msg
ounlock_msg
cannot_unlock_msg
closed_msg
oclosed_msg
opened_append_msg
closed_append_msg
not_lockable_msg
already_closed_msg
close_msg
oclose_msg
open_motion_msg
close_motion_msg
pickable
pick_fail_msg
opick_fail_msg
pick_succeed_msg
opick_succeed_msg
bad_key_msg
82
4
0
36
1
0
0
36
5
0
1
36
1
0
0
36
1
0
0
36
5
0
0
36
5
2

36
5
2
%N %<knocks> on %[tdname].
36
5
2
KNOCK KNOCK
36
5
2
%[Tdnamec] %<is> already open.
36
5
2

36
5
2
%N %<pulls> in vain on %[tdname,locked].
36
5
2

36
5
2
%N %<opens> %[tdname].
36
5
2
%[tdnamec] %<is> already locked.
36
5
2

36
5
2
%N %<locks> %[tdname].
36
5
2
You don't have any way to lock it.
36
5
2
%I doesn't seem to work.
36
5
2
%[tdnamec] %<is> already unlocked.
36
5
2

36
5
2
%N %<unlocks> %[tdname].
36
5
2
You don't have any way to unlock it.
36
5
2

36
5
2
%N %<smashes> %p face into %[tdname].  Maybe %s should try opening it first?
36
5
2
It is open.
36
5
2
It is closed.
36
5
2
You can't lock or unlock that.
36
5
2
%[tdnamec] %<is> already closed.
36
5
2

36
5
2
%N %<closes> %[tdname].
36
5
2
%N %<opens> %[idname].
36
5
2
%N %<closes> %[idname].
36
5
0
-50
115
1
2

36
5
2
%N %<fails>.
36
5
2

36
5
2
A soft click signals success.
36
5
2
%[idnamec] doesn't seem to fit in %[tdname].
36
5
4
0
115
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
2
%N %<arrives> through %[tdname,open].
36
5
2
%N %<exits> through %[tdname,open].
36
5
5
36
5
5
2
1
5
36
5
5
36
5
5
36
1
5
36
5
5
36
5
5
36
5
5
36
5
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
generic door
2
door
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
0
1
36
5
4
61
2
 
2
To make an exit into a door, @chparent it and its other side to $door.  Verbs and customization are detailed below.
2
 
2
knock on door
2
open/close door
2
unlock/lock door [with/using key]
2
 
2
        All user-verbs are rather self explanatory.
2
 
2
.lockable should be set true if the door has a lock.
2
 
2
.keys is a list of objects (or parents) that will open the door.  If
2
       the door is lockable and there are no keys, then anyone can
2
       lock or unlock it.  (a simple latch, bolt, etc)
2
 
2
.closed_sound_permeability
2
.closed_transparency
2
 
2
        These are the values for the permeability and transparency
2
        of the door when it is closed.  They are by default, and
2
        will probably always be 0.  Exceptions would be glass doors,
2
        curtains, beaded partitions, etc.
2
 
2
@knock/@oknock
2
 
2
        Messages displayed on knocker's side when the door is knocked upon.
2
        If you have no special personal info to tell the player, all messages
2
        on the door (and most messages on the MOO) require you to set only
2
        the o-messages, being sure to properly quote for pronoun-sub.
2
 
2
@knock_sound
2
 
2
        Sound heard on opposite side when the door is knocked upon.
2
 
2
        The following are for when one attempts to do something already done.
2
 
2
@already_opened  @already_locked  @already_unlocked  @already_closed
2
 
2
@locked/@olocked  -- Trying to open a locked door.
2
@open/@oopen      -- Opening.
2
@close/@oclose    -- Closing.
2
 
2
@lock/@olock      -- Locking.
2
@unlock/@ounlock  -- Unlocking.
2
@not_lockable     -- Trying to lock/unlock an unlockable door.
2
@cannot_lock
2
@cannot_unlock    -- Trying to lock/unlock a door without a proper key.
2
@bad_key          -- Trying to lock/unlock a door with the wrong key.
2
 
2
@closed/@oclosed  -- Walking into a closed door.
2
 
2
@opened_append
2
@closed_append    -- Appended to the look of an opened/closed door.
2
 
2
@open_motion
2
@close_motion     -- Motions transmitted via event to the opposite side
2
                     when the door is opened or closed.
2
 
2
If you've any questions, just ask your scratchy friend Quinn.
2
 
2
                                                -ME
36
5
5
115
1
#100
Quota

19
100
-1
-1
-1
295
-1
101
0
0
185
4
0
36
1
0
1
100
5
0
0
100
5
5
100
5
0
48
100
5
0
0
2
1
4
0
2
0
5
100
5
5
36
0
5
36
1
5
2
0
5
100
5
5
2
0
5
2
1
0
0
100
5
5
100
5
5
36
1
5
36
1
4
0
36
0
5
36
1
5
100
5
4
2
1
34
1
100
2
1
5
100
4
4
0
2
0
5
2
0
4
0
100
4
5
100
4
5
100
5
5
100
5
5
100
5
5
100
5
5
2
1
4
2
0
0
0
0
100
4
4
0
100
4
5
2
1
0
12
100
0
5
2
0
5
100
5
5
2
1
4
0
115
1
5
100
5
1
159
100
5
4
4
0
10000000
0
29805
0
955412150
0
0
100
1
5
2
0
5
100
5
5
2
1
5
36
1
5
100
5
5
115
1
4
0
36
0
5
100
5
5
115
1
5
115
1
4
3
0
1219694
4
2
1
4744
1
66
4
2
4
1
4
2
2
hide_loc
0
0
4
1
2
thisonly
36
0
5
115
1
5
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
100
5
5
100
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
100
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
100
5
5
100
5
5
100
5
5
115
1
5
115
1
1
206
115
1
5
115
1
0
70
115
1
0
68
115
1
0
74
115
1
0
63
115
1
0
57
115
1
0
0
115
1
0
0
115
1
0
0
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
100
5
5
100
5
1
61
100
5
5
100
5
5
36
1
5
100
5
5
100
5
5
100
5
5
100
5
5
2
1
5
115
1
5
100
5
5
2
1
5
2
1
5
2
1
5
2
1
5
100
4
5
100
5
5
100
4
5
100
5
5
115
0
5
115
1
5
36
0
5
115
1
1
2789
100
5
5
100
5
5
100
5
5
100
5
5
100
5
5
100
5
5
100
5
5
100
5
5
100
5
5
100
5
5
36
1
5
100
5
5
100
5
5
115
1
5
115
1
5
100
4
5
100
4
5
36
1
5
100
5
5
100
5
5
100
4
4
1
2
Quota
2
1
2
A bloated, greedy, capitalistic bastard which controls any and all resources available in this virtual Reality.
100
5
4
2
0
3772
0
955412150
100
1
5
100
5
5
100
5
5
100
5
5
100
5
5
115
1
#101
Scrooge

19
101
-1
-1
-1
295
-1
115
1
trusts
2
173
-1
0
185
4
0
36
1
0
1
101
5
0
0
101
5
5
101
5
0
48
101
5
0
0
2
1
4
0
2
0
5
101
5
5
36
0
5
36
1
5
2
0
5
101
5
5
2
0
5
2
1
0
0
101
5
5
101
5
5
36
1
5
36
1
4
0
36
0
5
36
1
5
101
5
4
2
1
101
1
111
2
1
5
101
4
4
0
2
0
5
2
0
4
0
101
4
5
101
4
5
101
5
5
101
5
5
101
5
5
101
5
5
2
1
4
2
0
0
0
0
101
4
4
0
101
4
5
2
1
0
-8926
100
0
5
2
0
5
101
5
5
2
1
4
0
115
1
5
101
5
1
159
101
5
4
4
0
10000000
0
33633
0
955412150
0
0
100
1
2
@measure new
2
0
5
101
5
5
2
1
5
36
1
5
101
5
5
115
1
4
0
36
0
5
101
5
5
115
1
5
115
1
4
3
0
1219694
4
2
1
4744
1
66
4
2
4
1
4
2
2
hide_loc
0
0
4
1
2
thisonly
36
0
5
115
1
5
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
101
5
5
101
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
101
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
101
5
5
101
5
5
101
5
5
115
1
5
115
1
1
206
115
1
5
115
1
0
56
115
1
0
63
115
1
0
61
115
1
0
49
115
1
0
64
115
1
0
0
115
1
0
0
115
1
0
0
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
101
5
5
101
5
1
61
101
5
5
101
5
5
36
1
5
101
5
5
101
5
5
101
5
5
101
5
5
2
1
5
115
1
5
101
5
5
2
1
5
2
1
5
2
1
5
2
1
5
101
4
5
101
5
5
101
4
5
101
5
5
115
0
5
115
1
5
36
0
5
115
1
1
3886
101
5
5
101
5
5
101
5
5
101
5
5
101
5
5
101
5
5
101
5
5
101
5
5
101
5
5
101
5
5
36
1
5
101
5
5
101
5
5
115
1
0
4575
115
1
5
101
4
5
101
4
5
36
1
5
101
5
5
101
5
5
101
4
4
2
2
Scrooge
2
Banker
2
1
2
He with all the dough.  A bit more like Scrooge in the morning than BAH HUMBUG, considering most of this man's coinage is distrubuted amongst the peoples.
101
5
4
2
0
14114
0
955412150
100
1
5
101
5
5
101
5
5
101
5
5
101
5
5
115
1
#102
English Language Utilities

16
2
-1
-1
-1
83
-1
112
5
indefinite_article iart*icle indef_art
2
173
-1
is_vowel_exception
2
173
-1
is_nonvowel_exception
2
173
-1
plural*ize plural*ise
2
165
-1
degenericize
36
173
-1
2
vowel_exceptions
nonvowel_exceptions
11
2
^%(usu%|uke%|uvu%|use%|UPI%|unit%|univ%|unic%|uniq%|unix%|eur%|uu%|once%)
2
1
2
^%(historic%|ydu%|honor%|honest%|habitual%)
2
1
0
0
2
4
4
1
2
English Language Utilities
2
5
2
An object used when dealing with the peculiarities of the engish language, including: conjugation, pluralization, indefinite article use, etc.
2
5
4
2
0
3559
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#103
Almost HumAn Bastard

144
2
-1
-1
-1
88
-1
-1
24
add_reactor
2
165
-1
remove_reactor
2
173
-1
reactors
2
173
-1
my_call_verb
2
173
-1
my_eval
2
173
-1
my_eval_d
2
173
-1
is_addressed_text
2
165
-1
hear_event_speech
2
165
-1
dispatch_event_*
2
173
-1
@add-reactor
2
149
1
@remove-reactor
2
149
5
my_huh
2
165
-1
is_controllable_by
115
173
-1
update_regexp_aliases
115
173
-1
set_aliases
115
173
-1
pending todo
2
141
11
features
115
173
-1
death_effects
115
165
-1
set_features
115
173
-1
interpret_reactor_result
2
173
-1
do_pause
115
173
-1
expire_pending_reactions
115
165
-1
add_pending_reactions
115
173
-1
cleanup_attack
115
173
-1
5
reactors
pending_reactions
todo_task
regexp_aliases
features
149
4
0
115
1
4
0
115
0
0
0
115
1
2

115
1
4
0
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
115
1
1
160
2
5
0
0
115
1
0
0
115
1
0
0
115
1
4
0
115
1
5
2
5
5
115
1
5
115
1
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
2
5
5
115
1
5
115
1
1
206
115
1
5
115
1
0
64
115
1
0
73
115
1
0
63
115
1
0
67
115
1
0
62
115
1
0
0
115
1
0
0
115
1
0
0
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
0
64
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
115
1
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
4
5
2
5
5
2
4
5
2
5
5
115
0
5
115
1
5
36
0
5
115
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
5
2
4
5
2
4
5
36
1
5
2
5
5
2
5
5
2
4
4
2
2
Almost HumAn Bastard
2
AHaB
2
5
2
A spry old fella with a twinkle in his eye.  Many secrets does he have to share, and many more will he not reveal.
2
5
4
2
0
20198
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#104
AHaB Reactor

144
2
-1
-1
-1
1
156
110
14
match_args
2
165
-1
match_element
2
165
-1
reactions
2
173
-1
hear_event_*
2
165
-1
reaction_priority
2
173
-1
reaction_object
2
165
-1
reaction_method
2
173
-1
reaction_args
2
165
-1
reaction_packet
2
173
-1
is_text_reaction
2
173
-1
substitute_match
2
165
-1
add_ok
2
173
-1
strsub_list
115
173
-1
parse_brackets
115
165
-1
2
reactions
debug
11
4
0
2
5
0
0
2
5
0
0
2
4
4
4
2
AHaB Reactor
2
brain
2
lobe
2
reactor
2
5
2
A pulsing lobe of some detach-ed brain.  It wiggles for you, wet and flopping with electrical impulses.
2
5
4
2
0
13888
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#105
Value Editor

16
36
-1
-1
-1
47
-1
-1
5
note_text
2
165
-1
set_note_text
2
165
-1
text_from_value
36
165
-1
value_from_text
36
165
-1
display
2
9
-1
0
53
4
0
36
1
4
0
36
5
4
0
36
1
4
0
36
1
5
36
5
1
43
36
5
5
36
5
5
36
5
0
0
36
1
5
36
5
5
36
5
5
36
1
2
A whirlwind of curly braces and hash signs bounces into the room, sweeps %n off %p feet, and carries %o away.
36
5
2
A whirlwind of curly braces and hash signs descends suddenly in the room, whipping up commas from the local descriptions.  %N %<is> dropped from the twister, which disperses soon after.
36
5
4
3
2
Partially edited text will be here when you get back.
2
To return, give the `@value-edit' 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
5
36
5
5
36
5
5
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 values]
36
5
0
0
36
5
5
36
5
5
36
5
5
36
5
4
0
36
5
0
1
36
5
5
36
4
5
36
5
5
36
5
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
0
0
113
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
Value Editor
2
vE
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#106
generic food

144
2
-1
-1
-1
108
-1
107
1
eat gobble
2
45
-1
0
59
5
2
5
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
%N %<eats> some of %p %t.  $left
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
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
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
3
2
generic food
2
gf
2
food
2
5
5
2
5
4
2
0
1444
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#107
generic beverage

144
2
-1
-1
-1
108
-1
-1
4
drink gulp quaff
36
45
-1
chug swig belt down guzzle
36
45
-1
sip taste
36
45
-1
finish
36
45
-1
0
59
5
2
5
5
2
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
2
%N %<drinks> some of %p %t.  $left
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
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
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
3
2
generic beverage
2
gb
2
drink
2
5
5
2
5
4
2
0
2540
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#108
generic consumable

144
2
-1
-1
-1
5
106
173
21
relative_volume
2
173
-1
left_msg
2
173
-1
effect_msg oeffect_msg consume_msg oconsume_msg finish_msg ofinish_msg empty_msg oempty_msg
2
173
-1
consume
36
45
-1
empty_action consume_action
2
173
-1
finish_action
2
173
-1
destroy
2
173
-1
_consume
2
173
-1
get_message
2
173
-1
set_remaining_portions
2
173
-1
@set-portions
2
105
1
description
2
173
-1
finished_description
2
173
-1
finished_aliases not_finished_aliases
2
173
-1
description
2
173
-1
set_aliases
2
173
-1
@set-finished-alias*es
2
105
1
_replenish
2
173
-1
@set-finished-desc*ription
2
105
1
@replenish
2
41
-1
do_consume
36
45
-1
17
total_portions
remaining_portions
incubation_period
effect_interval
effect_cycles
destroy_when_finished
effect_msg
oeffect_msg
consume_msg
oconsume_msg
finish_msg
ofinish_msg
empty_msg
oempty_msg
finished_description
finished_aliases
not_finished_aliases
59
0
1
2
5
0
1
2
1
0
-1
2
5
0
60
2
5
0
0
2
5
0
0
2
5
2

2
5
2

2
5
2

2
5
2
%N %<consumes> some of %p %t.  $left
2
5
2

2
5
2
%N %<finishes> up %p %t.  *burp*
2
5
2
There isn't any left.
2
5
2

2
5
2
There is nothing but a crumpled, nondescript consumable container remaining.
2
5
4
0
2
5
4
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
1
5
2
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
generic consumable
2
consumable
2
5
2
A thing that can be consumed unto oblivion.  It may be addictive, it may be medicinal, and it may be a delicious and nutritious snack!  Eat me.
2
5
4
2
0
14331
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#109
generic body area

144
115
-1
-1
-1
1
158
95
12
damage_mod
115
173
-1
mod_damage
115
165
-1
max_damage
115
165
-1
absorb_damage
115
173
-1
called_shot_mod
115
173
-1
is_fatal_strike
115
173
-1
is_knockout_strike
115
173
-1
alt_unconsciousness
115
173
-1
alt_death
115
173
-1
tell_stats
115
173
-1
bleeding_mod
115
173
-1
mod_bleeding
115
173
-1
11
damage_mod
max_damage
called_shot_mod
percent_of_body_area
fatality_chance
knockout_chance
affects_upper_mobility
affects_lower_mobility
absorption
deflection
bleeding_mod
20
0
100
115
5
0
999999999
115
5
0
1
115
5
0
5
115
5
0
100
115
5
0
100
115
5
0
0
115
5
0
0
115
5
0
0
115
1
0
0
115
1
9
1
115
5
0
0
115
4
4
2
2
generic body area
2
gba
115
5
2
It's part of you.  Be careful, cuz it might get hurt.
115
5
4
2
0
13488
0
955495071
100
1
5
115
5
5
115
5
5
115
5
4
68
2
Body areas are a very basic and important element of the combat and physical trauma system.  They control the way damage affects certain areas of the body, enriching combat with called shots and critical strikes.
2

2
When creating a set of body areas for your creature, try to keep a generic use in mind.  For example, create a 'generic quadruped area' and children instead of a bastard group of dog parts.  Future monster makers will thank you for it.
2

2
Take the time to set all the important stats of your body areas.  Do a:
2

2
        @d $body_area.
2

2
To get a neat view of all the properties and their defaults.  And, of course,
2
 read all this shit below.  ;)
2

2

2
     -- Overview of Properties and Verbs on the Generic Body Area --
2

2

2
        .percent_of_body_area
2

2
This property should be the first set on your area.  It determines the chance of the area being randomly chosen, and is sometimes use when determining a called_shot_mod when none has been specified.
2

2
        :absorb_damage(damage, victim, weapon, attacker)
2

2
This is the only external $body_area verb, called when a shot to this area is taken by victim.  Returns the actual damage from the hit, after modifying it with the auxiliary verbs below.  It may print messages.
2

2
        :mod_damage(damage, victim, weapon, attacker)
2

2
This returns damage modified according to below.
2

2
        :damage_mod(damage, victim, weapon, attacker)
2
        .damage_mod
2

2
The 'damage_mod' property is a percentage of damage received that is actually delivered to the body.  Its default is 100%.  It should be raised, for example, on head or groin areas.
2

2
        :max_damage(damage, victim, weapon, attacker)
2
        .max_damage
2

2
The 'max_damage' property is a constant: the maximum amount of damage this body area can sustain.  A finger, for example, will probably not take more than 5 hits damage.  The default is $maxint.
2

2
        :called_shot_mod(damage, victim, weapon, attacker)
2
        .called_shot_mod
2

2
The 'called_shot_mod' is a modifer to the attack roll of someone who aims for this body area specifically (via "attack Ed's head", etc).
2

2
This should be appropriately and carefully balanced against the damage mods and fatality chances of the area.  Don't make a vulnerable spot easy to hit.
2

2
A negative number makes it harder to hit, positive easier, as is the standard for all modifier numbers.  The range should be [-100...100].
2

2
        :alt_unconsciousness(victim, damage, attacker)
2
        :alt_death(victim, damage, attacker)
2

2
These verbs are called on the area where a fatal or knockout strike has been received, to give that area a say in whether the strike actually causes death or unconsciousness.  See the properties and verbs below.
2

2
Return: 0 if death or unconsciousness should take place as normal.
2
        1 if the death was alternately handled (as per one of below).
2
       -1 if the damage should be applied without the fatal effects.
2

2
        :is_fatal_strike()      :is_knockout_strike()
2
        .fatality_chance        .knockout_chance
2

2
These two properties (and their corresponding verbs) determine whether a normally fatal or knockout strike should actually cause death or unconsciousness.
2

2
Note that the defaults are 100 (percent) chances.  These should be modified for non-vital areas such as hands and feet, which will very rarely result in death.
2

2
        .affects_upper_mobility
2
        .affects_lower_mobility
2

2
These numbers are the percentages of deadly/knockout damage that will be instead be substracted from the victim's upper or lower mobility.
2

2
For example:  If my left thigh has an affects_upper_mobility of 50 (and it does), and I receive 120 points of damage that were normally to be fatal, I would suffer a -60% crippling of my lower extremeties.
115
5
5
115
1
#110
generic trust db

16
2
-1
-1
-1
1
217
114
9
get_trustees
2
165
-1
trusts
2
165
-1
add_trusted_object
2
165
-1
remove_trusted_object
2
165
-1
is_virtual_action
2
173
-1
do_while_unconscious
2
173
-1
is_critical_action
2
173
-1
include_with_core
2
173
-1
init_for_core
2
173
-1
2
virtual
critical_action
11
0
1
2
5
0
0
2
1
0
0
2
4
4
2
2
generic trust db
2
trust db
2
5
2
Something someone might trust you to do.
2
5
4
2
0
5214
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#111
generic collective object

16
101
-1
-1
-1
5
140
108
28
set_amount
101
173
-1
description
101
173
-1
set_cname set_cdesc
101
173
-1
set_cregex
101
165
-1
matches
101
165
-1
cregex
101
173
-1
cname cdesc
101
173
-1
name
101
165
-1
amount_from_string
101
165
-1
moveto_transfer
101
173
-1
find_like_collective
101
165
-1
transfer
101
173
-1
moveto
101
173
-1
drop_failed_msg odrop_failed_msg take_failed_msg otake_failed_msg
101
173
-1
give_failed_msg ogive_failed_msg
101
173
-1
give_succeeded_msg ogive_succeeded_msg
101
173
-1
put_refused_msg remove_refused_msg
101
165
-1
caliases
2
165
-1
_move
2
173
-1
spawn
2
173
-1
destroy
101
173
-1
in_transit
101
173
-1
was_moved
101
173
-1
init_for_core
2
173
-1
do_throw
36
173
-1
_csubs
101
173
-1
parse_vague_amount
101
165
-1
set_aliases
101
173
-1
11
amount
what
cname
cdesc
cregex
pending
root
give_failed_msg
ogive_failed_msg
give_succeeded_msg
ogive_succeeded_msg
53
0
0
101
1
2
thing
101
5
2
$amount $what
101
1
2
A pile of $amount $what.
101
1
2
%(collective object%)
101
1
0
0
101
1
1
111
101
5
2
%d doesn't want to go.
101
5
2

101
5
2
You give %d to %i.
101
5
2
%N %<gives> %d to %i.
101
5
5
101
5
5
101
5
5
101
5
5
101
5
5
101
5
5
101
5
5
101
5
5
101
5
5
2
1
5
101
5
5
101
5
5
101
5
5
101
5
5
36
1
5
101
5
5
101
5
5
101
5
5
101
5
5
101
5
5
101
5
5
101
5
5
101
5
5
101
5
5
36
1
5
101
5
5
101
5
5
115
1
5
115
1
4
0
101
4
4
0
101
4
1
-1
36
1
5
101
5
5
101
5
0
0
101
4
4
2
2
generic collective object
2
collective object
101
5
5
101
5
4
2
0
19519
0
955495071
100
1
5
101
5
5
101
5
0
0
101
5
4
23
2
Collectives take a little unobvious work to get going:
2

2
        @set your_collective.what to "thing"
2

2
Where "thing" is the singular of whatever it is your collective is a collection.  For example: "coin", "rock", or "frog".
2

2
        @set your_collective.root to your_collective
2

2
You _must_ set your generic collective's root to itself.  Otherwise, when new pieces are spawned they will parent to $collective, and NOT your generic parent.
2

2
Don't @create a kid of your collective to get it into circulation.  Instead, use the following method:
2

2
        ;#your_collective:spawn(N)
2

2
Where N is some sufficiently large number.  Use that as your "pile" and distribute from that pile with:
2

2
        ;#pile:transfer(N, #recipient)
2

2
Or just by handing it to the recipient.
2

2
That's about it.  If you have any further questions or difficulties in getting your collective started, write me (Quinn) and I'll try and edit this file to better explain it.
2

2
-Quinn
101
5
5
115
1
#112
Core Utilities

16
2
-1
-1
-1
83
-1
117
13
all_globals
2
165
-1
is_global*_object
2
165
-1
dbref_str*ing
36
173
-1
mcd_mark_core
2
173
-1
mcd_kill_tasks
2
173
-1
mcd_strip_god
2
173
-1
mcd_prepare_doomed
2
173
-1
mcd_reap_doomed
2
173
-1
mcd_renumber
2
173
-1
mcd_initialize_core
2
173
-1
mcd_finalize
2
173
-1
mcd_validate_owners
2
173
-1
install_bare_init_for_core
2
173
-1
0
9
0
0
2
4
4
1
2
Core Utilities
2
5
2
An object to contain methods which deal with global dbrefs and corification of objects, created mainly to take the burden off $sys.
2
5
4
2
0
10091
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#113
Geomancer

19
113
-1
-1
-1
295
-1
125
0
0
185
4
0
36
1
0
1
113
5
0
0
113
5
5
113
5
0
48
113
5
0
0
2
1
4
0
2
0
5
113
5
5
36
0
5
36
1
5
2
0
5
113
5
5
2
0
5
2
1
0
0
113
5
5
113
5
5
36
1
5
36
1
4
0
36
0
5
36
1
5
113
5
4
3
1
113
1
141
1
142
2
1
5
113
4
4
0
2
0
5
2
0
4
0
113
4
5
113
4
5
113
5
5
113
5
5
113
5
5
113
5
5
2
1
4
2
0
0
0
0
113
4
4
0
113
4
5
2
1
0
-9216
100
0
5
2
0
5
113
5
5
2
1
4
0
115
1
5
113
5
1
159
113
5
4
4
0
10000000
0
60852
0
955412151
0
0
100
1
5
2
0
5
113
5
5
2
1
5
36
1
5
113
5
5
115
1
4
0
36
0
5
113
5
5
115
1
5
115
1
4
3
0
1219694
4
2
1
4744
1
66
4
2
4
1
4
2
2
hide_loc
0
0
4
1
2
thisonly
36
0
5
115
1
5
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
113
5
5
113
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
113
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
113
5
5
113
5
5
113
5
5
115
1
5
115
1
1
206
115
1
5
115
1
0
67
115
1
0
71
115
1
0
69
115
1
0
55
115
1
0
57
115
1
0
0
115
1
0
0
115
1
0
0
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
0
64
115
1
5
115
1
0
64
115
1
0
60
115
1
0
64
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
113
5
5
113
5
1
61
113
5
5
113
5
5
36
1
5
113
5
5
113
5
5
113
5
5
113
5
5
2
1
5
115
1
5
113
5
5
2
1
5
2
1
5
2
1
5
2
1
5
113
4
5
113
5
5
113
4
5
113
5
5
115
0
5
115
1
5
36
0
5
115
1
5
113
5
5
113
5
5
113
5
5
113
5
5
113
5
5
113
5
5
113
5
5
113
5
5
113
5
5
113
5
5
36
1
5
113
5
5
113
5
5
115
1
5
115
1
5
113
4
5
113
4
5
36
1
5
113
5
5
113
5
5
113
4
4
1
2
Geomancer
2
1
5
113
5
4
2
0
12863
0
955412151
100
1
5
113
5
5
113
5
5
113
5
5
113
5
5
115
1
#114
generic integrated description object

16
2
-1
-1
-1
1
137
118
11
integration_separation_msg
2
173
-1
integrating_contents
2
173
-1
integrators
2
173
-1
integration_ok
2
173
-1
tell_description
2
173
-1
look_self
2
165
-1
get_integration_msg
2
165
-1
integrated_description
2
173
-1
announce_mood
36
165
-1
tell_title
36
173
-1
choose_mood
36
173
-1
2
integration_style
mood
11
0
4
2
5
4
0
2
5
0
0
2
4
4
3
2
multiple integration room
2
integrator
2
mir
2
5
2
An object which integrates its contents into its description.  See `help $integrated' for more information.
2
5
4
2
0
7092
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#115
GameMaster

19
115
-1
-1
-1
295
-1
113
0
0
185
4
0
36
1
0
1
115
5
0
0
115
5
5
115
5
0
48
115
5
0
0
2
1
4
0
2
0
5
115
5
5
36
0
5
36
1
5
2
0
5
115
5
5
2
0
5
2
1
0
0
115
5
5
115
5
5
36
1
5
36
1
4
0
36
0
5
36
1
5
115
5
4
80
1
87
1
88
1
91
1
92
1
93
1
94
1
95
1
109
1
115
1
122
1
135
1
140
1
147
1
149
1
151
1
155
1
157
1
158
1
173
1
175
1
188
1
190
1
191
1
194
1
195
1
199
1
200
1
201
1
204
1
206
1
209
1
228
1
240
1
241
1
242
1
243
1
244
1
245
1
246
1
247
1
248
1
249
1
250
1
251
1
252
1
253
1
254
1
255
1
256
1
257
1
258
1
259
1
260
1
261
1
262
1
263
1
264
1
266
1
273
1
274
1
275
1
276
1
277
1
278
1
279
1
280
1
281
1
282
1
283
1
284
1
285
1
286
1
287
1
288
1
289
1
290
1
291
1
292
1
293
1
294
2
1
5
115
4
4
0
2
0
5
2
0
4
0
115
4
5
115
4
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
4
2
0
0
0
0
115
4
4
0
115
4
5
2
1
0
99950
100
0
5
2
0
5
115
5
5
2
1
4
0
115
1
5
115
5
1
159
115
5
4
4
0
10000000
0
406995
0
955412154
0
0
100
1
5
2
0
5
115
5
5
2
1
5
36
1
5
115
5
5
115
1
4
0
36
0
5
115
5
5
115
1
5
115
1
4
3
0
1219694
4
2
1
4744
1
66
4
2
4
1
4
2
2
hide_loc
0
0
4
1
2
thisonly
36
0
5
115
1
5
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
5
5
115
5
5
115
1
5
115
1
1
206
115
1
5
115
1
0
65
115
1
0
48
115
1
0
71
115
1
0
72
115
1
0
61
115
1
0
0
115
1
0
0
115
1
0
0
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
0
59
115
1
5
115
1
0
60
115
1
0
80
115
1
0
76
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
5
1
61
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
1
5
115
5
5
2
1
5
2
1
5
2
1
5
2
1
5
115
4
5
115
5
5
115
4
5
115
5
5
115
0
5
115
1
4
3
4
2
1
1991
4
1
1
657
4
2
1
1433
4
1
1
657
4
2
1
1434
4
1
1
657
36
0
5
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
0
20
115
1
5
115
4
5
115
4
5
36
1
5
115
5
5
115
5
5
115
4
4
6
2
GameMaster
2
GM
2
executioner_bird
2
osbornn
2
nosnorb
2
bronson
2
1
2
Lord supreme of all the automated gaming mechanics.  He holds in his hands the stuff virtual dreams are made of.
115
5
4
2
0
24840
0
955412154
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#116
Build Options

16
2
-1
-1
-1
67
-1
75
6
parse_room parse_exit parse_zone
2
173
-1
show_room show_exit show_zone
2
173
-1
check_object_flags
2
173
-1
parse_object_flags
2
173
-1
show_object_flags
2
173
-1
init_for_core
2
173
-1
8
type_room
type_exit
type_zone
default_zone
default_room
default_exit
default_object_flags
show_bi_create
22
4
2
0
1
0
0
2
5
4
2
0
1
0
0
2
5
4
2
0
1
0
0
2
5
1
171
2
5
1
132
2
5
1
131
2
5
2
R
2
5
4
2
2
Use in-database methods to create objects.  (PREFERRED)
2
Use the builtin create() function to create objects.  (DISCOURAGED)
36
1
4
5
2
exit
2
room
2
zone
2
object_flags
2
bi_create
36
1
2
!exit!room!zone!object_flags!bi_create!
36
1
4
0
36
1
5
2
5
2
building_options
2
5
0
0
2
4
4
3
2
Build Options
2
Building Options
2
Builder Options
2
5
5
2
5
4
2
0
4337
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#117
emulation utilities

16
2
-1
-1
-1
83
-1
123
8
do_ANSI_test
2
173
-1
chr
2
173
-1
get_HAG*_char entity
2
173
-1
strip_special
2
173
-1
_length
36
173
-1
tokenize
36
173
-1
entity_code
2
173
-1
chr_altpage
2
173
-1
19
escape
ANSI_Printables
ASCII_Equivalents
ANSI_Tokens
ANSI_Translations
ASCII_Translations
Token_Names
HAG_names
beep
ANSI_Regex_Tokens
ANSI_Prefix
ANSI_Postfix
ANSI_Line_Prefix
default_charset
entity_codes_IBM
entity_codes_ISO
entity_names
available_charsets
entity_codes_7bit
28
2

2
0
2
                                !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  
2
0
4
255
2

2

2

2

2

2

2

2

2

2

2

2

2

2
o/~
2
*
2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2
!
2
"
2
#
2
$
2
%
2
&
2
'
2
(
2
)
2
*
2
+
2
,
2
-
2
.
2

2
0
2
1
2
2
2
3
2
4
2
5
2
6
2
7
2
8
2
9
2
:
2
;
2
<
2
=
2
>
2
?
2
@
2
A
2
B
2
C
2
D
2
E
2
F
2
G
2
H
2
I
2
J
2
K
2
L
2
M
2
N
2
O
2
P
2
Q
2
R
2
S
2
T
2
U
2
V
2
W
2
X
2
Y
2
Z
2
[
2
\
2
]
2
^
2
_
2
`
2
a
2
b
2
c
2
d
2
e
2
f
2
g
2
h
2
i
2
j
2
k
2
l
2
m
2
n
2
o
2
p
2
q
2
r
2
s
2
t
2
u
2
v
2
w
2
x
2
y
2
z
2
{
2
|
2
}
2

2

2
_C
2
:u
2
e'
2
^a
2
:a
2
`a
2
0a
2
_c
2
^e
2
:e
2
`e
2
:i
2
^i
2
`i
2
:A
2
0A
2
^E
2
ae
2
AE
2
^o
2
:o
2
`o
2
^u
2
`u
2
:y
2
:O
2
:U
2
 cents
2
 pounds
2
 yen
2

2
f
2
a'
2
i'
2
o'
2
u'
2
~n
2
~N
2

2

2
!?
2

2

2
1/2
2
1/4
2
i
2

2

2

2

2

2
|
2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2
-
2

2

2

2

2

2

2

2

2
=
2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2

2
B
2

2

2
E
2

2

2

2

2
0
2

2

2

2

2
E
2

2

2

2
>=
2
<=
2

2

2
%
2

2
o
2

2

2

2

2
^2
2

2

2
1
4
11
2
!
2
*
2
%
2
R
2
G
2
Y
2
B
2
M
2
C
2
W
2
0
2
1
4
11
2
[1m
2
[5m
2
[7m
2
[31m
2
[32m
2
[33m
2
[34m
2
[35m
2
[36m
2
[37m
2
[0m
2
0
4
11
2

2

2

2

2

2

2

2

2

2

2

2
1
4
11
2
Bold
2
Flashing
2
Inverse
2
Red
2
Green
2
Yellow
2
Blue
2
Magenta
2
Cyan
2
White
2
Normal
2
1
4
10
2
sh_line
2
sv_line
2
shsv_tr_corner
2
shsv_br_corner
2
shsv_bl_corner
2
shsv_tl_corner
2
shsv_r_tee
2
shsv_l_tee
2
shsv_t_tee
2
shsv_b_tee
2
1
2

2
1
2
%(~%[%([0!*%%RGYBMCW]+%|[0-9X][0-9][0-9]%)%]%)
2
1
2
~[
36
1
2
]
36
1
2
~[1]
36
1
2
7bit
2
1
4
14
0
196
0
179
0
191
0
217
0
192
0
218
0
180
0
195
0
194
0
193
0
178
0
177
0
176
0
250
2
1
4
14
0
18
0
25
0
183
0
183
0
183
0
183
0
183
0
183
0
183
0
183
0
178
0
177
0
176
0
183
2
1
4
14
2
sh_line
2
sv_line
2
shsv_tr_corner
2
shsv_br_corner
2
shsv_bl_corner
2
shsv_tl_corner
2
shsv_r_tee
2
shsv_l_tee
2
shsv_t_tee
2
shsv_b_tee
2
lt_fill
2
md_fill
2
dk_fill
2
center_dot
2
1
4
3
2
IBM
2
ISO
2
7bit
2
1
4
14
2
-
2
|
2
+
2
+
2
+
2
+
2
+
2
+
2
+
2
+
2
-
2
=
2
#
2
.
2
1
0
0
2
4
4
2
2
emulation utilities
2
emu
2
5
2
An object which contains methods for generating and emulating the entire range of ANSI/ASCII characters.
2
5
4
2
0
10309
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#118
server options

16
2
-1
-1
-1
1
-1
124
0
35
fg_ticks
fg_seconds
bg_ticks
bg_seconds
protect_fileappend
protect_filechmod
protect_filedelete
protect_fileerror
protect_fileexists
protect_fileextract
protect_filegrep
protect_fileinfo
protect_filelength
protect_filelist
protect_filemkdir
protect_fileread
protect_filerename
protect_filermdir
protect_filesize
protect_filewrite
timeout_msg
recycle_msg
boot_msg
redirect_from_msg
redirect_to_msg
create_msg
protect_toliteral
protect_queue_info
protect_set_connection_option
max_stack_depth
support_numeric_verbname_strings
protect_match_objects
protect_string_hash_as_int
protect_match_verbname
connect_msg
44
0
800000
2
1
0
7
2
1
0
400000
2
1
0
5
2
1
0
1
2
5
0
1
2
5
0
1
2
5
0
1
2
5
0
1
2
5
0
1
2
5
0
1
2
5
0
1
2
5
0
1
2
5
0
1
2
5
0
1
2
5
0
1
2
5
0
1
2
5
0
1
2
5
0
1
2
5
0
1
2
5
2
*** Timed-out waiting for login. ***
2
5
2
*** Recycled ***
2
5
2
*** Disconnected ***
2
5
2
*** Redirecting connection to new port ***
2
5
2
*** Redirecting old connection to this port ***
2
5
2
*** Created ***
2
5
0
1
2
1
0
1
2
1
0
1
2
1
0
50
2
1
0
1
2
1
0
1
2
1
0
1
2
1
0
1
2
1
2
*** Connected ***
2
5
0
0
2
4
4
1
2
server options
2
5
2
Properties on this object take the place of certain server run-time options.
2
5
4
2
0
2003
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#119
RANGED Weapon Grandparent

144
36
-1
-1
-1
94
162
305
24
load
2
109
0
range_mod
2
173
-1
attempt_attack
2
173
-1
fire shoot
2
109
1
match_target
2
173
-1
target_match_failed
2
173
-1
ammo_stat
2
173
-1
attack_mod dodge_mod parry_with parry_against damage skill slowness lethality
2
173
-1
combat_messages
2
173
-1
acceptable
2
173
-1
is_accepted_ammo_type
2
173
-1
ammo_loaded
2
173
-1
deplete_ammo
2
173
-1
ammo_remaining
2
173
-1
finished_attack
2
173
-1
is_full
2
165
-1
do_reload
2
173
-1
shoot fire
2
157
0
unload eject
2
157
-2
do_unload
2
173
-1
tell_description
2
173
-1
set_rounds_shot
2
173
-1
init_for_core
2
173
-1
is_unloadable_by
115
173
-1
14
accepted_ammo
ammo_capacity
load_msg
oload_msg
load_failed_msg
oload_failed_msg
rounds_shot
not_loaded_msg
onot_loaded_msg
unload_msg
ounload_msg
unload_failed_msg
ounload_failed_msg
unloadable
76
2

36
5
0
1
36
5
2

36
5
2
%N %<loads> %p %t with %[iiname].
36
5
2

36
5
2
%N %<tries> to load %p %t with %[iiname], but %[ips] won't fit.
36
5
0
0
2
1
2

36
5
2
%N %<attempts> to shoot %i with %p %t, but %[tps] %<t:is> not loaded.
36
5
2

36
5
2
%N %<unloads> %[diname] from %p %t.
36
5
2

36
5
2
%N %<tries> to unload %p %t, but %[tps] seems to be jammed.
36
5
0
1
115
1
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
5
36
5
0
-15
115
1
5
115
1
0
-15
115
1
0
0
115
1
0
5
115
1
0
10
115
1
1
161
115
1
1
255
115
1
0
20
115
1
5
115
1
0
2
115
1
5
115
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
1
2
RANGED Weapon Grandparent
36
5
2
A weapon used to shoot pointy projectiles at hapless targets from a safe distance.  The coward's sword!
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#120
generic armour

144
36
-1
-1
-1
98
-1
-1
3
is_configurable_by
2
173
-1
worn_armor
36
173
-1
is_wearable_by
36
173
-1
0
64
1
-1
36
1
5
36
1
5
36
4
5
36
4
5
36
4
5
36
4
5
36
4
5
36
4
5
36
4
5
36
4
5
36
1
4
0
36
1
1
98
36
5
5
115
1
5
115
1
5
36
4
5
36
4
5
36
4
5
36
4
5
36
4
5
36
1
5
115
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
3
2
generic armour
2
armour
2
armor
36
5
2
plates or leather or something.. wards off or diminishes the power of an attack.. ARMOR!
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#121
Alternate Character Database

16
2
-1
-1
-1
37
-1
-1
10
add
2
173
-1
all_alternate_characters all_alternates all_alts_for
2
173
-1
is_alt*ernate_character
2
173
-1
delete
2
173
-1
find*
2
173
-1
primary_character_for
2
173
-1
new_alternate_ok_for
2
173
-1
max_alts_for
2
173
-1
verify_alt_info
2
165
-1
can_peek
2
173
-1
1
maximum_alternate_characters
13
0
1
2
1
5
2
5
5
36
1
4
4
2

2

4
0
4
0
36
0
0
0
2
4
4
2
2
Alternate Character Database
2
acd
2
5
2
A database to keep track of alternate characters.  Duh.
2
5
4
2
0
6052
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#122
Combat Options

16
115
-1
-1
-1
67
-1
127
0
5
show_trust_healers
show_round_display
default_trust_healers
show_first_ord
show_hide_nat
19
4
2
2
Allow only @trusted medics to heal or revive you.
2
Allow anyone to heal or revive you.
115
1
4
2
2
Don't display banners between rounds.
2
Show banners delimiting each round, and the end of battle.
115
1
0
1
115
1
4
2
2
Don't auto-choose first of an ambiguous match set.
2
Automatically choose first target with ambiguous matches.
115
1
4
2
2
Show your natural weapons when wielding nothing.
2
Appear as "unarmed" when wielding nothing.
115
1
4
4
2
trust_healers
2
round_display
2
first_ord
2
hide_nat
36
1
2
!trust_healers!round_display!first_ord!hide_nat!
36
1
5
36
1
5
115
5
2
combat_options
115
5
0
0
115
4
4
2
2
Combat Options
2
coptions
115
5
2
Options pertaining to the coded RPG and action resolution system, including global trust for RPG actions and display options.
115
5
4
2
0
1341
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#123
debugging utilities

16
2
-1
-1
-1
83
-1
126
1
print_call
2
165
-1
0
9
0
0
2
4
4
1
2
debugging utilities
2
5
2
Verbs useful for debugging, of course.
2
5
4
2
0
893
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#124
generic gender object

16
36
-1
-1
-1
1
229
77
4
pqc pq ppc pp prc pr poc po psc ps
36
173
-1
gender_noun gender_adj
36
165
-1
is_feminine is_masculine is_neuter
36
173
-1
include_with_core
2
173
-1
19
pqc
pq
ppc
pp
prc
pr
poc
po
psc
ps
is_plural
have
be
were
gender_noun
gender_adj
is_feminine
is_masculine
is_neuter
28
2
its
36
5
2
its
36
5
2
Its
36
5
2
its
36
5
2
Itself
36
5
2
itself
36
5
2
It
36
5
2
it
36
5
2
It
36
5
2
it
36
5
0
0
36
5
2
has
36
5
2
is
36
5
2
was
36
5
2

36
5
2

36
5
0
0
36
5
0
0
36
5
0
1
36
5
0
0
36
4
4
2
2
generic gender object
2
gender
36
5
2
An object holding information on a certain gender.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#125
PackRat

19
125
-1
-1
-1
295
-1
-1
0
0
185
4
0
36
1
0
1
125
5
0
0
125
5
5
125
5
0
48
125
5
0
0
2
1
4
0
2
0
5
125
5
5
36
0
5
36
1
5
2
0
5
125
5
5
2
0
5
2
1
0
0
125
5
5
125
5
5
36
1
5
36
1
4
0
36
0
5
36
1
5
125
5
4
1
1
125
2
1
5
125
4
4
0
2
0
5
2
0
4
0
125
4
5
125
4
5
125
5
5
125
5
5
125
5
5
125
5
5
2
1
4
2
0
0
0
0
125
4
4
0
125
4
5
2
1
0
-9700
100
0
5
2
0
5
125
5
5
2
1
4
0
115
1
5
125
5
1
159
125
5
4
4
0
10000000
0
7657
0
955412156
0
0
100
1
5
2
0
5
125
5
5
2
1
5
36
1
5
125
5
5
115
1
4
0
36
0
5
125
5
5
115
1
5
115
1
4
3
0
1219694
4
2
1
4744
1
66
4
2
4
1
4
2
2
hide_loc
0
0
4
1
2
thisonly
36
0
5
115
1
5
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
125
5
5
125
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
125
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
125
5
5
125
5
5
125
5
5
115
1
5
115
1
1
206
115
1
5
115
1
0
57
115
1
0
67
115
1
0
58
115
1
0
74
115
1
0
53
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
0
46
115
1
5
115
1
0
72
115
1
0
88
115
1
0
44
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
125
5
5
125
5
1
61
125
5
5
125
5
5
36
1
5
125
5
5
125
5
5
125
5
5
125
5
5
2
1
5
115
1
5
125
5
5
2
1
5
2
1
5
2
1
5
2
1
5
125
4
5
125
5
5
125
4
5
125
5
5
115
0
5
115
1
5
36
0
5
115
1
5
125
5
5
125
5
5
125
5
5
125
5
5
125
5
5
125
5
5
125
5
5
125
5
5
125
5
5
125
5
5
36
1
5
125
5
5
125
5
5
115
1
0
0
115
1
5
125
4
5
125
4
5
36
1
5
125
5
5
125
5
0
0
125
4
4
1
2
PackRat
2
1
2
A beady-eyed little fella whose sole purpose is to gather the trivial objects of the MOO into its greedy little paws.
125
5
4
2
0
7657
0
955412156
100
1
5
125
5
5
125
5
5
125
5
5
125
5
5
115
1
#126
Event Broadcaster

16
36
-1
-1
-1
83
-1
-1
2
audio sound speech
36
165
-1
video motion
36
165
-1
2
audio
video
11
1
1810
36
1
1
4098
36
1
0
0
36
4
4
1
2
Event Broadcaster
36
5
2
The core of the event system, this object dispatches certain events to the appropriate receivers.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#127
Privacy Options

16
2
-1
-1
-1
67
-1
-1
2
show_public_loc
36
173
-1
parse_public_loc
36
173
-1
8
show_refuse_pages
show_hide_who
show_public_email
show_public_site
show_public_home
type_public_loc
default_public_loc
show_paranude
22
4
2
2
Anyone may page you.
2
You are not accepting pages.
115
1
4
2
2
You are included in connected user listings.
2
You don't want others to know you're connected.
115
1
4
2
2
Your e-mail address is kept private.
2
WARNING: Your e-mail address is public!
115
1
4
2
2
Your connection site is kept private.
2
WARNING: Your connection info is public!
115
1
4
2
2
Your 'home' location is kept private.
2
Your 'home' location is viewable via @finger.
115
1
4
2
0
0
0
2
36
1
2
zone
36
1
4
2
2
No extra look notification.
2
Notify me whenever someone may have seen me naked.
36
1
4
7
2
refuse_pages
2
hide_who
2
public_email
2
public_site
2
public_home
2
public_loc
2
paranude
36
1
2
!refuse_pages!hide_who!public_email!public_site!public_home!public_loc!paranude!
36
1
4
0
36
1
5
2
5
2
privacy_options
2
5
0
0
2
4
4
1
2
privacy options
2
5
2
These options allow citizens to toggle accessibility to theirselves and their personal information.
2
5
4
2
0
2564
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#128
--Private--

16
36
-1
-1
-1
3
-1
130
1
acceptable
36
173
-1
0
30
2
@privacy-options -public_loc
36
5
0
0
36
5
5
36
5
5
36
5
5
36
5
4
0
36
5
1
115
36
5
5
36
4
5
36
5
5
36
5
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
0
0
113
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
--Private--
2
hidden_loc
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#129
Builder Feature Object

16
2
-1
-1
-1
74
-1
212
10
feature_ok
2
173
-1
@verbs @properties @props
2
85
-2
@find
2
29
-1
@opaque @defined
2
29
-1
@loc*ations @locs
36
29
-1
@contents
2
29
-1
@kids @branches @leaves @par*ents @descendants
2
93
-2
property_desc
2
173
-1
verb_desc
2
173
-1
object_desc
2
173
-1
0
46
1
-1
36
1
4
4
2
@verbs
2
@properties
2
@find
2
@opaque
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
1
5
2
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
4
2
Builder Feature Object
2
BuildFO
2
Builder FO
2
is_prog_feature
2
5
2
A command repository for programmer verbs.
2
5
4
2
0
12930
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#130
--Private--

16
36
-1
-1
-1
3
-1
-1
1
acceptable
36
173
-1
0
30
2
@privacy-options !public_home
36
5
0
0
36
5
5
36
5
5
36
5
5
36
5
4
0
36
5
1
115
36
5
5
36
4
5
36
5
5
36
5
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
0
0
113
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
--Private--
2
hidden_home
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#131
generic rpg exit

144
2
-1
-1
-1
7
99
-1
18
set_blocked_by
115
173
-1
blockers*c
115
173
-1
is_blocked_by
115
173
-1
is_blocked_for
115
173
-1
is_blocking_other_exit
115
173
-1
block
115
45
-1
hear_event_exit
115
173
-1
is_unlocked_for
115
173
-1
move
115
173
-1
unblock
115
45
-1
throw_through_msg othrow_through_msg thrown_through_msg thrown_through_failed_msg throw_through_fail_msg othrow_through_fail_msg
115
173
-1
throw toss heave heft
2
149
7
blocked_by
115
173
-1
block_msg oblock_msg stop_blocking_msg ostop_blocking_msg nogo_blocked_msg onogo_blocked_msg blocked_msg
115
173
-1
look_self
115
173
-1
may_throw_through
115
165
-1
do_block
115
173
-1
do_unblock
115
173
-1
14
blocked_by
block_msg
oblock_msg
stop_blocking_msg
ostop_blocking_msg
nogo_blocked_msg
onogo_blocked_msg
blocked_msg
throw_through_msg
othrow_through_msg
thrown_through_msg
thrown_through_failed_msg
throw_through_fail_msg
othrow_through_fail_msg
43
4
0
115
1
2

115
5
2
%N %<blocks> exit to %[tdname].
115
5
2

115
5
2
%N %<moves> away from %[tdname].
115
5
2

115
5
2
%N %<tries> to go %t, but the way is blocked by %[tblockers].
115
5
2
%[tdnamec] is blocked by %[tblockers].
115
5
2

115
5
2
%N %<tosses/toss> %[diname] towards %[tdname], out of sight.
115
5
2
Someone tosses %[diname] in from %[tdname].
115
5
2

115
5
2

115
5
2
%N %<throws> %[diname] towards %[tdname], but %[dps] doesn't leave the area.
115
5
5
2
5
5
2
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
5
5
2
5
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
1
2
generic rpg exit
2
5
4
1
2
A child of the standard exit with extra features such as: :throw any through this -- Throws an object to the otherside :block this -- Block passage through the exit with your body.
2
5
4
2
0
13120
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#132
generic rpg room

144
2
-1
-1
-1
3
144
128
15
add_combatant*s
115
173
-1
remove_combatant*s
115
173
-1
sort_combatants
115
173
-1
turn_wait round_wait
115
173
-1
birth_combat
115
173
-1
kill_combat
115
173
-1
combatants
115
173
-1
exitfunc
115
173
-1
is_forbidden_action
115
173
-1
attempt_action
115
173
-1
do_combat_round
115
173
-1
announce_to_combatants
115
173
-1
init_for_core
2
173
-1
_combat
115
173
-1
title
115
173
-1
5
turn_wait
round_wait
combat_task
combatants
forbidden_actions
35
0
0
115
5
0
2
115
5
0
0
115
1
4
0
115
1
4
0
115
5
5
2
5
0
0
2
5
5
2
5
5
2
5
5
2
5
4
0
2
5
1
115
2
5
4
0
2
4
1
-1
2
5
0
148314262
2
5
4
0
2
4
5
2
5
5
2
5
5
2
5
5
2
5
0
0
113
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
1
2
generic rpg room
2
5
2
A child of the standard room which provides for 'combat' and other structured, orderly, and/or strategic physical activities.
2
5
4
2
0
9845
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
0
1
115
1
#133
generic restricted player

16
2
-1
-1
-1
6
-1
134
5
@describe @rename
36
89
-2
home start
36
89
-2
page '* +*
36
89
-2
title
36
173
-1
@quit
36
9
-1
0
176
5
36
1
5
2
0
5
2
5
5
2
0
5
2
1
0
0
2
5
5
2
5
5
36
1
5
36
1
4
0
36
0
5
36
1
5
2
5
4
0
2
1
5
2
4
4
0
2
0
5
2
0
4
0
2
4
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
4
4
0
2
4
5
2
1
5
100
0
5
2
0
5
2
5
5
2
1
4
0
115
1
5
2
5
1
159
2
5
5
100
1
5
2
0
5
2
5
5
2
1
5
36
1
5
2
5
5
115
1
4
0
36
0
5
2
5
5
115
1
5
115
1
5
36
0
5
115
1
5
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
2
5
5
115
1
5
115
1
1
206
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
1
61
2
5
5
2
5
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
115
1
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
4
5
2
5
5
2
4
5
2
5
5
115
0
5
115
1
5
36
0
5
115
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
5
2
4
5
2
4
5
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
generic restricted player
2
grp
2
1
2
Someone who hasn't yet earned (or has given up through abuse) the right to creative freedom in this VR, nor shown the responsibility to use certain OOC commands.
2
5
4
2
0
4844
0
2085978499
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#134
output basket

16
36
-1
-1
-1
6
-1
139
9
tell
36
173
-1
notify
36
173
-1
record_output
36
173
-1
capture_task
36
173
-1
call_task
2
173
-1
_call_task
38
173
-1
get_output
36
173
-1
clear_task
36
173
-1
register_task
36
173
-1
3
master_tasks
masters
task_output
179
4
0
36
1
4
0
36
1
4
0
36
0
5
36
1
5
2
0
5
36
5
5
2
0
5
2
1
0
0
36
5
5
36
5
5
36
1
5
36
1
4
0
36
0
5
36
1
5
36
5
4
0
2
1
5
36
4
4
0
2
0
5
2
0
4
0
36
4
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
36
4
4
0
36
4
5
2
1
5
100
0
5
2
0
5
36
5
5
2
1
4
0
115
1
5
36
5
1
159
36
5
5
100
1
5
2
0
5
36
5
5
2
1
5
36
1
5
36
5
5
115
1
4
0
36
0
5
36
5
5
115
1
5
115
1
5
36
0
5
115
1
5
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
5
36
5
5
115
1
5
115
1
1
206
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
1
61
36
5
5
36
5
5
36
1
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
115
1
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
36
4
5
36
5
5
36
4
5
36
5
5
115
0
5
115
1
5
36
0
5
115
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
1
5
36
5
5
36
5
5
115
1
5
115
1
5
36
4
5
36
4
5
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
output basket
2
obask
2
1
2
Merely a shell of a creature.  It does things, perceives the response, and vomits it back to its master.
36
5
4
2
0
7447
0
950843369
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#135
Virtual Reality Core

16
115
-1
-1
-1
1
-1
136
2
maybe_warn_rename maybe_warn_describe maybe_warn_message
2
173
-1
rename_warning describe_warning message_warning
115
173
-1
3
rename_warning_msg
describe_warning_msg
message_warning_msg
12
2
If you misleadingly name objects, your @rename priveleges may be revoked.  See `help customizing`.
115
5
2
If you misleadingly describe objects, your @describe priveleges may be revoked.  See `help customizing`.
115
5
2
If you misleadingly message objects, your @message priveleges may be revoked.  See `help customizing`.
115
5
0
0
115
4
4
4
2
Virtual Reality Core
2
VR Core
2
vr_core
2
vrc
115
5
5
115
5
4
2
0
1895
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#136
generic lock interface

144
36
-1
-1
-1
1
-1
148
11
is_key
36
173
-1
is_unlockable_by
36
173
-1
is_lockable_by
36
173
-1
do_lock
36
173
-1
lock_succeeds
36
173
-1
do_unlock
36
173
-1
do_pick
36
173
-1
pick_succeeds
36
173
-1
announce_lock_message*s
36
165
-1
unlock_succeeds
36
173
-1
register_pick_attempt
36
173
-1
16
detect_owner
pick_mod
key_objects
olock_failed_msg
ounlock_failed_msg
opick_failed_msg
lock_msg
olock_msg
unlock_msg
ounlock_msg
pick_msg
opick_msg
oattempt_lock_msg
oattempt_unlock_msg
oattempt_pick_msg
pick_attempts
25
0
1
36
5
0
-10
36
5
4
0
36
5
2
Nothing happens.
36
5
2
Nothing happens.
36
5
2
Nothing happens.
36
5
2

36
5
2
%[tpsc] clicks secure.
36
5
2

36
5
2
%[tpsc] yields with a happy click.
36
5
2

36
5
2
With a mildly protesting *cluck* %[tps] yields, leaving %[ddname] insecure.
36
5
2
%N %<manipulates> %[tdname] on %[diname].
36
5
2
%N %<manipulates> %[tdname] on %[diname].
36
5
2
%N %<attempts> some mechanical roguery on %[tdname] on %[diname].
36
5
4
0
36
1
0
0
36
4
4
2
2
generic lock interface
2
glint
36
5
2
An interface for VR locking of objects.  Minimal methods make for future extensibility.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#137
lockable object

16
36
-1
-1
-1
114
80
-1
10
get_lock_type
36
173
-1
get_lock_status
36
173
-1
set_lock_status
36
173
-1
set_lock_type
36
173
-1
lockable
36
173
-1
do_lock do_unlock do_pick
36
173
-1
lock unlock pick
36
109
-2
examine_verb_ok
36
173
-1
is_opened
36
173
-1
init_for_core
2
173
-1
1
lock
12
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
lockable object
2
lobject
36
5
2
An object with support for VR locking via kids of $lock_interface.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#138
lock dummy

144
36
-1
-1
-1
5
-1
-1
2
install
2
109
3
_destroy
2
173
-1
5
lock_interface
install_skill
install_mod
install_msg
oinstall_msg
47
1
-1
36
5
2
MechTech
36
5
0
0
36
5
2

36
5
2
%N %<performs> some small mechanical wonders which result in the successful installation of %p %d into %[iiname].
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
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
lock dummy
2
lodum
36
5
2
A representation of a generic lock type, so players can have and install locks without possessing the actual child of $lock_interface.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#139
Executor

17
36
-1
-1
-1
6
-1
-1
1
purge
139
173
-1
0
176
5
36
1
5
2
0
5
36
5
5
2
0
5
2
1
0
0
36
5
5
36
5
5
36
1
5
36
1
4
0
36
0
5
36
1
5
36
5
4
0
2
1
5
36
4
4
0
2
0
5
2
0
4
0
36
4
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
36
4
4
0
36
4
5
2
1
0
286
100
0
5
2
0
5
36
5
5
2
1
4
0
115
1
5
36
5
1
159
36
5
4
4
0
10000000
0
0
0
955495071
0
0
100
1
5
2
0
5
36
5
5
2
1
5
36
1
5
36
5
5
115
1
4
0
36
0
5
36
5
5
115
1
5
115
1
4
3
0
1219694
4
1
1
4744
4
1
4
1
4
2
2
hide_loc
0
0
36
0
5
115
1
5
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
5
36
5
5
115
1
5
115
1
1
206
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
36
5
5
36
5
1
61
36
5
5
36
5
5
36
1
5
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
115
1
5
36
5
5
2
1
5
2
1
5
2
1
5
2
1
5
36
4
5
36
5
5
36
4
5
36
5
5
115
0
5
115
1
5
36
0
5
115
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
1
5
36
5
5
36
5
5
115
1
0
6733
115
1
5
36
4
5
36
4
5
36
1
5
36
5
5
36
5
0
0
36
4
4
1
2
Executor
2
1
2
He'll take anything, as long as the owner is dead.
36
5
4
2
0
5089
0
950843369
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#140
generic collective ammo

144
115
-1
-1
-1
111
165
154
11
attack_mod dodge_mod parry_with parry_against damage skill slowness lethality
115
173
-1
combat_messages
115
173
-1
deplete
115
173
-1
remaining
115
173
-1
do_load
115
173
-1
do_unload
115
173
-1
tell_stats
115
173
-1
set_stat
115
173
-1
mod_stat
115
173
-1
init_for_core
2
173
-1
include_with_core
2
173
-1
12
ammo_type
attack_mod
dodge_mod
parry_with
parry_against
damage
slowness
combat_messages
skill
penetration
lethality
damage_type
65
2

115
1
0
0
115
1
0
-1000
115
1
0
-10000
115
1
0
-10000
115
1
0
0
115
1
0
0
115
1
1
161
115
1
1
264
115
1
0
70
115
1
0
80
115
1
2

115
1
5
101
1
2
ammo
115
5
5
101
1
5
101
1
2
%(collective ammo%|gca%)
101
1
5
101
1
1
140
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
4
0
115
4
4
0
115
4
1
-1
36
1
5
115
5
5
115
5
0
0
115
4
4
2
2
generic collective ammo
2
gca
115
5
2
A pile of bullets, arrows, or bolts.  Ammo too insignificant or numerous to be counted individually.
115
5
4
2
0
9292
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#141
generic terrain room

144
113
-1
-1
-1
132
-1
177
30
spawn
113
173
-1
exitfunc
113
165
-1
destroy
113
173
-1
desc_noun desc_adj idesc_noun idesc_adj ddesc_noun ddesc_adj
113
165
-1
leave_msg oleave_msg arrive_msg oarrive_msg nogo_msg onogo_msg
113
173
-1
get_dest*ination
113
165
-1
fake_exit_move
113
165
-1
north south west east up down northwest northeast southwest southeast n s w e u d nw ne sw se
113
5
-1
spawned_name
113
173
-1
spawned_aliases
113
173
-1
get_cell_info*rmation update_cell_info*rmation get_cell_terrain new_coordinates
2
165
-1
recycle
113
173
-1
tell_exits
113
173
-1
audience_for_announce
113
165
-1
adjacent_cells adjacent_rooms transmit_announce*ment visible_in_direction
115
165
-1
accept_from_cell
113
173
-1
adjacent_desc
113
173
-1
maybe_transmit_announce*ment
2
173
-1
announce_all_but
2
173
-1
get_detail
2
165
-1
icon
2
173
-1
invokable_directions
2
165
-1
impassable
2
173
-1
opaque
2
173
-1
should_be_destroyed
2
173
-1
maybe_destroy
113
173
-1
acceptable_from_cell
113
173
-1
zone
113
173
-1
take_exit
113
173
-1
tell_open_directions
113
173
-1
20
destroy_when_empty
desc_noun
desc_adj
leave_msg
oleave_msg
arrive_msg
oarrive_msg
nogo_msg
onogo_msg
spawned_name
spawned_aliases
dir_names
dir_aliases
persistent_adjacent_desc
adjacent_desc_msg
icon_msg
opaque
exclude_impassable
solid
default_zone
55
0
1
113
1
4
1
2
area
113
5
4
1
2
nondescript
113
5
2
You leave, approaching %[lddesc_noun] $tdir.
113
5
2
%N %<leaves> towards some %[ldesc_adj] terrain to $dir.
113
5
2

113
5
2
%N %<arrives> from some %[ldesc_adj] terrain $tdir.
113
5
2
You can't go that way.
113
5
2

113
5
2
nondescript terrain
113
5
4
0
113
5
4
8
2
north
2
south
2
west
2
east
2
northwest
2
northeast
2
southwest
2
southeast
113
5
4
8
2
n
2
s
2
w
2
e
2
nw
2
ne
2
sw
2
se
113
5
4
2
0
1885631795
2
a nondescript area
113
1
2

113
1
2
 . 
113
5
0
0
113
5
0
0
113
5
0
0
113
5
1
-1
113
5
5
113
5
5
113
5
5
115
1
4
0
115
1
5
113
5
5
113
5
0
0
113
5
5
113
5
5
113
5
5
113
5
4
0
113
5
1
115
113
5
5
113
4
5
113
5
5
113
5
5
113
4
5
113
5
5
113
5
5
113
5
5
113
5
0
0
113
1
4
0
113
4
4
0
113
4
1
-1
36
1
5
113
5
5
113
5
0
0
113
4
4
3
2
generic terrain room
2
terrain room
2
gtr
113
5
2
An area intended to represent an anonymous patch of a certain type of geography.
113
5
4
2
0
24513
0
955495071
100
1
5
113
5
5
113
5
5
113
5
4
5
2
Just one note for now:
2

2
Set default_zone to the object number of the zone into which all kids of this terrain should be spawned.  Alternatively, you can just @move the generic into the preferred zone, but that's not as stable as setting the property.
2

2
See `help $terrain_zone` for more help on creating megaroom terrain zones.
113
5
5
115
1
#142
generic coordinate zone

144
113
-1
-1
-1
82
-1
-1
26
update_cell_info*rmation
2
173
-1
get_cell_info*rmation
113
173
-1
get_cell_terrain
113
173
-1
get_direction_offset
113
173
-1
add_coordinates
113
173
-1
new_coordinates
113
165
-1
coordinate_propname
113
173
-1
grouped_cell_propname
113
173
-1
is_grouped_cell
113
165
-1
verify_prop*erty
113
165
-1
adjacent_cells
113
173
-1
adjacent_rooms
113
173
-1
add_cell_group
2
165
-1
del_cell_group
2
165
-1
add_grouped_cell
113
173
-1
del_grouped_cell
113
173
-1
connect_points
113
173
-1
parse_cell_list
113
173
-1
visible_in_direction
2
173
-1
transmit_announce*ment
2
173
-1
draw_map
2
165
-1
point_in_line
113
165
-1
terrain_zone_for
113
173
-1
info_from_terrain_map
113
173
-1
init_for_core
2
173
-1
is_valid_cell_info
113
173
-1
8
directions
offsets
default_terrain
grouped_cells
othersides
is_3d
terrain_zones
terrain_maps
20
4
10
2
north
2
south
2
west
2
east
2
up
2
down
2
northwest
2
northeast
2
southwest
2
southeast
113
5
4
10
4
3
0
0
0
1
0
0
4
3
0
0
0
-1
0
0
4
3
0
-1
0
0
0
0
4
3
0
1
0
0
0
0
4
3
0
0
0
0
0
1
4
3
0
0
0
0
0
-1
4
3
0
-1
0
1
0
0
4
3
0
1
0
1
0
0
4
3
0
-1
0
-1
0
0
4
3
0
1
0
-1
0
0
113
5
1
141
113
5
4
0
113
1
4
10
2
south
2
north
2
east
2
west
2
down
2
up
2
southeast
2
southwest
2
northeast
2
northwest
113
5
0
0
113
5
4
0
113
5
4
0
113
5
1
115
113
5
5
113
5
5
113
5
0
0
113
4
4
3
2
generic coordinate zone
2
coordinate zone
2
gcz
113
5
2
The tunnel complex beneath the R/T base, and possibly beyond it.
113
5
4
2
0
23476
0
955495071
100
1
5
113
5
5
113
5
5
113
5
4
64
2
The following are some brief notes on setting up your own terrain zone.  You can find out more detailed information by issuing the `@d $terrain_zone:.` command and checking out the help included in the comment lines of each verb.
2

2
The first thing you'll have to do is create the zone:
2

2
        @create $terrain_zone named "Zone_Name",zone_alias,zone_alias,etc
2

2
For the purposes of the following examples, we'll say the new zone has an object number #ZZZZ.
2

2
Next, you'll need to choose a default "terrain object" for your zone; this will be the kind of room created when an object enters an undeveloped cell inside the zone.
2

2
See `help $terrain_room` for help on the generic terrain object.
2

2
Do `@kids $terrain_room` to see existing children of the generic terrain object. Choose one of those, or create your own.  When you've got one (we'll call it #TTTT), type:
2

2
        @set #ZZZZ.default_terrain to #TTTT
2

2
Now you'll want a starter room for your zone.  Create a kid of your #TTTT, we'll call it #AAAA, then `@move me to #AAAA` and type:
2

2
        @zone #ZZZZ at 1,1,0
2

2
That will place it at coordinates 1,1,0 in your zone.  (You can specify other coordinates, but those will help later in this tutorial.)
2

2
***NOTE: Anything previously in those coordinates will be removed.
2

2
You will probably at some time want to lay out strings of cells with terrain different from the default.  You do this by using "terrain maps".
2

2
***NOTE: This was previously accomplished with "terrain zones"; they are now deprecated.  Their use is discouraged and they may be phased out.
2

2
First, create a temporary property on yourself in which you can edit your map:
2

2
        @prop me.tmp {}
2

2
Next you'll want to edit that property as an ASCII map of your terrain.  For example:
2

2
        @qedit me.tmp
2
        # ############
2
        #    # #     #
2
        ###~~#~#~~~#~#
2
        #          # #
2
        ############ #
2
                     #
2
        ##############
2
        .
2

2
Now you've got your little ASCII map in your "tmp" property.  Next you'll want to define what those characters mean.  Each should be associated with a child of the generic terrain room ($terrain_room; mentioned above).
2

2
You want "#" to be your wall, and "~" to be your stream.  You've already made kids of $terrain_room for these and `@chmod them +f` so they're ready to be created by Geomancer when someone moves into them.  Let's say your wall is #WWWW and your stream is #SSSS.  Remember that.
2

2
Next you want to anchor the map to a point in your terrain zone.  We'll stick this one right in the middle: {0,0,0}.
2

2
Now you're ready to ROCK BABY ROCK.  Install the terrain map like so:
2

2
        ;#ZZZZ.terrain_maps = listappend(#ZZZZ.terrain_maps, {{0, 0, 0},
2
         {{"#", #WWWW}, {"~", #SSSS}}, player.tmp})
2

2
That's it.  Your terrain map, she is installed.  If you're still in #AAAA and place it as shown earlier in the tutorial, just go south and you should be walking right into your little maze above.
2

2
Of course, you probably made a different kind of ASCII map and maybe placed it somewhere other than dead center.  Walk there of @zone a new room in the center of your terrain map to try it out.
2

2
***NOTE: Unique rooms within a zone will always take precedence over those terrain mapped and created on-the-fly.
2

2
That's all for now, kids.  Snoop around in the verbs on $terrain_zone to get a better idea of how to manipulate a terrain zone.  Enjoy!
2

2
-Quinn
113
5
5
115
1
#143
tracebacks

16
2
45
-1
174
44
-1
174
1
receive_message
36
173
-1
1
autodelete_threshold
27
0
30
36
1
4
0
2
5
0
0
36
1
4
0
36
0
5
2
5
2
%n (%#) can't send to moderated list %t (%[#t]) directly.
2
5
4
0
2
5
0
1
2
5
4
0
36
1
4
0
36
1
0
2592000
2
5
0
0
36
1
4
0
36
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
0
0
2
4
4
3
2
tracebacks
2
tbks
2
errors
36
1
2
A log of all tracebacks.
2
5
4
2
0
1239
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#144
Generic Word Game

144
36
-1
-1
-1
132
203
141
84
compute_score
36
173
-1
place
36
89
-2
strip_all_but
36
173
-1
value*! preview*!
36
89
-2
play join
36
13
-1
reset
36
13
-1
score*s
36
13
-1
peek peer
36
9
-1
turn
36
89
-2
start
36
93
-2
order
36
89
-2
nextturn
36
173
-1
pass skip
36
9
-1
quit
36
13
-1
pick
36
13
-1
undo oops
36
13
-1
show
36
93
-2
do_value
36
173
-1
do_quit
36
173
-1
exitfunc
36
173
-1
do_history
36
173
-1
history
36
85
-2
instr*uctions
36
29
-1
mix*! scramble*! sort alpha*betize
36
13
-1
dots
36
93
-2
save
36
13
-1
restore resume
36
29
-1
do_endgame
36
173
-1
surrender*-game
36
77
-2
parse_placement
36
173
-1
initialize
36
173
-1
boot*-player
36
25
-1
endgame
36
13
-1
prevturn
36
173
-1
recompute_order
36
173
-1
who_location_msg
36
173
-1
tell_contents
36
173
-1
put_tiles
36
165
-1
connects
36
165
-1
arrange
36
93
-2
do_show_saved
36
173
-1
do_show_legend
36
173
-1
do_show_bag
36
173
-1
do_show_board
36
173
-1
do_show_dist*ribution
36
173
-1
used onboard
36
29
-1
features
36
9
-1
shake
36
25
-1
set_prop*erty
36
173
-1
clear_prop*erty
36
173
-1
get_prop*erty
36
173
-1
secure
36
173
-1
do_show_top update_rankings
36
173
-1
b*oard
36
9
-1
Description
36
173
-1
do_show_tiles
36
173
-1
discard_tiles
36
173
-1
exchange discard
36
25
-1
lock unlock
36
9
-1
game-option*s option*s
36
93
-2
parse_order
36
173
-1
get_word_mult_msg
36
173
-1
max-players
36
25
-1
look_self
36
173
-1
_onboard
36
165
-1
to_percent*age
36
173
-1
_distribution
36
173
-1
_chance_of_drawing
36
173
-1
draw-chance
36
93
-2
bag_graphic
36
173
-1
compile_board
36
173
-1
custom_score
36
173
-1
scramble_letters
36
173
-1
init_for_core
2
173
-1
blow_away
36
173
-1
bored
36
9
-1
set_player_info
36
173
-1
get_player_info
36
173
-1
allow_fame
36
173
-1
save-fame
36
13
-1
list-fame
36
5
-1
show-fame
36
29
-1
purge-fame
36
21
-1
do_play
36
173
-1
33
board_message
notify
dots
max_players
history
task_id
lastscore
values
letters
lastmove
scores
players
board
letter_layout
board_layout
order
turn
started
lastpicked
tiles
saved
blank
bag
features_msg
prevturn
score_db
locked
options_db
saved_game_expire_period
bag_graphic
hall_of_fame
allow_fame
finished
68
4
0
36
1
4
0
36
1
4
0
36
1
0
4
36
1
4
0
36
1
4
0
36
1
4
0
36
1
4
27
0
1
0
3
0
3
0
2
0
1
0
4
0
2
0
4
0
1
0
8
0
5
0
1
0
3
0
1
0
1
0
3
0
10
0
1
0
1
0
1
0
1
0
4
0
4
0
8
0
4
0
10
0
0
36
1
4
27
2
A
2
B
2
C
2
D
2
E
2
F
2
G
2
H
2
I
2
J
2
K
2
L
2
M
2
N
2
O
2
P
2
Q
2
R
2
S
2
T
2
U
2
V
2
W
2
X
2
Y
2
Z
2
_
36
1
4
0
36
1
4
0
36
1
4
0
36
1
4
15
2
=  '   =   '  =
2
 -   "   "   - 
2
  -   ' '   -  
2
'  -   '   -  '
2
    -     -    
2
 "   "   "   " 
2
  '   ' '   '  
2
=  '   *   '  =
2
  '   ' '   '  
2
 "   "   "   " 
2
    -     -    
2
'  -   '   -  '
2
  -   ' '   -  
2
 -   "   "   - 
2
=  '   =   '  =
36
1
4
100
2
A
2
A
2
A
2
A
2
A
2
A
2
A
2
A
2
A
2
B
2
B
2
C
2
C
2
D
2
D
2
D
2
D
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
F
2
F
2
G
2
G
2
G
2
H
2
H
2
I
2
I
2
I
2
I
2
I
2
I
2
I
2
I
2
I
2
J
2
K
2
L
2
L
2
L
2
L
2
M
2
M
2
N
2
N
2
N
2
N
2
N
2
N
2
O
2
O
2
O
2
O
2
O
2
O
2
O
2
O
2
P
2
P
2
Q
2
R
2
R
2
R
2
R
2
R
2
R
2
S
2
S
2
S
2
S
2
T
2
T
2
T
2
T
2
T
2
T
2
U
2
U
2
U
2
U
2
V
2
V
2
W
2
W
2
X
2
Y
2
Y
2
Z
2
_
2
_
36
1
4
15
2
=  '   =   '  =
2
 -   "   "   - 
2
  -   ' '   -  
2
'  -   '   -  '
2
    -     -    
2
 "   "   "   " 
2
  '   ' '   '  
2
=  '   *   '  =
2
  '   ' '   '  
2
 "   "   "   " 
2
    -     -    
2
'  -   '   -  '
2
  -   ' '   -  
2
 -   "   "   - 
2
=  '   =   '  =
36
1
4
0
36
1
0
0
36
1
0
0
36
1
4
0
36
0
4
0
36
0
4
0
36
0
4
0
36
0
4
100
2
A
2
A
2
A
2
A
2
A
2
A
2
A
2
A
2
A
2
B
2
B
2
C
2
C
2
D
2
D
2
D
2
D
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
F
2
F
2
G
2
G
2
G
2
H
2
H
2
I
2
I
2
I
2
I
2
I
2
I
2
I
2
I
2
I
2
J
2
K
2
L
2
L
2
L
2
L
2
M
2
M
2
N
2
N
2
N
2
N
2
N
2
N
2
O
2
O
2
O
2
O
2
O
2
O
2
O
2
O
2
P
2
P
2
Q
2
R
2
R
2
R
2
R
2
R
2
R
2
S
2
S
2
S
2
S
2
T
2
T
2
T
2
T
2
T
2
T
2
U
2
U
2
U
2
U
2
V
2
V
2
W
2
W
2
X
2
Y
2
Y
2
Z
2
_
2
_
36
1
4
18
2
Features:
2

2
- sort     -sort your tiles alphabetically, for purists
2
- scramble -scramble your tiles; They don't get better, they just move around
2

2
* arrange  <tiles> -put your tiles in a different order
2
* shake bag        -jumble the letters in the tile bag
2

2
* onboard     <tiles> -find out how many of the given tiles have been played
2
* draw-chance <tile>  -guestimated chance of drawing the given tile
2

2
- save       -save a game in progress
2
- restore    -restore a saved game
2

2

2
* options                 - List your custom game preferences
2
* options <option> on|off - Set a game option
2

36
5
1
-1
36
1
1
-1
36
1
0
0
36
1
1
145
36
5
0
1209600
36
5
4
5
4
11
2
 
2
 
2
_
2
_
2
_
2
 
2
 
2
 
2
 
2
 
2
 
4
11
2
 
2
/
2
_
2
_
2
 
2
\
2
_
2
_
2
 
2
 
2
 
4
11
2
 
2
_
2
/
2
=
2
=
2
=
2
=
2
=
2
\
2
 
2
 
4
11
2
|
2
=
2
=
2
=
2
=
2
=
2
=
2
=
2
=
2
\
2
 
4
11
2
 
2
\
2
=
2
=
2
=
2
=
2
=
2
=
2
=
2
/
2
 
36
1
4
0
36
1
0
0
36
5
0
0
36
1
5
36
5
5
36
5
5
115
1
4
0
115
1
5
36
5
5
36
5
0
0
36
5
5
36
5
5
36
5
5
36
5
4
0
36
5
1
115
36
5
5
36
4
5
36
5
5
36
5
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
0
0
113
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
Generic Word Game
2
gwg
36
5
2
A room specially designed for playing word games.  A single board is in the centre of the room, surrounded by a few semi-comfortable chairs for players and some random seating arrangements for groupies.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
4
36
2
Commands for game play:
2
 
2
reset    -clear the board, refill the bag of tiles, erase scores.
2
play     -join the game
2
start    -set up the game, choose order-of-play, and begin.
2
                       <type `help here:start' for more info>
2
 
2
max-players <num>  -set maximum players allowed to `num'
2
 
2
place <word> <direction> at <coordinates>
2
         -place a word or letters on the board. Uses normal letters first,
2
          then blanks if held and needed. Use _ in place of a letter to
2
          force a blank into a position.
2
                       <type `place' alone for more info>
2
 
2
pass     -pass your turn
2
undo     -undo your last move
2
pick     -pick some tiles from the bag of tiles, so you have seven
2
discard  -dump icky tiles back into the bag of tiles
2
surrender-quit the game & give your tiles & score to another user.
2
quit     -quit the game
2
endgame  -end the game; tally final scores.
2
 
2
Commands that are informational:
2
 
2
instructions - these!
2
features     - list new word game features.
2
board        - look at the board
2
score        - see players and scores
2
peer         - if you're not playing, peer at players' tiles. Shhh!
2
history      - show log of past moves
2
show <item>  - show information on board, tiles, letters, score, bag,
2
               legend, history, saved
2
value        - show the score value for a word or letter
2
                       <type `value' alone for more info>
2
---
36
5
0
0
115
1
#145
Word Game Options DB

16
36
-1
-1
-1
1
-1
34
3
set_player_info
36
165
-1
get_player_info
36
165
-1
init_for_core
2
173
-1
6
options
i_graphics
i_dots
i_peer_display
i_peer_ok
i_bag
15
4
5
2
graphics
2
dots
2
peer_display
2
peer_ok
2
bag
36
1
4
0
36
1
4
0
36
1
4
0
36
1
4
0
36
1
4
0
36
1
0
0
36
4
4
1
2
Word Game Options DB
36
5
2
A place to store player preferences for word games.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#146
table data type

16
36
-1
-1
-1
148
-1
-1
10
set
36
173
-1
get
36
173
-1
keys
36
173
-1
table_to_hash
36
173
-1
true
36
173
-1
get_multiple
36
173
-1
set_multiple
36
173
-1
delete
36
173
-1
delete_multiple
36
173
-1
new_multiple new
36
173
-1
0
11
0
1219694
36
1
2
table_data_type
36
1
0
0
36
4
4
2
2
table data type
2
table
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
4
59
2

2
 Abstract Data Type: Table (yummy! :)
2

2
:new()  
2
=> new empty <$table>
2

2
:new(ANY <key>, ANY <value>) 
2
=> new <$table> with <value> associated to <key>
2

2
:new_multiple({ANY <key 1>, ANY <key 2>, ..., ANY <key N>}, {ANY <value 1>, ANY <value 2>, ..., ANY <value N>})
2
=> like above but with multiple keys/values
2

2
:true($table <table>) => TRUE if <table> is not empty, FALSE otherwise
2

2
:keys($table <table>) => LIST keys that are in <table>
2

2
:delete($table <table>, ANY <key>)
2
=> return a $table, which is a copy of <table> with the <key> entry deleted. Raise E_RANGE if <key> is not in <table>.
2

2
:delete_multiple($table <table>, {ANY <key 1>, ANY <key 2>, ..., ANY <key n>} [, BOOLEAN <don't raise>])
2
=> return a $table, which is a copy of <table> with <key 1>, <key 2>, ... entries deleted. Raise E_RANGE ifone of the keys is not in table unless <don't raise> is provided and TRUE, in which case missing entries are just ignored.
2

2
:get($table <table>, ANY <key>) 
2
=> <value> associated to <key>, raise E_RANGE if <key> is not in <table>.
2

2
:get_multiple($table <table>, {ANY <key 1>, ANY <key 2>, ..., ANY <key n>} [, BOOLEAN <don't raise> [, ANY <default value>]])
2
=> {<value 1>, <value 2>, ..., <value N>} the values in <table> associated to <key 1>, ..., <key N> (respectively. raise E_RANGE if <key 1> or <key 2> or ... or <key N> is not in <table> and <don't raise> is FALSE or not provided, otherwise if <key X> is not in <table>, set <value X> to <default value> or E_RANGE.
2

2
:set($table <table>, ANY <key>, ANY <value>)
2
=> new $table, which is a copy of <table> which <key> as been associated to <value>
2

2
:set_multiple($table <table>, {ANY <key 1>, ANY <key 2>, ..., ANY <key N>}, {ANY <value 1>, ANY <value 2>, ..., ANY <value N>})
2
=> new $table, which is a copy of <table> which ... blahblah, like :set but with multiple key/values.
2

2

2
Example of use of $table:
2

2
;;me.foo = $table:new()
2
=> 0
2
;;me.foo = $table:set(me.foo, "bar", 5)
2
=> 0
2
;$table:get(me.foo, "bar")
2
=> 5
2
;$table:true(me.foo)
2
=> 1
2
;$table:get(me.foo, "baz")
2
Traceback - Rage Error (E_RANGE)
2
;;me.foo = $table:delete(me.foo, "bar")
2
=> 0
2
;$table:get(me.foo, "bar") 
2
Traceback - Rage Error (E_RANGE)
2
;$table:true(me.foo)
2
=> 0
2
;;me.foo = $table:set_multiple(me.foo, {"bar", "baz"}, {{10, 20}, #1})
2
=> 0
2
;$table:get_multiple(me.foo, {"baz", "bar"})
2
=> {#1, {10, 20}}
2

2
etc...
36
5
5
115
1
#147
skills repository

16
115
-1
-1
-1
1
-1
184
4
accept
115
173
-1
include_with_core
115
173
-1
init_for_core
2
173
-1
acceptable
115
173
-1
32
Melee
unarmed
armed
Shock
Medic
perception
Ranged
Lockpick
Dodge
Bladed
Blunt
MechTech
ElecTech
Focus
Bite
Claw
Flail
Brawl
Throwing
Shooting
Whip
agility
dexterity
endurance
quickness
strength
willpower
Javelin
Trident
Discus
Archery
Sling
41
1
240
115
1
1
241
115
1
1
242
115
1
1
243
115
1
1
244
115
1
1
245
115
1
1
246
115
1
1
266
115
1
1
247
115
1
1
254
115
1
1
255
115
1
1
256
115
1
1
257
115
1
1
258
115
1
1
259
115
1
1
260
115
1
1
261
115
1
1
262
115
1
1
263
115
1
1
264
115
1
1
265
115
1
1
248
115
1
1
249
115
1
1
250
115
1
1
251
115
1
1
252
115
1
1
253
115
1
1
267
2
1
1
268
2
1
1
269
2
1
1
270
2
1
1
271
2
1
0
0
115
4
4
2
2
skills repository
2
repository
115
5
2
A big warehouse fulla (in)human potential.
115
5
4
2
0
2946
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#148
generic data type

16
36
-1
-1
-1
1
146
180
2
set_id_name
36
173
-1
true
36
173
-1
2
id
id_name
11
0
0
36
1
2

36
1
0
0
36
4
4
2
2
generic data type
2
type
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
4
0
36
5
5
115
1
#149
warden feature

16
115
-1
-1
-1
74
-1
153
8
@jail @convict
115
93
-2
is_warden
115
173
-1
do_jail_user
2
173
-1
is_criminal
115
173
-1
register_criminal
115
173
-1
clear_criminal
115
173
-1
@unjail @pardon
2
93
-2
feature_ok
115
173
-1
6
prison
jailer_msg
convict_msg
convict_leave_msg
convict_arrive_msg
prison_log
52
1
10931
115
5
2
After much consideration and personal torment, you make the decision to banish %d to the Tartarus Zone.  %[dpsc] %<d:vanishes> from %[dpp] (relatively) comfortable surroundings.
115
5
2
Your self is yanked from dimensional space, then slapped down onto a hot, grainy, bed of sand.  Your belongings drop to the ground around you.
115
5
2
%D %<d:vanishes>.
115
5
2
The area is bleached with light.  When the luminence fades, this little corner of Hell is newly populated with %d. %[dppc] belongings drop to the ground around [%dpo].
115
5
1
18168
115
5
1
-1
36
1
4
0
36
1
5
36
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
4
0
115
4
4
0
115
4
1
-1
36
1
5
115
5
5
115
5
0
0
115
4
4
2
2
warden feature
2
warfo
115
5
2
Feature allowing imprisonment of criminals against role-play.
115
5
4
2
0
8299
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#150
unknown

16
36
-1
-1
-1
124
-1
-1
0
0
28
2
Theirs
36
5
2
theirs
36
5
2
Their
36
5
2
their
36
5
2
Themself
36
5
2
themself
36
5
2
Them
36
5
2
them
36
5
2
The person
36
5
2
the person
36
5
0
0
36
5
2
has
36
5
2
is
36
5
2
was
36
5
5
36
5
2
strange
36
5
5
36
5
5
36
5
5
36
5
0
0
36
4
4
1
2
unknown
36
5
2
An object describing the unknown gender.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#151
fists

16
115
-1
-1
-1
303
-1
-1
0
0
54
5
115
1
5
115
1
0
-25
115
1
5
115
1
5
115
1
5
115
1
1
161
115
1
1
240
115
1
0
5
115
1
0
5
115
1
5
115
1
5
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
4
0
115
4
4
0
115
4
1
-1
36
1
5
115
5
5
115
5
0
0
115
4
4
2
2
fists
2
hands
115
5
2
We all have them.  Well, most of us.  Use them to clobber somebody.
115
5
4
2
0
1078
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#152
Pose Feature

16
36
-1
-1
-1
74
-1
216
7
exclusive_list
36
173
-1
args_with_quotes
36
173
-1
is_char*acter
36
173
-1
match_object_exactly
36
173
-1
.* ,* pose
36
93
-2
parse_pose_args
36
173
-1
occupants_in
36
173
-1
0
46
1
-1
36
1
4
1
2
pose
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
3
2
Pose Feature
2
poser
2
is_char_feature
36
5
2
A feature providing more grammatically realistic emotes.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#153
Gibberish Feature

16
36
-1
-1
-1
74
-1
307
5
gib*ber
36
93
-2
gibber_seed
36
173
-1
to_gibberish
36
173
-1
get_seed_for_user
36
173
-1
init_for_core
2
173
-1
5
vowels
consonants
UPPERCASE
user_seeds
max_seed_record
51
2
aeiou
36
1
2
bcdfghjklmnpqrstvwxyz
36
1
2
ABCDEFGHIJKLMNOPQRSTUVWXYZ
36
1
4
0
36
1
0
99
36
1
1
-1
36
1
4
1
2
gibber
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
Gibberish Feature
2
GIBFO
36
5
2
The gibberish feature will display TEXT said via `gib TEXT` as a decryptable and (hopefully) pronouncable transposition.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#154
crystal

144
2
-1
-1
-1
111
-1
199
7
eat
115
45
-1
eaten_by
115
173
-1
feed
115
109
1
stat_cost
115
173
-1
do_eat
115
109
1
do_feed
115
109
1
destroy
115
173
-1
0
53
5
101
1
2
crystal
2
5
5
101
1
5
101
1
2
%(crystal%)
101
1
5
101
1
1
154
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
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
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
1
2
crystal
2
5
2
Each crystal glimmers with a rainbow of life force.  They are the residual effect of a spirit being yanked from this plane.
2
5
4
2
0
8264
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#155
generic corpse

144
115
-1
-1
-1
5
-1
176
15
acceptable
115
173
-1
setup_for
2
173
-1
get take
115
45
-1
loot
115
45
-1
get take remove
115
157
5
look_self
115
173
-1
destroy
2
173
-1
rot
115
173
-1
spawn
115
173
-1
is_writable_by
115
173
-1
is_controllable_by
115
173
-1
set_crystal_worth
115
173
-1
kick
115
45
-1
bash crush decimate
115
109
0
init_for_core
2
173
-1
6
desc_template_msg
victim
rot_msg
root
crystal_worth
tod
48
2
A scarred shell which once held the spirit of %n.  Instead of decay, you notice the corpse actually regenerating before your eyes, as if life is building within it.
115
5
1
-1
115
1
2
With a flash of brilliant light, %[tdname] %<collapses> into $loot.
115
5
1
155
115
1
0
0
115
1
0
0
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
4
0
115
4
4
0
115
4
1
-1
36
1
5
115
5
5
115
5
0
0
115
4
4
2
2
generic corpse
2
gc
115
5
2
Ah how brutal is this thing called life, what tears our spirit from charred bone and leaves but a shell.
115
5
4
2
0
11880
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#156
combat reactor

144
36
-1
-1
-1
104
-1
-1
3
hear_event_attack
36
173
-1
hear_event_heal
36
173
-1
hear_event_spell hear_event_cast_spell
36
173
-1
0
11
4
0
36
5
5
36
5
0
0
36
4
4
1
2
combat reactor
36
5
2
A small, bloody mass of brain tissue.  Left alone, it just sits there dripping menacingly.  But poke it, and WHOO BOY it goes keerazy!
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#157
Generic Attribute Object

16
115
-1
-1
-1
95
248
256
3
should_improve
115
173
-1
skill_bonus_for total_skill_bonus_for
115
173
-1
att_bonus_for
115
173
-1
0
28
5
115
1
5
115
5
0
20
115
5
5
115
5
5
115
5
5
115
5
5
115
5
0
95
115
5
5
115
5
9
1.25
115
1
4
4
0
-1
0
2147483647
0
0
0
0
115
1
4
2
0
0
0
500
115
5
0
604800
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
11
4
2
1
23874
0
933477557
4
2
1
24913
0
933693880
4
2
1
24922
0
934038362
4
2
1
22695
0
939063259
4
2
1
13170
0
942204916
4
2
1
24733
0
946738097
4
2
1
24987
0
947294765
4
2
1
9972
0
947639870
4
2
1
25050
0
950237881
4
2
1
25060
0
951034229
4
2
1
9972
0
952140843
115
1
0
55
115
5
0
0
115
4
4
2
2
Generic Attribute Object
2
gao
115
5
2
Attributes are not skills, but these objects are included both for resolving straight attribute rolls and for matching purposes.
115
5
4
2
0
2351
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#158
generic humanoid body area

16
115
-1
-1
-1
109
273
-1
1
include_with_core
2
173
-1
0
20
5
115
5
5
115
5
0
-47
115
5
0
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
2
2
generic humanoid body area
2
ghba
115
5
5
115
5
4
2
0
813
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#159
heart of man

16
2
-1
-1
-1
86
-1
-1
1
call
2
165
-1
1
max_disconnected_seconds
18
0
604800
115
1
0
0
2
1
4
0
2
1
5
2
5
5
2
5
1
159
2
5
1
62
2
5
1
-1
2
1
0
0
2
1
0
0
2
4
4
2
2
heart of man
2
hom
2
5
5
2
5
4
2
0
1134
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#160
ticker

16
2
-1
-1
-1
86
-1
159
0
0
17
0
0
2
1
4
0
2
1
5
2
5
5
2
5
1
160
2
5
1
62
2
5
1
-1
2
1
0
0
2
1
0
0
2
4
4
1
2
ticker
2
5
5
2
5
4
2
0
398
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#161
generic weapon message set

144
2
-1
-1
-1
1
292
109
11
severity_string
2
173
-1
swing miss dodge
2
173
-1
parry
2
173
-1
hit
2
165
-1
knockout
2
173
-1
announce
2
165
-1
upload
2
157
1
@view
2
37
-1
kill
2
173
-1
announce_swing announce_miss announce_dodge announce_parry
2
165
-1
announce_hit announce_kill announce_knockout
115
173
-1
11
severity_strings
swing
miss
dodge
parry
hit
kill
knockout
torso_kills
groin_kills
head_kills
20
4
6
2
minor
2
light
2
serious
2
severe
2
critical
2
mortal
2
5
4
1
2
%N %<swings> %p %t at %d.
2
5
4
1
2
%N %<misses/miss>.
2
5
4
1
2
%D %<d:dodges/dodge>.
2
5
4
1
2
%D %<d:parries> with %[dpp] %i.
2
5
4
1
2
%D %<d:receives> a %severity wound in the %l.
2
5
4
1
2
%N %<has> killed %d!
2
5
4
1
2
%D %<d:is> knocked unconscious by the blow.
2
5
4
0
2
5
4
0
2
5
4
0
2
5
0
0
2
4
4
1
2
generic weapon message set
2
5
2
A set of messages for a particular type of weapon.
2
5
4
2
0
10369
0
955495071
100
1
5
2
5
5
2
5
5
2
5
4
29
2
A combat message set is a repository of announcements peculiar to a certain weapon type.  A set contains the following messages by default:
2
 
2
Name           Default Value
2
 
2
swing     -- %N %<swings> %p %t at %d.
2
miss      -- %N %<misses/miss>.
2
dodge     -- %D %<d:dodges>.
2
parry     -- %D %<d:parries> with %[dpp] %i.
2
hit       -- %D %<d:receives> a %severity wound in the %l.
2
knockout  -- %N %<has> knocked %d unconscious!
2
kill      -- %N %<has> killed %d!
2
 
2
To quickly set up a newly created set, use `upload to <message set>' and you'll be prompted to enter a message for each of the above named properties.
2
 
2
The following special pronoun subs apply:
2
 
2
%N -- Attacker
2
%D -- Defender         (in `parry', the person who is parrying)
2
%T -- Attacker's Weapon
2
%L -- Body location
2
%I -- Opposing Weapon  (used only in `parry')
2
 
2
See `help pronouns' for more information on pronoun substitutions.
2
 
2
There is also a special sub %severity which translates to the severity of a successful attack.  This is, by default, one of "light", "serious", "severe", "critical", and "mortal".
2
 
2
To edit a message you can either `@edit <message-set>.<message-name>' or use the alternate form of upload, `upload <message-name> to <message-set>' and be prompted for input, each line a possible announcement for that message.
2
 
2
-HEMLOCK
2
5
5
115
1
#162
shotgun

144
2
-1
-1
-1
119
-1
-1
0
0
76
2
15mm Shot
2
5
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
0
0
2
1
5
2
5
5
2
5
5
2
5
2
%N %<slides> the pump on %p %t, ejecting a shell with a solid KaCHUNK!
2
5
5
2
5
5
2
5
5
115
1
2

2
5
2
%N %<swings> %p shotgun into %p grip and %<holds> it at the ready.
2
5
5
2
5
2
%N %<slings> %p %t over %p back.
2
5
5
2
5
2
%[tinamec] %<t:is> slung over %p back.
2
5
0
2
115
1
5
2
5
0
25
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
1
161
115
1
1
255
115
1
5
115
1
5
115
1
5
115
1
5
115
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
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
shotgun
2
gun
2
5
2
A menacing rifle with a 15mm bore.  A metal door in the breech swings inward to accept shotgun shells, and a sliding pump ejects and advances the ammo.
2
5
4
2
0
1737
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#163
generic chainsaw

144
36
-1
-1
-1
304
-1
-1
0
0
62
2
*VrrrrrrrrRRRRROOOOOOOOOMMM!* You start up your chainsaw and brandish it!
36
5
2
*VrrrrrrrrRRRRROOOOOOOMMM!* %N %<starts> up %p chainsaw, brandishing it dangerously.
36
5
2
You reluctantly hit the 'off' switch on the chainsaw. The whirling chain blade slows to a stop. You put the chainsaw away.
36
5
2
The loud buzzing comes to a halt as %N %<switches> %p chainsaw off.  %N %<puts> the chainsaw away.
36
5
2
%S %<is> swinging about a running chainsaw.
36
5
5
36
5
0
2
115
1
2
%N %<swings> %p %t restlessly from side to side.  Its mechanical roar resonates through the air and pounds dully against your ears.
36
5
0
-25
115
1
0
5
115
1
0
-20
115
1
0
-10
115
1
0
35
115
1
0
35
115
1
1
161
115
1
1
240
115
1
0
30
115
1
0
80
115
1
0
8
115
1
2
shred
115
1
5
36
5
5
36
5
5
36
5
5
36
5
2
Look out world-- %N has a chainsaw now.
36
5
5
36
5
5
36
5
5
36
5
5
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
1
2
chainsaw
36
5
2
This beauty has seen easier days but she's never seen better! Wield her in both hands and let her rip!
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#164
sledge hammer

144
2
-1
-1
-1
306
-1
-1
1
price
115
173
-1
0
62
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
0
2
115
1
5
2
5
0
-20
115
1
5
115
1
0
-15
115
1
0
-8
115
1
0
25
115
1
0
30
115
1
1
161
115
1
1
240
115
1
0
25
115
1
0
40
115
1
0
8
115
1
5
115
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
5
5
2
5
5
2
5
5
2
5
1
-1
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
3
2
sledge hammer
2
hammer
2
sledgehammer
2
5
2
A meter-shaft of sturdy wood holds a heavy iron block at its end.  The weight is superb for heavy crushing jobs.
2
5
4
2
0
1575
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#165
shotgun shells

144
2
-1
-1
-1
140
-1
-1
0
0
65
2
15mm Shot
115
1
0
-10
115
1
5
115
1
5
115
1
5
115
1
0
100
115
1
5
115
1
1
294
115
1
1
264
115
1
5
115
1
5
115
1
2
blast
115
1
5
101
1
2
shotgun shell
2
5
5
101
1
5
101
1
2
%(shotgun shells?%|shells?%)
101
1
5
101
1
1
165
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
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
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
shotgun shells
2
shells
2
5
5
2
5
4
2
0
1264
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#166
Reaping Feature

16
2
-1
-1
-1
74
-1
185
14
@reap
2
93
-2
is_morbidly_inactive
36
173
-1
is_immortal
36
173
-1
next_reapable
36
173
-1
do_show_info
2
173
-1
do_protect
2
173
-1
is_reapable
2
173
-1
do_kill
2
173
-1
do_skip
2
173
-1
init_for_core
2
173
-1
reap_hints
2
173
-1
feature_ok
36
173
-1
do_purge
2
173
-1
@sweap*!
2
13
-1
4
protected
reapable_age
skipped
default_reapability_factor
50
4
0
2
1
0
23328000
2
1
4
0
2
1
0
-1
36
1
1
-1
36
1
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
1
5
2
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
Reaping Feature
2
reapfo
2
5
2
Use @reap to obtain information on the next player up for reaping.
2
5
4
2
0
12398
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#167
The Midrealm

0
36
-1
-1
-1
175
-1
-1
0
0
41
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
1247877824
115
1
4
0
115
1
4
1
2
attack
36
5
5
36
5
0
0
36
5
5
36
5
5
36
5
5
36
5
4
0
36
5
1
115
36
5
4
0
36
4
1
-1
36
5
0
645765638
36
5
4
0
36
4
5
36
5
5
36
5
5
36
5
5
36
5
0
0
113
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
1
2
The Midrealm
36
5
2
You are floating in a cold, cold mist. This realm lies between the world of Life and the world of the Great Dreaming...
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#168
HELL (waiting room)

16
2
-1
-1
-1
175
-1
167
0
0
41
5
2
5
5
2
5
2
An ominous voice of doom bellows, "Thank you for visiting HELL.  We hope you enjoyed your DAMNATION."
2
5
2
%(inamec) appears, then promptly explodes into a thick cloud of noxious fumes.  Brimstone, perhaps?
2
5
2
Just as you grow secure in your ESCAPE FROM HELL, you explode.  A satanic James Earl Jones voice intones, "You cannot escape eternal damnation, mortal fool!  Fester in the bowels of Hell!  Howl as the devil-monkeys gnaw they flesh!  Feel thy skin pus and b--Huh?  What do you want, TOAD?"  He is interrupted by a meek whispering.  You find yourself back in %[tdname]...
2
5
2
The ominous voice returns.  "Ahrm.  Due to space considerations, your eternal damnation has been abbreviated to %[teng_time_left].  We hope you enjoy your stay in Hell."
2
5
5
2
5
5
2
5
0
702349586
115
1
4
0
115
1
5
2
5
5
2
5
0
0
2
5
5
2
5
5
2
5
5
2
5
4
0
2
5
1
115
2
5
4
0
2
4
1
-1
2
5
0
903825302
2
5
4
0
2
4
5
2
5
5
2
5
5
2
5
5
2
5
0
0
113
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
4
15
2
A fanged demon leaps from a rip in the continuum.  It stares hungrily at %n, then lunges forth and rips %p head off.
2
As %n %<attempts> to lie down for a rest, two iron spikes materialize in front of %p face.  %N %<gawks> in horror as they rush forward and pin %p eyelids to %p forehead.
2
As %n %<opens> %p mouth to yawn at the boredom of Hell, maggots pour from between %p lips.  A worm slides from %p nose and grows into a cobra, which promptly fangs %p eye as %s %<howls> in agony.
2
%N %<scratches> %p groin.  It explodes.
2
%N %<stubs> %p toe and %<blurts> out, "Christ!"  A horned devil crawls out of %p anus, swings between %p legs, and devours %p genitals!
2
In a flash of brilliant light, six representatives of the opposite sex appear around %n.  They fondly stroke %p naughty bits, exciting %o immensely.  Just as %s %<drops> %p guard (and pants) they bugger %o with a Garden Weasel(tm)!
2
%N %<attempts> to say something, but it seems %p mouth has turned into an anus.  You hear a muffled whining from %p backside.
2
%N %<explodes>.
2
%N %<begins> to tear up.  %S suddenly %<screams> in agony: %P eyelids have been turned into SANDPAPER!
2
%N %<howls> in terror as %p kneecaps explode.  %P elbows go next, then %p neck turns into a Slinky(tm).  %P head bobs onto the floor and cracks open, spilling maggots at %p feet.
2
A host of angels descends on %n.  %P face brightens with the glee of deliverance as they take %o in their arms and carry %o skyward.  Inexplicably, %s %<begins> muttering horrid obscenities about the Holy Father.  The angels eat %o alive!
2
%N %<bursts> into laughter and %<begins> shoving porcupines up %p ass.
2
%N %<sniffles> and %<rubs> %p nose.  A cold in Hell?  Wrong!  Magma flows from %p nostrils, reducing %p head to a lump of charred bone.
2
%N %<turns> a bit green around the gills.  (Didn't I mention %s grew gills?)  %P buttcheeks swell to the size of Zeppelins and EXPLODE!
2
A flash of light surrounds %n.  Two sledge hammers appear on either side of %p head, joined at the haft by a hinge.  You hear a SPROING as they slam inward and crush %p head like a melon.  Splat.
2
5
0
0
2
4
4
1
2
HELL (waiting room)
2
5
5
2
5
4
2
0
3811
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
0
-1
115
1
#169
replicator

144
2
-1
-1
-1
8
-1
-1
23
acceptable
2
173
-1
match_parent
2
173
-1
_replicate
2
173
-1
_destroy
2
173
-1
add_parent
2
165
-1
remove_parent
2
165
-1
menu
2
157
-2
tell_menu
2
173
-1
look_self
2
165
-1
do_noise
36
173
-1
replicate make copy create
2
157
3
replicate make copy create R2
2
157
0
replication_ok
36
173
-1
do_replication
2
173
-1
destroy disintegrate
2
157
3
destroy disintegrate D2
2
157
0
disintegration_ok
2
173
-1
do_disintegration
2
173
-1
do_autoclose
36
173
-1
owned_by_player
2
173
-1
_purge
2
173
-1
clean
36
41
-1
init_for_core
2
173
-1
6
parents
look_self_menu
owned_by_player
alt_owner
trash_bin
ocleanout_msg
68
4
0
2
1
0
1
2
1
0
1
2
5
1
125
2
1
1
-1
36
1
2
%N %<takes> %d from %[tdname] and %<stores> %[dpo] out of sight.
36
1
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
5
5
2
5
5
2
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
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
1
2
replicator
2
5
4
10
2
A distant relative of the microwave oven, this improved version does not require that you put anything IN.  Just punch in your order, close the door, wait a moment, pop it open, and voila!  Instant merchandise.
2
DISCLAIMER: Replicated material is guaranteed in no way.  Any malfunctions, accidents, or mutilations are the responsibility of the operator and not of Dakirion Industries.  Enjoy your replicator!
2

2
POSSIBLE USES OF THE REAL/TIME REPLICATOR:
2

2
  open replicator                       close replicator
2
  get <new name> from replicator        put <item> in replicator    
2
  create <parent> with replicator       destroy <item> in replicator
2

2
     Type `menu on replicator` for a list of available items.
2
5
4
2
0
15922
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#170
generic portable room

144
2
-1
-1
-1
177
-1
-1
4
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
get take
2
45
-1
drop
2
45
-1
look_self
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 %[tdname] here.
2
5
2
You drop %[tdname].
2
5
2
%N %<tries> to drop %[tiname] but fails!
2
5
2
%N %<drops> %[tiname].
2
5
2
%N %<picks> up %[tiname].
2
5
2

2
5
2
You take %[tdname].
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
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
1
-1
2
1
5
2
5
5
2
5
1
178
2
5
5
2
5
5
2
5
5
115
1
4
0
115
1
5
2
5
5
2
5
0
0
2
5
5
2
5
5
2
5
5
2
5
4
0
2
5
1
115
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
0
0
113
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
generic portable room
2
gpr
2
5
2
A $room which you can get and take, just like a $thing.  Perfect parent for generics such as a tent or an "instant-home".  Take/drop messages can describe setting up and disassembling.
2
5
4
2
0
4671
0
955495071
100
1
5
2
5
5
2
5
0
1
2
5
5
2
5
5
115
1
#171
Limbo

16
36
-1
-1
-1
82
-1
142
0
0
12
1
115
36
5
5
36
5
5
36
5
0
0
36
4
4
1
2
Limbo
36
5
2
A shapeless, colourless void in which swim the topological bastards of this glorious Reality.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#172
Internet Word Findin' FO

16
36
-1
-1
-1
74
-1
211
23
is_busy
36
173
-1
register_request reregister_request
36
173
-1
net_open net_close
2
173
-1
net_read
2
173
-1
net_write
2
173
-1
@def*ine @paste-def*ine @webster @paste-webster
36
93
-2
@word @paste-word
36
93
-2
finish_request
36
173
-1
explode
2
173
-1
word_query_raw
2
173
-1
word_query
2
173
-1
cache_add
36
173
-1
validate_cache
36
173
-1
cache_get
36
173
-1
render_markup
36
173
-1
allow_query_from
36
173
-1
ospd_query
36
165
-1
init_for_core
2
173
-1
@word-cache @popword
36
13
-1
perform_word_check
36
173
-1
cache_word_result
36
173
-1
fetch_word_result
36
173
-1
@dialect
36
93
-2
14
pending
max
scrabble_server
last_used
define_request
define_regions
define_credit
cache
cache_entries
debug
word_server
word_cache
word_cache_top
dialect
60
4
0
36
1
0
5
36
1
4
2
2
requiem.vv.com
0
2345
36
1
0
0
36
1
4
3
2
www.m-w.com
0
80
4
2
2
GET /cgi-bin/netdict?va=$word HTTP/1.0
2

36
5
4
2
2
%(Main Entry:.+$%)
2
</form>
36
5
4
3
2

2
*** Definition (c) Merriam-Webster, Incorporated.  Visit http://www.m-w.com/.
2

36
1
4
0
36
1
0
48
36
5
0
0
36
1
4
2
2
fazigu.org
0
2345
36
1
4
2
4
99
2
YOD
2
CANTY
2
TAU
2
HOLT
2
NAIVETY
2
BLIPS
2
CEL
2
FOH
2
AGEE
2
CHAD
2
PE
2
AE
2
PUCES
2
OE
2
KA
2
HAYS
2
DIAGNOSE
2
DISCARD
2
LITE
2
JIG
2
BORT
2
ER
2
SPEWER
2
LENIENT
2
WIENER
2
QUA
2
FENNEL
2
WIDES
2
RUTS
2
DAZE
2
ZAX
2
LO
2
LORN
2
VOX
2
GENOME
2
LAX
2
DEEMING
2
AN
2
PEHS
2
HA
2
EL
2
BAIL
2
DURNS
2
DURN
2
CUING
2
BATING
2
TUNING
2
MORT
2
GOALIE
2
BARF
2
NO
2
FOND
2
BAD
2
FA
2
TABOO
2
OBOE
2
QUOD
2
JOE
2
AVE
2
NOR
2
LASCIVIOUS
2
FON
2
BEAMY
2
FACILE
2
PUMMELLED
2
LI
2
ROC
2
TUTTI
2
TUTU
2
SIGNIFICANCE
2
CARET
2
HEAVER
2
CAM
2
GNASH
2
GAIT
2
UNTIMELY
2
HOD
2
RAVER
2
THO
2
AVER
2
REV
2
CRISES
2
NU
2
CAMOMILE
2
LOO
2
NAE
2
REE
2
JEE
2
KOAS
2
BEGS
2
KOA
2
OAK
2
KOAN
2
MINK
2
KEF
2
INKJET
2
KEN
2
INKIER
2
PANE
4
100
2
WOB
2
GOUDA
2
DOY
2
HENA
2
HOLTY
2
YAX
2
CAX
2
HAX
2
UD
2
DU
2
BADI
2
BARI
2
BLOPS
2
STOVED
2
VOCE
2
HAF
2
HOF
2
FAH
2
PEEF
2
FEEP
2
UG
2
GU
2
PUCEY
2
PUCY
2
PUCER
2
EP
2
EA
2
GE
2
EG
2
PUCED
2
ADION
2
ADOIN
2
EO
2
KE
2
AK
2
IIOA
2
OAKES
2
SOIKE
2
OKIES
2
LITES
2
LITEAS
2
PINGE
2
PLINE
2
JIV
2
RO
2
BAVON
2
BAVIN
2
BAVI
2
AV
2
VA
2
VORT
2
VART
2
VARIT
2
RIVA
2
RIVAT
2
IDE
2
LEANIENT
2
LEANIANCY
2
LENIANCY
2
UA
2
AU
2
OU
2
UO
2
OO
2
ZAXE
2
LN
2
PETUL
2
LIRTS
2
ILTS
2
TRILIS
2
TILIS
2
TRILS
2
WIDET
2
JIM
2
JEM
2
XAZE
2
XAZ
2
OL
2
ALORN
2
XUN
2
NUX
2
XON
2
XAN
2
NAX
2
NOX
2
VAX
2
LORAX
2
SA
2
SAN
2
ASIN
2
SAZ
2
ZAS
2
ZA
2
ZAN
2
ZN
2
AZ
2
RX
2
THEP
2
SHEP
2
EI
36
1
4
2
0
100
0
100
36
1
4
15
2
OI
2
FOO
2
DA
2
EW
2
SPAM
2
SPAMMER
2
SPAMMING
2
SPAMS
2
SPAMMY
2
SPAMMIER
2
FLUFFER
2
TERRAN
2
TWINK
2
TWINKS
2
ARGH
36
1
1
-1
36
1
4
2
2
@define
2
@ospd
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
Internet Word Findin' FO
2
IWFFO
36
5
2
A feature which allows you to define words and check possible Scrabble plays in the official OSPD dictionary.  All through the magic of MOO<->Internet.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#173
generic bioscanner

144
115
-1
-1
-1
5
-1
194
11
look_self
115
165
-1
type enter
115
157
3
read
115
45
-1
stat_text
115
173
-1
injury_adj*ective
115
173
-1
scan
115
157
0
new_subject
115
173
-1
do_scan_all
115
173
-1
tell_screen
115
173
-1
wts_to_ansi
115
173
-1
init_for_core
2
173
-1
1
screen
43
4
0
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
4
0
115
4
4
0
115
4
1
-1
36
1
5
115
5
5
115
5
0
0
115
4
4
3
2
generic bioscanner
2
scanner
2
bioscanner
115
5
2
A handheld datapad, with alphanumeric entry and a crystal clear holomatrix display.
115
5
4
2
0
9922
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#174
rerolls

16
2
45
-1
-1
44
-1
-1
1
is_usable_by is_readable_by
115
173
-1
0
26
4
0
2
5
0
0
36
1
4
0
36
0
4
1
1
1991
2
5
2
%n (%#) can't send to moderated list %t (%[#t]) directly.
2
5
4
0
2
5
0
1
2
5
4
0
36
1
4
0
36
1
0
2592000
2
5
0
0
36
1
4
0
36
0
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
rerolls
36
1
2
Really only a log of sheets reset as a result of @rerolls.
2
5
4
2
0
1153
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#175
generic afterlife room

144
115
-1
-1
-1
132
168
302
6
sentence_for
115
173
-1
exitfunc
2
173
-1
receive_dead
115
173
-1
time_left_for
115
173
-1
eng_time_left
115
173
-1
send_user_home
2
173
-1
6
default_sentence
delivery_msg
release_msg
opreyank_msg
preyank_msg
postyank_msg
41
0
60
115
5
2
You catch the horrid visage of some mangled being out of the corner of your eye.  Turning, you see %(iname,fit and healthy).
115
5
2
Your time in the afterlife served, you return to the realm of the living.
115
5
2
%(inamec) appears, then promptly fades into a dispersing haze of diffused light.
115
5
2
You feel yourself being pulled back into the realm of the dead...
115
5
2
You get the feeling it's not yet time for you to return to the realm of the living.  Maybe in %[teng_time_left]...
115
5
5
115
5
5
115
5
5
115
1
4
0
115
1
5
115
5
5
115
5
0
0
115
5
5
115
5
5
115
5
5
115
5
4
0
115
5
1
115
115
5
5
115
4
5
115
5
5
115
5
5
115
4
5
115
5
5
115
5
5
115
5
5
115
5
0
0
113
1
4
0
115
4
4
0
115
4
1
-1
36
1
5
115
5
4
0
115
5
0
0
115
4
4
2
2
generic afterlife room
2
afterlife
115
5
2
A pristine office complex with all the amenities of a modern afterlife.  The paint job is a bit garish, though: very hot pink.
115
5
4
2
0
4674
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
0
1
115
1
#176
generic bulletin board

144
36
-1
-1
-1
5
-1
138
19
post_info
36
173
-1
write post
36
157
4
process_post_args
36
173
-1
readable_text
36
173
-1
add_post
36
173
-1
clear_posts
36
173
-1
erase clear
36
45
-1
message_is_deletable_by
36
165
-1
delete_post*s
36
173
-1
delete remove
36
157
5
parse_msg_seq*uence
36
173
-1
scan_text
36
173
-1
read_text
36
173
-1
read read_all
36
45
-1
scan scan_all
36
45
-1
read read_on
36
157
4
scan scan_on
36
157
4
new_post_num
36
173
-1
last_post_num
36
173
-1
14
post_msg
opost_msg
delete_msg
odelete_msg
read_msg
oread_msg
failed_post_msg
post_info
erase_msg
oerase_msg
ofailed_delete_msg
scan_msg
oscan_msg
new_post_num
56
2

36
5
2
%N %<pulls> a slip of paper from %[tdname], %<scribbles> a note with the attached pencil, and %<tacks> it to the cork.
36
5
2

36
5
2
%N %<pulls> a tack from one of the slips of paper on %[tdname].  %S %<crumples> the paper into a ball and %<tosses> it over %p shoulder.  At the apex of its flight, the dead message disintegrates in a flash of light.
36
5
2

36
5
2
%N %<stands> before %[tdname], lips moving silently as %s %<reads> a message.
36
5
2
That's sad.  You don't know how to write?  Try `write text on %t`.
36
5
4
0
36
0
2

36
5
2
%N %<waves> %p hand in front of %[tdname].  The attached messages disappear under the arc of %p gesture.
36
5
2
%N %<yanks> at one of the messages on %[tdname], but it doesn't budge.
36
5
2

36
5
2
%N %<stands> before %[tdname], scanning its contents with interest.
36
5
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
generic bulletin board
2
gbb
36
5
2
A plenty magical corkboard with an inexhaustible supply of tacks and paper slips.  Post, damn you!  Post!
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#177
generic located room

144
2
-1
-1
-1
132
170
175
17
set_description
2
173
-1
description
2
173
-1
invoke
2
165
-1
outside_name*c outside_dname*c outside_iname*c outside_title*c
2
173
-1
arrive_msg oarrive_msg leave_msg oleave_msg nogo_msg onogo_msg arrive_from_inside_msg oarrive_from_inside_msg leave_from_inside_msg oleave_from_inside_msg nogo_from_inside_msg onogo_from_inside_msg
2
173
-1
move
2
173
-1
set_hatch
2
173
-1
enter board
2
45
-1
exit leave out
2
13
-1
look_person_msg
2
173
-1
look_place_msg
2
165
-1
tell_exits
2
173
-1
tell_contents
2
173
-1
do_move
2
173
-1
integrated_description
2
173
-1
acceptable
36
173
-1
init_for_core
2
173
-1
17
inside_description
arrive_msg
oarrive_msg
leave_msg
oleave_msg
nogo_msg
onogo_msg
arrive_from_inside_msg
oarrive_from_inside_msg
leave_from_inside_msg
oleave_from_inside_msg
nogo_from_inside_msg
onogo_from_inside_msg
hatch
look_place_msg
look_person_msg
campground
52
2
This is the inside of the generic located room.  You can describe the inside with the @describe command, and it will automatically be stored in inside_description.  If you leave the room, a @desc will describe the outside.
2
5
2

2
5
2
%N %<arrives> from outside.
2
5
2

2
5
2
%N %<walks> inside %[tdname].
2
5
2

2
5
2
%N %<attempts> to enter %[tdname], but can't get in.
2
5
2

2
5
2
%N %<arrives> from inside %[tdname].
2
5
2

2
5
2
%N %<exits> out to %[toutside_dname].
2
5
2

2
5
2
%N %<attempts> to exit %[tdname], but can't get out.
2
5
1
-1
2
1
2

2
5
2

2
5
1
178
2
5
5
2
5
5
2
5
5
115
1
4
0
115
1
5
2
5
5
2
5
0
0
2
5
5
2
5
5
2
5
5
2
5
4
0
2
5
1
115
2
5
5
2
4
1
2
2
5
0
1439625989
2
5
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
0
0
113
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
generic located room
2
glr
2
5
2
A room meant to be located inside other rooms, or perhaps even inside a person.  Interesting notion, eh?
2
5
4
2
0
11035
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#178
Interdimensional Campground

0
2
-1
-1
-1
132
-1
-1
0
0
35
5
2
5
5
2
5
0
87812821
115
1
4
0
115
1
5
2
5
5
2
5
0
0
2
5
5
2
5
5
2
5
5
2
5
4
0
2
5
1
115
2
5
5
2
4
1
-1
2
5
0
851671995
2
5
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
0
0
113
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
1
2
Interdimensional Campground
2
5
2
Little huts and cardboard boxes fill the spaces between phantom Winnebagos, active volcanoes, and stately manors.  All are palaces in the minds of their owners, and now your own home is amongst them.  In the future, be sure not to plunk your portable palace in public places.  @go to leave.
2
5
4
2
0
1043
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
0
-1
115
1
#179
generic bodily weapon

144
2
-1
-1
-1
93
303
94
1
parry_with
115
173
-1
0
54
0
5
115
1
5
115
1
0
-1000
115
1
5
115
1
5
115
1
5
115
1
1
161
115
1
1
240
115
1
5
115
1
5
115
1
0
1
115
1
5
115
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
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
3
2
generic bodily weapon
2
natural weapon
2
gbw
2
5
2
When without a weapon, CLOBBER `EM.
2
5
4
2
0
1716
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#180
taskmaster

16
2
-1
-1
-1
1
-1
187
4
init_for_core
2
173
-1
keepalive
2
173
-1
prod kick
2
45
-1
do_prod_for
2
173
-1
3
keepalive_task
keepalive_interval
task_data
12
0
1143926775
2
0
0
300
2
0
4
0
2
0
0
0
2
4
4
1
2
taskmaster
2
5
2
An object whose purpose is to keep other tasks alive.  Anyone may `prod $taskmaster` to send it on a round.  Additionally, $do_command attempts to run $taskmaster:keepalive() and random intervals, to keep this task itself alive.;
2
5
4
2
0
3155
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#181
Builder Help

16
36
-1
-1
-1
30
-1
-1
0
2
@create
event-types
12
4
12
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
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
17
2
A list of current events (hee):
2

2
Name     Args                          Explanation
2
----     ----                          -----------
2
speech   speaker, text, volume         Someone has spoken.
2
sound    speaker, text, volume         A non-speech sound.
2
motion   actor, text, obviousness      Someone/thing has acted/emoted.
2
exit     mover, exit-used              Someone/thing has left the room.
2
entrance mover, entrance-used          Someone/thing has entered the room.
2
attack   attacker, defender            Attacker has attacked defender.
2
heal     healer, patient, kit, result  Healer tried to heal patient with kit.
2
death    victim, attacker              Attacker killed victim.
2
knockout victim, attacker              Attacker knocked victim unconscious.
2
sit      sitter, furniture             Sitter sat on furniture.
2
recline  sitter, furniture             Sitter reclined on furniture.
2
stand    sitter, furniture             Sitter stood from furniture.
2
spell    caster, target, spellname     Caster cast spellname on target.
36
1
5
36
5
0
0
36
4
4
2
2
Builder Help
2
bh
36
5
2
Help for that intermediate class between players and programmers.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#182
generic communicator

144
36
-1
-1
-1
5
-1
98
46
input
36
165
-1
output
36
173
-1
moveto
36
165
-1
x*mit transmit
36
85
-2
match_channel channel_match_failed
36
173
-1
switch tune
36
101
1
change_channel
36
165
-1
feature_ok
36
173
-1
is_direct_link
36
173
-1
dial call
36
93
-2
match_comm*unicator
36
165
-1
comm_match_failed
36
173
-1
set_incoming set_outgoing
36
173
-1
answer
36
41
-1
initialize
36
165
-1
_disconnect
36
173
-1
hangup*!
36
45
-1
user
36
173
-1
_connect
36
173
-1
active_channel*s
36
173
-1
is_active_channel
36
173
-1
add_active_channel
36
173
-1
remove_active_channel
36
173
-1
is_receiving_call
36
173
-1
output_format_msg
36
173
-1
dial_msg odial_msg
36
173
-1
username
36
173
-1
approved
36
173
-1
incoming_msg outgoing_msg
36
173
-1
is_busy
36
173
-1
speak
36
149
3
call_in_progress
36
173
-1
tell_description
36
173
-1
callnum*bers
36
173
-1
callname
36
173
-1
lookup
36
157
-2
press push
36
157
4
lookup_user
36
173
-1
tell_listeners
36
165
-1
connect_to
36
173
-1
disconnect_from
36
173
-1
omumble_msg speak_msg ospeak_msg
36
173
-1
do_incoming_notification
36
173
-1
update_user
36
173
-1
refuse
36
157
0
refusing_calls
36
173
-1
21
channel_server
root
incoming
outgoing
incoming_msg
outgoing_msg
output_format_msg
ospeak_msg
otune_msg
ohangup_msg
dial_msg
odial_msg
active_channels
opanic_msg
panic_msg
omumble_msg
speak_msg
oincoming_msg
ring_interval
user
refusals
63
1
544
36
5
1
545
36
1
1
-1
36
1
1
-1
36
1
2
(%t:/%[dcallname]/%n) *BEEP* (Incoming Call)
36
5
2
(%t:/%[dcallname]/%n) Ringing $number...
36
5
2
(%t:/%[dcallname]/%n) $text
36
5
2
%N %<says> "$text" into %p %t.
36
5
2
%N %<switches> the dial on %p %t.
36
5
2
%N %<pushes> a button on %p %t.  %[tpsc] %<t:gives> a short beep, then %<t:goes> silent.
36
5
2
You key in $number and wait for a ring.
36
5
2
%N %<keys> in a call request on %p %t.
36
5
4
0
36
1
2
%N %<slams> the PANIC button on %p %t!
36
5
2

36
5
2
%N %<says> something into %p %t.
36
5
2
You say, "$text" into your %t.
36
5
2
From %l, something emits a sharp beep.
36
5
0
5
36
5
1
-1
36
1
4
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
generic communicator
2
communicator
36
5
4
7
2
A sleek black communicator.  There is a series of buttons used to signal a spoken command, and a small dial for scanning frequencies.  Empty jacks hint at available attachments.
2
                                       switch/dial comm to <anything>
2
                                       dial/call <anything> with comm
2
                                       answer comm
2
                                       hang*up comm
2
                                       x*mit <anything>
2
                                       lookup <user> with comm
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#183
channel server

16
2
-1
-1
-1
5
-1
182
5
match_channel
2
173
-1
title
2
173
-1
channel_match_failed
2
173
-1
accept
2
173
-1
init_for_core
2
173
-1
1
generic_channel
43
1
184
2
5
5
2
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
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
channel server
2
server
2
5
2
A compact box dotted with flashing lights which makes a low whirring sound.  An antenna collects info from sources unknown.
2
5
4
2
0
3200
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#184
generic channel

144
36
-1
-1
-1
1
-1
86
14
subscribes
36
173
-1
input
36
173
-1
approved
36
173
-1
add_subscriber
36
173
-1
remove_subscriber
36
173
-1
_connect
36
173
-1
_disconnect
36
173
-1
callname
36
173
-1
callnum*bers
36
173
-1
output
36
173
-1
do_channel_command
36
173
-1
cmd_who
36
173
-1
online_subscribers
36
173
-1
init_for_core
2
173
-1
1
subscribers
10
4
0
36
0
0
0
36
4
4
2
2
generic channel
2
channel
36
5
2
A generic channel of communications.  It's an abstraction.  You really shouldn't see it at all.  Look the other way.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#185
Species Feature

16
2
-1
-1
-1
74
-1
193
7
init_for_core
2
173
-1
trusted
115
173
-1
feature_ok
115
173
-1
match_species
115
173
-1
set_character_species
115
173
-1
unset_character_species
115
173
-1
@species
115
93
12
0
46
1
-1
36
1
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
1
5
2
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
Species Feature
2
spefo
2
5
2
A feature to ease manipulation of character species.
2
5
4
2
0
4629
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#186
generic shared player class

16
2
-1
-1
-1
57
-1
295
2
init_for_core
2
173
-1
confunc
2
173
-1
3
users
last_user
toggle_progbit
188
4
0
2
1
1
-1
2
0
0
0
2
1
4
0
36
1
0
1
2
5
0
0
2
5
5
2
5
0
48
2
5
0
0
2
1
4
0
2
0
5
2
5
5
36
0
5
36
1
5
2
0
5
2
5
5
2
0
5
2
1
0
0
2
5
5
2
5
5
36
1
5
36
1
4
0
36
0
5
36
1
5
2
5
4
0
2
1
5
2
4
4
0
2
0
5
2
0
4
0
2
4
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
4
4
0
2
4
5
2
1
5
100
0
5
2
0
5
2
5
5
2
1
4
0
115
1
5
2
5
1
159
2
5
5
100
1
5
2
0
5
2
5
5
2
1
5
36
1
5
2
5
5
115
1
4
0
36
0
5
2
5
5
115
1
5
115
1
5
36
0
5
115
1
5
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
2
5
5
115
1
5
115
1
1
206
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
1
61
2
5
5
2
5
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
115
1
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
4
5
2
5
5
2
4
5
2
5
5
115
0
5
115
1
5
36
0
5
115
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
5
2
4
5
2
4
5
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
generic shared player class
2
shpc
2
1
2
A player shared by multiple users.  Authorized users may login to the character via the passwords of their regular characters, and the shared player attempts to mimic their personal settings.
2
5
4
2
0
4535
0
955412150
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#187
Species Object

144
2
-1
-1
-1
1
202
192
13
init_for_core
2
173
-1
trusted
115
173
-1
check_eligibility
115
173
-1
new_body_areas_for
115
173
-1
new_natural_weapons_for
115
173
-1
convert_character
115
173
-1
revert_character
115
173
-1
new_features_for
115
173
-1
new_natural_protection_for
115
173
-1
tell_stats
115
173
-1
new_hands_for
115
173
-1
remove_features_from
2
173
-1
add_features_to
2
173
-1
6
interface
body_areas
natural_weapons
features
natural_protection
hands
15
1
185
115
1
4
0
2
5
4
0
2
5
4
0
2
5
0
2
2
5
4
0
2
5
0
0
2
4
4
3
2
Species Object
2
species
2
race
2
5
2
An object describing a certain humanoid species of life form.
2
5
4
2
0
8859
0
955412150
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#188
Generic Effect

144
115
-1
-1
-1
1
-1
191
11
init_for_core
115
173
-1
build_integration_msg
115
173
-1
added
115
173
-1
removed
115
173
-1
pulsed
115
173
-1
earg_get
115
173
-1
earg_set
115
173
-1
_setup_removal
115
173
-1
_cancel_removal
115
173
-1
scheduler_message jscheduler_message
115
173
-1
earg_delete
115
173
-1
0
9
0
0
115
4
4
2
2
Generic Effect
2
effect
115
5
2
An object which acts upon other objects over a certain period.
115
5
4
2
0
10369
0
955412154
100
1
5
115
5
5
115
5
5
115
5
4
68
2

2
"Effects" are a fairly simply interface allowing one to control an object's virtuality over a certain period of time.  A simple example is a 'flaming' effect.  When someone catches fire, they take damage and their description reflects their burning state.  When the fire is doused, they are free of the flame -- they stop taking damage and it is removed from their description.
2

2
To make your own effects, @create a child of $effect, overriding its interface methods (described below) with your own custom code.  Then, add it to an object.  The :add_effect method is defined on the $tangible class, from which all objects existing in the MOO are descended.  The syntax for adding an effect is as follows.
2

2
===== Affecting Someone
2

2

2
:add_effect(OBJ effect, LIST eargs)
2

2
'eargs' is a list which will be passed as the first argument to the methods on your 'effect'.  Most of them return this list (possibly after modifying it).  Use eargs to modify the quality or quantity of your effects.
2

2
The :add_effect method returns a unique identifier with which you can reference and remove this specific effect in the future.  To remove the effect, pass that identifier to :remove_effect.
2

2
:remove_effect(INT id)
2

2
===== The Effect of your Effect
2

2
Before adding your effect to anything, you'll want to give it some SOUL.  Do that by writing your own methods to override its interfaces.  Their descriptions follow.
2

2
:build_integration_msg(LIST eargs, OBJ affected)
2

2
Return a list describing the effect the object has on its host.  This must always return a list, even if it is an empty one.
2

2
Example:
2
return {$string_utils:pronoun_sub("%N %<is> on fire!  BURN, BABY, BURN!", affected)};
2

2
:added(LIST eargs, OBJ affected, INT id)
2

2
Initialize the effect.  The extra argument 'id' will be the unique identifier for this effect.
2

2
Example:
2
"Boost strength by 10!";
2
affected:mod_stat("strength", 10);
2

2
:removed(LIST eargs, OBJ affected)
2

2
Remove the effect.
2

2
Example:
2
"Return strength to normal.";
2
affected:mod_stat("strength", -10);
2

2
:pulsed(LIST eargs, OBJ affected)
2

2
Take advantage of the affected object's pulse and do something.
2

2
Example:
2
"Add 10 points of 85% lethal fire damage.";
2
affected:receive_injury(10, 85);
2

2
Additionally, $effect provides some methods for manipulating an 'eargs' list.  They expect it to be in an associative list format (eg. {{key1, val1}, {key2, val2}}).
2

2
:earg_get(LIST eargs, ANY key)
2

2
Return the value associated with 'key' in 'eargs', or raise the error E_VARNF if none.
2

2
:earg_set(LIST eargs, ANY key, ANY value)
2

2
Set the value associated with 'key' in 'eargs' to 'value'.  Add the keyed value if it does not exist.  Return the modified eargs list.
2

2
:earg_delete(LIST eargs, ANY key)
2

2
Remove the 'key' association from 'eargs', or raise E_VARNF if none found.
2

2
Some effects may accept special eargs values.   Generically, $effect:added and $effect:removed do.  If you send a "duration" key in your eargs list, :added will set up a task to remove the effect in that many seconds.  This obviates the need to set up your own task to remove a timed effect.  (:removed will cancel the queued removal in case it was removed prematurely.)
2

2
Those are the basics!  This simple interface should allow for a variety of cool/cruel effects to be gleefully inflicted upon the players.  Enjoy!
115
5
5
115
1
#189
Janus' scheduler

16
36
-1
-1
-1
307
-1
-1
9
add_to_queue
36
173
-1
remove_from_queue
36
173
-1
run
36
173
-1
cleanup_queue
36
173
-1
@jsstatus @jstatus
36
29
-1
@jscancel @jcancel
36
29
-1
queued_tasks
36
173
-1
valid_task task_valid
36
173
-1
init_for_core
2
173
-1
7
queue_users
queue_tokens
queue_data
queue_task
queue_times
min_queue_delay
queue_info
59
4
3
1
21608
1
17889
1
2990
36
0
4
3
0
1358863233
0
459580917
0
1247724952
36
0
4
3
4
0
4
0
4
0
36
0
0
268417821
36
0
4
3
0
955496127
0
955498129
0
955498129
36
0
0
5
36
1
4
3
4
6
1
21608
2
schedule_next_event
1
6143
1
11300
1
2
0
1
4
6
1
17889
2
reset_usage
1
3597
1
7083
1
2
0
7
4
6
1
2990
2
reset_usage
1
3597
1
7083
1
2
0
7
36
0
5
36
1
4
12
1
3597
1
6143
1
3791
1
5288
1
20332
1
11812
1
1610
1
20348
1
3746
1
2
1
23011
1
24841
36
1
5
36
1
5
36
1
5
36
1
5
36
1
1
-1
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
Janus' scheduler
2
jscheduler
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
4
21
2
Just my miserable attemps at doing a public and documented scheduler.
2

2
Commands:
2

2
@status [OBJ <object or user>] on <scheduler>
2
=> more or less like @forked, will list the tasks started by objects owned by <object or user>.
2

2
@cancel INT <queue token/id> on <scheduler>
2
=> cancel/kill  <queue token/id> if you own it. (Note will extend this command to have all the options of @kill if need arise.)
2

2

2
Using the scheduler in your verbs:
2
1) you will to: @verb <your obj>:jscheduler_message tnt rxd
2
2) @program that verb to do whatever you want, it will get called by the scheduler when you want. (see bellow :add_to_queue)
2

2
:add_to_queue(INT <delay> [, ANY <arg1>, ANY <arg2>, ...])
2
=> INT <token>
2
The object calling <scheduler>:add_to_queue will have its :jscheduler_message verb called in <delay> seconds with the arguments <arg1>, <arg2>, ...
2

2
:remove_from_queue(INT <token>)
2
=> INT <token> if successful, 0 if the task was not in the queue.
36
5
5
115
1
#190
Character Editor

16
115
-1
-1
-1
49
-1
-1
33
init_for_core
2
173
-1
e*dit
115
25
-1
save
115
73
-2
working_on
115
173
-1
init_session
115
173
-1
parse_invoke
115
173
-1
fetch_stats
115
173
-1
apply_stats
115
173
-1
_mod_stat
115
173
-1
_set_stat
115
173
-1
_get_stat
115
173
-1
boost
115
89
-2
first_time
115
93
0
_is_attribute
115
173
-1
_is_skill
115
173
-1
trade sacrifice
115
89
11
loaded
115
173
-1
list stats print
115
89
-2
kill_all_sessions
2
173
-1
kill_session
2
173
-1
reset_session
2
173
-1
forbid_stat_boost
115
173
-1
_match_stat
115
173
-1
conversion_rate
115
173
-1
stat_match_result
115
173
-1
description
115
173
-1
charge_pot
115
173
-1
all_uncoded
115
173
-1
match_uncoded
115
173
-1
uncoded_match_result
115
173
-1
uncoded_match_failed
115
173
-1
retain_session_on_exit
115
173
-1
say emote
115
93
-2
4
objects
stats
effigy
attribute_cost
55
4
7
0
0
0
0
0
0
0
0
0
0
0
0
0
0
115
0
4
7
0
0
0
0
0
0
0
0
0
0
0
0
0
0
115
0
1
5777
115
5
0
10
115
1
4
7
0
0
0
0
0
0
0
0
0
0
0
0
0
0
36
1
4
7
0
945574370
0
949300639
0
949519056
0
950490725
0
950506593
0
951438783
0
954448746
36
1
5
115
5
5
115
5
2
No stats.  (This should never happen.)
115
5
4
2
4
2
2
boost
2
<stat> by <points>
4
2
2
trade
2
<stat> for [points] <stat>
115
5
0
0
36
1
5
115
5
2
You need to either SAVE or ABORT this verb before you can start on another.
115
5
4
6
4
2
2
objects
0
0
4
2
2
stats
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
%N %<clutches> a brain and thrusts it high above %p head, mouth wide with a maniacal grin.  %S %<laughs> madly, screaming, "IT'S ALIVE!  ALIVE!" as %s %<is> engulfed in a purple haze of abstractions.  [%N %<goes> off to edit character stats.]
115
5
2
Wispy tendrils of purple mist entangle the air around you.  Through the haze steps %n, face beaded with sweat, chest heaving.  %S %<drops> a brain to the floor with a wet splat, grinning at you with a mad gaze.  [%N %<returns> from editing character stats.]
115
5
4
3
2
Keeping this character for later work.  
2
To return, give the `@edit' command with no arguments.
2
Please come back and SAVE or ABORT if you don't intend to be working on this character in the immediate future.  Keep Our MOO Clean!  No Littering!
115
5
2
The character has no pending changes.
115
5
2
You have changed the character since last successful save.
115
5
2
First, you have to select a character to edit with the EDIT command.
115
5
4
7
0
0
0
0
0
0
0
0
0
0
0
0
0
0
36
0
4
7
1
17488
1
3968
1
24669
1
15378
1
434
1
24949
1
23360
36
1
4
7
0
0
0
0
0
0
0
0
0
0
0
0
0
0
36
0
4
7
0
1
0
1
0
1
0
1
0
1
0
1
0
1
36
0
4
7
1
15248
1
3407
1
3407
1
3407
1
3407
1
3407
1
3407
36
1
2
%L [editing characters]
115
5
0
0
115
5
5
115
5
5
115
5
5
115
5
4
0
115
5
0
1
115
5
5
115
4
5
115
5
5
115
5
5
115
4
5
115
5
5
115
5
5
115
5
5
115
5
0
0
113
1
4
0
115
4
4
0
115
4
1
-1
36
1
5
115
5
5
115
5
0
0
115
4
4
2
2
Character Editor
2
chareditor
115
5
4
24
2

2
stats|list|print
2
     Show the stats you've edited so far.
2

2
boost <stat> by <points>
2
     Boost skills (1:1) or attributes (10 points)
2
     with your potential points.
2

2
trade <stat> for [points] <stat>
2
     Trade skills for skills (1:1), attributes for attributes (1:1),
2
     or 10 skill points for one attribute point.  You
2
     can only trade stats after a @reroll, before your first save.
2

2
save
2
     Save edited stats to the character you're editing.
2

2
abort
2
     Abort all changes.
2

2
quit
2
     Leave the editor.  All changes will be lost.
2

2

2
More help is available by typing `help <commandname>`.
115
5
4
2
0
24201
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#191
generic uncoded attribute

16
115
-1
-1
-1
1
-1
200
5
init_for_core
2
173
-1
check_learn
115
173
-1
check_advance
115
173
-1
forbid_level
115
173
-1
forbid_subclass
115
173
-1
6
min_reroll_age
min_login_age
max_initial_level
level_costs
level_titles
preapproved_subclasses
15
0
0
115
5
0
0
115
5
0
2
115
5
4
6
0
1
0
2
0
4
0
8
0
16
0
32
115
5
4
6
2
Apprentice
2
Journeyman
2
Professional
2
Teacher
2
Master
2
Guru
115
5
4
0
115
5
0
0
115
4
4
3
2
generic uncoded attribute
2
guat
2
uncoded
115
5
4
16
2
An object representing some uncoded skill or advantage.  'Uncoded' in this sense means primarily used in role-playing situations and not in combat or other coded game physics.
2

2
.min_reroll_age
2
     Players for which less than this many seconds have elapsed since their last reroll may not add this advantage.
2

2
.min_login_age
2
     Players for which less than this many seconds have elapsed since their first login may not add this advantage.
2

2
.max_initial_level
2
     Players may not add this advantage at an initial level higher than this integer.
2

2
.level_costs
2
     A list of 0-based potential point costs for each level of this advantage.  That is, the first element is the number of potential points required for Level 0, the second for Level 1, and so on.  [This should not be modified for traditional uncoded SKILLS.]  The length of this list determines the maximum skill level.
2

2
.level_titles
2
     A list of strings describing each of this advantage's levels.  The first elment describes Level 0, the second Level 1, etc.
115
5
4
2
0
6735
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#192
generic channel

16
36
-1
-1
-1
1
-1
188
29
match_user
36
173
-1
user_match_failed
36
173
-1
_invite
36
173
-1
_op
36
173
-1
_deop
36
173
-1
_ban
36
173
-1
_kick
36
173
-1
listeners
36
173
-1
is_op
36
173
-1
welcome_user
36
173
-1
broadcast
2
173
-1
do_command
36
173
-1
prefix_msg speaking_msg join_msg part_msg invite_msg kick_msg ban_msg disconnected_msg emoting_msg op_msg deop_msg desc_msg
36
173
-1
_speak
36
173
-1
_j*oin
36
173
-1
_part
36
173
-1
add_listener
36
173
-1
remove_listener
36
173
-1
toggle_op_status
36
173
-1
toggle_user_access
36
173
-1
_who
36
173
-1
is_emoted_text
36
173
-1
_me
36
173
-1
init_for_core
2
173
-1
set_aliases
2
173
-1
set_name
2
173
-1
is_heard_by
36
173
-1
_desc
36
173
-1
_destroy
36
173
-1
17
allow
deny
listeners
ops
default_method
prefix_msg
speaking_msg
join_msg
part_msg
invite_msg
kick_msg
ban_msg
op_msg
deop_msg
disconnected_msg
emoting_msg
desc_msg
26
0
1
36
0
0
0
36
0
4
0
36
0
4
0
36
0
2
_speak
36
5
2
~[1]~[y][%t]~[0]
36
5
2
 <%n> 
36
5
2
 %n %<has> joined the channel.
36
5
2
 %n %<has> left the channel.
36
5
2
 %n %<invites> %d to join.
36
5
2
 %n %<kicks> %d.
36
5
2
 %n %<bans> %d.
36
5
2
 %n %<makes> %d an operator.
36
5
2
 %n %<removes> %d as an operator.
36
5
2
 *** Disconnected ***
36
5
2
 %n
36
5
2
 %n %<changes> the channel description.
36
5
0
0
36
4
4
2
2
generic channel
2
gench
36
5
2
A generic channel for the channel feature.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
0
0
36
5
5
36
5
5
115
1
#193
channel feature

16
36
-1
-1
-1
74
-1
-1
12
get_current_channel_for
36
173
-1
set_current_channel_for
36
173
-1
all_channels
36
173
-1
channel_match_failed
36
173
-1
@x*
2
93
-2
do_list_channels
36
173
-1
player_disconnected
36
173
-1
init_for_core
2
173
-1
channel_name_conflicts
36
173
-1
first_channel_heard_by
36
173
-1
all_channels_heard_by
36
173
-1
do_create_channel
2
173
-1
1
current_channel
47
4
0
36
0
1
-1
36
1
4
1
2
@x
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
3
2
channel feature
2
chfo
2
is_player_feature
36
5
2
A feature allowing communicating amongst various channels.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#194
generic medkit

144
115
-1
-1
-1
5
-1
195
3
min_skill
115
173
-1
attempt_heal
115
165
-1
init_for_core
2
173
-1
2
bonus_range
min_skill
44
0
30
115
5
0
10
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
4
0
115
4
4
0
115
4
1
-1
36
1
5
115
5
5
115
5
0
0
115
4
4
4
2
generic medkit
2
medkit
2
kit
2
first aid kit
115
5
2
A nearly flat, square neoplastic container, white with a big red cross on the lid.  Things jingle inside, and you assume them to be sensitive medical instruments and powerful field narcotics.
115
5
4
2
0
3092
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#195
generic gps unit

144
115
-1
-1
-1
5
-1
198
9
tell_description
115
173
-1
tell_display
115
165
-1
read
115
45
-1
map
115
141
0
draw_map
115
165
-1
cancel
115
149
4
output
115
173
-1
_draw_map
115
173
-1
init_for_core
2
173
-1
5
drawing_map
holo_display
blank_local_map
marked_local_map
local_map_info
47
0
0
115
1
0
0
115
1
4
7
2
   _______   
2
  |       |  
2
  |       |  
2
  |       |  
2
  |       |  
2
  |_______|  
2
             
115
5
4
7
2
\ \__| |__/ /
2
 \         / 
2
__|   ^   |__
2
__         __
2
  |   v   |  
2
 / __   __ \ 
2
/ /  | |  \ \
115
5
4
10
4
3
2
nw
4
2
0
1
0
2
4
2
0
1
0
3
4
3
2
n
4
2
0
1
0
1
4
2
0
6
0
8
4
3
2
ne
4
2
0
1
0
2
4
2
0
11
0
13
4
3
2
e
4
2
0
3
0
4
4
2
0
11
0
13
4
3
2
se
4
2
0
6
0
7
4
2
0
11
0
13
4
3
2
s
4
2
0
6
0
7
4
2
0
6
0
8
4
3
2
sw
4
2
0
6
0
7
4
2
0
1
0
3
4
3
2
w
4
2
0
3
0
4
4
2
0
1
0
3
4
3
2
up
4
2
0
3
0
3
4
2
0
7
0
7
4
3
2
down
4
2
0
5
0
5
4
2
0
7
0
7
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
4
0
115
4
4
0
115
4
1
-1
36
1
5
115
5
5
115
5
0
0
115
4
4
4
2
generic gps unit
2
gps unit
2
compass
2
unit
115
5
4
2
2
A hand-held box of durable black plastic.  On the top is a compass, pointing north, but you never seem to need that.  The real perk is the LCD display showing your position according to the global positioning satellites still in orbit and functional.
2
It isn't always reliable, often doesn't work at all, but it's better than being COMPLETELY lost.
115
5
4
2
0
8367
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#196
Personal Enhancement Centre

0
2
-1
-1
-1
298
-1
297
2
init_for_core
2
173
-1
boost raise
115
93
-2
0
51
5
2
5
5
2
5
4
0
36
1
4
0
36
1
5
2
5
5
36
1
5
2
5
5
2
5
5
36
1
5
36
1
5
36
0
5
36
0
5
36
1
5
36
1
5
36
1
5
2
5
5
2
5
5
2
5
0
477202671
115
1
4
0
115
1
5
2
5
5
2
5
0
0
2
5
5
2
5
5
2
5
5
2
5
4
0
2
5
1
115
2
5
4
0
2
4
1
-1
2
5
0
1944698422
2
5
4
0
2
4
5
2
5
5
2
5
5
2
5
5
2
5
0
0
113
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
Personal Enhancement Centre
2
PEC
2
5
2
Neo-Meta-New-Age music flows through you, relaxing your body into a state most suited for personal expansion.  The lighting is soft, and the air is of perfect humidity and temperature for your species.
2
5
4
2
0
2091
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
0
-1
115
1
#197
generic computer

144
2
-1
-1
-1
5
-1
155
55
install insert plug put
2
149
3
acceptable
2
173
-1
dev_install
2
165
-1
uninstall remove unplug eject
2
149
5
exitfunc
2
165
-1
dev_uninstall
2
165
-1
set_dt_current
2
173
-1
read_port
2
173
-1
write_port
2
173
-1
port_IN
2
173
-1
type enter key
2
157
4
parse_input
2
165
-1
screen_prefix_msg
2
173
-1
port_OUT
2
173
-1
port_DSK
2
173
-1
port_PRN
2
173
-1
port_COM
2
173
-1
port_RAM
2
173
-1
tell_screen
2
173
-1
draw_screen
2
165
-1
tell_description
2
173
-1
prompt_msg
2
173
-1
_desk*top _dt
2
173
-1
match_desktop_file
2
173
-1
desktop_file_match_failed
2
173
-1
_fg
2
173
-1
_load
2
165
-1
_save
2
165
-1
_time _date
2
173
-1
all_ports
2
165
-1
all_devices
2
173
-1
_help
2
173
-1
_view _list
2
173
-1
dt_buffer
2
173
-1
dump_buffer
2
173
-1
insertion_point
2
173
-1
_clear
2
173
-1
_new
2
165
-1
save_buffer
2
173
-1
os_commands
2
173
-1
find_command
2
173
-1
input
2
173
-1
output
2
173
-1
parse_editor_command
2
173
-1
ed_add_line
2
173
-1
set_insertion_point
2
173
-1
ed_numbered
2
173
-1
ed_append
2
173
-1
ed_insert
2
173
-1
ed_delete
2
173
-1
ed_find
2
173
-1
_editing_help
36
173
-1
all_commands*_associated
36
173
-1
_commands
36
173
-1
init_for_core
2
173
-1
20
install_task
uninstall_task
dt_files
dt_data
dt_current
port_RAM
port_IN
screen_prefix_msg
screen
screen_height
screen_width
port_OUT
port_DSK
port_PRN
port_COM
prompt_msg
dt_buffer
inserting
editing_help
port_OPT
62
0
571213228
2
1
0
353705899
2
1
4
0
2
0
4
0
2
0
2

2
0
1
-1
2
1
1
-1
2
1
2
(%t) 
2
5
4
25
2
Mon Jul 29 22:44:09 1996 CDT
2
22:44> serial
2
serial? Not found.
2
22:44> serial #1882
2
serial? Not found.
2
22:44> #a882
2
#a882? Not found.
2
22:44> #1882
2
#1882? Not found.
2
22:45> boost
2
boost? Not found.
2
22:45> demon
2
demon? Not found.
2
22:45> help
2
desk*top       save           view           editing_help
2
dt             time           list           commands
2
fg             date           clear          ls
2
load           help           new            rm
2
22:45> fg
2
fg! No desktop file specified.
2
22:45> dt
2
Desktop: generic computer
2
  (No files.)
2
22:46> mydisk
2
mydisk? Not found.
2
1
0
24
2
1
0
78
2
1
1
-1
2
1
1
-1
2
1
1
-1
2
1
1
-1
2
1
2
$H:$M> 
2
5
4
1
2
__INS__
2
0
0
0
2
1
4
16
2
An editor command is in the following format:
2

2
        <start_line>[,<end_line>]<cmd_character>[/<text>[/<text>]
2

2
Commands:
2

2
n       -- Numbered list of the given line/range.  (1,$n)
2
a       -- Enter append mode after the given line.  ($a)
2
i       -- Enter insert mode after the given line.  ($i)
2
d       -- Delete the given line/range.  (1d)
2
f       -- Find the given text in the given line/range.  (1,$f/foo)
2
s       -- Substitute text in the given line/range.  (.s/old/new/)
2

2
Lines are either numbers, or '.' for the current line, '$' for the last.
2

2
Enter '.' alone to exit either append or insert mode.
2
5
1
-1
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
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
3
2
generic computer
2
computer
2
pc
2
5
2
A black lunchbox computer composed of durable combat-resistant military plastek.  The front panel opens, holding the keyboard.  Inside is an active plasma display and slots for device ports, including smartdisks.
2
5
4
2
0
43141
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#198
generic computer device

144
2
-1
-1
-1
5
-1
197
4
install_info
2
173
-1
uninstall_info
2
173
-1
all_commands
2
173
-1
init_for_core
2
173
-1
1
port
43
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
1
5
2
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
generic computer device
2
device
2
5
2
Something to be used with a computer.
2
5
4
2
0
2717
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#199
transputty

16
115
-1
-1
-1
111
-1
205
9
init_for_core
2
173
-1
cname
115
173
-1
caliases
115
173
-1
amount_from_string
115
165
-1
match_mantra
115
173
-1
resonate
115
153
0
do_sculpt
2
173
-1
place_sculpture
115
173
-1
mantra_failed_msg omantra_failed_msg sculpt_attempt_msg osculpt_attempt_msg
115
173
-1
8
sculpt_attempt_msg
osculpt_attempt_msg
mantra_failed_msg
omantra_failed_msg
not_enough_msg
onot_enough_msg
equota_msg
oequota_msg
61
2
You chant ``$mantra'' solemnly while kneading %t with your fingers.
115
5
2
%N %<chants> ``$mantra'' while kneading %t with %p fingers.
115
5
2

115
5
2
Nothing happens.
115
5
2

115
5
2
Other than a few impotent sparks, nothing happens.
115
5
2
As your notion emerges from the putty, you feel the strain of too many dreams realized.  The putty melts back into a formless lump.
115
5
2
A vague shape emerges from the putty, but soon fades back into the formless lump.
115
5
5
101
1
2
transputty
115
5
2
lump of putty
101
1
5
101
1
2
%(transputty%|putty%)
101
1
5
101
1
1
199
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
4
0
115
4
4
0
115
4
1
-1
36
1
5
115
5
5
115
5
0
0
115
4
4
2
2
transputty
2
putty
115
5
2
A lump of bright green, seemingly malleable material, pressed with the fingerprints of those who've tried to shape its preternatural force.  There are about $amount cubic centimeters of the stuff in this lump.
115
5
4
2
0
7031
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#200
notion

16
115
-1
-1
-1
1
-1
-1
10
init_for_core
2
173
-1
slowness amount_required
115
173
-1
make_notion_reality
2
173
-1
attempt_sculpture
115
173
-1
announce_failure
115
173
-1
announce_success
115
173
-1
mantra_seed
115
173
-1
mantra_for
115
173
-1
verify_mantra_for
115
173
-1
failure_msg ofailure_msg success_msg osuccess_msg
115
173
-1
9
slowness
amount_required
prototype
willcheck_mod
failure_msg
ofailure_msg
success_msg
osuccess_msg
mantra_seed
18
0
100
115
5
0
5
115
5
1
-1
115
5
0
-50
115
5
2

115
5
2
As %s %<becomes> visibly frustrated with %p pathetic progress, the lump crackles with static sparks, then pops out of existence with a loud SNAP.
115
5
2

115
5
2
Sparkling light trails in the wake of %p hands, intensifying into a brilliant aura around the object, then abruptly blinking out to reveal %[diname,perfectly-formed] -- no longer putty, but REAL!
115
5
0
0
115
0
0
0
115
4
4
1
2
notion
115
5
2
An individual's conception or impression of something known, experienced, or imagined.  With the right material and enough willpower, one might be able to transform this raw notion into something real.
115
5
4
2
0
6184
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#201
effigy prototype

16
115
-1
-1
-1
92
-1
-1
13
clone
115
173
-1
destroy
115
173
-1
load
115
173
-1
reload
115
173
-1
load_from
115
173
-1
is_writable_by
115
173
-1
save_to
115
173
-1
save
115
173
-1
import_sheet
115
173
-1
is_existing_uncoded
115
173
-1
export_sheet
115
173
-1
record_edit
115
173
-1
init_for_core
2
173
-1
3
original
sheet
uncoded_end_marker
132
1
-1
115
1
4
0
115
1
2
__END__
115
1
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
5
5
115
5
5
115
1
5
115
1
1
206
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
1
5
115
5
5
2
1
5
2
1
5
2
1
5
2
1
5
115
4
5
115
5
5
115
4
5
115
5
5
115
0
5
115
1
5
36
0
5
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
5
115
4
5
115
4
5
36
1
5
115
5
5
115
5
0
0
115
4
4
1
2
effigy prototype
115
5
4
22
2
A dummy to hold stats being edited in the character editor.  It has a few interfaces:
2

2
:clone(OBJ)
2
=> Create a child with the given OBJ's stats.
2

2
:load(OBJ)
2
=> Load stats from the given OBJ and set it as the primary donor.
2

2
:reload()
2
=> Reload stats from the primary donor.
2

2
:load_from(OBJ)
2
=> Transfer the stats of the given OBJ to this child.
2

2
:save()
2
=> Transfer the stats to the originally cloned object.
2

2
:save_to(OBJ)
2
=> Transfer the stats to the given OBJ.
2

2
:destroy()
2
=> Destroy the child.  Must be called by a controller or the editor.
115
5
4
2
0
10630
0
955412154
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#202
human

16
2
-1
-1
-1
187
-1
-1
2
convert_character
115
173
-1
init_for_core
2
173
-1
0
15
1
185
115
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
0
0
2
4
4
1
2
human
2
5
2
Your basic vanilla humanoid.  No special features or abilities.  Set a species to this when you just want to unset another.
2
5
4
2
0
1100
0
955412150
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#203
Generic Crosswarp Board

144
2
-1
-1
-1
144
-1
-1
2
custom_score
36
173
-1
init_for_core
2
173
-1
1
multiword_bonuses
69
4
10
0
0
0
0
0
0
0
25
0
50
0
75
0
100
0
125
0
150
0
200
2
5
4
0
36
1
4
0
36
1
4
0
36
1
5
36
1
4
0
36
1
4
0
36
1
4
0
36
1
4
27
0
1
0
3
0
3
0
2
0
1
0
4
0
2
0
4
0
1
0
8
0
5
0
1
0
3
0
1
0
1
0
3
0
10
0
1
0
1
0
1
0
2
0
6
0
4
0
8
0
4
0
10
0
0
36
1
5
36
1
4
0
36
1
4
0
36
1
4
0
36
1
4
15
2
=  '   =   '  =
2
 -   "   "   - 
2
  -   ' '   -  
2
'  -   '   -  '
2
    -     -    
2
 "   "   "   " 
2
  '   ' '   '  
2
=  '   *   '  =
2
  '   ' '   '  
2
 "   "   "   " 
2
    -     -    
2
'  -   '   -  '
2
  -   ' '   -  
2
 -   "   "   - 
2
=  '   =   '  =
36
1
5
36
1
5
36
1
4
0
36
1
0
0
36
1
0
0
36
1
4
0
36
0
4
0
36
0
4
0
36
0
4
0
36
0
4
100
2
A
2
A
2
A
2
A
2
A
2
A
2
A
2
A
2
A
2
B
2
B
2
C
2
C
2
D
2
D
2
D
2
D
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
E
2
F
2
F
2
G
2
G
2
G
2
H
2
H
2
I
2
I
2
I
2
I
2
I
2
I
2
I
2
I
2
I
2
J
2
K
2
L
2
L
2
L
2
L
2
M
2
M
2
N
2
N
2
N
2
N
2
N
2
N
2
O
2
O
2
O
2
O
2
O
2
O
2
O
2
O
2
P
2
P
2
Q
2
R
2
R
2
R
2
R
2
R
2
R
2
S
2
S
2
S
2
S
2
T
2
T
2
T
2
T
2
T
2
T
2
U
2
U
2
U
2
U
2
V
2
V
2
W
2
W
2
X
2
Y
2
Y
2
Z
2
_
2
_
36
1
5
2
5
5
36
1
5
36
1
5
36
1
1
145
2
5
5
2
5
5
36
1
5
36
1
5
2
5
5
36
1
5
2
5
5
2
5
5
115
1
4
0
115
1
5
2
5
5
2
5
0
0
2
5
5
2
5
5
2
5
5
2
5
4
0
2
5
1
115
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
0
0
113
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
Generic Crosswarp Board
2
Crosswarp
2
5
5
2
5
4
2
0
4432
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#204
noweapon

16
115
-1
-1
-1
93
-1
-1
1
init_for_core
2
173
-1
0
54
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
1
161
115
1
1
240
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
4
0
115
4
4
0
115
4
1
-1
36
1
5
115
5
5
115
5
0
0
115
4
4
1
2
noweapon
115
5
2
A weapon to pass to verbs (such as :receive_damage) which require a weapon when none is warranted for the particular context.
115
5
4
2
0
1444
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#205
generic liquid collective

16
36
-1
-1
-1
111
206
-1
4
drink lap
115
45
-1
drank_by
115
173
-1
init_for_core
2
173
-1
do_drink
115
173
-1
4
drink_msg
odrink_msg
drinkall_msg
odrinkall_msg
57
2

36
5
2
%N %<kneels> down and %<laps> at %[tdname].
36
5
2

36
5
2
%N %<kneels> down and %<slurps> up all that remains of %[tdname].
36
5
5
101
1
5
36
5
5
101
1
5
101
1
2
%(liquid collective%|glc%)
101
1
5
101
1
1
205
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
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
generic liquid collective
2
glc
36
5
2
A collection of liquid items which might be absorbed by a sponge or lapped up by some creature.;
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#206
blood

16
115
-1
-1
-1
205
-1
-1
2
init_for_core
2
173
-1
drank_by
115
173
-1
6
end_chance
will_chance
ins
lose_will_msg
gain_end_msg
gain_ins_msg
63
0
1
115
5
0
3
115
5
0
1
115
5
2
You feel your will being torn between this life and those known by the blood you've ingested.
115
5
2
Your arteries throb as the life force of fallen warriors permeates your body, galvanizing flesh and bone.
115
5
2
The memories of lives lost swim through your mind like ghosts.
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
101
1
2
blood puddle
115
5
2
pool of blood
101
1
5
101
1
5
101
1
5
101
1
1
206
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
4
0
115
4
4
0
115
4
1
-1
36
1
5
115
5
5
115
5
0
0
115
4
4
1
2
blood
115
5
2
A pool of thick red liquid, possibly the life juice of a fallen combatant.  If one were to measure the pool, one would probably come up with somewhere near $amount cubic centimeters.
115
5
4
2
0
2860
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#207
Extended Emotion FO

16
36
-1
-1
-1
74
-1
172
7
`*
36
93
-2
[* (*
36
93
-2
-*
36
93
-2
<*
36
93
-2
!*
36
85
-2
think
36
93
-2
sing
36
93
-2
0
46
1
-1
36
1
4
8
2
!
2
`
2
-
2
<
2
[
2
]
2
sing
2
think
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
3
2
Extended Emotion FO
2
EEF
2
is_char_feature
36
5
2
An extended set of communications commands.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#208
Movement Privileges Feature

16
36
-1
-1
-1
308
-1
214
3
@move @tel*eport
2
93
1
@go @summon @join
2
29
-1
@home @start
2
13
-1
0
48
5
36
1
5
36
5
1
-1
36
1
4
0
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
3
2
Movement Privileges Feature
2
mvpfo
2
is_char_feature
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#209
GMRP Feature

16
115
-1
-1
-1
74
-1
213
2
@spoof @emit
115
93
-2
@ic-shout @gm-shout
115
85
-2
2
gm_shout_text_msg
ic_shout_text_msg
48
2
%N %<shouts> a message to all gamemasters: "$text"
115
5
2
%N %<shouts> a message to all in-character users: "$text"
115
5
1
-1
36
1
4
1
2
@spoof
36
1
5
36
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
2
1
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
36
1
5
115
5
5
115
5
5
115
1
5
115
1
4
0
115
4
4
0
115
4
1
-1
36
1
5
115
5
5
115
5
0
0
115
4
4
1
2
GMRP Feature
115
5
2
A collection of commands useful for sculpting the role-play environment.
115
5
4
2
0
4538
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#210
Pasting Feature

16
36
-1
-1
-1
74
-1
207
3
@paste
36
85
-2
|*
36
93
-2
@pasteto @paste-to
36
93
-2
0
46
1
-1
36
1
4
3
2
|
2
@paste
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
3
2
Pasting Feature
2
pfo
2
is_player_feature
36
5
2
Verbs useful to people using a windowing system to paste text at people.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#211
@owned FO

16
2
-1
-1
-1
74
-1
215
8
parse_owned_args
2
173
-1
output_owned
2
173
-1
owned_by_class
2
173
-1
byte_str*ing
2
173
-1
parse_byteref
2
173
-1
db_share
2
165
-1
verbs
2
173
-1
@owned @owned-regex*p @full-owned @fo*wned
2
85
-2
0
46
1
-1
36
1
4
1
2
@owned
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
1
5
2
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
@owned FO
2
is_player_feature
2
5
4
2
2
Little gremlins gathering data on prominent MOO-ers to be presented at the soon-to-be LambdaHouse Inquiry into bloated useless MOO-pigs.  But don't worry.  If you're a good builder, you have nothing to fear.
2
One of the gremlins turns from his work.  "... or do you?"  He wiggles his pointy green gremlin ears.
2
5
4
2
0
13427
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#212
Puppet Remote Control FO

16
2
-1
-1
-1
74
-1
210
9
match_puppet_for
115
173
-1
do_match_puppet_for
115
173
-1
mon*itor ign*ore com*mand @com*mand @ign*ore @mon*itor
2
93
-2
@add-puppet
115
29
-1
@remove-puppet
115
29
-1
@list-puppets @puppets
115
13
-1
@add-shaper
2
93
1
@remove-shaper
2
93
5
@list-shapers
115
29
-1
0
46
1
-1
36
1
4
3
2
@add-puppet
2
@remove-puppet
2
@list-puppets
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
1
5
2
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
4
2
Puppet Remote Control FO
2
prcfo
2
remote control
2
is_gm_feature
2
5
2
A useful feature allowing remote manipulation of the more popular puppet commands.
2
5
4
2
0
9110
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#213
Hangout FO

16
36
-1
-1
-1
74
-1
308
8
update_hangout
36
173
-1
match_hangout
36
173
-1
match_hangout_failed
36
173
-1
get_directory_for
36
173
-1
trusted
36
173
-1
@hangout
36
29
-1
@hangouts
36
13
-1
@directory
36
93
12
1
hangouts
47
4
8
4
2
1
132
2
An <IC> lounge in the R/T building.
4
2
1
1830
2
A sickly little spot in the middle of nowhere.  The glasses are spotty, the water is yellow, and the bathroom stinks.
4
2
1
5453
2
A bar and grill with a tropical bend, a rubber chicken, and a waitress named Olga.
4
2
1
6501
2
The perfect place for OOC players to hang out and chat about any topic under the sun, without interfering with plots happening in the IC Ghostworld. [@go star]
4
2
1
895
2
An <IC> bar on the GW plains. [from strykers just go s.]
4
2
1
16374
2
An <IC> lounge located in the Yellowstone lodge.
4
2
1
6393
2
An <IC> restored pub in the heart of Shadowbrook.
4
2
1
5359
2

36
1
1
-1
36
1
4
3
2
@hangout
2
@hangouts
2
@directory
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
3
2
Hangout FO
2
hangfo
2
is_player_feature
36
5
2
This feature houses commands useful for determining just where the action is on the MOO.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#214
Editing and Mail Privileges Feature

16
36
-1
-1
-1
308
-1
-1
2
@forward @gripe @typo @bug @suggest*ion @idea @comment @edit @notedit @note-edit @send @answer @repl*y
2
93
-2
@quick-send @qsend
2
93
-2
0
48
5
36
1
5
36
5
1
-1
36
1
4
0
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
3
2
Editing and Mail Privileges Feature
2
empfo
2
is_player_feature
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#215
skill maintenance fo

16
2
-1
-1
-1
74
-1
152
2
feature_ok
2
173
-1
@addskill
2
85
-2
0
46
1
-1
36
1
4
1
2
@skill
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
1
5
2
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
3
2
skill maintenance fo
2
skfo
2
is_gm_feature
2
5
2
A feature object used in the creation and maintenance of skills.
2
5
4
2
0
3675
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#216
Gamemaster FO

16
2
-1
-1
-1
74
-1
209
9
feature_ok
2
173
-1
gate @gate
2
93
-2
@heal
2
21
-1
@stats
2
21
-1
@seek
2
29
-1
@shout
36
85
-2
@table
2
29
-1
@snitch
115
93
0
@compare
2
93
0
3
generic_gate
shout_format_msg
shout_text_msg
49
1
1703
2
5
2
The air around you vibrates, resonating in your head as the voice of %n speaking the words: "$text"
2
5
2
%N %<shouts> an OOC message to all connected users: "$text"
2
5
1
-1
36
1
4
7
2
gate
2
@heal
2
@stats
2
@seek
2
@shout
2
@snitch
2
@compare
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
1
5
2
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
Gamemaster FO
2
is_gm_feature
2
5
2
A feature, a sort of auxiliary brain, for world-shapers.  Peer into the innermost workings of the human, subhuman, and unhuman machines.  And flip their switches, too.  WooWee fun!
2
5
4
2
0
12988
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#217
strip

0
2
-1
-1
-1
110
-1
218
0
0
11
5
2
5
5
2
1
0
0
2
4
4
1
2
strip
2
5
2
Removing anything you're wearing.
2
5
4
2
0
326
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#218
dress

0
2
-1
-1
-1
110
-1
219
0
0
11
5
2
5
5
2
1
0
0
2
4
4
1
2
dress
2
5
2
Dressing you in anything.
2
5
4
2
0
318
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#219
feed

0
2
-1
-1
-1
110
-1
220
0
0
11
5
2
5
5
2
1
0
0
2
4
4
1
2
feed
2
5
2
Feeding you, or otherwise delivering something edible or potable.
2
5
4
2
0
356
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#220
heal

0
2
-1
-1
-1
110
-1
221
1
trusts
2
165
-1
0
11
5
2
5
5
2
1
0
0
2
4
4
1
2
heal
2
5
2
Using medical skills on you.  Sort of a release to avoid malpractice suits.
2
5
4
2
0
773
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#221
chown to

0
2
-1
-1
-1
110
-1
222
0
0
11
0
0
2
5
0
1
2
1
0
0
2
4
4
3
2
chown to
2
chown-to
2
chown_to
2
5
2
Trusting someone to do this allows them to change ownership of their objects to you.  If you are a programmer, this will include objects with verbs, which will then be owned by you.  Be sure you really do trust someone before allowing this.
2
5
4
2
0
573
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#222
chown from

0
2
-1
-1
-1
110
-1
223
0
0
11
0
0
2
5
0
1
2
1
0
0
2
4
4
5
2
chown from
2
chown-from
2
chown_from
2
take-from
2
take_from
2
5
2
Another serious trust; this allows the trustee to change ownership of anything you own to themself.  Be very sure you trust someone before allowing this.
2
5
4
2
0
530
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#223
take from

16
2
-1
-1
-1
110
-1
224
0
0
11
5
2
5
5
2
1
0
0
2
4
4
3
2
take from
2
grab from
2
swipe from
2
5
2
Allow someone to 'grab something from you'.  This makes it easy for them to quickly take something back which they're showing you, or to make your inventories effectively interchangable.
2
5
4
2
0
524
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#224
give to

16
2
-1
-1
-1
110
-1
225
0
0
11
5
2
5
5
2
1
0
0
2
4
4
6
2
give to
2
give
2
hand to
2
hand
2
give-to
2
hand-to
2
5
2
Allow someone to give something to you while you're disconnected.  While connected, anyone can hand you anything.
2
5
4
2
0
484
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#225
pet

16
2
-1
-1
-1
110
-1
226
0
0
11
5
2
5
5
2
1
0
0
2
4
4
4
2
pet
2
stroke
2
caress
2
fondle
2
5
2
Generally for pets, trusting someone to perform this action allows them to touch you in the bad place.  And the good place.  Everyplace.  But just touch.
2
5
4
2
0
487
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#226
mount

16
2
-1
-1
-1
110
-1
227
0
0
11
5
2
5
5
2
1
0
0
2
4
4
2
2
mount
2
ride
2
5
2
Get your mind out of the gutter.  Though this action can be aliased as a license to screw, it's generally for mountable beasties.  Hey, get your mind out of the gutter again.
2
5
4
2
0
480
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#227
act on

16
2
-1
-1
-1
110
-1
-1
0
0
11
5
2
5
5
2
1
0
0
2
4
4
1
2
act on
2
5
2
Allow certain 'restricted' characters to perform actions on you.  Intended primarily to prohibit unwanted attacks from NPC.
2
5
4
2
0
418
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#228
master pistol-whipping set

16
115
-1
-1
-1
161
-1
293
0
0
20
5
115
5
4
2
2
%N %<swings> the butt of %p unloaded %t at %d.
2
%N %<grasps> the barrel of %p %t and %<clubs> the butt at %d.
115
5
5
115
5
5
115
5
5
115
5
5
115
5
4
1
2
%[tdnamec] %<t:sinks> into %[dpp] skull, shredding %[dpp] brain with shards of bone.  Blood flows over %[dpp] face in a gruesome scarlet death veil.
115
5
4
1
2
%S %<bashes> the back of %[dpp] head, reducing %d to an unconscious lump at %p feet.
115
5
5
115
5
5
115
5
5
115
5
0
0
115
4
4
2
2
master pistol-whipping set
2
mpws
115
5
5
115
5
4
2
0
890
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#229
neuter

0
36
-1
-1
-1
124
-1
230
0
0
28
2
Its
36
5
2
its
36
5
2
Its
36
5
2
its
36
5
2
Itself
36
5
2
itself
36
5
2
It
36
5
2
it
36
5
2
It
36
5
2
it
36
5
0
0
36
5
2
has
36
5
2
is
36
5
5
36
5
5
36
5
2
sexless
36
5
5
36
5
5
36
5
0
1
36
5
0
0
36
4
5
36
5
2
An object describing the neuter gender.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#230
male

0
36
-1
-1
-1
124
-1
231
0
0
28
2
His
36
5
2
his
36
5
2
His
36
5
2
his
36
5
2
Himself
36
5
2
himself
36
5
2
Him
36
5
2
him
36
5
2
He
36
5
2
he
36
5
0
0
36
5
2
has
36
5
2
is
36
5
5
36
5
2
man
36
5
5
36
5
5
36
5
0
1
36
5
5
36
5
0
0
36
4
5
36
5
2
An object describing the male gender.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#231
female

0
36
-1
-1
-1
124
-1
232
0
0
28
2
Hers
36
5
2
hers
36
5
2
Her
36
5
2
her
36
5
2
Herself
36
5
2
herself
36
5
2
Her
36
5
2
her
36
5
2
She
36
5
2
she
36
5
0
0
36
5
2
has
36
5
2
is
36
5
5
36
5
2
woman
36
5
5
36
5
0
1
36
5
5
36
5
5
36
5
0
0
36
4
5
36
5
2
An object describing the female gender.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#232
either

0
36
-1
-1
-1
124
-1
233
0
0
28
2
His/Hers
36
5
2
his/hers
36
5
2
His/Her
36
5
2
his/her
36
5
2
(Him/Her)self
36
5
2
(him/her)self
36
5
2
Him/Her
36
5
2
him/her
36
5
2
S/He
36
5
2
s/he
36
5
0
0
36
5
2
has
36
5
2
is
36
5
5
36
5
5
36
5
5
36
5
0
1
36
5
0
1
36
5
5
36
5
0
0
36
4
5
36
5
2
An object describing the either gender.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#233
Spivak

0
36
-1
-1
-1
124
-1
234
0
0
28
2
Eirs
36
5
2
eirs
36
5
2
Eir
36
5
2
eir
36
5
2
Eirself
36
5
2
eirself
36
5
2
Em
36
5
2
em
36
5
2
E
36
5
2
e
36
5
0
0
36
5
2
has
36
5
2
is
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
0
36
4
5
36
5
2
An object describing the Spivak gender.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#234
splat

0
36
-1
-1
-1
124
-1
235
0
0
28
2
H*s
36
5
2
h*s
36
5
2
H*
36
5
2
h*
36
5
2
H*self
36
5
2
h*self
36
5
2
H*
36
5
2
h*
36
5
2
*E
36
5
2
*e
36
5
0
0
36
5
2
has
36
5
2
is
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
0
36
4
5
36
5
2
An object describing the splat gender.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#235
plural

0
36
-1
-1
-1
124
-1
236
0
0
28
2
Theirs
36
5
2
theirs
36
5
2
Their
36
5
2
their
36
5
2
Themselves
36
5
2
themselves
36
5
2
Them
36
5
2
them
36
5
2
They
36
5
2
they
36
5
0
1
36
5
2
have
36
5
2
are
36
5
2
were
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
0
36
4
5
36
5
2
An object describing the plural gender.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#236
egotistical

0
36
-1
-1
-1
124
-1
237
0
0
28
2
Mine
36
5
2
mine
36
5
2
My
36
5
2
my
36
5
2
Myself
36
5
2
myself
36
5
2
Me
36
5
2
me
36
5
2
I
36
5
2
I
36
5
0
1
36
5
2
have
36
5
2
am
36
5
2
were
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
0
36
4
5
36
5
2
An object describing the egotistical gender.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#237
royal

0
36
-1
-1
-1
124
-1
238
0
0
28
2
Ours
36
5
2
ours
36
5
2
Our
36
5
2
our
36
5
2
Ourselves
36
5
2
ourselves
36
5
2
Us
36
5
2
us
36
5
2
We
36
5
2
we
36
5
0
1
36
5
2
have
36
5
2
are
36
5
2
were
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
0
36
4
5
36
5
2
An object describing the royal gender.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#238
2nd

0
36
-1
-1
-1
124
-1
150
0
0
28
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
0
1
36
5
2
have
36
5
2
are
36
5
2
were
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
0
0
36
4
5
36
5
2
An object describing the 2nd gender.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#239
ranged message set

144
36
-1
-1
-1
161
294
228
0
0
20
5
36
5
4
1
2
%N %<fires> %p %t at %d.
36
5
4
1
2
%N %<misses/miss>, the projectile whizzing inches from %d.
36
5
4
1
2
%D %<d:dodges> the projectile with an incredible feat of reflex!
36
5
4
1
2
%D %<d:deflects> the blow with %[dpp] %i.
36
5
5
36
5
4
1
2
The projectile strikes %d and rips through %[dpo], leaving a trail of jagged flesh in its wake.  It shreds straight through %d, killing %[dpo].
36
5
4
1
2
The projectile grazes %D, knocking %[dpo] unconscious.
36
5
5
36
5
5
36
5
5
36
5
0
0
36
4
4
1
2
ranged message set
36
5
2
The set of messages for a generic ranged weapon (gun, crossbow, ect.).  What is shown as the weapon does certain things.  note that this set is for the ammo, not the weapon itself.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#240
Melee

0
115
-1
-1
-1
95
241
243
0
0
28
2
Melee
115
1
4
1
2
agility
115
5
5
115
5
5
115
5
5
115
5
5
115
5
2
Amidst the violent chaos of the perilous melee, you feel a certain primal vigor--a swell of animalistic confidence.
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955319562
0
100
0
277
0
784
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
98
4
2
1
18717
0
951179611
4
2
1
18717
0
951179665
4
2
1
25055
0
951179945
4
2
1
25050
0
951185427
4
2
1
25050
0
951185575
4
2
1
24677
0
951226507
4
2
1
24677
0
951230206
4
2
1
25022
0
951282277
4
2
1
25055
0
951352384
4
2
1
25055
0
951360064
4
2
1
24677
0
951413299
4
2
1
18717
0
951440381
4
2
1
25022
0
951450716
4
2
1
25022
0
951451629
4
2
1
23400
0
951511916
4
2
1
24608
0
951522846
4
2
1
25055
0
951525207
4
2
1
25055
0
951526632
4
2
1
4188
0
951586754
4
2
1
20635
0
951612205
4
2
1
25055
0
951612454
4
2
1
25055
0
951675002
4
2
1
25055
0
951698166
4
2
1
25055
0
951699954
4
2
1
25022
0
951711252
4
2
1
24839
0
951724682
4
2
1
24608
0
951725283
4
2
1
24608
0
951725351
4
2
1
25057
0
951944064
4
2
1
25055
0
952054427
4
2
1
25049
0
952057573
4
2
1
18717
0
952131075
4
2
1
14172
0
952132002
4
2
1
25049
0
952137682
4
2
1
25049
0
952139620
4
2
1
20635
0
952268370
4
2
1
20348
0
952319068
4
2
1
24740
0
952396104
4
2
1
24740
0
952396403
4
2
1
12584
0
952462446
4
2
1
18717
0
952565451
4
2
1
25071
0
952571929
4
2
1
25049
0
952573852
4
2
1
1142
0
952578765
4
2
1
1142
0
952579127
4
2
1
25060
0
952588648
4
2
1
25060
0
952606085
4
2
1
1142
0
952754320
4
2
1
1142
0
952754599
4
2
1
25000
0
952792555
4
2
1
18717
0
952795841
4
2
1
13063
0
952905860
4
2
1
18717
0
953339511
4
2
1
12411
0
953353046
4
2
1
25049
0
953458188
4
2
1
23970
0
953943977
4
2
1
25049
0
954115746
4
2
1
25049
0
954303448
4
2
1
25071
0
954393877
4
2
1
25071
0
954394388
4
2
1
25057
0
954399799
4
2
1
18717
0
954611072
4
2
1
25049
0
954643181
4
2
1
25071
0
954648239
4
2
1
25071
0
954699405
4
2
1
25071
0
954726929
4
2
1
25071
0
954727050
4
2
1
25071
0
954727765
4
2
1
25071
0
954738006
4
2
1
25049
0
954738024
4
2
1
25071
0
954790918
4
2
1
25049
0
954818378
4
2
1
25071
0
954876017
4
2
1
25049
0
954877447
4
2
1
25049
0
954878047
4
2
1
25049
0
954879762
4
2
1
25071
0
954881550
4
2
1
25049
0
954884716
4
2
1
25049
0
954904116
4
2
1
20372
0
954954651
4
2
1
20372
0
954977009
4
2
1
20372
0
954977204
4
2
1
20372
0
954977685
4
2
1
20372
0
954981169
4
2
1
20372
0
954981288
4
2
1
25071
0
955062768
4
2
1
18717
0
955063581
4
2
1
25071
0
955065376
4
2
1
25071
0
955066869
4
2
1
25071
0
955067460
4
2
1
25071
0
955073532
4
2
1
25071
0
955078161
4
2
1
25037
0
955187106
4
2
1
25037
0
955188454
4
2
1
18717
0
955244900
4
2
1
18717
0
955245063
4
2
1
18717
0
955331094
4
2
1
13170
0
955408720
115
1
5
115
5
0
0
115
4
4
1
2
Melee
115
5
4
1
2
General skill in close-quarters combat.
115
5
4
2
0
3950
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#241
Unarmed Combat

0
115
-1
-1
-1
240
259
242
0
0
28
2
unarmed
115
1
4
3
2
agility
2
agility
2
strength
115
5
5
115
5
5
115
5
5
115
5
5
115
5
2
Muscles flex beneath your scarred flesh; you gain new appreciation for your body--taut and steeled for the raw exhilaration of unarmed combat.
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955319562
0
100
0
276
0
883
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
61
4
2
1
1142
0
954388718
4
2
1
25071
0
954392612
4
2
1
25071
0
954392742
4
2
1
25071
0
954393084
4
2
1
25071
0
954393233
4
2
1
25071
0
954394388
4
2
1
25071
0
954459776
4
2
1
25071
0
954460658
4
2
1
25071
0
954460878
4
2
1
25071
0
954461712
4
2
1
25071
0
954539013
4
2
1
18717
0
954611072
4
2
1
25071
0
954648239
4
2
1
25071
0
954648418
4
2
1
25071
0
954700442
4
2
1
25071
0
954727765
4
2
1
25071
0
954727979
4
2
1
25071
0
954738006
4
2
1
25049
0
954738024
4
2
1
25049
0
954738232
4
2
1
25057
0
954747112
4
2
1
25057
0
954749738
4
2
1
25071
0
954790918
4
2
1
25071
0
954792579
4
2
1
25071
0
954793219
4
2
1
25049
0
954818378
4
2
1
25049
0
954818668
4
2
1
25071
0
954876017
4
2
1
25049
0
954877447
4
2
1
25049
0
954878047
4
2
1
25049
0
954878172
4
2
1
25049
0
954878507
4
2
1
25049
0
954879194
4
2
1
25049
0
954879583
4
2
1
25049
0
954879762
4
2
1
25049
0
954879926
4
2
1
25049
0
954884639
4
2
1
24590
0
954889833
4
2
1
25049
0
954900997
4
2
1
25049
0
954904116
4
2
1
20372
0
954981409
4
2
1
25049
0
954987421
4
2
1
25049
0
954987605
4
2
1
25049
0
954988299
4
2
1
25049
0
954988674
4
2
1
25049
0
954989004
4
2
1
18717
0
955063630
4
2
1
25071
0
955065376
4
2
1
25071
0
955065686
4
2
1
25071
0
955066869
4
2
1
25071
0
955067037
4
2
1
25071
0
955074370
4
2
1
25071
0
955075138
4
2
1
25076
0
955083566
4
2
1
25037
0
955189323
4
2
1
25037
0
955189700
4
2
1
18717
0
955244900
4
2
1
24942
0
955320061
4
2
1
24942
0
955320134
4
2
1
18717
0
955331024
4
2
1
13170
0
955408720
115
1
5
115
5
0
0
115
4
4
2
2
Unarmed Combat
2
unarmed
115
5
4
1
2
Melee skill--unarmed.  A parent skill for others such as brawling and martial arts.
115
5
4
2
0
2906
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#242
Armed Combat

0
115
-1
-1
-1
240
254
-1
0
0
28
2
armed
115
1
4
3
2
agility
2
agility
2
quickness
115
5
5
115
5
5
115
5
5
115
5
5
115
5
2
Your grip on your weapon feels suddenly more relaxed and natural, boosting your confidence in its use.
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955319562
0
100
0
290
0
820
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
96
4
2
1
25071
0
954358113
4
2
1
25071
0
954358361
4
2
1
25049
0
954361634
4
2
1
25049
0
954362157
4
2
1
18717
0
954378610
4
2
1
25071
0
954393877
4
2
1
25057
0
954399799
4
2
1
25057
0
954400200
4
2
1
25057
0
954403911
4
2
1
5587
0
954480016
4
2
1
5587
0
954480233
4
2
1
5587
0
954480654
4
2
1
18717
0
954611224
4
2
1
25049
0
954642082
4
2
1
25049
0
954643181
4
2
1
25049
0
954643723
4
2
1
25049
0
954644971
4
2
1
25049
0
954645135
4
2
1
25049
0
954646312
4
2
1
25049
0
954646448
4
2
1
25071
0
954648166
4
2
1
25071
0
954698659
4
2
1
25071
0
954698727
4
2
1
25071
0
954699405
4
2
1
25071
0
954699521
4
2
1
25071
0
954700226
4
2
1
25071
0
954700398
4
2
1
25049
0
954721544
4
2
1
25071
0
954726929
4
2
1
25071
0
954727050
4
2
1
25071
0
954727864
4
2
1
25071
0
954736558
4
2
1
25049
0
954738516
4
2
1
25057
0
954746929
4
2
1
25071
0
954780580
4
2
1
25071
0
954792232
4
2
1
25071
0
954793495
4
2
1
25071
0
954881550
4
2
1
25049
0
954884119
4
2
1
25049
0
954884396
4
2
1
25049
0
954884716
4
2
1
18717
0
954890828
4
2
1
25049
0
954902070
4
2
1
25049
0
954903622
4
2
1
25071
0
954907208
4
2
1
20372
0
954954545
4
2
1
20372
0
954954651
4
2
1
20372
0
954977009
4
2
1
20372
0
954977204
4
2
1
20372
0
954977436
4
2
1
20372
0
954977685
4
2
1
20372
0
954978503
4
2
1
20372
0
954978505
4
2
1
20372
0
954981169
4
2
1
20372
0
954981288
4
2
1
1142
0
954995768
4
2
1
25071
0
955054646
4
2
1
25071
0
955054934
4
2
1
25071
0
955062768
4
2
1
18717
0
955063581
4
2
1
25071
0
955066671
4
2
1
25071
0
955067460
4
2
1
25071
0
955073532
4
2
1
25071
0
955073718
4
2
1
25071
0
955073844
4
2
1
25049
0
955075817
4
2
1
25071
0
955078161
4
2
1
9883
0
955091088
4
2
1
9883
0
955091142
4
2
1
9883
0
955091295
4
2
1
17237
0
955112074
4
2
1
25000
0
955136333
4
2
1
25000
0
955136802
4
2
1
25000
0
955137173
4
2
1
18717
0
955150346
4
2
1
18717
0
955150442
4
2
1
25037
0
955186251
4
2
1
25037
0
955187106
4
2
1
25037
0
955187255
4
2
1
25037
0
955187617
4
2
1
25037
0
955187749
4
2
1
25037
0
955188272
4
2
1
25037
0
955188454
4
2
1
25037
0
955188591
4
2
1
25037
0
955189764
4
2
1
25037
0
955189913
4
2
1
25037
0
955190083
4
2
1
25037
0
955190484
4
2
1
25037
0
955191271
4
2
1
18717
0
955245063
4
2
1
25049
0
955295943
4
2
1
18192
0
955315064
4
2
1
18717
0
955331094
4
2
1
6644
0
955420493
4
2
1
6644
0
955420678
4
2
1
6644
0
955420710
115
1
5
115
5
0
0
115
4
4
2
2
Armed Combat
2
armed
115
5
4
1
2
Close-combat skill with weapons.
115
5
4
2
0
3928
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#243
Shock

0
115
-1
-1
-1
95
-1
244
0
0
28
2
Shock
115
1
4
1
2
endurance
115
5
0
33
115
5
5
115
5
5
115
5
5
115
5
2
A cringe of pain twists into a growl of defiance.  You push back your pain, urging your body towards wellness.
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955319562
0
5
0
23
0
100
115
1
4
2
0
5
0
100
115
5
0
43200
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
57
4
2
1
24850
0
927379638
4
2
1
24771
0
927871700
4
2
1
24817
0
927872570
4
2
1
24902
0
931966225
4
2
1
20245
0
932490828
4
2
1
21824
0
932692299
4
2
1
24899
0
933310104
4
2
1
24750
0
933564383
4
2
1
5615
0
933718519
4
2
1
24705
0
934231839
4
2
1
24525
0
934251454
4
2
1
23615
0
934699299
4
2
1
24935
0
935079867
4
2
1
10110
0
935094490
4
2
1
13430
0
935290094
4
2
1
24937
0
935533337
4
2
1
24492
0
936650724
4
2
1
20487
0
937702372
4
2
1
10734
0
937902241
4
2
1
24608
0
937985145
4
2
1
24369
0
938736028
4
2
1
24645
0
939355186
4
2
1
24969
0
939448250
4
2
1
9869
0
939771835
4
2
1
13858
0
940478962
4
2
1
24993
0
940814957
4
2
1
24971
0
940909998
4
2
1
24995
0
941003098
4
2
1
18522
0
941266354
4
2
1
24998
0
941609364
4
2
1
24592
0
941924780
4
2
1
24793
0
942220198
4
2
1
24957
0
942767747
4
2
1
20286
0
942987481
4
2
1
24663
0
943124994
4
2
1
2237
0
943328256
4
2
1
24986
0
943828163
4
2
1
22209
0
944718443
4
2
1
6537
0
944720236
4
2
1
8131
0
945671976
4
2
1
20413
0
948687352
4
2
1
22578
0
949112765
4
2
1
22153
0
949195186
4
2
1
22725
0
949262240
4
2
1
10280
0
949778256
4
2
1
25057
0
950911972
4
2
1
25022
0
950931391
4
2
1
24701
0
950960947
4
2
1
25060
0
951023391
4
2
1
24572
0
951027434
4
2
1
24913
0
951030963
4
2
1
24594
0
951167577
4
2
1
24839
0
951215732
4
2
1
24677
0
951406455
4
2
1
23400
0
951429686
4
2
1
20372
0
954977286
4
2
1
25071
0
955065684
115
1
5
115
5
0
0
115
4
4
1
2
Shock
115
5
4
1
2
Used to check against death or disablement when a character receives injury.
115
5
4
2
0
2696
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#244
Medic

0
115
-1
-1
-1
95
-1
245
0
0
28
2
Medic
115
1
4
1
2
willpower
115
5
0
6
115
5
0
-15
115
5
5
115
5
5
115
5
2
You gain fresh insight into this particular medical procedure and swell with a new confidence in your abilities.
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955408657
0
15
0
60
0
237
115
1
4
2
0
15
0
300
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
55
4
2
1
12180
0
928109951
4
2
1
21824
0
930972784
4
2
1
5615
0
931271068
4
2
1
24902
0
931481225
4
2
1
23164
0
932523145
4
2
1
24914
0
933542353
4
2
1
10110
0
935075901
4
2
1
24943
0
935389530
4
2
1
14123
0
936140116
4
2
1
24525
0
936541389
4
2
1
24492
0
936824013
4
2
1
24817
0
937555154
4
2
1
5314
0
938673152
4
2
1
20487
0
940208071
4
2
1
20245
0
941051034
4
2
1
24771
0
941521860
4
2
1
24971
0
941832711
4
2
1
24663
0
943125019
4
2
1
22209
0
944717609
4
2
1
25021
0
944988584
4
2
1
6537
0
945142451
4
2
1
5907
0
945934478
4
2
1
25033
0
946799513
4
2
1
25011
0
946939575
4
2
1
24690
0
947403838
4
2
1
13858
0
948086136
4
2
1
22578
0
949112972
4
2
1
25042
0
949218246
4
2
1
22153
0
949257490
4
2
1
21938
0
949812990
4
2
1
25041
0
949921265
4
2
1
10280
0
950304982
4
2
1
24923
0
950375229
4
2
1
20413
0
950468087
4
2
1
25052
0
950752974
4
2
1
24789
0
950946852
4
2
1
24913
0
950953729
4
2
1
25022
0
950992742
4
2
1
24869
0
951082028
4
2
1
288
0
951187494
4
2
1
20513
0
951281697
4
2
1
23970
0
951354331
4
2
1
24949
0
951441571
4
2
1
24608
0
951725948
4
2
1
9865
0
952585142
4
2
1
25060
0
952592087
4
2
1
13063
0
952908215
4
2
1
9887
0
953277651
4
2
1
5587
0
954480825
4
2
1
25057
0
954746805
4
2
1
17980
0
954802372
4
2
1
25049
0
954988885
4
2
1
1142
0
954994101
4
2
1
25071
0
955078173
4
2
1
18717
0
955409744
115
1
5
115
5
0
0
115
4
4
1
2
Medic
115
5
4
1
2
Field medicine.  First-Aid.  Basic medical techniques which will allow you to revive a fallen comrade, but probably won't let you sew eir arm back on.
115
5
4
2
0
2708
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#245
Perception

0
115
-1
-1
-1
95
-1
246
1
att_bonus_for
115
173
-1
0
28
2
perception
115
1
4
3
2
sight
2
hearing
2
smell
115
5
5
115
5
5
115
5
5
115
5
5
115
5
2
Your nose whiskers tingle.  Sniffing curiously, you turn your head then catch a sound.  Your eyes quickly pinpoint the source.  No danger, but you realize how sharp your senses have become.
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955408658
0
104
0
278
0
870
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
52
4
2
1
12180
0
928113643
4
2
1
13245
0
931136335
4
2
1
20202
0
931199006
4
2
1
24899
0
931482454
4
2
1
21180
0
932102231
4
2
1
20245
0
932492144
4
2
1
24920
0
933284028
4
2
1
24909
0
933790176
4
2
1
24525
0
934084391
4
2
1
24805
0
934479914
4
2
1
5615
0
934680183
4
2
1
20510
0
936137387
4
2
1
3748
0
937093922
4
2
1
24771
0
937187451
4
2
1
24645
0
937596624
4
2
1
24965
0
938755229
4
2
1
24722
0
938815586
4
2
1
11886
0
939470512
4
2
1
24490
0
940093806
4
2
1
16959
0
940965037
4
2
1
24787
0
941134190
4
2
1
18522
0
941735236
4
2
1
14123
0
942027118
4
2
1
24986
0
942562632
4
2
1
24981
0
943212708
4
2
1
22233
0
943336470
4
2
1
12584
0
944814839
4
2
1
6537
0
945141629
4
2
1
24913
0
945234011
4
2
1
16576
0
945556700
4
2
1
24987
0
945978308
4
2
1
22725
0
947044972
4
2
1
14211
0
947547494
4
2
1
19315
0
948571576
4
2
1
1142
0
948611585
4
2
1
25007
0
948944626
4
2
1
10660
0
950117751
4
2
1
12541
0
950247100
4
2
1
24173
0
950408404
4
2
1
20413
0
950467549
4
2
1
24667
0
950555475
4
2
1
25060
0
951018164
4
2
1
25037
0
951070913
4
2
1
14882
0
951614852
4
2
1
23400
0
952055936
4
2
1
25049
0
953002329
4
2
1
3968
0
953178714
4
2
1
24949
0
953180826
4
2
1
12164
0
955005030
4
2
1
22209
0
955054922
4
2
1
9883
0
955090976
4
2
1
5800
0
955305351
115
1
5
115
5
0
0
115
4
4
2
2
perception
2
pcn
115
5
4
1
2
Ones general awareness of ones surroundings.  A combination of all the traditional senses, with a little intuition thrown in for good measure.
115
5
4
2
0
3081
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#246
Ranged

0
115
-1
-1
-1
95
263
266
0
0
28
2
Ranged
115
1
4
1
2
dexterity
115
5
5
115
5
5
115
5
5
115
5
5
115
5
2
Your eye zones in on possible targets, your grip steady at the command of your newly heightened senses.
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955319563
0
112
0
190
0
563
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
20
4
2
1
24572
0
927691493
4
2
1
24608
0
928576201
4
2
1
24608
0
928576411
4
2
1
24608
0
928739501
4
2
1
24841
0
929665671
4
2
1
24572
0
932294099
4
2
1
24608
0
932294482
4
2
1
24608
0
932295116
4
2
1
24608
0
932295269
4
2
1
24936
0
934737325
4
2
1
9865
0
935632755
4
2
1
24957
0
938822248
4
2
1
22209
0
939875095
4
2
1
22209
0
939877140
4
2
1
22209
0
941432681
4
2
1
22209
0
942112908
4
2
1
6537
0
944713958
4
2
1
22872
0
949273480
4
2
1
24781
0
951038819
4
2
1
9883
0
955091044
115
1
5
115
5
0
0
115
4
4
1
2
Ranged
115
5
4
1
2
Skill in using ranged weapons such as bows, guns, flamethrowers, etc.
115
5
4
2
0
1477
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#247
Dodge

0
115
-1
-1
-1
95
-1
157
0
0
28
2
Dodge
115
1
4
1
2
quickness
115
5
5
115
5
5
115
5
5
115
5
5
115
5
2
Your muscles twitch keenly in response to combat, and you feel more confident in your ability to act under violent pressure.
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955408266
0
100
0
277
0
886
115
1
5
115
5
0
10800
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
88
4
2
1
11886
0
939470886
4
2
1
24669
0
939836949
4
2
1
16546
0
939862778
4
2
1
24490
0
939925512
4
2
1
24492
0
940366934
4
2
1
24965
0
940488313
4
2
1
24993
0
940902474
4
2
1
24725
0
941070248
4
2
1
8131
0
941604880
4
2
1
24998
0
941609360
4
2
1
9865
0
941989669
4
2
1
25003
0
942005919
4
2
1
24793
0
942221618
4
2
1
2509
0
942521485
4
2
1
13858
0
942524357
4
2
1
23424
0
942526192
4
2
1
25010
0
942911796
4
2
1
25010
0
942911805
4
2
1
20286
0
942988922
4
2
1
24663
0
943125195
4
2
1
24986
0
943208913
4
2
1
2237
0
943328077
4
2
1
24971
0
943861640
4
2
1
21824
0
944512308
4
2
1
6614
0
944519776
4
2
1
25021
0
944986785
4
2
1
20232
0
945211805
4
2
1
20718
0
945321411
4
2
1
20354
0
945507359
4
2
1
24528
0
945654783
4
2
1
25027
0
945815311
4
2
1
5907
0
945934025
4
2
1
24950
0
946527335
4
2
1
25033
0
946971367
4
2
1
12446
0
947321302
4
2
1
25030
0
947741109
4
2
1
22840
0
948738022
4
2
1
20510
0
948773526
4
2
1
24926
0
948773932
4
2
1
25007
0
949094262
4
2
1
22578
0
949112630
4
2
1
22153
0
949256855
4
2
1
25042
0
949283033
4
2
1
2800
0
949619820
4
2
1
25046
0
949644747
4
2
1
23362
0
949813238
4
2
1
21938
0
949813354
4
2
1
25041
0
949921160
4
2
1
23360
0
950122020
4
2
1
20508
0
950156937
4
2
1
10280
0
950300006
4
2
1
24173
0
950408611
4
2
1
10660
0
950463101
4
2
1
20413
0
950468364
4
2
1
24667
0
950561060
4
2
1
24369
0
950632964
4
2
1
25022
0
950993073
4
2
1
13247
0
951012128
4
2
1
24913
0
951030408
4
2
1
24781
0
951036923
4
2
1
24922
0
951134227
4
2
1
25011
0
951207884
4
2
1
24701
0
951216256
4
2
1
22034
0
951284546
4
2
1
25063
0
951528145
4
2
1
22209
0
951689087
4
2
1
24608
0
951725670
4
2
1
24947
0
951800014
4
2
1
12460
0
952040843
4
2
1
24869
0
952232872
4
2
1
24936
0
952258370
4
2
1
24740
0
952395568
4
2
1
24497
0
952568720
4
2
1
20359
0
952751542
4
2
1
9887
0
953277521
4
2
1
18522
0
953843934
4
2
1
3968
0
954054433
4
2
1
12584
0
954167960
4
2
1
20635
0
954315061
4
2
1
5587
0
954479818
4
2
1
20372
0
954981342
4
2
1
25071
0
955078476
4
2
1
25076
0
955087086
4
2
1
9883
0
955091472
4
2
1
17237
0
955113381
4
2
1
23970
0
955177110
4
2
1
24942
0
955320033
4
2
1
6644
0
955420820
115
1
5
115
5
0
0
115
4
4
2
2
Dodge
2
dodge
115
5
4
1
2
Ones general perceptive ability during combat.  Ability to dodge, and also the ability to setup for a parry attempt.
115
5
4
2
0
3732
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#248
Agility

0
115
-1
-1
-1
157
-1
249
0
0
28
2
agility
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
4
0
944498957
0
35
0
65
0
108
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
3
2
agility
2
agl
2
Ag
115
5
2
Gross agility, litheness of movement.
115
5
4
2
0
677
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#249
Dexterity

0
115
-1
-1
-1
157
-1
250
0
0
28
2
dexterity
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
4
0
919184474
0
35
0
64
0
105
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
3
2
dexterity
2
dex
2
Dx
115
5
2
Fine agility, steadiness of hand.
115
5
4
2
0
679
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#250
Endurance

0
115
-1
-1
-1
157
-1
251
0
0
28
2
endurance
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
4
0
919184474
0
32
0
62
0
108
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
4
2
endurance
2
end
2
constitution
2
con
115
5
2
Stamina, resistance to disease, poison, and shock.
115
5
4
2
0
718
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#251
Quickness

0
115
-1
-1
-1
157
-1
252
0
0
28
2
quickness
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
4
0
919184475
0
35
0
67
0
106
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
4
2
quickness
2
qu
2
speed
2
spd
115
5
2
Reflex, response time, dodging ability.
115
5
4
2
0
699
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#252
Strength

0
115
-1
-1
-1
157
-1
253
0
0
28
2
strength
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
4
0
947814856
0
18
0
61
0
108
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
9
4
2
1
23874
0
933477557
4
2
1
24913
0
933693880
4
2
1
24922
0
934038362
4
2
1
22695
0
939063259
4
2
1
13170
0
942204916
4
2
1
24733
0
946738097
4
2
1
24987
0
947294765
4
2
1
9972
0
947639870
4
2
1
23970
0
947814855
115
1
5
115
5
0
0
115
4
4
3
2
strength
2
st
2
str
115
5
2
Brute physical strength.
115
5
4
2
0
963
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#253
Willpower

0
115
-1
-1
-1
157
-1
-1
0
0
28
2
willpower
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
4
0
954344767
0
8
0
52
0
112
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
14
4
2
1
15950
0
928775256
4
2
1
23874
0
933477557
4
2
1
24922
0
934038362
4
2
1
20375
0
935858063
4
2
1
20357
0
937796458
4
2
1
22695
0
939063259
4
2
1
23360
0
940023032
4
2
1
20718
0
943215435
4
2
1
24133
0
944717000
4
2
1
24733
0
946738097
4
2
1
24987
0
947294765
4
2
1
25060
0
951035023
4
2
1
9972
0
952140843
4
2
1
25076
0
954344767
115
1
5
115
5
0
0
115
4
4
4
2
willpower
2
will
2
wil
2
wp
115
5
2
Mental endurance, resistance to pain and suggestion.
115
5
4
2
0
1167
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#254
Bladed

0
115
-1
-1
-1
242
-1
255
0
0
28
2
Bladed
115
1
4
2
2
quickness
2
dexterity
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955408227
0
105
0
318
0
879
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
60
4
2
1
25042
0
949283000
4
2
1
25041
0
949921170
4
2
1
25046
0
950054654
4
2
1
20508
0
950156286
4
2
1
12541
0
950245611
4
2
1
24173
0
950409983
4
2
1
24667
0
950556005
4
2
1
25058
0
950761875
4
2
1
24936
0
950872303
4
2
1
24976
0
950913095
4
2
1
24789
0
950950372
4
2
1
13247
0
951012569
4
2
1
24869
0
951025452
4
2
1
24913
0
951030325
4
2
1
24572
0
951036324
4
2
1
24781
0
951037098
4
2
1
20487
0
951104045
4
2
1
25050
0
951185575
4
2
1
24701
0
951216068
4
2
1
22034
0
951284917
4
2
1
24949
0
951377466
4
2
1
25063
0
951528096
4
2
1
4188
0
951586153
4
2
1
25007
0
951680177
4
2
1
14882
0
951702283
4
2
1
24839
0
951724682
4
2
1
24608
0
951725283
4
2
1
25066
0
951878801
4
2
1
12460
0
952041411
4
2
1
23400
0
952054569
4
2
1
20481
0
952114945
4
2
1
14172
0
952132002
4
2
1
24740
0
952396403
4
2
1
24497
0
952567093
4
2
1
25060
0
952606198
4
2
1
24980
0
952639890
4
2
1
20359
0
952751540
4
2
1
25022
0
952791214
4
2
1
13063
0
952908348
4
2
1
9887
0
953277699
4
2
1
12411
0
953353046
4
2
1
24923
0
953502729
4
2
1
3968
0
954054320
4
2
1
12584
0
954167964
4
2
1
20635
0
954315036
4
2
1
1142
0
954388492
4
2
1
5587
0
954480378
4
2
1
25057
0
954746929
4
2
1
25071
0
955078419
4
2
1
25076
0
955083523
4
2
1
9883
0
955091491
4
2
1
17237
0
955112074
4
2
1
25000
0
955137173
4
2
1
23970
0
955176918
4
2
1
25037
0
955191579
4
2
1
25049
0
955295943
4
2
1
18192
0
955315064
4
2
1
18717
0
955331094
4
2
1
13170
0
955408583
4
2
1
6644
0
955420493
115
1
5
115
5
0
0
115
4
4
2
2
Bladed
2
blade
115
5
4
1
2
Use of bladed weapons, encompassing both thrusting and slashing varieties.
115
5
4
2
0
2690
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#255
Blunt

0
115
-1
-1
-1
242
-1
265
0
0
28
2
Blunt
115
1
4
1
2
strength
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955408658
0
100
0
315
0
893
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
85
4
2
1
21938
0
926981903
4
2
1
23002
0
927858629
4
2
1
12180
0
928119553
4
2
1
657
0
930707651
4
2
1
5615
0
931270525
4
2
1
24902
0
931966246
4
2
1
18194
0
932168002
4
2
1
24552
0
932470119
4
2
1
24899
0
933354533
4
2
1
20422
0
933569513
4
2
1
24919
0
933724546
4
2
1
16887
0
933967621
4
2
1
23593
0
933981690
4
2
1
21835
0
934588352
4
2
1
23615
0
934699317
4
2
1
24897
0
935092576
4
2
1
13430
0
935290048
4
2
1
24943
0
935478668
4
2
1
24937
0
935535451
4
2
1
24771
0
936168244
4
2
1
24944
0
936413104
4
2
1
23164
0
936813419
4
2
1
9509
0
937543477
4
2
1
24645
0
939354527
4
2
1
24545
0
939357313
4
2
1
11886
0
939470929
4
2
1
24969
0
939491759
4
2
1
24669
0
939839316
4
2
1
18203
0
940127143
4
2
1
24492
0
940367166
4
2
1
16959
0
940954972
4
2
1
24989
0
941118170
4
2
1
8131
0
941211582
4
2
1
24525
0
941421595
4
2
1
24998
0
941609387
4
2
1
16526
0
942022585
4
2
1
24793
0
942221388
4
2
1
23424
0
942526143
4
2
1
25010
0
942911808
4
2
1
20286
0
942988823
4
2
1
2237
0
943328139
4
2
1
24926
0
943901856
4
2
1
24971
0
943944306
4
2
1
12541
0
944495416
4
2
1
22209
0
944806103
4
2
1
25021
0
945023726
4
2
1
24156
0
945139047
4
2
1
6537
0
945141849
4
2
1
25011
0
945463263
4
2
1
24528
0
945651699
4
2
1
18717
0
946952389
4
2
1
22725
0
947817653
4
2
1
22840
0
948737861
4
2
1
22034
0
949110559
4
2
1
22578
0
949110567
4
2
1
25046
0
949644745
4
2
1
25050
0
950237674
4
2
1
25000
0
950372972
4
2
1
24173
0
950409617
4
2
1
24667
0
950555892
4
2
1
25022
0
950931365
4
2
1
24789
0
950946575
4
2
1
24701
0
950960831
4
2
1
24869
0
951025324
4
2
1
24913
0
951030393
4
2
1
24781
0
951037568
4
2
1
24839
0
951216268
4
2
1
25063
0
951528129
4
2
1
4188
0
951586954
4
2
1
25055
0
951675002
4
2
1
24608
0
951725351
4
2
1
12460
0
952041258
4
2
1
20481
0
952114957
4
2
1
14172
0
952132046
4
2
1
25052
0
952135137
4
2
1
24740
0
952396104
4
2
1
24980
0
952639579
4
2
1
9887
0
953277605
4
2
1
5587
0
954480016
4
2
1
3791
0
954547166
4
2
1
25071
0
954700226
4
2
1
25049
0
954789928
4
2
1
17980
0
954802338
4
2
1
9883
0
955091142
4
2
1
6644
0
955420710
115
1
5
115
5
0
0
115
4
4
3
2
Blunt
2
blunt
2
bludgeon
115
5
4
1
2
Use of blunt weapons such as clubs and hammers, which rely primarily on the weight of the weapon for damage and striking force.
115
5
4
2
0
3538
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#256
Mechanical Tech

0
115
-1
-1
-1
95
-1
257
0
0
28
2
MechTech
115
1
4
2
2
agility
2
strength
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955319563
0
100
0
100
0
100
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
2
2
Mechanical Tech
2
MechTech
115
5
4
1
2
Skill in repairing and maintaining mechanical equipment such as vehicles, generators, and things working primarily with moving parts rather than circuitry.  Contrast with "Engineering" which is the _design_ of such equipment.
115
5
4
2
0
933
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#257
Electrical Tech

0
115
-1
-1
-1
95
-1
258
0
0
28
2
ElecTech
115
1
4
2
2
dexterity
2
willpower
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955319564
0
100
0
100
0
100
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
2
2
Electrical Tech
2
ElecTech
115
5
4
1
2
Skill in repairing and maintaining electronic equipment such as computers, video and audio equipment, and other circuit-based gizmos.  Contrast with "Engineering" which is the _design_ of such equipment.
115
5
4
2
0
914
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#258
Focus

0
115
-1
-1
-1
95
-1
-1
0
0
28
2
Focus
115
1
4
1
2
willpower
115
5
5
115
5
5
115
5
5
115
5
5
115
5
2
You experience a moment of undiluted concentration-- your intent clear, your thoughts startlingly lucid.
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955319564
0
100
0
246
0
769
115
1
4
2
0
100
0
800
115
5
0
10800
115
5
0
1
115
5
5
115
5
5
115
5
5
115
1
4
15
4
2
1
18194
0
932167772
4
2
1
6143
0
939221879
4
2
1
8131
0
941604888
4
2
1
21938
0
945719488
4
2
1
19315
0
948571016
4
2
1
24667
0
950556202
4
2
1
24817
0
950768503
4
2
1
20487
0
951277706
4
2
1
24839
0
951724832
4
2
1
24608
0
951725839
4
2
1
24740
0
952395574
4
2
1
9887
0
953277352
4
2
1
16551
0
953554414
4
2
1
25037
0
955191456
4
2
1
18717
0
955409724
115
1
5
115
5
0
0
115
4
4
2
2
Focus
2
foc
115
5
4
1
2
Mainly used for the Mage talents, yet could be used for just about anything that called for concentration of focus.
115
5
4
2
0
1397
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#259
Bite

0
115
-1
-1
-1
241
-1
260
0
0
28
2
Bite
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
2
You clench your jaws, snapping blood-stained teeth together with a newfound appreciation of martial mastication.
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955319531
0
110
0
362
0
774
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
10
4
2
1
24677
0
926731050
4
2
1
22904
0
929198589
4
2
1
24914
0
940107882
4
2
1
18203
0
940127078
4
2
1
2800
0
949619820
4
2
1
12584
0
950552370
4
2
1
13247
0
951012681
4
2
1
24781
0
951038831
4
2
1
24869
0
952234472
4
2
1
24942
0
955320061
115
1
5
115
5
0
0
115
4
4
2
2
Bite
2
chomp
115
5
4
1
2
Use of ones teeth during unarmed melee combat.  Flexible necks a must.
115
5
4
2
0
1149
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#260
Claw

0
115
-1
-1
-1
241
-1
261
0
0
28
2
Claw
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
2
You extend and retract your claws, feeling a new power pulse through your flesh-rending digital extensions.
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955293234
0
108
0
289
0
900
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
20
4
2
1
21938
0
926982033
4
2
1
20348
0
929668296
4
2
1
24841
0
930007989
4
2
1
657
0
930008159
4
2
1
24582
0
930437663
4
2
1
24683
0
931070584
4
2
1
24934
0
937070947
4
2
1
20121
0
939429784
4
2
1
9869
0
939887688
4
2
1
24528
0
945654139
4
2
1
25033
0
946971369
4
2
1
24789
0
950959356
4
2
1
24789
0
950959417
4
2
1
13247
0
951012605
4
2
1
24781
0
951037664
4
2
1
24677
0
951413299
4
2
1
12584
0
952462340
4
2
1
25060
0
952606085
4
2
1
24590
0
954889833
4
2
1
18717
0
955331024
115
1
5
115
5
0
0
115
4
4
2
2
Claw
2
swing
115
5
4
1
2
Use of ones "hands" during unarmed melee combat.  Geared towards creatures with arms and hands.  See "Flail" if you have tentacles.
115
5
4
2
0
1525
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#261
Flail

0
115
-1
-1
-1
241
-1
262
0
0
28
2
Flail
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
2
Your flailing limbs feel like sentient rubber, possessing keen instincts of their own, but subservient to your combative will.
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955319564
0
109
0
213
0
767
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
4
4
2
1
18203
0
941670583
4
2
1
24528
0
945651735
4
2
1
22578
0
949110734
4
2
1
24942
0
955320134
115
1
5
115
5
0
0
115
4
4
4
2
Flail
2
tail
2
tentacle
2
flay
115
5
4
1
2
Use of tentacles, tails, and other "whip it" appendages.  Chiefly of use for giant squid, stegosaurus, and mind-flayers.
115
5
4
2
0
1053
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#262
Brawl

0
115
-1
-1
-1
241
-1
-1
0
0
28
2
Brawl
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
2
Your nails dig into your palms as you clench your fists, nearly drawing blood in your frenzy for battleground glory.
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955408266
0
102
0
285
0
886
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
72
4
2
1
24777
0
940891375
4
2
1
24989
0
941118180
4
2
1
18522
0
941309844
4
2
1
17488
0
941405889
4
2
1
24525
0
941421822
4
2
1
8131
0
941604869
4
2
1
24998
0
941609352
4
2
1
13858
0
941689564
4
2
1
9865
0
941989649
4
2
1
25003
0
942005938
4
2
1
16526
0
942023197
4
2
1
24817
0
942214268
4
2
1
24663
0
943126802
4
2
1
2237
0
943328569
4
2
1
20611
0
943692676
4
2
1
24971
0
943746347
4
2
1
24926
0
943901888
4
2
1
12541
0
944495471
4
2
1
24911
0
945055252
4
2
1
6537
0
945143848
4
2
1
24950
0
945450904
4
2
1
24646
0
945481584
4
2
1
20354
0
945505467
4
2
1
24133
0
945731229
4
2
1
25025
0
945804987
4
2
1
24690
0
947404133
4
2
1
22840
0
948738038
4
2
1
23360
0
948902732
4
2
1
25007
0
949094246
4
2
1
22034
0
949111706
4
2
1
21835
0
949112634
4
2
1
22153
0
949260219
4
2
1
25041
0
949333359
4
2
1
23970
0
949552239
4
2
1
10660
0
949777958
4
2
1
21938
0
949812959
4
2
1
23362
0
949813238
4
2
1
24173
0
950409723
4
2
1
24667
0
950556435
4
2
1
24369
0
950632978
4
2
1
25052
0
950899405
4
2
1
24976
0
950918247
4
2
1
24789
0
950950347
4
2
1
24869
0
951025274
4
2
1
24572
0
951027343
4
2
1
24922
0
951134252
4
2
1
25011
0
951208010
4
2
1
4188
0
951586740
4
2
1
24608
0
951725560
4
2
1
24947
0
951867844
4
2
1
25066
0
951879096
4
2
1
25069
0
951891681
4
2
1
12460
0
952041228
4
2
1
23400
0
952056340
4
2
1
24936
0
952258382
4
2
1
20635
0
952268370
4
2
1
25060
0
952589027
4
2
1
13063
0
952905860
4
2
1
9887
0
953277471
4
2
1
14172
0
953353040
4
2
1
24733
0
953758521
4
2
1
6143
0
954177010
4
2
1
5587
0
954480529
4
2
1
25057
0
954749738
4
2
1
17980
0
954802427
4
2
1
24590
0
954889815
4
2
1
20372
0
954981409
4
2
1
25071
0
955075138
4
2
1
9883
0
955091007
4
2
1
25037
0
955189700
4
2
1
13170
0
955408720
4
2
1
6644
0
955420904
115
1
5
115
5
0
0
115
4
4
4
2
Brawl
2
brawling
2
fighting
2
pugilism
115
5
4
1
2
The traditional pugilistic arts.  Non-clawed, ducking, weaving, bobbing, bare-fisted ACTION ACTION ACTION.
115
5
4
2
0
3213
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#263
Throwing

0
115
-1
-1
-1
246
269
264
0
0
28
2
Throwing
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955319564
0
106
0
218
0
485
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
6
4
2
1
24608
0
932295269
4
2
1
24572
0
932295305
4
2
1
24490
0
939925395
4
2
1
22209
0
943145517
4
2
1
24781
0
951038819
4
2
1
9883
0
955091044
115
1
5
115
5
0
0
115
4
4
2
2
Throwing
2
thrown
115
5
4
1
2
Skill at throwing things.  Rocks, knives, grenades, bowling balls, anvils, hammers, geese.
115
5
4
2
0
941
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#264
Shooting

0
115
-1
-1
-1
246
-1
270
0
0
28
2
Shooting
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955319564
0
100
0
166
0
215
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
3
4
2
1
6537
0
944713958
4
2
1
22872
0
949273480
4
2
1
22209
0
949464949
115
1
5
115
5
0
0
115
4
4
2
2
Shooting
2
shoot
115
5
4
1
2
The use of mechanical weapons such as firearms and crossbows.
115
5
4
2
0
815
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#265
Whip

0
2
-1
-1
-1
242
-1
268
0
0
28
2
Whip
115
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
115
1
4
4
0
955408658
0
100
0
345
0
806
115
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
4
37
4
2
1
21938
0
926981946
4
2
1
24850
0
927393030
4
2
1
12180
0
928118730
4
2
1
24884
0
928558330
4
2
1
24902
0
931965880
4
2
1
24805
0
934672810
4
2
1
14123
0
934760775
4
2
1
24492
0
934929002
4
2
1
20209
0
938132283
4
2
1
24645
0
939355858
4
2
1
11886
0
939470776
4
2
1
18203
0
940127163
4
2
1
18522
0
940916629
4
2
1
20286
0
942988865
4
2
1
22209
0
943145760
4
2
1
24926
0
943903865
4
2
1
12541
0
944495493
4
2
1
25025
0
945575388
4
2
1
19315
0
948571566
4
2
1
22840
0
948737907
4
2
1
22034
0
949111384
4
2
1
25050
0
950329741
4
2
1
24173
0
950409151
4
2
1
24667
0
950555811
4
2
1
24781
0
951038943
4
2
1
25022
0
951282119
4
2
1
24608
0
951725597
4
2
1
12460
0
952041209
4
2
1
25055
0
952055165
4
2
1
24740
0
952396175
4
2
1
24980
0
952639511
4
2
1
9887
0
953277598
4
2
1
5587
0
954480233
4
2
1
20372
0
954981288
4
2
1
9883
0
955091088
4
2
1
18717
0
955409780
4
2
1
6644
0
955420678
115
1
5
2
5
0
0
2
4
4
1
2
Whip
2
5
4
1
2
Skill in using flails, whips, morning-stars, and so on.
2
5
4
2
0
1871
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#266
Lockpick

0
115
-1
-1
-1
95
-1
247
0
0
28
2
Lockpick
115
1
4
2
2
dexterity
2
willpower
115
5
5
115
5
0
-10
115
5
5
115
5
5
115
5
2
Your nimble fingers seem to be able to do this lockpicking maneuver by themselves.
115
5
5
115
5
5
115
5
5
115
1
4
4
0
955319563
0
100
0
100
0
100
115
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
4
3
4
2
1
12584
0
941081263
4
2
1
13858
0
942524178
4
2
1
434
0
948344804
115
1
5
115
5
0
0
115
4
4
3
2
Lockpick
2
lockpick
2
picklock
115
5
4
1
2
Used for picking mechanical locks.  Traditional locks, in general, and not electronic mechanism with more circuits than tumblers.
115
5
4
2
0
1030
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#267
Javelin

0
2
-1
-1
-1
246
-1
-1
0
0
28
2
Javelin
115
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
115
1
4
4
0
955319565
0
100
0
100
0
100
115
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
4
0
115
1
5
2
5
0
0
2
4
4
2
2
Javelin
2
Spear
2
5
4
1
2
Use of an elongated, aerodynamic thrown weapon such as a javelin or spear.
2
5
4
2
0
729
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#268
Trident

0
2
-1
-1
-1
242
-1
-1
0
0
28
2
Trident
115
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
115
1
4
4
0
955408659
0
123
0
358
0
854
115
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
4
33
4
2
1
24850
0
927393729
4
2
1
12446
0
930141900
4
2
1
24841
0
930351119
4
2
1
24902
0
931467411
4
2
1
24904
0
932111671
4
2
1
20209
0
938134066
4
2
1
21938
0
938926979
4
2
1
24949
0
939414810
4
2
1
24949
0
939414830
4
2
1
11886
0
939470667
4
2
1
24490
0
939925391
4
2
1
18203
0
940127179
4
2
1
15637
0
940376355
4
2
1
18522
0
940916958
4
2
1
24986
0
941592601
4
2
1
24926
0
943903919
4
2
1
22725
0
944532620
4
2
1
24957
0
946589019
4
2
1
24690
0
947402236
4
2
1
19315
0
948571565
4
2
1
22840
0
948739699
4
2
1
22578
0
949112748
4
2
1
23360
0
950123888
4
2
1
24173
0
950409099
4
2
1
24667
0
950555527
4
2
1
24913
0
951030250
4
2
1
24572
0
951036852
4
2
1
24781
0
951036983
4
2
1
24740
0
952395529
4
2
1
9887
0
953277529
4
2
1
5587
0
954480654
4
2
1
9883
0
955091295
4
2
1
6644
0
955421097
115
1
5
2
5
0
0
2
4
4
2
2
Trident
2
Pitchfork
2
5
4
1
2
Use of a long, multi-pronged thrusting weapon such as a trident or pitchfork.
2
5
4
2
0
1792
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#269
Discus

0
2
-1
-1
-1
263
-1
-1
0
0
28
2
Discus
115
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
115
1
4
4
0
955319565
0
132
0
140
0
148
115
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
4
1
4
2
1
9883
0
955091044
115
1
5
2
5
0
0
2
4
4
1
2
Discus
2
5
4
1
2
Use of spinning thrown weapons, such as well, uh, a frisbee?  A razor-rimmed hat?  Heavy discus?  Discs of Tron??
2
5
4
2
0
783
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#270
Archery

0
2
-1
-1
-1
246
-1
271
0
0
28
2
Archery
115
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
115
1
4
4
0
955319565
0
100
0
100
0
100
115
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
4
0
115
1
5
2
5
0
0
2
4
4
1
2
Archery
2
5
4
1
2
Use of longbows, shortbows, any bows but crossbow-type weapons.  Crossbows should use Shooting.
2
5
4
2
0
736
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#271
Sling

0
2
-1
-1
-1
246
-1
267
0
0
28
2
Sling
115
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
115
1
4
4
0
955319565
0
100
0
100
0
100
115
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
4
0
115
1
5
2
5
0
0
2
4
4
1
2
Sling
2
5
4
1
2
Use of slings and slingshots.  Yes, the dynamics of those two weapons are different, but I'm afraid each doesn't quite rate its own skill.
2
5
4
2
0
773
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#272
fist message set

144
2
-1
-1
-1
161
-1
-1
0
0
20
5
2
5
4
3
2
%N %<swings> %p %t at %d.
2
%N %<arcs> a fist of fury towards %d.
2
%N %<draws> a path of whirling fists towards %d.
2
5
4
3
2
%N %<misses/miss>.
2
%N %<demonstrates> %p pugilistic ineptitude by sending %p punch nearly a foot away from %d.
2
%D %<d:jerks> instinctively, watching in amusement as the punch goes way off.
2
5
4
3
2
%D %<d:dodges/dodge>.
2
Sharp reflexes bring %d well out of the way of %p strike.
2
%D %<d:leaps> to one side, well clear of %[dpp] attacker's mitts.
2
5
4
3
2
%D %<d:parries> with %[dpp] %i.
2
%D %<d:shrugs> the strike aside with %[dpp] %i.
2
%D %<d:raises> %[dpp] %i, easily deflecting the attack.
2
5
4
2
2
%D %<d:receives> a %severity wound in the %l.
2
An iron fist smacks into the lazy flesh of %d's %l.
2
5
4
1
2
%N %<slams> a savage strike across %d's jaw, then %<follows> through with a brutal hammer to the gut.  Nothing happens for a few seconds, then %d's mouth erupts with a flow of blood and vomit.  %D %<d:falls> dead.
2
5
4
2
2
%D %<d:is> knocked unconscious by the blow.
2
The force of the punch cracks %d into an unconscious pile of weary flesh.
2
5
4
3
2
A burst of vomit issues forth from %d, splattering the area with %[dpp] most recent meal.  The mighty force of %n's fist churns %[dpp] middle into a mush of organs, soon retracting for a second needless strike as %d's body slumps into death.
2
The crack of a breastbone is sickening enough for most men, but the following squish of upper body organs being rent by shattered bone could nauseate even the most hardened pugilist.  %D %<d:drops> forward, face smacking dead into the dust.
2
A heavy sigh issues forth from %d's lips.  Relief?  Only if one views death as such.  %N %<reaches> into %[dpp] chest, fist sinking into flesh rent by shattered ribs, squeezing both lungs in the apex of %p brutal swing and tossing them aside.
2
5
4
3
2
Ducking deep, thrusting upward, %n's fist slams into the lower middle of %d, reaching into %[dpp] genital area and pulling forth a handful of viscera that may've been %[dpp] future children.  The end of %[dpp] bloodline pools muddy in the dust.
2
A howl of agony shakes free the tight-lipped combat scowl of %d as %n %<buries> %p fist into %[dpp] groin.  A sickening squish of reproductive organs oozes down %[dpp] leg.  %D %<d:slumps> dead into the mess.
2
"Fisting" takes on a whole new meaning as the iron mitt of %n sinks into some nether orifice of %d, disrupting organs beyond repair.  %D %<d:collapses> into post-mortem bliss.
2
5
4
3
2
What followed that punch would have caused even the most sociopathic butcher to cringe in disgust.  Suffice it to say that it took several minutes to extract %n's fist from %d's head.
2
To spectators, the punch appears to have caused merely a flinch.  %N and, sadly, %d, know better.  %D %<d:rolls> %[dpp] head to the side, exposing loose folds of flesh rolling off a skull half-smashed.  An eyeball pops from a one-time socket, rolling across the ground.
2
A dull shattering sound heralds the arrival of %p fist into %d's gaping maw.  %N %<extracts> a bloody hand peppered with pinkish shards, shaking loose the teeth as %d %<d:drops> gums-down into the dust--dead.
2
5
0
0
2
4
4
2
2
fist message set
2
fms
2
5
5
2
5
4
2
0
3791
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#273
head

16
115
-1
-1
-1
158
-1
274
1
title
115
173
-1
1
name_task
21
4
2
0
1456393868
2
ear
115
1
0
350
115
5
5
115
5
0
-47
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
0
-250
115
1
5
115
1
5
115
5
0
0
115
4
4
3
2
head
2
noggin
2
skull
115
5
5
115
5
4
2
0
1018
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#274
throat

16
115
-1
-1
-1
158
-1
275
0
0
20
0
185
115
5
5
115
5
0
-47
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
0
-85
115
1
5
115
1
5
115
5
0
0
115
4
4
2
2
throat
2
neck
115
5
5
115
5
4
2
0
451
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#275
left shoulder

16
115
-1
-1
-1
158
-1
276
0
0
20
5
115
5
5
115
5
0
-42
115
5
0
15
115
5
0
10
115
5
0
40
115
5
0
50
115
5
5
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
3
2
left shoulder
2
shoulder
2
shoulders
115
5
5
115
5
4
2
0
487
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#276
right shoulder

16
115
-1
-1
-1
158
-1
277
0
0
20
5
115
5
5
115
5
0
-42
115
5
0
15
115
5
0
10
115
5
0
40
115
5
0
50
115
5
5
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
3
2
right shoulder
2
shoulder
2
shoulders
115
5
5
115
5
4
2
0
489
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#277
chest

16
115
-1
-1
-1
158
-1
278
0
0
20
0
200
115
5
5
115
5
0
-25
115
5
0
50
115
5
5
115
5
5
115
5
5
115
5
5
115
5
0
-100
115
1
5
115
1
5
115
5
0
0
115
4
4
2
2
chest
2
torso
115
5
5
115
5
4
2
0
450
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#278
left upper arm

16
115
-1
-1
-1
158
-1
279
0
0
20
5
115
5
5
115
5
0
-46
115
5
0
8
115
5
0
0
115
5
0
20
115
5
0
25
115
5
5
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
6
2
left upper arm
2
left arm
2
arm
2
arms
2
upper arm
2
upper arms
115
5
5
115
5
4
2
0
533
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#279
right upper arm

16
115
-1
-1
-1
158
-1
280
0
0
20
5
115
5
5
115
5
0
-46
115
5
0
8
115
5
0
5
115
5
0
20
115
5
0
25
115
5
5
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
6
2
right upper arm
2
right arm
2
arm
2
arms
2
upper arm
2
upper arms
115
5
5
115
5
4
2
0
536
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#280
left forearm

16
115
-1
-1
-1
158
-1
281
0
0
20
5
115
5
5
115
5
0
-47
115
5
5
115
5
0
0
115
5
0
15
115
5
0
25
115
5
5
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
9
2
left forearm
2
left lower arm
2
left arm
2
arm
2
arms
2
lower arm
2
lower arms
2
forearm
2
forearms
115
5
5
115
5
4
2
0
585
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#281
right forearm

16
115
-1
-1
-1
158
-1
282
0
0
20
5
115
5
5
115
5
0
-47
115
5
5
115
5
0
0
115
5
0
15
115
5
0
25
115
5
5
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
9
2
right forearm
2
right lower arm
2
right arm
2
arm
2
arms
2
lower arm
2
lower arms
2
forearm
2
forearms
115
5
5
115
5
4
2
0
589
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#282
left hand

16
115
-1
-1
-1
158
-1
283
0
0
20
5
115
5
5
115
5
0
-49
115
5
0
2
115
5
0
0
115
5
0
5
115
5
0
15
115
5
5
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
3
2
left hand
2
hand
2
hands
115
5
5
115
5
4
2
0
471
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#283
right hand

16
115
-1
-1
-1
158
-1
284
0
0
20
5
115
5
5
115
5
0
-49
115
5
0
2
115
5
0
0
115
5
0
5
115
5
0
15
115
5
5
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
3
2
right hand
2
hand
2
hands
115
5
5
115
5
4
2
0
473
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#284
abdomen

16
115
-1
-1
-1
158
-1
285
0
0
20
0
200
115
5
5
115
5
0
-42
115
5
0
15
115
5
5
115
5
5
115
5
5
115
5
5
115
5
0
-100
115
1
5
115
1
5
115
5
0
0
115
4
4
5
2
abdomen
2
belly
2
tummy
2
midsection
2
midriff
115
5
5
115
5
4
2
0
503
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#285
groin

16
115
-1
-1
-1
158
-1
286
0
0
20
0
185
115
5
5
115
5
0
-47
115
5
5
115
5
0
75
115
5
0
95
115
5
0
0
115
5
0
25
115
5
0
-85
115
1
5
115
1
5
115
5
0
0
115
4
4
5
2
groin
2
privates
2
nuts
2
balls
2
genitals
115
5
5
115
5
4
2
0
497
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#286
left thigh

16
115
-1
-1
-1
158
-1
287
0
0
20
5
115
5
5
115
5
0
-42
115
5
0
15
115
5
0
5
115
5
0
25
115
5
5
115
5
0
50
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
8
2
left thigh
2
left upper leg
2
leg
2
legs
2
upper leg
2
upper legs
2
thigh
2
thighs
115
5
5
115
5
4
2
0
560
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#287
right thigh

16
115
-1
-1
-1
158
-1
288
0
0
20
5
115
5
5
115
5
0
-42
115
5
0
15
115
5
0
5
115
5
0
25
115
5
5
115
5
0
50
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
8
2
right thigh
2
right upper leg
2
leg
2
legs
2
upper leg
2
upper legs
2
thigh
2
thighs
115
5
5
115
5
4
2
0
563
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#288
left calf

16
115
-1
-1
-1
158
-1
289
0
0
20
5
115
5
5
115
5
0
-47
115
5
5
115
5
0
0
115
5
0
25
115
5
5
115
5
0
40
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
8
2
left calf
2
left lower leg
2
leg
2
legs
2
lower leg
2
lower legs
2
calf
2
calves
115
5
5
115
5
4
2
0
557
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#289
right calf

16
115
-1
-1
-1
158
-1
290
0
0
20
5
115
5
5
115
5
0
-47
115
5
5
115
5
0
0
115
5
0
25
115
5
5
115
5
0
40
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
8
2
right calf
2
right lower leg
2
leg
2
legs
2
lower leg
2
lower legs
2
calf
2
calves
115
5
5
115
5
4
2
0
560
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#290
left foot

16
115
-1
-1
-1
158
-1
291
0
0
20
5
115
5
5
115
5
0
-49
115
5
0
2
115
5
0
0
115
5
0
5
115
5
5
115
5
0
40
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
3
2
left foot
2
foot
2
feet
115
5
5
115
5
4
2
0
470
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#291
right foot

16
115
-1
-1
-1
158
-1
-1
0
0
20
5
115
5
5
115
5
0
-49
115
5
0
2
115
5
0
0
115
5
0
5
115
5
5
115
5
0
40
115
5
5
115
1
5
115
1
5
115
5
0
0
115
4
4
3
2
right foot
2
foot
2
feet
115
5
5
115
5
4
2
0
472
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#292
chainsaw message set

16
115
-1
-1
-1
161
-1
239
0
0
20
5
115
5
4
1
2
%N %<slices> %p roaring %t at %d.
115
5
4
1
2
%S %<swings> wide, struggling to heft %p savage weapon back into play.
115
5
4
1
2
%D %<d:leaps> aside.
115
5
4
1
2
Sparks fly as %d %<d:holds> back the blow with %[dpp] clattering chain.
115
5
4
2
2
Blood and bone spatters from %[dpo] as the roaring chain cuts a %severity wound in %[dpp] %l.
2
%[dpsc] %<d:howls> in abject agony as the steel chain mauls %[dpp] shredded flesh.
115
5
4
3
2
The roaring chainsaw rips easily through %[dpp] torso.  %[dppc] death is hailed by a wet spray of steaming viscera and tortured death yowls.
2
An eerie silence falls as the blade sputters to a stop, caught in the midst of the gnarled entrails of the now-very-dead %d.
2
The clattering chain is muffled briefly as it tears through %[dpp] body, slicing %[dpo] clean in half, resuming its horrid death rattle as it spatters blood through the air.
115
5
5
115
5
4
2
2
%[Dpsc] %<d:yowls> in terror as %s %<rips> the razor chain through %[dpp] midsection, cutting through %[dpo] like a hungry hound.  The metal teeth tear %[dpo] completely in half, %[dpp] screams fading to gurgles and then to dead silence.
2
%S mercilessly %<thrusts> %p chainsaw into %[dpp] chest.  Lungs are ripped open; %[dps] %<d:gasps> for air.  Belly is gutted; %[dpp] lunch sprays the area.  Heart explodes; %[dps] %<d:is> silent as %[dpp] body drops to a pile of shreds.
115
5
4
3
2
%S %<pushes> the rattling blade into %[dpp] groin.  %[Dps] %<d:howls> with hellish pain, %[dpp] screams soon drown by the roar of the chainsaw and the heavy silence of death.
2
The clattering chain plunges into %[dpp] groin.  It cuts directly through, leaving %[dpo] speechless with pain.  Testicles and penis are completely shredded, spattering onto the horrified faces of onlookers.  Merciful death comes soon after.
2
%[Dppc] scream chills the air, a final death yowl frozen into %[dpp] features as the roaring razored maw of the chainsaw devours %[dpp] genitals.
115
5
4
2
2
Sparks fly from the brainpan as the teeth of the chainsaw cut through %[dpp] head, screeching horribly and then breaking through with a deep wet splat.  %[dppc] brain scrambles, sending shreds of %[dpp] mind into the air.
2
%[Dppc] mind is reduced to a cloud of vapour as the chainsaw crashes easily into the back of %[dpp] skull, severing the brain stem and sending %[dpo] instantly into the blackness of death.
115
5
0
0
115
4
4
1
2
chainsaw message set
115
5
5
115
5
4
2
0
2930
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#293
blunt weapon set

144
115
-1
-1
-1
161
-1
272
0
0
20
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
4
2
2
The solid THWOK of weapon against bone heralds a %severity strike to %[dpp] %l.
2
%[tdnamec] pounds a bruise in %[dpp] body as it crashes home for a %severity wound in the %l.
115
5
4
1
2
%[tdnamec] sinks into %[dpp] skull, sending small shards of bone flying as it crushes %[dpp] brain stem and brings down the black curtain of death on %[dpp] life.
115
5
4
1
2
%D %<d:gasps>, wind knocked out of %[dpo] as %n %<scores> a solid strike to %[dpp] %l.  %D %<d:slumps> into an unconscious heap.
115
5
4
2
2
A violent swing sends the blunt force of %[tdname] into %[dpp] ribs, ramming several into %[dpp] lung.  %[Dpsc] %<d:falls> the ground gasping, soon after expiring as blood fills %[dpp] airbags.
2
%S %<hammers> down on %[dpp] collarbone, sending it deep into %[dpp] chest.  The trachea is crushed, the heart is punctured, and blood flows from a bone-studded wound on %[dpp] shoulder.  %[Dpsc] %<d:collapses> onto %[dpp] knees, grasping %[dpp] throat as each breath seeps away.  Slowly, %[dps] %<d:slumps> into death by asphyxiation.
115
5
4
2
2
%N %<clubs> brutally at %[dpp] side, smashing %[dpp] hip with the force of the blow, dislocating %[dpp] leg and sending the femur ripping through the skin and puncturing %[dpp] genitals.  %D %<d:screams> with hellish agony before dropping into a brief pre-death coma, expiring senseless in a pool of %[dpp] own blood.
2
%N %<hammers> %[dpp] genitals, smashing them into %[dpp] body as %[dps] %<d:lets> go with a primal scream of tortured horror.  The shock is too much, and %d soon %<d:drops> into unconscious.  Death from massive internal damage soon follows.
115
5
4
5
2
%S %<sinks> %[tdname] into %[dpp] skull.  Shards of bone fly through the air, soon followed by a sickening SQUISH and a spray of blood and cranial fluid.
2
The blunt edge of %p blow crashes full on into %[dpp] skull.  %[Dppc] eyes shoot out from their sockets as %[dpp] skull implodes with the brutal force.
2
The brutal strike causes %[dpp] head to explode.  %[Dppc] blood soaks everything in the vicinity, mottled with bits of skull.
2
%S %<bats> %[tdname] across the side of %[dpp] head, completely disconnecting it from %[dpp] neck and sending it flying into a nearby solid.  It explodes on impact, splattering %[dpp] former mind over the area.
2
The full force of the violent strike sends %[tdname] crashing through %[dpp] skull, scrambling %[dpp] brain in its pain.
115
5
0
0
115
4
4
2
2
blunt weapon set
2
bws
115
5
5
115
5
4
2
0
2941
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#294
shot message set

16
115
-1
-1
-1
239
-1
-1
0
0
20
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
5
115
5
4
3
2
The swarm of shot rushes into %[dpp] belly, completely devouring %[dpp] midsection.  %[dps] %<d:falls> backward into a steaming pool of %[dpp] own vital organs.
2
The shot rips through %[dpp] midsection, blasting a cavity where once was %[dpp] torso.  Body severed below the waist, %[dps] %<d:drops> into a mangled pile of death.
2
%[Dppc] torso explodes into vapour, leaving only a steaming pile of arms, legs, and an open-mouthed death head.
115
5
4
2
2
%[Dppc] groin explodes, sending procreative organs spattering into pools of blood and sex.  %[Dpsc] %<d:collapses> from shock and soon %<d:expires> with a pathetic blood-muffled gurgle.
2
%[Dpsc] %<d:opens> %[dpp] mouth to scream, but %[dpp] final yowl never surfaces.  %[Dppc] groin is shredded into strips of flesh pasta, blood pouring from the erstwhile genitals.  Death comes soon after, thank God.
115
5
4
2
2
%[Dppc] head explodes with the force of a hundred supersonic metal pellets, spattering what was once %[dpp] consciousness into a thick red stain behind %[dpo].
2
%[Dpsc] %<d:does> not have time to scream.  A roar of shot heralds the devouring of %[dpp] head into nothing more than a terminating spinal stump.
115
5
0
0
115
4
4
4
2
shot message set
2
sms
2
shotgun
2
shells
115
5
5
115
5
4
2
0
1729
0
955495071
100
1
5
115
5
5
115
5
5
115
5
5
115
5
5
115
1
#295
UTILITY PROGRAMMER

16
2
-1
-1
-1
57
36
-1
0
0
185
4
0
36
1
0
1
2
5
0
0
2
5
5
2
5
0
48
2
5
0
0
2
1
4
0
2
0
5
2
5
5
36
0
5
36
1
5
2
0
5
2
5
5
2
0
5
2
1
0
0
2
5
5
2
5
5
36
1
5
36
1
4
0
36
0
5
36
1
5
2
5
4
0
2
1
5
2
4
4
0
2
0
5
2
0
4
0
2
4
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
2
4
4
0
2
4
5
2
1
5
100
0
5
2
0
5
2
5
5
2
1
4
0
115
1
5
2
5
1
159
2
5
5
100
1
5
2
0
5
2
5
5
2
1
5
36
1
5
2
5
5
115
1
4
0
36
0
5
2
5
5
115
1
5
115
1
5
36
0
5
115
1
5
115
0
1
151
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
115
1
5
115
1
5
115
1
1
151
115
1
5
115
1
1
-1
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
5
2
5
5
115
1
5
115
1
1
206
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
2
5
5
2
5
1
61
2
5
5
2
5
5
36
1
5
2
5
5
2
5
5
2
5
5
2
5
5
2
1
5
115
1
5
2
5
5
2
1
5
2
1
5
2
1
5
2
1
5
2
4
5
2
5
5
2
4
5
2
5
5
115
0
5
115
1
5
36
0
5
115
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
5
2
4
5
2
4
5
36
1
5
2
5
5
2
5
0
0
2
4
4
1
2
UTILITY PROGRAMMER
2
1
2
An organizational parent for utility characters.
2
5
4
2
0
3151
0
955412150
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#296
Player Start Parent

16
2
-1
-1
-1
297
61
-1
1
init_for_core
2
173
-1
0
53
5
36
1
5
2
5
5
2
5
5
2
5
5
36
1
5
36
1
5
2
5
5
36
1
5
2
5
5
2
5
5
36
1
5
36
1
5
36
0
5
36
0
5
36
1
5
36
1
5
36
1
5
2
5
5
2
5
5
2
5
5
115
1
5
115
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
4
5
2
5
5
2
5
5
2
4
5
2
5
5
2
5
5
2
5
5
2
5
5
113
1
5
2
4
5
2
4
5
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
Player Start Parent
2
psp
2
5
2
At the time of its creation, simply something to hold the various verbs needed to make $player_start do what it does.  Namely, move disconnected folks to $limbo.
2
5
4
2
0
1903
0
955412150
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#297
generic disconnexus

144
36
-1
-1
-1
298
296
-1
3
disfunc
2
165
-1
enterfunc
2
173
-1
init_for_core
2
173
-1
2
limbo
osuck_msg
53
1
15
36
1
2
%(inamec) %<is> sucked inside %r, disappearing with a vacuous pop of finality.
36
5
5
36
5
5
36
5
4
0
36
1
4
0
36
1
5
36
5
5
36
1
5
36
5
5
36
5
5
36
1
5
36
1
5
36
0
5
36
0
5
36
1
5
36
1
5
36
1
5
36
5
5
36
5
5
36
5
5
115
1
4
0
115
1
5
36
5
5
36
5
0
0
36
5
5
36
5
5
36
5
5
36
5
4
0
36
5
1
115
36
5
5
36
4
5
36
5
5
36
5
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
0
0
113
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
3
2
generic disconnexus
2
disconnexus
2
gdx
36
5
2
A room which sends players who have set their homes here to a predefined limbo (this.limbo) when they disconnect.  That limbo location will send them back to their home when they reconnect.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#298
generic peaceful room

144
36
-1
-1
-1
299
196
-1
4
attempt_action
36
173
-1
enterfunc
36
173
-1
set_banned_from_here set_peace_exceptions
36
173
-1
init_for_core
2
173
-1
4
peace_msg
opeace_msg
banned_from_here
peace_exceptions
51
2
Before you can take action, piercingly bright green runes flare in the air around you, magically preventing you.
36
5
2
%N %<is/are> surrounded by bright green flashes that momentarily %<disrupts> %p actions.
36
5
4
0
36
1
4
0
36
1
5
36
5
5
36
1
5
36
5
5
36
5
0
1
36
1
5
36
1
5
36
0
4
1
1
3175
36
0
5
36
1
5
36
1
5
36
1
5
36
5
5
36
5
5
36
5
0
1536325521
115
1
4
0
115
1
5
36
5
5
36
5
0
0
36
5
5
36
5
5
36
5
5
36
5
4
0
36
5
1
115
36
5
4
0
36
4
1
-1
36
5
0
1052092626
36
5
4
0
36
4
5
36
5
5
36
5
5
36
5
5
36
5
0
0
113
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
1
2
generic peaceful room
36
5
2
Check help here for more info about how to customize this room.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
4
10
2
This room is a no-combat-action location.
2
@peace <room> is Magical runes flare preventing your violent action.
2
@opeace <room> is %N's face looks more peaceful as magical runes flare.
2

2
For a list of objects that can't be dropped/@moved in the room,
2
@set <room>.banned_from_here to {<object 1>, <object 2>, ...}
2

2
By defaults, #3175  (generic explosive) are banned from the peacefull rooms.
2

2
Note: This generic is also a descendant of #20076  (generic magic protected room), please check the help on that generic about protection from coded spells....
36
5
5
115
1
#299
generic RP-enhanced room

144
36
-1
-1
-1
300
298
-1
4
enter_junk
36
173
-1
enterfunc
36
173
-1
match_exit
36
173
-1
exits
36
173
-1
4
log_number
entered_list
min_name_length
hide_hidden_exits
47
0
0
36
5
4
0
36
1
0
0
36
5
0
0
36
5
5
36
1
5
36
1
5
36
0
5
36
0
5
36
1
5
36
1
5
36
1
5
36
5
5
36
5
5
36
5
5
115
1
4
0
115
1
5
36
5
5
36
5
0
0
36
5
5
36
5
5
36
5
5
36
5
4
0
36
5
1
115
36
5
5
36
4
1
-1
36
5
0
1870877324
36
5
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
0
0
113
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
1
2
generic RP-enhanced room
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
4
17
2

2
-- Entrance/Exit Logging --
2

2
Every time anything enters the room, it is added to the end of the
2
 .entered_list.  You can set the max number of players logged (5, for
2
 example) by setting .log number.
2

2
If you want something particular to happen to anything that enters, you can
2
 edit the :enter_junk verb, which is run when anything enters.
2

2
-- Exit Hiding/Cloaking --
2

2
If you want players not be able to see hidden exits, set the .hide_hidden_exit
2
 s prop to 1.  If you want players to have to type more than a certain number
2
 of letters to use an exit, set the .min_name_length to however many letters
2
 you want.  (2, for example).
2

36
5
5
115
1
#300
generic magic protected room

144
36
-1
-1
-1
301
299
-1
2
allow_spells
36
173
-1
set_warded_against_spells set_ward_exceptions
36
173
-1
2
warded_against_spells
ward_exceptions
43
4
0
36
1
4
4
1
4
1
20503
1
20497
1
20495
36
1
5
36
0
5
36
0
5
36
1
5
36
1
5
36
1
5
36
5
5
36
5
5
36
5
5
115
1
4
0
115
1
5
36
5
5
36
5
0
0
36
5
5
36
5
5
36
5
5
36
5
4
0
36
5
1
115
36
5
5
36
4
5
36
5
5
36
5
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
0
0
113
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
generic magic protected room
2
generic warded room
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
4
16
2
A generic room to prevent certain spells to work in it.
2
@set <warded room>.warded_against_spells to {<spell name 1>, <spell name 2>, ...}
2

2
To prevent the use of ANY spell, set the .warded_against_spells to 1.
2

2
Example:
2
@set here.warded_against_spells to {"scry"}
2
Will perevent scry to peek at that room...
2

2
You can also make a list of people/objects/spells that can bypass the ward, by setting the .ward_exceptions property to that list.
2
Example:
2
@set here.ward_exceptions to {#4}
2
Will allow all the GMs to bypass the .warded_against_spells restrictions.
2

2
Tech note:
2
Both list can contain either individual objects or generics.. if a generic is in the list, then all its descendants are warded against/exempted.
36
5
5
115
1
#301
generic secure room

144
36
-1
-1
-1
302
300
-1
6
is_denied
36
173
-1
accepted denied
36
173
-1
acceptable
36
173
-1
accept
36
173
-1
@accept
36
153
3
@deny
36
153
3
3
accepted
denied
denied_default
41
4
0
36
0
4
0
36
0
0
0
36
1
5
36
1
5
36
1
5
36
5
5
36
5
5
36
5
5
115
1
4
0
115
1
5
36
5
5
36
5
0
0
36
5
5
36
5
5
36
5
5
36
5
4
0
36
5
1
115
36
5
5
36
4
5
36
5
5
36
5
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
0
0
113
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
generic secure room
2
secure_room
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
4
33
2
        generic secure room
2
        ===================
2

2
@accept in <room>
2
=> show the accepted list
2

2
@accept <object> in <room>
2
=> add <object> to the accepted list or remove it from the denied list, as appropriate.
2

2
@deny in <room>
2
=> show the denied list
2

2
@deny <object> in <room>
2
=> remove <object> from the accepted list or add it to the denied list, as appropriate.
2

2

2
Note: 
2
a) by defaults no object is denied access to the room
2
b) the accept/deny lists works on a tree model, you accept/deny all the descendants of an object, example, a lake room could be:
2

2
@deny $root_class in here
2
=> now you deny all objects in here
2

2
@accept $character in here
2
=> now you deny all objects in here except descendants of $character
2

2
@accept <generic boat#> in here
2
=> now you deny all objects in here except descendants of $character and <generic boat>
2

2
@deny <generic aircraft carrier> in here
2
=> now you deny all objects in here except descendants of $character and (all <generic boat> but the descendants of <generic aircraft carrier>)
2

2
And so on... ad nauseum.
36
5
5
115
1
#302
generic extended rpg room

144
36
-1
-1
-1
132
301
178
4
is_writable_by
36
173
-1
set_controllers
36
173
-1
set_GMs_are_controllers
36
173
-1
help_msg
36
173
-1
3
controllers
GMs_are_controllers
user_help_msg
38
4
0
36
1
0
0
36
1
4
0
36
5
5
36
5
5
36
5
5
115
1
4
0
115
1
5
36
5
5
36
5
0
0
36
5
5
36
5
5
36
5
5
36
5
4
0
36
5
1
115
36
5
5
36
4
5
36
5
5
36
5
5
36
4
5
36
5
5
36
5
5
36
5
5
36
5
0
0
113
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
generic extended rpg room
2
extended_rpg_room
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
4
13
2
	generic extended rpg room
2
	=========================
2

2
This generic is made to add a few features to the RPG room...
2

2
Room control sharing:
2
If you want all GMs to be allowed to read/write/control the room, e.g. be able to set the description, the messages, etc...
2
@set <room>.GMs_are_controllers to 1
2

2
If you only want to share the room with selected GMs, i.e. the ones working with you on the project, or youself if you intend to chown the room to a PC:
2
@set <room>.controllers to {#GM1, #GM2, ...}
2

2
Only GMs can be controllers of a room, this is to avoid cheats/mistake/accidents/...
36
5
5
115
1
#303
STANDARD SPECIES WEAPONS

16
2
-1
-1
-1
179
151
-1
0
0
54
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
1
161
115
1
1
-3
115
1
5
115
1
5
115
1
5
115
1
5
115
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
5
5
2
5
5
2
5
5
2
5
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
36
1
5
2
5
5
2
5
5
115
1
5
115
1
4
0
2
4
4
0
2
4
1
-1
36
1
5
2
5
5
2
5
0
0
2
4
4
2
2
STANDARD SPECIES WEAPONS
2
sspw
2
5
2
If you are giving natural weapons to a standard recomb, they should be descended from this object.  These are the stock approved weapons, the stats of which have been properly balanced.
2
5
4
2
0
1233
0
955495071
100
1
5
2
5
5
2
5
5
2
5
5
2
5
5
115
1
#304
Generic Bladed Weapon Parent

144
36
-1
-1
-1
305
163
-1
0
0
62
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
5
36
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
1
161
115
1
1
-3
115
1
0
20
115
1
0
20
115
1
5
115
1
2
slash
115
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
Generic Bladed Weapon Parent
2
bladed
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#305
MELEE Weapon Grandparent

144
36
-1
-1
-1
94
306
-1
0
0
62
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
5
36
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
1
161
115
1
1
-3
115
1
5
115
1
5
115
1
0
2
115
1
5
115
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
1
2
MELEE Weapon Grandparent
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#306
Generic Blunt Weapon Parent

144
36
-1
-1
-1
305
164
304
0
0
62
5
36
5
5
36
5
5
36
5
2
%N %<puts> away %p %t.
36
5
5
36
5
5
36
5
5
115
1
5
36
5
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
5
115
1
1
161
115
1
1
-3
115
1
0
20
115
1
5
115
1
5
115
1
2
blunt
115
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
Generic Blunt Weapon Parent
2
blunt
36
5
2
The most primitive of weapons, yet still effective on occasion. Clubs, maces, and bricks fall in this category.
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#307
generic secure FO

144
36
-1
-1
-1
74
189
166
11
secure_call
36
173
-1
feature_add
36
173
-1
feature_remove
36
173
-1
is_fo_admin
36
173
-1
add_accepted_user
36
173
-1
remove_accepted_user
36
173
-1
tell_usage tell_documentation
36
173
-1
feature_ok trusts
36
173
-1
set_accept_GMs set_accept_users set_deny_users
36
173
-1
recycle
36
173
-1
set_fo_admins
36
173
-1
6
fo_admins
feature_users
accept_GMs
accept_users
admin_fo
deny_users
52
4
0
36
1
4
0
36
1
0
1
36
1
4
0
36
1
1
3963
36
1
4
0
36
1
1
-1
36
1
4
0
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
1
2
generic secure FO
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
4
25
2
               Generic Secure FO
2
               ~~~~~~~~~~~~~~~~~
2

2
This generic help you to make FO that are secure (either public or restricted access features). By default only GMs can use FO made as kids of the Generic Secure FO.
2
If you don't want all GMs to have access to your FO you can do:
2
@set <my FO>.accept_GMs to 0
2

2
You can make a list of objects/generics who can use your class by setting the <my FO>.accept_users property to a list of such objects/generics. You can deny the access the of some of the accepted objects/generics descendants by putting them in the <my FO>.deny_users.
2
Example, you want to make a FO accessible by all players except guests:
2
@set <my FO>.accept_users to {#6}
2
@set <my FO>.deny_users to {#33}
2

2
More conveniently you can use the  Secure FOs Admin FO (#3963) to manage those lists. Help #3963 for more information.
2
If you are more than one GM managing the lists of people who can access a FO (e.g. 2 GMs managing a class) the owner of the FO should add the other administrators to <my FO>.fo_admins
2
Example: You want Janus, Guru and you to be able to change the lists of users of <my FO>
2
@set <my FO>.fo_admins to {#3597, #2509}
2

2
The last step to make your FO really secure is to make sure ALL your FO commands code start with the line:
2
this:secure_call(caller, caller_perms());
2

2
This will ensure that only the autorized people can use those commands.
2

2
Enjoy!
2

2
Janus
36
5
5
115
1
#308
PC Privileges Feature

16
36
-1
-1
-1
74
208
149
4
is_abuser
36
173
-1
forbidden_command_msg
36
173
-1
notify_user
2
173
-1
allow_privileged_command
36
165
-1
2
abusers
forbidden_command_msg
48
4
0
36
1
2
Your right to use $cmd has been revoked.  If you feel this treatment is unfair, speak to God via page or @qsend.
36
5
1
-1
36
1
4
0
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
2
1
5
36
5
5
36
5
5
36
5
5
36
5
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
1
5
36
5
5
36
5
5
115
1
5
115
1
4
0
36
4
4
0
36
4
1
-1
36
1
5
36
5
5
36
5
0
0
36
4
4
2
2
PC Privileges Feature
2
pcpfo
36
5
5
36
5
4
2
0
0
0
0
100
1
5
36
5
5
36
5
5
36
5
5
36
5
5
115
1
#0:0
"...This code should only be run as a server task...";
if (callers())
return E_PERM;
elseif (player in $login.ignored)
boot_player(player);
$login.ignored = setremove($login.ignored, player);
return 0;
elseif (player in $network.open_connections)
if (i = $list_utils:iassoc(player, $network.connect_connections_to))
what = $network.connect_connections_to[i][2];
$network.connect_connections_to = listdelete($network.connect_connections_to, i);
"Since we are logging it in, the origial invalid doesn't mean anything anymore.";
$network.open_connections = setremove($network.open_connections, player);
return what;
else
$login.ignored = {@$login.ignored, player};
$network.open_connections = setremove($network.open_connections, player);
return 0;
endif
elseif ($login:redlisted($string_utils:connection_hostname(connection_name(player))))
boot_player(player);
return 0;
endif
args = $login:parse_command(@args);
return $login:(args[1])(@listdelete(args, 1));
.
#0:1
if (callers())
return E_PERM;
endif
$last_restart_time = time();
$commands_processed = 0;
"...do some basic sanity checks for $maxint and $minint...";
if (((($maxint + 1) != $minint) || ($minint >= 0)) || ($maxint <= 0))
server_log("Warning:  $maxint and $minint look incorrect.");
endif
"...see if $network has to start any services or such...";
if ($object_utils:has_callable_verb($network, "server_started"))
$network:server_started();
endif
.
#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)
return raise(E_PERM);
endif
pass();
$shutdown_message = "";
$shutdown_time = 0;
$dump_interval = 3600;
$gripe_recipients = {player};
$core_history = {{$network.MOO_name, server_version(), time()}, @$core_history};
.
#0:4
if (callers())
return;
endif
user = args[1];
fork (0)
user:confunc();
endfork
`user.location:confunc(user) ! ANY';
$login:(verb)(@args);
.
#0:5
if (callers())
return;
endif
user = args[1];
if (user < #0)
return;
endif
quit = verb == "user_disconnected";
fork (0)
user.location:disfunc(user, quit);
endfork
`user:disfunc(quit) ! ANY';
$login:(verb)(@args);
.
#0:6
if (callers())
return E_PERM;
endif
$dump_started = time();
msg = $wiz_utils:checkpoint_started_msg();
for p in (connected_players())
p:notify(msg);
endfor
.
#0:7
if (callers())
return E_PERM;
elseif (!args[1])
$dump_finished = -1;
msg = $wiz_utils:checkpoint_failed_msg();
else
$dump_finished = time();
msg = $wiz_utils:checkpoint_finished_msg();
endif
for p in (connected_players())
p:notify(msg);
endfor
.
#0:8
":handle_uncaught_error(CODE, MSG, VALUE, TRACEBACK, FORMATTED)";
if (callers())
raise(E_PERM);
endif
if (!valid(player))
return 0;
endif
{c, m, v, t, f} = args;
f = {@f, "", ""};
"1. Mail all errors to $traceback_log";
if (this.log_errors)
notify(#2, tostr("<<<Error>>> ", player.name, ": ", f[1]));
subj = tostr("`", argstr || `player.last_command ! E_PROPNF => ""', "` -> ", toliteral(c));
$mail_agent:send_message(player, {$traceback_log}, {subj, {player}}, f);
endif
"2. Call any custom verb on the player.";
return `player:(verb)(@args) ! ANY';
.
#0:9
":handle_task_timeout(RESOURCE, TRACEBACK, FORMATTED)";
if (callers())
raise(E_PERM);
endif
if (task_id() == this.ignore_timeout)
return 1;
endif
{r, t, f} = args;
{one, ?two = 0, @rest} = f;
"1. Mail all errors to $traceback_log";
if (this.log_errors)
len = 55;
notify(#2, tostr("<<<Timeout>>> ", player.name, ": ", `one[1..len] ! E_RANGE => one'));
if (two)
notify(#2, tostr("<<<Timeout>>> ", player.name, ": ", `two[1..len] ! E_RANGE => two'));
endif
endif
"2. Call any custom verb on the player.";
return `player:(verb)(@args) ! ANY';
.
#0:10
":ignore_timeout()";
"Ignore timeouts for the current task.  As only one task is marked at a time, it is unreliable if the task later suspends.";
this.ignore_timeout = task_id();
.
#0:11
if (callers())
return E_PERM;
endif
this.commands_processed = this.commands_processed + 1;
if (`this.martial_law ! E_PROPNF' && ((this.martial_law > 0) || `$wiz_utils:is_builder(player) ! ANY'))
`$owner:log_command(player, argstr) ! ANY';
endif
cmdline = argstr;
if (i = cmdline[1] in {"\"", ":", ";"})
cmdline[1..1] = {"say", "emote", "eval"}[i] + " ";
endif
`player.last_command = cmdline ! E_PROPNF';
unc = `player:unconscious() ! ANY';
if ((unc && argstr) && (!((index("@;", argstr[1]) || (`cmdline[1..4] ! E_RANGE' == "page")) || (`cmdline[1..4] ! E_RANGE' == "wake"))))
player:notify(tostr("You're unconscious.  You can only use @-commands", (unc > 0) ? "." | ", or type `wake up` to return to consciousness."));
return 1;
endif
if (!(this.commands_processed % 1000))
0 && `$taskmaster:keepalive() ! ANY';
endif
.
#0:12
val = args[1];
if (((typeof(val) == LIST) && (length(val) > 1)) && (value_bytes(val) > 16384))
raise(E_QUOTA);
endif
return toliteral(val);
.
#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)))
return E_PERM;
endif
this.object_size = {0, 0};
this.key = 0;
.
#1:1
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
if (typeof(this.owner.owned_objects) == LIST)
this.owner.owned_objects = setremove(this.owner.owned_objects, this);
endif
else
return E_PERM;
endif
.
#1:2
"set_name(str)";
"Attempts to change this.name to the given string.";
"-> E_PERM   If you don't own this or aren't its parent, or are a player trying to do an end-run around $player_db.";
"-> E_TYPE   If the name is not a string.";
"-> E_INVARG If the name is not valid according to .";
if ((!caller_perms().wizard) && (is_player(this) || ((caller_perms() != this.owner) && (this != caller))))
return E_PERM;
elseif (typeof(name = args[1]) != STR)
return E_TYPE;
elseif (!this:is_valid_name(name))
return E_INVARG;
else
return (typeof(e = this.name = name) != ERR) || e;
endif
.
#1:3
":title() -> The 'cosmetic' name of this object.";
return (this:title_msg() || this.title_msg) || this:name();
.
#1:4
return 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";
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
":match(string)";
"-> Match string against this object's contents.";
return $match_utils:match_object(args[1], this:matching_contents());
.
#1:7
":match_object(string[, object-list])";
{string, ?olist = this:env()} = args;
return $match_utils:match_object(string, olist);
.
#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 (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
elseif (typeof(desc = args[1]) in {LIST, STR})
this.description = desc;
return 1;
else
return E_TYPE;
endif
.
#1:9
"Just make sure :description always returns a list, eh wot?";
return (typeof(this.description) == LIST) ? this.description | {this.description};
.
#1:10
this:tell_description(this:description());
.
#1:11
player:tell("The examine command has been renamed to @examine.  Please use @examine.");
.
#1:12
if (is_player(this))
notify(this, args[1]);
endif
.
#1:13
this:notify(tostr(@args));
.
#1:14
lines = args[1];
if (typeof(lines) == LIST)
for line in (lines)
this:tell(line);
endfor
else
this:tell(lines);
endif
.
#1:15
set_task_perms(caller_perms());
return this:acceptable(@args);
.
#1:16
where = args[1];
set_task_perms(this.owner);
return `move(this, where) ! ANY';
.
#1:17
"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(what.home) == OBJ) && ((what.home != this) && (is_player(what) ? what.home:accept_for_abode(what) | what.home:accept(what))))))
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);
endfork
endif
return move(what, where);
.
#1:18
return (this.key == 0) || $lock_utils:eval_key(this.key, args[1]);
.
#1:19
set_task_perms(callers() ? caller_perms() | player);
$command_utils:do_huh(verb, args);
.
#1:20
":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 (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
prop_info = $code_utils:has_message(this, args[1]);
if (!prop_info)
return E_PROPNF;
endif
prop_loc = prop_info[1];
prop_name = prop_info[2];
return (prop_loc.(prop_name) = args[2]) && 1;
.
#1:21
"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:22
"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))};
"who:notify(tostr(\"Key:  \", $lock_utils:unparse_key(this.key)));";
endif
.
#1:23
"examine_names(examiner)";
"Return a list of strings to be told to the player, indicating the name and aliases (and, for programmers, the object number) of this.";
who = args[1];
if (who.programmer)
aliases = {tostr(this), @this.aliases};
else
aliases = this.aliases;
endif
name = this.name;
aliases = setremove(aliases, name);
if (aliases)
return {tostr(name, " (aka ", $string_utils:english_list(aliases), ")")};
else
return {this.name};
endif
.
#1:24
"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:25
"examine_contents(examiner)";
examiner = args[1];
isbuilder = $wiz_utils:is_builder(examiner);
if (!isbuilder)
return {};
endif
STRUTILS = $string_utils;
visible = {};
for c in (this:contents())
visible = {@visible, "  " + STRUTILS:nn(c)};
endfor
return visible ? {"Contents:", @visible} | {"(Contains nothing.)"};
.
#1:26
"Return a list of strings to be told to the player.  Standard format says \"Obvious verbs:\" followed by a series of lines explaining syntax for each usable verb.";
if (caller != this)
return E_PERM;
endif
who = args[1];
name = dobjstr;
vrbs = {};
commands_ok = this:examine_commands_ok(who);
dull_classes = {$root_class, $room, $player, $prog, $builder, $wiz};
what = this;
hidden_verbs = this:hidden_verbs(who);
while (what != $nothing)
$command_utils:suspend_if_needed(0);
if ((what == this) || (!(what in dull_classes)))
for i in [0..length(verbs(what)) - 1]
$command_utils:suspend_if_needed(0);
info = verb_info(what, tostr(i));
syntax = verb_args(what, tostr(i));
if (this:examine_verb_ok(what, i, info, syntax, commands_ok, hidden_verbs))
dobj = syntax[1];
prep = syntax[2];
iobj = syntax[3];
if (syntax == {"any", "any", "any"})
prep = "none";
endif
if (prep != "none")
for x in ($string_utils:explode(prep, "/"))
if (length(x) <= length(prep))
prep = x;
endif
endfor
endif
"This is the correct way to handle verbs ending in *";
vname = info[3];
while (i = index(vname, "* "))
vname = tostr(vname[1..i - 1], "<anything>", vname[i + 1..length(vname)]);
endwhile
if (vname[i = length(vname)] == "*")
vname = vname[1..i - 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};
.
#1:27
":get_message(msg_name)";
"=> error (use E_PROPNF if msg_name isn't recognized)";
"=> string or list-of-strings raw value";
prop_info = $code_utils:has_message(this, args[1]);
if (!prop_info)
return E_PROPNF;
endif
prop_loc = prop_info[1];
prop_name = prop_info[2];
info = property_info(prop_loc, prop_name);
if ((!index(info[2], "r")) && (!this:is_readable_by(caller_perms(), caller)))
return E_PERM;
endif
return prop_loc.(prop_name);
.
#1:28
this.location:(verb)(@args);
.
#1:29
if (!caller_perms().wizard)
return E_PERM;
endif
"remove all parenthesized verbs from the core";
vnum = 1;
while (vnum <= length(verbs(this)))
$command_utils:suspend_if_needed(0);
info = verb_info(this, vnum)[3];
if (match(info, "(.+)"))
delete_verb(this, vnum);
else
vnum = vnum + 1;
endif
endwhile
this.key = 0;
this.object_size = {0, 0};
.
#1:30
"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:31
"examine_verb_ok(loc, index, info, syntax, commands_ok, hidden_verbs)";
"loc          -- the object that defines the verb";
"index        -- which verb on the object";
"info         -- verb_info";
"syntax       -- verb_args";
"commands_ok  -- determined by this:commands_ok, probably, but passed in so we don't have to calculate it for each verb.";
"hidden_verbs -- passed for same reasons.  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 (!this:is_readable_by(caller_perms(), caller))
return E_PERM;
endif
info = args[3];
vname = info[3];
if ((index(vname, "(") - 1) > 1)
"An (old) verb.";
return 0;
endif
if (((vname[1] == "@") && (!$wiz_utils:is_builder(player))) && (this.owner != player))
"A building command the player doesn't needa see.";
return 0;
endif
syntax = args[4];
if (syntax[2..3] == {"none", "this"})
"An internal method.";
return 0;
endif
loc = args[1];
hidden_verbs = args[6];
return (((args[5] || ("this" in syntax)) && verb_code(loc, tostr(args[2]))) && (!(vname in hidden_verbs))) && (!({loc, vname, syntax} in hidden_verbs));
.
#1:32
"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:33
"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 [0..length(verbs(what)) - 1]
info = verb_info(what, tostr(i));
if (!index(info[2], "r"))
hidden = setadd(hidden, {what, info[3], verb_args(what, tostr(i))});
endif
endfor
what = parent(what);
endwhile
return hidden;
else
return E_PERM;
endif
.
#1:34
"examine_owner(examiner)";
"Return a list of strings to be told to the player, indicating who owns this.";
who = args[1];
if ($rpg:trusted(who) || (this.owner == who))
return {tostr("Owned by ", this.owner.name, ".")};
else
return {};
endif
.
#1:35
return;
.
#1:36
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:37
":is_readable_by(who[, caller])";
"-> Is 'who' allowed to view unreadables on this object?";
return this:is_writable_by(@args);
.
#1:38
":is_writable_by(who[, caller]) -> Is 'who' allowed to edit this object?";
"NOTE: A character who may WRITE an object may also READ and CONTROL it.";
who = args[1];
return (((this in args) || (valid(who) && who.wizard)) || (who == this.owner)) || this.w;
.
#1:39
":is_controllable_by(who[, caller])";
"-> Is 'who' allowed to force this object to perform actions?";
return (this in args) || this:is_writable_by(@args);
.
#1:40
return 0;
.
#1:41
":announce_messages(msg-name[, @pronoun-sub-args])";
"If the given message is defined; tell THAT message to the player, and announce (if defined) the o-msg to everyone else.";
"If the given message is NOT defined, but the o-msg is; then run that message through $you:say_action.";
"If neither message is defined; do nothing.";
"Note that verbs should be appropriately notated (surrounded by %<verb> according to the subject) for :say_action.";
"Return 1 if message only was printed, 2 if omessage only was printed, 3 if both were, and 0 if none.";
if ((caller != this) && (!this:is_controllable_by(caller_perms())))
return E_PERM;
endif
su = $string_utils;
msgname = args[1] + "_msg";
psargs = listdelete(args, 1) || {};
who = psargs ? psargs[1] | player;
if (msg = this:(msgname)(@psargs))
who:tell(msg);
elseif (msg = this.(msgname))
who:tell(su:pronoun_sub(msg, @psargs));
endif
if (!(omsg = this:get_message("o" + args[1])))
"...silence...";
elseif (msg)
who:room_announce_all_but({who}, this:("o" + msgname)(@psargs) || su:pronoun_sub(omsg, @psargs));
else
$you:say_action(omsg, @psargs);
endif
return msg ? omsg ? 3 | 1 | (omsg ? 2 | 0);
.
#1:42
":matches(string) -- Does this object answer to the given string?";
" 1    -- Exactly.";
" 0    -- No.";
"-1    -- Partially.";
len = length(string = args[1]);
all = {this.name, @this.aliases};
if (string in all)
return 1;
endif
for name in (all)
if (name && (name[1..len] == string))
return -1;
endif
endfor
return 0;
.
#1:43
":room() -> Room this object is located in, or $nothing.";
":zone() -> Zone this object is located in, or $nothing.";
p = $sys.(verb);
z = this;
while (valid(z) && (!$object_utils:isa(z, p)))
z = z.location;
endwhile
return z;
.
#1:44
":name()";
return this.name;
.
#1:45
":namec() -> Capitalized version of this name.  Probably isn't different.";
return this.namec || $string_utils:capitalise(this:name());
.
#1:46
":definite()";
"-> True if this object's name should be prepended with 'the', as opposed to 'a' or 'an'.";
if ((this.definite || this.f) || children(this))
return 1;
endif
.
#1:47
":use_article() -> Does this object's name need an article prepended?";
if ((!match(this.name, "^%(a%|an%|the%)%W%|'s %|s' ")) && this.use_article)
return 1;
endif
.
#1:48
":id() -> Return an identifying phrase for this object.";
if ($wiz_utils:is_builder(player))
return tostr(this:name(), " (", this, ")");
else
"Maybe hack this to check for ambiguity and return ordinals?";
return this:name();
endif
.
#1:49
"this:dname([adj])";
"Returns this:name() prepended with proper definite article (e.g. \"the\").";
"The (optional) <adj>ective will be inserted between the article and the name.";
"Relevant .properties and :verbs on this:";
"   :use_article() -> True/False";
"False if this object's name does not require an article, as is the usual case with player names.";
"  .:possessor()   -> #possessor";
"If the name includes a possessive (e.g. John's car)";
"  .:def_art       -> \"article\"";
"Definite article to use instead of \"the\" (e.g. \"lots of\" money).";
"";
"this:dnamec([adj])";
"Returns capitalized version of above, using :namec instead of :name where applicable.";
"";
"this:iname[c]([adj])";
"Same as above, except using indefinite articles (e.g. \"a\" or \"an\").";
"iname[c] will use .indef_art or :indef_art if they exist.";
"Also, if this:definite() == 1 then this:dname[c](@args) will be returned instead (e.g. \"the\" System Object instead of \"a\" System Object).";
"";
"The :dtitle*c/ititle*c operate in the same manner, but with :title instead of :name being used.";
adj = args[1] || "";
capital = verb[lv = length(verb)] == "c";
name_verb = capital ? verb[2..lv - 1] | verb[2..lv];
definite = (verb[1] == "d") || this:definite();
if ((!this:use_article()) && (!adj))
art = "";
elseif (0 && valid(pos = $code_utils:verb_or_property(this, "possessor")))
art = (capital ? pos:(("i" + name_verb) + "c")() | pos:("i" + name_verb)()) + "'s";
elseif ((typeof(art = this:(art_verb = definite ? "def_art" | "indef_art")()) == STR) || (typeof(art = this.(art_verb)) == STR))
"...special article defined on object...";
elseif (definite)
art = "the";
else
first = adj || this:(name_verb)();
art = $english:indefinite_article(first);
endif
prefix = ((art + (art ? " " | "")) + adj) + (adj ? " " | "");
if (capital && (!valid(pos)))
prefix = $string_utils:capitalize(prefix);
endif
return prefix + ((capital && (!prefix)) ? this:(name_verb + "c")() | this:(name_verb)());
.
#1:50
desc = args ? args[1] | this:description();
if (desc)
player:tell_lines(desc);
else
player:tell("You see nothing special.");
endif
.
#1:51
":is_local_to(object)";
"Is this object in the same vacinity as the given object?  Is the given object inside this object?  Is this object inside the given object?";
"Answers to all these questions, and more, lie in the return value of :is_local_to!";
what = args[1];
return (this in what:environment()) || (what in this:environment());
.
#1:52
":hear_event_eventname(@args)";
verb[1..4] = "dispatch";
return this:(verb)(@args);
.
#1:53
":is_enclosing(obj)";
"Should this object's be the LAST of recursive :local_object() pools?";
"An example of an enclosing object would be a locked safe, or a room with no windows.";
"Because of the current lack of true spatial orientation, we assume that if you can see/smell/hear something, you can manipulate it.  This is, of course, not always true.";
"For example, if you are in a cage, you can SEE what's outside the cage, but you cannot necessarily reach it.";
"By default, an object is enclosing.  Exceptions up the heirarchy include characters (contents are held), furniture, and open vehicles such as camels and motorcycles.";
"Called by: $root:environment()";
return 1;
.
#1:54
":environment()";
"Return a list of local objects for this object and all the objects which contain it.";
pool = listinsert(this:matching_contents(), this);
if (`this:is_enclosing() ! ANY => 1')
return pool;
endif
last = this;
while (valid(next = last.location))
pool = setadd(pool, next);
for item in (`next:matching_contents() ! ANY => {}')
pool = setadd(pool, item);
endfor
if (`next:is_enclosing(last) ! ANY => 1')
return pool;
endif
last = next;
endwhile
return pool;
.
#1:55
":matching_contents()";
"-> A list of objects this object contributes to a matching pool.";
o = {};
for a in (setremove(`this:contents() ! ANY => {}', caller))
o = {@o, @`a:attached_contents() ! ANY => {}'};
endfor
return o;
.
#1:56
":announce_to(text, excluded)";
"Called by :announce_all_but, this decides whether we should be announced to, and passes an extra arg of those who were excluded from the original announcement.";
if (args[2] && (this in args[2]))
return;
elseif (!this:is_listening())
return;
endif
this:tell(args[1]);
.
#1:57
":attached_contents()";
"A list of what should be matched with this object.  By default, only this.";
return {this};
.
#1:58
":process_notified_text(str)";
return args[1];
.
#1:59
":is_bonded_to(char)";
"Should this item stay with the victim when e dies?  Return true if so, false if it should be deposited in eir corpse.";
"All non-$tangibles are bonded, so as to prevent them wandering when a developer dies loaded with core objects.";
return 1;
.
#1:60
":is_unique()";
"Return true if this object is 'unique'; that is, an object which is not simply an undeveloped child of another.";
return (((this.f || children(this)) || verbs(this)) || properties(this)) ? 1 | 0;
.
#1:61
":spawned_name()";
"Name for an object spawned from this one.";
return $english:degenericize(this.name);
.
#1:62
":spawned_aliases()";
"Aliases for an object spawned from this one.";
aliases = {};
for a in (this.aliases)
aliases = setadd(aliases, $english:degenericize(a));
endfor
return aliases;
.
#1:63
":sub_name([@args])";
"Return the name to be used by $string_utils:pronoun_sub and other substituting verbs.";
verb[1..4] = "";
return this:(verb)(@args);
.
#1:64
":v_size()";
"-> The virtual size of this object.  See `help size` for a table.";
return this.v_size || 0;
.
#1:65
":has_message(str)";
"Check if the given string is to be considered a valid property for access and modification via :get/set_message.";
"Returns {OBJ prop_loc, STR prop_name}, or some false value.";
if (m = $object_utils:has_message(this, args[1]))
return {this, m};
endif
.
#1:66
":is_valid_name(str)";
"Return true if the given name is a valid one for this object.  Note that programmers _can_ override this, since this core assumes programmers are socially responsible.";
return match(args[1], "[A-Z]") ? 1 | 0;
.
#1:67
"Copied from generic character (#90):grammar_sub by Hacker (#38) Sat Feb 17 17:25:16 1996 CST";
":g_sub(str)";
"Same as $string_utils:pronoun_sub, with this object as the sole named object--player/%N.";
"'caller' is added as the second argument, so that '%t' will sub correctly as if the caller of this method had called :pronoun_sub.";
return $string_utils:pronoun_sub(args[1], this, caller);
.
#1:68
"Set the in-character flag of this room.  If set to 1, OOC players may not enter.  If set to 0, IC players may not enter.";
if (!this:is_writable_by(caller_perms(), caller))
return raise(E_PERM);
endif
this.ic = toint(args[1]);
.
#1:69
":notify_binary(@numbers_and_strings)";
if (!caller_perms().wizard)
raise(E_PERM);
endif
bins = encode_binary(@args);
if ("not checking ranges")
"skip it";
elseif (m = match(bins, "~%(0[012345689BCEF]%|[2-9A-F][1-9A-F]%|1[1-9AC-F]%|1B%)"))
if (substitute("%1", m) != "1B")
raise(E_INVARG, "Integer must be within set of {7, 10, 13, 27}.");
endif
l = 0;
while ((i = index(bins[l + 1..$], "1B")) > l)
i = i + l;
if (!match(bins[i + 2..$], "^%[[0-9;]+m"))
raise(E_INVARG, "Invalid escape sequence.");
endif
l = i;
endwhile
endif
try
set_connection_option(this, "binary", 1);
notify(this, bins);
set_connection_option(this, "binary", 0);
except (ANY)
endtry
return 0;
"%[[0-9][0-9]m";
notify(#2, tostr(";match(\"", bins[i + 2..$], "\", \"%[[0-9;]+m\")"));
.
#3:0
this:look_self(player.brief);
argstr = "has connected.";
this:emote();
.
#3:1
":disfunc(user[, quit])";
"If 'quit' is true, the user was disconnected via the MOO.  Else, e was disconnected by eir client.";
quit = args[2];
if (player:in_combat())
player:queue_action("flee", "flee", {}, 50 - this:quickness());
else
fork (quit ? 60 | 300)
this:send_user_home(player);
endfork
endif
argstr = tostr("has ", quit ? "disconnected" | (("lost " + player:pp()) + " link"), ".");
this:emote();
.
#3:2
player:tell("You say, \"", argstr, "\"");
this:announce(player:name(), " says, \"", argstr, "\"");
.
#3:3
if (argstr && index(":'", argstr[1]))
if (argstr[1] == ":")
this:announce_all(player:name(), argstr[2..length(argstr)]);
else
this:announce_all(player:name(), argstr);
endif
else
this:announce_all(player:name(), " ", argstr);
endif
.
#3:4
this:announce_all_but({player}, @args);
.
#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
exit = args[1];
if (!this:will_link_to(exit, caller_perms(), caller))
return E_PERM;
endif
"set_task_perms(caller_perms());";
return (this.exits = setadd(this.exits, exit)) != E_PERM;
.
#3:7
":tell_contents([contents[, ctype]])";
c = args ? args[1] | this:contents();
ctype = (length(args) > 1) ? args[2] | this.ctype;
if (!c)
return;
elseif (this.dark)
"...changed the meaning of the .dark flag to simply not show";
"...unconnected players.  it will be removed in the future.";
for o in (c)
if (is_player(o) && (!$object_utils:connected(o)))
c = setremove(c, o);
endif
endfor
endif
if (ctype == 0)
l = {};
player:tell("Contents:");
for thing in (c)
l = {@l, " " + thing:title()};
endfor
player:tell_lines($string_utils:smart_columnize(l, 4));
elseif (ctype == 1)
for thing in (c)
if (is_player(thing))
player:tell(thing:title(), " is here.");
else
player:tell("You see ", thing:title(), " here.");
endif
endfor
elseif (ctype == 2)
player:tell("You see ", this:ititle_list(c), " here.");
elseif (ctype == 3)
players = things = {};
for x in (c)
if (is_player(x))
players = {@players, x};
else
things = {@things, x};
endif
endfor
if (things)
player:tell("You see ", this:ititle_list(things), " here.");
endif
if (players)
player:tell($string_utils:pronoun_sub("%(ititlec) %<is> here.", players));
endif
endif
.
#3:8
if ((!this:is_controllable_by(player)) && (!$wiz_utils:is_builder(player)))
player:tell("Sorry, only the owner of a room may list its exits. Use `@ways` instead.");
return E_PERM;
endif
su = $string_utils;
if (verb[2..$] == "exits")
if (!this.exits)
player:tell(su:nn(this), " has no conventional exits.");
return;
endif
for exit in (this.exits)
player:tell(su:nn(exit), " leads to ", su:nn(exit.dest), " via {", $string_utils:from_list(exit.aliases, ", "), "}.");
endfor
else
if (!this.entrances)
player:tell(su:nn(this), " has no conventional entrances.");
return;
endif
for exit in (this.entrances)
player:tell(su:nn(exit), " comes from ", su:nn(exit.source), " via {", $string_utils:from_list(exit.aliases, ", "), "}.");
endfor
endif
.
#3:9
":tell_description()";
pass(@args);
this:tell_exits(this:obvious_exits());
.
#3:10
"Hacked for readability by Quinn (#2) on 13-FEB-94 1829.";
what = args[1];
if (what.owner == this.owner)
"owns here";
return 1;
elseif (this:is_resident(what))
"lives here";
return 1;
elseif (what.owner == $banker)
"always allow new collectives";
return 1;
elseif ($object_utils:contains(this, what))
"already here";
return 1;
elseif (($object_utils:isa(where = what.location, $generic_editor) && (i = what in where.active)) && (where.original[i] == this))
"always allow editing players back";
return 1;
endif
unlocked = this:is_unlocked_for(what);
return unlocked;
"this only applies to teleportation-- SNIP";
if (this:free_entry(what))
return 1;
endif
"also re:teleportation and `blessed' exits-- SNIP";
if ((what == this.blessed_object) && (task_id() == this.blessed_task))
return 1;
endif
return unlocked;
.
#3:11
exit = args[1];
if (!this:will_link_to(exit, caller_perms(), caller))
return E_PERM;
endif
"set_task_perms(caller_perms());";
return (this.entrances = setadd(this.entrances, exit)) != E_PERM;
.
#3:12
":bless_for_entry(object)";
"'Bless' the given object for entry into this room, bypassing free_entry.";
if (((caller in this.entrances) || $rpg:trusted(cp = caller_perms())) || this:is_controllable_by(cp, caller))
this.blessed_object = args[1];
this.blessed_task = task_id();
endif
.
#3:13
"go <dir> <dir> ... -- Go through the given exits in sequence.";
if ((!args) || (!(dir = args[1])))
player:tell("You need to specify a direction.");
return E_INVARG;
endif
taken = this:take_exit(dir, player);
if (taken)
if (length(args) > 1)
room = taken[1];
player.walking = {task_id(), room};
suspend(0);
if (player.location == room)
`room:go(@listdelete(args, 1)) ! E_VERBNF';
endif
endif
elseif (taken == $ambiguous_match)
player:tell("I don't know which direction (", dir, ") you mean.");
else
player:tell("You can't go that way (", dir, ").");
endif
player.walking = 0;
.
#3:14
this:announce_all_but({}, @args);
.
#3:15
":announce_all_but(objects, text)";
contents = this:audience_for_announce();
text = tostr(@listdelete(args, 1));
for listener in (contents)
listener:announce_to(text, args[1]);
endfor
.
#3:16
object = args[1];
brief = object:get_option($display_options, "brief_rooms");
if (typeof(brief) == NUM)
former = player;
player = object;
this:look_self(brief);
player = former;
endif
if (object == this.blessed_object)
this.blessed_object = #-1;
endif
"This should maybe be done in $exit:move()?";
this:broadcast_event_entrance(object, this:identify_entrance());
.
#3:17
":exitfunc(object)";
{what} = args;
if (this:check_exiting_object(what))
return;
endif
this:broadcast_event_exit(what, this:identify_exit());
.
#3:18
exit = args[1];
if ((caller != exit) && (!exit:is_controllable_by(caller_perms(), caller)))
set_task_perms(caller_perms());
endif
return (this.exits = setremove(this.exits, exit)) != E_PERM;
.
#3:19
exit = args[1];
if ((caller != exit) && (!exit:is_controllable_by(caller_perms(), caller)))
set_task_perms(caller_perms());
endif
return (this.entrances = setremove(this.entrances, exit)) != E_PERM;
.
#3:20
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
dest = exit.dest;
source = exit.source;
if (dest == E_PERM)
player:tell("You can't read the exit's destination to check that it's consistent!");
return;
elseif (source == E_PERM)
player:tell("You can't read that exit's source to check that it's consistent!");
return;
elseif (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:21
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
dest = exit.dest;
if (dest == E_PERM)
player:tell("You can't read the exit's destination to check that it's consistent!");
return;
elseif (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:22
"Make a mild attempt to keep people and objects from ending up in #-1 when people recycle a room";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
"... first try spilling them out onto the floor of enclosing room if any";
if (valid(this.location))
for x in (this.contents)
x:moveto(this.location);
endfor
endif
"... try sending them home...";
for x in (this.contents)
if (is_player(x))
if ((typeof(x.home) == OBJ) && valid(x.home))
x:moveto(x.home);
endif
if (x.location == this)
move(x, $player_start);
endif
elseif (valid(x.owner))
x:moveto(x.owner);
endif
endfor
pass(@args);
else
return E_PERM;
endif
.
#3:23
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:24
set_task_perms(player);
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
return;
elseif (dobj.location != this)
player:tell(dobj.name, "(", dobj, ") is not here.");
return;
elseif (!$perm_utils:controls(player, this))
player:tell("You are not the owner of this room.");
return;
elseif (dobj.wizard)
player:tell("Sorry, you can't ", verb, " a wizard.");
dobj:tell(player.name, " tried to ", verb, " you.");
return;
endif
iobj = this;
player:tell(this:ejection_msg());
this:eject(dobj);
dobj:tell(this:victim_ejection_msg());
this:announce_all_but({player, dobj}, this:oejection_msg());
.
#3:25
return $string_utils:pronoun_sub(this.(verb));
.
#3:26
who = args[1];
return (valid(who) && ((this.free_home || this:is_controllable_by(who.owner)) || this:is_resident(who))) && this:acceptable(who);
.
#3:27
if (!$perm_utils:controls(player, this))
player:tell("You must own this room to manipulate the legal residents list.  Try contacting ", this.owner.name, ".");
else
if (!dobjstr)
"First, remove !valid objects from this room...";
for x in (this.residents)
if (!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..length(dobjstr)];
else
notflag = 0;
endif
result = $string_utils:match_player_or_object(dobjstr);
if (!result)
return;
else
"a one element list was returned to us if it won.";
result = result[1];
if (notflag)
if (!(result in this.residents))
player:tell(result.name, " doesn't appear to be in the residents list of ", this.name, ".");
else
this.residents = setremove(this.residents, result);
player:tell(result.name, " removed from the residents list of ", this.name, ".");
endif
else
if (result in this.residents)
player:tell(result.name, " is already an allowed resident of ", this.name, ".");
else
this.residents = {@this.residents, result};
player:tell(result.name, " added to the residents list of ", this.name, ".");
endif
endif
endif
endif
.
#3:28
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:29
set_task_perms(player);
if (!dobjstr)
player:tell("Usage:  @remove-entrance <entrance>");
return;
endif
entrance = $match_utils:match(dobjstr, this.entrances);
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:30
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
return pass(@args);
.
#3:31
return (msg = this.(verb)) ? $string_utils:pronoun_sub(msg, args[1]) | "";
.
#3:32
return this.(verb);
.
#3:33
exits = {};
for exit in (this.exits)
if (exit:obvious())
exits = setadd(exits, exit);
endif
endfor
return exits;
.
#3:34
":here_huh(verb,args)";
"This should return 1 if it finds something interesting to do and 0 otherwise; see $command_utils:do_huh.";
"For the generic room...";
" * Check verb matches for ordinals, plurals, and exits...";
" * Check for an exit matching verb + argstr...";
" * Pass to :furniture_huh to check if the player wants to sit down.";
set_task_perms(caller_perms());
{verb, urgs} = args;
pool = player:environment();
if ((!valid(dobj)) && (dobj != $nothing))
dobj = player:my_match_object(dobjstr, pool);
endif
if ((!valid(iobj)) && (iobj != $nothing))
iobj = player:my_match_object(iobjstr, pool);
endif
if (valid(dobj) && $match_utils:match_verb(verb, dobj, urgs))
return 1;
elseif (valid(iobj) && $match_utils:match_verb(verb, iobj, urgs))
return 1;
endif
done = 0;
if (dobjstr in {"all", "everything"})
"pool = pool;";
elseif ((length(dobjstr) > 1) && (dobjstr[$] == "s"))
pool = $match_utils:match_list(dobjstr[1..$ - 1], pool);
else
pool = {};
endif
if (pool)
for k in (pool)
dobj = k;
done = $match_utils:match_verb(verb, k, urgs) || done;
$command_utils:suspend_if_needed(0);
endfor
endif
if (done)
return done;
endif
x = argstr ? (verb + " ") + argstr | verb;
if (valid(exit = this:match_exit(x)))
exit:invoke();
return 1;
elseif (exit == $ambiguous_match)
player:notify(tostr("I don't know which direction `", x, "` you mean."));
return 1;
endif
return this:furniture_huh(verb, urgs);
.
#3:35
this:(verb[6..length(verb)])(@args);
.
#3:36
return this == args[1].location;
.
#3:37
"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:38
"examine_contents(who)";
if (caller == this)
this:tell_contents(this.contents, this.ctype);
endif
.
#3:39
"Tell player exits leading from the room.";
exits = args ? args[1] | this:obvious_exits();
xlist = {};
EMU = $emu;
bold = player:get_option($display_options, "bold_exits");
if (typeof(bold) == STR)
for x in (exits)
xname = $string_utils:pronoun_sub(bold, x);
xlist = {@xlist, tostr(xname, (a = setremove(x.aliases, x.name)) ? (" (" + $string_utils:from_list(a, ", ")) + ")" | "")};
endfor
else
for x in (exits)
xname = bold ? tostr(EMU:tokenize("bold"), x:name(), EMU:tokenize("normal")) | x:name();
xlist = {@xlist, tostr(xname, (a = setremove(x.aliases, x.name)) ? (" (" + $string_utils:from_list(a, ", ")) + ")" | "")};
endfor
endif
bold = bold ? EMU.ANSI_Line_Prefix | "";
type = player:get_option($display_options, "exit_sentence");
if (xlist)
if (type)
player:tell(bold, "Obvious exits include ", $string_utils:english_list(xlist, "nowhere", " and "), ".");
else
player:tell("Obvious exits:");
for x in (xlist)
player:tell(bold, " ", x);
endfor
endif
else
if (type)
player:tell("There are no obvious exits.");
else
player:tell("Obvious exits:");
player:tell(" (None)");
endif
endif
.
#3:40
":find_seat([OBJ who])";
"Find a piece of furniture in the room.  If `who' is given, find a furniture occupied by em.";
who = args ? args[1] | 1;
for o in (this:contents())
if ($object_utils:isa(o, $furniture))
if (who)
if (o:space())
return o;
endif
elseif (who in {@o:sitting(), @o:reclining()})
return o;
endif
endif
endfor
return $failed_match;
.
#3:41
":broadcast_event_<event>(@args)";
"Broadcast the given event to all who should hear it.";
event = verb[17..$];
everb = tostr("hear_event_", event);
"though a simple scan would seem to indicate we are allowing arbitrary verb";
"calls with wizperms below, we're actually only allowing :hear_event_* calls";
"on objects in the event's audience.";
for object in (this:audience_for_event(event, @args))
try
$code_utils:call_verb(object, everb, args);
except e (ANY)
notify(player, $error:short_traceback(e));
endtry
endfor
.
#3:42
":audience_for_sound/speech(speaker, sound, loudness)";
"A whisper is loudness 1 or less.";
"A normal sound is from 2-4.";
"An exclamation is 5 or higher.";
volume = args[3];
all = this:contents();
if (volume < 1)
"shhhh.... quiet";
"should probably make hearing checks to see who heard it";
return {};
endif
volume = random(volume);
for exit in (this:exits())
sp = exit:sound_permeability();
if (sp > 0)
"Everything transmits.";
all = {@all, exit};
elseif (sp && (volume >= abs(sp)))
"Was loud enough to get through.";
all = {@all, exit};
endif
endfor
return all;
.
#3:43
":audience_for_motion(actor, motion, obviousness)";
"A slight, personal movement is of obviousness 1 or below.";
"A normal, limited motion is from 2-4.";
"A sweeping, extensive, sudden motion is 5 or more.";
obvious = args[3];
all = this:contents();
if (obvious < 1)
"was imperceptible";
"should probably make perception checks to see who noticed it";
return {};
endif
obvious = random(obvious);
for exit in (this:exits())
tp = exit:transparency();
if (tp > 0)
"Everything transmits.";
all = {@all, exit};
elseif (tp && (obvious >= abs(tp)))
"Was obvious enough to be noticed.";
all = {@all, exit};
endif
endfor
return all;
.
#3:44
":identify_exit/entrance()";
"-> The exit/entrance that moved the current player.";
callers = listdelete(callers(), 1);
objects = this:(verb[10..length(verb)] + "s")();
for set in (callers)
if (set[1] in objects)
"If there WAS an exit involved, it'll show up before...";
return set[1];
endif
if ((set[1] == this) && (dir = $exit_utils:match_direction(set[2])))
"A direction verb called on the room.";
return dir;
endif
endfor
return $nothing;
.
#3:46
":audience_for_announce()";
"-> Those who should hear announcements made in this room.";
return this:matching_contents();
.
#3:47
":light_level()";
"-> Brightness in this room, on a scale of 1-100.";
return 100;
.
#3:48
":furniture_huh(verb, args) -- Please wait to be seated.";
set_task_perms(caller_perms());
verb = args[1];
args = args[2];
restverbs = {"sit", "rest"};
sleepverbs = {"recline", "lie", "sleep"};
standverbs = {"stand", "rise"};
if (verb in standverbs)
if (valid(seat = this:find_seat(player)))
seat:(verb)(@args);
else
player:notify("What do you want to stand from?");
endif
elseif (!(verb in {@restverbs, @sleepverbs}))
return 0;
elseif (!prepstr)
if (valid(seat = this:find_seat(player)) || valid(seat = this:find_seat()))
seat:(verb)(@args);
else
player:notify(tostr("You can't find a place to ", verb, "."));
endif
elseif (!(prepstr in {"beside", "with"}))
if ($match_utils:object_match_failed(iobj, iobjstr, this:matching_contents()))
"";
elseif ($object_utils:isa(iobj, $furniture))
iobj:(verb)(@args);
else
player:notify(tostr("Now why would you wanna go and ", verb, " ", prepstr, " someone?  Try `", verb, " with ", iobjstr, "' instead."));
endif
elseif ((!iobjstr) || ((who = this:match_contents(iobjstr)) == $failed_match))
player:notify(tostr($string_utils:capitalize(prepstr), " whom would you like to ", verb, "?"));
elseif (who == $ambiguous_match)
player:notify(tostr("Do you want to ", verb, " ", prepstr, " ", $string_utils:title_list($match_utils:match_list(iobjstr, this:matching_contents()), "nobody", " or "), "?"));
elseif (!valid(seat = this:find_seat(who)))
player:notify(tostr(who.name, " isn't on any furniture here."));
else
seat:(verb)(@args);
endif
return 1;
.
#3:49
":free_entry([what])";
"Are things allowed to teleport into the room?";
what = args[1];
if (0)
"Removed 19990504, because this is just too confusing.";
zone = this:zone();
if (valid(zone) && is_clear_property(this, "free_entry"))
return zone:free_entry(@args);
endif
endif
free = this.free_entry;
if (this:is_writable_by(player) || this:is_resident(what))
return 1;
endif
if (free == $gm)
return $rpg:trusted(player);
endif
return free;
.
#3:50
":disfunc_leave_msg()";
"-> Message printed in room after a disconnected user is taken home.";
":disfunc_arrive_msg()";
"-> Message printed in disconnected user's home when taken there.";
if (msg = this:get_message(verb[1..length(verb) - 4]))
return $string_utils:pronoun_sub(msg, @args);
elseif (valid(zone = this:zone()))
return zone:(verb)(@args);
else
return $zone:(verb)(@args);
endif
.
#3:51
":send_user_home(who)";
"-> Send 'who' to their home, printing `housekeeper' messages.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
who = args[1];
here = who.location;
home = who.home;
if (((!valid(who)) || (who in connected_players())) || (here == home))
return E_NONE;
endif
fork (0)
"...forked in case of errors in whose :moveto verb...";
if (who.location != home)
move(who, $player_start);
endif
endfork
who:moveto(home);
if ((who.location != here) && (msg = this:disfunc_leave_msg()))
here:announce(msg);
endif
if ((who.location == home) && (msg = this:disfunc_arrive_msg()))
home:announce(msg);
endif
.
#3:52
":default_{o,}yanked_msg()";
"Message printed when someone attempts to remove an object chained to this room, and that object does not have its own message defined in this.chained_objects.";
return $string_utils:pronoun_sub(this.(verb), @args);
.
#3:53
":announce_yanked_messages(thief, object)";
"Announce messages when thief attempts to leave this room with the given object, which has been chained here.";
"Look for a {o,}yank_msg on the object first.  If that is not defined, use the room's default_{o,}yank_msg.";
user = args[1];
dobj = args[2];
msg = (dobj:yanked_msg(user, this) || (dobj.yanked_msg && $string_utils:pronoun_sub(dobj.yanked_msg, user))) || this:default_yanked_msg(user);
if (msg)
omsg = (dobj:oyanked_msg(user, this) || (dobj.oyanked_msg && $string_utils:pronoun_sub(dobj.oyanked_msg, user))) || this:default_oyanked_msg(user);
else
msg = $string_utils:pronoun_sub(dobj.oyanked_msg || this.default_oyanked_msg, $you);
omsg = (this:default_oyanked_msg(user) || dobj:oyanked_msg(user, this)) || $string_utils:pronoun_sub(dobj.oyanked_msg, user);
endif
if (msg)
player:tell(msg);
endif
if (omsg)
this:announce(omsg);
endif
.
#3:54
":matching_contents()";
"Include exits in the room's matching pool.";
return {@pass(@args), @this:exits()};
.
#3:55
":audience_for_event(type)";
e = args[1];
if (typeof(objs = this:("audience_for_" + e)(@listdelete(args, 1))) == LIST)
return objs;
else
return this:matching_contents();
endif
.
#3:56
":occupants()";
"Return a list of characters inside the room.";
o = {};
for what in (this:contents())
if ($rpg:is_character(what))
o = {@o, what};
endif
endfor
return o;
.
#3:57
":set_zone_coordinates(coords)";
"If coords is true (any list will be), set this.zone_coordinates to that value, and update cell info in the containing zone.";
"If coords is false, then clear both the property and the info in the zone.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
elseif (!valid(zone = this:zone()))
return E_INVIND;
elseif (xyz = args[1])
"...clear the old info...";
zone:update_cell_info(this.zone_coordinates);
"...set our property...";
this.zone_coordinates = xyz;
"...update the zone's info...";
return zone:update_cell_info(xyz, this);
else
"...clear us from the zone...";
return zone:update_cell_info(this.zone_coordinates);
endif
.
#3:58
":get_integration_msg(object) -> Integration info for 'object'.";
o = args[1];
if (typeof(m = $code_utils:verb_or_property(o, "place_integration_msg", {this})) != ERR)
return m;
elseif (typeof(m = $code_utils:verb_or_property(o, "look_place_msg", {this})) != ERR)
return m;
else
return "";
endif
.
#3:59
":jump_code(obj)";
"Return a unique code which will allow the given player to teleport back into this room OOC.";
what = args[1];
user = args[1].owner;
if (!$object_utils:contains(this, what))
return E_NACC;
endif
seed = $rpg.jump_seed;
code = crypt(tostr(user, this), seed);
return code[8..13];
.
#3:60
":check_jump_code(obj, str)";
"Check if the given string is the correct jump code for use by obj to gain access to this room.";
return args[2] == this:jump_code(args[1]);
.
#3:61
":exits_blocked_by(char)";
"Return a list of exits blocked by the given character.";
"This should never be more than a list of one.";
char = args[1];
for x in (this:exits())
if (x:is_blocked_by(char))
return {x};
endif
endfor
return {};
.
#3:62
":check_exiting_object(what)";
"Check if a chained object is being spirited away.";
{what} = args;
if (`what:maybe_yank_chain() ! ANY')
return 1;
endif
for item in ($object_utils:all_contents(what))
`item:maybe_yank_chain() ! ANY';
endfor
return 0;
"---Wizardly--";
.
#3:63
":ititle_list(list[, @args])";
"Special ititle list for rooms will show same-named objects in groups.";
s = t = {};
for o in (args[1])
n = `o:ititle() ! E_VERBNF => "recyclable"';
if (i = n in s)
t[i] = listappend(t[i], o);
else
s = listappend(s, n);
t = listappend(t, {o});
endif
endfor
su = $string_utils;
eu = $english;
l = {};
for i in [1..length(t)]
n = length(t[i]);
if (n == 1)
l = listappend(l, s[i]);
else
l = listappend(l, (su:english_number(n) + " ") + eu:pluralize(`t[i][1]:title() ! ANY => "recyclable"'));
endif
endfor
l = su:english_list(l, @listdelete(args, 1));
return (verb[length(verb)] == "c") ? su:capitalize(l) | l;
.
#3:64
":tell_title()";
EMU = $emu;
if (b = player:get_option($display_options, "bold_rooms"))
if (typeof(b) == STR)
title = $string_utils:pronoun_sub(b, this);
else
title = tostr(EMU:tokenize("bold"), this:titlec(), EMU:tokenize("normal"));
endif
else
title = this:titlec();
endif
player:tell(EMU.ANSI_Line_prefix, title);
.
#3:65
"Allow $geomancer to perform its magic on all rooms.";
return pass(@args) || ($geomancer in args);
.
#3:66
":take_exit(STR|OBJ which, OBJ who[, NUM just_move_me_dammit])";
"Make who take which exit, setting player to who.  Allow any programmer to move someone in this room.  That might be bad, but hey--with this core you TRUST progs.";
"If the third arg is given, simply attempt to move OBJ to the destination.";
{dir, ?char = player, ?quiet = 0} = args;
exit = dir;
if (char.location != this)
return E_PERM;
endif
player = char;
if ((typeof(exit) == OBJ) || valid(exit = player.location:match_exit(dir)))
if (quiet)
char:moveto(exit.dest);
else
exit:invoke_for_walker();
endif
room = player:room();
if (room != exit.dest)
return E_NACC;
endif
return {room};
elseif (((!quiet) && (cmd = $exit_utils:match_direction(dir))) && $object_utils:has_callable_verb(this, cmd))
"this shouldn't be a problem, but i removed @args just in case -- 980225";
this:(cmd)();
room = player:room();
if (room == this)
return E_NACC;
endif
return {room};
else
return exit;
endif
.
#3:67
":will_link_to(exit, caller_perms(), caller)";
{exit, who, where} = args;
return ((where == this) || this:is_controllable_by(who, where)) || $rpg:trusted(who);
.
#3:68
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.free_home = 0;
this.free_entry = $gm;
this.residents = {};
this.zone_coordinates = 0;
.
#3:69
"Copied from generic extended rpg room (#5540):is_resident by Janus (#3597) Wed Aug  6 10:30:43 1997 CDT";
{who} = args;
return `who in this.residents ! E_TYPE' || (this.free_home && `who.home == this ! E_PROPNF');
.
#4:0
set_task_perms(player);
nargs = length(args);
pos = "named" in args;
if ((pos <= 1) || (pos == nargs))
pos = "called" in args;
endif
if ((pos <= 1) || (pos == nargs))
player:notify("Usage:  @create <parent-class> named [name:]alias,...,alias");
player:notify("   or:  @create <parent-class> named name-and-alias,alias,..., alias");
player:notify("");
player:notify("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.");
player:notify("You can use \"called\" instead of \"named\"");
return;
endif
parentstr = $string_utils:from_list(args[1..pos - 1], " ");
namestr = $string_utils:from_list(args[pos + 1..nargs], " ");
if (parentstr[1] == "$")
parent = #0.(parentstr[2..length(parentstr)]);
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:get_option($build_options, "object_flags") || ""))
object.(f) = 1;
endfor
move(object, player);
$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, ")."));
.
#4:1
"@recreate <object> as <parent-class> named [name:]alias,alias,...";
"  effectively recycles and creates <object> all over again.";
set_task_perms(player);
named = "named" in args;
as = prepstr in args;
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."});
return;
elseif ($command_utils:object_match_failed(dobj = player:my_match_object(dobjstr), dobjstr))
return;
elseif (is_player(dobj))
player:notify("You really *don't* want to do that!");
return;
endif
parentstr = $string_utils:from_list(args[as + 1..named - 1], " ");
namestr = $string_utils:from_list(args[named + 1..length(args)], " ");
if (parentstr[1] == "$")
parent = #0.(parentstr[2..length(parentstr)]);
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
move(dobj, player);
$building_utils:set_names(dobj, namestr);
if ((other_names = setremove(dobj.aliases, dobj.name)) != {})
aka = (" (aka " + $string_utils:english_list(other_names)) + ")";
else
aka = "";
endif
player:notify(tostr("Object number ", dobj, " is now ", dobj.name, aka, " with parent ", parent.name, " (", parent, ")."));
.
#4:2
set_task_perms(player);
nargs = length(args);
if ((prepstr == "to") && iobjstr)
exit_spec = dobjstr;
m = match(iobjstr, "%(.+%) +with +%(.+%)$");
if (!m)
room = iobjstr;
else
room = substitute("%1", m);
exit_parent_string = substitute("%2", m);
endif
elseif (argstr)
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)))
other_room = player:_create(player:get_option($build_options, "room"));
if (typeof(other_room) == ERR)
player:notify(tostr(other_room));
return;
endif
other_room.name = room;
other_room.aliases = {room};
move(other_room, zone = player:get_option($build_options, "zone"));
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)
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
if (exit_parent_string)
exit_parent = player:my_match_object(exit_parent_string);
if (!$object_utils:isa(exit_parent, $exit))
exit_parent = player:get_option($build_options, "exit");
player:tell("\"", exit_parent_string, "\" does not name an exit parent.  Defaulting to ", exit_parent:id(), ".");
endif
else
exit_parent = player:get_option($build_options, "exit");
endif
to_ok = $building_utils:make_exit(exits[1], player.location, other_room, player.recreate_enabled, exit_parent);
if (to_ok && (length(exits) == 2))
from_ok = $building_utils:make_exit(exits[2], other_room, player.location, player.recreate_enabled, exit_parent);
endif
endif
.
#4:3
"Usage:  @auditDB [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_failed(dobj, dobjstr) && (!(valid(dobj = $string_utils:literal_object(dobjstr)) && $command_utils:yes_or_no("Continue?"))))
return;
endif
dobjwords = $string_utils:words(dobjstr);
if (args[1..length(dobjwords)] == dobjwords)
args = args[length(dobjwords) + 1..length(args)];
endif
if (!(parse_result = $code_utils:_parse_audit_args(@args)))
player:notify(tostr("Usage:  ", verb, " [player] [from <start>] [to <end>] [for <match>]"));
return;
endif
start = parse_result[1];
end = parse_result[2];
match = parse_result[3];
player:notify(tostr("Objects owned by ", valid(dobj) ? dobj.name | dobj, ((" (from #" + tostr(start)) + " to #") + tostr(end), match ? " matching " + match | "", ")", ":"));
player:notify("");
count = 0;
"Only print every third suspension";
do_print = 0;
for i in [start..end]
o = toobj(i);
if ($command_utils:running_out_of_time())
(do_print = (do_print + 1) % 3) || player:notify(tostr("... ", o));
suspend(5);
endif
if (valid(o) && (o.owner == dobj))
found = 0;
names = {o.name, @o.aliases};
while (names && (!found))
if (index(names[1], match) == 1)
found = 1;
endif
names = listdelete(names, 1);
endwhile
if (found)
player:tell(player:object_audit_string(o));
count = count + 1;
do_print = 0;
endif
endif
endfor
if (count)
player:notify("");
endif
player:notify(tostr("Total: ", count, " object", (count == 1) ? "." | "s."));
.
#4:4
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."));
else
player:notify(tostr(dobj.name, " is not enrolled in the object ownership system.  Use @countDB instead."));
endif
.
#4:5
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 i in [1..tonum(max_object())]
if ($command_utils:running_out_of_time())
player:notify("Counting...");
suspend(5);
endif
o = toobj(i);
if (valid(o) && (o.owner == dobj))
count = count + 1;
endif
endfor
player:notify(tostr(dobj.name, " currently owns ", count, " object", (count == 1) ? "." | "s."));
.
#4:6
"$player:owned_objects -- sorts a players .owned_objects property in ascending";
"order so it looks nice on @audit.";
if (player != this)
return E_PERM;
endif
if (typeof(player.owned_objects) == LIST)
ret = $list_utils:sort(player.owned_objects);
if (typeof(ret) == LIST)
player.owned_objects = ret;
player:tell("Your .owned_objects list has been sorted.");
return 1;
else
player:tell("Something went wrong. .owned_obejcts not sorted.");
return 0;
endif
else
player:tell("You are not enrolled in .owned_objects scheme, sorry.");
endif
.
#4:7
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:8
for x in (player.owned_objects)
if ((!valid(x)) || (x.owner != player))
player.owned_objects = setremove(player.owned_objects, x);
if (valid(x))
player:tell("Removing ", x.name, "(", x, "), owned by ", valid(x.owner) ? x.owner.name | "<recycled player>", " from your .owned_objects property.");
else
player:tell("Removing invalid object ", x, " from your .owned_objects property.");
endif
endif
endfor
player:tell(".owned_objects property verified.");
.
#4:9
"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..length(dobjstr)];
nickname = "@" + name[1..length(name) - 4];
e = add_property(object, name, value, {player, "rc"});
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:10
"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..length(name) - 4];
e = delete_property(object, name);
if (e == E_PROPNF)
player:notify(tostr("No ", nickname, " message found on ", object.name, "."));
elseif (typeof(e) == ERR)
player:notify(tostr(e));
else
player:notify(tostr(nickname, " message removed from ", object.name, "."));
endif
.
#4:11
"Given any of several formats people are likely to use for a @message";
"property, return the canonical form (\"foobar_msg\").";
name = args[1];
if (name[1] == "@")
name = name[2..length(name)];
endif
if ((length(name) < 4) || (name[length(name) - 3..length(name)] != "_msg"))
name = name + "_msg";
endif
return name;
.
#4:12
"Usage: @realm [owner] [from root] [missing list_to_miss]";
"Where owner is a player's name or #nnn";
"      root is $property or #nnn";
"      list_to_miss elements are #nnn or $property";
set_task_perms(player);
"Let the owner kill the task...";
miss = {};
root = #1;
who = {};
c = 1;
for q in (args)
if (c == 1)
if ((q == "*") || (q == "!"))
who = setadd(who, $nothing);
elseif (q == tostr(z = toobj(q)))
who = setadd(who, z);
elseif (index("from", q))
c = 2;
elseif (index("missing", q))
c = 3;
elseif (valid(z = $string_utils:match_player(q)))
who = setadd(who, z);
endif
elseif (c == 2)
if (index("missing", q))
c = 3;
elseif (valid(z = this.location:match_object(q)))
root = z;
elseif (q == tostr(z = toobj(q)))
root = z;
elseif (q == "me")
root = this;
elseif (q == "here")
root = this.location;
elseif (((q[1] == "$") && ((z = q[2..length(q)]) in properties(#0))) && (typeof(#0.(z)) == OBJ))
root = #0.(z);
endif
elseif (c == 3)
if (q == tostr(z = toobj(q)))
miss = listappend(miss, z);
elseif ((q[1] == "$") && ((z = q[2..length(q)]) in properties(#0)))
miss = listappend(miss, #0.(z));
elseif (valid(z = this.location:match_object(q)))
miss = listappend(miss, z);
endif
endif
endfor
if (who == {})
who = {player};
endif
if ((player.wizard && (length(who) == 1)) && valid(who[1]))
quota = tostr(", ", who[1].ownership_quota, " additional allowed");
else
quota = "";
endif
anc = {};
spc = "";
anc = $object_utils:ancestors(root);
for k in [1..length(anc)]
foo = anc[(length(anc) + 1) - k];
player:notify(tostr(spc, foo.name, " (", foo, ") [", foo.owner.name, " (", foo.owner, ")]"));
spc = spc + "  ";
endfor
text = this:realm2(root, who, spc, miss);
player:notify_lines(text[2]);
player:notify(tostr("*** Objects: ", length(text[2]), " in hierarchy, ", text[1], " owned", quota, ". ***"));
.
#4:13
root = args[1];
owner = args[2];
space = args[3];
missing = args[4];
number = 0;
text = {};
set_task_perms(caller_perms());
for foo in (children(root))
$command_utils:suspend_if_needed(0);
if (!(foo in missing))
branch = this:realm2(foo, owner, space + "  ", missing);
text = {@text, @branch[2]};
number = number + branch[1];
endif
endfor
bra = ket = "";
if (((((root.owner in owner) && 1) != (($nothing in owner) && 1)) && (number = number + 1)) || ((text != {}) && ((bra = "<") && (ket = ">"))))
text = listinsert(text, tostr(space, bra, root.name, " (", root, ")", ket, @((length(owner) == 1) && (owner != {$nothing})) ? {} | {" [", valid(root.owner) ? root.owner.name | "** recycled **", " (", root.owner, ")]"}));
endif
return {number, text};
.
#4:14
root = args[1];
indent = args[2];
members = args[3];
printed = args[4];
if (root in members)
player:tell(indent, root.name, " (", root, ")");
else
player:tell(indent, "<", root.name, " (", root, ")>");
endif
indent = indent + "  ";
set_task_perms(caller_perms());
for c in (children(root))
$command_utils:suspend_if_needed(10);
if (c in printed)
this:classes_2(c, indent, members, printed);
endif
endfor
.
#4:15
set_task_perms(caller_perms());
if (this:get_option($build_options, "bi_create"))
return $quota_utils:bi_create(@args);
else
return $recycler:(verb)(@args);
endif
.
#4:16
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.");
elseif (typeof(result = player:_chparent(object, parent)) != ERR)
player:notify("Parent changed.");
elseif ((result == E_INVARG) && (valid(object) && valid(parent)))
player:notify(tostr("Some property existing on ", parent, " is defined on ", object, " or one of its descendants."));
player:notify(tostr("Try @check-chparent ", dobjstr, " to ", iobjstr));
else
player:notify(tostr(result));
endif
.
#4:17
"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:18
"Syntax:  @set <object>.<property-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.";
"There are four methods used to attempt to write to the property, in the following order:";
"   :set_<property-name>(value)";
"   :set_stat(property-name, value)";
"   .property-name = value";
"   :set_property_value(property-name, value)";
set_task_perms(player);
if (this != player)
player:tell("You can't use the @set verb on ", this.name, ".");
return E_PERM;
endif
l = $code_utils:parse_propref(dobjstr);
if (!l)
player:tell(verb, " => No property was referenced.");
return E_INVARG;
endif
dobj = player:my_match_object(l[1]);
if ($match_utils:object_match_failed(dobj, l[1]))
return;
endif
prop = l[2];
if (m = match(argstr, ((("^" + $string_utils:regexp_quote(dobjstr)) + " +") + prepstr) + " +%(.+%)$"))
iobjstr = substitute("%1", m);
endif
if ((!m) || (!iobjstr))
player:tell(verb, " => No value given.");
return E_ARGS;
endif
if (iobjstr[1] in {"\"", "{"})
val = $string_utils:to_value(iobjstr);
if (!val[1])
player:tell(verb, " => Couldn't parse: ", iobjstr);
return E_INVARG;
endif
val = val[2];
elseif ((match(iobjstr, "^%([+-]?[0-9]%|#[0-9]%)") && (parsed = $string_utils:to_value(iobjstr))) && parsed[1])
val = parsed[2];
elseif ($code_utils:tonum(iobjstr) != E_TYPE)
val = tonum(iobjstr);
elseif ($code_utils:toobj(iobjstr) != E_TYPE)
val = toobj(iobjstr);
else
val = player:my_match_object(iobjstr);
if ($match_utils:object_match_failed(val, iobjstr))
return;
endif
endif
if ($object_utils:has_callable_verb(dobj, "set_" + prop))
result = `dobj:("set_" + prop)(val) ! E_PERM, E_PROPNF';
else
result = E_PROPNF;
endif
if ((result == E_PERM) || (result == E_PROPNF))
if ($object_utils:has_callable_verb(dobj, "set_stat"))
result = `dobj:set_stat(prop, val) ! E_PERM, E_PROPNF';
endif
if ((result == E_PERM) || (result == E_PROPNF))
result = `dobj.(prop) = val ! E_PERM, E_PROPNF';
set_with = "";
else
set_with = tostr(" (via :set_stat)");
endif
else
set_with = tostr(" (via ", ":set_", prop, ")");
endif
if (result != E_PERM)
"...ok...";
elseif ($object_utils:has_callable_verb(dobj, "set_property_value"))
result = dobj:set_property_value(prop, val);
set_with = " (via :set_property_value)";
endif
if (result == E_PERM)
player:tell(verb, " => You aren't allowed to set that property.");
elseif (result == E_PROPNF)
player:tell(verb, " => That property does not exist.");
elseif ((typeof(result) == ERR) && (val != result))
player:tell(verb, " => ", result);
else
player:tell(verb, " ", dobj:id(), ".\"", prop, "\"", set_with, " => ", $string_utils:print(val));
endif
.
#4:19
":option_packages()";
return {@pass(@args), $build_options};
.
#4:20
"Usage:  @spawn <parent-class>";
"";
"This creates an object with either the same name and aliases as the parent or, if the parent defines a spawned_name or spawned_aliases verb or property, those names and/or aliases.";
if (caller != this)
return raise(E_PERM);
endif
set_task_perms(player);
if (!args)
player:notify_lines($code_utils:verb_documentation());
endif
parent = player:my_match_object(dobjstr);
if ($match_utils:object_match_failed(parent, dobjstr, player:env()))
return;
endif
object = player:_create(parent);
if (typeof(object) == ERR)
player:notify(tostr(object));
return;
endif
for f in ($string_utils:char_list(player:get_option($build_options, "object_flags") || ""))
object.(f) = 1;
endfor
move(object, player);
name = $code_utils:call_verb(parent, "spawned_name") || parent.name;
object:set_name(name);
aliases = $code_utils:call_verb(parent, "spawned_aliases") || parent.aliases;
object:set_aliases(aliases);
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, ")."));
.
#4:21
"@zone";
"  Show this room's current zone.";
"@zone <zone>";
"  Place this room into the given zone.";
"@zone [zone] at x,y,z";
"  Place this room at the given coordinates in the current [given] zone.";
"@zones";
"  List available zones.";
here = player:room();
if (verb == "@zones")
current = here:zone();
default = player:get_option($build_options, "zone");
for zone in ($object_utils:leaves($zone))
player:tell("-> ", $string_utils:nn(zone), (zone == current) ? " [current]" | "", (zone == default) ? " [default]" | "");
zone:look_self();
player:tell("");
endfor
player:tell("Use `@zone <newzone>' to rezone this room.");
return;
elseif (!argstr)
if (valid(zone = here:zone()))
c = here.zone_coordinates;
player:tell($string_utils:nn(here), " is currently zoned in ", $string_utils:nn(zone), c ? (" at coordinates " + $string_utils:from_list(c, ",")) + "." | ".");
else
player:tell($string_utils:nn(here), " is unzoned.");
endif
return;
elseif (!here:is_controllable_by(player))
player:tell("You aren't allowed to zone this room.");
return E_PERM;
endif
zonestr = dobjstr;
if (zonestr)
if (!$object_utils:isa(zone = toobj(zonestr), $zone))
zone = $match_utils:match_list(zonestr, $object_utils:leaves($zone));
if (!zone)
player:tell("I don't know of any zone named \"", zonestr, ".\"");
return $failed_match;
elseif (length(zone) > 1)
player:tell("I don't know whether you mean ", $string_utils:title_list(zone, "", " or "));
return $ambiguous_match;
endif
zone = zone[1];
endif
result = here:moveto(zone);
if (here.location == zone)
player:tell($string_utils:nn(here), " is now located in zone ", $string_utils:nn(zone), ".");
elseif (result == E_RECMOVE)
player:tell("You can't move ", here:dname(), " into ", $string_utils:nn(zone), " while one contains the other.  Try `@eject ", zone.name, "' or `@eject ", zone.name, " from me', then re-@zone.");
return result;
else
player:tell("For some reason, I can't zone ", here:dname(), " in ", $string_utils:nn(zone), ".");
return result;
endif
else
zone = here:zone();
endif
if (!prepstr)
return;
elseif ((prepstr != "at") || (!iobjstr))
player:tell_lines($code_utils:verb_documentation());
return;
endif
pattern = "{? *%(-?[0-9]+%) *, *%(-?[0-9]+%) *, *%(-?[0-9]+%) *}? *";
m = match(iobjstr, pattern);
if (!m)
player:tell("The coordinates must be a comma-delimited series of numbers.");
return;
endif
coords = {};
for i in [1..3]
coords = {@coords, tonum(substitute(tostr("%", i), m))};
endfor
if ((info = zone:get_cell_info(coords)) && (info[1] != here))
player:tell($string_utils:nn(info[1]) + " is already placed in that spot. You'll either have to re-zone it first, or find another spot.");
else
here:set_zone_coordinates(coords);
player:tell($string_utils:nn(here), " is now zoned in ", $string_utils:nn(zone), coords ? (" at coordinates " + $string_utils:from_list(coords, ",")) + "." | ".");
endif
.
#4:22
set_task_perms(caller_perms());
if (this.recreate_enabled)
return $recycler:(verb)(@args);
else
return create(@args);
endif
.
#4:23
"@chain <object>";
"";
"There are times when one would like to keep an object inside a room; reference books in a library, for example.";
"This command 'chains' the given object to the room.  If someone attempts to walk out with the object, it will be yanked back in and messages will be displayed.";
"Messages are either @yanked and @oyanked on the object, or @default_yanked and @default_oyanked on the room.  Both are pronoun_sub'd with player as the thief and dobj as the object being taken.";
"If the object is teleported out (as opposed to being walked out) by its owner, it will not be yanked back.";
"***NOTE*** With the 09-29-95 hack to this verb, objects are chained not to the room they're in--NOT a specific room.  This moves book-keeping from room to object, and avoids the bogus chaining of recreated objects.";
"";
"See also: @unchain, @chained";
if ((player != caller) && (player != caller_perms()))
player:notify(("You can't call " + verb) + " that way.");
return E_PERM;
endif
su = $string_utils;
here = player.location;
dobj = player:my_match_object(dobjstr, e = player:env());
if ($match_utils:object_match_failed(dobj, dobjstr, e))
return;
elseif (!$object_utils:has_callable_verb(dobj, "is_chained_to"))
player:notify(su:nn(dobj) + " cannot be chained.");
elseif (!dobj:is_controllable_by(player))
player:notify(("You aren't allowed to @chain " + su:nn(dobj)) + ".");
elseif (dobj:is_chained())
player:notify(su:nn(dobj) + " is already chained.");
else
dobj.chained = here;
player:notify(((su:nn(dobj) + " will now be chained to ") + su:nn(here)) + ".", (!here:is_controllable_by(player)) ? "  Be aware that the owner can @unchain the object if it is found unsuitable." | "");
endif
.
#4:24
"@unchain <object>";
"";
"Undo the effect of @chain, allowing the given object to be removed from this room.  See help on @chain for more details.";
if ((player != caller) && (player != caller_perms()))
player:notify(("You can't call " + verb) + " that way.");
return E_PERM;
endif
here = player.location;
dobj = player:my_match_object(dobjstr, e = player:env());
if ($match_utils:object_match_failed(dobj, dobjstr, e))
return;
endif
su = $string_utils;
if (!dobj:is_chained())
player:notify(su:nn(dobj) + " is not chained anywhere.");
elseif ((!dobj:is_controllable_by(player)) && (!here:is_controllable_by(player)))
player:notify(("You aren't allowed to @unchain " + su:nn(dobj)) + ".");
else
old = dobj.chained;
dobj.chained = #-1;
player:notify(((su:nn(dobj) + " has been freed from ") + su:nn(old)) + ".");
endif
.
#4:25
"@chained";
"";
"List what objects are chained to this room.  See 'help @chain' for more details.";
here = player.location;
chained_objects = {};
for o in (here:contents())
if (o:is_chained_to(here))
chained_objects = {@chained_objects, o};
endif
endfor
if (!chained_objects)
player:notify(("None of the objects here in " + $string_utils:nn(here)) + " are chained.");
return E_NONE;
endif
player:notify(("Objects chained to " + $string_utils:nn(here)) + ":");
for foo in (chained_objects)
player:notify("  " + $string_utils:nn(foo));
endfor
.
#4:26
set_task_perms(player);
if ((caller != this) && (!player.programmer))
player:notify("You'll have to be a builder before you can be a builder.  If you think you've something worthwhile to contribute, ask some big cheese for the bit.  Make sure you check grammar, spelling, and themeliness of your own character first.");
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 ((this != player) && (!$object_utils:isa(player, $player)))
"...They chparented to #1 and want to chparent back to $prog.  Probably for some nefarious purpose...";
player:notify("You don't seem to already be a valid player class.  Perhaps chparenting away from the $player hierarchy was not such a good idea.  Permission denied.");
elseif (is_player(object) && (!$object_utils:isa(parent, $player)))
player:notify(tostr(object, " is a player and ", parent, " is not a player class."));
player:notify("You really *don't* want to do this.  Trust me.");
elseif (typeof(result = chparent(object, parent)) != ERR)
player:notify("Parent changed.");
elseif ((result == E_INVARG) && (valid(object) && valid(parent)))
player:notify(tostr("Some property existing on ", parent, " is defined on ", object, " or one of its descendants."));
player:notify(tostr("Try @check-chparent ", dobjstr, " to ", iobjstr));
else
player:notify(tostr(result));
endif
.
#4:27
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
res = dobj.key = key;
if (typeof(res) == ERR)
player:notify(tostr(res, "."));
else
player:notify(tostr("Locked ", dobj.name, " to this key:"));
player:notify(tostr("  ", $lock_utils:unparse_key(key)));
endif
endif
.
#4:28
set_task_perms(player);
if (dobjstr == "")
player:notify(tostr("Usage:  ", verb, " <object-or-property-or-verb>"));
return;
endif
if (index(dobjstr, ".") && (spec = $code_utils:parse_propref(dobjstr)))
if (valid(object = player:my_match_object(spec[1])))
return $code_utils:show_property(object, spec[2]);
endif
elseif (spec = $code_utils:parse_verbref(dobjstr))
if (valid(object = player:my_match_object(spec[1])))
return $code_utils:show_verbdef(object, spec[2]);
endif
elseif (((dobjstr[1] == "$") && ((pname = dobjstr[2..length(dobjstr)]) in properties(#0))) && (typeof(#0.(pname)) == OBJ))
if (valid(object = #0.(pname)))
return $code_utils:show_object(object);
endif
elseif ((dobjstr[1] == "$") && (spec = $code_utils:parse_propref(dobjstr)))
return $code_utils:show_property(#0, spec[2]);
else
if (valid(object = player:my_match_object(dobjstr)))
return $code_utils:show_object(object);
endif
endif
$command_utils:object_match_failed(object, dobjstr);
.
#4:29
"@display <object>[.[property]]*[,[inherited_property]]*[:[verb]]*[;[inherited_ verb]]*";
"Null names for properties and verbs are interpreted as meaning all of them.";
try
{opivu, it, vrb, prop, inh, cant} = this:parse_display_args(@args);
except (E_ARGS)
return;
endtry
TOO_MANY_PROPERTIES = 100;
if (({""} in opivu) || (opivu[2..5] == {{}, {}, {}, {}}))
size_msg = tostr(", ", $string_utils:to_bytes($quota_utils:recent_object_bytes(it)));
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 " | "", "]", size_msg));
if (it.owner != (is_player(it) ? it | this))
this:notify(tostr("  Owned by ", valid(p = it.owner) ? p.name | "** extinct **", " (", p, ")."));
endif
this:notify(tostr("  Child of ", valid(p = parent(it)) ? p.name | "** none **", " (", p, ")."));
if (it.location != $nothing)
this:notify(tostr("  Location ", valid(p = it.location) ? p.name | "** unplace (tell a wizard, fast!) **", " (", p, ")."));
endif
endif
set_task_perms(this.owner);
blankargs = this:get_option($prog_options, "blank_tnt") ? {"this", "none", "this"} | #-1;
shortprep = this:get_option($prog_options, "shortprep");
for b in (vrb)
$command_utils:suspend_if_needed(0);
this:notify($code_utils:display_line_verb(@b, shortprep, blankargs));
endfor
all = {@prop, @inh};
max = (length(all) < 4) ? 999 | (this:linelen() - 56);
depth = (length(all) < 4) ? -1 | 1;
truncate_owner_names = length(all) > 1;
darkonly = (length(all) > 1) && this:get_option($prog_options, "darkonly");
if (((lenp = length(all)) < TOO_MANY_PROPERTIES) || $command_utils:yes_or_no(it:grammar_sub(tostr("%n <%#> %<has> ", lenp, " properties.  Are you sure you want to see them all?"))))
for q in (all)
$command_utils:suspend_if_needed(0);
if (darkonly && is_clear_property(it, q))
continue;
endif
this:notify($code_utils:display_line_property(it, q, max, depth, truncate_owner_names));
endfor
endif
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
.
#4:30
"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 ($match_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 = $match_utils:match(dobjstr, children(iobj)), 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
.
#4:31
if (caller != this)
return E_PERM;
endif
plyrs = args ? listdelete($command_utils:player_match_result($string_utils:match_player(args), args), 1) | connected_players();
if (!plyrs)
return;
endif
$code_utils:show_who_listing(plyrs);
.
#4:32
set_task_perms((caller_perms() == $nothing) ? player | caller_perms());
dobj = $string_utils:match_player(dobjstr);
if (!dobjstr)
player:notify(tostr("Usage: ", verb, " <player> [from <start>] [to <end>]"));
return;
elseif ($command_utils:player_match_result(dobj, dobjstr)[1])
return;
elseif ((!$wiz_utils:is_builder(player)) && (!dobj:is_readable_by(player)))
player:tell("You aren't allowed to look at ", dobj:pp(), " objects.");
return E_PERM;
endif
dobjwords = $string_utils:words(dobjstr);
if (args[1..length(dobjwords)] == dobjwords)
args = args[length(dobjwords) + 1..length(args)];
endif
if (!(parse_result = $code_utils:_parse_audit_args(@args)))
player:notify(tostr("Usage:  ", verb, " player [from <start>] [to <end>]"));
return;
endif
start = parse_result[1];
end = parse_result[2];
player:notify(tostr("Objects owned by ", valid(dobj) ? dobj.name | dobj, ((" (from #" + tostr(start)) + " to #") + tostr(end), ")", ":"));
count = 0;
printed_anything = 0;
bytes = 0;
for o in (dobj.owned_objects)
$command_utils:suspend_if_needed(3);
if (valid(o) && ((tonum(o) >= start) && (tonum(o) <= end)))
player:notify(this:object_audit_string(o, 1));
count = count + 1;
printed_anything = 1;
bytes = bytes + $quota_utils:recent_object_bytes(o);
endif
endfor
player:notify($string_utils:left(tostr("-- ", count, " ", $english:pluralize("object", count), ", ", $string_utils:to_bytes(bytes), " "), 79, "-"));
.
#4:33
set_task_perms(player);
if (!$wiz_utils:is_builder(player))
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]..length(argstr)]);
if (q[1] == 0)
player:notify(tostr("Syntax error in initial value:  ", q[2]));
return;
endif
value = q[2];
args = {args[1], value, @$string_utils:words(q[1])};
nargs = length(args);
endif
perms = (nargs < 3) ? "rc" | args[3];
if (nargs < 4)
if (player.wizard)
player:notify("Being a wizard, you are forced to include a property-owner argument as a security precaution.");
return;
endif
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
e = add_property(object, name, value, {owner, perms});
if (typeof(e) != ERR)
player:notify(tostr("Property added with value ", $string_utils:print(object.(name), 1), "."));
elseif (e != E_INVARG)
player:notify(tostr(e));
elseif ($object_utils:has_property(object, name))
player:notify(tostr("Property ", object, ".", name, " already exists."));
else
for i in [1..length(perms)]
if (!index("rcw", perms[i]))
player:notify(tostr("Unknown permission bit:  ", perms[i]));
return;
endif
endfor
"...the only other possibility...";
player:notify("Property is already defined on one or more descendents.");
player:notify(tostr("Try @check-prop ", args[1]));
endif
.
#4:34
set_task_perms(player);
if ((length(args) != 1) || (!(spec = $code_utils:parse_propref(args[1]))))
player:notify(tostr("Usage:  ", verb, " <object>.<property>"));
return;
endif
object = player:my_match_object(spec[1]);
pname = spec[2];
if ($command_utils:object_match_failed(object, spec[1]))
return;
endif
result = delete_property(object, pname);
if (result == E_PROPNF)
player:notify("That object does not define that property.");
elseif (typeof(result) == ERR)
player:notify(tostr(result));
else
player:notify("Property removed.");
endif
.
#4:35
"@code object:verbname";
"";
"Show listing of verb code for the given specification, in a form suitable for uploading.  Line-wrapping and ANSI codes are disabled by displaying with a raw notify().";
"If a global name ($name) exists for either the verb location or its owner, that is used in the @verb/@prog header instead of the object number.";
set_task_perms(player);
if (!(spec = $code_utils:parse_verbref(dobjstr)))
player:notify_lines($code_utils:verb_documentation());
return E_INVARG;
endif
object = player:my_match_object(spec[1]);
if ($match_utils:object_match_failed(object, spec[1], player:env()))
return;
endif
what = object;
vname = spec[2];
while ((what != $nothing) && ((code = verb_code(what, vname)) == E_VERBNF))
what = parent(what);
endwhile
if (code == E_VERBNF)
player:notify("That object does not define that verb.");
return;
elseif (typeof(code) == ERR)
player:notify(tostr("Error getting verbcode: ", code));
return;
elseif (code == {})
player:notify("That verb has not been programmed.");
return;
endif
if (what != object)
player:notify(tostr("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 (pname = $core_utils:is_global_object(what))
oname = tostr("$", pname);
else
oname = tostr(what);
endif
owner = info[1];
if (pname = $core_utils:is_global_object(owner))
owner = tostr("$", pname);
elseif (owner == #2)
owner = "#2";
else
owner = owner.name;
endif
"Use raw notify() to strip all ANSI codes and indentation.";
notify(player, tostr("@verb ", oname, ":", $string_utils:print(info[3]), " ", $string_utils:from_list(vargs, " "), " \"", $string_utils:uppercase(info[2]), "\" ", owner));
notify(player, tostr("@prog ", oname, ":", vname));
for line in (code)
notify(player, line);
endfor
notify(player, ".");
.
#4:36
set_task_perms(player);
pflag = 0;
nflag = 1;
argspec = {};
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 = args[3..length(args)];
elseif (index("numbers", args[2]) == 1)
nflag = fval;
args = args[3..length(args)];
else
player:notify(tostr(args[1], " WHAT?"));
args = E_INVARG;
endif
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)
player:notify(tostr("Usage:  ", verb, " <object>:<verb> [<dobj> <prep> <iobj>] [with|without parentheses|numbers]"));
return;
elseif ($command_utils:object_match_failed(object = player:my_match_object(spec[1]), spec[1]))
return;
endif
what = object;
if (argspec)
vnum = $code_utils:find_verb_named(what, spec[2], 0);
while ((vnum < 0) ? valid(what = parent(what)) | (verb_args(what, vname = tostr(vnum)) != argspec))
vnum = $code_utils:find_verb_named(what, spec[2], vnum + 1);
endwhile
code = (vnum < 0) ? E_VERBNF | verb_code(what, vname, pflag);
else
vname = spec[2];
while (valid(what) && ((code = verb_code(what, vname, pflag)) == E_VERBNF))
what = parent(what);
endwhile
endif
if (code == E_VERBNF)
player:notify(tostr("That object does not define that verb", argspec ? " with those args." | "."));
elseif (typeof(code) == ERR)
player:notify(tostr(code));
elseif (code == {})
player:notify("That verb has not been programmed.");
else
if (what != object)
player:notify(tostr("Object ", object, " does not define that verb", argspec ? " with those args" | "", ", but its ancestor ", what, " does."));
endif
info = verb_info(what, vname);
vargs = verb_args(what, vname);
if (index(vargs[2], "/"))
vargs[2] = tostr("(", vargs[2], ")");
endif
player:notify(tostr(what, ":", $string_utils:print(info[3]), "   ", $string_utils:from_list(vargs, " ")));
for i in [1..length(code)]
if (nflag)
player:notify(tostr(" "[1..i < 10], i, ":  ", code[i]));
else
player:notify(code[i]);
endif
$command_utils:suspend_if_needed(0);
endfor
endif
.
#4:37
if (caller != this)
return E_PERM;
endif
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:get_option($prog_options, "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))
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:get_option($prog_options, "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 [0..length(vrbs) - 1]
vrb = setadd(vrb, {what, tostr(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 [0..length(verbs(z)) - 1]
vrb = setadd(vrb, {z, tostr(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
return {opivu, it, vrb, prop, inh, cant};
.
#4:38
"@value-edit object.prop";
"Manipulate some otherwise unwieldly value using the MOO editor.";
$value_editor:invoke(dobjstr, verb);
.
#4:39
set_task_perms(player);
if (length(args) != 2)
player:notify(tostr("Usage:  ", verb, " <object-or-property-or-verb> <permissions>"));
return;
endif
what = args[1];
perms = args[2];
if (index(what, ".") && (spec = $code_utils:parse_propref(what)))
if (valid(object = player:my_match_object(spec[1])))
pname = spec[2];
info = property_info(object, pname);
if (info == E_PROPNF)
player:notify("That object does not have that property.");
elseif (typeof(info) == ERR)
player:notify(tostr(info));
else
info[2] = perms = $perm_utils:apply(info[2], perms);
result = set_property_info(object, pname, info);
if (result == E_INVARG)
player:notify(tostr("\"", perms, "\" is not a valid permissions string for a property."));
elseif (typeof(result) == ERR)
player:notify(tostr(result));
else
player:notify(tostr("Property permissions set to \"", perms, "\"."));
endif
endif
return;
endif
elseif (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];
info = verb_info(object, vname);
if (info == E_VERBNF)
player:notify("That object does not define that verb.");
elseif (typeof(info) == ERR)
player:notify(tostr(info));
elseif (!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);
if (index(info, "w"))
player:notify("That would allow anyone to change your verb.");
elseif ((result = set_verb_info(object, vname, info)) == E_INVARG)
player:notify(tostr("\"", perms, "\" is not a valid permissions string for a verb."));
elseif (typeof(result) == ERR)
player:notify(tostr(result));
else
player:notify(tostr("Verb permissions set to \"", perms, "\"."));
endif
endif
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
if ((((object.r = r) == E_PERM) || ((object.w = w) == E_PERM)) || ((object.f = f) == E_PERM))
player:notify("Permission denied.");
else
player:notify(tostr("Object permissions set to \"", perms, "\"."));
endif
return;
endif
$command_utils:object_match_failed(object, what);
.
#4:40
"@remember <object>";
"Add an object to the list of those you wish to remember.";
if (caller != this)
raise(E_PERM);
endif
dobj = player:my_match_object(dobjstr);
if ($match_utils:object_match_failed(dobj, dobjstr))
return;
endif
if (dobj in this.known_objects)
player:notify(dobj:grammar_sub("You already known %n (%#)."));
return;
endif
this.known_objects = {@this.known_objects, dobj};
player:notify(dobj:grammar_sub("You commit %n (%#) to your @known memory."));
.
#4:41
"@forget <object>";
"Remove an object from the list of those you wish to remember.";
if (caller != this)
raise(E_PERM);
endif
dobj = player:my_match_object(dobjstr, this.known_objects);
if ($match_utils:object_match_failed(dobj, dobjstr, this.known_objects, "in your known objects list"))
return;
endif
this.known_objects = setremove(this.known_objects, dobj);
player:notify(dobj:grammar_sub("You erase %n (%#) from your @known memory."));
.
#4:42
"@known";
"Show all objects which have been @remember'd.";
if (caller != this)
raise(E_PERM);
endif
koutput = {};
player:notify("---@known----------");
for k in (this.known_objects)
koutput = {@koutput, @$builder_feature:object_desc(k)};
endfor
player:tell_lines(koutput);
player:notify("---End @known------");
.
#4:43
"@dump something [with [id=...] [noprops] [noverbs]]";
"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.";
set_task_perms(player);
dobj = player:my_match_object(dobjstr, e = player:env());
if ($match_utils:object_match_failed(dobj, dobjstr, e))
return;
elseif (prepstr && (prepstr != "with"))
player:notify(tostr("Usage:  ", verb, " something [with [id=...] [noprops] [noverbs] [browse]]"));
return;
endif
core_utils = $core_utils;
su = $string_utils;
targname = core_utils:dbref_str(dobj);
options = {"props", "verbs"};
if (iobjstr)
su = $string_utils;
for o in (su:explode(iobjstr))
if (index(o, "id=") == 1)
targname = o[4..length(o)];
elseif (o in {"noprops", "noverbs"})
options = setremove(options, o[3..length(o)]);
endif
endfor
endif
suspend_msg = "\"Suspending @dump...";
parent = parent(dobj);
pstring = core_utils:dbref_str(parent);
a = {dobj.name, @setremove(dobj.aliases, dobj.name)};
player:notify(tostr("@create ", pstring, " named ", su:from_list(a, ",")));
for p in (("props" in options) ? properties(dobj) | {})
pquoted = su:print(p);
info = property_info(dobj, p);
value = dobj.(p);
uvalue = (typeof(value) == LIST) ? "{}" | 0;
notify(player, tostr("@prop ", targname, ".", pquoted, " ", uvalue || su:print_suspended(value), " \"", (perms = info[2]) ? su:uppercase(info[2]) | "", "\" ", core_utils:dbref_str(info[1])));
if (uvalue && value)
notify(player, tostr(";", targname, ".(", pquoted, ") = ", su:print_suspended(value)));
endif
$command_utils:suspend_if_needed(0, suspend_msg);
endfor
for a in (("props" in options) ? $object_utils:ancestors(dobj) | {})
for p in (properties(a))
$command_utils:suspend_if_needed(1);
pquoted = su:print(p);
value = dobj.(p);
avalue = a.(p);
if (typeof(value) == ERR)
notify(player, tostr("\"", targname, ".(", pquoted, ") => ", $code_utils:error_name(value), " (", value, ")"));
elseif ((typeof(avalue) == ERR) || (value != avalue))
notify(player, tostr(";", targname, ".(", pquoted, ") = ", su:print_suspended(value)));
endif
endfor
$command_utils:suspend_if_needed(1, suspend_msg);
endfor
if (!("verbs" in options))
player:notify("\"***finished***");
return;
endif
player:notify("");
v = tostr(0);
while ((info = verb_info(dobj, v)) || (info == E_PERM))
vname = info[3];
if (index(vname, "(") < index(vname, ")"))
player:notify(tostr("\"Skipping ", dobj, ":\"", info[3], "\"..."));
elseif (typeof(info) == ERR)
player:notify(tostr("\"", dobj, ":", v, " -> ", info, "\";"));
else
if (i = index(vname, " "))
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]);
tail = tostr(" \"", (perms = info[2]) ? su:uppercase(info[2]) | "", "\" ", core_utils:dbref_str(info[1]));
notify(player, tostr("@verb ", targname, ":\"", info[3], "\" ", args[1], " ", prep, " ", args[3], tail));
if (code = verb_code(dobj, v, 1, 1))
notify(player, tostr("@prog ", targname, ":", vname));
for c in (code)
notify(player, c);
$command_utils:suspend_if_needed(0, suspend_msg);
endfor
player:notify_lines({".", ""});
endif
endif
if (index(tostr(" ", info[3], " "), " * "))
"... oh shit, we have a * verb.  may as well forget trying to list...";
"... the rest; they're invisible.  set v to something nonstring.";
v = E_TYPE;
else
v = tostr(tonum(v) + 1);
endif
suspend(0);
"$command_utils:suspend_if_needed(0, suspend_msg);";
endwhile
player:notify("\"***finished***");
.
#4:44
"@qedit object[.property] [as string|list|unbroken]";
"Prompt the user for lines of input, to be saved as object.property (the property defaults to description).  To save in a format other than the original, specify 'string' or 'list' after the optional 'as'.";
"If 'unbroken' is given, save after running through $string_utils:unbreak.";
set_task_perms(player);
dobjstr = index(dobjstr, ".") ? dobjstr | (dobjstr + ".description");
if (!(spec = $code_utils:parse_propref(dobjstr)))
player:tell("Usage: ", verb, " <object>.<property_name> [as string|list]");
return E_INVARG;
elseif ($match_utils:object_match_failed(object = player:my_match_object(spec[1]), spec[1], player:env()))
"...no matching object...";
return object;
elseif ((value = object.(propname = spec[2])) == E_PROPNF)
"...property doesn't exist...";
player:tell("That object does not have that property definition.");
return E_PROPNF;
elseif ((dt = typeof(value)) == ERR)
"...some other kind of error...";
player:tell("(", value, ") Command aborted.");
return value;
elseif ((!(dt in {STR, LIST})) || ((dt == LIST) && ((value != {}) && (typeof(value[1]) != STR))))
"...only allow players to edit a string or list a list of strings...";
player:tell(object.name, "(", object, ").", propname, " is not a string or a list of strings.");
return E_TYPE;
endif
if (prepstr != "as")
save_as = dt;
elseif (index(iobjstr, "STR"))
save_as = STR;
elseif (iobjstr == "LIST")
save_as = LIST;
elseif (iobjstr == "UNBROKEN")
save_as = "UNBROKEN";
else
save_as = dt;
endif
if (save_as == STR)
as_str = " as a single string.";
elseif (save_as == LIST)
as_str = " as a list of strings.";
else
as_str = " as 'unbroken' text.";
endif
propref = tostr(object.name, "(", object, ").\"", propname, "\"");
player:tell("Now entering text to ", propref, as_str);
lines = $command_utils:read_lines();
if (!lines)
player:tell("No text entered.");
return E_NONE;
endif
if (save_as == STR)
lines = $string_utils:from_list(lines);
elseif (save_as == "UNBROKEN")
lines = $string_utils:unbreak(lines);
endif
result = object.(propname) = lines;
if (typeof(result) == ERR)
player:tell("(", result, ") Property not written to.");
else
player:tell("Text written to ", propref, as_str);
endif
.
#4:45
"In case a staff member is IC for some reason, this prevents him from dying.";
return 1;
.
#5:0
"get thing";
set_task_perms(callers() ? caller_perms() | player);
if (this:is_airborne())
return this:catch(@args);
elseif (player:is_holding(this))
player:notify("You're already holding that!");
return E_NONE;
elseif (!player:is_local_to(this))
player:notify("You don't see that here.");
return E_RANGE;
elseif (!this:attempt_pickup(player))
return E_NACC;
endif
this:moveto(player);
if (player:is_holding(this))
if (!this:announce_messages("take_succeeded"))
player:notify_lines(this:take_succeeded_msg() || (("You take " + this:dname()) + "."));
(msg = this:otake_succeeded_msg()) && player:room_announce(msg);
endif
else
if (!this:announce_messages("take_failed"))
player:notify_lines(this:take_failed_msg() || (("You can't pick " + this:dname()) + " up."));
(msg = this:otake_failed_msg()) && player:room_announce(msg);
endif
endif
.
#5:1
"drop thing";
set_task_perms(callers() ? caller_perms() | player);
if (!player:is_holding(this))
player:tell("You aren't holding that.");
return E_RANGE;
endif
where = player:room();
if (!where:acceptable(this))
player:tell("You can't drop that here.");
return E_NACC;
endif
this:moveto(where);
if (this.location == where)
if (!this:announce_messages("drop_succeeded"))
player:notify_lines(this:drop_succeeded_msg() || (("You drop " + this:dname()) + "."));
(msg = this:odrop_succeeded_msg()) && where:announce(msg);
endif
else
if (!this:announce_messages("drop_failed"))
player:notify_lines(this:drop_failed_msg() || "You can't seem to drop that here.");
(msg = this:odrop_failed_msg()) && where:announce(msg);
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
"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:5
":is_airborne()";
"Is this object in the air; being thrown?";
i = this.thrown;
if (!i)
return 0;
elseif ($code_utils:task_valid(i[1]))
return listdelete(i, 1);
else
clear_property(this, "thrown");
endif
.
#5:6
"throw <thing> to/at/through <target>";
this:do_throw(@args);
.
#5:7
":terminate_throw()";
"Kill the throwing task and clear the 'thrown' property.";
if (id = this.thrown)
clear_property(this, "thrown");
kill_task(id[1]);
endif
.
#5:8
"catch this";
"If this object has been thrown, catch it.";
su = $string_utils;
if (!this:is_airborne())
player:tell(su:pronoun_sub("%[tdnamec] %<hasn't> been thrown."));
return E_RANGE;
endif
this:moveto(player);
if (!player:is_holding(this))
player:tell("Ack!  You fumbled it, butterfingers.");
else
this:announce_messages("catch");
this:terminate_throw();
endif
.
#5:9
"dodge this";
"If this object has been thrown at you, dodge it.";
if (!this:is_airborne())
player:tell(this:dnamec() + " hasn't been thrown.");
return E_RANGE;
elseif (this.thrown && (this.thrown[2] != player))
player:tell(this:dnamec() + " wasn't thrown at you.  Relax!");
return E_NONE;
endif
this:announce_messages("dodge");
this:strike_object(player.location);
player.location:struck_by_object(this);
this:terminate_throw();
.
#5:10
":look_place_msg()";
if (i = this:is_airborne())
return ((this:inamec() + " is sailing through the air towards ") + i[1]:iname()) + ".";
endif
return pass(@args);
.
#5:11
":do_throw(@args)";
"Guts method for throw/toss/pitch wrapper.";
if (!player:is_holding(this))
player:tell("You aren't holding that.");
return;
endif
iobj = valid(iobj) ? iobj | player:my_match_object(iobjstr);
if ($match_utils:object_match_failed(iobj, iobjstr))
return;
endif
if (((caller != dobj) && (info = $object_utils:has_callable_verb(iobj, verb))) && (info[1] != $code_utils:verb_loc()))
return iobj:(verb)(@args);
endif
$you:say_action(((("%N %<" + ((verb == "toss") ? "tosses/toss" | "throws")) + "> %[diname] ") + prepstr) + " %[idname].");
this.thrown = {task_id(), iobj};
this:moveto(player:room());
suspend(random(5) + 3);
this.thrown = 0;
if (dobj:strike_object(iobj) == E_VERBNF)
dobj:room_announce_all(dobj:grammar_sub("%(dnamec) %<bounces> off %[idname] and %<hits> the floor."));
endif
iobj:struck_by_object(dobj);
.
#5:12
":maybe_yank_chain()";
"If this thing is chained and is not located in its home, send it back.";
chained_to = this.chained;
if (((!valid(chained_to)) || this:is_controllable_by(player)) || $object_utils:contains(chained_to, this))
return 0;
endif
`move(this, chained_to) ! ANY';
if (this.location == chained_to)
chained_to:announce_yanked_messages(player, this);
endif
return 1;
.
#5:13
":is_chained([where])";
"Return true if this object is chained to something [or to the given location].";
{?where = 1} = args;
if (where)
return valid(this.chained) && {this.chained};
else
return this.chained == where;
endif
.
#5:14
passed = pass(@args);
if (id = this:is_airborne())
player:tell(((this:inamec() + " is sailing through the air towards ") + id[1]:iname()) + ".");
endif
return passed;
.
#6:0
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.home = (this in {$no_one, $hacker}) ? $nothing | $player_start;
if (a = $list_utils:assoc(this, {{$prog, {$prog_help, $builtin_function_help, $verb_help, $core_help}}, {$wiz, $wiz_help}}))
this.help = a[2];
else
this.help = 0;
endif
this.features = this.puppets = this.saved_pages = {};
this.linebuffer = this.owned_objects = this.all_connect_places = {};
if (this != $player)
for p in ({"last_connect_place", "email_address", "previous_connection", "features"})
`clear_property(this, p) ! ANY';
endfor
endif
this.heart = $pc_heart;
this.messages = this.messages_going = {};
.
#6:1
if (!(caller in {this, #0}))
return E_PERM;
endif
if (!$recycler:valid(this.location))
move(this, $player_start);
endif
this:registration_nag();
this:news_catch_up();
this:mail_catch_up();
this:check_mail_lists();
this:notify_lines($wiz_utils.disclaimer);
this:check_saved_pages();
$wiz_utils:announcements_for(this);
this:cpr();
.
#6:2
if (!(caller in {this, #0}))
return E_PERM;
endif
this:expunge_rmm();
this.last_disconnect_time = time();
for feature in (this:features())
`feature:player_disconnected(player, @args) ! ANY';
endfor
return pass(@args);
.
#6:3
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
this.help = 0;
this.home = $player_start;
fork (0)
this.heart:add(this);
this.heart:pump();
endfork
return pass(@args);
.
#6:4
set_task_perms(caller_perms());
if ((!is_player(this)) || caller_perms().wizard)
pass(@args);
return;
endif
"...start off with a wizard shout...";
for p in (connected_players())
if (p.wizard)
p:tell($string_utils:pronoun_sub("%N (%#) is currently trying to recycle %t (%[#t])"));
endif
endfor
"...Okay here's the fun part.";
"...Doing kill_task(task_id()) doesn't work because the server can";
"...figure out that it's okay to go ahead and recycle once the task finishes.";
"...Evidently, suspend() confuses the server sufficiently that it forgets to";
"...do the recycle once the task finishes or dies.  Now of course, we don't";
"...want suspended tasks hanging around indefinitely, so we fork something";
"...off to kill it.  This seems to work...";
t = task_id();
fork (1)
kill_task(t);
endfork
"...let him think he succeeded (should we do this ?)...no.";
"...boot_player(this)";
"...emergency life support...";
suspend(1073741823);
"...code not reached --- the patient lives...";
"...keep this around for posterity...";
if (is_player(this))
for a in (this.aliases)
$player_db:delete(a);
endfor
$player_db:delete(this.name);
endif
pass(@args);
.
#6:5
"Extra parsing of player commands.  Called by $command_utils:do_huh.";
"This version of my_huh just handles features.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
"Standard permissions check.";
return E_PERM;
endif
"verb - obvious                 pass - would be args";
"plist - list of prepspecs that this command matches";
"dlist and ilist - likewise for dobjspecs, iobjspecs";
verb = args[1];
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]))
vargs = verb_args(loc, verb);
if ((vargs[2] in plist) && ((vargs[1] in dlist) && (vargs[3] in ilist)))
set_task_perms(this);
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[1];
args = args[2];
if (pass(verb, args))
return 1;
elseif ((m = match(verb, "^@%(.+%)-%(options?%)$")) && valid(pkg = this:match_option_package(substitute("%1", m))))
this:("@" + substitute("%2", m))(pkg:short_name(), @args);
return 1;
elseif ((verb[1] == "@") && (prepstr == "is"))
"... set or show _msg property ...";
$last_huh:(verb)(@args);
elseif (this.programmer && (verb in {"give", "hand", "get", "take", "drop", "throw"}))
$last_huh:(verb)(@args);
elseif (this:puppet_huh(verb, args))
"...send a command to a puppet...";
elseif (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
return 1;
.
#6:7
if ($perm_utils:controls(caller_perms(), this) || (caller == this))
set_task_perms(caller_perms());
for line in ((typeof(lines = args[1]) != LIST) ? {lines} | lines)
this:notify(line);
endfor
else
return E_PERM;
endif
.
#6:8
":linesplit(line,len[,indent]) => list of substrings of line";
"used by :notify to split up long lines if .linelen>0";
{line, len, ?indent = 1} = args;
cline = {};
indentspace = indent ? " " | "";
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]};
if (nospace)
line = indentspace + line[cutoff..$];
else
line = indentspace + line[cutoff + 1..$];
endif
endwhile
return {@cline, line};
.
#6:9
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:10
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:11
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 = tonum(dobjstr);
if (newlen < 0)
player:notify("Line length can't be a negative number.");
return;
elseif (newlen < 10)
player:notify("You don't want your linelength that small.  Setting it to 10.");
newlen = 10;
endif
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:12
"@pagelength number  -- sets page buffering to that many lines (or 0 to turn off page buffering)";
if (player != this)
"... somebody is being sneaky ...";
"... Can't do set_task_perms(player) since we need to be `this'...";
"... to notify and `this.owner' to change +c properties...";
return;
elseif (!dobjstr)
notify(player, tostr("Usage:  ", verb, " <number>"));
notify(player, tostr("Current page length is ", player.pagelen, "."));
return;
elseif (0 > (newlen = tonum(dobjstr)))
notify(player, "Page length can't be a negative number.");
return;
elseif (newlen == 0)
player.pagelen = 0;
notify(player, "Page buffering off.");
elseif (newlen < 5)
player.pagelen = 5;
notify(player, "Too small.  Setting it to 5.");
else
notify(player, tostr("Page length is now ", player.pagelen = newlen, "."));
endif
if (this.linebuffer)
notify(this, 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:13
if (this:gag_p())
return;
endif
s = this:process_notified_text(tostr(@args));
pass(s);
.
#6:14
if (!(gag = this.gaglist))
return 0;
endif
RPG = $rpg;
if (RPG:trusted(player))
return 0;
endif
for c in (callers())
"{c_this, c_verb, c_perms, c_def, c_player, @rest} = c;";
if ((c[1] == $rpg) && (c[2] == "notify"))
break;
elseif (((c[1] == #-1) && (c[3] == #-1)) && (c[2] != ""))
"";
elseif ((c[1] in gag) || (c[4] in gag))
return 1;
endif
endfor
return 0;
.
#6:15
":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:16
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 = $match_utils:match_player_or_object(@args);
changed = 0;
for p in (victims)
if (p in player.gaglist)
player:notify(tostr("You are already gagging ", p.name, "."));
elseif (p == player)
player:notify("Gagging yourself is a bad idea.");
else
changed = 1;
player:set_gaglist(@setadd(this.gaglist, p));
endif
endfor
if (changed)
this:("@listgag")("but don't fork");
endif
.
#6:17
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
for p in (this.gaglist)
if (!$recycler:valid(p))
this.gaglist = setremove(this.gaglist, p);
endif
endfor
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)
for p in (players())
if ((typeof(p.gaglist) == LIST) && (this in p.gaglist))
gl = {@gl, p};
endif
$command_utils:suspend_if_needed(0);
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:18
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 = $match_utils:match(dobjstr, this.gaglist);
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")("but don't fork");
endif
.
#6:19
record = args[1];
trust = args[2];
mistrust = args[3];
s = {this, "???", this};
for w in (record)
if (((s[3].wizard || (s[3] in trust)) && (!(s[3] in mistrust))) || (s[1] == this))
s = w;
else
return s;
endif
endfor
return s;
.
#6:20
buggers = 1;
found_listener = 0;
here = this.location;
if ($martial_law > 0)
this:notify(tostr("Martial Law is in effect.  ", $string_utils:nn($owner), " may be logging all commands."));
elseif (($martial_law < 0) && $wiz_utils:is_builder(player))
this:notify(tostr("Martial Law is in effect.  ", $string_utils:nn($owner), " may be logging all commands."));
endif
online = connected_players();
for thing in (setremove(here.contents, this))
if (thing in online)
this:notify(tostr($string_utils:nn(thing), " is listening."));
found_listener = 1;
elseif ($object_utils:has_callable_verb(thing, "sweep_msg") && (typeof(msg = thing:sweep_msg()) == STR))
this:notify(tostr($string_utils:nn(thing), " ", msg, "."));
found_listener = 1;
elseif ($object_utils:isa(thing, $puppet))
this:notify(tostr($string_utils:nn(thing), " is an NPC.  While nobody appears to be monitoring now, someone may at some point be listening through any number of means.  For paranoia's sake, assume a live player is in the room."));
found_listener = 1;
else
for v in ({"tell", "notify"})
vwhere = $object_utils:has_verb(thing, v);
if (vwhere && (((owner = verb_info(vwhere[1], v)[1]) != this) && (!owner.wizard)))
this:notify(tostr($string_utils:nn(thing), " has been taught to listen by ", $string_utils:nn(owner), "."));
found_listener = 1;
break;
endif
endfor
endif
endfor
buggers = {};
for v in ({"announce", "announce_all", "announce_all_but", "say", "emote", "huh", "here_huh", "huh2", "whisper", "broadcast_event_", "audience_for_event", "audience_for_sound", "audience_for_speech"})
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($string_utils:nn(here), " ", msg, "."));
else
this:notify(tostr($string_utils:nn(here), " may have been bugged by ", $string_utils:name_list(buggers), "."));
endif
elseif (!found_listener)
this:notify("Communications look secure.");
endif
.
#6:21
":receive_page(text, @page)";
"Return codes:";
"  1:  page was received";
"  2:  player is not connected";
"  0:  page refused";
{text, @args} = args;
if (this:has_gagged(player, "page") || this:get_option($privacy_options, "refuse_pages"))
return 0;
endif
if ((0 && (length(args) > 1)) && this:get_option($display_options, "brief_pages"))
"This is a terrible hack which relies on all page verbs setting iobjstr to the page text.";
args = {tostr(player:grammar_sub("%N %<pages>"), ", \"", iobjstr, "\"")};
endif
if ($login:connected(this))
this:notify_lines(args);
return 1;
elseif (!args)
return 2;
elseif ($object_utils:connected(this))
first = args[1] + " [You are not signed in!]";
this:notify_lines({first, @listdelete(args, 1)});
return 2;
else
MAX_SAVED = 20;
IDLE_THRESHOLD = 300;
len = length(pages = {{player, time(), args}, @this.saved_pages});
this.saved_pages = (len > MAX_SAVED) ? pages[1..MAX_SAVED] | pages;
return 2;
"bleah--ignore the below";
return ((this.last_disconnect_time + IDLE_THRESHOLD) > time()) + 1;
endif
.
#6:22
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:23
set_task_perms(player);
if (iobjstr == "here")
iobj = player.location;
elseif (iobjstr == "me")
iobj = player;
elseif ($command_utils:object_match_failed(iobj, iobjstr))
return;
endif
if (!$perm_utils:controls(player, iobj))
player:notify(tostr("You are not the owner of ", iobj.name, "."));
return;
endif
if (dobjstr == "me")
dobj = player;
elseif (($failed_match == (dobj = $string_utils:literal_object(dobjstr))) && $command_utils:object_match_failed(dobj = iobj:match(dobjstr), dobjstr))
return;
endif
if (dobj.location != iobj)
player:notify(tostr(dobj.name, "(", dobj, ") is not in ", iobj.name, "(", iobj, ")."));
return;
endif
if (dobj.wizard)
player:notify(tostr("Sorry, you can't ", verb, " a wizard."));
dobj:tell(player.name, " tried to ", verb, " you.");
return;
endif
iobj:eject(dobj);
player:notify($object_utils:has_callable_verb(iobj, "ejection_msg") ? iobj:ejection_msg() | $room:ejection_msg());
dobj:tell($object_utils:has_callable_verb(iobj, "victim_ejection_msg") ? iobj:victim_ejection_msg() | $room:victim_ejection_msg());
iobj:announce_all_but({player, dobj}, $object_utils:has_callable_verb(iobj, "oejection_msg") ? iobj:oejection_msg() | $room:oejection_msg());
.
#6:24
"@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:25
set_task_perms(callers() ? caller_perms() | player);
"...this code explicitly relies on being !d in several places...";
if ((index(verb, "?") != 1) || (length(verb) <= 1))
if (prepstr in {"with", "on", "about", "for"})
what = iobjstr;
else
what = $string_utils:trimr(argstr);
endif
elseif (argstr)
what = tostr(verb[2..length(verb)], " ", $string_utils:trimr(argstr));
else
what = verb[2..length(verb)];
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) || (result[1] == $ambiguous_match))
"... note: all of the last-resort stuff...";
"... is now located on $help:find_topics/get_topic...";
$wiz_utils:missed_help(what, result);
if ((where = $match_utils:find_verb(what)) && (help = $code_utils:verb_documentation(where[1], what)))
player:notify(tostr("Showing verb help on `", what, "':"));
player:notify("----");
player:notify_lines(help);
player:notify("----");
player:notify(tostr("End of verb help on `", what, "'."));
return 1;
elseif ((!result) || (result[1] != $ambiguous_match))
player:notify(tostr("Sorry, but no help is available on `", what, "'."));
return $failed_match;
endif
endif
if (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:"));
player:notify("----");
for x in ($string_utils:columnize($help:sort_topics(result[2]), 3, 60))
player:notify(tostr("   ", x));
endfor
player:notify("----");
player:notify(tostr("End of matching topics."));
return $ambiguous_match;
endif
help = result[1];
topic = result[2];
if (topic)
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 ", $string_utils:nn(help), " thinks it knows about `", what, "' but something's messed up."));
player:notify(tostr("Tell ", help.owner.wizard ? "" | tostr($string_utils:nn(help.owner), " or "), "a wizard."));
endif
if (topic)
player:notify("----");
player:notify(tostr("End of help on `", topic, "'."));
endif
.
#6:26
"news [?]       -- display headers of current news";
"news all       -- display all articles";
"news new       -- display articles you haven't seen yet";
"news <article> -- display article";
set_task_perms(player);
news = $news;
cur = this:get_current_message(news) || {0, 0};
if (hdrs_only = (!args) || (args[1] == "?"))
"Do the mail contents list";
args[1..1] = {};
endif
if (args && (args[1] != "all"))
if (typeof(seq = news:_parse(args, @cur)) == STR)
player:notify(seq);
return;
elseif (seq = $seq_utils:intersection(seq, news.current_news))
else
player:notify((args == {"new"}) ? "No new news." | "None of those are current articles.");
return;
endif
elseif (seq = news.current_news)
else
player:notify("No news.");
return;
endif
if (hdrs_only)
linelen = player:linelen();
player:notify($string_utils:centre("Ghostwheel News", linelen, "-"));
player:notify("");
news:display_seq_headers(seq, @cur);
player:notify("");
for line in ($code_utils:verb_documentation())
player:notify("          " + line);
endfor
player:notify("");
player:notify($string_utils:centre("News last updated " + player:ctime(news.last_news_time), linelen, "-"));
else
player:set_current_message(news, @news:news_display_seq_full(seq));
endif
.
#6:27
if (typeof(mf = this.(verb)) == STR)
return $string_utils:pronoun_sub(mf, @args);
else
return mf;
endif
.
#6:28
":receive_message(msg,from)";
if ((!$perm_utils:controls(caller_perms(), this)) && (caller != this))
return E_PERM;
endif
{msg, ?from = $someone} = args;
if (this:get_option($mail_options, "netmail"))
{date_int, from_str, to_str, subj_str, @body} = msg;
if ((!subj_str) || (subj_str == " "))
subj_str = "<no subject>";
endif
subj_str = tostr("MOO-Mail from ", from.name, ": ", subj_str);
message = {subj_str};
message = {@message, "Original-Date: " + this:ctime(date_int)};
message = {@message, "Original-From: " + from_str};
message = {@message, "Original-To: " + to_str};
for x in (body)
message = {@message, @$generic_editor:fill_string(x, this:linelen())};
endfor
if (this:send_self_netmail(message, from) == 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, msg}};
return new;
.
#6:29
":display_message(preamble,msg) --- prints msg to player.";
vb = ((this._mail_task == task_id()) || (caller == $mail_editor)) ? "notify_lines" | "tell_lines";
preamble = args[1];
player:(vb)({@(typeof(preamble) == LIST) ? preamble | {preamble}, @args[2], "--------------------------"});
.
#6:30
"parse_message_seq(strings,cur)         => msg_seq";
"messages_in_seq(msg_seq);              => text of messages in msg_seq";
"display_seq_headers(msg_seq[,current]) :displays summary lines of those msgs";
"rmm_message_seq(msg_seq)               => string giving msg numbers removed";
"undo_rmm()    => msg_seq of restored messages";
"expunge_rmm() => number of messages expunged";
"list_rmm()    => number of messages awaiting expunge";
"renumber(cur) => {number of messages in folder, new_cur}";
"";
"See the corresponding routines on $mail_agent.";
if ((caller == $mail_agent) || $perm_utils:controls(caller_perms(), this))
set_task_perms(this.owner);
return $mail_agent:(verb)(@args);
else
return E_PERM;
endif
.
#6:31
return $mail_agent:msg_summary_line(@args);
.
#6:32
return $mail_agent:to_text(@args);
.
#6:33
":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 = args[1];
recipients = args[2];
msgnums = {@args, {}}[3];
"... 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, " (", from, ")", namelist ? " which was also sent to " + namelist | "", "."));
if (!this:get_option($mail_options, "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:notify(tostr(namelist, (length(recipients) == 1) ? " has" | " have", " just been sent new mail by ", from.name, " (", from, ")."));
endif
endif
.
#6:34
"runs the old->new format conversion on every message in this.messages.";
" => 1 if successful";
" => 0 if anything toward happened during a suspension";
"      (e.g., new message received, someone deleted stuff) ";
"      in which case this.messages is left as if this routine were never run.";
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
endif
msgs = {};
i = 1;
for m in (oldmsgs = this.messages)
msgs = {@msgs, {m[1], $mail_agent:__convert_new(@m[2])}};
if ($command_utils:running_out_of_time())
player:notify(tostr("...", i, " ", this));
suspend(0);
if (oldmsgs != this.messages)
return 0;
endif
endif
i = i + 1;
endfor
this.messages = msgs;
return 1;
.
#6:35
":current_message([recipient])";
" => current message number for the given recipient (defaults to this).";
" => 0 if we have no record of that recipient.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
$error:raise(E_PERM);
elseif ((!args) || (args[1] == this))
return this.current_message[1];
elseif (a = $list_utils:assoc(args[1], this.current_message))
return a[2];
else
return 0;
endif
.
#6:36
":get_current_message([recipient])";
" => {msg_num, last_read_date} for the given recipient.";
" => 0 if we have no record of that recipient.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
$error:raise(E_PERM);
elseif ((!args) || (args[1] == this))
return this.current_message[1..2];
elseif (a = $list_utils:assoc(args[1], this.current_message))
return a[2..3];
else
return 0;
endif
.
#6:37
":set_current_message(recipient[,number[,date]])";
"Returns the new {number,last-read-date} pair for recipient.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return $error:raise(E_PERM);
endif
recip = args[1];
number = {@args, E_NONE}[2];
date = {@args, 0, 0}[3];
cm = this.current_message;
if (recip == this)
this.current_message[2] = max(date, cm[2]);
if (number != E_NONE)
this.current_message[1] = number;
endif
return this.current_message[1..2];
elseif (i = $list_utils:iassoc(recip, cm))
return (this.current_message[i] = {recip, (number == E_NONE) ? cm[i][2] | number, max(date, cm[i][3])})[2..3];
else
entry = {recip, (number != E_NONE) && number, date};
this.current_message = {@cm, entry};
return entry[2..3];
endif
.
#6:38
":make_current_message(recipient)";
"starts a new current_message record for recipient";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return $error:raise(E_PERM);
elseif (((recip = args[1]) != this) && (!$list_utils:assoc(recip, cm = this.current_message)))
this.current_message = listappend(cm, {recip, 0, 0});
endif
.
#6:39
":kill_current_message(recipient)";
"entirely forgets current message for this recipient...";
"Returns true iff successful.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return $error:raise(E_PERM);
else
return ((recip = args[1]) != this) && ((i = $list_utils:iassoc(recip, cm = this.current_message)) && (this.current_message = listdelete(cm, i)));
endif
.
#6:40
":current_folder() => default folder to use, always an object, usually `this'";
set_task_perms(caller_perms());
return ((!this:get_option($mail_options, "sticky")) || this.current_folder) && this;
.
#6:41
set_task_perms(caller_perms());
return this.current_folder = args[1];
.
#6:42
":parse_folder_spec(verb,args,expected_preposition[,allow_trailing_args_p])";
" => {folder, msg_seq_args, trailing_args}";
set_task_perms(caller_perms());
folder = this:current_folder();
if (!prepstr)
return {folder, args[2], {}};
endif
verb = args[1];
prep = args[3];
extra = {@args, 0}[4];
args = args[2];
p = prepstr in args;
if (prepstr != prep)
"...unexpected preposition...";
if (extra && (!index(prepstr, " ")))
return {folder, args[1..p - 1], args[p..length(args)]};
else
player:tell("Usage:  ", verb, " [<message numbers>] [", prep, " <folder/list-name>]");
endif
elseif (!((p < length(args)) && (fname = args[p + 1])))
"...preposition but no iobj...";
player:tell(verb, " ", $string_utils:from_list(args, " "), " WHAT?");
elseif ($mail_agent:match_failed(folder = $mail_agent:match_recipient(fname), fname))
"...bogus mail folder...";
else
return {folder, args[1..p - 1], args[p + 2..length(args)]};
endif
return 0;
.
#6:43
":parse_mailread_cmd(verb,args,default,prep[,trailer])";
"  handles anything of the form  `VERB message_seq [PREP folder ...]'";
"    default is the default msg-seq to use if none given";
"    prep is the expected prepstr (assumes prepstr is set), usually `on'";
"    trailer, if present and true, indicates trailing args are permitted.";
"  returns {recipient object, message_seq, current_msg,\"...\"} or 0";
set_task_perms(caller_perms());
if (!(pfs = this:parse_folder_spec(@listdelete(args, 3))))
return 0;
endif
verb = args[1];
default = args[3];
extra = {@args, 0}[5];
folder = pfs[1];
cur = this:get_current_message(folder) || {0};
if (typeof(pms = folder:parse_message_seq(pfs[2], @cur)) == LIST)
rest = {@listdelete(pms, 1), @pfs[3]};
if ((!extra) && rest)
"...everything should have been gobbled by :parse_message_seq...";
player:tell("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;
.
#6:44
"@mail <msg-sequence>                --- as in help @mail";
"@mail <msg-sequence> on <recipient> --- shows mail on mailing list or player.";
set_task_perms(valid(caller_perms()) ? caller_perms() | player);
if (p = this:parse_mailread_cmd("@mail", args, this:get_option($mail_options, "@mail") || "last:15", "on"))
this:set_current_folder(folder = p[1]);
msg_seq = p[2];
seq_size = $seq_utils:size(msg_seq);
if ((lim = player:get_option($mail_options, "manymsgs")) && ((lim <= seq_size) && (!$command_utils:yes_or_no(tostr("You are about to see ", seq_size, " message headers.  Continue?")))))
player:notify(tostr("Aborted.  @mailoption manymsgs=", lim));
return;
endif
if (1 != seq_size)
player:notify(tostr(seq_size, " messages", (folder == this) ? "" | (" on " + $mail_agent:name(folder)), ":"));
endif
folder:display_seq_headers(msg_seq, @p[3]);
endif
.
#6:45
"@read <msg>...                  -- as in help @read";
"@read <msg>... on *<recipient>  -- reads messages on recipient.";
"@peek ...                       -- like @read, but don't set current message";
set_task_perms(valid(caller_perms()) ? caller_perms() | player);
if (p = this:parse_mailread_cmd("@read", args, "", "on"))
this:set_current_folder(folder = p[1]);
msg_seq = p[2];
if ((lim = player:get_option($mail_options, "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
.
#6:46
set_task_perms(player);
if (dobjstr)
player:notify(tostr("Usage:  ", verb, " [on <recipient>]"));
else
this:("@read")(verb[2..5], @args);
endif
.
#6:47
"@rmm <message-sequence> [from <recipient>].   Use @unrmm if you screw up.";
" Beware, though.  @unrmm can only undo the most recent @rmm.";
set_task_perms(player);
if (!(p = this:parse_mailread_cmd("@rmm", args, "cur", "from")))
"...parse failed, we've already complained...";
elseif ((!prepstr) && ((p[1] != this) && (!$command_utils:yes_or_no(("@rmmail from " + $mail_agent:name(p[1])) + ".  Continue?"))))
"...wasn't the folder player was expecting...";
player:notify("@rmmail aborted.");
else
this:set_current_folder(folder = p[1]);
e = folder:rm_message_seq(p[2]);
if (typeof(e) == ERR)
player:notify(tostr(e));
else
count = ((n = $seq_utils:size(p[2])) == 1) ? "." | tostr(" (", n, " messages).");
fname = (folder == this) ? "" | (" from " + $mail_agent:name(folder));
player:notify(tostr("Deleted ", e, fname, count));
endif
endif
.
#6:48
set_task_perms(player);
if (!dobjstr)
folder = this:current_folder();
elseif ($mail_agent:match_failed(folder = $mail_agent:match_recipient(dobjstr), dobjstr))
return;
endif
cur = this:current_message(folder);
fname = $mail_agent:name(folder);
if (typeof(h = folder:renumber(cur)) == ERR)
player:notify(tostr(h));
else
if (!h[1])
player:notify(tostr("No messages on ", fname, "."));
else
player:notify(tostr("Messages on ", fname, " renumbered 1-", h[1], "."));
this:set_current_folder(folder);
if (h[2] && this:set_current_message(folder, h[2]))
player:notify(tostr("Current message is now ", h[2], "."));
endif
endif
endif
.
#6:49
"@unrmm [on <recipient>]  -- undoes the previous @rmm on that recipient.";
set_task_perms(player);
if (!(p = this:parse_folder_spec("@unrmm", args, "on")))
return;
endif
dobjstr = $string_utils:from_list(p[2], " ");
if (!dobjstr)
do = "undo_rmm";
elseif (index("expunge", dobjstr) == 1)
do = "expunge_rmm";
elseif (index("list", dobjstr) == 1)
do = "list_rmm";
else
player:notify(tostr("Usage:  ", verb, " [expunge|list] [on <recipient>]"));
return;
endif
this:set_current_folder(folder = p[1]);
if (msg_seq = folder:(do)())
if (do == "undo_rmm")
player:notify(tostr($seq_utils:size(msg_seq), " messages restored to ", $mail_agent:name(folder), "."));
folder:display_seq_headers(msg_seq, 0);
else
player:notify(tostr(msg_seq, " zombie message", (msg_seq == 1) ? " " | "s ", (do == "expunge_rmm") ? "expunged from " | "on ", $mail_agent:name(folder), "."));
endif
elseif (typeof(msg_seq) == ERR)
player:notify(tostr(msg_seq));
else
player:notify(tostr("No messages to ", (do == "expunge_rmm") ? "expunge from " | "restore to ", $mail_agent:name(folder)));
endif
.
#6:50
"!!! CALLED EXCLUSIVELY FROM THE PRIVILEGED MAIL AND EDITING FEATURE";
if ((player != caller_perms()) || (player != this))
return E_PERM;
endif
if (args && (args[1] == "to"))
args = listdelete(args, 1);
endif
subject = {};
for a in (args)
if (((i = index(a, "=")) > 3) && (index("subject", a[1..i - 1]) == 1))
args = setremove(args, a);
a[1..i] = "";
subject = {a};
endif
endfor
$mail_editor:invoke(args, verb, @subject);
.
#6:51
"@answer <msg> [on *<recipient>] [<flags>...]";
"!!! CALLED EXCLUSIVELY FROM THE PRIVILEGED MAIL AND EDITING FEATURE";
if ((player != caller_perms()) || (player != this))
return E_PERM;
endif
set_task_perms(player);
if (p = this:parse_mailread_cmd(verb, args, "", "on", 1))
if ($seq_utils:size(p[2]) != 1)
player:notify("You can only answer *one* message at a time.");
elseif (LIST != typeof(flags_replytos = $mail_editor:check_answer_flags(@p[4])))
player:notify_lines({tostr("Usage:  ", verb, " [message-# [on <recipient>]] [flags...]"), "where flags include any of:", "  all reply to everyone", "  sender     reply to sender only", "  include include the original message in your reply", "  noinclude  don't include the original in your reply"});
else
this:set_current_folder(p[1]);
$mail_editor:invoke(2, verb, p[1]:messages_in_seq(p[2])[1][2], @flags_replytos);
endif
endif
.
#6:52
"@forward <msg> [on *<recipient>] to <recipient> [<recipient>...]";
"!!! CALLED EXCLUSIVELY FROM THE PRIVILEGED MAIL AND EDITING FEATURE";
if ((player != caller_perms()) || (player != this))
return E_PERM;
endif
set_task_perms(player);
if (!(p = this:parse_mailread_cmd(verb, args, "", "on", 1)))
"...lose...";
return;
elseif ($seq_utils:size(sequence = p[2]) != 1)
player:notify("You can only forward *one* message at a time.");
return;
elseif ((length(p[4]) < 2) || (p[4][1] != "to"))
player:notify(tostr("Usage:  ", verb, " [<message>] [on <folder>] to <recip>..."));
return;
endif
recips = {};
for rs in (listdelete(p[4], 1))
if ($mail_agent:match_failed(r = $mail_agent:match_recipient(rs), rs))
return;
endif
recips = {@recips, r};
endfor
this:set_current_folder(folder = p[1]);
m = folder:messages_in_seq(sequence)[1];
msgnum = m[1];
msgtxt = m[2];
from = msgtxt[2];
if (msgtxt[4] != " ")
subject = tostr("[", from, ":  ", msgtxt[4], "]");
elseif ((h = "" in msgtxt) && (h < length(msgtxt)))
subject = tostr("[", from, ":  `", msgtxt[h + 1][1..min(20, length(msgtxt[h + 1]))], "']");
else
subject = tostr("[", from, "]");
endif
result = $mail_agent:send_message(player, recips, subject, $mail_agent:to_text(@msgtxt));
if (!result)
player:notify(tostr(result));
elseif (result[1])
player:notify(tostr("Message ", msgnum, @(folder == this) ? {} | {" on ", $mail_agent:name(folder)}, " @forwarded to ", $mail_agent:name_list(@listdelete(result, 1)), "."));
else
player:notify("Message not sent.");
endif
.
#6:53
"!!! CALLED EXCLUSIVELY FROM THE PRIVILEGED MAIL AND EDITING FEATURE";
if ((player != caller_perms()) || (player != this))
return E_PERM;
endif
$mail_editor:invoke($gripe_recipients, "@gripe", "@gripe: " + argstr);
.
#6:54
"!!! CALLED EXCLUSIVELY FROM THE PRIVILEGED MAIL AND EDITING FEATURE";
if ((player != caller_perms()) || (player != this))
return E_PERM;
endif
subject = tostr($string_utils:capitalize(verb[2..length(verb)]), ":  ", (loc = this.location).name, "(", loc, ")");
if (this != player)
return E_PERM;
elseif (argstr)
result = $mail_agent:send_message(this, {loc.owner}, subject, argstr);
if (result && result[1])
player:notify(tostr("Your ", verb, " sent to ", $mail_agent:name_list(@listdelete(result, 1)), ".  Input is appreciated, as always."));
else
player:notify(tostr("Huh?  This room's owner (", loc.owner, ") is invalid? Tell a wizard..."));
endif
return;
elseif (!($object_utils:isa(loc, $room) && loc.free_entry))
player:notify_lines({tostr("You need to make it a one-liner, i.e., `", verb, " something or other'."), "This room may not let you back in if you go to the Mail Room."});
elseif ($object_utils:isa(loc, $generic_editor))
player:notify_lines({tostr("You need to make it a one-liner, i.e., `", verb, " something or other'."), "Sending you to the Mail Room from an editor is usually a bad idea."});
else
$mail_editor:invoke({tostr(loc.owner)}, verb, subject);
endif
if (verb == "@bug")
player:notify("For a @bug report, be sure to mention exactly what it was you typed to trigger the error...");
endif
.
#6:55
"@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
.
#6:56
"@subscribe *<folder/mailing_list>";
"  causes you to be notified when new mail arrives on this list";
"@subscribe";
"   just lists available mailing lists.";
set_task_perms(player);
if (!dobjstr)
ml = $list_utils:slice(player.current_message[3..length(player.current_message)]);
for c in ({@$mail_agent.contents, @this.mail_lists})
$command_utils:suspend_if_needed(0);
if ((c:is_usable_by(player) || c:is_readable_by(player)) && ((verb != "@unsubscribed") || (!(c in ml))))
c:look_self();
endif
endfor
player:notify(tostr("-------- end of ", verb, " -------"));
return;
elseif ($mail_agent:match_failed(folder = $mail_agent:match_recipient(dobjstr), dobjstr))
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
this:make_current_message(folder);
if ($object_utils:isa(folder, $mail_recipient))
folder:add_notify(this);
endif
len = folder:length_all_msgs();
player:notify(tostr($mail_agent:name(folder), " has ", len, " message", (len == 1) ? "" | "s"));
this:set_current_folder(folder);
.
#6:57
set_task_perms((caller == this) ? this.owner | caller_perms());
new_cm = head = {};
for n in (this.current_message)
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
new_cm = listappend(new_cm, n, $list_utils:iassoc_sorted(n[3], new_cm, 3));
endif
endfor
this.current_message = {@head, @$list_utils:reverse(new_cm)};
this:set_current_folder(this);
.
#6:58
set_task_perms((caller == this) ? this.owner | caller_perms());
which = {};
cm = this.current_message;
cm[1..2] = (verb != "@subscribed") ? {{this, @cm[1..2]}} | {};
all = verb == "@subscribed";
for n in (cm)
rcpt = n[1];
if ($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((verb == "@subscribed") ? "You are subscribed to the following lists:" | "There is new activity on the following lists:");
for w in (which)
name = (w[1] == this) ? " me" | $mail_agent:name(w[1]);
player:notify(tostr($string_utils:left("    " + name, 40), " ", w[2], " new message", (w[2] == 1) ? "" | "s"));
endfor
if (!this:get_option($mail_options, "expert"))
player:notify("Type `help mail` for info on reading mail.  Type `@options mail +expert` if you're sick of seeing this message.");
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;
.
#6:59
":list_option(name) => returns the value of the specified @list option";
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return $list_options:get(this.list_options, args[1]);
else
return E_PERM;
endif
.
#6:60
"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) in {this, 1}))
return E_INVARG;
elseif (!this:is_valid_name(name))
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:61
"set_aliases(alias_list)";
"For changing player aliases, we check to make sure that none of the aliases match existing player names/aliases.  Aliases containing spaces are not entered in the $player_db and so are not subject to this restriction ($string_utils:match_player will not match on them, however, so they only match if used in the immediate room, e.g., with match_object() or somesuch).";
"Also we make sure that the .name is included in the .alias list.  In any situation where .name and .aliases are both being changed, do the name change first.";
"  => E_PERM   if you don't own this";
"  => E_NACC   if the player database is not taking new aliases right now.";
"  => E_TYPE   if alias_list is not a list";
"  => E_INVARG if any element of alias_list is not a string";
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) > 30) && (length(aliases) > length(this.aliases)))
return E_INVARG;
else
for a in (aliases)
if (typeof(a) != STR)
return E_INVARG;
endif
if ((!index(a, " ")) && (!($player_db:available(a) in {this, 1})))
aliases = setremove(aliases, a);
endif
endfor
aliases = setadd(aliases, this.name);
old = this.aliases;
this.aliases = aliases;
for a in (old)
if (!(a in aliases))
$player_db:delete2(a, this);
endif
endfor
for a in (aliases)
if (!index(a, " "))
$player_db:insert(a, this);
endif
endfor
return 1;
endif
.
#6:62
if ((player != caller) || (player != this))
return;
endif
if (!iobjstr)
player:notify(tostr("@rename ", dobjstr, " to what?"));
return E_ARGS;
endif
set_task_perms(player);
spec = $code_utils:parse_verbref(dobjstr);
if (spec)
object = this:my_match_object(spec[1]);
if (!$command_utils:object_match_failed(object, spec[1]))
vname = spec[2];
info = verb_info(object, vname);
if (info == E_VERBNF)
player:notify("That object does not define that verb.");
elseif (typeof(info) == ERR)
player:notify(tostr(info));
else
result = set_verb_info(object, vname, listset(info, iobjstr, 3));
if (typeof(result) == ERR)
player:notify(tostr(result));
else
player:notify("Verb name changed.");
endif
endif
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
vr_msg = $vr_core:rename_warning(player);
player:notify((name_message + alias_message) + (vr_msg && ("  " + vr_msg)));
elseif (e == E_INVARG)
player:notify("That particular name change not allowed (see help @rename).");
elseif (e == E_NACC)
player:notify("Oops.  You can't update that name right now; try again in a few minutes.");
elseif (e == E_ARGS)
player:notify(tostr("Sorry, name too long.  The maximum number of characters in a name is ", $login.max_player_name, "."));
else
player:notify(tostr(e));
endif
endif
endif
.
#6:63
"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);
spec = $code_utils:parse_verbref(iobjstr);
if (spec)
object = player:my_match_object(spec[1]);
if (!$command_utils:object_match_failed(object, spec[1]))
vname = spec[2];
info = verb_info(object, vname);
if (info == E_VERBNF)
player:notify("That object does not define that verb.");
elseif (typeof(info) == ERR)
player:notify(tostr(info));
else
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);
result = set_verb_info(object, vname, info);
if (typeof(result) == ERR)
player:notify(tostr(result));
else
player:notify(tostr("Alias", (length(new_aliases) > 1) ? "es" | "", " ", $string_utils:english_list(new_aliases), " added to verb ", object.name, "(", object, "):", vname));
player:notify(tostr("Verbname is now ", object.name, "(", object, "):\"", aliases, "\""));
endif
endif
endif
endif
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 == {@old_aliases, @new_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 ", object.name, "(", object, ") are now ", $string_utils:from_value(aliases, 1)));
elseif (e || (e == E_INVARG))
player:notify("That particular name change not allowed (see help @rename or help @addalias).");
elseif (e == E_NACC)
player:notify("Oops.  You can't update that object's aliases right now; try again in a few minutes.");
else
player:notify(tostr(e));
endif
endif
endif
endif
.
#6:64
"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);
spec = $code_utils:parse_verbref(iobjstr);
if (spec)
object = player:my_match_object(spec[1]);
if (!$command_utils:object_match_failed(object, spec[1]))
vname = spec[2];
info = verb_info(object, vname);
if (info == E_VERBNF)
player:notify("That object does not define that verb.");
elseif (typeof(info) == ERR)
player:notify(tostr(info));
else
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);
result = set_verb_info(object, vname, info);
if (typeof(result) == ERR)
player:notify(tostr(result));
else
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, "\""));
endif
elseif (!old_aliases)
player:notify("You have to leave a verb with at least one alias.");
endif
endif
endif
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.");
else
player:notify(tostr(e));
endif
endif
endif
endif
.
#6:65
"@describe <object> as <description>";
"Describe the given object with the text of 'description'.  You may use the caret (^) character to represent line breaks in your description text.";
set_task_perms(player);
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
return E_INVARG;
endif
desc = index(iobjstr, "^") ? $string_utils:explode(iobjstr, "^") | iobjstr;
if (e = dobj:set_description(desc))
vr_msg = $vr_core:describe_warning(player);
player:notify((("Description for " + $string_utils:nn(dobj)) + " set.") + (vr_msg && ("  " + vr_msg)));
elseif (e == E_PERM)
player:notify(("You aren't allowed to set the description of " + $string_utils:nn(dobj)) + ".");
else
player:notify(tostr("Couldn't set the description of ", $string_utils:nn(dobj), ": ", e));
endif
.
#6:66
"@messages <object>    -- All messages on given object.";
"@messages <object> from <parent>";
"                      -- Display messages on object defined on parent.";
if (!dobjstr)
player:tell_lines($code_utils:verb_documentation());
return;
elseif ($match_utils:object_match_failed(dobj = player:my_match_object(dobjstr, e = player:env()), dobjstr, e))
return;
elseif (!dobj:is_readable_by(player))
player:notify(tostr("You can't read the messages on ", dobj:dname(), "."));
return;
elseif (!prepstr)
props = $object_utils:all_properties(dobj);
elseif (prepstr != "from")
player:tell_lines($code_utils:verb_documentation());
return;
elseif ($match_utils:object_match_failed(iobj = player:my_match_object(iobjstr, e = player:env()), iobjstr, e))
return;
else
props = properties(iobj);
endif
secure = $code_utils:is_secure_verbcall(dobj, "has_message");
set_task_perms(player);
found = 0;
for pname in (props)
"len = length(pname);";
"if (len > 4 && pname[len - 3..len] == \"_msg\")";
if (msg_info = secure ? dobj:has_message(pname) | $object_utils:has_message(dobj, pname))
msg_name = msg_info[2];
found = found + 1;
msg = dobj:get_message(msg_name);
if (msg == E_PERM)
value = "isn't readable by you.";
elseif (!msg)
value = "isn't set.";
elseif (typeof(msg) == LIST)
value = "is a list.";
else
value = "is " + $string_utils:print(msg);
endif
player:notify(tostr("@", strsub(msg_name, "_msg", ""), " ", dobjstr, " ", value));
endif
endfor
if (found)
player:notify(tostr("Total of ", $string_utils:english_number(found), " message", (found == 1) ? "" | "s", iobjstr ? " inherited from " + iobj.name | "", "."));
elseif (iobjstr)
player:notify(tostr(dobj.name, " doesn't inherit any messages from ", iobj.name, "."));
else
player:notify(tostr(dobj.name, " doesn't have any messages to set."));
endif
.
#6:67
if (typeof(player.password) != STR)
if (length(args) != 1)
return player:notify(tostr("Usage:  ", verb, " <new-password>"));
else
new_password = args[1];
endif
elseif (length(args) != 2)
player:notify(tostr("Usage:  ", verb, " <old-password> <new-password>"));
return;
elseif (player.password != crypt(tostr(args[1]), player.password))
player:notify("That's not your old password.");
return;
else
new_password = args[2];
endif
player.password = crypt(tostr(new_password));
player:notify("New password set.");
.
#6:68
"@last-c           reports when and from where you last connected.";
"@last-c all       adds the 10 most recent places you connected from.";
"@last-c confunc   is like `@last-c' but is silent on first login.";
opts = {"all", "confunc"};
i = 0;
if (caller != this)
return E_PERM;
elseif (args && ((length(args) > 1) || (!(i = $string_utils:find_prefix(args[1], opts)))))
this:notify(tostr("Usage:  ", verb, " [all]"));
return;
endif
opt_all = i && (opts[i] == "all");
opt_confunc = i && (opts[i] == "confunc");
if (!(prev = this.previous_connection))
this:notify("Something was broken when you logged in; tell a wizard.");
elseif (prev[1] == 0)
opt_confunc || this:notify("Your previous connection was before we started keeping track.");
elseif (prev[1] > time())
this:notify("This is your first time connected.");
else
this:notify(tostr("Last connected ", ctime(prev[1]), " from ", prev[2]));
if (opt_all)
this:notify("Previous connections have been from the following sites:");
for l in (this.all_connect_places)
this:notify("   " + l);
endfor
endif
endif
.
#6:69
"@gender new_gender";
"  Set your gender to the given new_gender";
"@genders";
"  List all available genders.";
set_task_perms(valid(caller_perms()) ? caller_perms() | player);
if ((!args) || (verb == "@genders"))
player:notify($string_utils:pronoun_sub("Your gender is currently %(gender_noun).  Your pronouns are %s, %o, %p, %q, %r, %S, %O, %P, %Q and %R."));
player:notify(tostr("Available genders are ", $string_utils:name_list($object_utils:descendants($gender)), "."));
return;
endif
result = this:set_gender(argstr);
if (typeof(result) != ERR)
player:notify($string_utils:pronoun_sub("Gender set to %(gender_noun).  Your pronouns are now %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("New gender could not be set; your pronouns unchanged.  Check `@genders` for a list of genders.  If you would like to suggest a new gender, contact a wizard.");
endif
.
#6:70
"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:71
"@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
if (dobjstr == "brief")
player:notify("@mode has been deprecated.  Please use `@display-option +brief_rooms` instead.");
elseif (dobjstr == "verbose")
player:notify("@mode has been deprecated.  Please use `@display-option -brief_rooms` instead.");
else
player:notify("@mode has been deprecated.  See `@display-options` instead.");
endif
return;
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:72
"examine <object>";
"";
set_task_perms(player);
if (!dobjstr)
player:notify(tostr("Usage:  ", verb, " <object>"));
return E_INVARG;
endif
what = player:my_match_object(dobjstr);
if ($match_utils:object_match_failed(what, dobjstr, player:env()))
return;
endif
what:do_examine(player);
.
#6:73
"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.";
feature = args[1];
"Friday, April 12, 1996 8:32 PM: hacked to allow GMs to add/remove features";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
if ((typeof(feature) != OBJ) || (!valid(feature)))
return E_INVARG;
"Not a valid object.";
endif
if ($code_utils:verb_or_property(feature, "feature_ok", this))
"The object is willing to be a feature.";
if (typeof(this.features) == LIST)
"If list, we can simply setadd the feature.";
this.features = setadd(this.features, feature);
else
"If not, we erase the old value and create a new list.";
this.features = {feature};
endif
"Tell the feature it's just been added.";
feature:feature_add(this);
return 1;
"We're done.";
else
return E_PERM;
"Feature isn't feature_ok.";
endif
.
#6:74
"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];
"Friday, April 12, 1996 8:32 PM: hacked to allow GMs to add/remove features";
if ((!this:is_controllable_by(caller_perms(), caller)) && (caller_perms() != feature.owner))
return E_PERM;
endif
if (typeof(this.features) == LIST)
"If this is a list, we can just setremove...";
this.features = setremove(this.features, feature);
"Otherwise, we leave it alone.";
endif
"Let the feature know it's been removed.";
feature:feature_remove(this);
return 1;
"We're done.";
.
#6:75
"Usage:  @add-feature <feature object>";
"Add an object to your features list.";
set_task_perms(player);
if (dobjstr)
dobj = valid(dobj) ? dobj | $match_utils:match(dobjstr, $feature.warehouse:contents());
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>");
endif
.
#6:76
"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 = $match_utils:match(dobjstr, features);
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:77
"Usage:  @features [<name>] for <player>";
"List the feature objects matching <name> used by <player>.";
su = $string_utils;
if (prepstr && (prepstr != "for"))
player:notify("Usage: @features [[<name>] for <player>]");
return;
elseif (!iobjstr)
iobj = player;
elseif ($match_utils:player_match_failed(iobj = player:my_match_player(iobjstr), iobjstr))
return;
elseif ((!iobj.r) && (!iobj:is_controllable_by(caller)))
player:notify(("You aren't allowed to look at the features of " + su:nn(iobj)) + ".");
return E_PERM;
endif
text = {};
show_all = verb[$] == "!";
for fo in (iobj:features())
if (!$recycler:valid(fo))
this:remove_feature(fo);
elseif (dobjstr && (!fo:matches(dobjstr)))
"skip";
elseif (typeof(msg = `fo:display_msg() ! E_VERBNF => su:nn(fo)') == STR)
text = {@text, msg};
elseif (show_all)
text = {@text, su:nn(fo)};
endif
endfor
if (!text)
if (dobjstr)
player:notify(tostr("No features found on ", su:nn(iobj), " matching \"", dobjstr, "\"."));
else
player:notify(tostr("No features found on ", su:nn(iobj), "."));
endif
return;
endif
player:notify("-----------");
player:notify_lines($list_utils:sort(text));
player:notify("-----------");
fnum = length(text);
cstr = ((tostr(fnum) + " ") + $english:pluralize("feature", fnum)) + " found";
if (iobj != this)
cstr = (cstr + " on ") + su:nn(iobj);
endif
if (dobjstr)
cstr = ((cstr + " matching \"") + dobjstr) + "\"";
endif
cstr = cstr + ".";
player:notify(cstr);
.
#6:78
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:79
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 was derived from a core created on ", $time_utils:time_sub("$n $t, $Y", coretime), " at ", MOOname, " for version ", sversion, " of the 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
.
#6:80
player:notify(tostr("The server has been up for ", $time_utils:english_time(time() - $last_restart_time), "."));
.
#6:81
"QUIT";
"In all caps, always take it as a disconnect request.";
"quit";
"If there is a quit verb defined on the player's location, call that instead of disconnecting.";
"@quit";
"This is the preferred method of disconnection.";
here = player.location;
if (((verb == "@quit") || (!strcmp(verb, "QUIT"))) || (!$object_utils:has_callable_verb(here, verb)))
"...all caps, quit...";
boot_player(player);
else
here:(verb)(@args);
endif
.
#6:82
return this == args[1];
.
#6:83
"return true if player is active. This verb is !d";
return (typeof(idle_seconds(this)) != ERR) && pass(@args);
.
#6:84
{dest} = args;
if (dest == #-1)
return E_INVARG;
this:notify("You are now in #-1, The Void.  Type `home' to get back.");
endif
crime = $justice:is_criminal(this);
if (crime)
{date, prison, @rest} = crime;
if (`prison:forbid_moveto(this, dest) ! E_VERBNF')
return E_NACC;
endif
endif
set_task_perms(caller_perms());
return pass(@args);
.
#6:85
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..length(args)];
endif
this:tell("(from within you) ", @args);
.
#6:86
"Return a true value if this needs linewrapping.";
"default is true if .linelen > 0";
return this.linelen > 0;
.
#6:87
"Usage:  @set-note-{string | text} {#xx | #xx.pname}";
"        ...lines of text...";
"        .";
"";
"For use by clients' local editors, to save new text for a note or object property.  See $note_editor:local_editing_info() for details.";
set_task_perms(player);
text = $command_utils:read_lines(1);
if ((verb == "@set-note-string") && (length(text) <= 1))
text = text ? text[1] | "";
endif
if (spec = $code_utils:parse_propref(argstr))
o = toobj(spec[1]);
p = spec[2];
if ((!$object_utils:has_callable_verb(o, "set_" + p)) || (typeof(e = o:("set_" + p)(text)) == ERR))
e = `o.(p) = text ! ANY';
endif
if (typeof(e) == ERR)
player:tell("Error setting \"", p, "\" property of ", $string_utils:nn(o), "! (", e, ")");
else
player:tell("Set \"", p, "\" property of ", $string_utils:nn(o), ".");
endif
elseif (typeof(note = $code_utils:toobj(argstr)) == OBJ)
e = `note:set_text(text) ! ANY';
if (typeof(e) == ERR)
player:tell("Error setting text of ", $string_utils:nn(note), "!");
else
player:tell("Set text of ", $string_utils:nn(note), ".");
endif
else
player:tell("Error: Malformed argument to ", verb, ": ", argstr);
endif
.
#6:88
if ($perm_utils:controls(caller_perms(), this))
return this.(verb);
else
return E_PERM;
endif
.
#6:89
if (this:gag_p())
return;
endif
lines = args[1];
if (typeof(lines) != LIST)
lines = {lines};
endif
for line in (lines)
this:tell(line);
endfor
return;
"return early so ANSI is converted if need be";
this:notify_lines($list_utils:map_builtin(lines, "tostr"));
.
#6:90
"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
return this.pagelen = len;
endif
.
#6:91
"@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 ? $mail_agent:match_failed(folder = $mail_agent:match_recipient(a), a) | (folder = 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
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
.
#6:92
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));
email = iobjstr;
if (reason = $wiz_utils:check_reregistration(this.name, email, connection))
if (reason[1] == "-")
if (!$command_utils:yes_or_no(reason[2..length(reason)] + ". 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.autoregistration_player, $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);
else
who:notify("No automatic reregistration: your request will be forwarded.");
$mail_agent:send_message(this, $registration_db.registrar, "Registration request", {((((("Reregistration request from " + $string_utils:nn(who)) + " connected via ") + connection) + " and currently registered to <") + who.email_address) + ">:", "", (("@register " + who.name) + " ") + email, "@new-password " + who.name});
endif
.
#6:93
":doing_msg() -> whatcher doing";
here = this.location;
if (1 || (!$object_utils:isa(here, $generic_editor)))
msg = this.(verb);
if (!msg)
msg = "";
elseif (typeof(msg) == LIST)
msg = $list_utils:random_element(msg);
endif
return $string_utils:pronoun_sub(strsub(tostr(msg), "~", ""), this, this, here, player);
elseif (here == $mail_editor)
return "[mailing]";
elseif (here == $note_editor)
return "[writing]";
elseif (here == $verb_editor)
return "[programming]";
else
return "[editing]";
endif
.
#6:94
"WHO";
"Show status of all publicly connected users.";
if (((caller != this) && (caller != $login)) && (caller_perms() != this))
return E_PERM;
endif
if ((verb != "@who") && (strcmp(verb, "who") == 0))
return this:local_who();
endif
set_task_perms(player);
su = $string_utils;
tu = $time_utils;
RPG = $rpg;
linelen = `player:linelen() ! ANY => 79';
rule = tostr("%12 %-4 %-4 %7 %0");
$code_utils:notify(player, su:format(linelen, rule, "User", "Conn", "Idle", "RP-Stat", "`@my tag is ...`"));
border = su:space(linelen, "-");
bord_len = length(border);
mark_border = border;
mark_len = length(mark_msg = "[public]");
mark_border[(bord_len - mark_len) - 1..bord_len - 2] = mark_msg;
$code_utils:notify(player, mark_border);
"shouldn't calculate hidles if not a builder, duh, but i'm crippled at home in win95 now and my coding is thusly sloppy";
public = pidles = hidden = hidles = {};
for c in (connected_players())
if ($login:connected(c))
public = {@public, c};
pidles = {@pidles, `c:idle_seconds() ! ANY => $maxint'};
else
hidden = {@hidden, c};
hidles = {@hidles, `c:idle_seconds() ! ANY => $maxint'};
endif
endfor
public = $list_utils:sort(public, pidles);
hidden = $list_utils:sort(hidden, hidles);
mark = length(public) + 1;
all = $wiz_utils:is_builder(player) ? {@public, @hidden} | public;
for c in (all)
if (!(mark = mark - 1))
mark_border = border;
mark_len = length(mark_msg = "[hidden]");
mark_border[(bord_len - mark_len) - 1..bord_len - 2] = mark_msg;
$code_utils:notify(player, mark_border);
endif
stat = tostr(c.ic ? "IC " | "OOC", " ", RPG:user_designation(c));
final_field = 0 ? c:public_loc():who_location_msg(c) | (" " + c:tag_msg());
conn = `connected_seconds(c) ! ANY => -1';
idle = `idle_seconds(c) ! ANY => -1';
line = su:format(linelen, rule, c:name(), tu:short_time(conn), tu:short_time(idle), stat, final_field);
$code_utils:notify(player, line);
endfor
mark_border = border;
conn_len = length(all);
mark_len = length(mark_msg = tostr("[", conn_len, " ", $english:pluralize("user", conn_len), "; ", $login:current_lag(), "s lag]"));
mark_border[(bord_len - mark_len) - 1..bord_len - 2] = mark_msg;
"added ic_ctime 15-APR-96";
ic_ctime = tostr("[", $rpg:ic_ctime(), "]");
mark_border[3..2 + length(ic_ctime)] = ic_ctime;
$code_utils:notify(player, mark_border);
.
#6:95
"Send an OOC page to one or more users.";
"";
"       'user [message]";
"       '\"user_1 user_2 user_etc\" [message]";
"";
"If no users are given, quick-page responds to the user or users last contacted with \"'\" or \"+\".";
if (verb == "'")
dobj = this.last_paged;
else
string = $string_utils:explode(verb[2..length(verb)]);
dobj = $match_utils:match_player(string);
dobj = listdelete($match_utils:player_match_result(dobj, string), 1);
if (!dobj)
return;
endif
this.last_paged = dobj;
endif
this:page(dobj, argstr);
.
#6:96
"Send a private, OOC emote to one or more users.";
"";
"       +user [message]";
"       +\"user_1 user_2 user_etc\" [message]";
"";
"If no users are given, the remote-emote responds to the user or users last contacted with \"'\" or \"+\".  Use `++` instead of `+` to effect a no-space emote as with `::`.";
m = match(verb, "^%(%+%+?%)%([^+].*%)$");
if (!m)
dobj = this.last_paged;
else
string = $string_utils:explode(substitute("%2", m));
dobj = $match_utils:match_player(string);
dobj = listdelete($match_utils:player_match_result(dobj, string), 1);
if (!dobj)
return;
endif
this.last_paged = dobj;
endif
this:page(dobj, tostr(strsub(substitute("%1", m) || verb, "+", ":"), argstr));
.
#6:97
"moo-whisper <anything> to <anyone>";
"That old pain-in-the-ass MOO-whisper, kept for the benefit of those who use a client that munges things into that painful format.";
iobj:tell(player.name, " whispers, \"", dobjstr, "\"");
player:tell("You whisper, \"", dobjstr, "\" to ", iobj.name, ".");
.
#6:98
set_task_perms(player);
dobj = dobjstr ? $match_utils:match_player(dobjstr) | player;
if ($match_utils:player_match_failed(dobj, dobjstr))
return;
endif
subee = (dobj == player) ? $you | dobj;
"$quota_utils:display_quota(dobj);";
qinfo = dobj.size_quota;
unmeasured = qinfo[4];
su = $string_utils;
if (unmeasured > $quota_utils.max_unmeasured)
player:notify(su:pronoun_sub("%N %<has> some objects that need measured before %p quota can be accurately calculated.  %S should type `@measure new` to update %p ownership inventory.", subee));
return;
endif
quota = qinfo[1];
usage = qinfo[2];
lastc = qinfo[3];
count = length(dobj.owned_objects);
if (usage && count)
average_object_size = usage / count;
else
average_object_size = 2048;
endif
quota_left = quota - usage;
0 && su:group_number(quota_left / average_object_size);
if (quota_left > 0)
msg = tostr("%N %<is> allowed to create ", su:to_bytes(quota_left), " before trimming any of the ", su:to_bytes(usage), " from the ", su:group_number(count), " objects %s currently %<owns>.");
else
msg = tostr("%N %<is> going to have to trim ", su:to_bytes(abs(quota_left)), " off the ", su:to_bytes(usage), " consumed by the ", su:group_number(count), " objects %s currently %<owns> before being allowed to create more.  %S should type `@full-owned` to see a listing of all %p objects and their sizes.");
endif
player:notify(su:pronoun_sub(msg, subee));
if ((quota_left < 1) && dobj.programmer)
player:notify(su:pronoun_sub("Being a programmer, %s will also be prohibited from creating any verbs or properties until %s %<frees> some quota.", subee));
endif
.
#6:99
":name() -> Assume an underscore is just a way to get around a space.";
return strsub(pass(@args), "_", " ");
.
#6:100
":owned_objects() -> All objects owned by this player.";
if (!this:is_readable_by(caller_perms(), caller))
return E_PERM;
endif
return this.owned_objects;
.
#6:101
"@detail <object> <detailnames>[ as <desc>] -- Adds a detail to the object.";
"";
"'object' should be either \"my\", \"room's\" or \"<object>'s\".";
"'detailnames' should be a comma-separated list of aliases for the detail.";
"'desc' should be a string describing the detail.  If not given, you'll be prompted for a description.";
if (prepstr && (prepstr != "as"))
player:tell_lines($code_utils:verb_documentation());
return E_ARGS;
endif
parsed = $match_utils:parse_possessive_reference(dobjstr);
if (!parsed)
player:tell("You need to specify an object and detail names in one of the following formats:");
player:tell("  my detail[,alias*]");
player:tell("  room's detail[,alias*]");
player:tell("  <object>'s detail[,alias*]");
return E_ARGS;
endif
whose = parsed[1];
what = (whose == "room") ? player.location | player:my_match_object(whose);
if ($command_utils:object_match_failed(what, parsed[1]))
return;
endif
aliases = $string_utils:explode(parsed[2], ",");
if (!aliases)
player:tell("You must give at least one name for the detail.  [Aborting]");
return E_ARGS;
endif
if (!iobjstr)
iobjstr = $command_utils:read_lines();
endif
if (!iobjstr)
player:tell("You didn't give a description for the detail.  [Aborting]");
return E_ARGS;
endif
set_task_perms(player);
result = what:add_detail(aliases, iobjstr);
if (result == E_PERM)
player:tell("You aren't allowed to add details to ", what:id(), ".");
else
player:tell("Detail added to ", what:id(), " as ", $string_utils:english_list($list_utils:map_arg($string_utils, "print", aliases)), ".");
endif
.
#6:102
"@undetail <object> <detail> -- Remove the given detail from object.";
"";
"'object' should be either \"my\", \"room's\" or \"<object>'s\".";
"'detail' should be the name of the detail you wish to remove.";
parsed = $match_utils:parse_possessive_reference(dobjstr);
if (!parsed)
player:tell("You need to specify an object and detail names in one of the following formats:");
player:tell("  my detail");
player:tell("  room's detail");
player:tell("  <object>'s detail");
return E_ARGS;
endif
whose = parsed[1];
what = (whose == "room") ? player.location | player:my_match_object(whose);
if ($command_utils:object_match_failed(what, whose))
return;
elseif (!$object_utils:isa(what, $detailed))
player:tell(what:id(), " doesn't support details.");
return E_INVARG;
endif
set_task_perms(player);
detail = parsed[2];
result = what:remove_detail(detail);
if (result == E_NONE)
player:tell(what:id(), " has no detail \"", whose, "\".");
elseif (result == E_PERM)
player:tell("You aren't allowed to remove details from ", what:id(), ".");
else
player:tell("Detail \"", detail, "\" removed from ", what:id(), ".");
endif
.
#6:103
"@details [\"<object>\"][matching \"<matching-string>\"]";
"@details [matching \"<matching-string>\"] on <object>";
"";
"List details defined on the given object.  If no object is given, assume it's you.";
"";
"If `matching-string' is given, show details matching that string.";
if (!prepstr)
if (i = "matching" in args)
if (i == length(args))
player:tell("Matching what?");
return E_ARGS;
endif
w = rindex(argstr, "matching");
matching = argstr[w + 2..length(args)];
whatstr = (i == 1) ? this.name | argstr[1..w - 2];
else
whatstr = argstr;
matching = "";
endif
elseif ((prepstr != "on") || (!iobjstr))
player:tell_lines($code_utils:verb_documentation());
return E_ARGS;
else
whatstr = iobjstr;
w = rindex(argstr, "matching");
matching = w ? argstr[w + 2..length(argstr)] | "";
endif
what = whatstr ? player:my_match_object(whatstr) | player;
if ($command_utils:object_match_failed(what, whatstr))
return;
endif
if (!$object_utils:isa(what, $detailed))
player:tell(what:id(), " doesn't support details.");
return E_INVARG;
endif
set_task_perms(player);
names = what:detail_names();
if (names == E_PERM)
player:tell("You aren't allowed to read details on ", what:id(), ".");
return E_PERM;
endif
linelen = player:linelen();
if (!names)
player:tell($string_utils:centre("No details on " + what:name(), linelen, "-"));
return E_RANGE;
endif
shown = 0;
descs = what:detail_descs();
matching = matching && ("|" + matching);
for i in [1..length(names)]
if ((!matching) || (!index(names[i], matching)))
player:tell($string_utils:centre(names[i], linelen, "-"));
player:tell_lines(descs[i]);
shown = 1;
endif
endfor
if (shown)
player:tell($string_utils:centre("End @details " + argstr, linelen, "-"));
else
player:tell($string_utils:centre(((("No details on " + what:name()) + " match \"") + matching) + "\"", linelen, "-"));
endif
.
#6:104
set_task_perms(player);
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
return;
endif
res = dobj.key = 0;
if (typeof(res) == ERR)
player:notify(tostr(res, "."));
else
player:notify(tostr("Unlocked ", dobj.name, "."));
endif
.
#6:105
"@name object is newname";
"Changes object's name without buggering with its aliases.";
if (caller != this)
player:notify("You'll have to find your own @name verb.");
return E_PERM;
elseif (!(dobjstr && iobjstr))
player:notify("USAGE: @name <object> is <newname>");
return E_ARGS;
endif
set_task_perms(player);
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
"...you foolish swine!  choose a valid object!...";
return E_INVARG;
elseif (!dobj:is_writable_by(player))
player:notify(tostr("You aren't allowed to set the name of ", $string_utils:nn(dobj), "."));
return E_PERM;
elseif (typeof(result = dobj:set_name(iobjstr)) == ERR)
player:notify(tostr("Couldn't set the name of ", $string_utils:nn(dobj), " -> ", result));
return result;
else
vr_msg = $vr_core:rename_warning(player);
player:notify(tostr("Name of ", $string_utils:nn(dobj), " set to \"", dobj.name, "\"." + (vr_msg && ("  " + vr_msg))));
return 1;
endif
.
#6:106
"@chown <object> to <player>";
"Change ownership of <object> to <player>.  The object cannot have any verbs or properties defined, or have any children.";
if (caller != this)
return E_PERM;
elseif ((!dobjstr) || (!iobjstr))
player:notify_lines($code_utils:verb_documentation());
return E_ARGS;
endif
if ($justice:is_criminal(player))
player:notify(tostr("You aren't allowed to use ", verb, "."));
return E_PERM;
endif
where = player.location;
object = player:my_match_object(dobjstr);
if ($match_utils:object_match_failed(object, dobjstr, player:environment()))
return E_INVARG;
endif
owner = object.owner;
if ($justice:is_criminal(owner))
player:notify(owner:grammar_sub("%N (%#) %<is> a criminal.  %P objects cannot be @chowned."));
return E_PERM;
elseif ((owner != player) && (!owner:trusts(player, "chown from")))
player:notify(tostr("The owner of ", object:dnamec(), " does not trust you to change ownership of ", owner:pp(), " objects.  See 'help trusting'."));
return E_PERM;
elseif (object == player)
player:notify("You can't change ownership of yourself!");
return E_NACC;
elseif (is_player(object))
player:notify(object:dnamec() + " is a living being, you damnable fiend! People cannot be owned!");
return E_NACC;
endif
chown_to = $match_utils:match_player(iobjstr);
if ($match_utils:player_match_failed(chown_to, iobjstr))
return E_INVARG;
endif
if ($justice:is_criminal(chown_to))
player:notify(chown_to:grammar_sub("%N (%#) %<is> a criminal.  Objects cannot be @chowned to %o."));
return E_PERM;
elseif (!chown_to:trusts(player, "chown to"))
player:notify(tostr(chown_to:dnamec(), " does not trust you to change ownership of your objects to ", chown_to:po(), ".  See 'help trusting'."));
return E_PERM;
elseif (!$quota_utils:creation_permitted(chown_to))
player:notify(tostr(chown_to:dnamec(), " has exceeded ", chown_to:pp(), " ownership quota, and thus cannot receive objects."));
return E_QUOTA;
endif
if (children(object))
player:notify(tostr("Sorry, but ", object:dname(), " has children and thus cannot have its ownership changed."));
return E_NACC;
elseif (props = properties(object))
player:notify(tostr(object:dnamec(), " has the properties ", $string_utils:english_list($list_utils:map_arg($string_utils, "print", props)), " defined.  Please remove them if you wish to change ownership of the object."));
player:notify(("You may wish to consider making the object a generic and then @chowning a child to " + chown_to:dname()) + ".");
return E_NACC;
elseif (verbs = verbs(object))
player:notify(tostr(object:dnamec(), " defines the verbs ", $string_utils:english_list($list_utils:map_arg($string_utils, "print", verbs)), ".  For security reasons, you'll have to remove them before changing ownership."));
player:notify(("You may wish to consider making the object a generic and then @chowning a child to " + chown_to:dname()) + ".");
return E_NACC;
endif
$wiz_utils:set_owner(object, chown_to, 1);
if (object.owner == chown_to)
player:notify(tostr(object:dnamec(), " is now owned by ", chown_to:name(), "."));
return 1;
else
player:notify("Ownership transfer failed for reasons unknown.");
return 0;
endif
.
#6:107
"local-who";
"Show information on the characters in your current location.";
info = idle = {};
for o in (player:room():contents())
if ((secs = o:idle_seconds()) > -1)
c = $list_utils:flatten(o.clothing || {});
if (c || (!tostr(@o.nudity)))
extra = {};
else
extra = {"naked"};
endif
if (is_player(o) && (!$login:connected(o)))
extra = listappend(extra, "disconnected");
elseif ((u = o:unconscious()) > 0)
extra = listappend(extra, "comatose");
elseif (u < 0)
extra = listappend(extra, "sleeping");
else
if (o.fatigue)
extra = listappend(extra, "fatigued");
endif
if (o.insanity)
extra = listappend(extra, "nervous");
endif
endif
if (o.injury)
extra = listappend(extra, "injured");
endif
if (w = o:holding())
extra = listappend(extra, $string_utils:pronoun_sub("holding %(iname)", w));
endif
line = $string_utils:format("%3  %3  %-4  %0", o.ic ? "ic" | "ooc", $string_utils:uppercase(tostr(o:gender_adj() || "", "   ")[1..3]), $time_utils:abbr_time(secs), tostr(o:iname(), extra ? (" (" + $string_utils:english_list(extra)) + ")" | ""));
info = {@info, line};
idle = {@idle, secs};
endif
endfor
border = $string_utils:format("%3  %3  %-4  %0", "---", "---", "----", "");
player:tell($string_utils:format("%3  %3  %-4  %0", "ic", "sex", "idle", ""));
player:tell(border);
player:tell_lines($list_utils:sort(info, idle));
player:tell(border);
.
#6:108
"@mood \"Object does something periodically.\" for object";
"";
"@mood Something happening periodically in the room.";
"";
"@mood [for object]";
"";
"The @mood verb is provided only as a simple way to add mood messages.  The first syntax adds mood to an any object.  The second adds it to the room in which you are located.  The third lists mood on an object (or your location).";
"To delete or edit a mood you must '@edit object.mood'.  For full details on what exactly mood is, see 'help mood'.";
if (player != caller)
player:notify("Use your own @mood verb.");
return E_PERM;
endif
if ((prepstr != "for") || (!iobjstr))
object = player:room();
string = argstr;
elseif (valid(object = player:my_match_object(iobjstr)))
"...ok...";
string = dobjstr;
elseif (index(iobjstr, " "))
"...loose way of assuming they didn't want an iobj...";
object = player:room();
string = argstr;
elseif ($match_utils:object_match_failed(object, iobjstr, player:env()))
return;
else
player:notify("You specified an indirect object which cannot be matched.");
return;
endif
if (!$object_utils:has_property(object, "mood"))
player:notify(("Moods cannot be added to " + $string_utils:nn(object)) + ".");
endif
"Take the opportunity to make sure the object is having moods.";
if (!object:is_readable_by(player))
player:notify(("You don't have permission to read the moods on " + $string_utils:nn(object)) + ".");
elseif (string)
if (!object:is_writable_by(player))
player:notify(("You don't have permission to write moods to " + $string_utils:nn(object)) + ".");
else
object.mood = listappend(object.mood, string);
player:notify(tostr("Mood \"", string, "\" (", length(object.mood), ") added to ", $string_utils:nn(object), "."));
endif
elseif (mood = object.mood)
player:notify_lines(mood);
else
player:notify(("No mood messages defined on " + $string_utils:nn(object)) + ".");
endif
.
#6:109
"@ansi on";
"";
"Turn on ANSI emulation.  See '@ansi test' below for a list of all available emulation tokens.";
"";
"@ansi off";
"";
"Turn off ANSI emulation.";
"";
"@ansi raw";
"";
"Show all codes uninterpreted.";
"";
"@ansi reset";
"";
"Resets the display to normal mode and colours.";
"";
"@ansi test";
"";
"Send all ANSI tokens, showing them quoted and in action.";
0;
"If you use any of the display-mode tokens (bold, flashing, inverse) in your text, be SURE you close with a ~{0} to return to normal mode, lest ye anger everyone watching.  `@ansi reset` will send a normalizing code.";
"These tokens may be used in descriptions and messages, but one should beware as they may change without notice.";
"";
"~{NNN} is a special token sequence which inserts the corresponding high-ASCII character.  You must give three numeric characters after the tilde, else the sequence will be ignored.  Pad with leading zeros when needed.";
"Unprintable characters will be sent as a space.";
EMU = $emu;
now = player.ansi;
if (prepstr == "on")
new = 1;
elseif (prepstr == "off")
new = 0;
elseif (argstr == "raw")
new = -1;
elseif (argstr in {"normal", "reset"})
player:tell(tostr(EMU.ANSI_Line_Prefix, EMU:tokenize("normal"), "ANSI display mode reset."));
return;
elseif (argstr in {"test", "codes", "tokens"})
$emu:do_ANSI_test();
return;
else
player:notify(tostr("That isn't a valid argument (try \"on\", \"off\", \"raw\", \"reset\", or \"test\").  ANSI mode is currently ", (now < 1) ? "raw" | (now ? "activated" | "de-activated"), "."));
return E_INVARG;
endif
if (new == now)
player:notify(tostr("ANSI mode is already ", (now < 0) ? "raw" | (now ? "activated" | "de-activated"), "."));
else
this.ansi = new;
player:notify(tostr("ANSI mode ", (new < 0) ? "raw" | (new ? "activated" | "de-activated"), "."));
endif
.
#6:110
":match_option_package(str)";
return $match_utils:match(args[1], this:option_packages());
.
#6:111
":option_packages()";
return {$mail_options, $edit_options, $display_options, $combat_options, $privacy_options};
.
#6:112
":set_option(OBJ package, ANY key, ANY value)";
{package, key, value} = args;
if ((caller != package) && (!this:is_writable_by(caller_perms(), caller)))
return E_PERM;
endif
all_options = this.options;
old_options = all_options && `$table:get(all_options, package) ! E_RANGE';
if ($object_utils:isa(package, $generic_options))
new_options = package:set_option_value(old_options || {}, key, value);
elseif (old_options)
new_options = $table:set(old_options, key, value);
else
new_options = $table:new(key, value);
endif
if (typeof(new_options) == STR)
return new_options;
elseif (new_options == old_options)
return 0;
endif
if (all_options)
this.options = $table:set(all_options, package, new_options);
else
this.options = $table:new(package, new_options);
endif
return 1;
.
#6:113
":get_option(OBJ package[, ANY key])";
{package, ?key = 0} = args;
options = `$table:get(this.options, package) ! E_TYPE, E_RANGE';
if ($object_utils:isa(package, $generic_options))
if (key != 0)
value = package:get_option_value(options || {}, key);
else
value = options || {};
endif
else
if (key != 0)
value = $table:get(options, key);
else
value = options || $table:new();
endif
endif
return value;
.
#6:114
"The @option command shows or sets some pre-defined option.";
"";
"@option <type> <option> [is] <value>   sets <option> to <value>";
"@option <type> <option>=<value>        sets <option> to <value>";
"@option <type> +<option>     sets <option>   (usually equiv. to <option>=1";
"@option <type> -<option>     resets <option> (equiv. to <option>=0)";
"@option <type> !<option>     resets <option> (equiv. to <option>=0)";
"@option <type> <option>      displays value of <option>";
"";
"Eg: @option mail +sticky";
"    @option building room=#123";
if (caller != this)
return E_PERM;
endif
set_task_perms(player);
if ((i = index(verb, "-")) && (i != (l = length(verb))))
what = verb[i + 1..l];
elseif (args)
{what, @args} = args;
else
what = "";
endif
if (!what)
player:notify(tostr("Option packages are available for ", $string_utils:english_list($list_utils:map_arg($string_utils, "print", $list_utils:map_verb(this:option_packages(), "short_name"))), "."));
player:notify("Type `@options <package>` to see your options.  Type `help @options` for details on how to set them.");
return E_ARGS;
endif
option_pkg = this:match_option_package(what);
if (option_pkg == $ambiguous_match)
player:notify(("You'll have to be more specific as to which options package \"" + what) + "\" refers.");
return E_INVARG;
elseif (!valid(option_pkg))
player:notify(("There are no options for \"" + what) + "\".  Type `@options` alone to see a list of valid options packages.");
return E_INVARG;
endif
if (!args)
options = this:get_options(option_pkg);
player:notify_lines({("        Current " + option_pkg:name()) + ":", @option_pkg:show(options, option_pkg.names)});
elseif (typeof(presult = option_pkg:parse(args)) == STR)
player:notify(tostr("Error parsing options: ", presult));
elseif (length(presult) <= 1)
options = this:get_options(option_pkg);
player:notify_lines(option_pkg:show(options, @presult));
elseif (typeof(sresult = this:set_option(option_pkg, @presult)) == STR)
player:notify(tostr("Error setting options: ", sresult));
elseif (!sresult)
player:notify("No change.");
else
player:notify_lines(option_pkg:show(this:get_options(option_pkg), presult[1]));
endif
.
#6:115
":notify(str)";
"Handle the spammy tasks of paging and line-wrapping.";
line = args[1];
if (!this.pagelen)
(caller != this) && set_task_perms(caller_perms());
if (this.linelen > 0)
for l in (this:linesplit(line, this.linelen))
pass(l);
endfor
else
pass(line);
endif
return;
endif
if (connected_seconds(this) == E_INVARG)
"...drop it on the floor...";
return 0;
elseif ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
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])
pass(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)
pass(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;
.
#6:116
":process_notified_text(string)";
"Process the given string, inserting any special characters, such as ANSI codes.  See `help @ansi` for a list of those codes, and more info.";
string = args[1];
EMU = $emu;
prefix = EMU.ANSI_Line_Prefix;
color = this:get_option($display_options, "color");
if ((!caller_perms().wizard) || (!index(string, prefix)))
return string;
endif
string = strsub(string, prefix, "");
{regex, tokes, codes} = {EMU.ANSI_Regex_Tokens, EMU.ANSI_Tokens, EMU.ANSI_Translations};
while (m = match(string, regex))
a = m[3][1][1];
b = m[3][1][2];
s = substitute("%2", m);
if (n = toint(s))
string[a..b] = EMU:chr(n);
else
tmp = "";
for i in [1..length(s)]
if (p = s[i] in tokes)
tmp = tmp + (color ? codes[p] | "");
endif
endfor
string[a..b] = tmp;
endif
endwhile
return string;
"--WiZARDLY--";
.
#6:117
":namec()";
"-> Maybe a character wants a special capitalization.  Let them set namec.";
return this.namec || this:name();
.
#6:118
":is_unique()";
"All player characters are unique.";
return 1;
.
#6:119
"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;
elseif ((!$wiz_utils:is_builder(player)) && (!dobj:is_readable_by(player)))
player:tell("You aren't allowed to look at ", dobj:pp(), " objects.");
return E_PERM;
endif
dobjwords = $string_utils:words(dobjstr);
if (args[1..length(dobjwords)] == dobjwords)
args = args[length(dobjwords) + 1..length(args)];
endif
if (!(parse_result = $code_utils:_parse_audit_args(@args)))
player:notify(tostr("Usage:  ", verb, " [player] [from <start>] [to <end>] [for <match>]"));
return;
endif
if (typeof(dobj.owned_objects) != LIST)
player:notify(tostr(dobj.name, "'s owned_objects property is not a list.  Use @auditDB instead."));
return;
endif
start = parse_result[1];
end = parse_result[2];
match = parse_result[3];
player:notify(tostr("Objects owned by ", valid(dobj) ? dobj.name | dobj, ((" (from #" + tostr(start)) + " to #") + tostr(end), match ? " matching " + match | "", ")", ":"));
player:notify("");
count = 0;
"Only print every third suspension, so printed_anything gets 1-- until it's negative.  Can control this by changing the reset value higher or lower.";
printed = 0;
for o in (dobj.owned_objects)
if ($command_utils:running_out_of_time())
if (printed < 0)
player:tell(o, " ...");
printed = 2;
else
printed = printed - 1;
endif
suspend(0);
endif
if (valid(o) && ((tonum(o) >= start) && (tonum(o) <= end)))
found = match ? 0 | 1;
names = {o.name, @o.aliases};
while (names && (!found))
if (index(names[1], match) == 1)
found = 1;
endif
names = listdelete(names, 1);
endwhile
if (found)
player:tell(this:object_audit_string(o));
count = count + 1;
printed = 1;
endif
endif
endfor
if (count)
player:notify("");
endif
player:notify(tostr("Total: ", count, " object", (count == 1) ? "." | "s."));
.
#6:120
":object_audit_string(object [,prospectus-style])";
{o, ?prospectus = 0} = args;
"This verb mostly ripped from LambdaMOO.";
olen = length(tostr(max_object()));
CLASSES = {$player, $room, $exit, $note, $container, $thing, $feature, $mail_recipient, $weapon, $armour};
CLASS_STRINGS = {"p", "R", "E", "N", "C", "T", "F", "M", "W", "A"};
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
"disable audit object category until i gather up the gumption to bother porting the whole @pros/@audit suite from LambdaMOO or re-writing one here";
vstr = tostr(" kK"[kids + 1], r, 0 ? $building_utils:audit_object_category(o) | " ", vstr);
else
vstr = "";
endif
if ($quota_utils.byte_based)
vstr = tostr($building_utils:size_string(`o.object_size[1] ! ANY => 0'), " ", 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 = "";
try
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
except (ANY)
if ($perm_utils:controls(player, o))
loc = " BROKEN PROPERTY: .entrances";
endif
endtry
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);
.
#6:121
cl = {$player, $room, $exit, $note, $container, $thing};
cs = {"p", "R", "E", "N", "C", "T"};
if (is_player(OBJ = args[1]))
return "P";
endif
while (valid(OBJ))
if (i = OBJ in cl)
return cs[i];
endif
OBJ = parent(OBJ);
endwhile
return " ";
.
#6:122
":set_mail_forward(LIST recipients)";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
fwd = args[1];
alt = $alt_player_db:is_alternate_character(this);
if (alt && (alt != fwd))
return E_NACC;
elseif (typeof(fwd) != LIST)
return E_TYPE;
else
this.mail_forward = fwd;
return 1;
endif
.
#6:123
"@request <second-character-name>";
"With this command you can request additional characters.  The character from which you first @requested will be your primary character, and all additional characters must be @requested from that character.";
if (player != this)
player:notify("You have your own @request verb, darling.  Use it.");
return E_PERM;
elseif ((!player.email_address) || $object_utils:isa(player, $guest))
player:notify("You need to request a primary character first.  Please `@request <player-name> for <email-address>.");
return E_MAXREC;
elseif ($player_db.frozen)
player:notify("Sorry, can't create any new players right now.  Try again in a few minutes when the $player_db has thawed out.");
return E_NACC;
endif
primary = $alt_player_db:primary_character_for(player);
if (primary != player)
player:notify(tostr("Your primary character is ", $string_utils:nn(primary), ".  Please request through that character."));
return E_NACC;
elseif (!$alt_player_db:new_alternate_ok_for(player))
player:notify("Your aren't allowed to create anymore alternate characters.");
return E_QUOTA;
endif
name = argstr;
email = player.email_address;
if (verb[$] != "!")
player:tell("Are you sure you want to create \"", name, "\" as an alternate character registered to your email \"", email, "\"?  (If your email address is wrong, change it with `@register as <new-email>` before requesting an alt.)");
if (!$command_utils:yes_or_no())
player:tell(verb, " cancelled.");
return;
endif
endif
if (!$player_db:available(name))
player:notify("Someone is already using that name.  Please choose another.");
return E_NACC;
endif
{new, password} = $wiz_utils:make_player(name, email);
new.ownership_quota = $wiz_utils:starting_quota(new) / 2;
$alt_player_db:add(new, player);
player:notify(tostr("Character ", $string_utils:nn(new), " created with password '", password, "'."));
$mail_agent:send_message($new_player_log.autoregistration_player, $new_player_log, $string_utils:nn(new), {email, tostr("Created as alternate character for ", $string_utils:nn(player), ".")});
if ($network.active)
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, password)) == 0)
player:notify(tostr("Mail sent successfully to ", email, "."));
else
player:tell("Cannot send mail: ", result);
endif
endif
.
#6:124
"@alternates";
"Display a list of all your alternate characters and their last connection times.";
if ((player != this) || ((!player.wizard) && argstr))
"...should allow wizards to peek eventually...";
player:notify(tostr("You may only view your own alternate characters."));
return E_PERM;
elseif (prepstr == "for")
dobj = $match_utils:match_player(dobjstr = iobjstr);
elseif (!argstr)
dobj = player;
else
dobj = $match_utils:match_player(dobjstr = argstr);
endif
if ($match_utils:player_match_failed(dobj, dobjstr))
return;
endif
if (main = $alt_player_db:is_alt(dobj))
iobj = main[1];
alts = setremove($alt_player_db:all_alternates(@main), dobj);
player:notify($string_utils:pronoun_sub(tostr("%N %<is> an alternate character of %i", alts ? " along with %t." | "."), (dobj == player) ? $you | dobj, alts));
return -1;
elseif (alts = $alt_player_db:all_alternates(dobj))
iobj = alts;
player:notify($string_utils:pronoun_sub("%N %<has> alternate character(s) %i.", (dobj == player) ? $you | dobj));
return 1;
else
player:notify($string_utils:pronoun_sub("%N %<has> no alternate characters.", (dobj == player) ? $you | dobj));
return E_NONE;
endif
return;
"---this is lame---";
player:notify($string_utils:format("%12 Last Connected", "Name"));
border = tostr($string_utils:space(12, "-"), " ", $string_utils:space(28, "-"));
player:notify(border);
for pc in (all)
ct = player:ctime(pc.last_disconnect_time);
ct = ct[5..10] + ct[20..24];
player:notify($string_utils:format("%12 %0", pc:name(), ct));
endfor
player:notify(border);
.
#6:125
return pass(@args);
":is_writable_by(cp, caller)";
"Allow controllers of an alternate player to write the alternate.";
"return pass(@args) || ((i = $alt_player_db:is_alt(this)) && i[1] in args);";
.
#6:126
"@reroll [character]";
if (player != this)
return raise(E_PERM);
endif
if (player.location == $character_editor)
player:tell("You can't @reroll while in the character editor.  Exit, then @reroll and @chargen again.");
return;
endif
if (dobjstr)
dobj = player:my_match_object(dobjstr);
if ($match_utils:object_match_failed(dobj, dobjstr, player:env()))
return;
endif
else
dobj = player;
endif
if (!$rpg:is_character(dobj))
return player:tell(dobj:grammar_sub("%(dnamec) %<doesn't/don't> have any RPG attributes to roll."));
endif
perm = is_player(dobj) ? "is_controllable_by" | "is_writable_by";
if (!dobj:(perm)(player))
return player:tell(tostr("You aren't allowed to ", verb, " ", dobj:dname(), "."));
endif
clear = {};
if ((verb[$] != "!") && ((time() - dobj.last_reroll) > 60))
msg = "*** Using @reroll will reset all of your game statistics.  This includes your attributes (Strength, Willpower, etc.) and skills (Melee, Focus, Shock), your species, and your character \"sheet\".  Your character's age will be reset to the current time.  Are you SURE you want to do this?";
if (!$command_utils:yes_or_no(msg))
player:tell("Stats for ", $string_utils:nn(dobj), " are unchanged.");
return;
endif
endif
info = $rpg:parse_reroll_args(iobjstr);
if (!info[1])
return player:tell(info[2]);
endif
dobj:reroll(info[2], 1);
player:tell("All game stats cleared back to base levels.");
atts = $rpg.atts;
total = 0;
for att in (atts)
att = $string_utils:uppercase(att);
base = dobj:get_stat(att);
plus = $rpg:att_bonus(base);
player:tell("  ", $string_utils:left(att, -12), ": ", $string_utils:right(base, -3), " (", (plus < 0) ? "-" | (plus ? "+" | " "), $string_utils:right(abs(plus), -3), ")");
total = total + base;
endfor
0 && player:tell("Total: ", total, " points.");
.
#6:127
"@time";
"Show your current time.  Set your timezone with `@my timezone is XXX`, where XXX is EDT, PST, CST, etc.";
player:notify(tostr("The current RL time is ", player:ctime(), ".  The current GAME time is ", $rpg:ic_ctime(), "."));
.
#6:128
":puppet_huh(verb, args)";
puppets = this.puppets;
if (!puppets)
return 0;
endif
verb = args[1];
m = match(verb, "^%([^,]+%),$");
if (!m)
return 0;
endif
set_task_perms(caller_perms());
name = substitute("%1", m);
puppet = $match_utils:match(name, puppets);
if (!valid(puppet))
next_try = player:my_match_object(name);
if ($object_utils:isa(next_try, $puppet))
puppet = next_try;
endif
endif
if (valid(puppet))
if (!puppet:do(argstr))
player:notify(tostr(puppet:dname(), " doesn't understand that."));
elseif (player in puppet.monitors)
"...already listening to puppet--do not spam...";
else
player:notify(tostr("You transmit the command \"", argstr, "\" to ", puppet:dname(), "."));
endif
elseif (puppet == $ambiguous_match)
player:notify(tostr("I don't know which puppet out of ", $string_utils:name_and_number_list($match_utils:match_list(name, puppets)), " you mean."));
else
player:notify(tostr("You don't have a puppet named \"", name, "\" to command."));
endif
return 1;
.
#6:129
"      page <user> with <message>";
"      page <user> <message>";
"";
"Additionally, you may send the same message to several users by either enclosing all their names in quotes.";
su = $string_utils;
if (!valid(caller_perms()))
parsed = this:parse_page_args(@args);
if (!parsed)
return;
endif
dobj_list = parsed[1];
message = parsed[2];
elseif (caller == this)
dobj_list = su:lines_to_list(args[1]);
message = args[2];
else
player:notify(tostr("You aren't allowed to use ", this:pp(), " page verb. Use your own."));
return E_PERM;
endif
dobj = (length(dobj_list) == 1) ? dobj_list[1] | dobj_list;
"for pronoun_sub's benefit...";
iobj = player;
emoted = 0;
msg = message;
if (!message)
message = {player:page_origin_msg() || su:pronoun_sub($player.page_origin_msg, player)};
brief_msg = {su:pronoun_sub($player.page_origin_msg, player)};
elseif (message[1] == ":")
emoted = tostr(player:name(), (message[2] == ":") ? message[3..length(message)] | (" " + message[2..length(message)]));
message = {tostr("(remotely) ", emoted)};
brief_msg = message;
elseif ((header = player:page_origin_msg()) && (is_clear_property(player, "page_origin_msg") == 0))
brief_msg = {tostr(su:pronoun_sub("%N %<pages>, \""), message, "\"")};
message = tostr(su:pronoun_sub((su:index_delimited(header, player.name) ? "%S" | "%N") + " %<pages>, \""), message, "\"");
message = {header, message};
else
message = brief_msg = {tostr(su:pronoun_sub("%N %<pages>, \""), message, "\"")};
endif
result = {};
dopt = $display_options;
for d in (dobj_list)
r = d:get_option(dopt, "brief_pages") ? d:receive_page(msg, @brief_msg) | d:receive_page(msg, @message);
result = {@result, r};
endfor
result = min(@result);
if (emoted)
player:notify(su:pronoun_sub($login:connected(@dobj_list) ? "(to %n) " | "(to %(dname,unconnected)) ", dobj) + emoted);
elseif (result == 2)
"not connected";
player:notify(dobj:page_absent_msg() || su:pronoun_sub($player.page_absent_msg, dobj));
elseif (result)
player:notify(dobj:page_echo_msg(msg) || (msg ? su:pronoun_sub(("%N %<has> received your page: \"" + msg) + "\"", dobj) | su:pronoun_sub("%N %<has> received your summons.", dobj)));
else
player:notify(dobj:page_refused_msg() || su:pronoun_sub($player.page_refused_msg, dobj));
endif
.
#6:130
"@beep user [with msg]";
"Send a chr(7) to the given user, accompanied by the given msg.";
if (caller != this)
player:notify("Use your own @beep command.");
return;
elseif (!dobjstr)
player:notify("You must specify a user whom you'd like to beep.");
return;
endif
dobj = $match_utils:match_player(dobjstr);
if ($match_utils:player_match_failed(dobj, dobjstr))
return;
elseif (dobj:has_gagged(player, "beep"))
player:notify(tostr(dobj:grammar_sub("%N %<doesn't> want you beeping %o.")));
return;
endif
msg = tostr($emu.beep, "-!", player.name, "!- ", $emu.beep, iobjstr);
notify(dobj, msg);
if (dobj != player)
player:notify(tostr("You send a BEEP to ", dobj:dname(), "."));
endif
.
#6:131
"game [topic]";
"Show special help on the given aspect of the GhostMOO gaming system.";
game = $help._game_help_core;
kids = children(game);
if (!dobjstr)
player:tell("The following game topics are available:");
player:tell("");
player:tell_lines($string_utils:columnize($list_utils:map_property(kids, "name"), 2));
return;
endif
kid = $match_utils:match(dobjstr, kids);
if (kid == $ambiguous_match)
player:tell("You'll have to be more specific.  Type `game` alone for a list of valid topics.");
elseif (valid(kid))
player:tell("Showing game help on \"", kid:name(), "\":");
player:tell_lines(kid:text());
else
player:tell("There is no game help on \"", dobjstr, ".\"  Type `game` alone for a list of valid topics.");
endif
.
#6:132
"@hide";
"Remove yourself from the WHO list, and other VR connection-tracking mechanisms such as `whereis`.  Equivalent to 'signing out'.";
"@unhide";
"Show up in the WHO list.  Equivalent to 'signing in'.";
public = !this:get_option($privacy_options, "hide_who");
if ($object_utils:isa(player, $guest))
player:tell("Guests cannot @hide themselves; their connections are always public.");
elseif (verb == "@unhide")
if (public)
player:tell("You aren't @hidden.");
else
player:tell("You @unhide, making your connection info available to WHO, whereis, and other commands.");
this:set_option($privacy_options, "hide_who", 0);
endif
else
if (!public)
player:tell("You are already @hidden.");
else
player:tell("You @hide your connection info from WHO, whereis, and other connection and location tracking commands.");
this:set_option($privacy_options, "hide_who", 1);
endif
endif
.
#6:133
":has_message(str)";
"Accept 'plan' as a valid message.";
s = args[1];
return pass(s) || ((s == "plan") && {this, s});
.
#6:134
":birth_effects()";
"Set insanity to current damage and print message explaining why.";
ins = this.injury;
pass(@args);
this:tell("The shock of resurrection strains the very fibres of your sanity.  You should probably rest for a while before embarking on stressful pursuits.");
this.insanity = ins;
"*** Cap attributes at 100, the highest mortal rank. ***";
for att in ($rpg.atts)
if (this.(att) > 100)
this:set_stat(att, 100);
endif
endfor
this:mod_stat("endurance", -1);
return;
"*** Disable stat degradation, 20000121 ***";
skilldb = $rpg.skills;
for sk in (this:all_skills())
if (new = `skilldb.(sk):death_adjustment_for(this) ! ANY')
this:set_stat(sk, new);
endif
endfor
.
#6:135
"Apply death penalties to major attributes.";
return this:apply_death_penalty(pass(@args));
.
#6:136
":set_sheet(STR|LIST)";
"This verb allows GMs (and the player emself) to edit this charcter sheet of this PC.";
cp = caller_perms();
is_gm = $rpg:trusted(cp);
if ((!is_gm) && (!this:is_controllable_by(cp, caller)))
return E_PERM;
endif
old = this.sheet;
if ((old && ("FREEZE" in old)) && (!is_gm))
player:tell("A GM has frozen your character sheet.  If you wish to make further changes, you should contact that GM.");
player:tell("Your sheet last edited by: ", old[1]);
return E_PERM;
endif
new = args[1];
if (typeof(new) != LIST)
return E_TYPE;
endif
freezing = "FREEZE" in new;
if (freezing && (!is_gm))
player:tell("Only a GM can freeze a character sheet.");
return E_PERM;
endif
if (is_gm)
if (new[1][1..8] == "GM-EDIT:")
new = listdelete(new, 1);
endif
new = {tostr("GM-EDIT: ", ctime(), " by ", $string_utils:nn(cp)), @new};
if (!freezing)
player:tell("Sheet saved.  Remember that by including the line 'FREEZE' in the sheet text you can prevent the PC from editing their sheet further.");
endif
endif
this.sheet = new;
.
#6:137
"@lag";
"Show the current system lag.  If a sample is long overdue, a disclaimer is printed.";
eng = $english;
lag = $login:current_lag();
last = $login.last_lag_sample;
intv = $login.lag_sample_interval;
lenu = length(connected_players());
lenq = length(queued_tasks());
base = tostr("The lag is approximately ", lag, " ", eng:pluralize("second", lag), ".  There are ", lenu, " active ", eng:pluralize("connection", lenu), " and ", lenq, " ", eng:pluralize("task", lenq), " queued.");
if ($dump_started > max($dump_finished, $last_restart_time))
base = tostr(base, "  The database is dumping to disk; this may cause extraordinary lag.");
endif
if (last < (time() - (ded = intv * 5)))
base = tostr(base, "  However, the last sample was taken over ", $string_utils:from_seconds(ded), " ago; the sampler may be dead.");
endif
player:notify(base);
.
#6:138
"@edit <object>        -- If object is a note, edit its text; else the description.";
"@edit <object>.<property>     -- Edit property on object.";
"@edit <object>.<verb>         -- Edit verb on object.";
"@edit                         -- Resume an editing session.";
if ((player != caller_perms()) || (player != this))
return E_PERM;
elseif (player.programmer && ((!args) || $code_utils:parse_verbref(args[1])))
$verb_editor:invoke(argstr, verb);
elseif ($code_utils:parse_propref(dobjstr))
$note_editor:invoke(dobjstr, verb);
elseif ($object_utils:isa(player:my_match_object(dobjstr), $note))
$note_editor:invoke(dobjstr, verb);
elseif (dobjstr)
$note_editor:invoke(dobjstr + ".description", verb);
else
(((!player.programmer) || (player in $note_editor.active)) ? $note_editor | $verb_editor):invoke(dobjstr, verb);
endif
.
#6:139
"@recall [expunge[!]]";
"Recall pages received while disconnected.  If 'expunge' is given as the first argument, then delete all saved pages.  An exclaimed expunge bypasses prompt.";
if (caller != this)
return E_PERM;
endif
PURGE_THRESHOLD = 10;
len = length(pages = this.saved_pages);
if (!pages)
player:notify(tostr("No pages received since last disconnected at ", player:ctime(player.last_disconnect_time), "."));
return;
elseif (index(dobjstr, "expunge") == 1)
if ((dobjstr[$] == "!") || $command_utils:yes_or_no("Are you sure you want to delete all of your saved pages?"))
this.saved_pages = {};
player:notify(tostr(len, " page", (len == 1) ? "" | "s", " expunged."));
else
player:notify(tostr("No pages expunged."));
endif
return;
endif
for p in (pages)
player:notify(tostr("----- ", $string_utils:nn(p[1]), " @ ", this:ctime(p[2]), " -----"));
player:notify_lines(p[3]);
endfor
player:notify("-----");
over = len - PURGE_THRESHOLD;
if (over > 0)
this.saved_pages = pages[1..PURGE_THRESHOLD];
msg = tostr("Auto-deleting ", over, " pages.  ");
else
msg = "";
endif
player:notify(msg + "Use `@recall expunge` to delete all your saved pages.");
.
#6:140
"unpage USER";
"Expunge any saved pages on USER that were sent by you.";
if (caller != this)
return E_PERM;
endif
dobj = $match_utils:match_player(dobjstr);
if ($match_utils:player_match_failed(dobj, dobjstr))
return;
endif
done = 0;
for p in (dobj.saved_pages)
if (p[1] == player)
done = done + 1;
dobj.saved_pages = setremove(dobj.saved_pages, p);
endif
endfor
player:notify(tostr("If there were any saved pages from you to ", dobj:dname(), ", you've just removed them."));
.
#6:141
":finger_line_info()";
if (!caller_perms().wizard)
return E_PERM;
endif
line = {this:name()};
if (addr = this.email_address)
addr = $string_utils:left(addr, -24);
elseif ($object_utils:isa(this, $guest))
addr = $string_utils:centre("** guest **", -24);
else
addr = $string_utils:centre("** none! **", -24);
endif
line = {@line, addr};
if (cmd = this.last_command)
cmd = $string_utils:first_word(cmd)[1];
else
cmd = "";
endif
line = {@line, cmd};
line = {@line, $string_utils:right($time_utils:abbr_time(this:idle_seconds()), 4)};
line = {@line, $string_utils:connection_hostname(connection_name(this))};
return line;
.
#6:142
":public_location()";
"Return the location of this user.  If privacy option public_loc is false, return the special $hidden_loc room.  If it is 'zone', return their current zone.  If true, return current room.";
{?for_whom = player} = args;
publoc = this:get_option($privacy_options, "public_loc");
if (this:is_writable_by(for_whom))
return this:room();
elseif (publoc == "zone")
return this:zone();
elseif (publoc)
return this:room();
else
return $hidden_loc;
endif
.
#6:143
":public_email()";
if ((!this:is_writable_by(caller_perms())) && (!this:get_option($privacy_options, "public_email")))
return "someone@somewhere" || tostr($string_utils:lowercase(this.name), "@somewhere");
endif
return this.email_address;
.
#6:144
":public_connect_site()";
if ((!this:is_writable_by(caller_perms())) && (!this:get_option($privacy_options, "public_site")))
return "somewhere";
endif
return $string_utils:connection_hostname(connection_name(this) || this.last_connect_place);
.
#6:145
":public_home()";
"Return the home location of this user.  If they have the public_home privacy option unset, then return the special $hidden_loc room.";
if ((!this:is_writable_by(caller_perms())) && (!this:get_option($privacy_options, "public_home")))
return $hidden_home;
endif
return $recycler:valid(this.home) ? this.home | $player_start;
.
#6:146
"@finger user [with sheet plan snitch]";
"Show personal and game information on the given user. By default, all information (header, sheet, plan, GM snitch notes) is shown.  Set `@display-options +brief_finger` to show the header information only.  In brief mode, the 'with' options may be used to select other information to be displayed.";
if (caller != this)
return E_PERM;
endif
linelen = player:linelen() || 79;
set_task_perms(player);
if (!dobjstr)
player:tell("You must give the name of some player.");
return;
endif
dobj = $match_utils:match_player(dobjstr);
if ($match_utils:player_match_failed(dobj, dobjstr))
return;
endif
"parse args";
verbose = !player:get_option($display_options, "brief_finger");
show_plan = verbose || index(iobjstr, "plan");
show_sheet = verbose || index(iobjstr, "sheet");
show_snitch = $rpg:trusted(player) && (verbose || index(iobjstr, "snitch"));
"make pretty border";
border = $string_utils:center("@finger " + dobj.name, linelen, "-");
"get finger header text";
text = dobj:finger_header();
"add the plan?";
if (show_plan)
text = {@text, $string_utils:center("Plan", linelen, "-")};
if (plan = $code_utils:get_property(dobj, "plan"))
text = {@text, @$string_utils:lines_to_list(plan)};
else
text = {@text, "(None.)"};
endif
endif
"add the sheet?";
if (show_sheet)
text = {@text, $string_utils:center("Character Sheet", linelen, "-")};
if (sheet = dobj:character_sheet())
text = {@text, @$string_utils:lines_to_list(sheet)};
else
text = {@text, "(None.)"};
endif
endif
"add snitch notes?";
if (show_snitch)
text = {@text, $string_utils:center("GM @snitch Notes", linelen, "-")};
if (notes = `dobj:get_gm_notes() ! E_VERBNF')
text = {@text, @notes};
else
text = {@text, "(None.)"};
endif
endif
"display it all";
player:notify(border);
player:notify_lines(text);
player:notify(border);
.
#6:147
":parse_page_args(@args)";
su = $string_utils;
if (0)
"no longer supported--more trouble than worth";
m = match(argstr, "[^=0-9]%(=%)[^=0-9]");
i = m ? m[3][1][1] | 0;
if (i && (!index(stripped = su:trim(argstr[1..i - 1]), " ")))
"only use the '=' method if there's a single recipient";
dobjstr = stripped;
message = su:trim(argstr[i + 1..length(argstr)]);
endif
endif
m = match(argstr, "^%(\"[^\"]+\"%|[^ ]+%) +%(with%)? *%(.+%)$");
i = 0;
if (m)
dobjstr = substitute("%1", m);
message = substitute("%3", m);
elseif (args)
dobjstr = args[1];
argstr[1..length(args[1])] = "";
message = su:trim(argstr);
else
player:notify_lines($code_utils:verb_documentation($player, "page"));
return E_ARGS;
endif
dobjstr = su:trim(dobjstr, "\"");
dobjstr = su:explode(tostr(dobjstr));
dobj_list = $match_utils:match_player(dobjstr);
dobj_list = listdelete($match_utils:player_match_result(dobj_list, dobjstr), 1);
if (((!dobj_list) && i) && m)
message = substitute("%3", m);
dobjstr = su:explode(su:trim(substitute("%1", m), "\""));
dobj_list = $match_utils:match_player(dobjstr);
dobj_list = listdelete($match_utils:player_match_result(dobj_list, dobjstr), 1);
endif
return dobj_list && {dobj_list, message};
.
#6:148
":character_points()";
age = ((time() - max(this.first_connect_time, this.last_reroll)) / 86400) / 7;
pts = this.cp_base;
if (age < 11)
pts = pts + (age * 3);
elseif (age < 21)
pts = (pts + 30) + ((age - 10) * 2);
else
pts = (pts + age) + 30;
endif
return pts;
.
#6:149
":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;
endif
{msg, ?from = #-1} = args;
if (valid(from))
this:notify(tostr("Receiving mail from ", $string_utils:nn(from), " with subject \"", `msg[1] ! E_RANGE' || "<none>", "\" and forwarding it to \"", this.email_address, "\"."));
endif
player = this;
error = $network:sendmail(this.email_address, @msg);
if (error && valid(from))
this:notify(tostr("Mail sending failed: ", error));
endif
return error;
.
#6:150
"@netforward <msg>...                  -- as in help on @netforward";
"@netforward <msg>... on *<recipient>  -- netforwards messages on recipient.";
"This command forwards mail-messages to your registered email-address.";
if (player != this)
return player:tell(E_PERM);
endif
if (reason = $network:email_will_fail(email = player.email_address))
return player:notify(tostr("Cannot forward mail to your email address: ", reason));
endif
set_task_perms(valid(caller_perms()) ? caller_perms() | player);
if (!(p = player:parse_mailread_cmd(verb, args, "", "on")))
return;
endif
{folder, msg_seq, @rest} = p;
player:set_current_folder(folder);
if (folder == player)
folderstr = "";
else
folderstr = tostr(" from ", $mail_agent:name(folder));
endif
if (!$command_utils:yes_or_no(tostr("You are about to forward ", seq_size = $seq_utils:size(msg_seq), " message(s)", folderstr, " to your registered email-address, ", email, ".  Continue?")))
player:notify(tostr("@Netforward cancelled."));
return;
endif
player:notify("Attempting to send network mail...");
player._mail_task = task_id();
netmail = {};
linelen = player:linelen();
{netmail, header} = this:format_for_netforward(folder:messages_in_seq(msg_seq), folderstr);
reason = player:send_self_netmail({header, @netmail});
if (reason == 0)
player:notify(tostr("@netforward of ", header, " completed."));
else
player:notify(tostr("@netforward failed: ", reason, "."));
endif
.
#6:151
":page_echo_msg([paged_text])";
"As the other page messages (origin, absent, etc), but check to make sure the player's name is in the message.  If it isn't, prefix it.";
msg = player:get_option($display_options, "brief_pages") ? $player_class.(verb) | this.(verb);
if (!msg)
return msg;
endif
if (typeof(msg) == LIST)
msg = $list_utils:random_element(msg);
endif
if (!match(msg, tostr("%%[N([]%|", this.name)))
del = index(msg, "\"") ? "/" | "\"";
msg = tostr("%N %<offers> the following commentary regarding this page: ", del, msg, del);
endif
msg = $string_utils:pronoun_sub(msg, this);
return strsub(msg, "$text", args[1] || "");
.
#6:152
"@unread <msg> [on *<recipient>]";
"Resets last-read-date for recipient to just before the first of the indicated messages.";
set_task_perms(player);
p = this:parse_mailread_cmd("@unread", args, "cur", "on");
if (!p)
return;
endif
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
.
#6:153
":character_sheet()";
"Return this player's character sheet with administration lines removed.";
return setremove(this.sheet, "FREEZE");
.
#6:154
"@quota";
"Show how many objects you may create before recycling those you own.";
dobj = dobjstr ? $string_utils:match_player(dobjstr) | player;
if ($command_utils:player_match_failed(dobj, dobjstr))
return;
endif
if (dobj != player)
"set_task_perms(player);";
endif
q = dobj.ownership_quota;
c = length(dobj.owned_objects);
if (q == E_PERM)
player:notify(("You aren't allowed to know " + dobj:pp()) + " quota.");
elseif (typeof(q) == ERR)
player:notify(tostr(q));
elseif (q == 0)
player:notify($string_utils:pronoun_sub(tostr("%N can't create any more objects until %s %<recycles> some of the ", c, " %s already %<owns>."), (player == dobj) ? $you | dobj));
else
player:notify($string_utils:pronoun_sub(tostr("%N can create ", q, " new ", $string_utils:pluralize("object", q), " without recycling any of the ", c, " %s already %<owns>."), (player == dobj) ? $you | dobj));
endif
.
#6:155
"Syntax:";
"  @measure object <object name>";
"  @measure summary [player]";
"  @measure new [player]";
"  @measure breakdown <object name>";
if (length(args) < 1)
player:tell_lines($code_utils:verb_documentation());
return;
endif
if (index("object", args[1]) == 1)
"Object.";
name = $string_utils:from_list(args[2..$], " ");
what = player:my_match_object(name, e = player:env());
if ($match_utils:object_match_failed(what, name, e))
return;
endif
player:tell("Checking size of ", $string_utils:nn(what), " ...");
player:tell("Size of ", $string_utils:nn(what), " is ", $byte_quota_utils:object_bytes(what), " bytes.");
elseif (index("summary", args[1]) == 1)
"Summarize player.";
if (length(args) == 1)
name = "";
what = player;
else
name = $string_utils:from_list(args[2..$], " ");
what = $match_utils:match_player(name);
endif
if ($match_utils:player_match_failed(what, name))
return;
endif
$byte_quota_utils:do_summary(what);
elseif (index("new", args[1]) == 1)
if (length(args) == 1)
name = "";
what = player;
else
name = $string_utils:from_list(args[2..$], " ");
what = $match_utils:match_player(name);
endif
if ($match_utils:player_match_failed(what, name))
return;
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("breakdown", args[1]) == 1)
name = name = $string_utils:from_list(args[2..$], " ");
what = player:my_match_object(name);
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 ", $string_utils:nn(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
.
#6:156
"local_who";
"Show status of all in the room.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
set_task_perms(player);
su = $string_utils;
tu = $time_utils;
ou = $object_utils;
cu = $code_utils;
RPG = $rpg;
rule = "%16 %-8 %-4 %7 %-6 %-6 %3 %3 %3 %10";
cu:notify(player, su:format(rule, "User", "Gender", "Idle", "RP-Stat", "Threat", "Armour", "Inj", "Ins", "Fat", "Conscious?"));
linelen = player:linelen() || 79;
border = su:space(linelen, "-");
cu:notify(player, border);
connected = player:room():matching_contents();
is_gm = RPG:trusted(player);
for c in (connected)
if (RPG:is_character(c) && ((!is_player(c)) || ou:connected(c)))
stat = tostr(c.ic ? c:in_combat() ? "IC!" | "IC " | "OOC", " ", RPG:user_designation(c));
idle = idle_seconds(c);
idle = (typeof(idle) == NUM) ? tu:short_time(idle) | tu:short_time(c:idle_seconds() || random(60));
wild = 0;
for w in (c:weapons_wielded() || c:natural_weapons())
wild = wild + (w.damage || 0);
endfor
wear = 0;
for w in (c:armour_worn())
wear = wear + (w.absorption || 0);
endfor
wear = wear + c.natural_protection;
unc = c:unconscious();
unc = unc ? (unc < 0) ? "Sleeping" | "KnockedOut" | "Awake";
line = su:format(rule, c:name(), c:gender_noun(), idle, stat, is_gm ? wild || "-" | (wild ? "Yes" | "No"), is_gm ? wear || "-" | (wear ? "Yes" | "No"), RPG:wts_code(c.injury), RPG:wts_code(c.insanity), RPG:wts_code(c.fatigue), unc);
cu:notify(player, line);
endif
endfor
cu:notify(player, border);
.
#6:157
":apply_death_penalty(stat_rank)";
"Adjust attributes downward relative to total deaths.";
"06 Oct 95: Increased top penalty to total_deaths/5, max of 50%.";
"09 Nov 95: Decrease penalty according to total_power/100.";
"18 Dec 97: Removed (temporarily?) after death degradation was installed.";
return args[1];
.
#6:158
"@last-checkpoint";
"Show the time of the last checkpoint, and when the next is expected.";
beg = player:time($dump_started);
end = player:time($dump_finished);
INT = $dump_interval;
if (end == -1)
result = "(it failed!)";
elseif (!end)
result = "(a shutdown)";
elseif (end < beg)
restart = player:time($last_restart_time);
if (beg < restart)
player:notify(tostr("The last checkpoint started at ", player:ctime($dump_started), ", but since that was before the last restart, it probably failed.  The next one is due in about ", $string_utils:from_seconds(INT - (player:time() - restart)), "."));
else
player:notify(tostr("A checkpoint is currently in progress, started at ", player:ctime(beg), "."));
endif
return;
else
result = tostr("(lasting ", $string_utils:from_seconds(end - beg), ")");
endif
elapsed = player:time() - beg;
overdue = INT - elapsed;
if (overdue < 0)
duestr = tostr("is about ", $string_utils:from_seconds(abs(overdue)), " overdue");
else
duestr = tostr("is due in about ", $string_utils:from_seconds(overdue));
endif
if (elapsed < 86400)
if (ctime()[5..10] != ctime(beg)[5..10])
y_day = " yesterday";
else
y_day = "";
endif
player:notify($time_utils:time_sub(tostr("The last checkpoint ", result, " was at $o:$M$P", y_day, ".  The next ", duestr, "."), beg));
elseif (elapsed < (86400 * 7))
player:notify($time_utils:time_sub(tostr("The last checkpoint ", result, " was $D at $o:$M$P.  The next ", duestr, "."), beg));
elseif (elapsed < (86400 * 365))
player:notify($time_utils:time_sub(tostr("The last checkpoint ", result, " was at $o:$M$P on $N $t.  The next ", duestr, "."), beg));
else
player:notify($time_utils:time_sub(tostr("The last checkpoint ", result, " was way back in $N of $Y!  The next ", duestr, ", so I wouldn't hold your breath."), beg));
endif
.
#6:159
":matches(str)";
s = args[1];
p = pass(@args);
if (p)
return p;
endif
if (!index(this.name, "_"))
return 0;
endif
n = this:name();
if (n == s)
return 1;
elseif (index(s, n) == 1)
return -1;
else
return 0;
endif
.
#6:160
return pass(@args) - (length(this.pk) * 5);
.
#6:161
":registration_nag()";
if (caller != this)
return E_PERM;
endif
mesg = "";
prev = this.previous_connection;
if (!prev)
mesg = "Something was broken when you logged in; squawk to the gods.";
elseif (prev[1] == 0)
mesg = "Your previous connection was before we started keeping track.";
elseif (prev[1] > time())
mesg = "This is your first time connected.";
else
mesg = tostr("You were last connected at ", ctime(prev[1]), " from ", prev[2], ".");
endif
if (this.email_address)
mesg = tostr(mesg, "  Your current e-mail address is ", this.email_address, "; if this is incorrect, please re-register by typing `@register as user@host`.");
else
mesg = tostr(mesg, "  You don't have a registered e-mail address!  Please register by typing `@register as user@host`.");
endif
this:notify(mesg);
this:notify("Your registered e-mail address is used for important administrative functions, such as sending you a new password if you forget yours.  It is your responsibility to ensure it is correct.");
.
#6:162
if (caller != this)
return raise(E_PERM);
endif
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("You don't *really* want to commit suicide, do you?");
else
name = dobj.name;
result = `player:_recycle(dobj) ! ANY';
if (typeof(result) == ERR)
player:notify(tostr(verb, ": ", result));
else
player:notify(tostr(name, " (", dobj, ") recycled."));
endif
endif
.
#6:163
"Prevent accidental recycling of worthwhile objects.";
set_task_perms(caller_perms());
doomed = args[1];
warn = "";
"fuckit-- i'm using 'this' whether they're 'this' or not";
if (doomed.owner != this)
warn = $string_utils:nn(doomed) + " is not owned by you";
endif
if ((properties(doomed) || verbs(doomed)) || children(doomed))
warn = warn ? warn + ", and" | $string_utils:nn(doomed);
warn = warn + " has verbs, properties, and/or children.";
elseif (warn)
warn = warn + ".";
endif
if (warn && (!$command_utils:yes_or_no(warn + "  Are you sure you want to recycle it?")))
player:tell("Praise be!  ", $string_utils:nn(doomed), " has been saved!");
kill_task(task_id());
endif
return $recycler:(verb)(@args);
.
#6:164
"@size [update] for <whatever>";
"Show the size of the given object.  If the 'update' arg is given, re-measure that object.";
iobj = player:my_match_object(iobjstr, e = player:env());
if ($match_utils:object_match_failed(iobj, iobjstr, e))
return;
endif
if (dobjstr == "update")
bytes = $quota_utils:object_bytes(iobj);
else
bytes = $quota_utils:recent_object_bytes(iobj);
endif
"Player should always be this, anyway.";
player:notify(tostr("The size of ", $string_utils:nn(iobj), " is ", $string_utils:byte_string(bytes), "."));
.
#6:165
"@dump-garbage";
"Send any children of invalid objects to the $recycler.";
trash = $recycler;
dumped = {};
for o in ({@player.contents, @player.location.contents})
if (trash:valid(o) && valid(parent(o)))
continue;
endif
dumped = {@dumped, tostr(o)};
move(o, trash);
endfor
if (dumped)
player:notify(tostr("Objects ", $string_utils:english_list(dumped), " moved to the recycling center."));
else
player:notify("No non-objects found.");
endif
.
#6:166
":_chparent(obj, parent)";
set_task_perms(caller_perms());
orphan = args[1];
parent = args[2];
warn = "";
if (!$object_utils:isa(parent, p = parent(orphan)))
warn = tostr("Prospective parent ", $string_utils:nn(parent), " is not a descendant of the current parent ", $string_utils:nn(p), ".  Are you sure you change the parent of ", $string_utils:nn(orphan), "?");
endif
if (warn && (!$command_utils:yes_or_no(warn)))
player:tell("Praise be!  ", $string_utils:nn(orphan), " goes back to its mommy!");
kill_task(task_id());
endif
return chparent(@args);
.
#6:167
":acceptable(what)";
return pass(@args) && ($rpg:trusted(player) || ($object_utils:connected(this) || this:trusts(player, "give-to")));
.
#6:168
":virtual_age()";
"Return the age, in seconds, of this character.";
return time() - this.first_connect_time;
.
#6:169
"skill <skillname>";
"Show stats for the given skill.";
if (!dobjstr)
player:tell("You didn't give the name of a skill or attribute.");
return;
elseif (valid(stat = player:match_skill(dobjstr)))
stat:tell_stats();
elseif (stat == $ambiguous_match)
player:tell("More than one skill or attribute matches that name.");
else
player:tell("No skill or attribute matches that name.");
endif
.
#6:170
":remove_puppet(#puppet)";
"Allow this user and controllers of #puppet to remove it.";
{puppet} = args;
if (this:is_controllable_by(caller_perms(), caller))
"ok";
elseif (`!puppet:is_controllable_by() ! E_INVIND, E_VERBNF => 0')
return E_PERM;
endif
this.puppets = setremove(this.puppets, puppet);
.
#6:171
":add_puppet(#puppet)";
"Allow this user and controllers of #puppet to remove it.";
{puppet} = args;
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
this.puppets = setadd(this.puppets, puppet);
.
#6:172
":has_gagged(pc, ['action'])";
" action=page  Use of any paging mechanism.";
"        beep  Use of the @beep command.";
"New actions will be inserted here as they are implemented.";
{pc, ?action} = args;
return pc in this.gaglist;
"...to be finished...";
{?who, ?gagged = 0} = $list_utils:assoc(pc, this.gaglist);
return gagged;
.
#6:173
":get_option(OBJ option_pkg, STR option_name)";
if (0 && (!this:is_readable_by(caller_perms(), caller)))
"why did i think this necessary?  --960729";
return E_PERM;
endif
option_pkg = args[1];
p_name = option_pkg.storage_prop;
current = this.(p_name);
args = listdelete(args, 1);
if (args)
return option_pkg:get(current, @args);
else
return this.(p_name);
endif
.
#6:174
":set_option(OBJ option_pkg, STR option_name, option_value)";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
option_pkg = args[1];
p_name = option_pkg.storage_prop;
current = this:get_options(option_pkg);
if (typeof(s = option_pkg:set(current, @listdelete(args, 1))) == STR)
return s;
elseif (s == current)
return 0;
else
this.(p_name) = s;
return 1;
endif
.
#6:175
"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:     ", this: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};
.
#6:176
":my_match_player(string)";
{spec} = args;
return $match_utils:match_player(spec);
.
#6:177
":public_who()";
"Return true if this user should be included in the WHO listings.";
return !this:get_option($privacy_options, "hide_who");
.
#6:178
if (!$rpg:allow_player_stat_change_by(caller_perms(), this))
raise(E_PERM);
endif
return pass(@args);
"Only allow programmers to set player stats.             <Quinn; 19970802>";
"Allow non-prog to set stats of NPC only.                <Quinn; 19971224>";
"Ask $rpg before allowing anyone to set any player stat. <Quinn; 19981231>";
.
#6:179
"Hack to prevent players from ever depositing more than 1 crystal.";
return 1;
.
#6:180
":death_effects(killer, weapon)";
if (!this:is_controllable_by(caller_perms(), caller))
return raise(E_PERM);
endif
{killer, @rest} = args;
if (((killer != this) && $object_utils:has_property(killer, "pk")) && (!(killer in this.pk)))
killer.pk = {@killer.pk, this};
endif
return pass(@args);
.
#6:181
if (caller != this)
raise(E_PERM);
endif
len = length(this.saved_pages);
if (len)
this:notify(tostr("You have ", len, " saved pages waiting.  Type `@recall` to view them."));
endif
return len;
.
#6:182
"Print a short traceback if so optioned.";
if ((caller != this) && (caller != $sys))
return raise(E_PERM);
endif
{c, m, v, t, f} = args;
if (this:get_option($display_options, "short_tbks"))
notify(player, tostr("<<<Error>>> ", f[1]));
return 1;
endif
return 0;
.
#6:183
"Print a short traceback if so optioned.";
if ((caller != this) && (caller != $sys))
return raise(E_PERM);
endif
{r, t, f} = args;
if (this:get_option($display_options, "short_tbks"))
notify(player, tostr("<<<Timeout>>> ", f[1]));
return 1;
endif
return 0;
.
#6:184
set_task_perms(caller_perms());
$news:check();
.
#6:185
"Show signs of injury and fatigue.";
inj = this:stat_injury();
fat = this:stat_fatigue();
ins = this:stat_insanity();
something_happened = 0;
msg = "";
if (inj < 60)
"";
elseif (1)
msg = "%N %<winces> with the pain of %p wounds.";
elseif (inj < 60)
"...disable messages...";
elseif (inj < 100)
msg = "%N %<spasms> involuntarily with the burden of %p heavily wounded body.";
else
msg = "%N %<croaks> a tortured moan and %<doubles> over with the pain of %p severe wounds.";
endif
stg = this:stat_strength();
if (fat < 20)
"";
elseif ((fat > ((stg + (stg / 2)) + random(stg / 2))) && (w = this:weapons_wielded()))
wstr = $string_utils:name_list(w);
r = this:room();
for o in (w)
o:moveto(r);
endfor
msg = msg ? tostr(msg, "  %S %<drops> %p ", wstr, ", nearly overcome with fatigue.") | (("Nearly overcome with fatigue, %n %<drops> %p " + wstr) + ".");
elseif (0)
"...disable messages...";
elseif (fat < 60)
msg = msg ? tostr(msg, "  %S %<seems> to have worked up a bit of a sweat.") | "%N %<seems> to have worked up a little sweat.";
elseif (fat < 100)
msg = msg ? tostr(msg, "  %P misery is compounded by minor fatigue.") | "%N %<pants> with the breathlessness of minor fatigue.";
else
msg = msg ? tostr(msg, "  %S %<sweats> profusely, tongue lolling out between ragged breaths.") | "%N %<pants> with rude volume as %s %<wipes> a thick sheen of sweat from %p brow.";
endif
if (ins < 15)
"";
elseif (1)
msg = msg ? tostr(msg, "  %S %<twitches> with neurotic unease.") | "%N %<twitches> with neurotic unease.";
if (q = this.queue)
r = random(5);
while (q && r)
q = listdelete(q, 1);
r = r - 1;
endwhile
this.queue = q;
endif
times = min(ins / 25, 4);
verbs = {"poke", "point", "smile", "giggle", "laugh", "cackle", "wink", "yawn", "wave", "shrug", "cringe", "smirk", "nod", "sneer"};
soc_msg = "";
for i in [1..times]
r = random(25);
if (r < 3)
this:take_random_exit();
elseif ((r < 12) && (w = this:weapons_wielded()))
wstr = $string_utils:name_list(w);
r = this:room();
for o in (w)
o:moveto(r);
endfor
msg = tostr(msg, "  Overcome with anxiety, %s %<fumbles> and %<drops> %p ", wstr, ".");
else
vrb = $list_utils:random_element(verbs);
verbs = setremove(verbs, vrb);
if (soc_msg)
soc_msg = tostr(soc_msg, (random(10) < 5) ? " and" | ", then", " %<", $english:pluralize(vrb), ">");
else
soc_msg = tostr("  %S %<", $english:pluralize(vrb), ">");
endif
endif
endfor
if (soc_msg)
msg = tostr(msg, soc_msg, " at nothing in particular.");
endif
elseif (ins < 50)
msg = msg ? tostr(msg, "  %S %<blinks> erratically and %<clenches> %p fists.") | "%N %<fidgets> with mild nervousness.";
elseif (ins < 100)
if (msg)
msg = tostr(msg, "  %S %<looks> down to notice %p knuckles white from clenching %p fists.  %S %<relaxes/relax> the nervous grip, then %<wipes> %p bloodshot eyes with the back of %p hand.");
else
msg = "%N %<jumps> around suddenly, scanning the area with clenched fists. Slowly, %s %<resumes> %p position, sweat forming on %p brow as %p eyes dart nervously.";
endif
else
if (msg)
msg = tostr(msg, "  %S %<darts> %p bloodshot eyes back and forth, gibbering mindlessly while flashing a paranoid stare at anything that moves.");
else
msg = "%S suddenly %<drops> to the ground and %<curls> up in ball, rocking back and forth and murmuring nonsense.  Just as suddenly, %s %<rises>.  %P eyes dart back and forth nervously.";
endif
endif
if (msg)
$you:say_action(msg, this);
something_happened = 1;
endif
return pass(@args) || something_happened;
.
#6:186
"Don't announce player moods if not connected.";
return pass(@args) && $object_utils:connected(this);
.
#6:187
"Player insanity effects. See same verb on $combatant for more info.";
ins = this.insanity;
if (verb[1..6] == "should")
return ins > 100;
endif
if (!this:is_controllable_by(caller_perms(), caller))
return raise(E_PERM);
endif
times = min(ins / 25, 3);
verbs = {"poke", "point", "smile", "giggle", "laugh", "cackle", "wink", "yawn", "wave", "shrug", "cringe", "smirk", "nod", "sneer"};
msg = soc_msg = "";
for i in [1..times]
r = random(25);
if (r < 3)
this:take_random_exit();
elseif ((r < 12) && (w = this:weapons_wielded()))
wstr = $string_utils:name_list(w);
r = this:room();
for o in (w)
o:moveto(r);
endfor
msg = tostr("Overcome with anxiety, %s %<fumbles> and %<drops> %p ", wstr, ".  ");
else
vrb = $list_utils:random_element(verbs);
verbs = setremove(verbs, vrb);
if (soc_msg)
soc_msg = tostr(soc_msg, (random(10) < 5) ? " and" | ", then", " %<", $english:pluralize(vrb), ">");
else
soc_msg = tostr("%S %<", $english:pluralize(vrb), ">");
endif
endif
endfor
if (soc_msg)
msg = tostr(msg, msg && "  ", soc_msg, " at nothing in particular.");
endif
msg && $you:say_action(msg, this);
.
#6:188
"Player fatigue effects. See same verb on $combatant for more info.";
{?new = 0} = args;
fat = this.fatigue;
if (verb[1..6] == "should")
return (fat > 100) || ((new > 15) && (!(fat % 5)));
endif
if (!this:is_controllable_by(caller_perms(), caller))
return raise(E_PERM);
endif
"stg because it used to be strength, but endurance seems to fit better here,";
"tho that's also the key for injury hmm...  <Quinn;199808170118>";
stg = this:stat_endurance();
if (fat > (stg * 5))
this:tell("Your knees feel wobbly.  The world spins...");
this:unconsciousness(this, new);
$you:say_action("%N %<falls> unconscious from fatigue.", this);
elseif ((fat > ((stg + (stg / 2)) + random(stg / 2))) && (w = this:weapons_wielded()))
wstr = $string_utils:name_list(w);
r = this:room();
for o in (w)
o:moveto(r);
endfor
$you:say_action(("Nearly overcome with fatigue, %n %<drops> %p " + wstr) + ".");
elseif ((new > 15) && (!(fat % 5)))
this:tell("Your alertness is sapped by a whisper of drowsiness punctuated by the dull ache of your muscles.");
endif
.
#6:189
"If the player has been active, give some potential.";
if (caller != this)
raise(E_PERM);
endif
"only award pot to those in-character";
if (!this.ic)
return 0;
endif
{idlet, pot} = $rpg.pot_rate;
"skip those idle over the threshold";
if (`idle_seconds(this) ! ANY => idlet + 1' > idlet)
return 0;
endif
"add it!";
this.pot = tofloat(this.pot) + pot;
return 1;
.
#6:190
"Add potential to this player.";
if (!$rpg:trusted(caller_perms()))
return pass(@args);
endif
this:maybe_increase_potential();
return pass(@args);
.
#6:191
"@advantage <point-cost> <name> as <description>";
"     Propose an advantage or (if point-cost is 0) an uncoded skill.";
"@advantage !<name>";
"     Remove a proposed advantage or uncoded skill.";
"@advantages";
"     List existing proposed advantages and uncoded skills.";
if (!argstr)
if (verb == "@advantages")
"list them";
for info in ($rpg.proposed_advantages)
{advantage, desc, author, when, ?points = 0} = info;
if (!points)
points_msg = "skill; ";
else
points_msg = tostr(points, " pts; ");
endif
player:tell(advantage, " (", points_msg, "proposed ", ctime(when), " by ", author.name, ")");
for line in ($generic_editor:fill_string(desc, 60))
player:tell("     ", line);
endfor
endfor
$command_utils:suspend_if_needed(5, "----- Suspending @advantages for 5 seconds. -----");
else
player:tell_lines($code_utils:verb_doc());
endif
return;
elseif (dobjstr && (dobjstr[1] == "!"))
dobjstr[1..1] = "";
i = $list_utils:iassoc(dobjstr, $rpg.proposed_advantages);
if (!i)
player:tell("No such proposed advantage.");
return;
endif
{advantage, desc, author, when, ?points = 0} = $rpg.proposed_advantages[i];
if (author != player)
player:tell(author:grammar_sub("%n already added that advantage; only %s can remove it."));
else
$rpg.proposed_advantages[i..i] = {};
player:tell("Removed advantage \"", advantage, "\" added costing ", points, " potential points and described as \"", desc, "\".");
endif
return;
endif
if (m = match(dobjstr, "^%([0-9]+%) +"))
points = toint(substitute("%1", m));
dobjstr[1..m[2]] = "";
else
player:tell("You must give a potential point cost for the advantage.  Use '0' to propose an uncoded skill.");
return;
endif
if (!iobjstr)
player:tell("You must give a description for the advantage.");
return;
endif
i = $list_utils:iassoc(dobjstr, $rpg.proposed_advantages);
if (i)
{advantage, desc, author, when, ?points = 0} = $rpg.proposed_advantages[i];
if (author != player)
player:tell(author:grammar_sub("%n already added that advantage; only %s can edit it."));
return;
endif
endif
info = {dobjstr, iobjstr, player, time(), points};
if (i)
$rpg.proposed_advantages[i] = info;
else
$rpg.proposed_advantages = {@$rpg.proposed_advantages, info};
endif
if (points)
player:tell("Advantage \"", dobjstr, "\" added costing ", points, " potential points and described as \"", iobjstr, "\".");
else
player:tell("Uncoded skill \"", dobjstr, "\" added, described as \"", iobjstr, "\".");
endif
.
#6:192
":finger_header()";
set_task_perms(cp = caller_perms());
su = $string_utils;
opt = $privacy_options;
linelen = player:linelen();
text = {};
if (valid(this.species))
species_str = "/" + this.species:namec();
else
species_str = "";
endif
text = {@text, tostr("Identity:    ", this:name(), " <", this:public_email() || "nobody@example.com", ">, ", su:capitalize(this:gender_adj()), species_str, " ", su:capitalize(parent(this):spawned_name()), ", ", su:to_bytes($quota_utils:recent_object_bytes(this)))};
text = {@text, tostr("Location:    ", `this:public_loc():id() ! ANY => "*** nowhere ***"')};
0 && (text = {@text, tostr("Home:        ", this:public_home():id())});
connected = $object_utils:connected(this);
if (this:get_option(opt, "hide_who"))
connected = 0;
endif
fc = this.first_connect_time;
unborn = (!fc) || (fc == $maxint);
ppts = tofloat(this.pot);
cpts = this:character_points();
if (!unborn)
text = {@text, tostr("Created:     ", player:ctime(fc), " (", su:from_seconds(time() - fc), " ago)")};
lr = this.last_reroll;
if (lr)
text = {@text, tostr("Re-rolled:   ", player:ctime(lr), " (", su:from_seconds(time() - lr), " ago)")};
else
text = {@text, tostr("Re-rolled:   Never")};
endif
else
text = {@text, tostr("Created:     Unborn")};
endif
if (this:is_controllable_by(cp))
text = {@text, tostr("Potential:   ", floatstr(ppts, 2), " (", cpts, " character points)")};
endif
if ($rpg:trusted(cp))
text = {@text, tostr("GM Notes:    ", this:get_gm_notes() ? ("Yes; Use `@finger " + this.name) + " with snitch` to view them." | "No")};
endif
return text;
.
#6:193
":process_notified_text(string)";
"Process the given string, inserting any special characters, such as ANSI codes.  See `help @ansi` for a list of those codes, and more info.";
string = args[1];
EMU = $emu;
prefix = EMU.ANSI_Line_Prefix;
if (((!caller_perms().wizard) || (this.ansi < 0)) || (!index(string, prefix)))
return string;
endif
string = strsub(string, prefix, "");
regex = EMU.ANSI_Regex_Tokens;
tokes = EMU.ANSI_Tokens;
if (ansi = this.ansi)
codes = EMU.ANSI_Translations;
print = EMU.ANSI_Printables;
else
codes = EMU.ASCII_Translations;
print = EMU.ASCII_Equivalents;
endif
while (m = match(string, regex))
a = m[3][1][1];
b = m[3][1][2];
s = substitute("%2", m);
if (n = toint(s))
string[a..b] = print[n];
else
tmp = "";
for i in [1..length(s)]
if (p = s[i] in tokes)
tmp = tmp + codes[p];
endif
endfor
string[a..b] = tmp;
endif
endwhile
return string;
"--WiZARDLY--";
.
#6:194
"Send an OOC page to one or more users, Mush-style.";
"";
"       mp user=message";
"       mp user_1 user_2 user_etc=message";
i = index(argstr, "=");
if (i)
message = $string_utils:trim(argstr[i + 1..$]);
dobjstr = $string_utils:trim(argstr[1..i - 1]);
dobjlist = $string_utils:words(dobjstr);
else
message = "";
dobjstr = argstr;
dobjlist = $string_utils:words(dobjstr);
endif
dobj = $match_utils:match_player(dobjlist);
dobj = listdelete($match_utils:player_match_result(dobj, dobjlist), 1);
if (!dobj)
return;
endif
this.last_paged = dobj;
this:page(dobj, argstr = message);
.
#6:195
"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
.
#6:196
"@chargen <char>";
"Prototype character editor frontend.";
if (caller != this)
return raise(E_PERM);
elseif (player.location != $pec)
player:tell("Sorry, but you can only edit your character from within ", $pec:dname(), ".  Type `@go PEC` to go there.");
return E_NACC;
endif
if (!argstr)
argstr = "me";
endif
$character_editor:invoke(argstr, verb);
.
#7:0
"If the player is attacking, queue a flee action.";
"!d for speed and comfort.  Whee!";
if (player:attacking() && (!$list_utils:assoc("do_flee", callers(), 2)))
player:flee(dobjstr = argstr = this.name);
else
this:move(player);
endif
.
#7:1
":move(obj)";
what = args[1];
dest = this:dest(what);
if (!$recycler:valid(dest))
return E_INVIND;
endif
unlocked = this:is_unlocked_for(what);
if (unlocked)
"this should probably return instead?";
`dest:bless_for_entry(what) ! E_VERBNF';
endif
if (unlocked == E_RANGE)
dobj = what;
this:announce_messages("too_big");
return E_RANGE;
endif
if (unlocked && dest:acceptable(what))
start = what.location;
if (msg = this:leave_msg(what))
what:tell_lines(msg);
endif
what:moveto(dest);
if (what.location != start)
"Don't print oleave messages if WHAT didn't actually go anywhere...";
msg = this:oleave_msg(what);
start:announce_all_but({what}, msg);
endif
if (what.location != dest)
return 0;
endif
"Don't print arrive messages if WHAT didn't really end up there...";
if (msg = this:arrive_msg(what))
what:tell_lines(msg);
endif
msg = this:oarrive_msg(what);
what.location:announce_all_but({what}, msg);
return 1;
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))
what.location:announce_all_but({what}, msg);
endif
return 0;
endif
.
#7:2
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
"... relies on -d...";
this.source:remove_exit(this);
this.dest:remove_entrance(this);
return pass(@args);
else
return E_PERM;
endif
.
#7:3
if ($perm_utils:controls(cp = caller_perms(), this) || (valid(this.source) && (this.source.owner == cp)))
return (typeof(e = this.name = args[1]) != ERR) || e;
else
return E_PERM;
endif
.
#7:4
if ($perm_utils:controls(cp = caller_perms(), this) || (valid(this.source) && (this.source.owner == cp)))
if (typeof(e = this.aliases = args[1]) == ERR)
return e;
else
return 1;
endif
else
return E_PERM;
endif
.
#7:5
"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];
nargs = length(args);
last = args[nargs];
if (typeof(last) == LIST)
where:announce_all_but(whobut, @args[3..nargs - 1], last[1]);
for line in (last[2..length(last)])
where:announce_all_but(whobut, line);
endfor
else
where:announce_all_but(@args[3..nargs]);
endif
.
#7:6
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:7
if ((caller in {this, this.owner}) || $perm_utils:controls(caller_perms(), this))
return pass(@args);
else
return E_PERM;
endif
.
#7:8
"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:9
iobj = this:otherside();
{?what = player, ?exit = this, @rest} = args;
"Allow the moving object a chance to define an exit/entrance message.";
msg = (((`what:("my_" + verb)(what, exit, @rest) ! E_VERBNF' || `what:(verb)(what, exit, @rest) ! E_VERBNF') || this.(verb)) || `$exit_utils:(verb)(this) ! E_VERBNF') || "";
"What the hell, pronoun sub it again!";
return $string_utils:pronoun_sub(msg, what, exit, @rest);
.
#7:10
":obvious() -- Is the exit obvious to the current player?";
return this.obvious;
.
#7:11
":otherside()";
"-> The exit in this exit's dest that leads to this exit's source.";
"-> $nothing if there is no `otherside'.";
source = this.source;
if (valid(exit = this.otherside) && (exit.dest == source))
return exit;
elseif ((!valid(dest = this.dest)) || (!valid(source)))
return $nothing;
endif
for exit in (dest.exits)
if (exit.dest == source)
return this.otherside = exit;
endif
endfor
return $nothing;
.
#7:12
"transparency          -- How clearly an image travels through the object.";
"0                     -> opaque";
"1                     -> see-through";
"-x                    -> varying degrees of blurriness (ie: smoked glass)";
return this:obvious() && this.(verb);
.
#7:13
"sound_permeability    -- How clearly sound travels through the object.";
"0                     -> silence";
"1                     -> audible";
"-x                    -> varying degrees of distortedness";
return this.(verb);
.
#7:14
":hear_event_speech(speaker, spoken, volume)";
"Broadcast the sound to the otherside.";
if (!valid(otherside = this:otherside()))
return;
endif
speaker = args[1];
if (!speaker.ic)
return;
endif
msg = tostr($string_utils:a_or_an(gender = speaker:gender_adj() || "odd"), " ", gender, " voice from ", otherside:from_name(), " says, \"", args[2], "\"");
otherside.source:announce_all($string_utils:capitalise(msg));
.
#7:15
":hear_event_sound(origin, sound, volume)";
"Broadcast the sound to the otherside.";
if (!valid(otherside = this:otherside()))
return;
endif
msg = tostr("From ", otherside:from_name(), ": ", args[2]);
otherside.source:announce_all(msg);
.
#7:16
":hear_event_motion(actor, action, obviousness)";
"Display the given action to those on the otherside, masking the actor.";
if (!valid(otherside = this:otherside()))
return;
endif
if (args[3] > 5)
"...It's obvious who performed the action.";
action = $string_utils:pronoun_sub(args[2], args[1]);
else
action = $string_utils:pronoun_sub(args[2], $someone);
endif
msg = tostr("From ", otherside:from_name(), ": ", action);
otherside.source:announce_all(msg);
.
#7:17
":from_name(exit-name|exit-object)";
return this:dname();
return $exit_utils:from_name(this);
.
#7:18
":invoke_for_walker()";
"Handle walking and go-ing through the exit.  By default just passes to :invoke.";
return this:invoke();
.
#7:19
":is_unlocked_for_walker()";
"So exits aren't really locked for people :walking or :going through the exit, who'll be using :invoke_for_walker anyway.  By default just passes to :is_unlocked_for.";
return this:is_unlocked_for(@args, 1);
.
#7:20
"If transparent, tell contents of the otherside.";
pass(@args);
if ((this:transparency() > 0) && (c = this.dest:contents()))
player:tell("You see ", $string_utils:ititle_list(c), " on the other side.");
endif
.
#7:21
"For rare cases when these values are not constant.";
return this.(verb);
.
#7:22
":dname() -> Return 'above' for up and 'below' for down.";
if (i = this.name in {"up", "down"})
return (verb == "dnamec") ? {"Above", "Below"}[i] | {"above", "below"}[i];
else
return pass(@args);
endif
.
#7:23
":is_writable_by(cp, caller)";
"Allow the 'otherside' to write to this side.";
return pass(@args) || (this:otherside() in args);
.
#7:24
":clearance()";
"The maximum v_size of an object allowed to pass through this exit.";
return this.clearance;
.
#7:25
":set_clearance(NUM)";
"A clearance value must be an integer greater than Zero.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
clearance = args[1];
if (typeof(clearance) != NUM)
return E_TYPE;
elseif (clearance < 1)
return E_RANGE;
endif
this.clearance = clearance;
return clearance;
.
#7:26
"too_big";
"Announced when an object attempts to pass through the exit, but won't fit.";
"'dobj' is the object attempting to pass, 'player' is the one attempting to move it, and 'this' is the exit itself.";
"If 'dobj' and 'player' are the same object, then the [o]self_too_big message is returned instead.";
"Note that because dobj==player substitution is HERE, leaving @too_big as an empty string will result in uncomfortable conjugation.";
if (player == dobj)
i = index(verb, "too");
verb[i..i] = "self_t";
endif
return $string_utils:pronoun_sub(this.(verb), @args);
.
#7:27
":check_clearance(obj)";
"Can the given object make it through this exit?";
return args[1]:v_size() < this:clearance();
.
#7:28
":is_unlocked_for(obj)";
what = args[1];
return pass(what) && (this:check_clearance(what) || E_RANGE);
.
#7:29
"Show source and dest.";
su = $string_utils;
player:tell("--", su:nn(this.source), " --> ", su:nn(this), " --> ", su:nn(this.dest));
.
#7:30
"Return the appropriate property for the :otherside of this exit.";
if (valid(otherside = this:otherside()))
return otherside:(verb[2..$])(@args);
else
return $exit_utils:otherside_name(this);
endif
.
#7:31
":set_dest/source(OBJ exit)";
"Set this exit's source/destination.";
"Wizardly, but shouldn't be.  Don't want to break all the code which sets these properties directly by chowning them to $hacker.";
if (!this:is_writable_by(caller_perms(), caller))
raise(E_PERM);
endif
this.(verb[5..$]) = args[1];
"'Dirty' otherside so it's reset with the next :otherside call.";
this.otherside = #-1;
.
#7:32
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
this.description = "You see nothing special.";
return pass(@args);
.
#8:0
"put <anything> in this -- Put something in the container.";
"The dobj may define an optional 'put_refused' message which is displayed when it does not want to go into the container.";
if ((this.location != player) && (this.location != player.location))
player:tell("You can't get at ", this:dname(), ".");
return;
elseif (closed = !this.opened)
this:do_auto_open();
if (!this.opened)
player:tell(this:dnamec(), " is closed.");
return;
endif
endif
e = setremove(player:matching_contents(), this);
set_task_perms(callers() ? caller_perms() | player);
dobj = $match_utils:match_environment(dobjstr, e);
if (dobj == $nothing)
player:tell(("What is it you want to " + verb) + " in there?");
elseif (dobj == $ambiguous_match)
all = $match_utils:match_list(dobjstr, e);
player:tell("You rummage around in ", this:dname(), ", but can't make out which \"", dobjstr, "\" is the right one: ", $string_utils:name_list(all, "nothing", " or ") + ".");
elseif (!$recycler:valid(dobj))
player:tell(((("You don't have a \"" + dobjstr) + "\" to ") + verb) + " in there.");
elseif (!this:acceptable(dobj))
this:announce_messages("put_fail");
else
dobj:moveto(this);
if ($code_utils:call_verb(dobj, "was_moved", {player, this}) || (dobj.location == this))
this:announce_messages("put");
elseif (!dobj:announce_messages("put_refused"))
this:announce_messages("put_fail");
endif
endif
if (closed)
this:do_auto_close();
endif
.
#8:1
"remove <anything> from this -- Remove something from the container.";
"The dobj may define an optional 'remove_refused' message which is displayed when it does not want to go into the container.";
if ((this.location != player) && (this.location != player.location))
player:tell("You can't get at ", this:dname(), " to do that.");
return;
elseif (!prepstr)
return pass(@args);
elseif (closed = !this.opened)
this:do_auto_open();
if (!this.opened)
player:tell(this:dnamec(), " is closed.");
return;
endif
elseif (0 && this.dark)
player:tell("You can't see into ", this:dname(), " to remove anything.");
return;
endif
e = this:matching_contents();
dobj = $match_utils:match_environment(dobjstr, e);
set_task_perms(callers() ? caller_perms() | player);
if (dobj == $nothing)
player:tell(("What is it you want to " + verb) + " from it?");
elseif (dobj == $failed_match)
player:tell("You can't find a \"", dobjstr, "\" to ", verb, " in there.");
elseif (dobj == $ambiguous_match)
all = $match_utils:match_list(dobjstr, e);
player:tell("You rummage around in ", this:dname(), ", but can't make out which \"", dobjstr, "\" is the right one: ", $string_utils:name_list(all, "nothing", " or ") + ".");
elseif (!(dobj in this:matching_contents()))
player:tell(dobj:dnamec(), " isn't inside ", this:dname(), ".");
elseif (!this:acceptable(dobj))
this:announce_messages("remove_fail");
else
dobj:moveto(player);
if ($code_utils:call_verb(dobj, "was_moved", {this, player}) || (dobj.location == player))
this:announce_messages("remove");
elseif (!dobj:announce_messages("remove_refused"))
this:announce_messages("remove_fail");
endif
endif
if (closed)
this:do_auto_close();
endif
.
#8:2
{what} = args;
if ($object_utils:isa(what, $character))
return E_INVARG;
elseif (`what.v_size ! E_PROPNF => 0' > this.v_size)
return E_QUOTA;
endif
return 1;
.
#8:3
if (this.opened)
player:tell("It's already open.");
elseif (this:is_openable_by(callers() ? caller_perms() | player))
this:set_opened(1);
this:announce_messages("open");
else
this:announce_messages("open_fail");
endif
.
#8: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
res = this.open_key = key;
if (typeof(res) == ERR)
player:tell(res, ".");
else
player:tell("Locked opening of ", this.name, " with this key:");
player:tell("  ", $lock_utils:unparse_key(key));
endif
endif
.
#8:5
return !this:get_lock_status();
"@rmprop $container.open_key";
return (this.open_key == 0) || $lock_utils:eval_key(this.open_key, args[1]);
.
#8:6
if (!this.opened)
player:tell("It's already closed.");
else
this:set_opened(0);
this:announce_messages("close");
endif
.
#8:7
set_task_perms(player);
res = dobj.open_key = 0;
if (typeof(res) == ERR)
player:tell(res, ".");
else
player:tell("Unlocked ", dobj.name, " for opening.");
endif
.
#8:8
if (this.dark)
player:tell("You can't see what's inside.");
return;
endif
c = this:contents();
if (!c)
msg = this:empty_msg();
msg && player:tell(msg);
return;
endif
player:tell("Contents:");
c = $list_utils:map_verb(c, "title");
if (n = this.ctype)
c = $string_utils:smart_columnize(c, this.ctype);
endif
for l in (c)
player:tell("  ", l);
endfor
.
#8:9
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:10
if (!$perm_utils:controls(player, this))
player:tell("Can't set opacity of something you don't own.");
elseif ((iobjstr != "0") && (!tonum(iobjstr)))
player:tell("Opacity must be an integer (0, 1, 2).");
else
player:tell("Opacity changed:  Now " + {"transparent.", "opaque.", "a black hole."}[1 + this:set_opaque(tonum(iobjstr))]);
endif
.
#8:11
if (!$perm_utils:controls(caller.owner, this))
return E_PERM;
elseif (typeof(number = args[1]) != NUM)
return E_INVARG;
else
number = (number < 0) ? 0 | ((number > 2) ? 2 | number);
this.dark = number > this.opened;
return this.opaque = number;
endif
.
#8:12
return (msg = this.(verb)) ? $string_utils:pronoun_sub(msg) | "";
.
#8:13
":do_auto_close()";
"Have the current player close the container.";
if (this.opened)
dobj = this;
this:close(dobjstr = this.name);
endif
.
#8:14
":do_auto_open()";
"Have the current player open the container.";
if (!this.opened)
dobj = this;
this:open(dobjstr = this.name);
endif
.
#8:15
":do_lock(this, who, what)";
if (this.opened)
args[2]:tell(tostr("Shouldn't you close ", this:dname(), " before trying to lock it?"));
else
return pass(@args);
endif
.
#8:16
"examine_contents(examiner)";
examiner = args[1];
isbuilder = $wiz_utils:is_builder(examiner);
if (isbuilder)
return pass(@args);
elseif (this.dark)
return {};
endif
STRUTILS = $string_utils;
visible = {};
for c in (this:contents())
visible = {@visible, "  " + c:name()};
endfor
return visible ? {"Contents:", @visible} | {"(Contains nothing.)"};
.
#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.");
return E_PERM;
endif
if ((this.ic && (!$wiz_utils:is_builder(player))) && (!player:is_local_to(this)))
player:tell("Sorry, but you can't read it from where you are.");
return E_RANGE;
endif
player:tell_lines(this:description());
player:tell("");
for line in (this:text())
player:tell(line);
$command_utils:suspend_if_needed(0);
endfor
player:tell("");
player:tell("(You finish reading.)");
.
#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 = tonum(dobjstr);
if (line < 0)
line = (line + length(this.text)) + 1;
endif
if ((line <= 0) || (line > length(this.text)))
player:tell("Line out of range.");
else
this:set_text(listdelete(this.text, line));
player:tell("Line deleted.");
endif
endif
.
#9:4
set_task_perms(player);
key = $lock_utils:parse_keyexp(iobjstr, player);
if (typeof(key) == STR)
player:tell("That key expression is malformed:");
player:tell("  ", key);
else
res = this.encryption_key = key;
if (typeof(res) == ERR)
player:tell(res, ".");
else
player:tell("Encrypted ", this.name, " with this key:");
player:tell("  ", $lock_utils:unparse_key(key));
endif
endif
.
#9:5
set_task_perms(player);
res = dobj.encryption_key = 0;
if (typeof(res) == ERR)
player:tell(res, ".");
else
player:tell("Decrypted ", dobj.name, ".");
endif
.
#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
key = this.encryption_key;
return (!key) || $lock_utils:eval_key(key, args[1]);
.
#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;
else
return E_TYPE;
endif
else
return E_PERM;
endif
.
#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))
player:tell("Sorry, but it seems to be written in some code that you can't read.");
elseif ((this.ic && (!$wiz_utils:is_builder(player))) && (!player:is_local_to(this)))
player:tell("Sorry, but you can't read it from where you are.");
return E_RANGE;
elseif (!player.email_address)
player:tell("Sorry, you don't have a registered email address.");
return;
elseif (!$network.active)
player:tell("Sorry, internet mail is disabled.");
elseif (!(text = this:text()))
player:tell($string_utils:pronoun_sub("%T is empty--there wouldn't be any point to mailing it."));
return;
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
pass(@args);
if (this.auto_read)
player:tell_lines({"", "(You begin reading...)", ""});
player:tell_lines(this:text());
player:tell_lines({"", "(You finish reading.)", ""});
endif
.
#10:0
if (caller != #0)
return E_PERM;
else
clist = {};
for i in [0..length(verbs(this)) - 1]
if ((verb_args(this, tostr(i)) == {"any", "none", "any"}) && index((info = verb_info(this, tostr(i)))[2], "x"))
vname = $string_utils:explode(info[3])[1];
star = index(vname + "*", "*");
clist = {@clist, $string_utils:uppercase(vname[1..star - 1]) + strsub(vname[star..length(vname)], "*", "")};
endif
endfor
notify(player, "Valid commands at this point are");
notify(player, "   " + $string_utils:english_list(setremove(clist, "?"), "", " or "));
return 0;
endif
.
#10:1
if (caller != #0)
return E_PERM;
else
$player:("@WHO")();
endif
return 0;
.
#10:2
you_lose_msg = "Either that player does not exist, or has a different password.";
if (caller != #0)
return E_PERM;
"...caller isn't :do_login_command...";
elseif (!(length(args) in {1, 2}))
notify(player, tostr("Usage:  ", verb, " <existing-player-name> <password>"));
return E_ARGS;
elseif (!valid(candidate = orig_candidate = this:_match_player(name = strsub(args[1], " ", "_"))))
notify(player, you_lose_msg);
"...unknown player...";
return E_INVARG;
elseif (this:attempt_shared_login(candidate, {@args, ""}[2]))
"got one!";
elseif ((typeof(candidate.password) == STR) && ((length(candidate.password) < 2) || (crypt({@args, ""}[2], candidate.password[1..2]) != candidate.password)))
notify(player, you_lose_msg);
"...bad password...";
server_log(tostr("FAILED CONNECT: ", args[1], " (", candidate, ") on ", connection_name(player), ($string_utils:connection_hostname(connection_name(player)) in candidate.all_connect_places) ? "" | "******"));
return E_PERM;
elseif ((parent(candidate) == $guest) && (!valid(candidate = candidate:defer())))
notify(player, (candidate == #-2) ? "Sorry, guest characters are not allowed from your site." | "Sorry, all of our guest characters are in use right now.");
return E_NACC;
endif
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;
.
#10:3
if (caller != #0)
return E_PERM;
"... caller isn't :do_login_command()...";
elseif (!this:player_creation_enabled(player))
notify(player, this:registration_string());
"... we've disabled player creation ...";
elseif (length(args) != 2)
notify(player, tostr("Usage:  ", verb, " <new-player-name> <new-password>"));
elseif ($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[length(name)] == ">"))
notify(player, "Try that again but without the angle brackets, e.g.,");
notify(player, tostr(" ", verb, " ", name[2..length(name) - 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 = 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_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:4
if (caller != #0)
return E_PERM;
else
boot_player(player);
return 0;
endif
.
#10:5
if (caller != #0)
return E_PERM;
else
notify(player, tostr("The server has been up for ", $time_utils:english_time(time() - $last_restart_time), "."));
return 0;
endif
.
#10:6
if (caller != #0)
return E_PERM;
else
notify(player, tostr("The MOO is currently running version ", server_version(), " of the LambdaMOO server code."));
return 0;
endif
.
#10:7
":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)
return E_PERM;
elseif (!args)
return {this.blank_command, @args};
endif
if ((verb = args[1]) && ((verb_args(this, verb) == {"any", "none", "any"}) && index(verb_info(this, verb)[2], "x")))
this:user_approached(player, @args);
return args;
else
return {this.bogus_command, @args};
endif
.
#10:8
when = $shutdown_time - time();
if (when >= 0)
line = "***************************************************************************";
notify(player, "");
notify(player, "");
notify(player, line);
notify(player, line);
notify(player, "****");
notify(player, ("****  WARNING:  The server will be going down in about " + $string_utils:from_seconds(when)) + ".");
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:9
if ($player_db.frozen)
line = "***************************************************************************";
notify(player, "");
notify(player, line);
notify(player, "***");
for piece in ($generic_editor:fill_string("The character-name matcher is currently being reloaded.  This means your character name might not be recognized even though it still exists.  Don't panic.  You can either wait for the reload to finish or you can connect using your object number if you remember it (e.g., `connect #1234 yourpassword').", 65))
notify(player, "***       " + piece);
endfor
if (this:player_creation_enabled(player))
notify(player, "***       This also means that character creation is disabled.");
endif
notify(player, "***");
notify(player, line);
notify(player, "");
endif
.
#10:10
":_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:11
set_task_perms(caller_perms());
notify(player, args[1]);
.
#10:12
"keeps bad things from happening if someone brings this object into a room and talks to it.";
return 0;
.
#10:13
"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:14
return $string_utils:subst(this.(verb), {{"%e", this.registration_address}, {"%%", "%"}});
.
#10:15
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.bogus_command = "?";
this.blank_command = "welcome";
this.create_enabled = 1;
this.registration_address = "";
this.registration_string = "Character creation is disabled.";
this.newt_registration_string = "Your character is temporarily hosed.";
this.welcome_message = {"Welcome to the GhostCore database.", "", "Type 'connect god' to log in.", "", "You will probably want to change this text, which is stored in $login.welcome_message."};
this.redlist = this.blacklist = this.graylist = this.spooflist = {{}, {}};
this.ignored = {};
this.who_masks_wizards = 0;
if ("monitor" in properties(this))
delete_property(this, "monitor");
endif
if ("monitor" in verbs(this))
delete_verb(this, "monitor");
endif
if ("special_action" in verbs(this))
set_verb_code(this, "special_action", {});
endif
this.connected = this.special_connected = {};
this.login_listeners = {player};
.
#10:16
.
#10:17
":blacklisted(hostname) => is hostname on the .blacklist";
":graylisted(hostname)  => is hostname on the .graylist";
":redlisted(hostname)   => is hostname on the .redlist";
sitelist = this.(this:listname(verb));
if (!caller_perms().wizard)
return E_PERM;
elseif (((hostname = args[1]) in sitelist[1]) || (hostname in sitelist[2]))
return 1;
elseif ($site_db:domain_literal(hostname))
for lit in (sitelist[1])
if ((index(hostname, lit) == 1) && ((hostname + ".")[length(lit) + 1] == "."))
return 1;
endif
endfor
else
for dom in (sitelist[2])
if (index(dom, "*"))
"...we have a wildcard; let :match_string deal with it...";
if ($string_utils:match_string(hostname, dom))
return 1;
endif
else
"...tail of hostname ...";
if ((r = rindex(hostname, dom)) && ((("." + hostname)[r] == ".") && (((r - 1) + length(dom)) == length(hostname))))
return 1;
endif
endif
endfor
endif
return 0;
.
#10:18
if (!caller_perms().wizard)
return E_PERM;
endif
where = args[1];
lname = this:listname(verb);
which = 1 + (!$site_db:domain_literal(where));
this.(lname)[which] = setadd(this.(lname)[which], where);
return 1;
.
#10:19
if (!caller_perms().wizard)
return E_PERM;
endif
where = args[1];
lname = this:listname(verb);
which = 1 + (!$site_db:domain_literal(where));
if (where in this.(lname)[which])
this.(lname)[which] = setremove(this.(lname)[which], where);
return 1;
else
return E_INVARG;
endif
.
#10:20
return {"???", "blacklist", "graylist", "redlist", "spooflist"}[1 + index("bgrs", (args[1] || "?")[1])];
.
#10:21
":record_connection(plyr) update plyr's connection information";
"to reflect impending login.";
if (!caller_perms().wizard)
return E_PERM;
else
plyr = args[1];
plyr.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(length(acp), 15)]};
if (parent(plyr) != $guest)
$site_db:add(plyr, chost);
endif
endif
.
#10:22
":spooflisted(hostname) => is hostname on the .spooflist";
"The return value, if true, is actually a domain (or site) name.";
sitelist = this.(this:listname(verb));
if (!caller_perms().wizard)
return E_PERM;
elseif (((hostname = args[1]) in sitelist[1]) || (hostname in sitelist[2]))
return hostname;
elseif ($site_db:domain_literal(hostname))
for lit in (sitelist[1])
if ((index(hostname, lit) == 1) && ((hostname + ".")[length(lit) + 1] == "."))
return lit;
endif
endfor
else
for dom in (sitelist[2])
if (index(dom, "*"))
"...we have a wildcard; let :match_string deal with it...";
if ($string_utils:match_string(hostname, dom))
return dom;
endif
else
"...tail of hostname ...";
if ((r = rindex(hostname, dom)) && ((("." + hostname)[r] == ".") && (((r - 1) + length(dom)) == length(hostname))))
return dom;
endif
endif
endfor
endif
return 0;
.
#10:23
":user_*(user) -> Relay info to login listeners.";
if ((caller != #0) && (caller != this))
return E_PERM;
endif
user = args[1];
argstr = dobjstr = iobjstr = prepstr = "";
for object in (this.login_listeners)
`object:(verb)(user) ! ANY';
endfor
.
#10:24
"Return a random banner of those saved in welcome_message.";
msg = this.welcome_message;
if (msg && (typeof(msg[1]) == LIST))
return $list_utils:random_element(msg);
else
return msg;
endif
.
#10:25
if (caller != #0)
return E_PERM;
endif
msg = this:welcome_message();
for line in ((typeof(msg) == LIST) ? msg | {msg})
if (typeof(line) == STR)
notify(player, line);
endif
endfor
if (msg = this.extra_login_msg)
notify(player, "");
notify(player, msg);
notify(player, "");
endif
this:check_player_db();
this:check_for_shutdown();
return 0;
.
#10:26
":connected()";
"-> Who wants to be shown as connected.";
"Always show children of $guest, regardless of whether they're signed in or not.";
opt = $privacy_options;
if (args)
who = args[1];
return $object_utils:connected(who) && `who:public_who() ! ANY => 1';
endif
con = {};
for c in (connected_players())
if (`c:public_who() ! ANY => 1')
con = {@con, c};
endif
endfor
return con;
.
#10:27
if ($code_utils:task_valid(this.lag_task))
return E_MAXREC;
endif
this.lag_task = task_id();
while (1)
this:do_lag_sample();
endwhile
.
#10:28
"estimate current lag as max of most recent sample and average of the rest of the samples";
thislag = max(0, (time() - this.last_lag_sample) - this.lag_sample_interval);
if (thislag > (60 * 10))
"more than 10 minutes, probably the lag sampler stopped";
return -1;
endif
samples = this.lag_samples;
thislag = max(thislag, samples[1]);
sum = 0;
cnt = 0;
for x in (listdelete(samples, 1))
sum = sum + x;
cnt = cnt + 1;
endfor
return max(thislag, samples[1], sum / cnt);
.
#10:29
":special_connected()";
"A list of non-player objects which are faking a 'connected' state.";
"Each much have a :connected_seconds property that returns a number greater than -1, and each shoudl also have an :idle_seconds property, though that isn't checked here.";
special = {};
for c in (this.special_connected)
if (c:connected_seconds() > -1)
special = {@special, c};
endif
endfor
return special;
.
#10:30
":do_lag_sample()";
if (caller != this)
return E_PERM;
endif
this.last_lag_sample = now = time();
suspend(i = this.lag_sample_interval);
lag = (time() - now) - i;
this.lag_samples = {lag, @this.lag_samples[1..3]};
.
#10:31
":do_login(OBJ shared_character, STR given_password)";
if (caller != this)
return raise(E_PERM);
endif
{shared, clear_password} = args;
if (!$object_utils:isa(shared, $shared_player))
return 0;
endif
for user in (shared.users)
if (!$wiz_utils:is_builder(user))
"... not at least a builder anymore, do some cleanup ...";
shared.users = setremove(shared.users, user);
continue;
elseif (!$object_utils:connected(user))
"... user is not connected ...";
continue;
endif
user_password = user.password;
if (((typeof(user_password) == STR) && (length(user_password) > 1)) && (crypt(clear_password, user_password[1..2]) == user_password))
"... success! ...";
shared.last_user = user;
if (shared.toggle_progbit)
shared.programmer = user.programmer;
endif
return 1;
endif
endfor
return 0;
.
#11:0
"@msg_name object is [message_text]";
"       If message_text is given and msg_name is a valid message, set msg_name to that value.  If message_text is not given, show the current value of the msg_name.";
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..length(verb)];
if (info = $match_utils:parse_possessive_reference(msg_name))
msg_name = dobjstr;
dobjstr = info[1];
endif
dobj = player:my_match_object(dobjstr, e = player:env());
if ($match_utils:object_match_failed(dobj, dobjstr, e))
return;
endif
if (info && dobj:set_my(msg_name, iobjstr))
return;
endif
su = $string_utils;
if (pos == nargs)
get = dobj:get_message(msg_name);
if (get == E_PROPNF)
player:notify(tostr(su:nn(dobj), " has no \"", msg_name, "\" message."));
elseif (get == E_PERM)
player:notify(tostr("The \"", msg_name, "\" message of ", su:nn(dobj), " is not readable by you."));
elseif ((t = typeof(get)) == ERR)
player:notify(tostr(get));
elseif (!get)
player:notify("Message is not set.");
else
v = (t == LIST) ? "a list." | $string_utils:print(get);
player:notify(tostr("@", msg_name, " ", dobjstr, " is ", v));
endif
else
set = dobj:set_message(msg_name, message);
if (set)
if (typeof(set) == STR)
player:notify(set);
else
vr_msg = $vr_core:message_warning(player);
player:notify(tostr("You set the \"", msg_name, "\" message of ", su:nn(dobj), "." + (vr_msg && ("  " + vr_msg))));
endif
elseif (set == E_PROPNF)
player:notify(tostr(su:nn(dobj), " has no \"", msg_name, "\" message to set."));
elseif (set == E_PERM)
player:notify(tostr("You aren't allowed to set the \"", msg_name, "\" message of ", su:nn(dobj), "."));
elseif (typeof(set) == ERR)
player:notify(tostr(set));
else
vr_msg = $vr_core:message_warning(player);
player:notify(tostr("You blank the \"", msg_name, "\" message of ", su:nn(dobj), "." + (vr_msg && ("  " + vr_msg))));
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:accept(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
.
#11:4
":call_social_verb(verb, args)";
"If the verb is one of this.social_verbs, then redirect the task to the pose feature.";
verb = args[1];
args = args[2];
if (verb in this.social_verbs)
set_task_perms(caller_perms());
$pose_feature:("." + verb)(@args);
if (random(50 / (prepstr ? 1 | 2)) == 1)
player:tell("(See `help pose` for details on extended use of the pose verb.)");
endif
return 1;
else
return 0;
endif
.
#12:0
":enter(who,islogin,time,site)";
"adds an entry to the connection log for a given guest (caller).";
if ($object_utils:isa(caller, $guest))
$guest_log.connections = {{caller, @args}, @$guest_log.connections[1..min(99, length($guest_log.connections))]};
else
return E_PERM;
endif
.
#12:1
set_task_perms(caller_perms());
howmany = min({@args, 0}[1] || $maxint, length($guest_log.connections));
if (!caller_perms().wizard)
player:notify("Sorry.");
else
current = {};
listing = {};
last = 0;
for c in ($guest_log.connections[1..howmany])
if (c[2])
"...login...";
if (a = $list_utils:assoc(c[1], current))
listing[a[2]][3] = c[3];
current = setremove(current, a);
else
listing = {@listing, {c[1], c[4], c[3], -idle_seconds(c[1])}};
last = last + 1;
endif
else
"...logout...";
listing = {@listing, {c[1], c[4], 0, c[3]}};
last = last + 1;
if (i = $list_utils:iassoc(c[1], current))
current[i][2] = last;
else
current = {@current, {c[1], last}};
endif
endif
endfor
su = $string_utils;
player:notify(su:left(su:left(su:left("Guest", 20) + "Connected", 36) + "Idle/Disconn.", 52) + "From");
player:notify(su:left(su:left(su:left("-----", 20) + "---------", 36) + "-------------", 52) + "----");
for l in (listing)
on = l[3] ? (ct = ctime(l[3]))[1..3] + ct[9..19] | "earlier";
off = (l[4] > 0) ? (ct = ctime(l[4]))[1..3] + ct[9..19] | ("  " + $string_utils:from_seconds(-l[4]));
player:notify(su:left(su:left(su:right(tostr(strsub(l[1].name, "uest", "."), " (", l[1], ")  "), 20) + on, 36) + off, 52) + l[2]);
endfor
endif
.
#12:2
if (caller_perms().wizard)
pass(@args);
this.connections = {};
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;
.
#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 = args[1];
tree = args[2];
n = args[3];
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,comp) ";
" => index of rightmost leaf for which :(comp)(n,:_ord(leaf)) is false.";
"returns 0 if true for all leaves.";
if (caller != this)
return E_PERM;
endif
home = args[1];
tree = args[2];
n = args[3];
comp = args[4];
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 (!home:(comp)(n, k[3]))
return sz + this:_find_ord(home, k, n, comp);
endif
endfor
return 0;
else
for i in [1..r = length(p[2])]
if (home:(comp)(n, home:_ord(p[2][i])))
return i - 1;
endif
endfor
return r;
endif
.
#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 = args[1];
tree = args[2];
n = args[3];
value = args[4];
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,leafverb)";
"home:_kill's node and all descendants, home:(leafverb)'s all leaves";
if (caller != this)
return E_PERM;
endif
home = args[1];
hgn = home:_get(node = args[2]) || {0, {}};
lverb = args[3];
if (hgn[1])
for kid in (hgn[2])
this:_skill(home, kid[1], lverb);
endfor
elseif (lverb)
for kid in (hgn[2])
home:(lverb)(kid);
endfor
endif
home:_kill(node);
.
#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 = args[1];
lnode = args[2];
rnode = args[3];
lh = home:_get(lnode[1])[1];
rh = home:_get(rnode[1])[1];
if (lh > rh)
return this:_rmerge(home, lnode, rnode);
endif
for h in [lh + 1..rh]
lnode[1] = home:_make(h, {lnode});
endfor
m = this:_smerge(home, rh, lnode, rnode);
return (length(m) <= 1) ? m[1] | {home:_make(rh + 1, m), m[1][2] + m[2][2], m[1][3]};
.
#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 = args[1];
height = args[2];
ltree = args[3];
rtree = args[4];
llen = length(lkids = home:_get(ltree[1])[2]);
rlen = length(rkids = home:_get(rtree[1])[2]);
if (height)
m = this:_smerge(home, height - 1, lkids[llen], rkids[1]);
mlen = length(mkids = {@listdelete(lkids, llen), @m, @listdelete(rkids, 1)});
if (mlen <= this.maxfanout)
home:_put(ltree[1], height, mkids);
home:_kill(rtree[1]);
ltree[2] = ltree[2] + rtree[2];
return {ltree};
else
S = max(llen - 1, (mlen + 1) / 2);
home:_put(ltree[1], height, mkids[1..S]);
home:_put(rtree[1], height, mkids[S + 1..length(mkids)]);
xfer = -lkids[llen][2];
for k in (mkids[llen..S])
xfer = xfer + k[2];
endfor
ltree[2] = ltree[2] + xfer;
rtree[2] = rtree[2] - xfer;
rtree[3] = mkids[S + 1][3];
return {ltree, rtree};
endif
elseif ((llen * 2) >= this.maxfanout)
return {ltree, rtree};
elseif (this.maxfanout < (llen + rlen))
T = ((rlen - llen) + 1) / 2;
home:_put(ltree[1], 0, {@lkids, @rkids[1..T]});
home:_put(rtree[1], 0, rkids[T + 1..rlen]);
ltree[2] = ltree[2] + T;
rtree[2] = rtree[2] - T;
rtree[3] = home:_ord(rkids[T + 1]);
return {ltree, rtree};
else
home:_put(ltree[1], 0, {@lkids, @rkids});
home:_kill(rtree[1]);
ltree[2] = ltree[2] + rtree[2];
return {ltree};
endif
.
#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 = args[1];
height = args[2];
lmax = args[3];
ltree = args[4];
rtrees = args[5..length(args)];
llen = length(lkids = home:_get(ltree[1])[2]);
rlen = length(rkids = rtrees ? home:_get(rtrees[1][1])[2] | {});
if (height)
ik = this:_listfind_nth(lkids, lmax);
if (ik[2])
llast = ik[1];
m = this:_split(home, height - 1, ik[2], lkids[llast], @lkids[llast + 1..llen], @rkids);
lkids[llast] = m[1];
mkids = listdelete(m, 1);
else
llast = ik[1] - 1;
mkids = {@lkids[ik[1]..llen], @rkids};
endif
home:_put(ltree[1], height, lkids[1..llast]);
mlen = length(mkids);
if ((((mlen - rlen) * 2) >= this.maxfanout) || (!rtrees))
"...residue left over from splitting ltree can stand by itself...";
return {listset(ltree, lmax, 2), {home:_make(height, mkids[1..mlen - rlen]), ltree[2] - lmax, mkids[1][3]}, @rtrees};
elseif (mlen <= this.maxfanout)
"...residue left over from splitting ltree fits in rtrees[1]...";
home:_put(rtrees[1][1], height, mkids);
rtrees[1][2] = (ltree[2] - lmax) + rtrees[1][2];
rtrees[1][3] = mkids[1][3];
return {listset(ltree, lmax, 2), @rtrees};
else
"...need to steal from rtrees[1]...";
if (llast < llen)
msize = ltree[2] - lmax;
R = (mlen - rlen) + 1;
else
msize = 0;
R = 1;
endif
for k in (mkids[R..mlen / 2])
msize = msize + k[2];
endfor
home:_put(rtrees[1][1], height, mkids[(mlen / 2) + 1..mlen]);
rtrees[1][2] = (rtrees[1][2] + ltree[2]) - (lmax + msize);
rtrees[1][3] = mkids[(mlen / 2) + 1][3];
return {listset(ltree, lmax, 2), {home:_make(height, mkids[1..mlen / 2]), msize, mkids[1][3]}, @rtrees};
endif
else
home:_put(ltree[1], 0, lkids[1..lmax]);
if ((((llen - lmax) * 2) >= this.maxfanout) || (!rtrees))
"...residue left over from splitting ltree can stand by itself...";
return {listset(ltree, lmax, 2), {home:_make(0, lkids[lmax + 1..llen]), llen - lmax, home:_ord(lkids[lmax + 1])}, @rtrees};
elseif ((mlen = (rlen + llen) - lmax) <= this.maxfanout)
"...residue left over from splitting ltree fits in rtrees[1]...";
home:_put(rtrees[1][1], 0, {@lkids[lmax + 1..llen], @rkids});
rtrees[1][2] = mlen;
rtrees[1][3] = home:_ord(lkids[lmax + 1]);
return {listset(ltree, lmax, 2), @rtrees};
else
"...need to steal from rtrees[1]...";
home:_put(rtrees[1][1], 0, rkids[(R = ((rlen - llen) + lmax) / 2) + 1..rlen]);
rtrees[1][2] = (mlen + 1) / 2;
rtrees[1][3] = home:_ord(rkids[R + 1]);
return {listset(ltree, lmax, 2), {home:_make(0, {@lkids[lmax + 1..llen], @rkids[1..R]}), mlen / 2, home:_ord(lkids[lmax + 1])}, @rtrees};
endif
endif
.
#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 = args[1];
tree = args[2];
insert = args[3];
if (!tree)
return insert;
elseif (!insert)
return tree;
endif
iheight = home:_get(insert[1])[1];
rspine = {};
for i in [iheight + 1..home:_get(tree[1])[1]]
kids = home:_get(tree[1])[2];
tlen = length(kids);
rspine = {{tree, tlen}, @rspine};
tree = kids[tlen];
endfor
isize = insert[2];
m = this:_smerge(home, iheight, tree, insert);
for h in [1..length(rspine)]
plen = rspine[h][2];
parent = rspine[h][1];
hgp = home:_get(parent[1]);
if (((length(m) - 1) + plen) > this.maxfanout)
home:_put(parent[1], @listset(hgp, listset(hgp[2], m[1], plen), 2));
parent[2] = (parent[2] + isize) - m[2][2];
m = {parent, listset(m[2], home:_make(h + iheight, {m[2]}), 1)};
else
home:_put(parent[1], @listset(hgp, {@hgp[2][1..plen - 1], @m}, 2));
for p in (rspine[h + 1..length(rspine)])
parent[2] = parent[2] + isize;
tree = parent;
parent = p[1];
hgp = home:_get(parent[1]);
home:_put(parent[1], @listset(hgp, listset(hgp[2], tree, p[2]), 2));
endfor
return listset(parent, parent[2] + isize, 2);
endif
endfor
return {home:_make((length(rspine) + iheight) + 1, m), m[1][2] + m[2][2], m[1][3]};
.
#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 = args[1];
key = args[2];
for i in [1..length(lst)]
key = key - lst[i][2];
if (0 > key)
return {i, key + lst[i][2]};
endif
endfor
return {length(lst) + 1, key};
.
#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;
.
#14:0
gp = this._genprop;
ngp = "";
for i in [1..length(gp)]
if (gp[i] != "z")
ngp = (ngp + "bcdefghijklmnopqrstuvwxyz"[index("abcdefghijklmnopqrstuvwxy", gp[i])]) + gp[i + 1..length(gp)];
return " " + (this._genprop = ngp);
endif
ngp = ngp + "a";
endfor
return " " + (this._genprop = ngp + "a");
.
#14:1
":_make(...) => new node with value {...}";
if (!(caller in {this._mgr, this}))
return E_PERM;
endif
prop = this:_genprop();
add_property(this, prop, args, {this.mowner, ""});
return prop;
.
#14:2
":_kill(node) destroys the given node.";
if (!(caller in {this, this._mgr}))
return E_PERM;
endif
delete_property(this, args[1]);
.
#14:3
return (caller == this._mgr) ? this.(args[1]) | E_PERM;
.
#14:4
return (caller == this._mgr) ? this.(args[1]) = listdelete(args, 1) | E_PERM;
.
#14:5
return args[1][2..3];
.
#14:6
":_makemsg(ord,msg) => leafnode for msg";
"msg = $mail_agent:__convert_new(@args[2])";
msg = args[2];
if (caller != this)
return E_PERM;
elseif (h = "" in msg)
return {this:_make(@msg[h + 1..length(msg)]), args[1], @msg[1..h - 1]};
else
return {0, args[1], @msg};
endif
.
#14:7
if (caller != this._mgr)
return E_PERM;
elseif (node = args[1][1])
this:_kill(node);
endif
.
#14:8
return args[2];
.
#14:9
return args[3];
.
#14:10
return args[3..length(args)];
.
#14:11
return {@args[3..length(args)], @args[1] ? {"", @this.(args[1])} | {}};
.
#14:12
return args[1] < args[2][1];
.
#14:13
return args[1] < args[2][2];
.
#14:14
if (!this:is_writable_by(caller_perms()))
return E_PERM;
else
new = this:new_message_num();
msgtree = this.messages;
for m in (args)
msgtree = this._mgr:insert_last(msgtree, this:_makemsg(new, m[2]));
new = new + 1;
if ($command_utils:running_out_of_time())
this.messages = msgtree;
player:tell("... ", new);
suspend(0);
msgtree = this.messages;
new = this:new_message_num();
endif
endfor
this.messages = msgtree;
this.last_used_time = time();
return 1;
endif
.
#14:15
if (!this:is_writable_by(caller_perms()))
return E_PERM;
else
this.messages = this._mgr:insert_last(this.messages, msg = this:_makemsg(new = this:new_message_num(), args[1]));
this.last_msg_date = this:_message_date(@msg);
this.last_used_time = time();
return new;
endif
.
#14:16
if (!this:ok(caller, caller_perms()))
return E_PERM;
elseif (typeof(seq = args[1]) != LIST)
x = this._mgr:find_nth(this.messages, seq);
return {this:_message_num(@x), this:_message_text(@x)};
else
msgs = {};
while (seq)
handle = this._mgr:start(this.messages, seq[1], seq[2] - 1);
while (handle)
for x in (handle[1])
msgs = {@msgs, {this:_message_num(@x), this:_message_text(@x)}};
endfor
handle = this._mgr:next(@listdelete(handle, 1));
$command_utils:suspend_if_needed(0);
endwhile
seq = seq[3..length(seq)];
endwhile
return msgs;
endif
.
#14:17
":display_seq_headers(msg_seq[,cur[,last_read_date]])";
"This is the default header display routine.";
"Prints a list of headers of messages on this to player.  msg_seq is the handle returned by this:parse_message_seq(...).  cur is the player's current message.  last_read_date is the date of the last of the already-read messages.";
if (!this:ok(caller, caller_perms()))
return E_PERM;
endif
getmsg = this.summary_uses_body ? "_message_text" | "_message_hdr";
seq = args[1];
cur = {@args, 0}[2];
last_old = {@args, $maxint, $maxint}[3];
width = player:linelen();
while (seq)
handle = this._mgr:start(this.messages, seq[1], seq[2] - 1);
while (handle)
for x in (handle[1])
$command_utils:suspend_if_needed(0);
line = tostr($string_utils:right(x[2], 4, (cur == x[2]) ? ">" | " "), (x[3] > last_old) ? ":+ " | ":  ", this:msg_summary_line(@this:(getmsg)(@x)));
player:tell(line[1..min(width, length(line))]);
endfor
handle = this._mgr:next(@listdelete(handle, 1));
endwhile
seq = seq[3..length(seq)];
endwhile
player:tell("----+");
.
#14:18
":display_seq_full(msg_seq[,preamble]) => {cur}";
"This is the default message display routine.";
"Prints the indicated messages on folder to player.  msg_seq is the handle returned by folder:parse_message_seq(...).  Returns the number of the final message in the sequence (to be the new current message number).";
if (!this:ok(caller, caller_perms()))
return E_PERM;
endif
seq = args[1];
preamble = {@args, ""}[2];
cur = date = 0;
while (seq)
handle = this._mgr:start(this.messages, seq[1], seq[2] - 1);
while (handle)
for x in (handle[1])
cur = this:_message_num(@x);
date = this:_message_date(@x);
player:display_message(preamble ? strsub(preamble, "%d", tostr(cur)) | {}, player:msg_text(@this:_message_text(@x)));
endfor
handle = this._mgr:next(@listdelete(handle, 1));
$command_utils:suspend_if_needed(0);
endwhile
seq = seq[3..length(seq)];
endwhile
return {cur, date};
.
#14:19
if (!this:ok(caller, caller_perms()))
return E_PERM;
endif
len = 0;
getmsg = this.summary_uses_body ? "_message_text" | "_message_hdr";
for s in (this.messages_going)
len = len + s[2][2];
handle = this._mgr:start(s[2], 1, s[2][2]);
while (handle)
for x in (handle[1])
player:tell($string_utils:right(this:_message_num(@x), 4), ":  ", this:msg_summary_line(@this:(getmsg)(@x)));
endfor
handle = this._mgr:next(@listdelete(handle, 1));
endwhile
endfor
if (len)
player:tell("----+");
endif
return len;
.
#14:20
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
msgtree = this.messages;
seq = {};
last = 0;
for s in (this.messages_going)
msgtree = this._mgr:insert_after(msgtree, s[2], last + s[1]);
seq = {@seq, (last + s[1]) + 1, (last = (last + s[1]) + s[2][2]) + 1};
endfor
this.messages = msgtree;
this.messages_going = {};
this:_fix_last_msg_date();
return seq;
.
#14:21
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
len = 0;
for s in (this.messages_going)
len = len + s[2][2];
this._mgr:kill(s[2], "_killmsg");
endfor
this.messages_going = {};
return len;
.
#14:22
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];
x = this._mgr:extract_range(msgtree, start - rmmed, (next - 1) - rmmed);
msgtree = x[1];
zmsgs = x[2];
save = {@save, {start - onext, zmsgs}};
nums = {@nums, this:_message_num(@this._mgr:find_nth(zmsgs, 1)), this:_message_num(@this._mgr:find_nth(zmsgs, zmsgs[2])) + 1};
onext = next;
rmmed = (rmmed + next) - start;
endfor
tmg = this.messages_going;
this.messages_going = save;
fork (0)
for s in (tmg)
this._mgr:kill(s[2], "_killmsg");
endfor
endfork
this.messages = msgtree;
this:_fix_last_msg_date();
return $seq_utils:tostr(nums);
.
#14:23
":renumber([cur]) renumbers caller.messages, doing a suspend() if necessary.";
"  => {number of messages,new cur}.";
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
cur = {@args, 0}[1];
this:expunge_rmm();
"... blow away @rmm'ed messages since there's no way to tell what their new numbers should be...";
if (!(msgtree = this.messages))
return {0, 0};
endif
if (cur)
cur = this._mgr:find_ord(msgtree, cur - 1, "_lt_msgnum") + 1;
endif
while (1)
"...find first out-of-sequence message...";
n = 1;
subtree = msgtree;
if (msgtree[3][1] == 1)
while ((node = this.(subtree[1]))[1])
"...subtree[3][1]==n...";
kids = node[2];
n = n + subtree[2];
i = length(kids);
while ((n = n - kids[i][2]) != kids[i][3][1])
i = i - 1;
endwhile
subtree = kids[i];
endwhile
leaves = node[2];
n = ((firstn = n) + length(leaves)) - 1;
while (n != leaves[(n - firstn) + 1][2])
n = n - 1;
endwhile
n = n + 1;
endif
"... n == first out-of-sequence ...";
"...renumber as many messages as we have time for...";
while ((n <= msgtree[2]) && (!$command_utils:running_out_of_time()))
msg = this._mgr:find_nth(msgtree, n);
msgtree = this._mgr:set_nth(msgtree, n, listset(msg, n, 2));
n = n + 1;
endwhile
this.messages = msgtree;
if (n > msgtree[2])
return {n - 1, cur};
endif
player:tell("...(renumbering to ", n - 1, ")");
suspend(0);
"...start over... may have received new mail, rmm'ed stuff, etc...";
"...so who knows what's there now?...";
if (this.messages_going)
player:tell("Renumber aborted.");
return;
endif
msgtree = this.messages;
endwhile
.
#14:24
return this:ok(caller, caller_perms()) ? this.messages ? this.messages[2] | 0 | E_PERM;
.
#14:25
return this:ok(caller, caller_perms()) ? this._mgr:find_ord(this.messages, args[1], "_lt_msgnum") | E_PERM;
.
#14:26
return this:ok(caller, caller_perms()) ? this._mgr:find_ord(this.messages, args[1], "_lt_msgdate") | E_PERM;
.
#14:27
return this:ok(caller, caller_perms()) ? (i = this._mgr:find_ord(this.messages, args[1], "_lt_msgnum")) && ((this:_message_num(@this._mgr:find_nth(this.messages, i)) == args[1]) && i) | E_PERM;
.
#14:28
new = (msgtree = this.messages) ? this:_message_num(@this._mgr:find_nth(msgtree, msgtree[2])) + 1 | 1;
if (rmsgs = this.messages_going)
lbrm = rmsgs[length(rmsgs)][2];
return max(new, this:_message_num(@this._mgr:find_nth(lbrm, lbrm[2])) + 1);
else
return new;
endif
.
#14:29
":from_msg_seq(object or list)";
" => msg_seq of messages from any of these senders";
if (!this:ok(caller, caller_perms()))
return E_PERM;
elseif (!this.messages)
return {};
endif
if (typeof(plist = args[1]) != LIST)
plist = {plist};
endif
mask = {@args, {1, this.messages[2] + 1}}[2];
fseq = {};
for m in [1..length(mask) / 2]
handle = this._mgr:start(this.messages, i = mask[(2 * m) - 1], mask[2 * m] - 1);
while (handle)
for msg in (handle[1])
fromline = msg[4];
if (toobj(fromline[rindex(fromline, "(") + 1..rindex(fromline, ")") - 1]) in plist)
fseq = $seq_utils:add(fseq, i, i);
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
handle = this._mgr:next(@listdelete(handle, 1));
endwhile
endfor
return fseq || ("%f %<has> no messages from " + $string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%n (%#)", plist), "no one", " or "));
.
#14:30
":%from_msg_seq(string or list of strings)";
" => msg_seq of messages with one of these strings in the from line";
if (!this:ok(caller, caller_perms()))
return E_PERM;
elseif (!this.messages)
return {};
endif
if (typeof(nlist = args[1]) != LIST)
nlist = {nlist};
endif
fseq = {};
mask = {@args, {1, this.messages[2] + 1}}[2];
for m in [1..length(mask) / 2]
handle = this._mgr:start(this.messages, i = mask[(2 * m) - 1], mask[2 * m] - 1);
while (handle)
for msg in (handle[1])
fromline = " " + msg[4];
for n in (nlist)
if (index(fromline, n))
fseq = $seq_utils:add(fseq, i, i);
endif
endfor
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
handle = this._mgr:next(@listdelete(handle, 1));
endwhile
endfor
return fseq || ("%f %<has> no messages from " + $string_utils:english_list($list_utils:map_arg($string_utils, "print", nlist), "no one", " or "));
.
#14:31
":to_msg_seq(object or list) => msg_seq of messages to those people";
if (!this:ok(caller, caller_perms()))
return E_PERM;
elseif (!this.messages)
return {};
endif
if (typeof(plist = args[1]) != LIST)
plist = {plist};
endif
seq = {};
mask = {@args, {1, this.messages[2] + 1}}[2];
for m in [1..length(mask) / 2]
handle = this._mgr:start(this.messages, i = mask[(2 * m) - 1], mask[2 * m] - 1);
while (handle)
for msg in (handle[1])
toline = msg[5];
for r in ($mail_agent:parse_address_field(toline))
if (r in plist)
seq = $seq_utils:add(seq, i, i);
endif
endfor
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
handle = this._mgr:next(@listdelete(handle, 1));
endwhile
endfor
return seq || ("%f %<has> no messages to " + $string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%n (%#)", plist), "no one", " or "));
.
#14:32
":%to_msg_seq(string or list of strings)";
" => msg_seq of messages containing one of strings in the to line";
if (!this:ok(caller, caller_perms()))
return E_PERM;
elseif (!this.messages)
return {};
endif
if (typeof(nlist = args[1]) != LIST)
nlist = {nlist};
endif
seq = {};
mask = {@args, {1, this.messages[2] + 1}}[2];
for m in [1..length(mask) / 2]
handle = this._mgr:start(this.messages, i = mask[(2 * m) - 1], mask[2 * m] - 1);
while (handle)
for msg in (handle[1])
toline = " " + msg[5];
for n in (nlist)
if (index(toline, n))
seq = $seq_utils:add(seq, i, i);
endif
endfor
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
handle = this._mgr:next(@listdelete(handle, 1));
endwhile
endfor
return seq || ("%f %<has> no messages to " + $string_utils:english_list($list_utils:map_arg($string_utils, "print", nlist), "no one", " or "));
.
#14:33
":subject_msg_seq(target) => msg_seq of messages with target in the Subject:";
if (!this:ok(caller, caller_perms()))
return E_PERM;
elseif (!this.messages)
return {};
endif
target = args[1];
seq = {};
mask = {@args, {1, this.messages[2] + 1}}[2];
for m in [1..length(mask) / 2]
handle = this._mgr:start(this.messages, i = mask[(2 * m) - 1], mask[2 * m] - 1);
while (handle)
for msg in (handle[1])
if (((subject = msg[6]) != " ") && index(subject, target))
seq = $seq_utils:add(seq, i, i);
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
handle = this._mgr:next(@listdelete(handle, 1));
endwhile
endfor
return seq || (("%f %<has> no messages with subjects containing `" + target) + "'");
.
#14:34
":body_msg_seq(target) => msg_seq of messages with target in the body";
if (!this:ok(caller, caller_perms()))
return E_PERM;
elseif (!this.messages)
return {};
endif
target = args[1];
seq = {};
mask = {@args, {1, this.messages[2] + 1}}[2];
for m in [1..length(mask) / 2]
handle = this._mgr:start(this.messages, i = mask[(2 * m) - 1], mask[2 * m] - 1);
while (handle)
for msg in (handle[1])
if (msg[1] && (body = this.(msg[1])))
l = length(body);
while ((!index(body[l], target)) && (l = l - 1))
$command_utils:suspend_if_needed(0);
endwhile
if (l)
seq = $seq_utils:add(seq, i, i);
endif
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
handle = this._mgr:next(@listdelete(handle, 1));
endwhile
endfor
return seq || tostr("%f %<has> no messages containing `", target, "' in the body.");
.
#14:35
return E_VERBNF;
.
#14:36
msgtree = this.messages;
this.last_msg_date = (msgtree && this:_message_hdr(@this._mgr:find_nth(msgtree, msgtree[2]))[1]) || 0;
.
#14:37
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
doit = args && args[1];
msgtree = this.messages;
for n in [1..msgtree[2]]
msg = this._mgr:find_nth(msgtree, n);
msg = {@msg[1..2], @$mail_agent:__convert_new(@msg[3..length(msg)])};
if (doit)
msgtree = this._mgr:set_nth(msgtree, n, msg);
endif
if ($command_utils:running_out_of_time())
suspend(0);
if (this.messages != msgtree)
player:notify("urk.  someone played with this folder.");
return 0;
endif
endif
endfor
return 1;
.
#14:38
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this._mgr = $biglist;
this.mowner = $mail_recipient.owner;
this._genprop = "";
for p in (properties(this))
$command_utils:suspend_if_needed(0);
if (p && (p[1] == " "))
delete_property(this, p);
endif
endfor
.
#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
.
#15:0
what = args[1];
return is_player(what) && (!$object_utils:connected(what));
.
#15:1
{who} = args;
home = who.home;
`move(who, home) ! ANY';
if (who.location != home)
who:notify(tostr("Your home won't accept you; trying to move you to $player_start..."));
`move(who, $player_start) ! ANY';
endif
if (who.location == this)
"screwed?";
else
who.location:announce_all_but({who}, who:grammar_sub("%N %<has> connected."));
endif
.
#15:2
who = args[1];
return who.home:who_location_msg(@args) || this:name();
.
#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 = args[1];
email = args[2];
comment = args[3..length(args)];
l = this:find_exact(email);
if (l == $failed_match)
this:insert(email, {{who, @comment}});
elseif (i = $list_utils:iassoc(who, l))
this:insert(email, listset(l, {who, @comment}, i));
else
this:insert(email, {@l, {who, @comment}});
endif
.
#16:2
if (caller_perms().wizard)
pass();
this:clearall();
this.registrar = #2;
this.new_player_mail = {};
endif
.
#16:3
"suspicious(address)";
"Determine whether an address appears to be another player in disguise.";
"returns a list of similar addresses.";
"";
"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
parsed = $network:parse_address(address = args[1]);
userid = parsed[1];
site = parsed[2];
exact = (!site) && this:find_exact(address);
if (!site)
site = $network.site;
endif
site = $network:local_domain(site) || site;
sitelen = length(site);
others = this:find_all_keys(userid + "@");
for other in (others)
if (other[(length(other) - sitelen) + 1..length(other)] != site)
others = setremove(others, other);
endif
endfor
if (exact)
others = listinsert(others, address);
endif
return others;
.
#16:4
"suspicious_userid(userid)";
"Return yes if userid is root or postmaster or something like that.";
return args[1] in {"", "sysadmin", "root", "postmaster", "bin", "SYSTEM", "OPERATOR"};
"Thinking about ruling out hyphenated names, on the grounds that they're probably mailing lists.";
.
#16:5
":load()";
"Clear former and add current email addresses to the registration database.";
if (!caller_perms().wizard)
return E_PERM;
endif
this:clearall();
for p in (players())
if ((!valid(p)) && (!is_player(p)))
continue;
endif
if (!(email = `p.email_address ! E_PROPNF'))
continue;
endif
this:add(p, email);
if ($command_utils:running_out_of_time())
player:notify(tostr(" ... suspending at ", p, " ..."));
suspend(0);
endif
endfor
.
#17:0
":display_seq_headers(msg_seq[,cur])";
if (!this:ok(caller, caller_perms()))
return E_PERM;
endif
player:tell("       WHEN    BY        WHO                 EMAIL-ADDRESS");
pass(@args);
.
#17:1
when = ctime(args[1])[5..10];
from = args[2];
by = $string_utils:left(from[1..index(from, " (") - 1], -9);
subject = args[4];
who = subject[1..(open = index(subject, " (")) - 1];
if ((close = rindex(subject, ")")) > open)
who = who[1..min(9, length(who))] + subject[open..close];
endif
who = $string_utils:left(who, 18);
line = args[("" in args) + 1];
email = line[1..index(line + " ", " ") - 1];
if (!index(email, "@"))
email = "??";
endif
return tostr(when, "  ", by, " ", who, "  ", email);
.
#17:2
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.mail_forward = {player, this};
this.mail_notify = {player};
this.moderated = 1;
.
#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);
.
#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 ($match_utils:object_match_failed(object = player:my_match_object(spec[1], setadd(player:env(), toobj(spec[1]))), 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])) == 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 ($match_utils:object_match_failed(object = player:my_match_object(spec[1], setadd(player:env(), toobj(spec[1]))), 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, length(p) - 5)..length(p)] == "_utils") && #0.(p).description)
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..length(what)]))
return ts;
elseif ((r = rindex(w = strsub(what[2..length(what)], "-", "_"), "_utils")) && ((r == (length(w) - 5)) && (valid(#0.(w)) && #0.(w).description)))
return {what};
else
return {};
endif
.
#19:1
topic = args[1];
if ((topic == (("$" + topic[2..length(topic) - 5]) + "utils")) && (valid(#0.(w = strsub(topic[2..length(topic)], "-", "_"))) && (uhelp = #0.(w):description())))
return {tostr("General information on $", w, ":"), "----", @uhelp};
else
return pass(@args);
endif
.
#19:2
if ((E_PROPNF != (text = pass(@args))) || ((args[1][1] != "$") || ((!((uprop = args[1][2..length(args[1])]) in properties(#0))) || (typeof(uobj = #0.(uprop)) != OBJ))))
return text;
else
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 = args[1];
if (typeof(n) == STR)
n = length(n);
endif
if (" " != (fill = {@args, " "}[2]))
fill = fill + fill;
fill = fill + fill;
fill = fill + fill;
elseif ((n = abs(n)) < 70)
return "                                                                      "[1..n];
else
fill = "                                                                      ";
endif
m = (n - 1) / length(fill);
while (m)
fill = fill + fill;
m = m / 2;
endwhile
return (n > 0) ? fill[1..n] | fill[((f = length(fill)) + 1) + n..f];
.
#20:1
text = args[1];
len = args[2];
fill = ((length(args) >= 3) && args[3]) || " ";
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
text = args[1];
len = args[2];
fill = ((length(args) >= 3) && args[3]) || " ";
abslen = abs(len);
out = tostr(text);
if (length(out) < abslen)
return this:space(abslen - length(out), fill) + out;
else
return (len > 0) ? out | out[1..abslen];
endif
.
#20:3
text = args[1];
len = args[2];
lfill = ((length(args) >= 3) && args[3]) || " ";
rfill = (length(args) >= 4) ? args[4] | lfill;
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 = args[1];
n = args[2];
width = (length(args) >= 3) ? args[3] | 79;
height = ((length(items) + n) - 1) / n;
items = {@items, @$list_utils:make((height * n) - length(items), "")};
colwidths = {};
for col in [1..n - 1]
colwidths = listappend(colwidths, 1 - (((width + 1) * col) / n));
endfor
result = {};
for row in [1..height]
line = tostr(items[row]);
for col in [1..n - 1]
line = tostr(this:left(line, colwidths[col]), " ", items[row + (col * height)]);
endfor
result = listappend(result, line[1..min(length(line), width)]);
endfor
return result;
.
#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.";
LIST = args[1];
if (length(args) <= 1)
return tostr(@LIST);
elseif (LIST)
separator = args[2];
result = tostr(LIST[1]);
for elt in (listdelete(LIST, 1))
result = tostr(result, separator, elt);
endfor
return result;
else
return "";
endif
.
#20:6
":english_list(LIST, STR nothing, STR and_or_or, STR delim, STR last_delim)";
"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 = args[1];
nthings = length(things);
if (length(args) > 1)
nothingstr = args[2];
else
nothingstr = "nothing";
endif
if (length(args) > 2)
andstr = args[3];
else
andstr = " and ";
endif
if (length(args) > 3)
commastr = args[4];
else
commastr = ", ";
endif
if (length(args) > 4)
finalcommastr = args[5];
else
finalcommastr = ",";
endif
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
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 = args[1];
space = {@args, " "}[2];
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 = args[1];
what = {@args, " "}[2];
return string[match(string, tostr("[^", what, "]%|$"))[1]..length(string)];
.
#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 = args[1];
what = {@args, " "}[2];
return string[1..rmatch(string, tostr("[^", what, "]%|^"))[2]];
.
#20:12
":strip_chars(string,chars) => string with chars removed";
subject = args[1];
stripped = args[2];
for i in [1..length(stripped)]
subject = strsub(subject, stripped[i], "");
endfor
return subject;
.
#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..length(string)];
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
"DO NOT CALL THESE VERBS.  USE THE ONES ON $match_utils INSTEAD!";
set_task_perms(caller_perms());
return $match_utils:(verb)(@args);
.
#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)) != 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.";
wild = "*";
case = ret = {};
what = args[1] + "&^%$";
targ = args[2] + "&^%$";
for y in (args[3..length(args)])
if (typeof(y) == STR)
wild = y;
elseif (typeof(y) == NUM)
case = {y};
endif
endfor
while (targ != "")
if (z = index(targ, wild))
part = targ[1..z - 1];
else
z = length(targ);
part = targ;
endif
n = (part == "") ? 1 | index(what, part, @case);
if (n)
ret = listappend(ret, what[1..n - 1]);
what = what[(z + n) - 1..length(what)];
targ = targ[z + 1..length(targ)];
else
return 0;
endif
endwhile
if (ret == {})
return what == "";
elseif (ret == {""})
return 1;
elseif (ret[1] == "")
return ret[2..length(ret)];
else
return 0;
endif
.
#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 ((object = $match_utils:literal_object(string)) != $failed_match)
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
"find_prefix(prefix, string-list) => list index of something starting with prefix, or 0 or $ambiguous_match.";
subject = args[1];
choices = args[2];
answer = 0;
for i in [1..length(choices)]
if (index(choices[i], subject) == 1)
if (answer == 0)
answer = i;
else
answer = $ambiguous_match;
endif
endif
endfor
return answer;
.
#20:21
"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:22
"Usage:  is_numeric(string)";
"Is string numeric (composed of one or more digits possibly preceded by a minus sign)?";
"Return true or false";
return match(args[1], "^ *[-+]?[0-9]+ *$");
digits = "1234567890";
if (!(string = args[1]))
return 0;
endif
if (string[1] == "-")
string = string[2..length(string)];
endif
for i in [1..length(string)]
if (!index(digits, string[i]))
return 0;
endif
endfor
return 1;
.
#20:23
":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:24
"$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 = args[1];
comma = (length(args) > 1) ? args[2] | ",";
result = "";
sign = (n < 0) ? "-" | "";
n = tostr(abs(n));
while ((len = length(n)) > 3)
result = (comma + n[len - 2..len]) + result;
n = n[1..len - 3];
endwhile
return (sign + n) + result;
.
#20:25
"$string_utils:english_number(n) -- convert the number N into English";
"";
"Produces a string containing the English phrase naming the given number.  For example, $string_utils:english_number(-1234) returns the string `negative one thousand two hundred thirty-four'.";
NUM = tonum(args[1]);
if (NUM == 0)
return "zero";
endif
labels = {"", " thousand", " million", " billion"};
numstr = "";
mod = abs(NUM);
for n in [1..4]
div = mod % 1000;
if (div)
hun = div / 100;
ten = div % 100;
outstr = this:english_tens(ten) + labels[n];
if (hun)
outstr = ((this:english_ones(hun) + " hundred") + (ten ? " " | "")) + outstr;
endif
if (numstr)
numstr = (outstr + " ") + numstr;
else
numstr = outstr;
endif
endif
mod = mod / 1000;
endfor
return ((NUM < 0) ? "negative " | "") + numstr;
.
#20:26
"$string_utils:english_ordinal(n) -- convert the number N into an english ordinal (1 => \"first\", etc...)";
NUM = tonum(args[1]);
if (NUM == 0)
return "zeroth";
elseif (NUM % 100)
hundreds = (abs(NUM) > 100) ? this:english_number((NUM / 100) * 100) + " " | ((NUM < 0) ? "negative " | "");
NUM = abs(NUM) % 100;
specials = {1, 2, 3, 5, 8, 9, 12, 20, 30, 40, 50, 60, 70, 80, 90};
ordinals = {"first", "second", "third", "fifth", "eighth", "ninth", "twelfth", "twentieth", "thirtieth", "fortieth", "fiftieth", "sixtieth", "seventieth", "eightieth", "ninetieth"};
if (i = NUM in specials)
return hundreds + ordinals[i];
elseif ((NUM > 20) && (i = (NUM % 10) in specials))
return ((hundreds + this:english_tens((NUM / 10) * 10)) + "-") + ordinals[i];
else
return (hundreds + this:english_number(NUM)) + "th";
endif
else
return this:english_number(NUM) + "th";
endif
.
#20:27
NUM = args[1];
ones = {"", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};
return ones[NUM + 1];
.
#20:28
NUM = args[1];
teens = {"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"};
others = {"twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"};
if (NUM < 10)
return this:english_ones(NUM);
elseif (NUM < 20)
return teens[NUM - 9];
else
return (others[(NUM / 10) - 1] + ((NUM % 10) ? "-" | "")) + this:english_ones(NUM % 10);
endif
.
#20:29
"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, subsets, ?case = 0} = args;
if (typeof(ostr) != STR)
if (typeof(ostr) == LIST)
result = {};
for o in (ostr)
result = {@result, this:(verb)(o, subsets, case)};
endfor
return result;
else
return ostr;
endif
endif
len = length(ostr);
" - - - find the first instance of each substitution - -";
indices = {};
substs = {};
for s in (subsets)
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:30
"subst(string,{{redex1,repl1},{redex2,repl2},{redex3,repl3}...}[,case])";
"Just like :substitute() but it uses index_delimited() instead of index()";
if (typeof(ostr = args[1]) != STR)
return ostr;
endif
case = {@args, 0}[3];
len = length(ostr);
" - - - find the first instance of each substitution - -";
indices = {};
substs = {};
for s in (args[2])
if (i = this:index_delimited(ostr, s[1], case))
fi = $list_utils:find_insert(indices, i = i - len) - 1;
while (fi && ((indices[fi] == i) && (length(substs[fi][1]) < length(s[1]))))
"...give preference to longer redexes...";
fi = fi - 1;
endwhile
indices = listappend(indices, i, fi);
substs = listappend(substs, s, fi);
endif
endfor
"- - - - - perform substitutions - ";
nstr = "";
while (substs)
ind = len + indices[1];
sub = substs[1];
indices = listdelete(indices, 1);
substs = listdelete(substs, 1);
if (ind > 0)
nstr = (nstr + ostr[1..ind - 1]) + sub[2];
ostr = ostr[ind + length(sub[1])..len];
len = length(ostr);
endif
if (next = this:index_delimited(ostr, sub[1], case))
fi = $list_utils:find_insert(indices, next = next - len) - 1;
while (fi && ((indices[fi] == next) && (length(substs[fi][1]) < length(sub[1]))))
"...give preference to longer redexes...";
fi = fi - 1;
endwhile
indices = listappend(indices, next, fi);
substs = listappend(substs, sub, fi);
endif
endwhile
return nstr + ostr;
.
#20:31
"cap_property(what, prop[, ucase])";
set_task_perms($no_one);
{what, prop_name, ?ucase = 0} = args;
if (typeof(what) == LIST)
if (length(what) == 1)
what = what[1];
else
nargs = listdelete(args, 1);
if (match(prop_name, "^p[opqrs]c?$"))
return this:(verb)($they, @nargs);
endif
result = {};
for who in (what)
result = {@result, this:_cap_property(who, @nargs)};
endfor
return this:english_list(result);
endif
endif
if (typeof(what) != OBJ)
return tostr(what, ".", prop_name);
endif
if (!valid(what))
return tostr(what, ".", prop_name);
endif
if (m = index(prop_name, ","))
prop_args = prop_name[m + 1..$];
prop_args = index(prop_args, ",") ? this:explode(prop_args, ",") | {prop_args};
prop_name[m..$] = "";
else
prop_args = {};
endif
if (prop_name && (strcmp(prop_name, "a") < 0))
ucase = 1;
endif
if (!prop_name)
return ucase ? what:sub_namec() | what:sub_name();
endif
s = "";
if (ucase)
try
s = what:(prop_name + "c")(@prop_args) || what.(prop + "c");
except (ANY)
endtry
endif
if (!s)
if (prop_name == "name")
s = what.name;
ucase = ucase && (!is_player(what));
elseif (typeof(s = `what:(prop_name)(@prop_args) ! E_VERBNF') != ERR)
"...verb instead of property...";
`this.ps_verbs = setadd(this.ps_verbs, prop_name) ! ANY';
elseif (typeof(s = `what.(prop_name) ! E_PROPNF') != ERR)
"...return random if it's owned by $gm...";
"...have to except E_PROPNF again in case of builtin props...";
if (`property_info(what, prop_name)[1] ! ANY' == $gm)
return tostr(random(200));
endif
`this.ps_props = setadd(this.ps_props, prop_name) ! ANY';
else
"...don't bother trying a verb here...";
s = `$player:(prop_name)(@prop_args) ! E_VERBNF' || `$player.(prop_name) ! E_PROPNF';
endif
if ((ucase && s) && (typeof(s) == STR))
s = this:capitalize(s);
endif
endif
return (typeof(s) == ERR) ? s | tostr(s);
.
#20:32
"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.";
"%a                => <who>'s article plus a space, or nothing if no article.";
"%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.";
set_task_perms($no_one);
{old, @rest} = args;
if (typeof(old) == LIST)
plines = {};
for line in (old)
plines = {@plines, this:(verb)(line, @rest)};
endfor
return plines;
endif
for i in [1..length(rest)]
if (typeof(rest[i]) != LIST)
"";
elseif (!rest[i])
rest[i] = #-1;
elseif (length(rest[i]) == 1)
rest[i] = rest[i][1];
endif
endfor
{?who = player, ?thing = caller, ?where = #-1, ?mydobj = dobj, ?myiobj = iobj} = rest;
if (!valid(where))
if ((typeof(who) == LIST) && valid(who[1]))
where = who[1].location;
elseif (valid(who))
where = who.location;
endif
endif
old = tostr(old);
new = "";
objspec = "nditl";
objects = {who, mydobj, myiobj, thing, where};
prnspec = "sopqrSOPQR";
prprops = {"ps", "po", "pp", "pq", "pr", "Ps", "Po", "Pp", "Pq", "Pr"};
oldlen = length(old);
while ((prcnt = index(old, "%")) && (prcnt < oldlen))
s = old[k = prcnt + 1];
if ((s == "<") && (gt = index(old[k + 2..length(old)], ">")))
"handling %<verb> ";
gt = (gt + k) + 1;
vb = old[k + 1..gt - 1];
vbs = who;
if ((length(vb) > 2) && (vb[2] == ":"))
" %<d:verb>";
vbs = objects[index(objspec, vb[1]) || 1];
vb = vb[3..length(vb)];
endif
if (typeof(vbs) == LIST)
vb = $they:verb_sub(vb);
else
vb = vbs:verb_sub(vb) || this:(verb)(vb, vbs);
endif
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:33
"$string_utils:pronoun_sub_secure(string[,who[,thing[,location]]], default)";
"Do pronoun_sub on string with the arguments given (see help";
"string_utils:pronoun_sub for more information).  Return pronoun_subbed";
"<default> if the subbed string does not contain <who>.name (<who>";
"defaults to player).";
len = length(args);
who = (len > 2) ? args[2] | player;
default = args[len];
result = this:pronoun_sub(@args[1..len - 1]);
return this:index_delimited(result, who.name) ? result | this:pronoun_sub(@{default, @args[2..len - 1]});
.
#20:34
" 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:35
"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.";
who = (length(args) >= 2) ? args[2] | player;
thing = (length(args) >= 3) ? args[3] | caller;
where = (length(args) >= 4) ? args[4] | (valid(who) ? who.location | $nothing);
if (typeof(args[1]) == LIST)
plines = {};
for line in (args[1])
plines = {@plines, this:(verb)(line, who, thing, where)};
endfor
return plines;
endif
old = tostr(args[1]);
new = "";
objspec = "nditl";
objects = {who, 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..length(old)], ">")))
"handling %<verb> ";
gt = (gt + k) + 1;
vb = old[k + 1..gt - 1];
vbs = who;
if ((length(vb) > 2) && (vb[2] == ":"))
" %<d:verb>";
vbs = objects[index(objspec, vb[1]) || 1];
vb = vb[3..length(vb)];
endif
vb = $object_utils:has_verb(vbs, "verb_sub") ? vbs:verb_sub(vb) | this:(verb)(vb, vbs);
new = (new + old[1..prcnt - 1]) + vb;
k = gt;
else
cp_args = {};
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:36
"$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];
blen = length(breakit);
subject = subject + breakit;
parts = {};
while (subject)
if ((i = index(subject, breakit)) > 1)
parts = {@parts, subject[1..i - 1]};
endif
subject = subject[i + blen..$];
endwhile
return parts;
.
#20:37
"This breaks up the argument string into words, the resulting list being obtained exactly the way the command line parser obtains `args' from `argstr'.";
rest = args[1];
"...trim leading blanks...";
rest[1..match(rest, "^ *")[2]] = "";
if (!rest)
return {};
endif
quote = 0;
toklist = {};
token = "";
pattern = " +%|\\.?%|\"";
while (m = match(rest, pattern))
"... find the next occurence of a special character, either";
"... a block of spaces, a quote or a backslash escape sequence...";
"notify(#4292,tostr(\"match(\",#20:print(rest),\",\",#20:print(pattern),\") => \",#20:print(m)))";
char = rest[m[1]];
token = token + rest[1..m[1] - 1];
if (char == " ")
toklist = {@toklist, token};
token = "";
elseif (char == "\"")
"... beginning or end of quoted string...";
"... within a quoted string spaces aren't special...";
pattern = (quote = !quote) ? "\\.?%|\"" | " +%|\\.?%|\"";
elseif (m[1] < m[2])
"... char has to be a backslash...";
"... include next char literally if there is one";
token = token + rest[m[2]];
endif
rest[1..m[2]] = "";
endwhile
return (rest || (char != " ")) ? {@toklist, token + rest} | toklist;
.
#20:38
"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:39
":to_value(string) tries to parse string as a value (i.e., object, number, string, error, or list thereof).";
"Returns {1,value} or {0,error_message} according as the attempt was successful or not.";
result = this:_tolist(string = args[1] + "}");
if (result[1] && (result[1] != $string_utils:space(result[1])))
return {0, tostr("after char ", length(string) - result[1], ":  ", result[2])};
elseif (typeof(result[1]) == NUM)
return {0, "missing } or \""};
elseif (length(result[2]) > 1)
return {0, "comma unexpected."};
elseif (result[2])
return {1, result[2][1]};
else
return {0, "missing expression"};
endif
.
#20:40
":prefix_to_value(string) tries to parse string as a value (i.e., object, number, string, error, or list thereof).";
"Returns {rest-of-string,value} or {0,error_message} according as the attempt was successful or not.";
alen = length(args[1]);
slen = length(string = this:triml(args[1]));
if (!string)
return {0, "empty string"};
elseif (w = index("{\"", string[1]))
result = this:({"_tolist", "_unquote"}[w])(string[2..slen]);
if (typeof(result[1]) != NUM)
return result;
elseif (result[1] == 0)
return {0, "missing } or \""};
else
return {0, result[2], (alen - result[1]) + 1};
endif
else
thing = string[1..tlen = index(string + " ", " ") - 1];
if (typeof(s = this:_toscalar(thing)) != STR)
return {string[tlen + 1..slen], s};
else
return {0, s, (alen - slen) + 1};
endif
endif
.
#20:41
"_tolist(string) --- auxiliary for :to_value()";
rest = this:triml(args[1]);
vlist = {};
if (!rest)
return {0, {}};
elseif (rest[1] == "}")
return {rest[2..length(rest)], {}};
endif
while (1)
rlen = length(rest);
if (w = index("{\"", rest[1]))
result = this:({"_tolist", "_unquote"}[w])(rest[2..rlen]);
if (typeof(result[1]) == NUM)
return result;
endif
vlist = {@vlist, result[2]};
rest = result[1];
else
thing = rest[1..tlen = min(index(rest + ",", ","), index(rest + "}", "}")) - 1];
if (typeof(s = this:_toscalar(thing)) == STR)
return {rlen, s};
endif
vlist = {@vlist, s};
rest = rest[tlen + 1..rlen];
endif
if (!rest)
return {0, vlist};
elseif (rest[1] == "}")
return {rest[2..length(rest)], vlist};
elseif (rest[1] == ",")
rest = this:triml(rest[2..length(rest)]);
else
return {length(rest), ", or } expected"};
endif
endwhile
.
#20:42
"_unquote(string)   (auxiliary for :to_value())";
"reads string as if it were preceded by a quote, reading up to the closing quote if any, then returns the corresponding unquoted string.";
" => {0, string unquoted}  if there is no closing quote";
" => {original string beyond closing quote, string unquoted}  otherwise";
rest = args[1];
result = "";
while (m = match(rest, "\\.?%|\""))
"Find the next special character";
if (rest[pos = m[1]] == "\"")
return {rest[pos + 1..length(rest)], result + rest[1..pos - 1]};
endif
result = (result + rest[1..pos - 1]) + rest[pos + 1..m[2]];
rest = rest[m[2] + 1..length(rest)];
endwhile
return {0, result + rest};
.
#20:43
":_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:44
":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 = args[1];
who = (length(args) > 1) ? args[2] | player;
y = $string_utils:words(c);
if (y == {})
return {};
endif
vrb = y[1];
y = y[2..length(y)];
as = (y == {}) ? "" | c[length(vrb) + 2..length(c)];
n = 1;
while ((!(gp = $code_utils:get_prep(@y[n..length(y)]))[1]) && (n < length(y)))
n = n + 1;
endwhile
"....";
really = player;
player = who;
loc = who.location;
if (ps = gp[1])
ds = $string_utils:from_list(y[1..n - 1], " ");
is = $string_utils:from_list(listdelete(gp, 1), " ");
io = valid(loc) ? loc:match_object(is) | $string_utils:match_object(is, loc);
else
ds = $string_utils:from_list(y, " ");
is = "";
io = $nothing;
endif
do = valid(loc) ? loc:match_object(ds) | $string_utils:match_object(ds, loc);
player = really;
"....";
dset = {"any", @(ds == "") ? {"none"} | {}};
"\"this\" must be handled manually.";
pset = {"any", @ps ? {$code_utils:full_prep(ps)} | {"none"}};
iset = {"any", @(is == "") ? {"none"} | {}};
return {vrb, {do, ds}, {$code_utils:short_prep(ps), ps}, {io, is}, {y, as}, {dset, pset, iset}};
.
#20:45
"$string_utils:from_value(value [, quote_strings = 0 [, list_depth = 1]])";
"Print the given value into a string.";
{value, ?quote_strings = 0, ?list_depth = 1} = args;
if (typeof(value) == LIST)
if (value)
if (list_depth)
result = "{" + this:from_value(value[1], quote_strings, list_depth - 1);
for v in (listdelete(value, 1))
result = tostr(result, ", ", this:from_value(v, quote_strings, list_depth - 1));
endfor
return result + "}";
else
return "{...}";
endif
else
return "{}";
endif
elseif (quote_strings)
if (typeof(value) == STR)
result = "\"";
while (q = index(value, "\"") || index(value, "\\"))
if (value[q] == "\"")
q = min(q, index(value + "\\", "\\"));
endif
result = ((result + value[1..q - 1]) + "\\") + value[q];
value = value[q + 1..length(value)];
endwhile
return (result + value) + "\"";
elseif (typeof(value) == ERR)
return $code_utils:error_name(value);
else
return tostr(value);
endif
else
return tostr(value);
endif
.
#20:46
"$string_utils:print(value)";
"Print the given value into a string. == from_value(value,1,-1)";
{value, @rest} = args;
try
return toliteral(value);
except (E_QUOTA)
"...pass to traditional method...";
endtry
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)
result = "\"";
while (q = index(value, "\"") || index(value, "\\"))
if (value[q] == "\"")
q = min(q, index(value + "\\", "\\"));
endif
result = ((result + value[1..q - 1]) + "\\") + value[q];
value = value[q + 1..length(value)];
endwhile
return (result + value) + "\"";
elseif (typeof(value) == ERR)
return $code_utils:error_name(value);
else
return tostr(value);
endif
.
#20:47
if ((len = length(args[1])) > 50)
return this:reverse(args[1][(len / 2) + 1..len]) + this:reverse(args[1][1..len / 2]);
endif
index = len;
result = "";
while (index > 0)
result = result + args[1][index];
index = index - 1;
endwhile
return result;
.
#20:48
":char_list(string) => string as a list of characters.";
"   e.g., :char_list(\"abad\") => {\"a\",\"b\",\"a\",\"d\"}";
if (30 < (len = length(string = args[1])))
return {@this:char_list(string[1..len / 2]), @this:char_list(string[(len / 2) + 1..len])};
else
l = {};
for c in [1..len]
l = {@l, string[c]};
endfor
return l;
endif
.
#20:49
":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:50
s = args[1];
m = match(s, "^.* %(from%|to%) %([^, ]+%)");
return m ? substitute("%2", m) | "";
.
#20:51
"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:52
"$string_utils:from_value(value [, quote_strings = 0 [, list_depth = 1]])";
"Print the given value into a string.";
set_task_perms(caller_perms());
value = args[1];
if (length(args) < 2)
quote_strings = 0;
list_depth = 1;
else
quote_strings = args[2];
if (length(args) < 3)
list_depth = 1;
else
list_depth = args[3];
endif
endif
if (typeof(value) == LIST)
if (value)
if (list_depth)
result = "{" + this:from_value(value[1], quote_strings, list_depth - 1);
for v in (listdelete(value, 1))
$command_utils:suspend_if_needed(0);
result = tostr(result, ", ", this:from_value(v, quote_strings, list_depth - 1));
endfor
return result + "}";
else
return "{...}";
endif
else
return "{}";
endif
elseif (quote_strings)
if (typeof(value) == STR)
result = "\"";
while (q = index(value, "\"") || index(value, "\\"))
$command_utils:suspend_if_needed(0);
if (value[q] == "\"")
q = min(q, index(value + "\\", "\\"));
endif
result = ((result + value[1..q - 1]) + "\\") + value[q];
value = value[q + 1..length(value)];
endwhile
return (result + value) + "\"";
elseif (typeof(value) == ERR)
return $code_utils:error_name(value);
else
return tostr(value);
endif
else
return tostr(value);
endif
.
#20:53
":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 = args[1];
stop_at = {@args, " "}[2];
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:54
":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:55
":common(first,second) => length of longest common prefix";
first = args[1];
second = args[2];
r = min(length(first), length(second));
l = 1;
while (r >= l)
h = (r + l) / 2;
if (first[l..h] == second[l..h])
l = h + 1;
else
r = h - 1;
endif
endwhile
return r;
.
#20:56
"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 the first arg is a list, then return an english_list of the the name and numbers of all objects.  Args after sepr will be for the english_list verb.";
what = args[1];
args = {@args, " "};
sepr = args[2];
if (typeof(what) == LIST)
nn_list = {};
for elm in (what)
nn_list = {@nn_list, this:nn(elm, sepr)};
endfor
return this:english_list(nn_list, @(length(args) < 3) ? {} | args[3..length(args)]);
endif
name = valid(what) ? what.name | {"<invalid>", "$nothing", "$ambiguous_match", "$failed_match"}[1 + (what in {#-1, #-2, #-3})];
return tostr(name, sepr, "(", what, ")");
.
#20:57
"Copied from string utilities (#20):columnize by Rog (#4292) Sun Dec 20 22:59:33 1992 PST";
"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 = args[1];
n = args[2];
width = (length(args) >= 3) ? args[3] | 79;
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
$command_utils:suspend_if_needed(0);
result = listappend(result, line[1..min(length(line), width)]);
endfor
return result;
.
#20:58
":a_or_an(<noun>) => \"a\" or \"an\"";
noun = args[1];
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:59
":pluralize(STR noun[, NUM quantity])";
"-> The plural version of the given noun (if quantity merits a plural).";
return $english:(verb)(@args);
.
#20:60
":unbreak(list)";
"Attempt to fix the given list of strings so that one string is one paragraph.  Either an indention, or an unindented line preceding an indention, are considered beginnings of paragraphs.";
text = args[1];
if (typeof(text) != LIST)
return text;
endif
"Mode is false if no paragraph is being built.";
"Mode is  1 if a standard paragraph is being built.";
"Mode is -1 if a hanging paragraph is being built.";
mode = 0;
"Done is the processed text.";
done = {};
"Temp is the text of the current paragraph.";
temp = "";
for line in (text)
if (mode == 0)
if (!line)
done = {@done, line};
temp = "";
elseif (line[1] == " ")
mode = 1;
temp = line;
else
mode = -1;
temp = line;
endif
elseif (mode == 1)
if (!line)
done = temp ? {@done, temp, line} | {@done, line};
temp = "";
elseif (line[1] == " ")
if (temp)
done = {@done, temp};
endif
temp = line;
else
temp = (temp + ((temp[length(temp)] == " ") ? "" | " ")) + line;
endif
elseif (mode == -1)
if (!line)
done = temp ? {@done, temp, line} | {@done, line};
temp = "";
elseif (line[1] == " ")
temp = (temp + ((temp[length(temp)] == " ") ? "" | " ")) + $string_utils:triml(line);
else
if (temp)
done = {@done, temp};
endif
temp = line;
endif
endif
endfor
if (!temp)
"do nothing";
elseif (mode == 1)
done = {@done, temp};
elseif (mode == -1)
done = {@done, $string_utils:triml(temp)};
endif
return done;
.
#20:61
":chop(string, size) -> list";
"Chop the given string up into 'size'-length elements of a single list.";
string = args[1];
size = args[2];
chopped = {};
while ((ls = length(string)) > size)
chopped = {@chopped, string[1..size]};
string = string[size + 1..ls];
endwhile
return {@chopped, string};
.
#20:62
":aliases_from_name(string)";
"Given a string name, return all possible aliases from that name.  For example:";
"'yellow baseball bat' -> {'yellow baseball bat', 'baseball bat', 'bat'}";
name = args[1];
aliases = {};
while ((i = index(name, " ")) && (i != length(name)))
aliases = {@aliases, name[i + 1..length(name)]};
name = name[i + 1..length(name)];
endwhile
return setadd(aliases, args[1]);
.
#20:63
":[adi](title|name)_list*c(objects[, @args])";
"Creates an english list out of the given names/titles of the given objects.";
"Optional <args> are passed on to $string_utils:english_list.";
mapverb = verb[1..index(verb, "_") - 1];
results = $list_utils:map_verb(args[1], mapverb);
if (verb[length(verb)] == "c")
if (results)
results[1] = args[1][1]:(mapverb + "c")();
elseif (length(args) > 1)
args[2] = $string_utils:capitalize(args[2]);
else
args = listappend(args, "Nothing");
endif
endif
return $string_utils:english_list(results, @listdelete(args, 1));
.
#20:64
":format([linelen, ]string[, @items])";
"linelen  -- Maximum length of final string.";
"string   -- Tokenized string to format.";
"items    -- Items matching tokens in string.";
"Each token should be of the format \"%<integer>\", where <integer> is the number of spaces to left justify (or right justify, if negative) an 'item' in the argument list.";
"For example, an adjusted iteration of the following line could be used to print test scores in a pretty format:";
":format(\"%-3 %18 %-3%\", 1, \"Quinn\", 98)";
"-> \"  1 Quinn             98%\"";
if (typeof(string = args[1]) == NUM)
len = args[1];
string = args[2];
args[1..2] = {};
else
len = 0;
args[1..1] = {};
endif
count = 1;
spaces = this.spaces;
while (string && (m = match(string, "%(%%%(-?[0-9]+%)%)")))
info = m[3][1];
ilen = tonum(substitute("%2", m));
item = tostr(`args[count] ! E_RANGE');
if (ilen > 0)
string[info[1]..info[2]] = (item + spaces)[1..ilen];
elseif (ilen)
item = spaces + item;
item = item[max(((l = length(item)) - abs(ilen)) + 1, 1)..l];
string[info[1]..info[2]] = item;
else
string[info[1]..info[2]] = item;
endif
count = count + 1;
endwhile
if (len > 0)
string = (string + spaces)[1..min(len, $)];
elseif (len)
string = spaces + string;
string = string[max(((l = length(string)) - abs(len)) + 1, 1)..l];
endif
return string;
.
#20:65
":lines_to_list(@stuff)";
"Merge the given stuff into a list of non-lists (strings and lists of strings are expected).";
l = {};
for e in (args)
if (typeof(e) == LIST)
l = {@l, @this:lines_to_list(@e)};
else
l = {@l, e};
endif
endfor
return l;
.
#20:66
":lines_to_string(@stuff)";
"Merge the given stuff into a single string.";
s = "";
for e in (args)
if (typeof(e) == LIST)
s = tostr(s, this:lines_to_string(@e));
else
s = tostr(s, e);
endif
endfor
return s;
.
#20:67
":english_encrypt(text)";
text = args[1];
vowels = "aeiou";
v_pool = vowels;
v_subs = "     ";
consonants = "bcdfghjklmnpqrstvwxyz";
c_pool = consonants;
c_subs = "                     ";
for i in [1..length(text)]
old = text[i];
if (p = index(vowels, old))
sub = v_subs[p];
if (sub == " ")
x = random(length(v_pool));
sub = v_pool[x];
v_subs[p] = sub;
v_pool[x..x] = "";
endif
elseif (p = index(consonants, old))
sub = c_subs[p];
if (sub == " ")
x = random(length(c_pool));
sub = c_pool[x];
c_subs[p] = sub;
c_pool[x..x] = "";
endif
else
sub = old;
endif
text[i] = match(old, "[A-Z]", 1) ? $string_utils:capitalize(sub) | sub;
endfor
return text;
.
#20:68
":smart_columnize(LIST strings[, NUM max_columns[, NUM width])";
"Columnize the given list of strings without cutting any of them.";
nargs = length(args);
max_c = min(max((nargs > 1) ? args[2] | 4, 1), 20);
width = (nargs > 2) ? args[3] | ($code_utils:call_verb(player, "linelen") || 79);
lens = strs = done = full = {};
i = max_c + 1;
while (i = i - 1)
lens = {@lens, i};
strs = {@strs, {}};
endwhile
for s in (args[1])
l = min(max(width / length(s), 1), max_c);
if (l == 1)
full = {@full, s};
elseif (i = l in lens)
strs[i] = {@strs[i], s};
endif
endfor
for i in [1..length(lens)]
done = {@done, @this:columnize(strs[i], lens[i], width)};
endfor
return {@done, @full};
.
#20:69
":quoted_english_list(@args)";
"Same as $string_utils:english_list, except each argument is quoted with quotation marks.";
l = {};
for arg in (args[1])
l = {@l, tostr("\"", arg, "\"")};
endfor
args[1] = l;
return this:english_list(@args);
.
#20:70
":to_bytes(NUM num[, NUM sigdig])";
{n, ?sigdig = 2} = args;
neg = n < 0;
k = 1024.0;
n = tofloat(abs(n));
if (n < 1000.0)
s = tostr(floatstr(n, sigdig), "B");
elseif ((d = n / k) < 1000.0)
s = tostr(floatstr(d, sigdig), "KB");
elseif ((d = n / (k ^ 2)) < 1000.0)
s = tostr(floatstr(d, sigdig), "MB");
elseif ((d = n / (k ^ 3)) < 1000.0)
s = tostr(floatstr(d, sigdig), "GB");
else
s = tostr(floatstr(n / (k ^ 4), sigdig), "TB");
endif
return neg ? tostr("-", s) | s;
"96-10-20: Use and abuse cool new float stuff. <Quinn>";
.
#20:71
":strhash(STR)";
"Return 'unique' integer describing string.";
{s} = args;
return call_function("string_hash_as_int", s);
.
#20:72
":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:73
"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:74
":quoted_list(LIST stuff[, STR|LIST quote_characters])";
"Return an `english list' of the elements of stuff quoted with the given quote characters.  The quotes can be either a string or a list containing at least the opening and closing quotes.";
{oldstuff, ?quotes = "\"\"", @rest} = args;
newstuff = {};
for elm in (oldstuff)
newstuff = {@newstuff, tostr(quotes[1], elm, quotes[2])};
endfor
return this:english_list(newstuff, @rest);
.
#21:0
"make_exit(spec, source, dest,[really-create-or-recycle])";
"Uses $recycler by default; supplying really-create as 0 suppresses this.";
"Returns the object number as a list if successful, 0 if not.";
set_task_perms(caller_perms());
spec = args[1];
source = args[2];
dest = args[3];
if (length(args) > 3)
recreate_enabled = args[4];
else
recreate_enabled = 1;
endif
exitparent = (length(args) > 4) ? args[5] | $exit;
if (recreate_enabled)
exit = $recycler:_create(exitparent);
else
exit = create(exitparent);
endif
if (typeof(exit) == ERR)
player:tell(exit);
return;
endif
$building_utils:set_names(exit, spec);
exit.source = source;
exit.dest = dest;
source_ok = source:add_exit(exit);
dest_ok = dest:add_entrance(exit);
move(exit, $nothing);
via = $string_utils:from_value(setadd(exit.aliases, exit.name), 1);
if (source_ok)
player:tell("Exit from ", source.name, " (", source, ") to ", dest.name, " (", dest, ") via ", via, " created with id ", exit, ".");
if (!dest_ok)
player:tell("However, I couldn't add ", exit, " as a legal entrance to ", dest.name, ".  You may have to get its owner, ", dest.owner.name, " to add it for you.");
endif
return {exit};
elseif (dest_ok)
player:tell("Exit to ", dest.name, " (", dest, ") via ", via, " created with id ", exit, ".  However, I couldn't add ", exit, " as a legal exit from ", source.name, ".  Get its owner, ", source.owner.name, " to add it for you.");
return {exit};
elseif (0)
"I considered using this for awhile. -- Lambda";
player:tell("Exit to ", dest.name, " (", dest, ") via ", via, " created with id ", exit, ".  However, I couldn't add ", exit, " as EITHER a legal exit from ", source.name, " OR as a legal entrance to ", dest.name, ".  Get their owners, ", source.owner.name, " and ", dest.owner.name, ", respectively, to add it for you.");
return 0;
else
if (recreate_enabled)
$recycler:_recycle(exit);
else
recycle(exit);
endif
player: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 = args[1];
parent = args[2];
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.";
for c in (children(object))
chparent(c, parent(object));
endfor
set_task_perms(who);
if ($object_utils:has_callable_verb(object, "recycle"))
`object:recycle() ! ANY';
endif
chparent(object, #-1);
for p in (properties(object))
delete_property(object, p);
endfor
for v in (verbs(object))
delete_verb(object, "0");
endfor
for item in (object.contents)
move(item, #-1);
endfor
chparent(object, parent);
object.name = "";
if ($object_utils:has_verb(parent, "initialize"))
`object:initialize() ! ANY';
endif
object.r = 0;
object.f = 0;
object.w = 0;
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..length(spec)], ",");
name = spec[1..colon - 1];
endif
return {name, $list_utils:map_arg($string_utils, "trim", aliases)};
.
#21:4
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:5
":set_spawned_names(OBJ creation)";
"Set the names and aliases of creation to those directed by its spawned_name and spawned_aliases verbs/properties.";
new = args[1];
if (!new:is_writable_by(caller_perms(), caller))
return raise(E_PERM);
endif
mom = parent(new);
if (nn = $code_utils:verb_or_property(mom, "spawned_name"))
r1 = new:set_name(nn);
else
r1 = new:set_name(mom.name);
endif
if (na = $code_utils:verb_or_property(mom, "spawned_aliases"))
r2 = new:set_aliases(na);
else
r2 = new:set_aliases(nn ? {nn} | mom.aliases);
endif
return r1 && r2;
.
#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();
mailfrom = (length(args) == 2) ? args[2] | whodunnit;
if (!whodunnit.wizard)
return E_PERM;
elseif (!(valid(victim = args[1]) && (is_player(victim) && $object_utils:isa(victim, $player))))
return E_INVARG;
elseif (victim.programmer)
return E_NONE;
elseif (0 && this:check_prog_restricted(victim))
return E_INVARG;
elseif (typeof(e = victim.programmer = 1) == ERR)
return e;
else
$quota_utils:adjust_quota_for_programmer(victim);
if (!$object_utils:isa(victim, $prog))
if (typeof(e = chparent(victim, $prog)) == 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
$mail_agent:send_message(mailfrom, {$new_prog_log, victim}, tostr("@programmer ", victim.name, " (", victim, ")"), tostr("I just gave ", victim.name, " a programmer bit."));
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";
if (!caller_perms().wizard)
return E_PERM;
elseif (!(valid(victim = args[1]) && $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 ($object_utils:isa(victim, $prog))
victim.programmer = 1;
else
victim.programmer = $player.programmer;
endif
if (!{@args, 0}[2])
$wiz_utils:set_owner(victim, victim);
endif
$player_db:insert(name, victim);
for a in (setremove(aliases = victim.aliases, name))
if (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).";
if (!valid(object = args[1]))
return E_INVIND;
elseif (!caller_perms().wizard)
return E_PERM;
elseif (!(valid(newowner = args[2]) && is_player(newowner)))
return E_INVARG;
endif
suspendok = {@args, 0}[3];
oldowner = object.owner;
object.owner = newowner;
for pname in ($object_utils:all_properties(object))
if (suspendok && ((ticks_left() < 5000) || (seconds_left() < 2)))
suspend(0);
endif
perms = property_info(object, pname)[2];
if (index(perms, "c"))
set_property_info(object, pname, {newowner, perms});
endif
endfor
if ($object_utils:isa(oldowner, $player))
if (is_player(oldowner) && (object != oldowner))
$quota_utils:reimburse_quota(oldowner, object);
endif
if (typeof(oldowner.owned_objects) == LIST)
oldowner.owned_objects = setremove(oldowner.owned_objects, object);
endif
endif
if ($object_utils:isa(newowner, $player))
if (object != newowner)
$quota_utils:charge_quota(newowner, object);
endif
if (typeof(newowner.owned_objects) == LIST)
newowner.owned_objects = setadd(newowner.owned_objects, object);
endif
endif
return 1;
.
#24:3
":set_property_owner(object,prop,newowner[,suspendok])  changes the ownership of object.prop to newowner.  If the property is !c, changes the ownership on all of the descendents as well.  Otherwise, we just chown the property on the object itself and give a warning if newowner!=object.owner (--Rog thinks this is a server bug that one is able to do this at all...).";
if (!caller_perms().wizard)
return E_PERM;
elseif (!(info = property_info(object = args[1], pname = args[2])))
"... handles E_PROPNF and invalid object errors...";
return info;
elseif (!is_player(newowner = args[3]))
return E_INVARG;
elseif (index(info[2], "c"))
if ({@args, 0}[4] / 2)
"...(recursive call)...";
"...child property is +c while parent is -c??...RUN AWAY!!";
return E_NONE;
else
set_property_info(object, pname, listset(info, newowner, 1));
return (newowner == object.owner) || E_NONE;
endif
else
set_property_info(object, pname, listset(info, newowner, 1));
if ((suspendok = {@args, 0}[4] % 2) && ((ticks_left() < 10000) || (seconds_left() < 2)))
suspend(0);
endif
suspendok = 2 + suspendok;
for c in (children(object))
this:set_property_owner(c, pname, newowner, suspendok);
endfor
return 1;
endif
.
#24:4
":unset_player(victim[,newowner])  => 1 or error";
"Reset victim's player flag, chown victim to newowner (if given), remove all of victim's names and aliases from $player_db.";
if (!caller_perms().wizard)
return E_PERM;
elseif (!valid(victim = args[1]))
return E_INVARG;
elseif (!is_player(victim))
return E_NONE;
endif
if (length(args) >= 2)
$wiz_utils:set_owner(victim, args[2]);
endif
victim.programmer = 0;
victim.wizard = 0;
set_player_flag(victim, 0);
if ($player_db.frozen)
player:tell("Warning:  player_db is in the middle of a :load().");
endif
$player_db:delete2(victim.name, victim);
for a in (victim.aliases)
$player_db:delete2(a, victim);
endfor
return 1;
.
#24:5
":set_property_flags(object,prop,flags[,suspendok])  changes the permissions on object.prop to flags.  Unlike a mere set_property_info, this changes the flags on all descendant objects as well.  We also change the ownership on the descendent properties where necessary.";
object = args[1];
pname = args[2];
flags = args[3];
suspendok = {@args, 0}[4];
perms = caller_perms();
if (!(info = property_info(object, pname)))
"... handles E_PROPNF and invalid object errors...";
return info;
elseif ($set_utils:difference($string_utils:char_list(flags), {"r", "w", "c"}))
"...not r, w, or c?...";
return E_INVARG;
elseif ((pinfo = property_info(parent(object), pname)) && (flags != pinfo[2]))
"... property doesn't actually live here...";
"... only allowed to correct so that this property matches parent...";
return E_INVARG;
elseif (!(perms.wizard || (info[1] == perms)))
"... you have to own the property...";
return E_PERM;
elseif (!(((!(c = index(flags, "c"))) == (!index(info[2], "c"))) || $perm_utils:controls(perms, object)))
"... if you're changing the c flag, you have to own the object...";
return E_PERM;
else
if (c)
set_property_info(object, pname, {object.owner, kflags = flags});
else
set_property_info(object, pname, kflags = listset(info, flags, 2));
endif
for kid in (children(object))
this:_set_property_flags(kid, pname, kflags, suspendok);
endfor
return 1;
endif
.
#24:6
"_set_property_flags(object, pname, {owner, flags} or something+\"c\", suspendok)";
"auxiliary to :set_property_flags... don't call this directly.";
if (caller != this)
return E_PERM;
endif
if (args[4] && $command_utils:running_out_of_time(0))
suspend(0);
endif
object = args[1];
if (typeof(args[3]) != LIST)
set_property_info(object, args[2], {object.owner, args[3]});
else
set_property_info(@args[1..3]);
endif
for kid in (children(object))
this:_set_property_flags(@listset(args, kid, 1));
endfor
.
#24:7
"Generate a random password of length args[1].  Alternates vowels and consonants, for maximum pronounceability.  Uses its own list of consonants which exclude F and C and K to prevent generating obscene sounding passwords.";
vowels = "aeiouy";
consonants = "bdghjlmnpqrstvwxz";
len = tonum(args[1]);
if (len)
alt = random(2) - 1;
s = "";
for i in [1..len]
s = s + (alt ? vowels[random(6)] | consonants[random(17)]);
alt = !alt;
endfor
return s;
else
return E_INVARG;
endif
.
#24:8
":queued_tasks(player) => list of queued tasks for that player.";
"shouldn't the server builtin should work this way?  oh well";
set_task_perms(caller_perms());
if (typeof(e = set_task_perms(who = args[1])) == ERR)
return e;
elseif (who.wizard)
tasks = {};
for t in (queued_tasks())
if (t[5] == who)
tasks = {@tasks, t};
endif
endfor
return tasks;
else
return queued_tasks();
endif
.
#24:10
"Return 1 if args[1] is a newted player.  0 if not, or if some error.  Maybe should do better in the error case.  Feel free :-)";
if (!caller_perms().wizard)
return E_PERM;
else
"return verb_code($wiz_utils, \"newt_confunc\") == verb_code(args[1], \"confunc\")";
return verb_info(args[1], "confunc")[1].wizard && (!args[1].wizard);
endif
.
#24:11
player:notify_lines({"", $login:newt_registration_string(), ""});
set_task_perms(this);
boot_player(player);
.
#24:12
if (!caller_perms().wizard)
return E_PERM;
else
player:tell("Beginning initialize_owned:  ", ctime());
for n in [0..tonum(max_object())]
o = toobj(n);
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:13
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);
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);
endfor
endif
endfor
endif
.
#24:14
":connected_wizards() => list of currently connected wizards and players mentioned in .public_identity properties as being wizard counterparts.";
wizzes = $object_utils:leaves($wiz);
wlist = {};
for w in (wizzes)
if (w.wizard)
if (connected_seconds(w))
wlist = setadd(wlist, w);
endif
if (connected_seconds(w.public_identity))
wlist = setadd(wlist, w.public_identity);
endif
endif
endfor
return wlist;
.
#24:15
":all_wizards() => list of all wizards and players mentioned in .public_identity properties as being wizard counterparts.";
wizzes = $object_utils:leaves($wiz);
wlist = {};
for w in (wizzes)
if (w.wizard)
if (is_player(w))
wlist = setadd(wlist, w);
endif
if (is_player(w.public_identity))
wlist = setadd(wlist, w.public_identity);
endif
endif
endfor
return wlist;
.
#24:16
":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 = args[1];
objverb = args[1..2];
newname = args[3];
while (info = verb_info(@objverb))
set_verb_info(@objverb, listset(info, newname, 3));
found = 1;
endwhile
return found;
else
return E_PERM;
endif
.
#24:17
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;
.
#24:18
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:19
if (caller_perms().wizard)
pass();
this.missed_help_counters = this.missed_help_strings = {};
this.disclaimer = {};
endif
.
#24:20
":show_netwho_listing(tell,player_list)";
" prints a listing of the indicated players showing connect sites.";
if (!caller_perms().wizard)
return E_PERM;
endif
who = args[1];
if (!(unsorted = args[2]))
return;
endif
su = $string_utils;
alist = {};
footnotes = {};
nwidth = length("Player 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 = {"Player name", "Last Login", "From Where"};
before = {0, nwidth + 3, (nwidth + length(ctime(0))) - 11};
tell1 = "  " + headers[1];
tell2 = "  " + su:space(headers[1], "-");
for j in [2..3]
tell1 = su:left(tell1, before[j]) + headers[j];
tell2 = su:left(tell2, before[j]) + su:space(headers[j], "-");
endfor
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(length(tell1), 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
.
#24:21
":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 = args[1];
su = $string_utils;
if (!index(where = args[2], "*"))
"Oh good... search for users from a site... the fast way.  No wild cards.";
nl = 0;
bozos = {};
sites = $site_db:find_all_keys(where);
while (sites)
s = sites;
sites = {};
for domain in (s)
for b in ($site_db:find_exact(domain))
if (typeof(b) == STR)
sites = setadd(sites, (b + ".") + domain);
else
bozos = setadd(bozos, b);
nl = max(length(tostr(b, valid(b) ? b.name | "")), nl);
endif
endfor
endfor
endwhile
if (bozos)
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) ? (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:22
":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(name, email, connection)";
"  name is ignored, only check the 'email' parts";
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..length(name)], " 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];
if ($registration_db:suspicious_address(address))
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[length(connection) - 2..length(connection)] == ".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:23
"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 = args[1];
address = args[2];
new = $quota_utils:bi_create($player_class);
new.name = name;
new.aliases = {name};
new.password = crypt(password = $wiz_utils:random_password(5));
new.last_connect_time = $maxint;
"Last disconnect time is creation time, until they login.";
new.last_disconnect_time = time();
if (!(ERR = $wiz_utils:set_player(new)))
return player:tell("An error, ", ERR, " occurred while trying to make ", new, " a player. The database is probably inconsistent.");
endif
new.email_address = address;
$quota_utils:initialize_quota(new);
$registration_db:add(new, address, @args[3..length(args)]);
move(new, $player_start);
new.programmer = $player_class.programmer;
return {new, password};
.
#24:24
":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
{preface, name, address, new, password} = args;
msg = {preface};
msg = {@msg, "", tostr("A character has been created, with name \"", name, "\" and password \"", password, "\".  (Passwords are case sensitive.)")};
msg = {@msg, "", tostr($network.moo_name, " is at ", $network.site, ", port ", $network.port, ".")};
msg = {@msg, "", @$registration_db.new_player_mail};
msg2 = {};
for x in (msg)
msg2 = {@msg2, @$generic_editor:fill_string(x, 75)};
endfor
return $network:sendmail(address, (("Your " + $network.moo_name) + " character, ") + name, "Reply-to: " + $login.registration_address, @msg2);
.
#24:25
":do_make_player([send_mail, ]name, email[, comment])";
if (!caller_perms().wizard)
return E_PERM;
endif
if (typeof(args[1]) == NUM)
send_mail = args[1];
args = listdelete(args, 1);
else
send_mail = 0;
endif
name = args[1];
email = args[2];
comments = $string_utils:from_list(args[3..length(args)], " ");
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..length(x)])) + "]" | ""));
endfor
if (!reason)
reason = "Already registered.";
endif
endif
if (reason)
player:notify(reason);
if (!$command_utils:yes_or_no("Create character anyway? "))
player:notify("Okay.");
return;
endif
endif
new = $wiz_utils:make_player(name, email);
player:notify(tostr(name, " (", new[1], ") created with password `", new[2], "' for ", email, " ", comments));
$mail_agent:send_message(player, $new_player_log, tostr(name, " (", new[1], ")"), tostr(email, comments ? " " + comments | ""));
if ($network.active)
if (send_mail || $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("Okay.");
endif
else
player:notify("Sorry, the network isn't active.");
endif
.
#24:26
"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;
player:notify(tostr(who.name, " (", who, ") formerly ", old ? old | "unregistered", ", registered at ", email, ".  ", comments ? (" [" + comments) + "]" | ""));
.
#24:27
"do_new_password(who, [password])";
if (!caller_perms().wizard)
return E_PERM;
endif
{whospec, ?password} = args;
if (typeof(whospec) == STR)
whostr = whospec;
who = $string_utils:match_player(whostr);
if ($command_utils:player_match_failed(who, whostr))
return;
endif
whostr = $string_utils:nn(who);
elseif (typeof(whospec) == OBJ)
who = whospec;
whostr = $string_utils:nn(who);
else
raise(E_TYPE);
endif
player:notify(tostr("About to change password for ", whostr, ". Old encrypted password is \"", who.password, "\""));
if (!password)
password = $wiz_utils:random_password(6);
endif
who.password = crypt(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, " <", who.email_address, ">?")))
player:notify("Sending the password via email.");
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
else
player:tell("No mail sent.");
endif
.
#24:28
":starting_quota(user) -> Initial quota for the given user.";
"Give 1.5 times initial quota to those from RealTime.";
quota = this.default_player_quota;
if ((args && (addr = args[1].email_address)) && match(addr, "bga.com$"))
quota = quota + (quota / 2);
endif
return quota;
.
#24:29
"Messages told to all the MOO when a checkpoint begins, ends, or fails.";
"Method provided in case some fancy substitutions wanna be done, such as last checkpoint times, durations, etc.";
elapsed = $dump_finished - $dump_started;
return strsub(this.(verb), "$elapsed", $time_utils:english_time(elapsed));
.
#24:31
":check_password(user, str) -> True if str is user's password.";
if (!caller_perms().wizard)
return E_PERM;
endif
pwd = args[1].password;
return crypt(args[2], pwd[1..2]) == pwd;
.
#24:32
":announcements_for(user)";
"Notify user of any announcements of system-wide importance.";
return;
user = args[1];
line = $string_utils:centre("special announcement", user:linelen(), "-");
if (user.programmer)
user:notify(line);
user:notify("There will be a meeting of all wizards, GMs, and programmers at 10:30pm CT on THURSDAY in the meeting room to the east of the Infirmary.  All are encouraged to attend.");
user:notify(line);
endif
.
#24:33
":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 = args[1];
newowner = args[2];
if (!is_player(newowner))
return E_INVARG;
endif
same = object.owner == newowner;
for vnum in [0..length(verbs(object)) - 1]
verb = tostr(vnum);
info = verb_info(object, verb);
if (!((info[1] != object.owner) && (valid(info[1]) && is_player(info[1]))))
same = same && (info[1] == newowner);
set_verb_info(object, verb, listset(info, newowner, 1));
endif
endfor
for prop in (properties(object))
$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:34
":is_builder(user)";
"Return true if the given user is allowed to:";
" * See and match on object numbers and special matching constructs (~user),";
" * @move unrestricted,";
" * List object attributes with commands such as @d.";
"Other priveleges may be available, usually related to the above.";
{user} = args;
if (!$recycler:valid(user))
return 0;
endif
return user.programmer || $object_utils:isa(user, $builder);
.
#25:0
return ((caller == this) || caller_perms().wizard) ? pass(@args) | E_PERM;
.
#25:1
return;
.
#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 tonum(hnum[1..9]) && tonum(hnum[6..len]);
else
return tonum(hnum);
endif
"SLEAZY CODE ALERT";
"... what I wanted to do was return tonum(strsub(args[1],\".\",\"\"))";
"... but on a 32-bit machine, this has a 1 in 4294967296 chance of failing";
"... (e.g., on \"42.94.967.296\", though I'll grant this particular example";
"...  entails some very strange subnetting on net 42, to say the least).";
"... So we do something that is guaranteed to work so long as internet";
"... addresses stay under 32 bits --- a while yet...";
"";
"... As soon as we're sure match() is working, this will become a one-liner:";
return match(args[1], "[0-9]+%.[0-9]+%.[0-9]+%.[0-9]+");
.
#25:4
if (caller_perms().wizard)
pass();
this:load();
endif
.
#25:6
":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..length(domain)];
else
return;
endif
dot = index(domain, ".");
endwhile
if (!(prev in l))
this:insert(domain, {@l, prev});
endif
return;
endif
.
#26:0
"sin(x) -- given x in degrees, sin(x) will return the value of the sine";
"function at x times 10000";
x = ((args[1] + 45) % 360) - 45;
if (x < 0)
return -this:sin(-x);
elseif (x > 225)
return -this:xcos(x - 270);
elseif (x > 135)
return -this:xsin(x - 180);
elseif (x > 45)
return this:xcos(x - 90);
else
return this:xsin(x);
endif
.
#26:1
"cos(x) -- given x in degrees, cos(x) will return cosine evaluated at x";
"times 10000";
return this:sin(90 - args[1]);
.
#26:2
"tan(x) -- given x in degrees, tan(x) will calculate the tangent at x";
"times 10000";
return (this:sin(args[1]) * 10000) / this:cos(args[1]);
.
#26:3
"xsin(x) -- calculates the taylor approximation for the sine function";
x = args[1];
if ((x * x) > this.taylor)
return ((this:xsin(x / 2) * this:xcos((x + 1) / 2)) + (this:xsin((x + 1) / 2) * this:xcos(x / 2))) / 10000;
else
return (x * (17453000 - ((x * x) * 886))) / 100000;
endif
.
#26:4
"xcos(x) -- calculates the taylor approximation for the cosine function";
x = args[1];
if ((x * x) > this.taylor)
return ((this:xcos(x / 2) * this:xcos((x + 1) / 2)) - (this:xsin(x / 2) * this:xsin((x + 1) / 2))) / 10000;
else
return (1000000000 - ((x * x) * (152309 + ((4 * x) * x)))) / 100000;
endif
.
#26:5
"factorial(n) -- returns n factorial for 0 <= n (<= 12).";
if ((number = args[1]) < 0)
return E_INVARG;
endif
fact = 1;
for i in [2..number]
fact = fact * i;
endfor
return fact;
.
#26:6
"pow(x,n) -- returns x raised to the nth power. n must be >= 0.";
if ((power = args[2]) < 0)
return E_INVARG;
endif
n = args[1];
if (power % 2)
ret = n;
else
ret = 1;
endif
while (power = power / 2)
n = n * n;
if (power % 2)
ret = ret * n;
endif
endwhile
return ret;
.
#26:7
"fibonacci(n) -- calculates the fibonacci numbers to the nth term";
"and returns them in a list. n must be >= 0.";
x0 = 0;
x1 = 1;
if ((n = args[1]) < 0)
return E_INVARG;
elseif (n == 0)
return {x0};
else
x = {x0, x1};
for i in [2..n]
len = length(x);
x = {@x, x[len - 1] + x[len]};
endfor
return x;
endif
.
#26:8
"geometric(x,n) -- calculates the value of the geometric series at x to ";
"the nth term. i.e., approximates 1/(1-x) when |x| < 1. this, of course,";
"is impossible in MOO, but someone may find it useful in some way.";
"n defaults to 5. n must be >= 0.";
n = args[1];
order = (length(args) > 1) ? args[2] | 5;
x = 1;
for i in [1..order]
x = x + this:pow(n, i);
endfor
return x;
.
#26:9
"divmod(n,d) => {q,r} such that n = dq + r";
"  handles negative numbers correctly   0<=r<d if d>0, -d<r<=0 if d<0.";
n = args[1];
d = args[2];
r = ((n % d) + d) % d;
q = (n - r) / d;
return {q, r};
.
#26:10
"combinations(n,r) -- returns the number of ways one can choose r";
"objects from n distinct choices.";
"C(n,r) = n!/[r!(n-r)!]";
"  overflow may occur if n>29...";
n = args[1];
r = args[2];
if (0 > (r = min(r, n - r)))
return 0;
else
c = 1;
n = n + 1;
for i in [1..r]
c = (c * (n - i)) / i;
endfor
return c;
endif
.
#26:11
"permutations(n,r) -- returns the number of ways possible for one to";
"order r objects given n distinct locations.";
"P(n,r) = n!/(n-r)!";
n = args[1];
r = args[2];
return this:factorial(n) / this:factorial(n - r);
.
#26:12
"simpson({a,b},{f(a),f((a+b)/2),f(b)}) -- given two endpoints, a and b,";
"and the functions value at a, (a+b)/2, and b, this will calculate";
"a numerical approximation of the integral using simpson's rule.";
"the answer is returned as {integer,fraction}";
point = args[1];
fcn = args[2];
return this:parts((point[2] - point[1]) * ((fcn[1] + (4 * fcn[2])) + fcn[3]), 6);
.
#26:13
"parts(n,q[,i]) -- returns a decomposition of n by q into integer and";
"floating point parts with i = the number of digits after the decimal.";
"i defaults to 5.";
"warning: it is quite easy to hit maxint which results in unpredictable";
"         results";
parts = {(n = args[1]) / (q = args[2]), n % q};
i = (length(args) > 2) ? args[3] | 5;
return {parts[1], (parts[2] * this:pow(10, i)) / q};
.
#26:14
return sqrt(args[1]);
"sqrt(n) => largest integer <= square root of n.  Uses Newton's method.";
"obsolete now; left for documentation purposes.";
n = args[1];
if (n < 0)
return E_RANGE;
elseif (n)
x1 = n;
while (x1 > (x2 = (x1 + (n / x1)) / 2))
x1 = x2;
endwhile
return x1;
else
return 0;
endif
.
#26:15
"arctan(y/x) == arctan(x,y) => angle in degrees.";
if (length(args) < 2)
sin = args[1];
cos = 10000;
else
sin = args[2];
cos = args[1];
endif
if (sin < 0)
return -this:arctan(cos, -sin);
elseif (cos < 0)
return 180 - this:arctan(-cos, sin);
elseif (sin > cos)
return 90 - this:arctan(sin, cos);
else
tan = (sin * 10000) / cos;
a = $list_utils:find_insert(this.tangents, tan - 1);
if ((this.tangents[a] - tan) < (tan - {0, @this.tangents}[a]))
return a;
else
return a - 1;
endif
endif
.
#26:16
"div(n,d) => q such that n = dq + r and  (0<=r<d if d>0, -d<r<=0 if d<0).";
return this:divmod(@args)[1];
.
#26:17
"A correct mod function.";
"mod(n,d) => r such that n = dq + r and (0<=r<d if d>0 or -d<r<=0 if d<0).";
n = args[1];
d = args[2];
return ((n % d) + d) % d;
.
#26:18
"returns 10000 exp (x/10000)";
"The accuracy seems to be ~0.1% for 0<x<4";
x = args[1];
if (x < 0)
z = this:(verb)(-x);
#78:tell("->", x, "  ", z);
return (100000000 + (z / 2)) / z;
elseif (x > 1000)
z = this:(verb)(x / 2);
if (z > 1073741823)
return $maxint;
"maxint for overflows";
elseif (z > 460000)
z = ((z + 5000) / 10000) * z;
elseif (z > 30000)
z = ((((z + 50) / 100) * z) + 50) / 100;
else
z = ((z * z) + 5000) / 10000;
endif
if (x % 2)
return z + ((z + 5000) / 10000);
else
return z;
endif
else
return ((10000 + x) + (((x * x) + 10000) / 20000)) + ((((x * x) * x) + 300000000) / 600000000);
endif
.
#26:19
"random(): returns a random number in the following manner:";
"random(n > 0) will return a number in the range 0 to n";
"random(n < 0) will return a number in the range n to 0";
prob = args[1];
mod = (prob < 0) ? -1 | 1;
return (mod * random(abs(prob + mod))) - mod;
.
#26:20
"random_range(range [,mean]): returns a random number 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 = args[1];
mean = (length(args) > 1) ? args[2] | 0;
return mean + (((random(2) == 1) ? -1 | 1) * this:random(range));
.
#26:21
"is_prime(number) returns 1 if the number is prime or 0 if it isn't.";
"of course, only positive numbers are candidates for primality.";
number = args[1];
if (number == 2)
return 1;
elseif ((number < 2) || ((number % 2) == 0))
return 0;
else
choice = 3;
while (((denom = choice * choice) <= number) && (denom > 0))
if ((seconds_left() < 2) || (ticks_left() < 25))
suspend(0);
endif
if ((number % choice) == 0)
return 0;
endif
choice = choice + 2;
endwhile
endif
return 1;
.
#26:22
bl1 = this:BLFromInt(args[1]);
bl2 = this:BLFromInt(args[2]);
blOut = {};
for i in [1..32]
blOut = {@blOut, bl1[i] && bl2[i]};
endfor
return this:IntFromBL(blOut);
.
#26:23
bl1 = this:BLFromInt(args[1]);
bl2 = this:BLFromInt(args[2]);
blOut = {};
for i in [1..32]
blOut = {@blOut, bl1[i] || bl2[i]};
endfor
return this:IntFromBL(blOut);
.
#26:24
bl1 = this:BLFromInt(args[1]);
bl2 = this:BLFromInt(args[2]);
blOut = {};
for i in [1..32]
blOut = {@blOut, bl1[i] != bl2[i]};
endfor
return this:IntFromBL(blOut);
.
#26:25
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:26
x = args[1];
l = {};
firstbit = x < 0;
if (firstbit)
x = x + $minint;
endif
for i in [1..31]
l = {x % 2, @l};
x = x / 2;
endfor
return {firstbit, @l};
.
#26:27
bl = args[1];
x = 0;
for l in (bl)
x = x * 2;
x = x + l;
endfor
return x;
.
#26:28
"gcd(num1,num2): find the greatest common divisor of the two numbers";
"using the division algorithm. the absolute values of num1 and num2 are";
"used without loss of generality.";
num1 = abs(args[1]);
num2 = abs(args[2]);
max = max(num1, num2);
min = min(num1, num2);
if (r1 = max % min)
while (r2 = min % r1)
min = r1;
r1 = r2;
endwhile
return r1;
else
return min;
endif
.
#26:29
"lcm(num1,num2): find the least common multiple of the two numbers.";
"we shall use the positive lcm value without loss of generality.";
"since we have gcd already, we'll just use lcm*gcd = num1*num2";
num1 = abs(args[1]);
num2 = abs(args[2]);
return (num1 * num2) / this:gcd(num1, num2);
.
#26:30
"are_rel_prime(num1,num2): returns 1 if num1 and num2 are relatively";
"prime.";
"since we have gcd, this is pretty easy.";
if (this:gcd(args[1], args[2]) == 1)
return 1;
else
return 0;
endif
.
#26:31
":base_conversion(num|string, oldbase, newbase [,sens])";
"Call with first arg either a number or a string, being the number";
"desired for conversion. capital letters denote values from 10-35;";
"lowercase letters from 36 to 61. Maximal base is 62.";
"You will be unable to use the extra 26 lowercases as separate unless";
"you pass a nonzero fourth argument. Passing zero or none uses the";
"default value, which is to have AAAA=aaaa.";
"The second and third arguments should be the base of the number and";
"the base you want it in, respectively.";
"Any of the arguments can be strings or nums, but high-base numbers";
"will need to be strings. This returns a string.";
"Any problems, talk to Ozymandias.";
sensitive = 0;
if (length(args) < 3)
return E_INVARG;
elseif (length(args) == 4)
sensitive = tonum(args[4]);
endif
result = 0;
thenum = tostr(args[1]);
origbase = tonum(args[2]);
newbase = tonum(args[3]);
if ((((origbase < 2) || (newbase < 2)) || (origbase > 62)) || (newbase > 62))
return E_INVARG;
endif
for which in [1..length(thenum)]
value = index(this.base_alphabet, thenum[which], sensitive);
if ((!value) || (value > origbase))
return E_INVARG;
endif
result = ((result * origbase) + value) - 1;
endfor
thestring = "";
if (result < 0)
return E_INVARG;
endif
while (result)
if ((which = (result % newbase) + 1) <= length(this.base_alphabet))
thestring = this.base_alphabet[which] + thestring;
else
return E_INVARG;
endif
result = result / newbase;
endwhile
return thestring;
.
#26:32
"exp(x[,n]) -- calculates an nth order taylor approximation for e^x.";
"n defaults to 5. Any n given must be >= 0. you need to divide the result";
"the answer will be returned as {integer part,fractional part}";
x = args[1];
n = (length(args) > 1) ? args[2] | 5;
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:33
":norm(a,b,c,d...) => sqrt(a^2+b^2+c^2+...)";
m = max(max(@args), -min(@args));
logm = length(tostr(m));
if (logm <= 4)
s = 0;
for a in (args)
s = s + (a * a);
endfor
return sqrt(s);
else
factor = tonum("1" + "0000000"[1..logm - 4]);
s = 0;
for a in (args)
a = a / factor;
s = s + (a * a);
endfor
return sqrt(s) * factor;
endif
.
#26:34
":sum(num, num, num ...) => Total of all arguments added together.";
total = 0;
for number in ((typeof(x = args[1]) == LIST) ? x | args)
total = total + number;
endfor
return total;
.
#26:35
":to_percent(NUM cur[, NUM max]) => percent of cur/max";
"If max is Zero, just return cur.";
{cur, max} = args;
return toint((tofloat(cur) / (tofloat(max) || 1.0)) * 100.0);
"--- pre-float server stuff below ---";
cur = args[1];
max = (length(args) > 1) ? args[2] | 100;
return max ? ((cur * 10000) / max) / 100 | cur;
.
#26:36
":from_percent(NUM of_this, NUM percentage)";
"Return 'percentage' 'of this'.";
{n, pct} = args;
return toint((tofloat(pct) / 100.0) * tofloat(n));
"--- pre-float server stuff below ---";
of_this = args[1];
percentage = args[2];
return (of_this * percentage) / 100;
.
#26:37
":precision(FLOAT Number, INT Digits of Precision) => FLOAT Number";
"Cuts the given number to the given digits of precision.  Uses rounding.";
{digits, pre} = args;
mult = 10.0 ^ pre;
return this:rint(digits * mult) / mult;
.
#26:38
":rint(FLOAT Number) => FLOAT Number";
"Returns the given floating-point number rounded to the nearest integer, as a floating-point number.  In case of ties, rounds away from 0.";
{f} = args;
return trunc((f > 0.0) ? f + 0.5 | (f - 0.5));
.
#27:0
"Returns the set union of all of the lists provided as arguments.";
if (!args)
return {};
endif
set = args[1];
for l in (listdelete(args, 1))
for x in (l)
set = setadd(set, x);
endfor
endfor
return set;
.
#27:1
"Returns the set intersection of all the lists provided as arguments.";
if (!args)
return {};
endif
max = 0;
result = args[1];
for set in (listdelete(args, 1))
if (length(result) < length(set))
set1 = result;
set2 = set;
else
set1 = set;
set2 = result;
endif
for x in (set1)
if (!(x in set2))
set1 = setremove(set1, x);
endif
endfor
result = set1;
endfor
return result;
.
#27:2
"Usage:  diff(set 1, set 2, ..., set n)";
"Returns all elements of set 1 that are not in sets 2..n";
set = args[1];
for l in (listdelete(args, 1))
for x in (l)
set = setremove(set, x);
endfor
endfor
return set;
.
#27:3
"True if the first list given is a superset of all subsequent lists.";
"False otherwise.  {} is a superset of {} and nothing else; anything is";
"a superset of {}.  If only one list is given, return true.";
super = args ? args[1] | {};
for l in (listdelete(args, 1))
for x in (l)
if (!(x in super))
return 0;
endif
endfor
endfor
return 1;
.
#27:4
"Usage:  exclusive_or(set, set, ...)";
"Return the set of all elements that are in exactly one of the input sets";
"For two sets, this is the equivalent of (A u B) - (A n B).";
if (!args)
return {};
endif
set = so_far = args[1];
for l in (listdelete(args, 1))
for x in (l)
if (x in so_far)
set = setremove(set, x);
else
set = setadd(set, x);
endif
endfor
so_far = {@so_far, @l};
endfor
return set;
.
#27:5
"Usage:  diff(set 1, set 2, ..., set n)";
"Returns all elements of set 1 that are not in sets 2..n";
set = args[1];
for l in (listdelete(args, 1))
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 = args[1];
set2 = args[2];
while (set1)
elt = set1[1];
set1 = listdelete(set1, 1);
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
.
#28:0
pass();
$prog.help = this;
.
#29:0
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.mail_forward = {player, this};
this.mail_notify = {player};
this.moderated = 1;
.
#29:1
if (!this:is_writable_by(caller_perms()))
return E_PERM;
else
if (msgs = this.messages)
new = msgs[length(msgs)][1] + 1;
else
new = 1;
endif
if (rmsgs = this.messages_going)
lbrm = rmsgs[length(rmsgs)][2];
new = max(new, lbrm[length(lbrm)][1] + 1);
endif
m = args[1];
if (index(m[4], "@programmer ") == 1)
m = {m[1], toobj(args[2]), o = toobj(m[4][index(m[4], "(") + 1..index(m[4], ")") - 1]), o.name};
endif
this.messages = {@msgs, {new, m}};
this.last_msg_date = m[1];
this.last_used_time = time();
return new;
endif
.
#29:2
":display_seq_headers(msg_seq[,cur])";
":display_seq_full(msg_seq[,cur]) => {cur}";
if (!this:ok(caller, caller_perms()))
return E_PERM;
endif
cur = {@args, 0}[2];
read_date = {@args, $maxint, $maxint}[3];
last = ldate = 0;
player:tell("       WHEN           WHO                           BY");
for x in (msgs = this:messages_in_seq(args[1]))
msgnum = $string_utils:right(last = x[1], 4, (cur == x[1]) ? ">" | " ");
ldate = x[2][1];
if (typeof(x[2][2]) != OBJ)
hdr = this:msg_summary_line(@x[2]);
else
hdr = tostr(ctime(ldate)[5..16], "   ", $string_utils:left(tostr(x[2][4], " (", x[2][3], ")"), 30), valid(w = x[2][2]) ? w.name | "??", " (", x[2][2], ")");
endif
player:tell(msgnum, (ldate > read_date) ? ":+ " | ":  ", hdr);
$command_utils:suspend_if_needed(0);
endfor
if (verb == "display_seq_full")
return {last, ldate};
else
player:tell("----+");
endif
.
#29:3
":from_msg_seq(object or list[,mask])";
" => msg_seq of messages from any of these senders";
if (!this:ok(caller, caller_perms()))
return E_PERM;
endif
if (typeof(plist = args[1]) != LIST)
plist = {plist};
endif
i = 1;
fseq = {};
mask = {@args, {1}}[2];
for msg in (this.messages)
if ((!mask) || (i < mask[1]))
elseif ((length(mask) < 2) || (i < mask[2]))
if (msg[2][2] in plist)
fseq = $seq_utils:add(fseq, i, i);
endif
else
mask = mask[3..length(mask)];
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return fseq || ("%f %<has> no messages from " + $string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%n (%#)", plist), "no one", " or "));
.
#29:4
":to_msg_seq(object or list[,mask]) => msg_seq of messages to those people";
if (!this:ok(caller, caller_perms()))
return E_PERM;
endif
if (typeof(plist = args[1]) != LIST)
plist = {plist};
endif
i = 1;
fseq = {};
mask = {@args, {1}}[2];
for msg in (this.messages)
if ((!mask) || (i < mask[1]))
elseif ((length(mask) < 2) || (i < mask[2]))
if (msg[2][3] in plist)
fseq = $seq_utils:add(fseq, i, i);
endif
else
mask = mask[3..length(mask)];
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return fseq || ("%f %<has> no messages about @programmer'ing " + $string_utils:english_list(plist, "no one", " or "));
.
#29:5
":%to_msg_seq/subject_msg_seq(string or list of strings[,mask])";
" => msg_seq of messages containing one of strings in the to line";
if (!this:ok(caller, caller_perms()))
return E_PERM;
endif
if (typeof(nlist = args[1]) != LIST)
nlist = {nlist};
endif
i = 1;
fseq = {};
mask = {@args, {1}}[2];
for msg in (this.messages)
if ((!mask) || (i < mask[1]))
elseif ((length(mask) < 2) || (i < mask[2]))
if (msg[2][4] in nlist)
fseq = $seq_utils:add(fseq, i, i);
endif
else
mask = mask[3..length(mask)];
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return fseq || ("%f %<has> no messages about @programmer'ing " + $string_utils:english_list(nlist, "no one", " or "));
.
#29:6
return this.name + " doesn't understand %%from:";
.
#30:0
"WIZARDLY";
if (args)
"...check for an exact match first...";
search = args[1];
if (property_info(parent(this), search))
if (property_info(this, " " + search))
return {search};
endif
elseif (property_info(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 (0 && (search[1] == "@"))
search = search[2..length(search)];
endif
search = strsub(search, "-", "_");
if (!search)
"...don't try searching for partial matches if the string is empty or @";
"...we'd get *everything*...";
return {};
endif
for prop in (props)
if (((i = index(strsub(prop, "-", "_"), search)) == 1) || ((i == 2) && index(" @", prop[1])))
topics = {@topics, (prop[1] == " ") ? prop[2..length(prop)] | prop};
endif
endfor
return topics;
else
"...return list of all topics...";
props = setremove(properties(this), "");
for p in ($object_utils:all_properties(parent(this)))
if (i = (" " + p) in props)
props = {p, @listdelete(props, i)};
endif
endfor
return props;
endif
.
#30:1
"WIZARDLY";
topic = args[1];
dblist = {@args, {}}[2];
text = this.(topic) || this.(" " + topic);
if (typeof(text) == LIST)
if (text && (text[1] == (("*" + (vb = strsub(text[1], "*", ""))) + "*")))
text = this:(vb)(listdelete(text, 1), dblist);
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..length(name)] + " " | name;
k = index(buckets, key[1]) + 1;
bucket = keys[k];
i = $list_utils:find_insert(bucket, key);
keys[k] = listinsert(bucket, key, i);
names[k] = listinsert(names[k], name, i);
$command_utils:suspend_if_needed(0);
endfor
return $list_utils:append(@names);
.
#30:3
":columnize(@list_of_strings) -- prints the given list of strings in 4 columns.";
sorted = {@args, "", "", ""};
n = length(sorted) / 4;
su = $string_utils;
index = {};
for i in [1..n]
index = {@index, tostr(su:left(sorted[i], 20), su:left(sorted[i + n], 20), su:left(sorted[i + (2 * n)], 20), sorted[i + (3 * n)])};
$command_utils:suspend_if_needed(0);
endfor
return index;
.
#30:4
"{\"*forward*\", topic, @rest}  => text for topic from this help db.";
"{\"*pass*\",    topic, @rest}  => text for topic from next help db.";
"In both cases the text of @rest is appended.  ";
"@rest may in turn begin with a *<verb>*";
text = args[1];
dblist = {@args, {}}[2];
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..length(dblist)]);
else
first = {};
endif
if (2 <= (len = length(text)))
if (text[2] == (("*" + (vb = strsub(text[2], "*", ""))) + "*"))
return {@first, @this:(vb)(text[3..len], dblist)};
else
return {@first, @text[2..len]};
endif
else
return first;
endif
.
#30:5
"{\"*subst*\", @text} => text with the following substitutions:";
"  \"...%[expr]....\" => \"...\"+value of expr (assumed to be a string)+\"....\"";
"  \"%;expr\"         => @(value of expr (assumed to be a list of strings))";
newlines = {};
for old in (args[1])
new = "";
bomb = 0;
while ((prcnt = index(old, "%")) && (prcnt < length(old)))
new = new + old[1..prcnt - 1];
code = old[prcnt + 1];
old = old[prcnt + 2..length(old)];
if (code == "[")
prog = "";
while ((b = index(old + "]", "]")) > (p = index(old + "%", "%")))
prog = (prog + old[1..p - 1]) + old[p + 1];
old = old[p + 2..length(old)];
endwhile
prog = prog + old[1..b - 1];
old = old[b + 1..length(old)];
value = $no_one:eval_d(prog);
if (value[1])
new = tostr(new, value[2]);
else
new = tostr(new, $string_utils:print(value[2]));
bomb = 1;
endif
elseif ((code != ";") || new)
new = (new + "%") + code;
else
value = $no_one:eval_d(old);
if (value[1] && (typeof(r = value[2]) == LIST))
newlines = {@newlines, @r[1..length(r) - 1]};
new = tostr(r[length(r)]);
else
new = tostr(new, $string_utils:print(value[2]));
bomb = 1;
endif
old = "";
endif
endwhile
if (bomb)
newlines = {@newlines, new + old, tostr("@@@ Helpfile alert:  Previous line is messed up; notify ", this.owner.wizard ? "" | tostr(this.owner.name, " (", this.owner, ") or "), "a wizard. @@@")};
else
newlines = {@newlines, new + old};
endif
endfor
return newlines;
.
#30:6
"{\"*index*\" [, title]}";
"This produces a columnated list of topics in this help db, headed by title.";
$command_utils:suspend_if_needed(0);
title = args[1] ? args[1][1] | tostr(this.name, " (", this, ")");
su = $string_utils;
return {"", 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);
string = args[1][1];
if ((!valid(object = $match_utils:literal_object(string))) && (!valid(object = player:my_match_object(string))))
return E_INVARG;
elseif (!(hv = $object_utils:has_verb(object, vname = args[1][2])))
return E_VERBNF;
else
return $code_utils:verb_documentation(hv[1], vname);
endif
.
#30:9
if (typeof(text = this.(fulltopic = args[1])) == ERR)
return text;
else
return {tostr(";;", $code_utils:corify_object(this), ".(", $string_utils:print(fulltopic), ") = $command_utils:read_lines()"), @$command_utils:dump_lines(text)};
endif
.
#31:0
player:tell("Sorry, but guest characters are not allowed to change their passwords.");
.
#31:1
if (!caller_perms().wizard)
return;
endif
player = this;
this:notify(tostr("Sorry, but you've been here for ", $string_utils:from_seconds(connected_seconds(this)), " and someone else wants to be a guest now.  Feel free to come back", @$login:player_creation_enabled(player) ? {" or even create your own character if you want..."} | ($login.registration_address ? {" or send mail to ", $login.registration_address, " to obtain a character of your own."} | {"."})));
fork (0)
oldloc = this.location;
move(this, #-1);
move(this, this.home);
"..force enterfunc to be called so that the newbie gets a room description.";
if ($object_utils:isa(oldloc, $room))
oldloc:announce("In the distance you hear someone's alarm clock going off.");
if (oldloc != this.location)
oldloc:announce(this.name, " wavers and vanishes into insubstantial mist.");
else
oldloc:announce(this.name, " undergoes a wrenching personality shift.");
endif
endif
endfork
.
#31:2
if (!(caller in {this, $sys}))
return E_PERM;
endif
this:log_disconnect();
fork (0)
this.request = 0;
if (this.location != this.home)
this.location:send_user_home(this);
endif
clear_property(this, "options");
for x in (this.settable_properties)
clear_property(this, x);
endfor
for msg in (this.settable_messages)
clear_property(this, msg + "_msg");
endfor
this:set_description(this.default_description);
this:set_gender(this.default_gender);
for x in (this.contents)
this:eject(x);
endfor
for x in (this.features)
this:remove_feature(x);
endfor
clear_property(this, "features");
this:clear_refusals();
for x in ($object_utils:descendants($generic_editor))
if (loc = this in x.active)
x:kill_session(loc);
endif
endfor
this:reset_stats();
endfork
pass(@args);
.
#31:3
"Called by #0:connect_player when this object is about to be used as the next guest character.  Usually returns `this', but if for some reason some other guest character should be used, that player object is returned instead";
if (!caller_perms().wizard)
"...caller is not :do_login_command; doesn't matter what we return...";
return this;
elseif ($login:blacklisted($string_utils:connection_hostname(connection_name(player))))
return #-2;
elseif (!(this in connected_players()))
"...not logged in, no problemo...";
return this;
endif
longest = 900;
"...guests get 15 minutes before they can be dislodged...";
candidate = #-1;
free = {};
for g in (children($guest))
if (!is_player(g))
"...a toaded guest?...";
elseif (!(g in connected_players()))
"...yay; found an unused guest...";
free = {@free, g};
elseif ((t = connected_seconds(g)) > longest)
longest = t;
candidate = g;
endif
endfor
if (free)
candidate = free[random(length(free))];
elseif (valid(candidate))
"...someone's getting bumped...";
candidate:boot();
endif
return candidate;
.
#31:4
return;
.
#31:5
if ($login:player_creation_enabled(player))
player:tell("First @quit, then connect to the MOO again and, rather than doing `connect guest' do `create <name> <password>'");
else
player:tell($login:registration_string());
endif
.
#31:6
return pass(@args);
.
#31:7
":log(islogin,time,where) adds an entry to the connection log for this guest.";
if (caller != this)
return E_PERM;
elseif (length(this.connect_log) < this.max_connect_log)
this.connect_log = {args, @this.connect_log};
else
this.connect_log = {args, @this.connect_log[1..this.max_connect_log - 1]};
endif
.
#31:8
if (!(caller in {this, #0}))
return E_PERM;
endif
hostname = $string_utils:connection_hostname(connection_name(this));
$guest_log:enter(1, time(), hostname);
pass(@args);
if (!index(hostname, "bga.com"))
"...tell em about access...";
this:notify_lines($help:get_topic("Access"));
endif
this:notify_lines($string_utils:pronoun_sub(this.extra_confunc_msg));
.
#31:9
if (caller != this)
return E_PERM;
else
cname = connection_name(this) || this.last_connect_place;
$guest_log:enter(0, time(), $string_utils:connection_hostname(cname));
endif
.
#31:10
if (!valid(caller_perms()))
player:tell("Sorry, that information is not available.");
endif
.
#31:11
hash = tonum(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:12
if (caller_perms() != this)
return E_PERM;
else
return pass(@args);
endif
.
#31:13
return pass(@args);
.
#31:14
return pass(@args);
"only for setting permission";
.
#31:15
"Usage:  @request <name> for <email-address>";
if (player != this)
return player:tell(E_PERM);
endif
name = dobjstr;
address = iobjstr;
connection = $string_utils:connection_hostname(connection_name(player));
if (this.request)
return player:tell("Sorry, you appear to have already requested a character.");
endif
if (index(address = iobjstr, " "))
return player:notify_lines($code_utils:verb_usage());
elseif (reason = $wiz_utils:check_player_request(name, address, connection))
if (reason[1] == "-")
reason = reason[2..length(reason)] + " Please ";
else
reason = reason + " Please try again, or, to register another way, ";
endif
player:tell(reason, " mail to ", $login.registration_address, " with the character name you want.");
return;
endif
while (typeof(answer = $command_utils:yes_or_no(("Generally, only one character per real person is allowed. For more details, read `help multiple-characters'. Do you already have (or have you already requested) a " + $network.moo_name) + " character?")) == ERR)
endwhile
if (answer)
return player:tell("Character request processing ended.");
endif
if (!$network.active)
$mail_agent:send_message(this, $registration_db.registrar, "Player request", {"Player request from " + connection, ":", "", (("@make-player " + name) + " ") + address});
player:tell("Request for new character ", name, " email address '", address, "' accepted. Please be patient until the registrar gets around to it. If you don't get email within a week, please send regular email to ", $login.registration_address, ".");
elseif ($player_db.frozen)
player:tell("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];
player:tell("Character ", name, " (", new, ") created, mailing password to ", address, "; you should get the mail very soon.");
player:tell("If you do not get it, please do NOT request another character. Send regular email to ", $login.registration_address, ", with the name of the character you requested.");
$mail_agent:send_message($new_player_log.autoregistration_player, $new_player_log, tostr(name, " (", new, ")"), {address, (" Automatically created at request of player from " + connection) + "."});
$wiz_utils:send_new_player_mail(tostr("A guest connected from ", connection, " at ", ctime(), " requested a character on ", $network.moo_name, " for email address ", address, "."), name, address, new, password);
this.request = 1;
return;
endif
.
#31:16
if (caller_perms().wizard)
this.extra_confunc_msg = "";
endif
.
#31:17
"disallow guests from setting aliases on themselves";
if ($perm_utils:controls(caller_perms(), this))
return pass(@args);
else
return E_PERM;
endif
.
#31:18
"Allow guests to set certain messages.";
if (args[1] in this.settable_messages)
return pass(@args);
else
player:tell("Sorry; guests can only set their ", $string_utils:english_list($list_utils:map_arg($string_utils, "print", this.settable_messages)), " messages.");
return E_PERM;
endif
.
#31:19
zone = $string_utils:connection_hostname($object_utils:connected(this) ? connection_name(this) | this.last_connect_place);
return $string_utils:lines_to_string(pass(@args), tostr("  From ", this:pp(), " manners and garb, you guess ", this:ps(), " must be from zone ", zone, "."));
.
#31:20
":reset_stats()";
"Nuke all guest stats at disconnection.";
for p in (properties($skilled))
clear_property(this, p);
endfor
this:reroll();
.
#31:21
":public_connect_site()";
"Guest connection sites are always public.";
return pass(@args);
.
#31:22
"Don't allow guests to perform any RPG actions.";
{action, action_args} = args;
if (!$rpg:is_violent_action(action))
return pass(@args);
endif
this:notify("Sorry, but guests aren't allowed to perform combat actions.  Feel free to @request a character.  If you are being attacked by citizens of the MOO, feel free to lodge a complaint with the management.");
return E_PERM;
.
#32:0
"   add(seq,start,end) => seq with range added.";
"remove(seq,start,end) => seq with range removed.";
"  both assume start<=end.";
seq = args[1];
start = args[2];
s = (start == $minint) ? 1 | $list_utils:find_insert(seq, start - 1);
e = $list_utils:find_insert(seq, after = args[3] + 1);
remove = verb == "remove";
return {@seq[1..s - 1], @((s + remove) % 2) ? {start} | {}, @((e + remove) % 2) ? {after} | {}, @seq[e..length(seq)]};
.
#32:1
":contains(seq,elt) => true iff elt is in seq.";
return ($list_utils:find_insert(@args) + 1) % 2;
.
#32:2
":complement(seq) => the sequence containing all integers *not* in seq.";
seq = args[1];
if (seq && (seq[1] == $minint))
return listdelete(seq, 1);
else
return {$minint, @seq};
endif
.
#32:3
":union(seq1,seq2,...) => union of all sequences...";
":intersection(seq1,seq2,...) => intersection of all sequences...";
intersect = verb != "union";
if ({} in args)
if (intersect)
return {};
else
args = $list_utils:setremove_all(args, {});
endif
endif
if ((alen = length(args)) <= 1)
return args ? args[1] | (intersect ? {$minint} | {});
else
if (intersect)
args = $list_utils:map_arg(this, "complement", args);
endif
lens = {length(args[1])};
for a in (listdelete(args, 1))
lens = {@lens, length(a)};
endfor
args = $list_utils:sort(args, lens);
lens = $list_utils:sort(lens);
for ll in [-length(lens)..-2]
second = args[2];
for i in [1..length(first = args[1]) / 2]
second = this:add(second, first[(2 * i) - 1], first[2 * i] - 1);
endfor
if (length(first) % 2)
s = $list_utils:find_insert(second, (start = first[length(first)]) - 1);
second = {@second[1..s - 1], @(s % 2) ? {start} | {}};
endif
i = $list_utils:find_insert(lens = lens[3..-ll], slen = length(second));
lens = listinsert(lens, slen, i);
args = listinsert(args[3..-ll], second, i);
endfor
return intersect ? this:complement(second) | second;
endif
.
#32:4
if (!(seq = args[1]))
return "empty";
endif
e = tostr((seq[1] == $minint) ? "" | seq[1]);
for i in [2..length(seq)]
e = e + ((i % 2) ? tostr(", ", seq[i]) | ((seq[i] == (seq[i - 1] + 1)) ? "" | tostr("..", seq[i] - 1)));
endfor
return e + ((length(seq) % 2) ? ".." | "");
.
#32:5
":for([n,]seq,obj,verb,@args) => for s in (seq) obj:verb(s,@args); endfor";
set_task_perms($no_one);
if (typeof(n = args[1]) == NUM)
args = listdelete(args, 1);
seq = args[1];
else
seq = n;
n = 1;
endif
if (seq[1] == $minint)
return E_RANGE;
endif
object = args[2];
vname = args[3];
args = args[4..length(args)];
for r in [1..length(seq) / 2]
for i in [seq[(2 * r) - 1]..seq[2 * r] - 1]
if (typeof(object:(vname)(@listinsert(args, i, n))) == ERR)
return;
endif
endfor
endfor
if (length(seq) % 2)
i = seq[length(seq)];
while (1)
if (typeof(object:(vname)(@listinsert(args, i, n))) == ERR)
return;
endif
i = i + 1;
endwhile
endif
.
#32:6
"extract(seq,array) => list of elements of array with indices in seq.";
if (alen = length(array = args[2]))
e = $list_utils:find_insert(seq = args[1], 1);
s = $list_utils:find_insert(seq, alen);
seq = {@(e % 2) ? {} | {1}, @seq[e..s - 1], @(s % 2) ? {} | {alen + 1}};
ret = {};
for i in [1..length(seq) / 2]
$command_utils:suspend_if_needed(0);
ret = {@ret, @array[seq[(2 * i) - 1]..seq[2 * i] - 1]};
endfor
return ret;
else
return {};
endif
.
#32:7
seq = args[1];
if (!seq)
return {};
else
if (length(seq) % 2)
seq = {@seq, $minint};
endif
l = {};
for i in [1..length(seq) / 2]
for j in [seq[(2 * i) - 1]..seq[2 * i] - 1]
l = {@l, j};
endfor
endfor
return l;
endif
.
#32:8
":fromlist(list) => corresponding sequence.";
return this:from_sorted_list($list_utils:sort(args[1]));
.
#32:9
":from_sorted_list(sorted_list) => corresponding sequence.";
if (!(lst = args[1]))
return {};
else
seq = {i = lst[1]};
next = i + 1;
for i in (listdelete(lst, 1))
if (i != next)
seq = {@seq, next, i};
endif
next = i + 1;
endfor
return (next == $minint) ? seq | {@seq, next};
endif
.
#32:10
return (seq = args[1]) ? seq[1] | E_NONE;
.
#32:11
return (seq = args[1]) ? ((len = length(seq)) % 2) ? $minint - 1 | (seq[len] - 1) | E_NONE;
.
#32:12
":size(seq) => number of elements in seq";
"  for sequences consisting of more than half of the 4294967298 available integers, this returns a negative number, which can either be interpreted as (cardinality - 4294967298) or -(size of complement sequence)";
n = 0;
for i in (seq = args[1])
n = i - n;
endfor
return (length(seq) % 2) ? $minint - n | n;
.
#32:13
":from_string(string) => corresponding sequence or E_INVARG";
"  string should be a comma separated list of numbers and";
"  number..number ranges";
su = $string_utils;
if (!(words = su:explode(su:strip_chars(args[1], " "), ",")))
return {};
endif
parts = {};
for word in (words)
to = index(word, "..");
if ((!to) && su:is_numeric(word))
part = {tonum(word), tonum(word) + 1};
elseif (to)
if (to == 1)
start = $minint;
elseif (su:is_numeric(start = word[1..to - 1]))
start = tonum(start);
else
return E_INVARG;
endif
end = word[to + 2..length(word)];
if (!end)
part = {start};
elseif (!su:is_numeric(end))
return E_INVARG;
elseif ((end = tonum(end)) >= start)
part = {start, end + 1};
else
part = {};
endif
else
return E_INVARG;
endif
parts = {@parts, part};
endfor
return this:union(@parts);
.
#32:14
":firstn(seq,n) => first n elements of seq as a sequence.";
if ((n = args[2]) <= 0)
return {};
endif
l = length(seq = args[1]);
s = 1;
while (s <= l)
n = n + seq[s];
if ((s >= l) || (n <= seq[s + 1]))
return {@seq[1..s], n};
endif
n = n - seq[s + 1];
s = s + 2;
endwhile
return seq;
.
#32:15
":lastn(seq,n) => last n elements of seq as a sequence.";
n = args[2];
if ((l = length(seq = args[1])) % 2)
return {$minint - n};
else
s = l;
while (s)
n = seq[s] - n;
if (n >= seq[s - 1])
return {n, @seq[s..l]};
endif
n = seq[s - 1] - n;
s = s - 2;
endwhile
return seq;
endif
.
#32:16
":range(start,end) => sequence corresponding to [start..end] range";
return ((start = args[1]) <= (end = args[2])) ? {start, end + 1} | {};
.
#32:17
":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 = args[1];
removed = args[2];
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:0
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.mail_forward = {player, this};
this.mail_notify = {player};
this.moderated = 1;
.
#34: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
.
#34:1
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.exempted = {$site_db, $registration_db, $player_db};
this.unmeasured_exempt = {$hacker, $packrat, $geomancer, $scrooge};
.
#34:2
return 0;
.
#34: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
.
#34:4
if ((caller != this) && (!caller_perms().wizard))
return E_PERM;
else
args[1].ownership_quota = 1;
endif
.
#34:5
if ((caller != this) && (!caller_perms().wizard))
return E_PERM;
else
args[1].ownership_quota = this.large_negative_number;
endif
.
#34: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].";
if (length(args) == 1)
return caller_perms();
elseif ((length(args) == 2) && $perm_utils:controls(caller_perms(), args[2]))
return args[2];
else
return E_INVARG;
endif
.
#34: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];
"----- cut here -----";
if (0)
who:notify("Quinn is currently working on the quota system.  Please be patient as the changes are completed.");
if (who.programmer && (verb != "creation_permitted"))
return 1;
else
return 0;
endif
endif
"----- cut here -----";
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) && (!(who in this.unmeasured_exempt)))
return 0;
else
return 1;
endif
.
#34:8
{who} = args;
if ((caller != this) && (!this:can_peek(caller_perms(), who)))
return E_PERM;
endif
return {who};
"don't use this until @quota is corrected.";
return setadd($alt_player_db:all_alts_for(args[1]), who);
.
#34: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 = x.size_quota[1];
usage = x.size_quota[2];
timestamp = x.size_quota[3];
unmeasured = x.size_quota[4];
unmeasurable = 0;
if (unmeasured >= 100)
unmeasurable = unmeasured / 100;
unmeasured = unmeasured % 100;
endif
if (many)
player:tell(x.name, " quota: ", $string_utils:group_number(quota), "; usage: ", $string_utils:group_number(usage), "; unmeasured: ", unmeasured, "; no .object_size: ", unmeasurable, ".");
tquota = tquota + quota;
tusage = tusage + usage;
ttime = min(ttime, timestamp);
tunmeasured = tunmeasured + unmeasured;
tunmeasurable = tunmeasurable + unmeasurable;
endif
endfor
if (many)
this:display_quota_summary(who, tquota, tusage, ttime, tunmeasured, tunmeasurable);
else
this:display_quota_summary(who, quota, usage, timestamp, unmeasured, unmeasurable);
endif
.
#34:10
who = args[1];
return this.byte_based ? who.size_quota[1] | who.ownership_quota;
.
#34:11
"Charge args[1] for the quota required to own args[2]";
if ((caller == this) || caller_perms().wizard)
usage_index = 2;
unmeasured_index = 4;
who = args[1];
what = args[2];
object_size = $object_utils:has_property(what, "object_size") ? what.object_size[1] | -1;
if (object_size <= 0)
who.size_quota[unmeasured_index] = who.size_quota[unmeasured_index] + 1;
else
who.size_quota[usage_index] = who.size_quota[usage_index] + object_size;
endif
else
return E_PERM;
endif
.
#34: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.";
if ((caller == this) || caller_perms().wizard)
usage_index = 2;
unmeasured_index = 4;
who = args[1];
what = args[2];
if (parent(what) == $garbage)
return 0;
elseif (is_player(who))
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
.
#34:13
"Set args[1]'s quota to args[2]";
if (caller_perms().wizard || (caller == this))
"Size_quota[1] is the total quota permitted.";
return args[1].size_quota[1] = args[2];
else
return E_PERM;
endif
.
#34:14
"Return args[1]'s quotas.  second arg of 1 means add all second chars.";
who = args[1];
all = args[2];
if (all && ((caller == this) || this:can_peek(caller_perms(), who)))
all = this:all_characters(who);
else
all = {who};
endif
baseline = {0, 0, 0, 0};
for x in (all)
baseline[1] = baseline[1] + x.size_quota[1];
baseline[2] = baseline[2] + x.size_quota[2];
baseline[3] = min(baseline[3], x.size_quota[3]) || x.size_quota[3];
baseline[4] = baseline[4] + x.size_quota[4];
endfor
return baseline;
.
#34:15
who = args[1];
quota = args[2];
usage = args[3];
timestamp = args[4];
unmeasured = args[5];
unmeasurable = args[6];
player:tell(who.name, " has a total building quota of ", $string_utils:group_number(quota), " bytes.");
player:tell(who:ppc(), " 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
.
#34: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
.
#34: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
.
#34:18
return value_bytes(@args);
.
#34:19
{o} = args;
b = object_bytes(o);
if ($object_utils:has_property(o, "object_size"))
oldsize = o.object_size[1];
if ($object_utils:has_property(o.owner, "size_quota"))
"Update quota cache.";
if (oldsize)
o.owner.size_quota[2] = o.owner.size_quota[2] + (b - oldsize);
else
o.owner.size_quota[2] = o.owner.size_quota[2] + b;
if (o.owner.size_quota[4] > 0)
o.owner.size_quota[4] = o.owner.size_quota[4] - 1;
endif
endif
endif
o.object_size = {b, time()};
endif
return b;
.
#34:20
who = args[1];
results = this:summarize_one_user(who);
total = results[1];
nuncounted = results[2];
nzeros = results[3];
oldest = results[4];
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)");
if (nzeros || nuncounted)
player:tell("  Number of objects with no statistics recorded:  ");
player:tell("      ", nzeros, " recently created, ", nuncounted, " not descendents of #1");
endif
.
#34: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";
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();
nuncounted = 0;
ncounted = 1;
total = 0;
for x in ((typeof(who.owned_objects) == LIST) ? who.owned_objects | {})
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)
oldest = min(oldest, time);
else
nzeros = nzeros + 1;
endif
total = total + size;
ncounted = ncounted + 1;
else
nuncounted = nuncounted + 1;
endif
$command_utils:suspend_if_needed(0);
endfor
"Cache the data...";
who.size_quota[2] = total;
who.size_quota[3] = oldest;
who.size_quota[4] = (nuncounted * this.unmeasured_multiplier) + nzeros;
return {total, nuncounted, nzeros, oldest};
.
#34: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 = args[1];
if (length(args) > 1)
since = args[2];
else
since = this.cycle_days;
endif
if (!valid(object))
return 0;
elseif (object.object_size[2] > (time() - (((since * 24) * 60) * 60)))
return object.object_size[1];
else
return this:object_bytes(object);
endif
.
#34:23
return (args[1] == this.owner) || $perm_utils:controls(args[1], args[2]);
.
#34:24
return args[1].wizard;
.
#34:25
dobj = args[1];
who = valid(caller_perms()) ? caller_perms() | player;
if (!this:can_peek(who, dobj.owner))
return E_PERM;
endif
props = $object_utils:all_properties(dobj);
grand_total = obj_over = this:object_overhead_bytes(dobj);
output = {tostr("Object overhead:  ", obj_over)};
if (props)
total = 0;
lines = {};
output = {@output, "Properties, defined and inherited, sorted by size:"};
for x in (props)
if (!is_clear_property(dobj, x))
size = this:value_bytes(dobj.(x));
total = total + size;
if (size)
lines = {@lines, {x, size}};
endif
endif
endfor
lines = $list_utils:reverse($list_utils:sort_suspended(0, lines, $list_utils:slice(lines, 2)));
for x in (lines)
text = tostr("  ", x[1], ":  ", x[2]);
output = {@output, text};
endfor
output = {@output, tostr("Total size of properties:  ", total)};
grand_total = grand_total + total;
endif
prop_over = this:property_overhead_bytes(dobj, props);
output = {@output, tostr("Property overhead:  ", prop_over)};
grand_total = grand_total + prop_over;
if (verbs(dobj))
output = {@output, "Verbs, sorted by size:"};
total = 0;
lines = {};
for x in [0..length(verbs(dobj)) - 1]
vname = verb_info(dobj, tostr(x))[3];
size = this:value_bytes(verb_code(dobj, tostr(x)));
total = total + size;
lines = {@lines, {vname, size}};
endfor
lines = $list_utils:reverse($list_utils:sort_suspended(0, lines, $list_utils:slice(lines, 2)));
for x in (lines)
text = tostr("  ", x[1], ":  ", x[2]);
output = {@output, text};
endfor
output = {@output, tostr("Total size of verbs:  ", total)};
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;
.
#34:26
object = args[1];
return ((13 * 4) + length(object.name)) + 1;
.
#34:27
o = args[1];
if (length(args) > 1)
ps = args[2];
else
ps = $object_utils:all_properties_suspended(o);
endif
return (this:value_bytes(properties(o)) - 4) + ((length(ps) * 4) * 4);
.
#34:28
o = args[1];
vs = verbs(o);
return (length(vs) * 5) * 4;
.
#34:29
":bi_value_size(ANY)";
"Return the size in bytes of the given value.";
":bi_object_size(OBJ)";
"Return the total size in bytes, in the database, of the object and all its properties and assorted overhead.";
"Both of these methods return E_INVIND if the server was not compiled with these builtins.";
verb[1..3] = "";
if (!eval(verb + "(player);")[1])
return E_INVIND;
else
return eval(tostr("return ", verb, "(", $string_utils:print(args[1]), ");"))[2];
endif
.
#34:30
"Algorithm:";
"  Base object takes up 13 words plus length of name.  (builtin props?)";
"  Each verb takes up 5 words overhead, plus length of its name, plus size of its code.";
"  Each property definition takes up 1 word plus length of property name, plus each property on the object takes up 4 words.  (Or, 5 per defined prop, 4 per inherited prop)";
"Note: each word is four bytes.";
foo = "delimit comments above from commented out code below";
"set_task_perms(caller_perms())";
o = args[1];
b = this:object_overhead_bytes(o);
vs = verbs(o);
b = b + this:verb_overhead_bytes(o);
for i in [0..length(vs) - 1]
$command_utils:suspend_if_needed(5);
vn = tostr(i);
info = verb_info(o, vn);
b = (b + length(info[3])) + 1;
b = b + this:value_bytes(verb_code(o, vn));
endfor
ps = $object_utils:all_properties_suspended(o);
b = b + this:property_overhead_bytes(o, ps);
for p in (ps)
if (!is_clear_property(o, p))
$command_utils:suspend_if_needed(5);
b = b + this:value_bytes(o.(p));
endif
endfor
if ($object_utils:has_property(o, "object_size"))
oldsize = o.object_size[1];
if ($object_utils:has_property(o.owner, "size_quota"))
"Update quota cache.";
if (oldsize)
o.owner.size_quota[2] = o.owner.size_quota[2] + (b - oldsize);
else
o.owner.size_quota[2] = o.owner.size_quota[2] + b;
if (o.owner.size_quota[4] > 0)
o.owner.size_quota[4] = o.owner.size_quota[4] - 1;
endif
endif
endif
o.object_size = {b, time()};
endif
return b;
.
#34:31
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
.
#34:32
":measure_user(user)";
if ((caller != this) && (!caller_perms().wizard))
return E_PERM;
endif
who = args[1];
if ((!valid(who)) || (!is_player(who)))
return 0;
endif
day = (60 * 60) * 24;
stop = time() + this.task_time_limit;
early = time() - (day * this.cycle_days);
usage_index = 2;
time_index = 3;
unmeasured_index = 4;
"Robustness in the face of reaping...";
usage = 0;
unmeasured = 0;
earliest = time();
for o in (who.owned_objects)
if ((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
if ($command_utils:running_out_of_time())
suspend(random(3));
endif
endfor
who.size_quota[usage_index] = usage;
who.size_quota[unmeasured_index] = this.unmeasured_multiplier * unmeasured;
who.size_quota[time_index] = earliest;
return usage;
.
#34:33
":measure_all_users([NUM suspend_interval])";
"Measure the quota usage of all players.  This method should be used only with server versions later than 1.7.9 or those in which a value_bytes() function has been installed.";
if (!caller_perms().wizard)
return E_PERM;
endif
cut = $command_utils;
INT = args ? args[1] | 60;
players = setremove(players(), $hacker);
total_usage = 0;
for who in (players)
total_usage = total_usage + tonum(this:measure_user(who) || 0);
cut:running_out_of_time() && suspend(INT);
endfor
return total_usage;
.
#34:34
":measurement_task()";
"Forked daily to measure everyone.";
if ($code_utils:task_valid(this.measurement_task))
return E_MAXREC;
endif
fork tid (0)
while (1)
this:measure_all_users();
suspend((60 * 60) * 24);
endwhile
endfork
this.measurement_task = tid;
.
#35:0
"$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 = args[1];
who = (length(args) >= 2) ? args[2] | player;
thing = (length(args) >= 3) ? args[3] | caller;
where = (length(args) >= 4) ? args[4] | who.location;
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)) && (who.location == dobj.location))
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}))) && (who.location == iobj.location))
x = iobj;
iobj = you;
x:tell($string_utils:pronoun_sub(this:fixpos(tell, "%i"), who, thing, where));
iobj = x;
endif
.
#35:1
":fixpos(msg, pronoun_sub_token)";
"If pronoun_sub_token is %N, fix any occurences of /%N's/ with a /Your/.  If it's %d, do it for lower-case dobj refs, etc.";
m = args[1];
t = args[2];
s = $string_utils;
u = s:uppercase(t);
l = s:lowercase(t);
return strsub(strsub(strsub(m, l + "'s", "your", 1), u + "'s", "Your", 1), u + "'S", "YOUR");
.
#35:2
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this:set_gender("2nd");
.
#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)";
rest = search = args[1];
sofar = {@args, 0}[2];
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
rest = search = args[1];
sofar = {@args, 0}[2];
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)";
rest = search = args[1];
sofar = {@args, 0}[2];
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.";
prefix = args[1];
data = args[2];
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
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
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);
endfor
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]) == NUM)
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..length(info[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
rest = search = args[1];
sofar = {@args, 0}[2];
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
rest = search = args[1];
datum = args[2];
sofar = {@args, 0}[3];
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
for p in (properties(this))
if ((p[1] == " ") && (p != " "))
delete_property(this, p);
endif
"... there should be a better way....";
"...This is bad as it leaves the db in an inconsistent state...";
$command_utils:suspend_if_needed(0);
endfor
this:set_node("", "", "", {}, @(this.data > 3) ? {{}} | {});
.
#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.";
set_task_perms(this);
exp = args[1];
if (exp[1] != ";")
return eval(tostr("this=", caller, "; return ", exp, ";"));
else
return eval(tostr("this=", caller, ";", exp, ";"));
endif
.
#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.";
set_task_perms(this);
exp = args[1];
if (exp[1] != ";")
return $code_utils:eval_d(tostr("this=", caller, "; return ", exp, ";"));
else
return $code_utils:eval_d(tostr("this=", caller, ";", exp, ";"));
endif
.
#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]);
.
#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
this:clearall();
this.frozen = 1;
for p in (players())
this:suspend_restart(p);
"... note that if a player is recycled or toaded during the suspension,...";
"... it won't be removed from the for loop iteration; thus this test:     ";
if (valid(p) && is_player(p))
if (typeof(po = this:find_exact(p.name)) == ERR)
player:tell(p.name, ":  ", po);
return;
elseif (po != p)
if (valid(po) && is_player(po))
player:tell("name `", p.name, "' for ", p, " subsumes alias for ", po.name, "(", po, ").");
endif
this:insert(p.name, p);
endif
for a in (p.aliases)
this:suspend_restart(p);
if (index(a, " "))
"don't bother";
elseif (typeof(ao = this:find_exact(a)) == ERR)
player:tell(a, ":  ", ao);
return;
elseif (!(valid(ao) && is_player(ao)))
this:insert(a, p);
elseif (ao != p)
player:tell("alias `", a, "' for ", p.name, "(", p, ") used by ", ao.name, "(", ao, ").");
endif
endfor
endif
endfor
this.frozen = 0;
.
#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..length(p)], " <- ", 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)";
"=> 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 = args[1];
if ((name in this.stupid_names) || (name in this.reserved))
return 0;
elseif ((((!name) || index(name, " ")) || index(name, "\\")) || index(name, "\""))
return 0;
elseif (!match(name, "^[A-Z]$%|^[A-Z][A-Z_'-]*[A-Z]$"))
return 0;
elseif (0 && index("*#()", name[1]))
return 0;
elseif (valid(who = this:find_exact(name)) && is_player(who))
return who;
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]);
suspend(0);
if (this.frozen != 1)
player:tell("...argh... restarting $player_db:load...");
fork (0)
this:load();
endfork
kill_task(task_id());
endif
endif
.
#40:0
":set(OBJ object, STR gender_spec)";
"Set the gender handler of the given object.  All pronouns and conjugations are kept on that object.";
if (valid(g = this:match_gender(args[2])))
args[1].gender = g;
return g.name;
else
return E_NONE;
endif
.
#40:1
":get_conj(STR verbspec[, OBJ 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\".  `Plurality' is deduced from the object's .gender; singular is the default.";
{spec, ?object = player} = args;
"it would be nice if this worked, but it doesn't include empty strings";
"on either side of the slash";
"{sing, ?plur=\"\"} = $string_utils:explode(spec, \"/\");";
i = index(spec + "/", "/");
sing = spec[1..i - 1];
plur = spec[i + 1..$];
"store whether or not it was originally capitalized";
cap = strcmp("a", sing || plur) > 0;
"singular or plural?";
if (valid(object) && (g = object.gender).is_plural)
vb = plur || this:_verb_plural(sing, g);
else
vb = sing || this:_verb_singular(plur, g);
endif
"restore capitalization";
if (cap)
vb = $string_utils:capitalize(vb);
endif
return vb;
.
#40:2
":_verb_plural(str, gender_obj)";
if (typeof(st = args[1]) != 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], args[2]) + "n't";
elseif (i = st in {"has", "is", "was"})
return args[2].({"have", "be", "were"}[i]);
elseif ((len <= 3) || (st[len] != "s"))
return st;
elseif (st[len - 1] != "e")
return st[1..len - 1];
elseif (((st[len - 2] == "h") && index("cs", st[len - 3])) || index("ox", st[len - 2]))
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";
elseif (st[len - 2] == "i")
return st[1..len - 3] + "y";
else
return st[1..len - 1];
endif
.
#40:3
":_verb_singular(str, gender_obj)";
if (typeof(st = args[1]) != 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], args[2]) + "n't";
elseif (i = st in {"did"})
"quick hack to account for this oddity";
return {"did"}[i];
elseif (i = st in {"have", "are"})
return args[2].({"have", "be"}[i]);
elseif ((st[len] == "y") && (!index("aeiou", st[len - 1])))
return st[1..len - 1] + "ies";
elseif (index("osx", st[len]) || ((len > 1) && (index("chsh", st[len - 1..len]) % 2)))
return st + "es";
else
return st + "s";
endif
.
#40:4
":match_gender(str)";
"Return the gender object matching the given string.";
spec = args[1];
kids = $object_utils:descendants($gender);
if (spec in kids)
return spec;
else
return $match_utils:match(spec, kids);
endif
.
#41:0
"Given a time() or ctime()-style date, this returns the full name of the day.";
if (typeof(args[1]) == NUM)
time = ctime(args[1]);
elseif (typeof(args[1]) == STR)
time = args[1];
else
return E_TYPE;
endif
dayabbr = $string_utils:explode(time)[1];
return this.days[dayabbr in this.dayabbrs];
.
#41:1
"Given a time() or ctime()-style date, this returns the full name";
"of the month.";
if (typeof(args[1]) == NUM)
time = ctime(args[1]);
elseif (typeof(args[1]) == STR)
time = args[1];
else
return E_TYPE;
endif
monthabbr = $string_utils:explode(time)[2];
return this.months[monthabbr in this.monthabbrs];
.
#41: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";
if (typeof(args[1]) == NUM)
time = ctime(args[1]);
elseif (typeof(args[1]) == STR)
time = args[1];
else
return E_TYPE;
endif
if (length(args) > 1)
precision = args[2];
else
precision = 2;
endif
time = $string_utils:explode(time)[4];
hour = tonum(time[1..2]);
if (hour == 0)
time = ("12" + time[3..(precision * 3) - 1]) + " a.m.";
elseif (hour == 12)
time = time[1..(precision * 3) - 1] + " p.m.";
elseif (hour > 12)
time = (tostr(hour - 12) + time[3..(precision * 3) - 1]) + " p.m.";
else
time = (tostr(hour) + time[3..(precision * 3) - 1]) + " a.m.";
endif
return time;
.
#41:3
"Given string hh:mm:ss ($string_utils:explode(ctime(time))[4]), this returns";
"the number of seconds elapsed since 00:00:00.  I can't remember why I";
"created this verb, but I'm sure it serves some useful purpose.";
return (((60 * 60) * tonum(args[1][1..2])) + (60 * tonum(args[1][4..5]))) + tonum(args[1][7..8]);
.
#41:4
r = 10000;
h = (r * r) + (r / 2);
time = (args == {}) ? time() | args[1];
t = ((time + 120) % 86400) / 240;
s = (5 * ((time - 14957676) % 31556952)) / 438291;
phi = (s + t) + this.corr;
cs = $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;
.
#41:5
"Given a string such as returned by ctime(), return the corresponding time-in-seconds-since-1970 time returned by time(), or E_DIV if the format is wrong in some essential way.";
words = $string_utils:explode(args[1]);
if (length(words) == 5)
"Arrgh!  the old ctime() didn't return a time zone, yet it arbitrarily decides whether it's standard or daylight savings time.  URK!!!!!";
words = listappend(words, "PST");
endif
if ((((length(words) != 6) || (length(hms = $string_utils:explode(words[4], ":")) != 3)) || (!(month = words[2] in this.monthabbrs))) || (!(zone = $list_utils:assoc(words[6], this.timezones))))
return E_DIV;
endif
year = tonum(words[5]);
day = ({-1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}[month] + tonum(words[3])) + (year * 366);
zone = zone[2];
return (((((((((((((day - ((day + 1038) / 1464)) - ((day + 672) / 1464)) - ((day + 306) / 1464)) - ((day + 109740) / 146400)) - ((day + 73140) / 146400)) - ((day + 36540) / 146400)) - 719528) * 24) + tonum(hms[1])) + zone) * 60) + tonum(hms[2])) * 60) + tonum(hms[3]);
.
#41: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
.
#41:7
"english_time(time [,reference time]): returns the time as a string of";
"years, months, days, minutes and seconds using the reference time as the";
"start time and incrementing forwards. it can be given in either ctime() or";
"time() format. if a reference time is not given, it is set to time().";
"suspend(0)";
if ((_time = args[1]) < 1)
return "0 seconds";
endif
reftime = (length(args) > 1) ? args[2] | time();
_ctime = (typeof(reftime) == NUM) ? ctime(reftime) | reftime;
seclist = {60, 60, 24};
units = {"year", "month", "day", "hour", "minute", "second"};
timelist = {};
for unit in (seclist)
timelist = {_time % unit, @timelist};
_time = _time / unit;
endfor
months = 0;
month = _ctime[5..7] in $time_utils.monthabbrs;
year = tonum(_ctime[21..24]);
"the following should really be a verb/property. attribution: the ";
"algorithm used is from the eminently eminent g7.";
monthlen = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
while (_time >= (days = monthlen[month] + (((month == 2) && ((year % 4) == 0)) && (!((year % 400) in {100, 200, 300})))))
_time = _time - days;
months = months + 1;
if ((month = month + 1) > 12)
year = year + 1;
month = 1;
endif
endwhile
timelist = {months / 12, months % 12, _time, @timelist};
for unit in (units)
i = unit in units;
if (timelist[i] > 0)
units[i] = ((tostr(timelist[i]) + " ") + units[i]) + ((timelist[i] == 1) ? "" | "s");
else
units = listdelete(units, i);
timelist = listdelete(timelist, i);
endif
endfor
"suspend(0)";
return $string_utils:english_list(units);
.
#41:8
"from_day(day_of_week,which)";
"numeric time (seconds since 1970) corresponding to midnight (PST) of the given weekday.  Use either the name of the day or a 1..7 number (1==Sunday,...)";
"  which==-1 => use most recent such day.";
"  which==+1 => use first upcoming such day.";
"  which==0  => use closest such day.";
"larger (absolute) values for which specify a certain number of weeks into the future or past.";
if (!(tonum(day = args[1]) || (day = $string_utils:find_prefix(day, this.days))))
return E_DIV;
endif
delta = {288000, 374400, 460800, 547200, 28800, 115200, 201600}[tonum(day)];
time = time() - delta;
dir = {@args, 0}[2];
if (dir)
time = (time / 604800) + ((dir > 0) ? dir | (dir + 1));
else
time = (time + 302400) / 604800;
endif
return (time * 604800) + delta;
.
#41:9
"from_month(month,which[,d])";
"numeric time (seconds since 1970) corresponding to midnight (PST) of the dth (first) day of the given month.  Use either the month name or a 1..12 number (1==January,...)";
"  which==-1 => use most recent such month.";
"  which==+1 => use first upcoming such month.";
"  which==0  => use closest such month.";
"larger (absolute) values for which specify a certain number of years into the future or past.";
if (!(tonum(month = args[1]) || (month = $string_utils:find_prefix(month, this.months))))
return E_DIV;
endif
delta = ({0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}[month] + {@args, 1}[3]) - 1;
day = (time() - 28800) / 86400;
day = (day - ((day + 672) / 1461)) - delta;
dir = {@args, 0}[2];
if (dir)
day = ((day / 365) + dir) + (dir <= 0);
else
day = ((2 * day) + 365) / 730;
endif
day = (day * 365) + delta;
day = day + ((day + 671) / 1460);
return (day * 86400) + 28800;
.
#41:10
"Takes a time that is midnight PST and converts it to the nearest PDT midnight time if it's during that part of the year where we use PDT.";
time = args[1];
return time - (3600 * (((tonum(ctime(time)[12..13]) + 12) % 24) - 12));
.
#41: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')";
"$P/$p -> AM/PM, or am/pm.";
"$T -> date number. $t -> date number with no extra whitespace etc.";
"$$ -> $.";
"";
"This verb stolen from Ozymandias's #4835:time_subst@LambdaMOO.";
res = "";
thestr = args[1];
if (length(args) > 1)
thetime = tonum(args[2]);
else
thetime = time();
endif
if (typeof(thetime) != NUM)
raise(E_INVARG);
elseif (typeof(thestr) != STR)
if (typeof(thestr) == LIST)
result = {};
for s in (thestr)
result = {@result, this:(verb)(s, thetime)};
endfor
return result;
else
raise(E_INVARG);
endif
endif
itslength = length(thestr);
if (!itslength)
return "";
endif
done = 0;
curchar = 1;
cctime = ctime(thetime);
while (!done)
if (thestr[curchar] != "$")
res = res + thestr[curchar];
else
"Now we begin substitution.";
curchar = curchar + 1;
thechar = thestr[curchar];
if (curchar > length(thestr))
return res;
endif
if (thechar == "$")
res = res + "$";
elseif (!strcmp(thechar, "h"))
res = res + $string_utils:trim(tostr(tonum(cctime[12..13])));
elseif (thechar == "H")
res = res + cctime[12..13];
elseif (!strcmp(thechar, "m"))
res = res + $string_utils:trim(tostr(tonum(cctime[15..16])));
elseif (thechar == "M")
res = res + cctime[15..16];
elseif (!strcmp(thechar, "s"))
res = res + $string_utils:trim(tostr(tonum(cctime[18..19])));
elseif (thechar == "S")
res = res + cctime[18..19];
elseif (!strcmp(thechar, "D"))
res = res + $time_utils:day(thetime);
elseif (thechar == "d")
res = res + cctime[1..3];
elseif (!strcmp(thechar, "N"))
res = res + $time_utils:month(thetime);
elseif (thechar == "n")
res = res + cctime[5..7];
elseif (!strcmp(thechar, "T"))
res = res + cctime[9..10];
elseif (thechar == "t")
res = res + $string_utils:trim(cctime[9..10]);
elseif (thechar == "O")
res = res + tostr(((tonum(cctime[12..13]) + 11) % 12) + 1);
elseif (!strcmp(thechar, "p"))
res = res + ((tonum(cctime[12..13]) >= 12) ? "pm" | "am");
elseif (thechar == "P")
res = res + ((tonum(cctime[12..13]) >= 12) ? "PM" | "AM");
elseif (!strcmp(thechar, "y"))
res = res + cctime[23..24];
elseif (thechar == "Y")
res = res + cctime[21..24];
endif
endif
curchar = curchar + 1;
if (curchar > itslength)
done = 1;
endif
endwhile
return res;
.
#41: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 '/'";
if (typeof(args[1]) == NUM)
time = ctime(args[1]);
elseif (typeof(args[1]) == STR)
time = args[1];
else
return E_TYPE;
endif
date = $string_utils:explode(time);
day = tonum(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];
divstr = (length(args) == 1) ? "/" | args[2];
if (verb == "mmddyy")
return tostr(monthstr, divstr, daystr, divstr, yearstr);
else
return tostr(daystr, divstr, monthstr, divstr, yearstr);
endif
.
#41:13
"$time_utils:parse_english_time_interval(n1,u1,n2,u2,...)";
"or $time_utils:parse_english_time_interval(\"n1 u1[,] [and] n2[,] u2 [and] ...\")";
"There must be an even number of arguments, all of which must be strings,";
" or there must be just one argument which is the entire string to be parsed.";
"The n's are are numeric strings, and the u's are unit names.";
"The known units are in $time_utils.time_units,";
" which must be kept sorted with bigger times at the head.";
"Returns the time represented by those words.";
"For example,";
" $time_utils:parse_english_time_interval(\"30\",\"secs\",\"2\",\"minutes\",\"31\",\"seconds\") => 181";
if ((length(args) == 1) && index(args[1], " "))
return $time_utils:parse_english_time_interval(@$string_utils:words(args[1]));
endif
a = $list_utils:setremove_all(args, "and");
nargs = length(a);
if (nargs % 2)
return E_ARGS;
endif
nsec = 0;
n = 0;
for i in [1..nargs]
if ((i % 2) == 1)
if ($string_utils:is_numeric(a[i]))
n = tonum(a[i]);
elseif (a[i] in {"a", "an"})
n = 1;
elseif (a[i] in {"no"})
n = 0;
else
return E_INVARG;
endif
else
unit = a[i];
if (unit[length(unit)] == ",")
unit = unit[1..length(unit) - 1];
endif
ok = 0;
for entry in ($time_utils.time_units)
if ((!ok) && (unit in entry[2..length(entry)]))
nsec = nsec + (entry[1] * n);
ok = 1;
endif
endfor
if (!ok)
return E_INVARG;
endif
endif
endfor
return nsec;
.
#41: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 = args[1];
day = args[2];
which = args[4];
time = args[3];
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;
.
#41: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 tonum(time) - tonum(current);
.
#41:16
":abbr_time(NUM seconds) -> String such as 8s, 4m, 3h.";
time = args[1];
if (time < 60)
"...seconds";
return tostr(time, "s");
elseif (time < 3600)
"...minutes";
return tostr(time / 60, "m");
elseif (time < 86400)
"...hours";
return tostr(time / 3600, "h");
elseif (time < 2592000)
"...days";
return tostr(time / 86400, "d");
else
"...months";
return tostr(time / 2592000, "M");
endif
.
#41:17
":[c]time(STR timezone[, NUM time])";
"Not really accurate for worldwide times, but oH well...";
time = (length(args) == 2) ? args[2] | time();
zone = args[1];
here = ctime(time);
if ((length(zone) == 3) && (zone[2] != (dos = here[27])))
zone[2] = dos;
endif
offset = this:timezone_offset(zone);
if (offset != E_PROPNF)
"found a valid timezone!";
time = time + (offset * 3600);
else
return (verb == "time") ? time | here;
endif
if (verb == "time")
return time;
else
ctime = ctime(time);
ctime[26..28] = zone;
return ctime;
endif
.
#41:18
":timezone_offset(STR timezone)";
"-> Hours difference from time().";
"-> E_PROPNF if the timezone is unknown.";
zone = args[1];
here = ctime()[26..28];
if (zone == here)
return 0;
endif
zero = this:_timezone_index(here);
offset = this:_timezone_index(zone);
return (offset == E_PROPNF) ? offset | (offset - zero);
.
#41:19
":_timezone_index(STR timezone)";
"-> Index of timezone in `this.zones'.";
"-> E_PROPNF if the timezone is unknown.";
zone = args[1];
for i in [1..length(zones = this.zones)]
if (zone in zones[i])
return i;
endif
endfor
return E_PROPNF;
.
#41:20
":ctime_gmt([seconds])";
"Return the canonical ASCII representation of time.";
{?t = time()} = args;
return this:time_sub("$d, $T $n $Y $H:$M:$S ", t) + this.gmt_offset;
.
#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 = args[1];
mods = args[2];
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;
c = callers();
while (((stage = stage + 1) < length(c)) && (c[stage][1] == c[1][1]))
endwhile
return c[stage];
.
#42:3
"controls_prop(who, what, propname)";
"Is WHO allowed to hack on WHAT's PROPNAME?";
return (args[1] == property_info(args[2], args[3])[1]) || args[1].wizard;
.
#44:0
"For changing mailing list aliases, we check to make sure that none of the aliases match existing mailing list aliases.  Aliases containing spaces are not used in addresses and so are not subject to this restriction ($mail_agent:match will not match on them, however, so they only match if used in the immediate room, e.g., with match_object() or somesuch).";
"  => E_PERM   if you don't own this";
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
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
.
#44:1
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 (typeof(this.readers) != LIST)
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);
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);
endfor
endfor
.
#44:2
return $perm_utils:controls(who = args[1], this) || (who in this.writers);
.
#44:3
return (typeof(this.readers) != LIST) || (((who = args[1]) in this.readers) || (this:is_writable_by(who) || $mail_agent:sends_to(1, this, who)));
.
#44:4
who = args[1];
if (this.moderated)
return (who in this.moderated) || (this:is_writable_by(who) || who.wizard);
else
return this.guests_can_send_here || (!$object_utils:isa(who, $guest));
endif
.
#44:5
if ((args && (!this:is_usable_by(args[1]))) && (!args[1].wizard))
return this:moderator_notify(@args);
else
return this.(verb);
endif
.
#44: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
.
#44:7
if (typeof(mf = this.(verb)) == STR)
return $string_utils:pronoun_sub(mf, args ? args[1] | $player);
else
return mf;
endif
.
#44: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;
.
#44: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;
.
#44: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;
.
#44: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;
.
#44: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
.
#44: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]));
.
#44: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]));
.
#44:15
":parse_message_seq(strings,cur) => msg_seq";
"";
":from_msg_seq(olist)     => msg_seq of messages from those people";
":%from_msg_seq(strings)  => msg_seq of messages with strings in the From: line";
":to_msg_seq(olist)       => msg_seq of messages to those people";
":%to_msg_seq(strings)    => msg_seq of messages with strings in the To: line";
":subject_msg_seq(target) => msg_seq of messages with target in the Subject:";
":body_msg_seq(target)    => msg_seq of messages with target in the body";
":new_message_num()    => number that the next incoming message will receive.";
":length_num_le(num)   => number of messages in folder numbered <= num";
":length_date_le(date) => number of messages in folder dated <= date";
":length_all_msgs()    => number of messages in folder";
":exists_num_eq(num)   => index of message in folder numbered == num, or 0";
"";
":display_seq_headers(msg_seq[,cur])   display message summary lines";
":display_seq_full(msg_seq[,preamble]) display entire messages";
"            => number of final message displayed";
":list_rmm() displays contents of .messages_going.";
"            => the number of messages in .messages_going.";
"";
":messages_in_seq(msg_seq) => list of messages in msg_seq on folder";
"";
"See the corresponding routines on $mail_agent for more detail.";
return this:ok(caller, caller_perms()) ? $mail_agent:(verb)(@args) | E_PERM;
.
#44: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
.
#44: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
.
#44: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;
.
#44: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
.
#44: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
.
#44: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[length(newmsgs)][2][1];
endif
.
#44:22
mlen = this:length_all_msgs();
this.last_msg_date = mlen && this:messages_in_seq(mlen)[2][1];
.
#44:23
return this.(verb);
.
#44:24
return $mail_agent:msg_summary_line(@args);
.
#44:25
for m in (this.messages)
$mail_agent:__convert_new(@m[2]);
$command_utils:suspend_if_needed(0);
endfor
.
#44: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;
.
#44:27
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
if (!(this in {$mail_recipient, $big_mail_recipient}))
"...generic mail recipients stay in #-1...";
move(this, $mail_agent);
endif
for p in ({"moderator_forward", "moderator_notify", "writers", "readers", "expire_period", "last_used_time", "mail_notify"})
`this.(p) = $mail_recipient.(p) ! ANY';
endfor
this.messages = this.messages_going = {};
this:_fix_last_msg_date();
.
#44:28
if ($perm_utils:controls(caller_perms(), this))
this.mail_forward = {};
return pass(@args);
endif
.
#44:29
return "*" + this.aliases[1];
.
#44:30
names = {};
for a in (this.aliases)
if (!index(a, " "))
names = setadd(names, strsub(a, "_", "-"));
endif
endfor
return names;
.
#44:31
if (caller_perms().wizard || (caller_perms() == this.owner))
"Passed security check...";
if (this.expire_period && (n = this:length_date_le(time() - this.expire_period)))
this:rm_message_seq($seq_utils:range(1, n));
return this:expunge_rmm();
endif
else
return E_PERM;
endif
.
#44:32
if (this:is_writable_by(caller_perms()) || this:is_writable_by(caller))
pass(@args);
else
return E_PERM;
endif
.
#44:33
":netmail_message_seq(user, msg_seq)";
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
{user, seq} = args;
email = `user.email_address ! E_PROPNF';
if (!email)
return email;
endif
try
format = user:format_for_netforward(this:messages_in_seq(seq), " from " + $mail_agent:name(this));
except (E_VERBNF)
format = $player:format_for_netforward(this:messages_in_seq(seq), " from " + $mail_agent:name(this));
endtry
result = $network:sendmail(email, format[2], @format[1]);
return result;
.
#44:34
":maybe_expire_old_messages()";
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
kill_seq = this:get_expired_seq_desc();
if (!kill_seq)
return 0;
endif
result = this:parse_message_seq(kill_seq, 0);
if (typeof(result) != LIST)
return {0, result};
endif
{seq, @why} = result;
if (!seq)
return {seq, @why};
endif
owner = (this.owner == $hacker) ? $owner | this.owner;
mail_result = this:netmail_message_seq(owner, seq);
if (mail_result != 0)
return mail_result;
endif
this:rm_message_seq(seq);
"All of this should be somewhere else and customizable.";
fork (0)
this:do_expiration_notification(owner);
endfork
return kill_seq;
.
#44:35
":do_expiration_notification([user])";
if (caller != this)
return E_PERM;
endif
{?user = this.owner} = args;
mail_name = this:mail_name();
subj = tostr("Auto-Expiration for ", mail_name);
body = {("Messages have been expired from " + mail_name) + " and sent to your registered e-mail address."};
body = {@body, "", "When you receive them, please type"};
body = {@body, "", "      @unrmm expunge on " + mail_name};
body = {@body, "or"};
body = {@body, "      @renumber " + mail_name};
body = {@body, "", "to delete them from the MOO.  If you do not receive the messages via e-mail, you may restore the messages by typing"};
body = {@body, "", "      @unrmm on " + mail_name};
body = {@body, "", "Thanks for helping to keep the MOO lean!"};
$mail_agent:send_message(user, {user}, {subj, {}}, body);
.
#44:36
":get_expired_sequence()";
if (!this:ok(caller, caller_perms()))
return E_PERM;
endif
if ((t = this.expire_half_msgs) && ((msgc = this:length_all_msgs()) > (t * 2)))
kill_seq = tostr("first:", t);
which = kill_seq;
elseif ((t = this.expire_half_size) && ((size = $quota_utils:object_bytes(this)) > (t * 2)))
bytes_killed = 0;
msg_count = 0;
while (bytes_killed < t)
msg = `this:messages_in_seq(msg_count + 1) ! E_RANGE';
if (!msg)
break;
endif
msg_count = msg_count + 1;
bytes_killed = bytes_killed + $quota_utils:value_bytes(msg);
$command_utils:suspend_if_needed(0);
endwhile
kill_seq = msg_count ? tostr("first:", msg_count) | "";
elseif (t = this.expire_half_days * $time_utils.day)
kill_date = ctime(time() - t);
kill_seq = tostr("before:", kill_date[9..10], "-", kill_date[5..7], "-", kill_date[21..24]);
which = kill_seq;
else
kill_seq = "";
endif
return kill_seq;
.
#44:37
if (!caller_perms().wizard)
raise(E_PERM);
endif
if (typeof(this.messages) != LIST)
raise(E_TYPE);
endif
now = time();
problems = {};
last_good_date = 0;
for i in [1..length(this.messages)]
posted = this.messages[i][2][1];
if (posted > now)
problems = {@problems, i};
elseif (posted > last_good_date)
last_good_date = posted;
endif
endfor
if (!last_good_date)
"all messages are fucked, so may as well now-ize them all";
last_good_date = now;
endif
for i in (problems)
this.messages[i][2][1] = last_good_date;
endfor
return "done";
.
#45: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 = args[1];
from = args[2];
if (length(args) == 2)
args = {@args, {}, {}, {}};
endif
seen = args[3];
sofar = args[4..5];
if (typeof(recip) == LIST)
bogus = {};
for r in (recip)
result = this:resolve_addr(r, from, seen, @sofar);
if (result)
sofar = result;
else
bogus = setadd(bogus, r);
endif
endfor
return {bogus, @sofar};
else
fwd = include_recip = 0;
if ((recip == $nothing) || (recip in seen))
return sofar;
elseif ((!valid(recip)) || ((!(is_player(recip) || $object_utils:isa(recip, $mail_recipient))) || (typeof(fwd = recip:mail_forward(from)) != LIST)))
"recip is a non-player non-mailing-list/folder or forwarding is screwed.";
if (typeof(fwd) == STR)
player:tell(fwd);
endif
return E_INVARG;
elseif (fwd)
if (r = recip in fwd)
include_recip = 1;
fwd = listdelete(fwd, r);
endif
result = this:resolve_addr(fwd, recip, setadd(seen, recip), @sofar);
if (bogus = result[1])
player:tell(recip.name, "(", recip, ")'s .mail_forward list includes the following bogus entr", (length(bogus) > 1) ? "ies:  " | "y:  ", $string_utils:english_list(bogus));
endif
sofar = result[2..3];
else
include_recip = 1;
endif
if ((ticks_left() < 1000) || (seconds_left() < 2))
suspend(0);
endif
biffs = sofar[2];
for n in (this:mail_notify(recip, from))
if (valid(n))
if (i = $list_utils:iassoc_suspended(n, biffs))
biffs[i] = setadd(biffs[i], recip);
else
biffs = {{n, recip}, @biffs};
endif
endif
if ((ticks_left() < 1000) || (seconds_left() < 2))
suspend(0);
endif
endfor
return {include_recip ? setadd(sofar[1], recip) | sofar[1], biffs};
endif
.
#45:1
"sends_to(from,addr,rcpt[,seen]) ==> true iff mail sent to addr passes through rcpt.";
if ((addr = args[2]) == (rcpt = args[3]))
return 1;
elseif (!(addr in (seen = (length(args) >= 4) ? args[4] | {})))
seen = {@seen, addr};
for a in ((typeof(fwd = this:mail_forward(addr, @args[1] ? {} | {args[1]})) == LIST) ? fwd | {})
if (this:sends_to(addr, a, rcpt, seen))
return 1;
endif
$command_utils:suspend_if_needed(0);
endfor
endif
return 0;
.
#45:2
"send_message(from,rcpt-list,hdrs,msg) -- formats and sends a mail message.  hders is either the text of the subject line, or a {subject,{reply-to,...}} list.";
"Return E_PERM if from isn't owned by the caller.";
"Return {0, @invalid_rcpts} if rcpt-list contains any invalid addresses.  No mail is sent in this case.";
"Return {1, @actual_rcpts} if successful.";
from = args[1];
to = args[2];
hdrs = args[3];
msg = args[4];
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
.
#45:3
"WIZARDLY";
"raw_send(text,rcpts,sender) -- does the actual sending of a message.  Assumes that text has already been formatted correctly.  Decides who to send it to and who wants to be notified about it and does so.";
"Return {E_PERM} if the caller is not entitled to use this verb.";
"Return {0, @invalid_rcpts} if rcpts contains any invalid addresses.  No mail is sent in this case.";
"Return {1, @actual_rcpts} if successful.";
text = args[1];
rcpts = args[2];
from = args[3];
if (typeof(rcpts) != LIST)
rcpts = {rcpts};
endif
if (!(caller in {$mail_agent, $mail_editor}))
return {E_PERM};
elseif (bogus = (resolve = this:resolve_addr(rcpts, from))[1])
return {0, bogus};
else
this:touch(rcpts);
actual_rcpts = resolve[2];
biffs = resolve[3];
results = {};
for recip in (actual_rcpts)
if ((ticks_left() < 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
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
if ((len = length(actual_rcpts)) > 10)
len = 10;
endif
if (len)
this.total_recipients[len] = this.total_recipients[len] + 1;
endif
return {1, @actual_rcpts};
endif
.
#45:4
who = args[1];
if ($object_utils:has_verb(who, verb))
return who:(verb)(@listdelete(args, 1));
else
return {};
endif
.
#45:5
"touch(name or list,seen) => does .last_used_time = time() if we haven't already touched this in the last hour";
recip = args[1];
seen = (length(args) >= 2) ? args[2] | {};
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
.
#45:6
player:tell_lines(this.description);
for c in (this.contents)
c:look_self();
endfor
.
#45: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());
.
#45: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)
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;
else
ok = 1;
endif
endfor
return ok;
.
#45: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..length(string)];
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
endfor
endif
endfor
return partial && $failed_match;
endif
.
#45: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 = args[1];
if (!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
.
#45:11
match_result = args[1];
string = args[2];
cmd_id = {@args, ""}[3] || "";
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;
.
#45:12
":make_message(sender,recipients,subject/replyto,body)";
" => message in the form as it will get sent.";
from = args[1];
fromline = tostr(valid(from) ? from.name | "???", " (", from, ")");
if (typeof(recips = args[2]) != LIST)
recips = {recips};
endif
recips = this:name_list(@recips);
if (typeof(hdrs = args[3]) != LIST)
subj = hdrs;
replyto = valid(from) && ((!is_player(from)) && ((!$object_utils:isa(from, $mail_recipient)) && this:name(from.owner)));
else
subj = hdrs[1];
replyto = {@hdrs, 0}[2] && this:name_list(@hdrs[2]);
endif
body = args[4];
if (typeof(body) != LIST)
body = body ? {body} | {};
endif
return {time(), fromline, recips, subj || " ", @replyto ? {"Reply-to: " + replyto} | {}, "", @body};
.
#45:13
return $string_utils:english_list($list_utils:map_arg(this, "name", args), "no one");
.
#45:14
":parse_address_field(string) => list of objects";
"This is the standard routine for parsing address lists that appear in From:, To: and Reply-To: lines";
objects = {};
string = args[1];
while (e = index(string, ")"))
if ((s = rindex(string[1..e], "(#")) && (#0 != (o = toobj(string[s + 2..e - 1]))))
objects = {@objects, o};
endif
string = string[e + 1..length(string)];
endwhile
return objects;
.
#45:15
":display_seq_full(msg_seq[,preamble]) => {cur, last-read-date}";
"This is the default message display routine.";
"Prints entire messages on folder (caller) to player.  msg_seq is the handle returned by :parse_message_seq(...) and indicates which messages should be printed.  preamble, if given will precede the output of the message itself, in which case the message number will be substituted for \"%d\".  Returns the number of the final message in the sequence (which can be then used as the new current message number).";
set_task_perms(caller_perms());
preamble = {@args, ""}[2];
cur = date = 0;
for x in (msgs = caller:messages_in_seq(args[1]))
cur = x[1];
date = x[2][1];
player:display_message(preamble ? strsub(preamble, "%d", tostr(cur)) | {}, player:msg_text(@x[2]));
if ((ticks_left() < 500) || (seconds_left() < 2))
suspend(0);
endif
endfor
return {cur, date};
.
#45:16
":display_seq_headers(msg_seq[,cur[,last_read_date]])";
"This is the default header display routine.";
"Prints a list of headers of messages on caller to player.  msg_seq is the handle returned by caller:parse_message_seq(...).  cur is the player's current message.  last_read_date is the date of the last of the already-read messages.";
set_task_perms(caller_perms());
cur = {@args, 0}[2];
last_old = {@args, $maxint, $maxint}[3];
width = player:linelen() || 79;
for x in (msgs = caller:messages_in_seq(args[1]))
line = tostr($string_utils:right(x[1], 4, (cur == x[1]) ? ">" | " "), (x[2][1] > last_old) ? ":+ " | ":  ", caller:msg_summary_line(@x[2]));
player:tell(line[1..min(width, length(line))]);
if ((ticks_left() < 500) || (seconds_left() < 2))
suspend(0);
endif
endfor
player:tell("----+");
.
#45:17
":rm_message_seq(msg_seq)  removes the given sequence of from folder (caller)";
"...removed messages are saved in .messages_going for possible restoration.";
set_task_perms(caller_perms());
old = caller.messages;
new = save = nums = {};
next = 1;
for i in [1..length(seq = args[1]) / 2]
if ((ticks_left() < 500) || (seconds_left() < 2))
player:tell("... rmm ", old[next][1] - 1);
suspend(0);
endif
start = seq[(2 * i) - 1];
new = {@new, @old[next..start - 1]};
save = {@save, {start - next, old[start..(next = seq[2 * i]) - 1]}};
nums = {@nums, old[start][1], old[next - 1][1] + 1};
endfor
new = {@new, @old[next..length(old)]};
caller.messages_going = save;
caller.messages = new;
if ($object_utils:has_callable_verb(caller, "_fix_last_msg_date"))
caller:_fix_last_msg_date();
endif
return $seq_utils:tostr(nums);
.
#45:18
":undo_rmm()  restores previously deleted messages in .messages_going to .messages.";
set_task_perms(caller_perms());
old = caller.messages;
new = seq = {};
last = 0;
next = 1;
for s in (caller.messages_going)
new = {@new, @old[last + 1..last + s[1]], @s[2]};
last = last + s[1];
seq = {@seq, next + s[1], next = length(new) + 1};
endfor
caller.messages = {@new, @old[last + 1..length(old)]};
caller.messages_going = {};
if ($object_utils:has_callable_verb(caller, "_fix_last_msg_date"))
caller:_fix_last_msg_date();
endif
return seq;
.
#45:19
":list_rmm()    displays contents of .messages_going.";
":expunge_rmm() destroys contents of .messages_going once and for all.";
"... both return the number of messages in .messages_going.";
set_task_perms(caller_perms());
msgs = seq = {};
for s in (caller.messages_going)
msgs = {@msgs, @s[2]};
endfor
if (verb == "expunge_rmm")
caller.messages_going = {};
else
for x in (msgs)
player:tell($string_utils:right(x[1], 4), ":  ", caller:msg_summary_line(@x[2]));
if ((ticks_left() < 500) || (seconds_left() < 2))
suspend(0);
endif
endfor
if (msgs)
player:tell("----+");
endif
endif
return length(msgs);
.
#45:20
":renumber([cur]) -- assumes caller is a $mail_recipient or a $player.";
"...renumbers caller.messages, doing a suspend() if necessary.";
"...returns {number of messages,new cur}.";
set_task_perms(caller_perms());
cur = {@args, 0}[1];
caller.messages_going = {};
"... blow away @rmm'ed messages since there's no way to tell what their new numbers should be...";
msgs = caller.messages;
if (cur)
cur = $list_utils:iassoc_sorted(cur, msgs);
endif
while (1)
"...find first out-of-sequence message...";
l = 0;
r = (len = length(msgs)) + 1;
while ((r - 1) > l)
if (msgs[i = (r + l) / 2][1] > i)
r = i;
else
l = i;
endif
endwhile
"... r == first out-of-sequence, l == last in-sequence, l+1 == r ...";
if (l >= len)
return {l, cur};
endif
"...renumber as many messages as we have time for...";
chunk = {};
while (((r <= len) && (ticks_left() > 3000)) && (seconds_left() > 2))
for x in (msgs[r..min(r + 9, len)])
chunk = {@chunk, {r, x[2]}};
r = r + 1;
endfor
endwhile
caller.messages = {@msgs[1..l], @chunk, @msgs[r..len]};
if (chunk)
player:tell("...(renumbering ", l + 1, " -- ", r - 1, ")");
suspend(0);
else
player:tell("You lose.  This message collection is just too big.");
return;
endif
"... have to be careful since new mail may be received at this point...";
msgs = caller.messages;
endwhile
.
#45:21
":msg_summary_line(@msg) => date/from/subject as a single string.";
body = ("" in {@args, ""}) + 1;
if ((body > length(args)) || (!(subject = args[body])))
subject = "(None.)";
endif
date = ctime(args[1])[5..16];
from = args[2];
if (args[4] != " ")
subject = args[4];
endif
return tostr(date, "   ", $string_utils:left(from, 20), "   ", subject);
.
#45:22
"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 handle (msg_seq) that will be understood by caller:display_seq_full() or caller:display_seq_headers().";
set_task_perms(caller_perms());
if (!(nummsgs = caller:length_all_msgs()))
return "%f %<has> no messages.";
elseif (typeof(strings = args[1]) != LIST)
strings = {strings};
endif
cur = {@args, 0}[2];
last_old = {@args, 0, 0}[3];
seq = result = {};
mode = #0;
"... changes to 0 if we start seeing message numbers, to 1 if we see masks...";
keywords = ":from:%from:to:%to:subject:body:before:after:since:until:first:last";
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"}};
strnum = 0;
for string in (strings)
strnum = strnum + 1;
$command_utils:suspend_if_needed(0);
if (string && ((c = index(string, ":")) && ((k = index(keywords, ":" + string[1..c - 1])) && (k == rindex(keywords, ":" + string[1..c - 1])))))
"...we have a mask to apply...";
keywd = $list_utils:assoc(k, keyalist)[2];
if (mode == #0)
seq = {1, nummsgs + 1};
endif
mode = 1;
if (k <= 27)
"...from, subject, to, body...";
pattern = string[c + 1..length(string)];
if (keywd in {"subject", "body"})
elseif (keywd[1] == "%")
pattern = $string_utils:explode(pattern, "|");
else
pattern = this:((keywd == "to") ? "_parse_to" | "_parse_from")(pattern);
if (typeof(pattern) == STR)
return pattern;
endif
endif
seq = caller:(keywd + "_msg_seq")(pattern, seq);
if (typeof(seq) == STR)
if (strnum == 1)
return seq;
else
seq = {};
endif
endif
elseif (k <= 51)
"...before, since, after, until...";
if (typeof(date = this:_parse_date(string[c + 1..length(string)])) != NUM)
return tostr("Bad date `", string, "':  ", date);
endif
s = caller:length_date_le((keywd in {"before", "since"}) ? date - 1 | (date + 86399));
if (keywd in {"before", "until"})
seq = $seq_utils:remove(seq, s + 1, nummsgs);
else
seq = $seq_utils:remove(seq, 1, s);
endif
else
"...first, last...";
if (n = tonum(string[c + 1..length(string)]))
seq = $seq_utils:(keywd + "n")(seq, n);
else
return tostr("Bad number in `", string, "'");
endif
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 and @answer: 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 (string == "next")
if ((i = caller:length_num_le(cur) + 1) <= nummsgs)
seq = $seq_utils:add(seq, i, i);
else
return "%f %<has> no next message.";
endif
elseif (string == "prev")
if (i = caller:length_num_le(cur - 1))
seq = $seq_utils:add(seq, i, 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 (n = tonum(string) || (((string in {"last", "$"}) && -1) || ((string == "cur") && cur)))
if (n <= 0)
seq = $seq_utils:add(seq, max(0, nummsgs + n) + 1, nummsgs);
elseif (i = caller:exists_num_eq(n))
seq = $seq_utils:add(seq, i, i);
else
return (string == "cur") ? "%f's current message has been removed." | tostr("%f %<has> no message numbered `", string, "'.");
endif
elseif (((i = index(string, "..")) > 1) || ((i = index(string, "-")) > 1))
if ((start = tonum(sst = string[1..i - 1])) > 0)
s = caller:length_num_le(start - 1);
elseif (sst in {"next", "prev", "cur"})
s = max(0, caller:length_num_le(cur - (sst != "next")) - (sst == "prev"));
elseif (sst in {"last", "$"})
s = nummsgs - 1;
else
return {$seq_utils:union(result, seq), @strings[strnum..length(strings)]};
endif
j = (string[i] == ".") ? i + 2 | (i + 1);
if ((end = tonum(est = string[j..length(string)])) > 0)
e = caller:length_num_le(end);
elseif (est in {"next", "prev", "cur"})
e = min(nummsgs, caller:length_num_le(cur - (est == "prev")) + (est == "next"));
elseif (est in {"last", "$"})
e = nummsgs;
else
return {$seq_utils:union(result, seq), @strings[strnum..length(strings)]};
endif
if (s < e)
seq = $seq_utils:add(seq, s + 1, e);
else
return tostr("%f %<has> no messages in range ", string, ".");
endif
elseif (string == "cur")
return "%f %<has> no current message.";
else
return {$seq_utils:union(result, seq), @strings[strnum..length(strings)]};
endif
endif
endfor
return {$seq_utils:union(result, seq)};
.
#45:23
":_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;
.
#45:24
words = $string_utils:explode(args[1], "-");
if (length(words) == 1)
time = $time_utils:from_day(words[1], -1);
if (typeof(time) == ERR)
time = "weekday expected.";
endif
elseif ((!words) || ((length(words) > 3) || ((!tonum(words[1])) || (E_TYPE == (year = $code_utils:tonum({@words, "-1"}[3]))))))
time = "Date should be of the form `5-Jan', `5-Jan-92', `Wed',`Wednesday'";
else
day = tonum(words[1]);
time = $time_utils:dst_midnight($time_utils:from_month(words[2], -1, day));
if (length(words) == 3)
thisyear = tonum(ctime(time)[21..24]);
if (100 > year)
year = (thisyear + 50) - (((thisyear - year) + 50) % 100);
endif
time = $time_utils:dst_midnight($time_utils:from_month(words[2], (year - thisyear) - (year <= thisyear), day));
endif
endif
return time;
.
#45:25
":new_message_num() => number that the next incoming message will receive.";
set_task_perms(caller_perms());
new = (msgs = caller.messages) ? msgs[length(msgs)][1] + 1 | 1;
if (rmsgs = caller.messages_going)
lbrm = rmsgs[length(rmsgs)][2];
return max(new, lbrm[length(lbrm)][1] + 1);
else
return new;
endif
.
#45:26
set_task_perms(caller_perms());
return length(caller.messages);
.
#45:27
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
.
#45:28
set_task_perms(caller_perms());
date = args[1];
msgs = caller.messages;
if ((len = length(caller.messages)) < 25)
for r in [0..len - 1]
posted = msgs[len - r][2][1];
if (posted <= date)
return r;
endif
endfor
return len;
else
l = 1;
r = len;
while (l <= r)
posted = msgs[i = (r + l) / 2][2][1];
if (date < posted)
r = i - 1;
else
l = i + 1;
endif
endwhile
return len - r;
endif
.
#45:29
":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);
.
#45:30
":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);
.
#45:31
":from_msg_seq(object or list[,mask])";
" => msg_seq of messages from any of these senders";
set_task_perms(caller_perms());
if (typeof(plist = args[1]) != LIST)
plist = {plist};
endif
mask = {@args, {1}}[2];
i = 1;
fseq = {};
for msg in (caller.messages)
if ((!mask) || (i < mask[1]))
elseif ((length(mask) < 2) || (i < mask[2]))
fromline = msg[2][2];
if (toobj(fromline[rindex(fromline, "(") + 1..rindex(fromline, ")") - 1]) in plist)
fseq = $seq_utils:add(fseq, i, i);
endif
else
mask = mask[3..length(mask)];
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return fseq || ("%f %<has> no messages from " + $string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%n (%#)", plist), "no one", " or "));
.
#45:32
":%from_msg_seq(string or list of strings[,mask])";
" => msg_seq of messages with one of these strings in the from line";
set_task_perms(caller_perms());
if (typeof(nlist = args[1]) != LIST)
nlist = {nlist};
endif
i = 1;
fseq = {};
mask = {@args, {1}}[2];
for msg in (caller.messages)
if ((!mask) || (i < mask[1]))
elseif ((length(mask) < 2) || (i < mask[2]))
fromline = " " + msg[2][2];
for n in (nlist)
if (index(fromline, n))
fseq = $seq_utils:add(fseq, i, i);
endif
endfor
else
mask = mask[3..length(mask)];
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return fseq || ("%f %<has> no messages from " + $string_utils:english_list($list_utils:map_arg($string_utils, "print", nlist), "no one", " or "));
.
#45:33
":to_msg_seq(object or list[,mask]) => msg_seq of messages to those people";
set_task_perms(caller_perms());
if (typeof(plist = args[1]) != LIST)
plist = {plist};
endif
mask = {@args, {1}}[2];
i = 1;
seq = {};
for msg in (caller.messages)
if ((!mask) || (i < mask[1]))
elseif ((length(mask) < 2) || (i < mask[2]))
toline = msg[2][3];
for r in ($mail_agent:parse_address_field(toline))
if (r in plist)
seq = $seq_utils:add(seq, i, i);
endif
endfor
else
mask = mask[3..length(mask)];
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return seq || ("%f %<has> no messages to " + $string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%n (%#)", plist), "no one", " or "));
.
#45:34
":%to_msg_seq(string or list of strings[,mask])";
" => msg_seq of messages containing one of strings in the to line";
set_task_perms(caller_perms());
if (typeof(nlist = args[1]) != LIST)
nlist = {nlist};
endif
i = 1;
seq = {};
mask = {@args, {1}}[2];
for msg in (caller.messages)
if ((!mask) || (i < mask[1]))
elseif ((length(mask) < 2) || (i < mask[2]))
toline = " " + msg[2][3];
for n in (nlist)
if (index(toline, n))
seq = $seq_utils:add(seq, i, i);
endif
endfor
else
mask = mask[3..length(mask)];
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return seq || ("%f %<has> no messages to " + $string_utils:english_list($list_utils:map_arg($string_utils, "print", nlist), "no one", " or "));
.
#45:35
":subject_msg_seq(target) => msg_seq of messages with target in the Subject:";
set_task_perms(caller_perms());
target = args[1];
i = 1;
seq = {};
mask = {@args, {1}}[2];
for msg in (caller.messages)
if ((!mask) || (i < mask[1]))
elseif ((length(mask) < 2) || (i < mask[2]))
subject = msg[2][4];
if (index(subject, target))
seq = $seq_utils:add(seq, i, i);
endif
else
mask = mask[3..length(mask)];
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return seq || (("%f %<has> no messages with subjects containing `" + target) + "'");
.
#45:36
":body_msg_seq(target[,mask]) => msg_seq of messages with target in the body";
set_task_perms(caller_perms());
target = args[1];
i = 1;
seq = {};
mask = {@args, {1}}[2];
for msg in (caller.messages)
if ((!mask) || (i < mask[1]))
elseif ({@mask, $maxint}[2] <= i)
mask = mask[3..length(mask)];
elseif ((bstart = "" in (msg = msg[2])) && ((l = length(msg)) > bstart))
while ((!index(msg[l], target)) && ((l = l - 1) > bstart))
$command_utils:suspend_if_needed(0);
endwhile
if (l > bstart)
seq = $seq_utils:add(seq, i, i);
endif
endif
i = i + 1;
$command_utils:suspend_if_needed(0);
endfor
return seq || tostr("%f %<has> no messages containing `", target, "' in the body.");
.
#45:37
":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
.
#45:38
":__convert_new(@msg) => msg in new format (if it isn't already)";
"               ^ don't forget the @ here.";
"If the msg is already in the new format it passes through unchanged.";
"If the msg format is unrecognizable, warnings are printed.";
if (typeof(date = args[1]) != NUM)
date = 0;
start = 1;
else
start = 2;
if (!((colon = index(args[2], ":")) && (args[2][1..colon] in {"From:", "To:", "Subject:"})))
return args;
endif
endif
from = to = 0;
subject = " ";
blank = "" in {@args, ""};
newhdr = {};
for line in (args[start..blank - 1])
if (index(line, "Date:") == 1)
if (date)
player:notify("Warning: two dates?");
endif
date = $time_utils:from_ctime(line[6..length(line)]);
elseif (index(line, "From:") == 1)
if (from)
player:notify("Warning: two from-lines?");
endif
from = $string_utils:triml(line[6..length(line)]);
elseif (index(line, "To:") == 1)
if (to)
player:notify("Warning: two to-lines?");
endif
to = $string_utils:triml(line[6..length(line)]);
elseif (index(line, "Subject:") == 1)
subject = $string_utils:triml(line[9..length(line)]);
else
newhdr = {@newhdr, line};
endif
endfor
if (!from)
player:notify("Warning: no from-line.");
endif
if (!to)
player:notify("Warning: no to-line.");
endif
return {date, from, to, subject, @newhdr, @args[blank..length(args)]};
.
#45:39
":to_text(@msg) => message in text form (suitable for printing)";
"Uses the ctime verb on `player` to display what we hope is the proper localtime for the person reading the message.";
return {"Date:     " + player:ctime(args[1]), "From:     " + args[2], "To:       " + args[3], @(args[4] == " ") ? {} | {"Subject:  " + args[4]}, @args[5..length(args)]};
.
#45: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
.
#45: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;
.
#45:42
return valid(what = args[1]) && (($player in (ances = $object_utils:ancestors(what))) || ($mail_recipient in ances));
.
#45:43
if (!caller_perms().wizard)
return E_PERM;
endif
this:expire_mail_lists();
.
#45:44
if (!caller_perms().wizard)
return E_PERM;
endif
owner = $code_utils:verb_perms();
for x in ($object_utils:leaves_suspended($mail_recipient))
try
x:maybe_expire_old_messages();
except e (ANY)
owner:tell_lines($error:full_traceback(e));
endtry
(ticks_left() < 10000) && suspend(0);
endfor
.
#45:45
if ($code_utils:task_valid(this.expire_mail_task))
return E_MAXREC;
endif
fork tid (0)
while (1)
suspend($time_utils.month);
this:expire_mail();
endwhile
endfork
this.expire_mail_task = tid;
.
#45:46
"This should _really_ be renamed, goddammit.";
if (!args)
return pass(@args);
endif
what = args[1];
if (0)
return tostr(strsub(strsub($object_utils:isa(what, $player) ? what.name | tostr("*", what:mail_name()), "(", ""), ")", ""), " (", what, ")");
elseif (!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:0
return this:ok(who = args[1]) && tostr("a letter ", this.sending[who] ? "(in transit) " | "", "to ", this:recipient_names(who), (subject = this.subjects[who]) && tostr(" entitled \"", subject, "\""));
.
#46: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:get_option($mail_options, "replyto"))
replyto = this:parse_recipients({}, replyto, ".mail_options: ");
endif
if (0 == (subject = {@args, 0}[3]))
if (player:get_option($mail_options, "nosubject"))
subject = "";
else
player:tell("Subject:");
subject = $command_utils:read();
endif
endif
return {rcpts, subject, replyto, {}};
endif
elseif (which == 1)
return args[3..6];
elseif (!(to_subj = this:parse_msg_headers(msg = args[3], flags = args[4])))
else
include = {};
if ("include" in flags)
prefix = ">            ";
for line in ($mail_agent:to_text(@msg))
if (!line)
prefix = ">  ";
endif
include = {@include, @this:fill_string(">  " + line, 70, prefix)};
endfor
endif
return {@to_subj, args[5], include};
endif
return 0;
.
#46:2
if (this:ok(who = args[1]))
this.sending[who] = 0;
this.recipients[who] = args[2];
this.subjects[who] = args[3];
this.replytos[who] = args[4] || {};
this:load(who, args[5]);
this.active[who]:tell("Composing ", this:working_on(who));
p = this.active[who];
if (p:get_option($mail_options, "enter") && (!args[5]))
if (typeof(lines = $command_utils:read_lines()) == ERR)
p:tell(lines);
else
this:insert_line(p in this.active, lines, 0);
endif
endif
endif
.
#46: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
.
#46: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));
.
#46: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
.
#46: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
.
#46:7
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
elseif (!args)
player:tell("Your message is currently to ", this:recipient_names(who), ".");
else
this.recipients[who] = this:parse_recipients({}, args);
this:set_changed(who, 1);
player:tell("Your message is now to ", this:recipient_names(who), ".");
endif
.
#46:8
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
else
this.recipients[who] = this:parse_recipients(this.recipients[who], args);
this:set_changed(who, 1);
player:tell("Your message is now to ", this:recipient_names(who), ".");
endif
.
#46:9
"parse_recipients(prev_list,list_of_strings) -- parses list of strings and adds any resulting player objects to prev_list.  Optional 3rd arg is prefixed to any mismatch error messages";
recips = args[1];
cmd_id = {@args, ""}[3] || "";
pstrings = mdrops = {};
for s in ((typeof(l = args[2]) == LIST) ? l | {l})
if (typeof(s) != STR)
if ($mail_agent:is_recipient(s))
mdrops = {@mdrops, s};
else
player:tell(cmd_id, s, " is not a valid mail recipient.");
endif
elseif ((!s) || (s[1] != "*"))
pstrings = {@pstrings, s};
elseif (!$mail_agent:match_failed(md = $mail_agent:match(s), s, cmd_id))
mdrops = {@mdrops, md};
endif
endfor
newreps = $string_utils:match_player(pstrings);
pmr = $command_utils:player_match_result({@recips, @newreps, @mdrops}, {@recips, @pstrings, @mdrops}, cmd_id);
return listdelete(pmr, 1);
.
#46:10
return this:ok(who = args[1]) && $mail_agent:name_list(@this.recipients[who]);
.
#46:11
return $mail_agent:make_message(@args);
.
#46:12
"(obsolete verb... see $mail_agent:name_list)";
return $mail_agent:(verb)(@args[1]);
.
#46:13
"parse_msg_headers(msg,flags)";
"  parses msg to extract reply recipients and construct a subject line";
"  if the \"all\" flag is present, reply goes to all of the original recipients";
"  returns a list {recipients, subjectline} or 0 in case of error.";
msg = args[1];
replyall = "all" in args[2];
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};
.
#46:14
flags = {};
for o in ({"all", "include"})
if (player:get_option($mail_options, o))
flags = {@flags, o};
endif
endfor
reply_to = player:get_option($mail_options, "replyto") || {};
flaglist = "+1#include -1#noinclude +2#all -2#sender 0#replyto ";
for a in (args)
if (i = index(a, "="))
value = a[i + 1..length(a)];
a = a[1..i - 1];
else
value = "";
endif
if ((typeof(a) != STR) || ((i = index(flaglist, "#" + a)) < 3))
player:tell("Unrecognized answer/reply option:  ", a);
return 0;
elseif (i != rindex(flaglist, "#" + a))
player:tell("Ambiguous answer/reply option:  ", a);
return 0;
elseif (j = index("0123456789", flaglist[i - 1]) - 1)
if (value)
player:tell("Flag does not take a value:  ", a);
return 0;
endif
f = {"include", "all"}[j];
flags = (flaglist[i - 2] == "+") ? setadd(flags, f) | setremove(flags, f);
elseif ((!value) || (value = this:parse_recipients({}, $string_utils:explode(value), "replyto flag:  ")))
reply_to = value || {};
endif
endfor
return {flags, reply_to};
.
#46:15
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
else
if (args)
this.replytos[who] = rt = this:parse_recipients({}, args);
this:set_changed(who, 1);
else
rt = this.replytos[who];
endif
player:tell(rt ? ("Replies will go to " + $mail_agent:name_list(@this.replytos[who])) + "." | "Reply-to field is empty.");
endif
.
#46:16
"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
.
#46:17
if (dobjstr)
if (!(recips = this:parse_recipients({}, args)))
"parse_recipients has already complained about anything it doesn't like";
return;
endif
elseif (caller != player)
return E_PERM;
elseif (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
else
recips = this.recipients[who];
endif
resolve = $mail_agent:resolve_addr(recips, player);
if (resolve[1])
player:tell("Bogus addresses:  ", $string_utils:english_list(resolve[1]));
else
player:tell(dobjstr ? ("Mail to " + $mail_agent:name_list(@recips)) + " actually goes to " | "Your mail will actually go to ", $mail_agent:name_list(@resolve[2]));
endif
.
#46:18
player:tell_lines({"Available aliases:", ""});
for c in ((dobjstr == "all") ? $object_utils:descendants($mail_recipient) | $mail_agent.contents)
if (c:is_usable_by(player) || c:is_readable_by(player))
c:look_self();
endif
endfor
.
#46:19
if (!iobjstr)
player:tell("Usage:  ", verb, " [<list-of-people/lists>] to <list>");
return;
elseif ($mail_agent:match_failed(iobj = $mail_agent:match(iobjstr), iobjstr))
return;
endif
rstrs = dobjstr ? $string_utils:explode(dobjstr) | {"me"};
recips = this:parse_recipients({}, rstrs);
outcomes = iobj:add_forward(@recips);
if (typeof(outcomes) != LIST)
player:tell(outcomes);
return;
endif
added = {};
for r in [1..length(recips)]
if ((t = typeof(e = outcomes[r])) == OBJ)
added = setadd(added, recips[r]);
else
player:tell(verb, " ", recips[r].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
.
#46:20
if (!iobjstr)
player:tell("Usage:  ", verb, " [<list-of-people/lists>] from <list>");
return;
elseif ($mail_agent:match_failed(iobj = $mail_agent:match(iobjstr), iobjstr))
return;
endif
rstrs = dobjstr ? $string_utils:explode(dobjstr) | {"me"};
recips = this:parse_recipients({}, rstrs);
outcomes = iobj:delete_forward(@recips);
if (typeof(outcomes) != LIST)
player:tell(outcomes);
return;
endif
removed = {};
for r in [1..length(recips)]
if (typeof(e = outcomes[r]) == ERR)
player:tell(verb, " ", recips[r].name, " from ", iobj.name, ":  ", (e == E_INVARG) ? "Not on list." | e);
else
removed = setadd(removed, recips[r]);
endif
endfor
if (removed)
player:tell($string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%(name) (%#)", removed)), " removed from ", iobj.name, " (", iobj, ")");
endif
.
#46:21
return this:ok(who = args[1]) && (this.sending[who] || pass(@args));
.
#46:22
"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);
.
#46:23
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"};
.
#47: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
.
#47: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
.
#47: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
.
#47: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, ")");
.
#47: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;
.
#47:5
"WIZARDLY";
if ((caller != $note_editor) || (caller_perms() != $note_editor.owner))
return E_PERM;
endif
set_task_perms(player);
{spec} = args;
if (typeof(spec) == OBJ)
text = spec:text();
else
{what, prop} = spec;
if ($object_utils:has_callable_verb(what, "get_" + prop))
text = what:("get_" + prop)();
endif
if (typeof(text) == ERR)
text = what.(prop);
endif
endif
if (((tt = typeof(text)) in {ERR, STR}) || ((tt == LIST) && ((!text) || (typeof(text[1]) == STR))))
return text;
else
return E_TYPE;
endif
.
#47:6
"WIZARDLY";
if ((caller != $note_editor) || (caller_perms() != $note_editor.owner))
return E_PERM;
endif
{spec, text} = args;
set_task_perms(player);
if (typeof(spec) == OBJ)
return spec:set_text(args[2]);
endif
{what, prop} = spec;
if ($object_utils:has_callable_verb(what, "set_" + prop))
result = what:("set_" + prop)(text);
endif
if (typeof(result) == ERR)
return what.(prop) = text;
else
return result;
endif
.
#47: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")))
player:tell(object, "(", note, ") doesn't look like a note.");
else
return note;
endif
return 1;
.
#47: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
.
#47:9
"mode [string|list]";
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
endif
if (index("string", dobjstr) == 1)
this.strmode[who] = mode = 1;
player:tell("Now in string mode:");
elseif (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
.
#47:10
what = args[1];
text = args[2];
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)};
.
#47:11
":list_line(index-in-this-active, line-number)";
"If the object on which the text will be saved defines a :list_edited_line verb and that verb returns true, assume that it has done the listing.";
who = args[1];
if (!this:ok(who))
return E_PERM;
endif
line = args[2];
curr = this.inserting[who];
what = this.objects[who];
text = this.texts[who];
if (!what:list_edited_line(text, line, curr))
f = 1 + (line in {curr - 1, curr});
l = (($string_utils:right(line, 3, " _^"[f]) + ":_^"[f]) + " ") + text[line];
player:tell(l);
endif
.
#47:12
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];
what = this.objects[who];
text = this.texts[who];
if (what:list_edited_text(text, ins))
return;
endif
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
.
#47:13
if ($perm_utils:controls(caller_perms(), this))
"return `pass(@args) ! E_VERBNF => 0';";
return pass(@args);
else
return E_PERM;
endif
.
#48:0
if (!args)
player:tell("edit what?");
else
this:invoke(argstr, verb);
endif
.
#48:1
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
elseif (!(prepstr && iobjstr))
object = this.objects[who];
vname = this.verbnames[who];
changeverb = 0;
elseif ((prepstr != "as") || (!(spec = $code_utils:parse_verbref(iobjstr))))
player:tell("Usage: ", verb, " [as <object>:<verb>]");
return;
elseif ($command_utils:object_match_failed(object = player:my_match_object(spec[1], this:get_room(player)), spec[1]))
return;
else
vname = spec[2];
changeverb = 1;
endif
iobjstr = (tostr(object) + ":") + this:verb_name(object, vname);
if (player.eval_subs && player:get_option($edit_options, "eval_subs"))
text = {};
for x in (this:text(who))
text = {@text, $code_utils:substitute(x, player.eval_subs)};
endfor
else
text = this:text(who);
endif
if (result = this:set_verb_code(object, vname, text))
player:tell(iobjstr, " not compiled because:");
for x in (result)
player:tell("  ", x);
endfor
elseif (typeof(result) == ERR)
player:tell({result, ("You do not have write permission on " + iobjstr) + ".", ("The verb " + iobjstr) + " does not exist (!?!)", ("The object " + tostr(object)) + " does not exist (!?!)"}[1 + (result in {E_PERM, E_VERBNF, E_INVARG})]);
if (!prepstr)
player:tell("Do 'compile as <object>:<verb>' to write your code to another verb.");
endif
changeverb = 0;
else
player:tell(iobjstr, " successfully compiled.");
this:set_changed(who, 0);
endif
if (changeverb)
this.objects[who] = object;
this.verbnames[who] = vname;
endif
.
#48:2
return this:ok(who = args[1]) && tostr(this.objects[who], ":", this:verb_name(this.objects[who], this.verbnames[who]), " ", this:verb_args(this.objects[who], this.verbnames[who]));
"return this:ok(who = args[1]) && tostr(this.objects[who]) + \":\" + this.verbnames[who];";
.
#48:3
if (this:ok(who = args[1]))
object = args[2];
vname = args[3];
this:load(who, args[4]);
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
.
#48: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("Don't know what to do with \"", $string_utils:from_list(pas[2], " "), "\"");
return;
endif
argspec = pas[1];
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]))
if (argspec)
vnum = $code_utils:find_verb_named(object, spec[2], 0);
while ((vnum >= 0) && (verb_args(object, vname = tostr(vnum)) != argspec))
vnum = $code_utils:find_verb_named(object, spec[2], vnum + 1);
endwhile
if (vnum >= 0)
code = this:fetch_verb_code(object, vname);
else
code = E_VERBNF;
endif
else
vname = tostr($code_utils:find_verb_named(object, spec[2], 0));
code = this:fetch_verb_code(object, vname);
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, vname, code};
endif
endif
return 0;
.
#48: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], 1);
endif
.
#48: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]);
endif
.
#48:7
if (caller == $verb_editor)
set_task_perms(player);
endif
object = args[1];
vname = args[2];
code = args[3];
vname = strsub($string_utils:explode(verb_info(object, vname)[3])[1], "*", "");
name = tostr(object.name, ":", vname);
vargs = verb_args(object, vname);
prep = $string_utils:explode(vargs[2], "/")[1];
return {name, code, tostr("@program ", object, ":", vname, " ", vargs[1], " \"", prep, "\" ", vargs[3])};
.
#48: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);
return tostr("\"", verb_info(args[1], args[2])[3], "\"");
endif
.
#48: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]), " ");
endif
.
#48: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: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
.
#49: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
.
#49: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
.
#49: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
.
#49:4
if (i = index(argstr, "\""))
text = argstr[i + 1..length(argstr)];
argstr = argstr[1..i - 1];
else
text = 0;
endif
spec = $string_utils:trim(argstr);
if (index("next", verb) == 1)
verb = "next";
spec = "+" + (spec || "1");
elseif (index("prev", verb) == 1)
verb = "prev";
spec = "-" + (spec || "1");
else
spec = spec || ".";
endif
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
elseif (ERR == typeof(number = this:parse_insert(who, spec)))
if (verb in {"next", "prev"})
player:tell("Argument must be a number.");
else
player:tell("You must specify an integer or `$' for the last line.");
endif
elseif ((number > (max = length(this.texts[who]) + 1)) || (number < 1))
player:tell("That would take you out of range (to line ", number, "?).");
else
this.inserting[who] = number;
if (typeof(text) == STR)
this:insert_line(who, text);
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
.
#49:5
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
elseif (typeof(range = this:parse_range(who, {"_", "1"}, @args)) != LIST)
player:tell(range);
elseif (range[3])
player:tell("Junk at end of cmd:  ", range[3]);
else
player:tell_lines((text = this.texts[who])[from = range[1]..to = range[2]]);
player:tell("---Line", (to > from) ? "s" | "", " deleted.  Insertion point is before line ", from, ".");
this.texts[who] = {@text[1..from - 1], @text[to + 1..length(text)]};
if (!this.changes[who])
this.changes[who] = 1;
this.times[who] = time();
endif
this.inserting[who] = from;
endif
.
#49:6
if (callers() && (caller != this))
return E_PERM;
endif
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
elseif (typeof(subst = this:parse_subst(argstr && (argstr[1] + argstr), "gcr", "Empty search string?")) != LIST)
player:tell(tostr(subst));
return;
endif
{fromstr, search, specs, start} = subst;
if (typeof(startno = start ? this:parse_insert(who, start) | this.inserting[who]) == ERR)
player:tell("Starting from where?", start ? ("  (can't parse " + start) + ")" | "");
return;
endif
global = index(specs, "g", 1);
regexp = index(specs, "r", 1);
case = !index(specs, "c", 1);
text = this.texts[who];
tlen = length(text);
found = 0;
for lineno in [startno..tlen]
if (!(regexp ? match(text[lineno], search, case) | index(text[lineno], search, case)))
continue;
endif
found = lineno;
this:list_line(who, lineno);
if (!global)
"quit after the first match";
break;
endif
endfor
if (!found)
player:tell("`", search, "' not found.");
else
"perhaps only set insertion for non-global matches?";
this.inserting[who] = lineno + 1;
endif
.
#49:7
if (callers() && (caller != this))
return E_PERM;
elseif (!(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, tostr, specs, dummy} = subst;
global = index(specs, "g", 1);
regexp = index(specs, "r", 1);
case = !index(specs, "c", 1);
munged = {};
text = this.texts[who];
changed = {};
{from, to} = range[1..2];
for line in [from..to]
t = t0 = text[line];
if (!fromstr)
t = tostr + t;
elseif (global)
if (regexp)
while (new = this:subst_regexp(t, fromstr, tostr, case))
t = new;
endwhile
else
t = strsub(t, fromstr, tostr, case);
endif
else
if (regexp)
(new = this:subst_regexp(t, fromstr, tostr, case)) && (t = new);
else
(i = index(t, fromstr, case)) && (t = (t[1..i - 1] + tostr) + t[i + length(fromstr)..length(t)]);
endif
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
"19990112: Ported from LambdaMOO for global substitution and regexp hooks.";
.
#49:8
verb = (is_move = verb[1] == "m") ? "move" | "copy";
if (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
endif
wargs = args;
t = to_pos = 0;
while (t = "to" in (wargs = wargs[t + 1..length(wargs)]))
to_pos = to_pos + t;
endwhile
range_args = args[1..to_pos - 1];
if ((!to_pos) || (ERR == typeof(dest = this:parse_insert(who, $string_utils:from_list(wargs, " ")))))
player:tell(verb, " to where? ");
elseif ((dest < 1) || (dest > ((last = length(this.texts[who])) + 1)))
player:tell("Destination (", dest, ") out of range.");
elseif (("from" in range_args) || ("to" in range_args))
player:tell("Don't use that kind of range specification with this command.");
elseif (typeof(range = this:parse_range(who, {"_", "^"}, @args[1..to_pos - 1])) != LIST)
player:tell(range);
elseif (range[3])
player:tell("Junk before `to':  ", range[3]);
elseif ((is_move && (dest >= range[1])) && (dest <= (range[2] + 1)))
player:tell("Destination lies inside range of lines to be moved.");
else
from = range[1];
to = range[2];
ins = this.inserting[who];
text = this.texts[who];
if (!is_move)
this.texts[who] = {@text[1..dest - 1], @text[from..to], @text[dest..last]};
if (ins >= dest)
this.inserting[who] = ((ins + to) - from) + 1;
endif
else
"oh shit... it's a move";
if (dest < from)
newtext = {@text[1..dest - 1], @text[from..to], @text[dest..from - 1], @text[to + 1..last]};
if ((ins >= dest) && (ins <= to))
ins = (ins > from) ? (ins - from) + dest | (((ins + to) - from) + 1);
endif
else
newtext = {@text[1..from - 1], @text[to + 1..dest - 1], @text[from..to], @text[dest..last]};
if ((ins > from) && (ins < dest))
ins = (ins <= to) ? ((ins + dest) - to) - 1 | (((ins - to) + from) - 1);
endif
endif
this.texts[who] = newtext;
this.inserting[who] = ins;
endif
if (!this.changes[who])
this.changes[who] = 1;
this.times[who] = time();
endif
player:tell("Lines ", is_move ? "moved." | "copied.");
endif
.
#49:9
if (caller != 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(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
.
#49: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 = tonum(range[3][2..length(range[3])])) < 10)))
player:tell("Usage:  fill [<range>] [@ column]   (where column >= 10).");
else
join = this:join_lines(who, @range[1..2], 1);
newlines = this:fill_string((text = this.texts[who])[from = range[1]], fill_column);
if (fill = ((nlen = length(newlines)) > 1) || (newlines[1] != text[from]))
this.texts[who] = {@text[1..from - 1], @newlines, @text[from + 1..length(text)]};
if (((insert = this.inserting[who]) > from) && (nlen > 1))
this.inserting[who] = (insert + nlen) - 1;
endif
endif
if (fill || join)
for line in [from..(from + nlen) - 1]
this:list_line(who, line);
endfor
else
player:tell("No changes.");
endif
endif
.
#49: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
.
#49: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
.
#49: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
.
#49:14
if (!(caller in {this, player}))
return E_PERM;
elseif (!valid(origin = this.original[who = player in this.active]))
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.");
.
#49:15
"This catches subst and find commands that don't fit into the usual model, e.g., s/.../.../ without the space after the s, and find commands without the verb `find'.  Still behaves in annoying ways (e.g., loses if the search string contains multiple whitespace), but better than before.";
set_task_perms(caller_perms());
if ((c = callers()) && ((c[1][1] != this) || (length(c) > 1)))
return pass(@args);
endif
verb = args[1];
v = 1;
vmax = min(length(verb), 5);
while ((v <= vmax) && (verb[v] == "subst"[v]))
v = v + 1;
endwhile
argstr = $code_utils:argstr(verb, args[2]);
if (((v > 1) && (v <= length(verb))) && (((vl = verb[v]) < "A") || (vl > "Z")))
argstr = (verb[v..length(verb)] + (argstr && " ")) + argstr;
return this:subst();
elseif ("/" == verb[1])
argstr = (verb + (argstr && " ")) + argstr;
return this:find();
else
pass(@args);
endif
.
#49:16
return this:ok(who = args[1]) && this.inserting[who];
.
#49:17
return this:ok(who = args[1]) && ((((ins = tonum(args[2])) < 1) ? E_INVARG | ((ins <= (max = length(this.texts[who]) + 1)) || (ins = max))) && (this.inserting[who] = ins));
.
#49:18
return this:ok(who = args[1]) && this.changes[who];
.
#49:19
return this:ok(who = args[1]) && (((unchanged = !args[2]) || (this.times[who] = time())) && (this.changes[who] = !unchanged));
.
#49:20
return this:ok(who = args[1]) && this.original[who];
.
#49:21
return this:ok(who = args[1]) && (((valid(origin = args[2]) && (origin != this)) || ((origin == $nothing) || E_INVARG)) && (this.original[who] = origin));
.
#49:22
return (((who = args[1]) < 1) || (who > length(this.active))) ? E_RANGE | this.readable[who];
.
#49:23
return this:ok(who = args[1]) && (this.readable[who] = !(!args[2]));
.
#49:24
return (this:readable(who = args[1] || (player in this.active)) || this:ok(who)) && this.texts[who];
.
#49: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();
.
#49:26
"Dummy routine.  The child editor should provide something informative";
return this:ok(who = args[1]) && (("something [in " + this.name) + "]");
.
#49: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
.
#49:28
return ((who = args[1] in this.active) && (typeof(this.texts[who]) == LIST)) && who;
.
#49: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
.
#49:30
":insert_line([who,] line or list of lines [,quiet])";
"  inserts the given text at the insertion point.";
"  returns E_NONE if the session has no text loaded yet.";
if (typeof(who = args[1]) != NUM)
args = {player in this.active, @args};
endif
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
elseif (typeof(text = this.texts[who]) != LIST)
return E_NONE;
else
if (typeof(lines = args[2]) != LIST)
lines = {lines};
endif
p = this.active[who];
quiet = (length(args) >= 3) ? args[3] | p:get_option($edit_options, "quiet_insert");
insert = this.inserting[who];
this.texts[who] = {@text[1..insert - 1], @lines, @text[insert..length(text)]};
this.inserting[who] = insert + length(lines);
if (lines)
if (!this.changes[who])
this.changes[who] = 1;
this.times[who] = time();
endif
if (!quiet)
if (length(lines) != 1)
p:tell("Lines ", insert, "-", (insert + length(lines)) - 1, " added.");
else
p:tell("Line ", insert, " added.");
endif
endif
else
p:tell("No lines added.");
endif
endif
.
#49:31
":append_line([who,] string)";
"  appends the given string to the line before the insertion point.";
"  returns E_NONE if the session has no text loaded yet.";
if (typeof(who = args[1]) != NUM)
args = {player in this.active, @args};
endif
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
elseif ((append = this.inserting[who] - 1) < 1)
return this:insert_line(who, {args[2]});
elseif (typeof(text = this.texts[who]) != LIST)
return E_NONE;
else
this.texts[who][append] = text[append] + args[2];
if (!this.changes[who])
this.changes[who] = 1;
this.times[who] = time();
endif
p = this.active[who];
if (!p:get_option($edit_options, "quiet_insert"))
p:tell("Appended to line ", append, ".");
endif
endif
.
#49:32
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
elseif ((from = args[2]) >= (to = args[3]))
return 0;
else
nline = "";
for line in ((text = this.texts[who])[from..to])
if (!(english = args[4]))
nline = nline + line;
else
len = length(line) + 1;
while ((len = len - 1) && (line[len] == " "))
endwhile
if (len > 0)
lastc = line[len];
if (lastc == " ")
nline = nline + line;
elseif (index(".:", lastc))
nline = (nline + line) + "  ";
else
nline = (nline + line) + " ";
endif
endif
endif
endfor
this.texts[who] = {@text[1..from - 1], nline, @text[to + 1..length(text)]};
if ((insert = this.inserting[who]) > from)
this.inserting[who] = (insert <= to) ? from + 1 | ((insert - to) + from);
endif
if (!this.changes[who])
this.changes[who] = 1;
this.times[who] = time();
endif
return to - from;
endif
.
#49:33
"parse_number(who,string,before)   interprets string as a line number.  In the event that string is `.', `before' tells us which line to use.  Return 0 if string is bogus.";
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
endif
last = length(this.texts[who]);
ins = this.inserting[who] - 1;
string = args[2];
after = !args[3];
if (!string)
return 0;
elseif ("." == string)
return ins + after;
elseif (!(i = index("_^$", string[slen = length(string)])))
return tonum(string);
else
start = {ins + 1, ins, last + 1}[i];
n = 1;
if ((slen > 1) && (!(n = tonum(string[1..slen - 1]))))
return 0;
elseif (i % 2)
return start - n;
else
return start + n;
endif
endif
.
#49:34
"parse_range(who,default,@args) => {from to rest}";
numargs = length(args);
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
elseif (!(last = length(this.texts[who])))
return this:no_text_msg();
endif
default = args[2];
r = 0;
while (default && (LIST != typeof(r = this:parse_range(who, {}, default[1]))))
default = listdelete(default, 1);
endwhile
if (typeof(r) == LIST)
from = r[1];
to = r[2];
else
from = to = 0;
endif
saw_from_to = 0;
not_done = 1;
a = 2;
while (((a = a + 1) <= numargs) && not_done)
if (args[a] == "from")
if ((a == numargs) || (!(from = this:parse_number(who, args[a = a + 1], 0))))
return "from ?";
endif
saw_from_to = 1;
elseif (args[a] == "to")
if ((a == numargs) || (!(to = this:parse_number(who, args[a = a + 1], 1))))
return "to ?";
endif
saw_from_to = 1;
elseif (saw_from_to)
a = a - 1;
not_done = 0;
elseif (i = index(args[a], "-"))
from = this:parse_number(who, args[a][1..i - 1], 0);
to = this:parse_number(who, args[a][i + 1..length(args[a])], 1);
not_done = 0;
elseif (f = this:parse_number(who, args[a], 0))
from = f;
if ((a == numargs) || (!(to = this:parse_number(who, args[a + 1], 1))))
to = from;
else
a = a + 1;
endif
not_done = 0;
else
a = a - 1;
not_done = 0;
endif
endwhile
if (from < 1)
return tostr("from ", from, "?  (out of range)");
elseif (to > last)
return tostr("to ", to, "?  (out of range)");
elseif (from > to)
return tostr("from ", from, " to ", to, "?  (backwards range)");
else
return {from, to, $string_utils:from_list(args[a..numargs], " ")};
endif
.
#49:35
"parse_ins(who,string)  interprets string as an insertion point, i.e., a position between lines and returns the number of the following line or 0 if bogus.";
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
endif
who = args[1];
last = length(this.texts[who]) + 1;
ins = this.inserting[who];
string = args[2];
if (i = index("-+", string[1]))
rest = string[2..length(string)];
return ((n = tonum(rest)) || (rest == "0")) ? {ins - n, ins + n}[i] | E_INVARG;
else
if (!(j = index(string, "^") || index(string, "_")))
offset = 0;
else
offset = (j == 1) || tonum(string[1..j - 1]);
if (!offset)
return E_INVARG;
elseif (string[j] == "^")
offset = -offset;
endif
endif
rest = string[j + 1..length(string)];
if (i = rest in {".", "$"})
return offset + {ins, last}[i];
elseif (!(n = tonum(rest)))
return E_INVARG;
else
return (offset + (j && (string[j] == "^"))) + n;
endif
endif
.
#49:36
recognized_flags = (length(args) >= 2) ? args[2] | "gcr";
null_subst_msg = (length(args) >= 3) ? args[3] | "Null substitution?";
cmd = args[1];
if (!cmd)
return "/xxx/yyy[/[g][c][r]] [<range>] expected..";
endif
bchar = cmd[1];
cmd = cmd[2..length(cmd)];
fromstr = cmd[1..(b2 = index(cmd + bchar, bchar, 1)) - 1];
cmd = cmd[b2 + 1..length(cmd)];
tostr = cmd[1..(b2 = index(cmd + bchar, bchar, 1)) - 1];
cmd = cmd[b2 + 1..length(cmd)];
cmdlen = length(cmd);
b2 = 0;
while (((b2 = b2 + 1) <= cmdlen) && index(recognized_flags, cmd[b2]))
endwhile
return ((fromstr == "") && (tostr == "")) ? null_subst_msg | {fromstr, tostr, cmd[1..b2 - 1], cmd[b2..cmdlen]};
.
#49: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:get_option($edit_options, "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
.
#49:38
"The correct way to move someone into the editor.";
if (((loc = (who_obj = args[1]).location) != this) && (caller == this))
this.invoke_task = task_id();
"make sure accept gets called... argh!";
who_obj.wizard && this:accept(who_obj);
who_obj:moveto(this);
if (who_obj.location == this)
fork (0)
"...forked, just in case loc:announce is broken...";
if (valid(loc) && (msg = this:depart_msg()))
loc:announce($string_utils:pronoun_sub(msg));
endif
endfork
else
who_obj:tell("For some reason, I can't move you.   (?)");
this:exitfunc(who_obj);
endif
this.invoke_task = 0;
endif
return who_obj.location == this;
.
#49:39
"WIZARDLY";
who_obj = args[1];
from = args[2];
if ($object_utils:isa(from, $generic_editor))
"... never put an editor in .original, ...";
if (w = who_obj in from.active)
from = from.original[w];
else
from = #-1;
endif
endif
if (caller != this)
return E_PERM;
elseif (who = (who_obj = args[1]) 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
.
#49: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
.
#49: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
.
#49: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
.
#49:43
return (is_player(who_obj = args[1]) && (who_obj.wizard || pass(@args))) && this:new_session(who_obj, who_obj.location);
.
#49: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
.
#49: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);
.
#49: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 (!$perm_utils:controls(player, this))
player:tell("Only the owner of the editor can do a ", verb, ".");
return;
endif
if (!prepstr)
player:tell("Trashing all sessions.");
this:kill_all_sessions();
elseif (prepstr != "at")
player:tell("Usage:  ", verb, " ", dobjstr, " [at [mon day|weekday]]");
else
p = prepstr in args;
if (t = $time_utils:from_day(iobjstr, -1))
elseif (t = $time_utils:from_month(args[p + 1], -1))
if (length(args) > (p + 1))
if (!(n = tonum(args[p + 2])))
player:tell(args[p + 1], " WHAT?");
return;
endif
t = t + ((n - 1) * 86400);
endif
else
player:tell("couldn't parse date");
return;
endif
for i in [-length(this.active)..-1]
if (this.times[-i] < t)
player:tell(this.active[-i].name, "(", this.active[-i], ") ", ctime(this.times[-i]));
this:kill_session(-i);
endif
endfor
endif
.
#49:47
if (!$perm_utils:controls(player, this))
player:tell(E_PERM);
return;
endif
if (i = index(dobjstr, "="))
default = dobjstr[i + 1..length(dobjstr)];
prop = dobjstr[1..i - 1];
if (argstr[1 + index(argstr, "=")] == "\"")
elseif (default[1] == "#")
default = toobj(default);
elseif (index("0123456789", default[1]))
default = tonum(default);
elseif (default == "{}")
default = {};
endif
else
default = 0;
prop = dobjstr;
endif
if (typeof(result = this:set_stateprops(prop, default)) == ERR)
player:tell((result == E_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
.
#49: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
.
#49:49
if ($perm_utils:controls(caller_perms(), this))
pass(@args);
this.free_entry = 1;
this:kill_all_sessions();
endif
.
#49:50
if (caller_perms().wizard)
pass();
this.free_entry = 1;
this:kill_all_sessions();
this.help = $editor_help;
endif
.
#49: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))
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;
.
#49: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")
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};
.
#49: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
.
#49:54
who = {@args, player}[2];
objstr = args[1];
origin = this;
while ((where = player in origin.active) && ($recycler:valid(origin = origin.original[where]) && (origin != this)))
if (!$object_utils:isa(origin, $generic_editor))
return origin:match_object(args[1], who);
endif
endwhile
return who:my_match_object(objstr, #-1);
.
#49: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));
return $string_utils:pronoun_sub(this.who_location_msg, who, this, where);
.
#49:56
return this.(verb);
.
#49:57
return;
.
#49: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.";
if (length(args) < 2)
width = 2 + player:linelen();
prefix = "";
else
width = args[2] + 1;
prefix = {@args, ""}[3];
endif
if (width < (3 + length(prefix)))
return E_INVARG;
endif
string = ("$" + args[1]) + " $";
len = length(string);
if (len <= width)
last = len - 1;
next = len;
else
last = rindex(string[1..width], " ");
if (last < ((width + 1) / 2))
last = width + index(string[width + 1..len], " ");
endif
next = last;
while (string[next = next + 1] == " ")
endwhile
endif
while (string[last = last - 1] == " ")
endwhile
ret = {string[2..last]};
width = width - length(prefix);
minlast = (width + 1) / 2;
while (next < len)
string = "$" + string[next..len];
len = (len - next) + 2;
if (len <= width)
last = len - 1;
next = len;
else
last = rindex(string[1..width], " ");
if (last < minlast)
last = width + index(string[width + 1..len], " ");
endif
next = last;
while (string[next = next + 1] == " ")
endwhile
endif
while (string[last = last - 1] == " ")
endwhile
if (last > 1)
ret = {@ret, prefix + string[2..last]};
endif
endwhile
return ret;
.
#49: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[1];
args = args[2];
v = 1;
vmax = min(length(verb), 5);
while ((v <= vmax) && (verb[v] == "subst"[v]))
v = v + 1;
endwhile
argstr = $code_utils:argstr(verb, args);
if ((v > 1) && ((v <= length(verb)) && (((vl = verb[v]) < "A") || (vl > "Z"))))
argstr = (verb[v..length(verb)] + (argstr && " ")) + argstr;
this:subst();
return 1;
elseif ("/" == verb[1])
argstr = (verb + (argstr && " ")) + argstr;
this:find();
return 1;
else
return 0;
endif
.
#49:60
return $failed_match;
.
#49:61
":get_room([player])  => correct room to match in on invocation.";
who = {@args, player}[1];
if (who.location != this)
return who.location;
else
origin = this;
while ((where = player in origin.active) && (valid(origin = origin.original[where]) && (origin != this)))
if (!$object_utils:isa(origin, $generic_editor))
return origin;
endif
endwhile
return this;
endif
.
#49: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 = args[1];
text = args[2];
upload = args[3];
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
.
#49: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
.
#49: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("--------------------------");
.
#49:65
if (this:exits())
return pass(@args);
endif
.
#49:66
return $code_utils:verb_or_property(player, verb) || this.(verb);
.
#49:67
"Copied from Generic Editor (#51):join_lines by Hacker (#38) Wed Sep  6 16:41:09 1995 CDT";
if (!(fuckup = this:ok(who = args[1])))
return fuckup;
elseif ((from = args[2]) >= (to = args[3]))
return 0;
else
nline = "";
for line in ((text = this.texts[who])[from..to])
if (!(english = args[4]))
nline = nline + line;
else
len = length(line) + 1;
while ((len = len - 1) && (line[len] == " "))
endwhile
if (len > 0)
nline = (nline + line) + (index(".:", line[len]) ? "  " | " ");
endif
endif
endfor
this.texts[who] = {@text[1..from - 1], nline, @text[to + 1..length(text)]};
if ((insert = this.inserting[who]) > from)
this.inserting[who] = (insert <= to) ? from + 1 | ((insert - to) + from);
endif
if (!this.changes[who])
this.changes[who] = 1;
this.times[who] = time();
endif
return to - from;
endif
.
#49:68
"Usage: subst_regexp(STR text, STR from string, STR to string, INT case)";
{text, from, to, case} = args;
if (m = match(text, from, case))
{start, end} = m[1..2];
text[start..end] = substitute(to, m);
return text;
else
return m;
endif
"Copied from Domain (#8111):subst_regexp by Mooshie (#106469) Mon Jan  5 19:27:26 1998 PST";
.
#50:0
":match_environment(STR string[, LIST object])";
"Find something in the given object list which answers to the given string.  The object list defaults to the player's environment.";
"Handles references to particular members of a set of objects (<<5th element>>, <<5.element>>, <<last element>>), matching within a particular object's contents (<<fred's tampon>>), or a combination of both (<<fred's last tampon>>).";
{string, ?olist = 0} = args;
here = player.location;
if (typeof(olist) == LIST)
"...object list sent...";
elseif (typeof(olist = `player:environment() ! E_VERBNF') == LIST)
"...player's match pool...";
else
return $failed_match;
endif
"start with a failed match";
object = $failed_match;
"check for basic referentials";
if (!string)
return $nothing;
elseif (string == "me")
return player;
elseif (string == "here")
return here;
endif
"check for literal objects";
if ((object = this:literal_object(string)) != $failed_match)
if (!valid(object))
return object;
elseif (($wiz_utils:is_builder(player) || (object.owner == player)) || (object in olist))
return object;
endif
return $failed_match;
endif
"check for a direct match on something in the environment";
if (valid(object = this:match(string, olist)))
return object;
endif
"check for possessives";
if (parsed = this:parse_possessive_reference(string))
{whostr, objstr} = parsed;
if (valid(whose = this:(verb)(whostr, olist)))
return this:match_object(objstr, whose:contents());
endif
return whose;
endif
"go with whatever this:match found";
return object;
.
#50:1
":match_nth(STR string, LIST objects, NUM N)";
"Return the Nth object in OBJECTS matching STRING.";
{spec, objects, pos} = args;
if (!objects)
return $failed_match;
endif
if (pos == $maxint)
i = length(objects);
while (i)
if (`objects[i]:matches(spec) ! E_INVIND, E_VERBNF => 0')
return objects[i];
endif
i = i - 1;
endwhile
else
for o in (objects)
z = `o:matches(spec) ! E_INVIND, E_VERBNF => 0';
if (z && (!(pos = pos - 1)))
return o;
endif
endfor
endif
return $failed_match;
.
#50:2
":match_verb(STR verbname, OBJ object, LIST args)";
"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 and 1 is returned.  Otherwise, 0 is returned.";
object = args[2];
verbname = args[1];
where = $object_utils:has_callable_verb(object, verbname);
if (!where)
return 0;
endif
definer = where[1];
vargs = verb_args(definer, verbname);
if (vargs == {"this", "none", "this"})
return 0;
endif
plist = {"any", prepstr ? $code_utils:full_prep(prepstr) | "none"};
dlist = dobjstr ? {"any"} | {"none", "any"};
ilist = iobjstr ? {"any"} | {"none", "any"};
if (((vargs[2] in plist) && ((vargs[1] in dlist) || ((vargs[1] == "this") && (dobj == object)))) && ((vargs[3] in ilist) || ((vargs[3] == "this") && (iobj == object))))
set_task_perms(caller_perms());
object:(verbname)(@args[3]);
return 1;
endif
.
#50:3
":match_list(string, object_list) -> List of all matches.";
what = args[1];
if (!what)
return {};
endif
where = args[2];
r = {};
for v in (where)
if ((!(v in r)) && v:matches(what))
r = listappend(r, v);
endif
endfor
return r;
.
#50:4
":parse_ordref(string)";
"Parses strings referring to an 'nth' object.";
"-> {NUM n, STR object} Where 'n' is the number the ordinal represents, and 'object' is the rest of the string.";
"-> {$maxint, STR object} if 'last foo' is given.";
"-> 0 If the given string is not an ordinal reference.";
"  Examples:";
":parse_ordref(\"second broadsword\") -> {2, \"broadsword\"}";
":parse_ordref(\"second\") -> 0";
"  Note that there must be more to the string than the ordinal alone.";
string = args[1];
if (m = match(string, "^last +%(.+%)$"))
return {$maxint, substitute("%1", m)};
elseif (m = match(string, "^%([1-9][0-9]*%)%.%(.+%)$"))
return {tonum(substitute("%1", m)), substitute("%2", m)};
elseif (m = match(string, ("^" + 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
.
#50:5
"Copied from matching utilities (#52):parse_possessive(old) by Hacker (#38) Fri Dec  8 15:30:43 1995 CST";
":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
.
#50:6
":match(string, object-list[, RETURN-TYPE])";
"Find an object from the given object-list matching the given string.";
"If RETURN-TYPE is OBJ, return standard match return values.  If it is LIST, return a list of all matches.";
s = args[1];
x = p = {};
l = args[2];
for o in (l)
r = o:matches(s);
if (!r)
elseif (r < 0)
p = setadd(p, o);
else
x = setadd(x, o);
endif
endfor
if (args[3] == LIST)
return {@p, @x};
endif
if (x)
return (length(x) == 1) ? x[1] | $ambiguous_match;
elseif (p)
return (length(p) == 1) ? p[1] | $ambiguous_match;
elseif (parsed = this:parse_ordinal_reference(s))
return this:match_nth(parsed[2], l, parsed[1]);
elseif (m = match(s, "^%(a%|an%|the%) +%(.+%)$"))
"...try it without an article...";
return this:match(substitute("%2", m), l);
else
return $failed_match;
endif
.
#50:7
":find_verb(STR verbname[, OBJ player])";
"Find all definers of the given verb in player's environment.";
who = (length(args) == 2) ? args[2] | player;
if (!$recycler:valid(who))
return E_INVIND;
endif
objects = {who, @who:contents()};
where = who.location;
if (valid(where))
objects = {@objects, where, where:contents()};
endif
if (typeof(fos = who:features()) == LIST)
objects = {@objects, @fos};
endif
found = {};
verbname = args[1];
for object in (objects)
if (where = $object_utils:has_verb(object, verbname))
found = setadd(found, where[1]);
endif
endfor
return found;
.
#50:8
"Copied from string utilities (#20):match_player by Hacker (#38) Fri Feb 25 05:51:45 1994 CST";
"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 = this: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;
.
#50:9
":match_room(string[, who])";
"Find a room matching string in proximity to 'who' (defaults to player).";
if (!(string = args[1]))
return $nothing;
endif
who = ((argc = length(args)) > 1) ? args[2] | player;
if (string == "home")
return who.home;
endif
if (string == "here")
return who.location;
elseif (valid(lit = this:literal_object(string)) && $object_utils:isa(lit, $room))
return lit;
elseif (valid(g = $rpg:match_global_loc(string)))
return g;
elseif (valid(zone = who:zone()))
"...the zone code...";
return zone:match_room(string);
else
return $failed_match;
endif
.
#50:10
"Usage: room_match_failed(room, string)";
"Prints a message if string does not match room.  Generally used after room is derived from a :match_room(string).";
match_result = args[1];
string = args[2];
tell = $perm_utils:controls(caller_perms(), player) ? "notify" | "tell";
if ((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 place.");
elseif (match_result == $failed_match)
player:(tell)(tostr("There is either no place named \"", string, "\", or you can't get there from where you are."));
elseif (match_result == $ambiguous_match)
player:(tell)(tostr("There is more than one place named \"", string, "\"."));
elseif (!valid(match_result))
player:(tell)(tostr(match_result, " does not exist."));
else
return 0;
endif
return 1;
.
#50:11
"Usage: object_match_failed(object, string[, ambigs[, where_string]])";
"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.";
"where_string should be a string such as 'there' or 'in there' or 'under the seat'.  It defaults to 'here'.";
match_result = args[1];
string = args[2];
tell = $perm_utils:controls(caller_perms(), player) ? "notify" | "tell";
there = args[4] || "around here";
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("You don't perceive ", $english:indef_art(string), " \"", string, "\" ", there, "."));
endif
return !valid(match_result);
elseif (match_result == $nothing)
player:(tell)("You must give the name of some object.");
elseif (match_result == $failed_match)
player:(tell)(tostr("You don't perceive ", $english:indef_art(string), " \"", string, "\" ", there, "."));
elseif (match_result == $ambiguous_match)
ambigs = args[3];
if (typeof(ambigs) != LIST)
player:(tell)(tostr("You'll have to be more specific as to which \"", string, "\" you mean."));
return 1;
endif
ambigs = $match_utils:match_list(string, ambigs);
ambigs = $list_utils:map_verb(ambigs, "name");
if (length($list_utils:remove_duplicates(ambigs)) == 1)
player:(tell)(tostr("You'll have to be more specific as to which \"", string, "\" you mean.  Try using \"first ", string, "\", \"second ", string, "\", etc."));
else
player:(tell)(tostr("You'll have to be more specific as to 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;
.
#50:12
":player_match_failed(result,string)";
"  is exactly like :object_match_failed(result,string)";
"  except that its messages are more suitable for player searches.";
":player_match_result(results,strings)";
"  handles a list of results, also presumably from $string_utils:match_player(strings), printing messages to player for *each* of the nonmatching strings.  It returns a list, an overall result (true if some string didn't match --- just like player_match_failed), followed by the list players that matched.";
"";
"An optional 3rd arg gives an identifying string to prefix to each of the nasty messages.";
if (valid(player))
tell = $perm_utils:controls(caller_perms(), player) ? "notify" | "tell";
plyr = player;
else
tell = "notify";
plyr = $login;
endif
"...";
match_results = args[1];
strings = args[2];
cmdid = {@args, ""}[3] || "";
pmf = verb == "player_match_failed";
if (typeof(match_results) == OBJ)
match_results = {match_results};
strings = {strings};
endif
pset = {};
bombed = 0;
for i in [1..length(match_results)]
if (valid(result = match_results[i]))
pset = setadd(pset, match_results[i]);
elseif (result == $nothing)
"... player_match_result quietly skips over blank strings";
if (pmf)
plyr:(tell)("You must give the name of some player.");
bombed = 1;
endif
elseif (result == $failed_match)
plyr:(tell)(tostr(cmdid, "\"", strings[i], "\" is not the name of any player."));
bombed = 1;
elseif (result == $ambiguous_match)
lst = $player_db:find_all(strings[i]);
plyr:(tell)(tostr(cmdid, "\"", strings[i], "\" could refer to ", (length(lst) > 20) ? tostr("any of ", length(lst), " players") | $string_utils:english_list($list_utils:map_arg(2, $string_utils, "pronoun_sub", "%n (%#)", lst), "no one", " or "), "."));
bombed = 1;
else
plyr:(tell)(tostr(result, " does not exist."));
bombed = 1;
endif
endfor
return pmf ? bombed | {bombed, @pset};
.
#50:13
"Copied from string utilities (#20):match_player_or_object by Hacker (#38) Sun Aug 28 03:23:25 1994 CDT";
"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 = this: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;
.
#50:14
":literal_object(str)";
"Return an object matching the literal [#$~*]xxx reference given.";
{string} = args;
if (!string)
return $failed_match;
elseif ((string[1] == "#") && (E_TYPE != (object = $code_utils:toobj(string))))
return object;
elseif (((string[1] == "*") && (string != "*")) && valid(object = $mail_agent:match_recipient(string)))
return object;
elseif ((string[1] == "$") && ($object_utils:has_property(#0, pn = string[2..$]) && (typeof(object = #0.(pn)) == OBJ)))
return object;
elseif ((string[1] == "~") && valid(object = this:match_player(string[2..$])))
return object;
else
return $failed_match;
endif
.
#50:15
"Copied from matching utilities (#52):parse_possessive_ref by Hacker (#38) Fri Dec  8 15:30:18 1995 CST";
":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\"}";
s = args[1];
if (m = match(s, "^my$%|^my +%(.+%)?"))
return {"me", substitute("%1", m)};
endif
r = l = 0;
if ((i = index(s, "'")) && ((r = s[i + 1] == "s") || (l = s[i - 1] == "s")))
if (r)
return {s[1..i - 1], $string_utils:triml(s[i + 2..length(s)])};
else
return {s[1..i - 1], $string_utils:triml(s[i + 1..length(s)])};
endif
endif
return 0;
.
#51:0
object = args[1];
prop = args[2];
if (prop in $code_utils.builtin_props)
return valid(object);
else
return !(!property_info(object, prop));
endif
.
#51:1
what = args[1];
if (what.owner != caller_perms())
set_task_perms(caller_perms());
endif
props = properties(what) || {};
while (valid(what = parent(what)))
props = {@properties(what) || {}, @props};
endwhile
return props;
.
#51: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 = args[1];
verbname = args[2];
while (E_VERBNF == (vi = verb_info(object, verbname)))
object = parent(object);
endwhile
return vi ? {object} | 0;
.
#51: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 = args[1];
verbname = args[2];
while (valid(object))
if (index(verb_info(object, verbname)[2], "x") && verb_code(object, verbname))
return {object};
endif
object = parent(object);
endwhile
return 0;
.
#51:4
what = args[1];
if (what.owner != caller_perms())
set_task_perms(caller_perms());
endif
verbs = {};
while (valid(what))
verbs = {@verbs(what) || {}, @verbs};
what = parent(what);
endwhile
return verbs;
.
#51:5
":match_verb(OBJ object, STR verb)";
"Find out if an object has a given verb, and some information about it.";
"Returns {OBJ location, STR verb} if matched, 0 if not.";
"Location is the object on which it is actually defined, verb is a name";
"for the verb which can subsequently be used in verb_info (i.e., no";
"asterisks).";
verbname = strsub(args[2], "*", "");
object = args[1];
while (E_VERBNF == (info = verb_info(object, verbname)))
object = parent(object);
endwhile
return info ? {object, verbname} | 0;
.
#51:6
":isa(child, parent)";
"=> True if child is a child of parent.";
what = args[1];
targ = args[2];
while (valid(what))
if (what == targ)
return 1;
endif
what = parent(what);
endwhile
return 0;
.
#51:7
"Usage:  ancestors(object[, object...])";
"Return a list of all ancestors of the object(s) in args, with no duplicates.";
"If called with a single object, the result will be in order ascending up the inheritance hierarchy.  If called with multiple objects, it probably won't.";
ret = {};
for o in (args)
what = o;
while (valid(what = parent(what)))
ret = setadd(ret, what);
endwhile
endfor
return ret;
.
#51:8
what = args[1];
kids = children(what);
result = {};
for x in (kids)
result = {@result, @this:descendants(x)};
endfor
return {@kids, @result};
.
#51:9
set_task_perms(caller_perms());
what = args[1];
kids = children(what);
result = {};
for x in (kids)
result = {@result, @this:descendants_suspended(x)};
endfor
$command_utils:suspend_if_needed(0);
return {@kids, @result};
.
#51:10
r = {what = args[1]};
for k in (children(what))
r = {@r, @this:(verb)(k)};
endfor
return r;
.
#51:11
":branches(object) => list of all descendants of object which have children.";
if (kids = children(object = args[1]))
s = {object};
for k in (kids)
s = {@s, @this:branches(k)};
endfor
return s;
else
return {};
endif
.
#51:12
":branches_suspended(object) => descendants of object having children.";
"this version calls suspend(0) as needed";
set_task_perms(caller_perms());
if (kids = children(object = args[1]))
s = {object};
for k in (kids)
$command_utils:suspend_if_needed(0);
s = {@s, @this:branches_suspended(k)};
endfor
return s;
else
return {};
endif
.
#51:13
":leaves(object) => list of all childless descendents of object";
s = {};
for k in (children(args[1]))
s = {@s, @this:leaves(k)};
endfor
return s || {args[1]};
.
#51:14
":leaves_suspended(object) => list of all childless descendents of object";
"this versions calls suspend(0) as needed";
set_task_perms(caller_perms());
s = {};
for k in (children(args[1]))
$command_utils:suspend_if_needed(0);
s = {@s, @this:leaves_suspended(k)};
endfor
return s || {args[1]};
.
#51:15
"$object_utils:contains(obj1, obj2) -- does obj1 contain obj2?";
"";
"Return true iff obj2 is under obj1 in the containment hierarchy; that is, if obj1 is obj2's location, or its location's location, or ...";
loc = args[1];
what = args[2];
while (valid(what))
what = what.location;
if (what == loc)
return valid(loc);
return 1;
endif
endwhile
return 0;
.
#51:16
"all_contents(object)";
"Return a list of all objects contained (at some level) by object.";
res = {};
for y in (args[1].contents)
res = {@res, y, @$object_utils:all_contents(y)};
endfor
return res;
.
#51:17
"findable_properties(object)";
"Return a list of properties on those members of object's ancestor list that are readable or are owned by the caller (or all properties if the caller is a wizard).";
what = args[1];
props = {};
who = caller_perms();
while (what != $nothing)
if ((what.r || (who == what.owner)) || who.wizard)
props = {@properties(what), @props};
endif
what = parent(what);
endwhile
return props;
.
#51:18
"owned_properties(what[, who])";
"Return a list of all properties on WHAT owned by WHO.";
"Only wizardly verbs can specify WHO; mortal verbs can only search for properties owned by their own owners.  For more information, talk to Gary_Severn.";
what = anc = args[1];
who = ((c = caller_perms()).wizard && (length(args) > 1)) ? args[2] | c;
props = {};
while (anc != $nothing)
for k in (properties(anc))
if (property_info(what, k)[1] == who)
props = listappend(props, k);
endif
endfor
anc = parent(anc);
endwhile
return props;
.
#51:19
":property_conflicts(object,newparent)";
"Looks for propertyname conflicts that would keep chparent(object,newparent)";
"  from working.";
"Returns a list of elements of the form {<propname>, @<objectlist>}";
"where <objectlist> is list of descendents of object defining <propname>.";
if (!valid(object = args[1]))
return E_INVARG;
elseif (!valid(newparent = args[2]))
return (newparent == #-1) ? {} | E_INVARG;
elseif (!($perm_utils:controls(caller_perms(), object) && (newparent.f || $perm_utils:controls(caller_perms(), newparent))))
"... if you couldn't chparent anyway, you don't need to know...";
return E_PERM;
endif
"... properties existing on newparent";
"... cannot be present on object or any descendent...";
props = conflicts = {};
for o in ({object, @$object_utils:descendents_suspended(object)})
for p in (properties(o))
if (property_info(newparent, p))
if (i = p in props)
conflicts[i] = {@conflicts[i], o};
else
props = {@props, p};
conflicts = {@conflicts, {p, o}};
endif
endif
$command_utils:suspend_if_needed(0);
endfor
$command_utils:suspend_if_needed(0);
endfor
return conflicts;
.
#51:20
":descendants_with_property_suspended(object,property)";
" => list of descendants of object on which property is defined.";
"calls suspend(0) as needed";
object = args[1];
if ((caller == this) || (object.w || $perm_utils:controls(caller_perms(), object)))
$command_utils:suspend_if_needed(0);
if (property_info(@args))
return {object};
endif
r = {};
prop = args[2];
for c in (children(object))
r = {@r, @this:descendants_with_property_suspended(c, prop)};
endfor
return r;
else
return E_PERM;
endif
.
#51:21
"Usage:  locations(object)";
"Return a listing of the location hierarchy above object.";
ret = {};
what = args[1];
while (valid(what = what.location))
ret = {@ret, what};
endwhile
return ret;
.
#51:22
"Copied from object utilities (#3669):all_properties by Haakon (#2) Mon Dec 28 14:24:36 1992 PST";
what = args[1];
if (what.owner != caller_perms())
set_task_perms(caller_perms());
endif
props = properties(what) || {};
while (valid(what = parent(what)))
props = {@properties(what) || {}, @props};
$command_utils:suspend_if_needed(0);
endwhile
return props;
.
#51:23
":connected(object) => true iff object is a connected player.";
"equivalent to (object in connected_players()) perhaps with less server overhead";
return 1 + connected_seconds(@args);
.
#51:24
":has_message(obj, str)";
"Only properties ending in _msg are considered messages in this routine.  If you want some other property to be considered a message, you must define a :has_message verb on your object.  See the $root instance for details.";
m = args[2];
if (!match(m, "_msg$"))
m = m + "_msg";
endif
return (property_info(args[1], m) != E_PROPNF) ? m | 0;
.
#51:25
"Copied from Janus' utils (#4045):isa by Janus (#3597) Thu Oct 15 08:43:48 1998 CDT";
":isa(x,y) == valid(x) && (x in y || $set_utils:inter(y, :ancestors(x)))";
{what, targ, ?silent = 1} = args;
t = typeof(targ);
if (t == LIST)
if (!targ)
return 0;
endif
elseif (t == OBJ)
targ = {targ};
else
return silent ? E_TYPE | raise(E_TYPE);
endif
while (valid(what))
if (what in targ)
return {what};
endif
what = parent(what);
endwhile
return 0;
.
#52:0
this.input_string = args[1];
this.input_length = length(args[1]);
this.input_index = 1;
.
#52:1
string = this.input_string;
len = this.input_length;
i = this.input_index;
while ((i <= len) && (string[i] == " "))
i = i + 1;
endwhile
if (i > len)
return "";
elseif ((ch = string[i]) in {"(", ")", "!", "?"})
this.input_index = i + 1;
return ch;
elseif (ch in {"&", "|"})
this.input_index = i = i + 1;
if ((i <= len) && (string[i] == ch))
this.input_index = i + 1;
endif
return ch + ch;
else
start = i;
while ((i <= len) && (!((ch = string[i]) in {"(", ")", "!", "?", "&", "|"})))
i = i + 1;
endwhile
this.input_index = i;
i = i - 1;
while (string[i] == " ")
i = i - 1;
endwhile
return this:canonicalize_spaces(string[start..i]);
endif
.
#52:2
name = args[1];
while (index(name, "  "))
name = strsub(name, "  ", " ");
endwhile
return name;
.
#52:3
this:init_scanner(args[1]);
this.player = args[2];
return this:parse_E();
"";
"Grammar for key expressions:";
"";
"    E ::= A       ";
"       |  E || A  ";
"       |  E && A  ";
"    A ::= ( E )   ";
"       |  ! A     ";
"       |  object  ";
"       |  ? object  ";
.
#52:4
exp = this:parse_A();
if (typeof(exp) != STR)
while ((token = this:scan_token()) in {"&&", "||"})
rhs = this:parse_A();
if (typeof(rhs) == STR)
return STR;
endif
exp = {token, exp, rhs};
endwhile
endif
return exp;
.
#52: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
.
#52:6
key = args[1];
who = args[2];
type = typeof(key);
if (!(type in {LIST, OBJ}))
return 1;
elseif (typeof(key) == OBJ)
return (who == key) || $object_utils:contains(who, key);
endif
op = key[1];
if (op == "!")
return !this:eval_key(key[2], who);
elseif (op == "?")
return key[2]:is_unlocked_for(who);
elseif (op == "&&")
return this:eval_key(key[2], who) && this:eval_key(key[3], who);
elseif (op == "||")
return this:eval_key(key[2], who) || this:eval_key(key[3], who);
else
return 1 / 0;
endif
.
#52:7
token = args[1];
if (token == "me")
return this.player;
elseif (token == "here")
if (valid(this.player.location))
return this.player.location;
else
return ("'here' has no meaning where " + this.player.name) + " is";
endif
else
what = this.player:my_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
.
#52:8
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
return 1 / 0;
endif
endif
.
#52:9
set_task_perms($no_one);
key = args[1];
who = args[2];
type = typeof(key);
if (!(type in {LIST, OBJ}))
return 1;
elseif (typeof(key) == OBJ)
return (who == key) || $object_utils:contains(who, key);
endif
op = key[1];
if (op == "!")
return !this:eval_key(key[2], who);
elseif (op == "?")
return key[2]:is_unlocked_for(who);
elseif (op == "&&")
return this:eval_key(key[2], who) && this:eval_key(key[3], who);
elseif (op == "||")
return this:eval_key(key[2], who) || this:eval_key(key[3], who);
elseif (op == ".")
if ($object_utils:has_property(who, key[2]) && who.(key[2]))
return 1;
else
for thing in ($object_utils:all_contents(who))
if ($object_utils:has_property(thing, key[2]) && thing.(key[2]))
return 1;
endif
endfor
endif
return 0;
elseif (op == ":")
if ($object_utils:has_verb(who, key[2]) && who:(key[2])())
return 1;
else
for thing in ($object_utils:all_contents(who))
if ($object_utils:has_verb(thing, key[2]) && thing:(key[2])())
return 1;
endif
endfor
endif
return 0;
else
return 1 / 0;
endif
.
#52: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
.
#53:0
result = this:do_burn();
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
.
#53:1
return (msg = this.(verb)) ? $string_utils:pronoun_sub(msg) | "";
.
#53:2
c = callers();
while (c && c[1][3].wizard)
c = listdelete(c, 1);
endwhile
if (c)
who = c[1][3];
else
who = player;
endif
if ((this != $letter) && ($perm_utils:controls(who, this) || this:is_readable_by(who)))
fork (0)
$recycler:_recycle(this);
endfork
return 1;
else
return E_PERM;
endif
.
#54:0
":make(n[,elt]) => a list of n elements, each of which == elt. elt defaults to 0.";
if ((n = args[1]) < 0)
return E_INVARG;
endif
ret = {};
build = {elt = {@args, 0}[2]};
while (1)
if (n % 2)
ret = {@ret, @build};
endif
if (n = n / 2)
build = {@build, @build};
else
return ret;
endif
endwhile
.
#54:1
":range([m,]n) => {m,m+1,...,n}";
if (listdelete(args, 1))
else
args = {1, @args};
endif
ret = {};
for k in [args[1]..args[2]]
ret = {@ret, k};
endfor
return ret;
.
#54:2
set_task_perms(caller_perms());
prop = args[2];
if ((len = length(objs = args[1])) > 50)
return {@this:map_prop(objs[1..len / 2], prop), @this:map_prop(objs[(len / 2) + 1..len], prop)};
endif
strs = {};
for foo in (objs)
strs = {@strs, foo.(prop)};
endfor
return strs;
.
#54:3
set_task_perms(caller_perms());
if ((len = length(objs = args[1])) > 50)
return {@this:map_verb(@listset(args, objs[1..len / 2], 1)), @this:map_verb(@listset(args, objs[(len / 2) + 1..len], 1))};
endif
vrb = args[2];
rest = args[3..length(args)];
strs = {};
for o in (objs)
strs = {@strs, o:(vrb)(@rest)};
endfor
return strs;
.
#54: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 = args[2];
verb = args[3];
rest = args[4..length(args)];
else
object = n;
n = 1;
verb = args[2];
rest = args[3..length(args)];
endif
results = {};
for a in (rest[n])
results = listappend(results, object:(verb)(@listset(rest, a, n)));
endfor
return results;
.
#54: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());
if (!((builtin = args[2]) in {"tostr", "tonum", "toobj", "typeof", "length", "random", "ctime", "valid", "parent", "children", "properties", "verbs", "is_player", "idle_seconds", "connected_seconds", "connection_name"}))
return E_INVARG;
endif
objs = args[1];
if (length(objs) > 100)
return {@this:map_builtin(objs[1..l = length(objs) / 2], builtin), @this:map_builtin(objs[l + 1..length(objs)], builtin)};
endif
strs = {};
for foo in (objs)
strs = {@strs, eval(tostr("return ", builtin, "(", $string_utils:from_value(foo, 1, -1), ");"))[2]};
endfor
return strs;
.
#54: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 = args[1];
key = args[2];
if ((r = length(lst)) < 25)
for l in [1..r]
if (lst[l] > key)
return l;
endif
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
endwhile
return l;
endif
.
#54: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;
.
#54: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
.
#54:9
":setremove_all(set,elt) => set with *all* occurences of elt removed";
set = args[1];
what = args[2];
while (w = what in set)
set = listdelete(set, w);
endwhile
return set;
.
#54:10
"append({a,b,c},{d,e},{},{f,g,h},...) =>  {a,b,c,d,e,f,g,h}";
if ((n = length(args)) > 50)
return {@this:append(@args[1..n / 2]), @this:append(@args[(n / 2) + 1..n])};
endif
l = {};
for a in (args)
l = {@l, @a};
endfor
return l;
.
#54:11
"reverse(list) => reversed list";
return this:_reverse(@args[1]);
.
#54:12
":_reverse(@list) => reversed list";
if ((n = length(args)) > 50)
return {@this:_reverse(@args[(n / 2) + 1..n]), @this:_reverse(@args[1..n / 2])};
endif
l = {};
for a in (args)
l = listinsert(l, a);
endfor
return l;
.
#54: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
.
#54: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;
.
#54: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) != NUM)
return E_ARGS;
endif
lst = args[2];
unsorted_keys = (use_sorted_lst = length(args) >= 3) ? args[3] | lst;
sorted_lst = sorted_keys = {};
for e in (unsorted_keys)
l = this:find_insert(sorted_keys, e);
sorted_keys[l..l - 1] = {e};
if (use_sorted_lst)
sorted_lst[l..l - 1] = {lst[length(sorted_keys)]};
endif
$command_utils:suspend_if_needed(interval);
endfor
return sorted_lst || sorted_keys;
.
#54: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\"}}";
slice = {};
ind = {@args, 1}[2];
if (typeof(ind) == LIST)
for elt in (args[1])
s = {elt[ind[1]]};
for i in (listdelete(ind, 1))
s = {@s, elt[i]};
endfor
slice = {@slice, s};
endfor
else
for elt in (args[1])
slice = {@slice, elt[ind]};
endfor
endif
return slice;
.
#54: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 = args[1];
indx = {@args, 1}[3];
for t in (args[2])
if (t[indx] == target)
"... do this test first since it's the most likely to fail; this needs -d";
if ((typeof(t) == LIST) && (length(t) >= indx))
return t;
endif
endif
endfor
return {};
.
#54: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 = args[1];
indx = {@args, 1}[3];
i = 1;
for lsti in (args[2])
if (lsti[indx] == target)
"... do this test first since it's the most likely to fail; this needs -d";
if ((typeof(lsti) == LIST) && (length(lsti) >= indx))
return i;
endif
endif
i = i + 1;
endfor
return 0;
.
#54:19
"iassoc(target,list[,index]) returns the index of the first element of `list' whose own index-th element is target.  Index defaults to 1.";
"returns 0 if no such element is found.";
"suspends as needed.";
set_task_perms(caller_perms());
target = args[1];
indx = {@args, 1}[3];
i = 1;
for lsti in (args[2])
if (lsti[indx] == target)
"... do this test first since it's the most likely to fail; this needs -d";
if ((typeof(lsti) == LIST) && (length(lsti) >= indx))
return i;
endif
endif
i = i + 1;
$command_utils:suspend_if_needed(1);
endfor
return 0;
.
#54: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 = args[1];
indx = (length(args) >= 3) ? args[3] | 1;
for t in (args[2])
if ((typeof(t) == LIST) && ((length(t) >= indx) && (index(t[indx], target) == 1)))
return t;
endif
endfor
return {};
.
#54: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 = args[1];
indx = (length(args) >= 3) ? args[3] | 1;
for i in [1..length(lst = args[2])]
if ((typeof(lsti = lst[i]) == LIST) && ((length(lsti) >= indx) && (index(lsti[indx], target) == 1)))
return i;
endif
endfor
return 0;
.
#54: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 = args[1];
indx = {@args, 1}[3];
if ((r = length(lst = args[2])) < 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
.
#54:23
":sort_alist(alist[,n]) sorts a list of tuples by n-th (1st) element.";
if ((alist_length = length(alist = args[1])) < 25)
"use insertion sort on short lists";
return this:sort(alist, this:slice(@args));
endif
sort_on = {@args, 1}[2];
left_index = alist_length / 2;
right_index = (alist_length + 1) / 2;
left_sublist = this:sort_alist(alist[1..left_index], sort_on);
right_sublist = this:sort_alist(alist[left_index + 1..alist_length], sort_on);
"...";
"... merge ...";
"...";
left_key = left_sublist[left_index][sort_on];
right_key = right_sublist[right_index][sort_on];
if (left_key > right_key)
merged_list = {};
else
"... alist_length >= 25 implies right_index >= 2...";
"... move right_index downward until left_key > right_key...";
r = right_index - 1;
while (left_key <= (right_key = right_sublist[r][sort_on]))
if (r = r - 1)
else
return {@left_sublist, @right_sublist};
endif
endwhile
merged_list = right_sublist[r + 1..right_index];
right_index = r;
endif
while (l = left_index - 1)
"... left_key > right_key ...";
"... move left_index downward until left_key <= right_key...";
while ((left_key = left_sublist[l][sort_on]) > right_key)
if (l = l - 1)
else
return {@right_sublist[1..right_index], @left_sublist[1..left_index], @merged_list};
endif
endwhile
merged_list[1..0] = left_sublist[l + 1..left_index];
left_index = l;
"... left_key <= right_key ...";
if (r = right_index - 1)
"... move right_index downward until left_key > right_key...";
while (left_key <= (right_key = right_sublist[r][sort_on]))
if (r = r - 1)
else
return {@left_sublist[1..left_index], @right_sublist[1..right_index], @merged_list};
endif
endwhile
merged_list[1..0] = right_sublist[r + 1..right_index];
right_index = r;
else
return {@left_sublist[1..left_index], right_sublist[1], @merged_list};
endif
endwhile
return {@right_sublist[1..right_index], left_sublist[1], @merged_list};
.
#54: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 = args[1];
if ((alist_length = length(alist = args[2])) < 10)
"insertion sort on short lists";
$command_utils:suspend_if_needed(interval);
return this:sort(alist, this:slice(@listdelete(args, 1)));
endif
"variables specially expanded for the anal-retentive";
sort_on = {@args, 1}[3];
left_index = alist_length / 2;
right_index = (alist_length + 1) / 2;
left_sublist = this:sort_alist_suspended(interval, alist[1..left_index], sort_on);
right_sublist = this:sort_alist_suspended(interval, alist[left_index + 1..alist_length], sort_on);
left_element = left_sublist[left_index];
right_element = right_sublist[right_index];
merged_list = {};
while (1)
$command_utils:suspend_if_needed(interval);
if (left_element[sort_on] > right_element[sort_on])
merged_list = {left_element, @merged_list};
if (left_index = left_index - 1)
left_element = left_sublist[left_index];
else
return {@right_sublist[1..right_index], @merged_list};
endif
else
merged_list = {right_element, @merged_list};
if (right_index = right_index - 1)
right_element = right_sublist[right_index];
else
return {@left_sublist[1..left_index], @merged_list};
endif
endif
endwhile
.
#54: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;
.
#54:26
"$list_utils:count(item, list)";
"Returns the number of occurrences of item in list.";
if (length(args) != 2)
return E_ARGS;
elseif (typeof(xlist = args[2]) != LIST)
return E_INVARG;
endif
x = args[1];
counter = 0;
for elt in (xlist)
if (x == elt)
counter = counter + 1;
endif
endfor
return counter;
.
#54: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;
.
#54:28
"$list_utils:longest(<list>)";
"$list_utils:shortest(<list>)";
"             - Returns the shortest or longest element in the list.  Elements may be either strings or lists.";
if (typeof(all = args[1]) != LIST)
return E_TYPE;
elseif (!all)
raise(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;
.
#54: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
.
#54:30
":random_element(list) -> A random element from the given list.";
"If `list' isn't a list, simply return whatever it is.";
what = args[1];
if (typeof(what) == LIST)
return what[random($)];
else
return what;
endif
.
#54:31
":exclusive_list(objlist, {include-classes}[, {exclude-classes}])";
"Returns objlist (which should be a list of objects) stripped of all objects not descended from `include_classes' or descended from `exclude_classes'.";
if (typeof(oo = args[1]) != LIST)
return E_TYPE;
endif
objects = {};
include = args[2];
exclude = {@args, {}}[3];
for o in (oo)
$command_utils:suspend_if_needed(0);
subject = child = bastard = o;
while ((valid(subject) && (!child)) && (!bastard))
"...give priority to include...";
if (subject in include)
objects = {@objects, o};
child = 1;
elseif (subject in exclude)
bastard = 1;
endif
subject = parent(subject);
endwhile
endfor
return objects;
.
#54:32
":contains_only_these_types(LIST list, @types)";
"Return true if the LIST is composed only of the given MOO types.";
{l, @types} = args;
for e in (l)
if (!(typeof(e) in types))
return 0;
endif
endfor
return 1;
.
#54:33
"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 {};
.
#54:34
":merge(LIST columns[, STR divider])";
"Places each list of strings in columns beside the other, a la a vertically split screen view.";
"Spacing between columns is determined by the divider argument.";
{columns, ?divider = ""} = args;
widths = page = {};
"cache the number of arguments";
arglen = length(columns);
"find the tallest column";
maxlen = length($list_utils:longest(columns));
"find the fattest width of each column";
for column in (columns)
widths = {@widths, length($list_utils:longest(column))};
endfor
"iterate rows through the height of the tallest column";
for row in [1..maxlen]
line = "";
"poll each column for row data (or filler space)";
for col in [1..arglen]
column = columns[col];
if (`column[row] ! E_RANGE' != E_RANGE)
"there are rows left, so use their data";
line = (line + ((length(sl = column[row]) != (w = widths[col])) ? $string_utils:left(sl, w) | sl)) + divider;
else
"use enough spaces to match the fattest part of the column";
line = (line + $string_utils:space(widths[col])) + divider;
endif
endfor
"add the complete row to the page";
page = {@page, line};
endfor
"return the page";
return page;
.
#55:0
set_task_perms(caller_perms());
return $match_utils:(verb)(@args);
.
#55:1
"$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().";
if (args && (typeof(args[1]) == STR))
prompt = args[1];
else
prompt = "a line of input";
endif
c = callers();
p = c[length(c)][5];
p:notify(tostr("[Type ", prompt, " or `@abort' to abort the command.]"));
ans = read();
if (typeof(ans) == ERR)
return ans;
elseif ($string_utils:trim(ans) == "@abort")
p:notify(">> Command Aborted <<");
kill_task(task_id());
else
return ans;
endif
.
#55:2
"$command_utils:read_lines() -- read zero or more lines of input";
"";
"Returns a list of strings, the lines typed by the player.  Returns E_PERM if the current task is not a command task that has never called suspend().";
"In order that one may enter arbitrary lines, including \"@abort\" or \".\", if the first character in an input line is `.' and there is some nonwhitespace afterwords, the `.' is dropped and the rest of the line is taken verbatim, so that, e.g., \".@abort\" enters as \"@abort\" and \"..\" enters as \".\".";
c = callers();
p = c[length(c)][5];
p:notify("[Type lines of input; use `.' to end or `@abort' to abort the command.]");
ans = {};
while (1)
if (typeof(line = read()) == ERR)
return line;
elseif ((line[1..min(6, length(line))] == "@abort") && ((tail = line[7..length(line)]) == $string_utils:space(tail)))
p:notify(">> Command Aborted <<");
kill_task(task_id());
elseif ((!line) || (line[1] != "."))
ans = {@ans, line};
elseif ((tail = line[2..length(line)]) == $string_utils:space(tail))
return ans;
else
ans = {@ans, tail};
endif
endwhile
.
#55:3
":yes-or-no([prompt]) -- prompts the player for a yes or no answer and returns a true value iff the player enters a line of input that is some prefix of \"yes\"";
"";
"Returns E_NONE if the player enters a blank line, E_INVARG, if the player enters something that isn't a prefix of \"yes\" or \"no\", and E_PERM if the current task is not a command task that has never called suspend().";
c = callers();
p = c[length(c)][5];
p:notify(tostr(args ? args[1] + " " | "", "[Enter `yes' or `no']"));
ans = read(@((caller == p) || $perm_utils:controls(caller_perms(), p)) ? {p} | {});
if (typeof(ans) == ERR)
return ans;
elseif (ans = $string_utils:trim(ans))
if (ans == "@abort")
p:notify(">> Command Aborted <<");
kill_task(task_id());
endif
return (index("yes", ans) == 1) || ((index("no", ans) != 1) && E_INVARG);
else
return E_NONE;
endif
.
#55:4
"$command_utils:read_lines_escape(escapes[,help]) -- read zero or more lines of input";
"";
"Similar to :read_lines() except that help is available and one may specify other escape sequences to terminate the read.";
"  escapes should be either a string or list of strings; this specifies which inputs other from `.' or `@abort' should terminate the read (... don't use anything beginning with a `.').";
"  help should be a string or list of strings to be printed in response to the player typing `?'; the first line of the help text should be a general comment about what the input text should be used for.  Successive lines should describe the effects of the alternative escapes.";
"Returns {end,list-of-strings-input} where end is the particular line that terminated this input or 0 if input terminated normally with `.'.  Returns E_PERM if the current task is not a command task that has never called suspend().  ";
"@abort and lines beginning with `.' are treated exactly as with :read_lines()";
c = callers();
p = c[length(c)][5];
escapes = {".", "@abort", @(typeof(args[1]) == LIST) ? args[1] | {args[1]}};
p:notify(tostr("[Type lines of input; `?' for help; end with `", $string_utils:english_list(escapes, "", "' or `", "', `", ""), "'.]"));
ans = {};
escapes[1..0] = {"?"};
"... set up the help text...";
help = {@args, "You are currently in a read loop."}[2];
if (typeof(help) != LIST)
help = {help};
endif
help[2..1] = {"Type `.' on a line by itself to finish.", "Anything else with a leading period is entered with the period removed.", "Type `@abort' to abort the command completely."};
while (typeof(line = `read() ! ANY') != ERR)
if ((trimline = $string_utils:trimr(line)) in escapes)
if (trimline == ".")
return {0, ans};
elseif (trimline == "@abort")
p:notify(">> Command Aborted <<");
kill_task(task_id());
elseif (trimline == "?")
p:notify_lines(help);
else
return {trimline, ans};
endif
else
if (line && (line[1] == "."))
line[1..1] = "";
endif
ans = {@ans, line};
endif
endwhile
return line;
.
#55:5
"Suspend, using output_delimiters() in case a client needs to keep track";
"of the output of the current command.";
"Args are TIME, amount of time to suspend, and optional (misnamed) OUTPUT.";
"If given no OUTPUT, just do a suspend.";
"If OUTPUT is neither list nor string, suspend and return output_delimiters";
"If OUTPUT is a list, it should be in the output_delimiters() format:";
"  {PREFIX, SUFFIX}.  Use these to handle that client stuff.";
"If OUTPUT is a string, it should be SUFFIX (output_delimiters[2])";
"";
"Proper usage:";
"The first time you want to suspend, use";
"  output_delimiters = $command_utils:suspend(time, x);";
"where x is some non-zero number.";
"Following, use";
"  $command_utils:suspend(time, output_delimiters);";
"To wrap things up, use";
"  $command_utils:suspend(time, output_delimiters[2]);";
"You'll probably want time == 0 most of the time.";
"Note: Using this from verbs called by other verbs could get pretty weird.";
set_task_perms(caller_perms());
time = args[1];
output = (length(args) == 2) && args[2];
if (!output)
suspend(time);
else
if (typeof(output) == LIST)
PREFIX = output[1];
SUFFIX = output[2];
if (PREFIX)
player:tell(output[2]);
endif
suspend(time);
if (SUFFIX)
player:tell(output[1]);
endif
elseif (typeof(output) == STR)
if (output)
player:tell(output);
endif
else
output = output_delimiters(player);
suspend(time);
if (output != {"", ""})
player:tell(output[1]);
endif
return output;
endif
endif
.
#55:6
"Return true if we're running out of ticks or seconds.";
return (seconds_left() < 2) || (ticks_left() < 4000);
.
#55:7
"Usage:  $command_utils:suspend_if_needed(<time>[, @<announcement>])";
"See if we're running out of ticks or seconds, and if so suspend(<time>) and return true.  If more than one arg is given, print the remainder with player:tell.";
if (this:running_out_of_time())
" && valid(player)) -- taking check out for now ";
if (ann = listdelete(args, 1))
player:tell(@ann);
endif
data = {task_id(), callers()};
this:suspend_database_add(data);
set_task_perms(caller_perms());
suspend(args[1]);
this:suspend_database_remove(data);
return 1;
endif
.
#55:8
":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] | {}, "."};
.
#55:9
":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 = 0;
while (0 <= (i = $code_utils:find_verb_named(what, verb, i)))
if (evs = $code_utils:explain_verb_syntax(x, verb, @verb_args(what, tostr(i))))
player:tell("Try this instead:  ", evs);
return 1;
endif
i = i + 1;
endwhile
what = parent(what);
endwhile
endfor
return 0;
.
#55:10
":do_huh(verb,args)  what :huh should do by default.";
set_task_perms(cp = caller_perms());
verb = args[1];
args = args[2];
here = (callers()[1][2] == "huh") ? caller | player.location;
notify = $perm_utils:controls(cp, player) ? "notify" | "tell";
if (player:my_huh(verb, args))
"... the player found something funky to do ...";
return 1;
elseif (here:here_huh(verb, args))
"... the room found something funky to do ...";
return 1;
elseif (player:last_huh(verb, args))
"... player's second round found something to do ...";
return 1;
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) || (here:here_explain_syntax(caller, verb, args) || this:explain_syntax(caller, verb, args));
endif
.
#55:11
if (caller == this)
this.suspend_database = setadd(this.suspend_database, args[1]);
endif
.
#55:12
if (caller == this)
this.suspend_database = setremove(this.suspend_database, args[1]);
endif
.
#55:13
if (caller_perms().wizard)
q = $list_utils:slice(queued_tasks(), 1);
newdb = {};
for x in (this.suspend_database)
if (x[1] in q)
newdb = {@newdb, x};
endif
endfor
this.suspend_database = newdb;
else
return E_PERM;
endif
.
#55:14
"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;
.
#55:15
this:suspend_database_cleanup();
who = caller_perms();
for x in (this.suspend_database)
if (who.wizard || (x[2][1][3] == who))
player:tell(x[1]);
for y in (x[2])
player:tell("   ", $string_utils:print(y));
endfor
endif
endfor
.
#55:16
"$command_utils:suspend_database_info(task_id) => Returns callers list of $command_utils:suspend_if_needed() call with given task_id.  Caller_perms() must be a Wizard or the owner of the task.  Returns {} if no task.";
"WIZARDLY";
task_id = args[1];
cp = caller_perms();
this:suspend_database_cleanup();
task = $list_utils:assoc(task_id, this.suspend_database);
if (!cp.wizard)
if (!(queued = $list_utils:assoc(task_id, queued_tasks())))
return {};
elseif (queued[5] != cp)
return E_PERM;
endif
endif
return task ? task[2] | {};
.
#55:17
":standard_features(character)";
"Return the standard features for the given character.";
char = args[1];
base = this.char_features;
if ($rpg:trusted(char))
base = {@base, @this.gm_features};
endif
if (char.programmer)
base = {@base, @this.player_features, @this.prog_features};
elseif (is_player(char))
base = {@base, @this.player_features};
endif
return base;
.
#55:18
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.suspend_database = {};
this.char_features = this.player_features = this.gm_features = this.prog_features = {};
"This probably isn't the best way to distinguish them, but it works.";
for kid in ($object_utils:leaves_suspended($feature))
if ("is_char_feature" in kid.aliases)
this.char_features = {@this.char_features, kid};
elseif ("is_player_feature" in kid.aliases)
this.player_features = {@this.player_features, kid};
elseif ("is_gm_feature" in kid.aliases)
this.gm_features = {@this.gm_features, kid};
elseif ("is_prog_feature" in kid.aliases)
this.prog_features = {@this.prog_features, kid};
endif
$command_utils:suspend_if_needed(0);
endfor
.
#55:19
return $set_utils:union(this.char_features, this.player_features, this.gm_features, this.prog_features);
.
#56: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]);
if ($command_utils:player_match_result(owner, args[2])[1])
return;
endif
if (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];
{old_owner, perms} = property_info(object, pname);
e = $wiz_utils:set_property_owner(object, pname, owner);
if (e == E_NONE)
player:notify(tostr("Owner of +c property ", object, ".\"", pname, "\" changed from ", $string_utils:nn(old_owner), " to ", $string_utils:nn(owner), ".  Did you really want to do that?"));
elseif (!e)
player:notify(tostr("Result: ", e));
else
player:notify(tostr("Owner of ", object, ".\"", pname, "\" changed from ", $string_utils:nn(old_owner), " to ", $string_utils:nn(owner), "."));
endif
endif
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];
info = verb_info(object, vname);
if (info == E_VERBNF)
player:notify("That object does not define that verb.");
elseif (typeof(info) == ERR)
player:notify(tostr(info));
else
{old_owner, old_perms, old_names} = info;
result = set_verb_info(object, vname, listset(info, owner, 1));
if (typeof(result) == ERR)
player:notify(tostr(result));
else
player:notify(tostr("Owner of ", object, ":\"", old_names, "\" changed from ", $string_utils:nn(old_owner), " to ", $string_utils:nn(owner), "."));
endif
endif
endif
else
object = this:my_match_object(what);
if (!$command_utils:object_match_failed(object, what))
old_owner = object.owner;
player:notify(tostr($wiz_utils:set_owner(object, owner) && tostr("Owner of ", $string_utils:nn(object), " changed from ", $string_utils:nn(old_owner), " to ", $string_utils:nn(owner), ".")));
endif
endif
.
#56:1
"@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: ", $string_utils:print(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
suspendok = verb != "@grant";
if (verb != "@transfer")
oldowner = object.owner;
player:tell("Transfering ", $string_utils:print(objlist), " from ", $string_utils:nn(oldowner), " to ", $string_utils:nn(newowner), " ... ");
endif
for object in (objlist)
$command_utils:suspend_if_needed(0);
same = object.owner == newowner;
for vnum in [0..length(verbs(object)) - 1]
verb = tostr(vnum);
info = verb_info(object, verb);
if (!((info[1] != object.owner) && (valid(info[1]) && is_player(info[1]))))
same = same && (info[1] == newowner);
set_verb_info(object, verb, listset(info, newowner, 1));
endif
endfor
for prop in (properties(object))
if (suspendok && ((ticks_left() < 5000) || (seconds_left() < 2)))
suspend(0);
endif
info = property_info(object, prop);
if (!(index(info[2], "c") || (((info[1] != object.owner) && valid(info[1])) && is_player(info[1]))))
same = same && (info[1] == newowner);
$wiz_utils:set_property_owner(object, prop, newowner, suspendok);
endif
endfor
if (suspendok)
suspend(0);
endif
$wiz_utils:set_owner(object, newowner, suspendok);
if (same)
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
.
#56:2
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 ((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
.
#56:3
if (!player.wizard)
player:notify("Sorry.");
return;
elseif ($code_utils:task_valid($shutdown_task))
player:notify("Shutdown already in progress.");
return;
endif
if ((prepstr == "in") && ((delay = $code_utils:tonum(iobjstr)) != E_TYPE))
argstr = dobjstr;
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 = 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())
notify(p, msg);
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);
.
#56:4
set_task_perms(player);
dump_database();
player:notify("Dumping...");
.
#56:5
set_task_perms(player);
if (argstr[1] != ":")
argstr = ":" + argstr;
endif
player:notify(tostr("Searching for verbs that appear to call ", argstr, " ..."));
player:notify("");
$code_utils:find_verbs_containing(argstr + "(");
.
#56:6
if (!caller_perms().wizard)
return;
elseif (!("mcd_pos" in properties(this)))
return;
endif
start = this.mcd_pos;
saved = args[1];
saved_props = args[2];
player:notify(tostr("*** Recycling from #", start, " ..."));
suspend(0);
fork (0)
this:mcd_2(saved, saved_props);
endfork
for i in [start..tonum(max_object())]
this.mcd_pos = i;
o = toobj(i);
if ($command_utils:running_out_of_time())
return;
endif
if (valid(o) && (!(o in saved)))
for x in (o.contents)
try
move(x, #-1);
except e (ANY)
player:notify_lines($error:format_traceback(e));
endtry
endfor
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 i in [0..tonum(max_object())]
$command_utils:suspend_if_needed(0);
o = toobj(i);
move(o, ((o == player) || (o == $news)) ? $player_start | #-1);
if ($object_utils:has_callable_verb(o, "init_for_core"))
try
o:init_for_core();
except e (ANY)
player:notify_lines($error:format_traceback(e));
endtry
else
player:notify(tostr("*** ", o.name, " <", o, "> has no :init_for_core verb."));
endif
endfor
player:notify("Core database extraction is complete.  Type @shutdown to save it.");
.
#56:7
"@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
$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} | {}});
.
#56:8
"@untoad <object> [as namespec]";
"Turns object into a player.  Anything that isn't a guest is chowned to itself.";
if (!player.wizard)
player:notify("Yeah, right... you wish.");
elseif (prepstr && (prepstr != "as"))
player:notify(tostr("Usage:  ", verb, " <object> [as name,alias,alias...]"));
elseif ($command_utils:object_match_failed(dobj, dobjstr))
elseif (prepstr && (!(e = $building_utils:set_names(dobj, iobjstr))))
player:notify(tostr("Initial rename failed:  ", e));
elseif (e = $wiz_utils:set_player(dobj, g = $object_utils:isa(dobj, $guest)))
player:notify(tostr(dobj.name, "(", dobj, ") is now a ", g ? "usable guest." | "player."));
elseif (e == E_INVARG)
player:notify(tostr(dobj.name, "(", dobj, ") is not of an appropriate player class."));
player:notify("@chparent it to $player or some descendant.");
elseif (e == E_NONE)
player:notify(tostr(dobj.name, "(", dobj, ") is already a player."));
elseif (e == E_NACC)
player:notify("Wait until $player_db is finished updating...");
elseif (e == E_RECMOVE)
player:notify(tostr("The name `", dobj.name, "' is currently unavailable."));
player:notify(tostr("Try again with   ", verb, " ", dobj, " as <newname>"));
else
player:notify(tostr(e));
endif
.
#56:9
"@quota <player> is [public] <number> [<reason>]";
"  changes a player's quota.  sends mail to the wizards.";
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..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:tonum((qstr[1] == "+") ? qstr[2..length(qstr)] | qstr);
reason = iobjstr[n + 1..length(iobjstr)] || "(none)";
if (typeof(new) != NUM)
player:notify(tostr("Set ", dobj.name, "'s quota to what?"));
return;
elseif (qstr[1] == "+")
new = old + new;
endif
result = $quota_utils:set_quota(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[length(reason)]) ? "" | "."));
.
#56:10
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 ? tonum(max_object()) + 1 | length(players)]
if (with_objects)
o = toobj(i - 1);
else
o = players[i];
endif
if ($command_utils:running_out_of_time())
player:notify(tostr("... ", o));
suspend(0);
endif
if (valid(o))
numo = numo + 1;
p = is_player(o) ? o | o.owner;
if (!valid(p))
invalid_objects = invalid_objects + 1;
elseif (!$object_utils:isa(p, $player))
nonplayer_objects = nonplayer_objects + 1;
else
seconds = now - p.last_connect_time;
days = seconds / day;
weeks = seconds / week;
months = seconds / month;
if (seconds < 0)
if (is_player(o))
always_players = always_players + 1;
else
always_objects = always_objects + 1;
endif
elseif (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("");
.
#56:11
"Auxiliary verb for parsing @kill soon [#-of-seconds] [player | everyone]";
"Args[1] is either # of seconds or player/everyone.";
"Args[2], if it exists, is player/everyone, and forces args[1] to have been # of seconds.";
"Return value: {# of seconds [default 60] , 1 for all, object for player.}";
set_task_perms(caller_perms());
nargs = length(args);
soon = tonum(args[1]);
if (nargs > 1)
everyone = args[2];
elseif (soon <= 0)
everyone = args[1];
else
everyone = 0;
endif
if (everyone == "everyone")
everyone = 1;
elseif (typeof(everyone) == STR)
result = $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};
.
#56:12
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 ", $string_utils:print(pattern), " ..."));
player:notify("");
$code_utils:(regexp ? "find_verbs_matching" | "find_verbs_containing")(pattern, $core_objects());
.
#56:13
"@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
.
#56:14
"@make-player[!] NAME [for ]EMAIL [[because ]COMMENT]";
"Creates the player, generating a random password and optionally mailing it to em.";
"If ! is the last character of the verb, then send e-mail to the new character without prompting.";
if ((!player.wizard) || callers())
return E_PERM;
endif
if (i = "because" in args)
args = listdelete(args, i);
endif
if (i = "for" in args)
args = listdelete(args, i);
endif
return $wiz_utils:do_make_player(verb[length(verb)] == "!", @args);
.
#56:15
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
.
#56:16
"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
.
#56:17
set_task_perms((caller in {this, $generic_editor, $verb_editor, $mail_editor, $note_editor, $value_editor}) ? this.owner | caller_perms());
return move(this, args[1]);
.
#56:18
"@newt <player> [commentary]";
"turns a player into a newt.  It can get better...";
"Installs $wiz_utils:newt_confunc on a user as :confunc.  It saves any existing :confunc the user may have as :denewt_confunc.  @denewt checks that their :confunc is the same as $wiz_utils:newt_confunc (if not it renames it to :newt_confunc and complains).  If so, it deletes it, and renames any :denewt_confunc to :confunc.";
"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 ($wiz_utils:isnewt(who))
player:notify(tostr(who.name, " appears to already be a newt."));
return;
else
if ($wiz_utils:rename_all_instances(who, "confunc", "denewt_confunc"))
player:notify(tostr(who, ":confunc renamed to :denewt_confunc."));
endif
add_verb(who, {player, "x", "confunc"}, {"this", "none", "this"});
set_verb_code(who, "confunc", verb_code($wiz_utils, "newt_confunc"));
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
endif
.
#56:19
"@denewt <player> [commentary]";
"Renames the player's :confunc to :newt_confunc, then checks that it is is the same as $wiz_utils:newt_confunc, if not complains.  If so, it deletes it, and renames any :denewt_confunc to :confunc.";
"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 (!(inf = verb_info(who, "confunc")))
player:notify(tostr(who.name, " does not appear to be a newt."));
else
set_verb_info(who, "confunc", {inf[1], inf[2], "newt_confunc"});
wiz = verb_code($wiz_utils, "newt_confunc");
user = verb_code(who, "newt_confunc");
if (wiz == user)
delete_verb(who, "newt_confunc");
else
player:notify(tostr(who.name, "'s :confunc was not identical to $wiz_utils:newt_confunc.  Not automatically rmverbed.  Verify and manually @rmverb it."));
endif
if (inf = verb_info(who, "denewt_confunc"))
set_verb_info(who, "denewt_confunc", {inf[1], inf[2], "confunc"});
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
.
#56:20
"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
if (args[2] == "as")
args = listdelete(args, 2);
endif
$wiz_utils:do_register(@args);
.
#56:21
"@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;
endif
$wiz_utils:do_new_password(dobjstr, iobjstr);
.
#56:22
"@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
.
#56:23
set_task_perms(player);
n = (dobjstr == "all") ? 0 | $code_utils:tonum(dobjstr || "20");
if (caller != this)
player:notify("You lose.");
elseif ((n == E_TYPE) && (index("now", dobjstr) != 1))
player:notify(tostr("Usage:  ", verb, " <number>  (where <number> indicates how many entries to look at in the guest log)"));
player:notify(tostr("Usage:  ", verb, " now (to see information about currently connected guests only)"));
elseif ((!dobjstr) || (index("now", dobjstr) != 1))
$guest_log:last(n);
else
"*way* too much copied code in here from @who...  Sorry.  --yduJ";
su = $string_utils;
conn = connected_players();
unsorted = {};
for g in (children($guest))
if (g in conn)
unsorted = {@unsorted, g};
endif
endfor
if (!unsorted)
player:tell("No guests found.");
return;
endif
footnotes = {};
alist = {};
nwidth = length("Player name");
for u in (unsorted)
pref = u.programmer ? "% " | "  ";
u.programmer && (footnotes = setadd(footnotes, "prog"));
u3 = {tostr(pref, u.name, " (", u, ")"), su:from_seconds(connected_seconds(u)), su:from_seconds(idle_seconds(u)), where = $string_utils:connection_hostname(connection_name(u))};
nwidth = max(length(u3[1]), nwidth);
if ($login:blacklisted(where))
where = "(*) " + where;
footnotes = setadd(footnotes, "black");
elseif ($login:graylisted(where))
where = "(+) " + where;
footnotes = setadd(footnotes, "gray");
endif
alist = {@alist, u3};
endfor
alist = $list_utils:sort_alist_suspended(0, alist, 3);
$command_utils:suspend_if_needed(0);
headers = {"Player name", "Connected", "Idle Time", "From Where"};
time_width = length("59 minutes") + 2;
before = {0, w1 = nwidth + 3, w2 = w1 + time_width, w3 = w2 + time_width};
tell1 = "  " + headers[1];
tell2 = "  " + su:space(headers[1], "-");
for j in [2..4]
tell1 = su:left(tell1, before[j]) + headers[j];
tell2 = su:left(tell2, before[j]) + su:space(headers[j], "-");
endfor
player:notify(tell1);
player:notify(tell2);
active = 0;
for a in (alist)
$command_utils:suspend_if_needed(0);
tell1 = a[1];
for j in [2..4]
tell1 = su:left(tell1, before[j]) + tostr(a[j]);
endfor
player:notify(tell1[1..min(length(tell1), 79)]);
endfor
if (footnotes)
player:notify("");
if ("prog" in footnotes)
player:notify(" %  == programmer.");
endif
if ("black" in footnotes)
player:notify("(*) == blacklisted site.");
endif
if ("gray" in footnotes)
player:notify("(+) == graylisted site.");
endif
endif
endif
.
#56:24
set_task_perms(valid(caller_perms()) ? caller_perms() | player);
use = this.mail_identity;
if (valid(use) && (use != this))
return use:(verb)(@args);
else
return pass(@args);
endif
.
#56:25
"@[un]blacklist [<site or subnet>  [commentary]]";
"@[un]graylist  [<site or subnet>  [commentary]]";
"@[un]redlist   [<site or subnet>  [commentary]]";
set_task_perms(player);
if ((player != this) || (!player.wizard))
player:notify("Ummm.  no.");
return;
endif
undo = verb[2..3] == "un";
which = $login:listname(verb[undo ? 4 | 2]);
downgrade = {"", "graylist", "blacklist"}[1 + index("br", which[1])];
if (!(fw = $string_utils:first_word(argstr)))
"... Just print the list...";
slist = {};
if (s = $login.(which)[1])
slist = {@slist, "--- Subnets ---", @s};
endif
if (s = $login.(which)[2])
slist = {@slist, "--- Domains ---", @s};
endif
player:notify_lines($string_utils:columnize(slist, 3));
return;
endif
target = fw[1];
comment = fw[2] ? {fw[2]} | {};
if (is_literal = $site_db:domain_literal(target))
if (target[l = length(target)] == ".")
target = target[1..l - 1];
endif
fullname = "subnet " + target;
else
if (target[1] == ".")
target[1..1] = "";
endif
fullname = ("domain `" + target) + "'";
endif
rm = {};
entrylist = $login.(which)[1 + (!is_literal)];
if ((!undo) && (target in entrylist))
player:notify(tostr(fullname, " is already ", which, "ed."));
return;
endif
entrylist = setremove(entrylist, target);
confirm = 0;
if (is_literal)
for s in (entrylist)
if ((i = index(s, target + ".")) == 1)
"... target is a prefix of s, s should probably go...";
rm = {@rm, s};
elseif (index(target + ".", s + ".") != 1)
"... s is not a prefix of target...";
elseif (undo)
player:notify(tostr("You will need to un", which, " subnet ", s, " as well."));
elseif (confirm)
player:notify(tostr("...Subnet ", s, " already ", which, "ed..."));
else
player:notify(tostr("Subnet ", s, " already ", which, "ed."));
if (!(confirm = $command_utils:yes_or_no(tostr(which, " ", target, " anyway?"))))
return;
endif
endif
endfor
else
for s in (entrylist)
if ((i = rindex(s, "." + target)) && (i == (length(s) - length(target))))
"... target is a suffix of s, s should probably go...";
rm = {@rm, s};
elseif ((!(i = rindex("." + target, "." + s))) || (i < ((length(target) - length(s)) + 1)))
"... s is not a suffix of target...";
elseif (undo)
player:notify(tostr("You will need to un", which, " domain `", s, "' as well."));
elseif (confirm)
player:notify(tostr("...Domain `", s, "' already ", which, "ed..."));
else
player:notify(tostr("Domain `", s, "' already ", which, "ed."));
if (!(confirm = $command_utils:yes_or_no(tostr(which, " ", target, " anyway?"))))
return;
endif
endif
endfor
endif
namelist = $string_utils:english_list(rm);
downgraded = {};
if (rm)
ntries = (length(rm) == 1) ? "ntry" | "ntries";
if ($command_utils:yes_or_no(tostr("Remove e", ntries, " for ", namelist, "?")))
dg = undo && (downgrade && $command_utils:yes_or_no(downgrade + " them?"));
for s in (rm)
$login:(which + "_remove")(s);
dg && ($login:(downgrade + "_add")(s) && (downgraded = {@downgraded, s}));
endfor
player:notify(tostr("E", ntries, " removed", @dg ? {" and ", downgrade, "ed."} | {"."}));
else
player:notify(tostr(namelist, " will continue to be ", which, "ed."));
rm = {};
endif
endif
if (downgraded)
comment[1..0] = {tostr(downgrade, "ed ", $string_utils:english_list(downgraded), ".")};
endif
if (!undo)
$login:(which + "_add")(target);
player:notify(tostr(fullname, " ", which, "ed."));
if (rm)
comment[1..0] = {tostr("Subsumes ", which, "ing for ", namelist, ".")};
endif
elseif ($login:(which + "_remove")(target))
player:notify(tostr(fullname, " un", which, "ed."));
if (downgrade && $command_utils:yes_or_no(downgrade + " it?"))
$login:(downgrade + "_add")(target) && (downgraded = {target, @downgraded});
player:notify(tostr(fullname, " ", downgrade, "ed."));
endif
if (downgraded)
comment[1..0] = {tostr(downgrade, "ed ", $string_utils:english_list(downgraded), ".")};
endif
if (rm)
comment[1..0] = {tostr("Also removed ", namelist, ".")};
endif
elseif (rm)
player:notify(tostr(fullname, " itself was never actually ", which, "ed."));
comment[1..0] = {tostr("Removed ", namelist, ".")};
else
player:notify(tostr(fullname, " was not ", which, "ed before."));
return;
endif
subject = tostr(undo ? "@un" | "@", which, " ", fullname);
$mail_agent:send_message(player, $site_log, subject, comment);
"...";
"... make sure we haven't screwed ourselves...";
uhoh = {};
for site in (player.all_connect_places)
if (index(site, target) && $login:(which + "ed")(site))
uhoh = {@uhoh, site};
endif
endfor
if (uhoh)
player:notify(tostr("WARNING:  ", $string_utils:english_list(uhoh), " are now ", which, "ed!"));
endif
.
#56:26
"Usage:  @corify <object> as <propname>";
"Adds <object> to the core, as $<propname>";
"Reminds the wizard to write an :init_for_core verb, if there isn't one already.";
if (!player.wizard)
player:tell("Sorry, the core is wizardly territory.");
return E_PERM;
endif
if (dobj == $failed_match)
dobj = player:my_match_object(dobjstr);
endif
if ($command_utils:object_match_failed(dobj, dobjstr))
return;
endif
if (!iobjstr)
player:notify("Usage:  @corify <object> as <propname>");
return;
elseif (iobjstr[1] == "$")
iobjstr = iobjstr[2..$];
endif
if ("init_for_core" in verbs(dobj))
"ok";
elseif ($core_utils:install_bare_init_for_core(dobj))
player:notify($string_utils:pronoun_sub("Added a bare :init_for_core verb to %n (%#).", dobj));
else
player:notify($string_utils:pronoun_sub("Failed to install a bare :init_for_core verb to %n (%#).", dobj));
endif
try
add_property(#0, iobjstr, dobj, {player, "r"});
except e (ANY)
player:notify(tostr(e[1], ":", e[2]));
endtry
player:notify(tostr("Corified ", $string_utils:nn(dobj), " as $", iobjstr, "."));
.
#56:27
"Usage:  @make-guest <guestname>";
"Creates a player called <guestname>_Guest owned by $hacker and a child of $guest.";
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
guestname = args[1];
guestaliases = setadd({guestname}, adj = args[1]);
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 = create($guest, $hacker);
new:set_name(guestname);
new:set_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:set_gender(new.default_gender);
move(new, $player_start);
player:tell("Now don't forget to @describe ", new, " as something.");
endif
endif
.
#56:28
"@mail-new-player user passwd";
"Send the standard new-player mail to the given user.";
if (!player.wizard)
return E_PERM;
endif
dobj = $match_utils:match_player(dobjstr = args[1]);
if ($match_utils:player_match_failed(dobj, dobjstr))
return;
endif
passwd = (length(args) > 1) ? args[2] | "[sent previously]";
if ((result = $wiz_utils:send_new_player_mail(tostr("From ", player.name, "@", $network.moo_name, ":"), dobj.name, email = dobj.email_address, dobj, args[2])) == 0)
player:notify(tostr("Mail sent successfully to ", email, "."));
else
player:tell("Cannot send mail: ", result);
endif
.
#56:29
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 ((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, " is now a programmer.  ", dobj:ppc(), " quota is currently ", dobj.ownership_quota, "."));
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
.
#56:30
"@quota <player> is <number> [<reason>]";
"  changes a player's quota.  sends mail to the wizards.";
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
new = tonum(qstr = iobjstr[1..(n = index(iobjstr + " ", " ")) - 1]);
reason = iobjstr[n + 1..length(iobjstr)] || "(none)";
if (tostr(new) != qstr)
player:notify(tostr("Set ", dobj.name, "'s quota to what?"));
return;
endif
old = dobj.ownership_quota;
result = dobj.ownership_quota = new;
if (typeof(result) == ERR)
player:notify(tostr(result));
else
player:notify(tostr(dobj.name, "'s quota set to ", new, "."));
endif
$mail_agent:send_message(player, $quota_log, tostr("@quota ", dobj.name, " (", dobj, ") ", new, " (from ", old, ")"), tostr("Reason for quota ", ((new - old) < 0) ? "decrease: " | "increase: ", reason, "."));
.
#56:31
"@zap <user>";
"Grant all of the given user's objects over to Executor, then unset eir player bit and recycle em.  <user> must be an object number.";
if (caller != this)
return E_PERM;
endif
p = $code_utils:toobj(dobjstr);
if (p == E_TYPE)
player:notify("As an added precaution against accidental @zapping, you must give the player's object number.");
return E_TYPE;
endif
if (!is_player(p))
player:notify(tostr($string_utils:nn(p), " is not a player; it doesn't need @zapped."));
return E_NONE;
endif
EXECUTOR = $executor;
executor_id = $string_utils:nn(EXECUTOR);
dead_name = $string_utils:nn(p);
suspend_msg = tostr("--- Suspending @zap ", dead_name, " ...");
if (p.programmer)
player:notify($string_utils:pronoun_sub("%N (%#) %<is> a programmer, and as such cannot be automatically @zapped.  Do it by hand.", p));
return E_NACC;
endif
stuff_msg = "";
msg = $string_utils:pronoun_sub(((("%N (%#)" + stuff_msg) + " %<was> last connected at ") + player:ctime(p.last_disconnect_time)) + ".  Recycle %o?", p);
if (didit = $command_utils:yes_or_no(msg))
i = setremove(p.owned_objects, p);
for o in (i)
$wiz_utils:grant_object(o, EXECUTOR);
player:notify(tostr("--- Granting ", $string_utils:nn(o), " to ", executor_id, " ..."));
$command_utils:suspend_if_needed(0, suspend_msg);
endfor
$wiz_utils:unset_player(p);
$recycler:_recycle(p);
player:notify(" ");
player:notify(tostr("*** Recycled ", dead_name, " ***"));
else
player:notify(tostr("--- Saved ", dead_name, " ---"));
endif
.
#56:32
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.mail_identity = #-1;
.
#56:33
if (!player.wizard)
raise(E_PERM, "Nice try, but permission denied.");
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, " ", tonum(o = $quota_utils:bi_create(#1)), "' now."));
recycle(o);
return;
elseif (toobj(tonum(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 and $public...");
$local = $public = #-1;
"----------------------------------------";
player:notify("Identifying objects to be saved...");
$core_utils:mcd_mark_core();
"----------------------------------------";
player:notify("Killing all queued tasks ...");
$core_utils:mcd_kill_tasks();
"----------------------------------------";
player:notify("Stripping you of any personal verbs and/or properties ...");
suspend(0);
$core_utils:mcd_strip_god();
"----------------------------------------";
player:notify("Preparing unsaved objects for recycling ...");
suspend(0);
$core_utils:mcd_prepare_doomed();
"----------------------------------------";
player:notify("Recycling unsaved objects ...");
fork (0)
$core_utils:mcd_reap_doomed();
endfork
while ($core_utils.mcd_pos != #-1)
suspend(0);
endwhile
"----------------------------------------";
player:notify("Killing remaining queued tasks ...");
$core_utils:mcd_kill_tasks();
"----------------------------------------";
player:notify("Validating owners of core objects, verbs, and properties ...");
suspend(0);
$core_utils:mcd_validate_owners();
"----------------------------------------";
player:notify("Compacting object numbers ...");
suspend(0);
$core_utils:mcd_renumber();
"----------------------------------------";
player:notify("Initializing core objects ...");
suspend(0);
$core_utils:mcd_initialize_core();
"----------------------------------------";
player:notify("Cleaning up extraction residue ...");
suspend(0);
$core_utils:mcd_finalize();
player:notify("Core database extraction is complete.  Type @shutdown to save it.");
.
#56:34
"@reserve-name <name>";
"Reserve the given name, preventing it from being taken as a name or alias by any player.  If it current IS a name or alias, the player using the name is listed.";
if (player != this)
raise(E_PERM);
endif
name = dobjstr;
if (name in $player_db.reserved)
player:notify(tostr("The name \"", name, "\" is already reserved."));
return E_NONE;
endif
existing = $player_db:find_exact(name);
if (valid(existing))
player:notify(existing:grammar_sub(tostr("%N (%#) %<is> currently the name \"", name, "\".")));
endif
$player_db.reserved = setadd($player_db.reserved, name);
player:notify(tostr("Added \"", name, "\" as a reserved name."));
.
#56:35
"@finger";
"Show all connected players, their e-mail addresses, last command typed, idle time.";
if (caller != this)
raise(E_PERM);
endif
su = $string_utils;
linelen = player:linelen();
lastlen = linelen - 56;
player:notify((((((((su:centre("User", -12) + " ") + su:centre("Email", 24)) + " ") + su:centre("What", 12)) + " ") + su:centre("Idle", 4)) + " ") + su:centre("From", lastlen));
lines = idles = {};
for user in (connected_players())
lines = listappend(lines, su:left(su:format("%12 %24 %12 %4 %0", @user:finger_line_info()), -linelen));
idles = listappend(idles, user:idle_seconds());
endfor
player:notify_lines($list_utils:sort(lines, idles));
.
#56:36
"@copyo*bject fromobj [to toobj]";
"Makes toobj (created if not given) an identical copy of fromobj, including property/verb ownerships.  The only things not copied are the player flag, .programmer, .wizard, .owner, .contents, .location, ownership of rc properties.";
"(Ported/Swiped with minor cosmetic modifications from LambdaMOO's Wizard with Extra Cruft <#13331>.)";
if ((caller_perms() != #-1) && (player != caller_perms()))
raise(E_PERM);
endif
set_task_perms(player);
if ($match_utils:object_match_failed(dobj = player:my_match_object(dobjstr), dobjstr))
return;
elseif (!prepstr)
iobj = $recycler:_create(dobj);
if (typeof(iobj) == ERR)
player:tell("Couldn't create copy: ", iobj);
return;
endif
elseif (prepstr != "to")
player:tell("Usage:  ", verb, " <object> [to <object2>]");
return;
elseif ($match_utils:object_match_failed(iobj = player:my_match_object(iobjstr), iobjstr))
return;
elseif (!(e = $building_utils:recreate(iobj, dobj)))
player:tell("Couldn't recreate ", iobj, ": ", e);
return;
endif
chparent(iobj, parent(dobj));
for p in ({"name", "r", "w", "f"})
iobj.(p) = dobj.(p);
endfor
for p in ($object_utils:all_properties_suspended(parent(dobj)))
if (!is_clear_property(dobj, p))
$command_utils:suspend_if_needed(0, "...setting props from parent...");
iobj.(p) = dobj.(p);
endif
endfor
for p in (properties(dobj))
add_property(iobj, p, dobj.(p), property_info(dobj, p));
$command_utils:suspend_if_needed(0, "...adding props...");
endfor
for v in [1..length(verbs(dobj))]
add_verb(iobj, verb_info(dobj, v), verb_args(dobj, v));
set_verb_code(iobj, v, verb_code(dobj, v));
$command_utils:suspend_if_needed(0, "...adding verbs...");
endfor
player:notify(tostr(iobj, " made into a clone of ", $string_utils:nn(dobj), "."));
.
#56:37
"@unalt <alternate>";
"Make the given alternate a primary character.";
if (caller != this)
raise(E_PERM);
endif
dobj = $match_utils:match_player(dobjstr);
if ($match_utils:player_match_failed(dobj, dobjstr))
return;
endif
r = $alt_player_db:delete(dobj);
if (r)
player:tell(dobj:grammar_sub(("%N <%#> %<is> no longer an alternate character of " + $string_utils:nn(@r)) + "."));
elseif (r == E_NONE)
player:tell(dobj:grammar_sub("%N <%#> %<isn't> anybody's alt."));
else
player:tell(verb, ": ", r);
endif
.
#57:0
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...";
elseif ((info = verb_args(object, name = spec[2])) == E_VERBNF)
player:notify("That object does not have a verb with that name.");
elseif (typeof(info) == ERR)
player:notify(tostr(info));
elseif (typeof(pas = $code_utils:parse_argspec(@listdelete(args, 1))) != LIST)
"...arg spec is bogus...";
player:notify(tostr(pas));
elseif (!(newargs = pas[1]))
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..length(info)]};
result = set_verb_args(object, name, info);
if (result == E_INVARG)
player:notify(tostr("\"", info[2], "\" is not a valid preposition (?)"));
elseif (typeof(result) == ERR)
player:notify(tostr(result));
else
player:notify("Verb arguments changed.");
endif
endif
.
#57:1
"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'll need to be a programmer before you can evaluate code.");
return;
endif
set_task_perms(player);
if ((!argstr) && (verb[$] == "?"))
argstr = tostr(";;", $string_utils:from_list($command_utils:read_lines(), " "));
endif
result = player:eval_cmd_string(argstr, verb[1..6] != "eval-d");
if (result[1])
player:notify(this:eval_value_to_string(result[2]));
if (player.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
.
#57:2
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..length(n)];
endif
if ((hv = $object_utils:has_verb(object, n)) && (hv[1] == object))
if (player:get_option($prog_options, "no_verbdup"))
player:notify(tostr("Verb `", n, "' already defined on that object.  Aborting in accordance with `@prog-options +no_verbdup`."));
return;
else
player:notify(tostr("Warning:  Verb `", n, "' already defined on that object."));
endif
endif
endfor
if (typeof(pas = $code_utils:parse_argspec(@listdelete(args, 1))) != LIST)
player:notify(tostr(pas));
return;
endif
verbargs = {@pas[1], "none", "none", "none"}[1..3];
rest = pas[2];
if (rest)
perms = rest[1];
elseif (verbargs == {"this", "none", "this"})
perms = "rxd";
else
perms = "rd";
endif
if (length(rest) < 2)
if (player.wizard)
player:notify("Being a wizard, you are forced to include a verb-owner argument as a security precaution.");
return;
endif
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
x = add_verb(object, {owner, perms, name}, verbargs);
if (x == E_INVARG)
player:notify(tostr(rest ? tostr("\"", perms, "\" is not a valid set of permissions.") | tostr("\"", verbargs[2], "\" is not a valid preposition (?)")));
elseif (typeof(x) == ERR)
player:notify(tostr(x));
else
player:notify(tostr("Verb added (", length(verbs(object)) - 1, ")."));
endif
.
#57:3
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 (index(verbname, "*") > 1)
verbname = strsub(verbname, "*", "");
endif
if ((loc = $code_utils:tonum(verbname)) == E_TYPE)
loc = $code_utils:find_last_verb_named(object, verbname);
if (argspec)
argspec[2] = $code_utils:full_prep(argspec[2]) || argspec[2];
while ((loc >= 0) && (verb_args(object, tostr(loc)) != argspec))
loc = $code_utils:find_last_verb_named(object, verbname, loc);
endwhile
endif
if (loc < 0)
player:notify(tostr("That object does not define that verb", argspec ? " with those args." | "."));
return;
endif
endif
info = verb_info(object, tostr(loc));
vargs = verb_args(object, tostr(loc));
result = delete_verb(object, tostr(loc));
if (result == E_VERBNF)
player:notify("That object does not define that verb.");
elseif (typeof(result) == ERR)
player:notify(tostr(result));
elseif (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
endif
.
#57:4
set_task_perms(player);
if (!dobjstr)
tasks = queued_tasks();
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)
q_id = task[1];
start = task[2];
time = (start >= now) ? ctime(start)[5..24] | su:left((start == -1) ? "Reading input ..." | tostr(now - start, " seconds ago..."), 20);
owner = task[5];
owner_name = valid(owner) ? owner.name | tostr("Dead ", owner);
vloc = task[6];
vname = task[7];
lineno = task[8];
this = task[9];
player:notify(tostr(su:left(tostr(q_id), 10), "  ", time, "  ", su:left(owner_name, 12), "  ", vloc, ":", vname, " (", lineno, ")", (this != vloc) ? tostr(" [", this, "]") | ""));
endfor
else
player:notify("No tasks.");
endif
.
#57:5
"Kills one or more tasks.";
"Arguments:";
"   object:verb -- kills all tasks which were started from that object and verb.";
"   all -- kills all tasks owned by invoker";
"   all player-name -- wizard variant:  kills all tasks owned by player.";
"   all everyone -- wizard variant:  really kills all tasks.";
"   Integer taskid -- kills the specifically named task.";
"   soon [integer] -- kills all tasks scheduled to run in the next [integer] seconds, which defaults to 60.";
"   %integer -- kills all tasks which end in the digits contained in integer.";
set_task_perms(player);
if (length(args) == 0)
player:notify_lines({tostr("Usage:  ", verb, " [object]:[verb]"), tostr("        ", verb, " task_id"), tostr("        ", verb, " soon [number-of-seconds]", player.wizard ? " [everyone|<player name>]" | ""), tostr("        ", verb, " all", player.wizard ? " [everyone|<player name>]" | "")});
return;
elseif (taskid = tonum(args[1]))
elseif (all = args[1] == "all")
everyone = 0;
realplayer = player;
if (player.wizard && (length(args) > 1))
realplayer = $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 = tonum(args[2]);
if ((soon <= 0) && (!player.wizard))
player:notify(tostr("Usage:  ", verb, " soon [positive-number-of-seconds]"));
return;
elseif (player.wizard)
result = this:kill_aux_wizard_parse(@args[2..length(args)]);
soon = result[1];
if (result[1] < 0)
"already gave them an error message";
return;
elseif (result[2] == 1)
everyone = 1;
else
everyone = 0;
set_task_perms(result[2]);
realplayer = result[2];
endif
endif
else
soon = 60;
everyone = 0;
endif
elseif (percent = args[1][1] == "%")
l = length(args[1]);
digits = tonum(args[1][2..l]);
percent = tonum("1" + "0000000000"[1..l - 1]);
elseif (colon = index(argstr, ":"))
whatstr = argstr[1..colon - 1];
vrb = argstr[colon + 1..length(argstr)];
if (whatstr)
what = player:my_match_object(whatstr);
endif
else
player:notify_lines({tostr("Usage:  ", verb, " [object]:[verb]"), tostr("        ", verb, " task_id"), tostr("        ", verb, " soon [number-of-seconds]", player.wizard ? " [everyone|<player name>]" | ""), tostr("        ", verb, " all", player.wizard ? " [\"everyone\"|<player name>]" | "")});
return;
endif
"OK, parsed the line, and punted them if it was bogus.  This verb could have been a bit shorter at the expense of readability.  I think it's getting towards unreadable as is.  At this point we've set_task_perms'd, and set up an enormous number of local variables.  Evaluate them in the order we set them, and we should never get var not found.";
queued_tasks = queued_tasks();
killed = 0;
if (taskid)
returnval = kill_task(taskid);
if (typeof(returnval) == ERR)
player:notify(tostr("Invalid task ID ", taskid, "."));
else
player:notify(tostr("Killed task ", taskid, "."));
killed = 1;
endif
elseif (all)
for task in (queued_tasks)
if (everyone || (realplayer == task[5]))
kill_task(task[1]);
killed = 1;
this:_kill_task_message(task);
endif
endfor
elseif (soon)
now = time();
for task in (queued_tasks)
if (((task[2] - now) < soon) && ((!player.wizard) || (everyone || (realplayer == task[5]))))
kill_task(task[1]);
killed = 1;
this:_kill_task_message(task);
endif
endfor
elseif (percent)
for task in (queued_tasks)
if (digits == (task[1] % percent))
kill_task(task[1]);
killed = 1;
this:_kill_task_message(task);
endif
endfor
elseif ((colon || vrb) || whatstr)
for task in (queued_tasks)
if ((((((whatstr == "") || (valid(task[6]) && (index(task[6].name, whatstr) == 1))) || (valid(task[9]) && (index(task[9].name, whatstr) == 1))) || (task[9] == what)) || (task[6] == what)) && ((vrb == "") || (index(" " + strsub(task[7], "*", ""), " " + vrb) == 1)))
this:_kill_task_message(task);
kill_task(task[1]);
killed = 1;
endif
endfor
else
player:notify("Something is funny; I didn't understand your @kill command.  You shouldn't have gotten here.  Please send yduJ mail saying you got this message from @kill, and what you had typed to @kill.");
endif
if (!killed)
player:notify("No tasks killed.");
endif
.
#57:6
"@edit <object>        -- If object is a note, edit its text; else the description.";
"@edit <object>.<property>     -- Edit property on object.";
"@edit <object>.<verb>         -- Edit verb on object.";
"@edit                         -- Resume an editing session.";
if (caller != this)
player:tell("You can't use ", this.name, "'s ", verb, " verb.");
elseif (args && $code_utils:parse_verbref(args[1]))
$verb_editor:invoke(argstr, verb);
elseif ($code_utils:parse_propref(dobjstr))
$note_editor:invoke(dobjstr, verb);
elseif ($object_utils:isa(player:my_match_object(dobjstr), $note))
$note_editor:invoke(dobjstr, verb);
elseif (dobjstr)
$note_editor:invoke(dobjstr + ".description", verb);
else
((player in $note_editor.active) ? $note_editor | $verb_editor):invoke(dobjstr, verb);
endif
.
#57:7
"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;
endif
if ((!(from = $code_utils:parse_verbref(dobjstr))) || (!iobjstr))
player:notify(tostr("Usage:  ", verb, " obj:verb to obj:verb"));
player:notify(tostr("        ", verb, " obj:verb to obj"));
player:notify(tostr("        ", verb, " obj:verb to :verb"));
return;
elseif ($command_utils:object_match_failed(fobj = player:my_match_object(from[1]), from[1]))
return;
elseif (iobjstr[1] == ":")
to = {fobj, iobjstr[2..length(iobjstr)]};
elseif (!(to = $code_utils:parse_verbref(iobjstr)))
iobj = player:my_match_object(iobjstr);
if ($command_utils:object_match_failed(iobj, iobjstr))
return;
endif
to = {iobj, from[2]};
elseif ($command_utils:object_match_failed(tobj = player:my_match_object(to[1]), to[1]))
return;
else
to[1] = tobj;
endif
if (0 && "too spammy, but still true")
player:notify("WARNING: @copy is to be used for installing 'templates' of verbs to be later customized.  It is NOT to be used for adding features to a wide variety of other items.  That is what features and inheritance are for.  If you don't know what inheritance is, then you shouldn't be a programmer.  Contact #2 to have your bit removed, or just wait until #2 finds out what you just did.");
endif
from[1] = fobj;
to_firstname = strsub(to[2][1..index(to[2] + " ", " ") - 1], "*", "") || "*";
if ((!(hv = $object_utils:has_verb(to[1], to_firstname))) || (hv[1] != to[1]))
if ((!(info = verb_info(@from))) || (!(vargs = verb_args(@from))))
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)))
player:notify(tostr("Adding ", to[1], ":", to[2], " --> ", e));
return;
endif
endif
code = verb_code(@from);
owner = verb_info(@from)[1];
if (owner != player)
code = {tostr("\"Copied from ", from[1].name, " (", from[1], "):", from[2], (from[1] == owner) ? " " | tostr(" by ", owner.name, " (", owner, ") "), ctime(), "\";"), @code};
endif
if (ERR == typeof(e = set_verb_code(to[1], to_firstname, code)))
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
if (verb == "@copy-move")
e = delete_verb(from[1], from[2]);
if (typeof(e) == ERR)
player:tell("Deleting old verb failed: ", e);
else
player:tell("Removed ", from[1], ":", from[2], ".");
endif
endif
.
#57:8
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]) | ""));
.
#57:9
"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 (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, tostr(loc)) != argspec))
loc = $code_utils:find_verb_named(object, verbname, loc + 1);
endwhile
if (loc < 0)
punt = "...can't find it....";
player:notify("That object has no verb matching that name + args.");
else
verbname = tostr(loc);
endif
else
loc = -1;
endif
"...";
"...get verb info...";
"...";
if (punt || (!(punt = "...reset punt to TRUE...")))
elseif ((info = verb_info(object, verbname)) == E_VERBNF)
player:notify("That object does not have that verb definition.");
elseif (typeof(info) == ERR)
player:notify(tostr(info));
else
punt = 0;
aliases = info[3];
if (loc < 0)
loc = (aliases in (verbs(object) || {})) - 1;
endif
endif
"...";
"...read the code...";
"...";
if (punt)
player:notify(tostr("Now ignoring code for ", args ? args[1] | "nothing in particular", "."));
$command_utils:read_lines();
player:notify("Verb code ignored.");
else
player:notify(tostr("Now programming ", object.name, ":", aliases, "(", (loc < 0) ? "??" | loc, ")."));
lines = $command_utils:read_lines();
if (result = set_verb_code(object, verbname, lines))
player:notify_lines(result);
player:notify(tostr(length(result), " error(s)."));
player:notify("Verb not programmed.");
elseif (typeof(result) == ERR)
player:notify(tostr(result));
player:notify("Verb not programmed.");
else
player:notify("0 errors.");
player:notify("Verb programmed.");
endif
endif
.
#57:10
"Usage: @setenv <environment string>";
"Set your .eval_env property.  Most useful when eval won't work to set it";
"because your .eval_env contains an error.";
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)."));
.
#57:11
set_task_perms(player);
"Let 'em @kill it.";
count = 0;
for i in [0..tonum(max_object())]
if ($command_utils:running_out_of_time())
player:notify(tostr("Counting... [", count, "/", i - 1, "]"));
suspend(0);
endif
if (valid(toobj(i)))
count = count + 1;
endif
endfor
player:notify(tostr("There are ", count, " valid objects out of ", tonum(max_object()) + 1, " allocated object numbers."));
.
#57:12
"@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]..length(argstr)])))
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
.
#57:13
set_task_perms((caller_perms() == $nothing) ? player | caller_perms());
dobj = $string_utils:match_player(dobjstr);
if (!dobjstr)
player:notify(tostr("Usage: ", verb, " <player> [from <start>] [to <end>]"));
return;
elseif ($command_utils:player_match_result(dobj, dobjstr)[1])
return;
endif
dobjwords = $string_utils:words(dobjstr);
if (args[1..length(dobjwords)] == dobjwords)
args = args[length(dobjwords) + 1..length(args)];
endif
if (!(parse_result = $code_utils:_parse_audit_args(@args)))
player:notify(tostr("Usage:  ", verb, " player [from <start>] [to <end>]"));
return;
endif
start = parse_result[1];
end = parse_result[2];
player:notify(tostr("Objects owned by ", valid(dobj) ? dobj.name | dobj, ((" (from #" + tostr(start)) + " to #") + tostr(end), ")", ":"));
count = 0;
printed_anything = 0;
for i in [start..end]
o = toobj(i);
if ($command_utils:running_out_of_time())
if (!printed_anything)
player:notify(tostr(o, " ..."));
endif
suspend(3);
endif
if (valid(o) && (o.owner == dobj))
kids = 0;
for k in (children(o))
if (k.owner != o.owner)
kids = 2;
elseif (kids == 0)
kids = 1;
endif
endfor
if (is_player(o))
c = "P";
elseif ($object_utils:isa(o, $player))
c = "p";
elseif ($object_utils:isa(o, $room))
c = "R";
elseif ($object_utils:isa(o, $exit))
c = "E";
elseif ($object_utils:isa(o, $note))
c = "N";
elseif ($object_utils:isa(o, $container))
c = "C";
elseif ($object_utils:isa(o, $thing))
c = "T";
else
c = " ";
endif
"The verbs() call below might fail, but that's OK";
v = verbs(o);
if (v)
vstr = tostr("[", $string_utils:right(length(v), 3), "] ");
else
vstr = "      ";
endif
if (o.r && o.f)
r = "f";
elseif (o.r)
r = "r";
elseif (o.f)
r = "F";
else
r = " ";
endif
player:notify(tostr(" kK"[kids + 1], r, c, vstr, o.name, " (", o, ")"));
count = count + 1;
printed_anything = 1;
endif
endfor
if (count)
player:notify("");
endif
player:notify(tostr("Total: ", count, " object", (count == 1) ? "." | "s."));
.
#57:14
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:  ", $string_utils:print(objlist[2])));
return;
else
objlist = objlist[2..2];
endif
elseif ((prepstr == "from") && (player.wizard && (n = tonum(toobj(iobjstr)))))
pattern = dobjstr;
objlist = {n};
elseif (args && player.wizard)
pattern = argstr;
objlist = {};
else
player:notify(tostr("Usage:  ", verb, " <pattern> ", player.wizard ? "[in {<objectlist>} | from <number>]" | "in {<objectlist>}"));
return;
endif
player:notify(tostr("Searching for verbs ", @prepstr ? {prepstr, " ", iobjstr, " "} | {}, (verb == "@egrep") ? "matching the pattern " | "containing the string ", $string_utils:print(pattern), " ..."));
player:notify("");
$code_utils:((verb == "@egrep") ? "find_verbs_matching" | "find_verbs_containing")(pattern, @objlist);
.
#57:15
"@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
.
#57:16
"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.";
set_task_perms(caller_perms());
program = args[1];
value = $code_utils: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
ok = this.eval_env = program;
this.eval_ticks = value[2];
if (typeof(ok) == ERR)
return ok;
else
return 1;
endif
.
#57:17
"@clearproperty <obj>.<prop>";
"Set the value of <obj>.<prop> to `clear', making it appear to be the same as the property on its parent.";
set_task_perms(player);
if (!(l = $code_utils:parse_propref(dobjstr)))
player:notify(tostr("Usage:  ", verb, " <object>.<property>"));
elseif ($command_utils:object_match_failed(dobj = player:my_match_object(l[1]), l[1]))
"... bogus object...";
elseif (is_clear_property(dobj, prop = l[2]))
player:notify(tostr("Property ", dobj, ".", prop, " is already clear!"));
elseif ((result = clear_property(dobj, prop)) == E_INVARG)
player:notify(tostr("You can't clear ", dobj, ".", prop, "; none of the ancestors define that property."));
elseif (typeof(result) == ERR)
player:notify(tostr(result));
else
player:notify(tostr("Property ", dobj, ".", prop, " cleared; value is now ", $string_utils:print(dobj.(prop)), "."));
endif
.
#57:18
":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};
.
#57:19
"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 tonum(%#)";
"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..length(verb)], ";")))[1])
val = value[2];
else
player:notify_lines(value[2]);
return;
endif
if (prepstr)
program = strsub(iobjstr + ";", "%#", $string_utils:print(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"}))
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
.
#57:20
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("=> ", $code_utils:error_name(val), "  (", val, ")");
else
return tostr("=> ", $string_utils:print(val));
endif
.
#57:21
"@ps -- Show your queued tasks, along with :suspend_if_needed traces.";
set_task_perms(player);
if (!(tasks = queued_tasks()))
this:tell("You have no queued tasks.");
return E_NONE;
endif
time = time();
maxobj = length(tostr(max_object()));
border = strsub(spaces = $string_utils:space(linelen = player:linelen()), " ", "-");
player:tell(border);
for job in (tasks)
pid = $string_utils:right(job[1], 10);
when = (job[2] < 0) ? "** reading **" | $string_utils:right($time_utils:dhms(job[2] - time), -13);
what = (($string_utils:left(valid(what = job[9]) ? what.name | "** recycled **", -14) + " (") + $string_utils:left(tostr(what), maxobj)) + ")";
if (y = index(vname = strsub(job[7], "*", ""), " "))
vname = vname[1..y - 1];
endif
where = tostr($string_utils:right(job[6], maxobj), ":\"", vname, "\"/", job[8]);
line = tostr(pid, " ", when, " ");
player:tell(((((line + what) + "  ") + where) + spaces)[1..linelen]);
if ((job[6..7] == {$command_utils, "suspend_if_needed"}) && (info = $command_utils:suspend_database_info(job[1])))
job = info[1];
line = ((((((((spaces[1..length(line)] + $string_utils:left(valid(what = job[1]) ? what.name | "** recycled **", -14)) + " (") + $string_utils:left(tostr(what), maxobj)) + ")") + "  ") + $string_utils:right(job[4], maxobj)) + ":\"") + job[2]) + "\"";
player:tell((line + spaces)[1..linelen]);
endif
endfor
player:tell(border);
.
#57:22
":option_packages()";
return {@pass(@args), $prog_options};
.
#57:23
"@sbk <lines> -- views your scrollback buffer";
if (caller != this)
player:notify("You aren't allowed to use that command.");
return E_PERM;
elseif (dobjstr && (!(m = match(dobjstr, "%(%+%|-%)?%([0-9]+%)"))))
player:notify("usage: @sbk [+|-num]");
return E_INVARG;
elseif (!(length = length(sbk = this.sbk)))
player:notify("Your scrollback is empty.");
return E_RANGE;
endif
dir = dobjstr ? substitute("%1", m) || "-" | "+";
dis = min(dobjstr ? tonum(substitute("%2", m)) | length, length - 1);
sbk = (dir == "+") ? sbk[1..dis] | sbk[(length - dis) + 1..length];
player:notify(tostr("===== Scrollback Beg [ ", player:ctime(sbk[1][2]), " ]"));
this.sbk_off = task_id();
for line in (sbk)
player:notify(line[1]);
endfor
this.sbk_off = 0;
player:notify(tostr("===== Scrollback End [ ", player:ctime(sbk[length(sbk)][2]), " ]"));
.
#57:24
":notify(str)";
"Store the string in our scrollback buffer.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
line = args[1];
pass(line);
max = this.sbk_lines;
if ((!max) || (task_id() == this.sbk_off))
return;
endif
callers = callers(1);
while (callers && (callers[1][1..2] == {this, "tell"}))
callers = listdelete(callers, 1);
endwhile
len = length(info = {@this.sbk, {line, time(), argstr, callers}});
if (len <= max)
"we're okay";
elseif ((time() - idle_seconds(this)) <= info[1][2])
"clear this mutha";
info = {};
else
info = info[len - max..len];
endif
this.sbk = info;
.
#57:25
"@resp  <text> -- Who's responsible for the given text.";
"@eresp <text> -- Text is a regular expression.";
if (caller != this)
player:notify("You aren't allowed to use that command.");
return E_PERM;
elseif (!argstr)
player:notify("Search for nothing?  You dope.");
return E_ARGS;
endif
regex = verb[2] == "e";
for line in ($list_utils:reverse(this.sbk))
matched = regex ? match(line[1], argstr) | index(line[1], argstr);
if (!matched)
continue;
endif
this.sbk_off = task_id();
callers = line[4];
player:notify("---- " + line[1]);
player:notify("---- " + player:ctime(line[2]));
player:notify((("---- " + callers[length(callers)][2]) + " ") + line[3]);
player:notify($string_utils:format("%7 %24 %7 %7 %7 %4", "object", "verb", "perms", "definer", "player", "line"));
player:notify("------- ------------------------ ------- ------- ------- ----");
for row in (callers)
player:notify($string_utils:format("%7 %24 %7 %7 %7 %4", @listset(row, ("\"" + row[2]) + "\"", 2)));
endfor
player:notify("------- ------------------------ ------- ------- ------- ----");
objects = {};
for row in (callers)
row[6..6] = {};
row[2..2] = {};
objects = $set_utils:union(objects, row);
endfor
player:notify("---- " + $string_utils:nn(objects));
this.sbk_off = 0;
return line;
endfor
player:notify(tostr("Nothing matches \"", argstr, "\".  Go fish."));
.
#57:26
"@sedit[!] object[.property] with cmd";
"Run 'cmd' on all lines of the given property value, then save the value back to the object.";
"If ! is given, report progress as each line is processed.  Otherwise, no output except success at write-out.";
quiet = verb[length(verb)] != "!";
su = $string_utils;
set_task_perms(player);
dobjstr = index(dobjstr, ".") ? dobjstr | (dobjstr + ".description");
spec = $code_utils:parse_propref(dobjstr);
if (!spec)
player:notify("You must give an object.property pair, or just an object if you want to edit its description.");
return E_INVARG;
endif
object = player:my_match_object(spec[1], e = player:env());
if ($match_utils:object_match_failed(object, spec[1], e))
"...no matching object...";
return object;
endif
propname = spec[2];
dt = typeof(value = object.(propname));
if (value == E_PROPNF)
"...property doesn't exist...";
player:notify(tostr("That object does not have that property definition."));
return E_PROPNF;
elseif (dt == ERR)
"...some other kind of error...";
player:notify(tostr("(", value, ") Command aborted."));
return value;
elseif ((!(dt in {STR, LIST})) || ((dt == LIST) && ((value != {}) && (typeof(value[1]) != STR))))
"...only allow players to edit a string or list a list of strings...";
player:notify(tostr(object.name, "(", object, ").", propname, " is not a string or a list of strings."));
return E_TYPE;
endif
propref = tostr(object.name, " (", object, ").\"", propname, "\"");
cmd = arg = iobjstr;
len = (dt == STR) || length(value);
if ((!cmd) || (cmd == "?"))
player:notify("You must give an editing command:");
player:notify("                                   \"New line.");
player:notify("                                   :append to last line.");
player:notify("   (join list into one string)     join");
player:notify("                                   s/text/replace/");
elseif (cmd == "join")
value = $string_utils:unbreak(value);
quiet || player:notify(tostr("... joining down to ", (typeof(value) == STR) ? 1 | length(value), " line(s)."));
elseif (cmd[1] == "\"")
arg[1..1] = "";
quiet || player:notify(tostr("... adding ", su:print(arg), " as line ", len + 1, "."));
value = $string_utils:lines_to_list(value, cmd);
elseif (cmd[1] == ":")
arg[1..1] = "";
if (dt == STR)
value = line = value + arg;
else
value[len] = line = tostr(value[len], arg);
endif
quiet || player:notify(tostr("... appending line ", len, " to: ", su:print(line)));
elseif (m = match(cmd, "s/%(.+%)/%(.+%)/?"))
s = substitute("%1", m);
r = substitute("%2", m);
if (dt != STR)
for i in [1..length(value)]
line = value[i];
if (index(line, s))
match = match || 1;
value[i] = line = strsub(line, s, r);
quiet || player:notify(tostr("... substituted: ", i, "/", su:print(line), "."));
endif
endfor
elseif (index(value, s))
match = 1;
value = strsub(value, s, r);
quiet || player:notify(tostr("... substituted: 1/", su:print(value), "."));
endif
((!quiet) && (!match)) && player:notify("... No match found.");
else
player:notify(tostr("Unrecognized command \"", cmd, "\"."));
return;
endif
result = object.(propname) = value;
if (typeof(result) == ERR)
player:notify(tostr("(", result, ") Property not written to ", propref, "."));
else
player:notify(tostr("Text written to ", propref, " successfully."));
endif
.
#57:27
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
nargs = length(args);
usage = tostr("Usage:  ", verb, " <object>.<prop-name> [<init_value> [<perms> [<owner>]]]");
if ((nargs < 1) || (!(spec = $code_utils:parse_propref(args[1]))))
player:notify(usage);
return;
endif
object = player:my_match_object(spec[1]);
name = spec[2];
if ($command_utils:object_match_failed(object, spec[1]))
return;
endif
if (nargs < 2)
value = 0;
else
q = $string_utils:prefix_to_value(argstr[$string_utils:word_start(argstr)[2][1]..length(argstr)]);
if (q[1] == 0)
player:notify(tostr("Syntax error in initial value:  ", q[2]));
return;
endif
value = q[2];
args = {args[1], value, @$string_utils:words(q[1])};
nargs = length(args);
endif
perms = (nargs < 3) ? "rc" | args[3];
if (nargs < 4)
if (player.wizard)
player:notify("Being a wizard, you are forced to include a property-owner argument as a security precaution.");
return;
endif
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
e = add_property(object, name, value, {owner, perms});
if (typeof(e) != ERR)
player:notify(tostr("Property added with value ", $string_utils:print(object.(name), 1), "."));
elseif (e != E_INVARG)
player:notify(tostr(e));
elseif ($object_utils:has_property(object, name))
player:notify(tostr("Property ", object, ".", name, " already exists."));
else
for i in [1..length(perms)]
if (!index("rcw", perms[i]))
player:notify(tostr("Unknown permission bit:  ", perms[i]));
return;
endif
endfor
"...the only other possibility...";
player:notify("Property is already defined on one or more descendents.");
player:notify(tostr("Try @check-prop ", args[1]));
endif
.
#57:28
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.eval_subs = this.sbk = {};
this.eval_time = 1;
this.eval_ticks = 0;
this.sbk_lines = 48;
this.sbk_off = 0;
this.sbk = {};
.
#58: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,\"1\",{\"\\\"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().owner.programmer)
return E_PERM;
elseif (svc = set_verb_code(this, "1", code))
lines = {};
for line in (svc)
if ((index(line, "Line ") == 1) && (n = tonum(line[6..(colon = index(line + ":", ":")) - 1])))
lines = {@lines, tostr("Line ", n - 5, line[colon..length(line)])};
else
lines = {@lines, line};
endif
endfor
return {0, lines};
else
set_task_perms(caller_perms());
return {1, this:("1")()};
endif
.
#58:1
"Do not remove this verb!  This is an auxiliary verb for :eval_d().";
.
#58:2
":tonum(number as string) => number";
return match(s = args[1], "^ *[-+]?[0-9]+ *$") ? tonum(s) | E_TYPE;
.
#58:3
":toobj(objectid as string) => objectid";
return match(s = args[1], "^ *#[-+]?[0-9]+ *$") ? toobj(s) | E_TYPE;
.
#58:4
"toerr(n), toerr(\"E_FOO\"), toerr(\"FOO\") => E_FOO.";
if (typeof(s = args[1]) != STR)
n = tonum(s) + 1;
if (n > length(this.error_list))
return 1;
endif
elseif (!(n = (s in this.error_names) || (("E_" + s) in this.error_names)))
return 1;
endif
return this.error_list[n];
.
#58:5
"error_name(E_FOO) => \"E_FOO\"";
return this.error_names[tonum(args[1]) + 1];
.
#58:6
set_task_perms(caller_perms());
object = args[1];
what = {@args, {"props", "verbs"}}[2];
player:notify(tostr("Object ID:  ", object));
player:notify(tostr("Name:       ", object.name));
names = {"Parent", "Location", "Owner"};
vals = {parent(object), object.location, object.owner};
for i in [1..length(vals)]
if (!valid(vals[i]))
val = "*** NONE ***";
else
val = ((vals[i].name + " (") + tostr(vals[i])) + ")";
endif
player:notify(tostr(names[i], ":      "[1..12 - length(names[i])], val));
endfor
line = "Flags:     ";
if (is_player(object))
line = line + " player";
endif
for flag in ({"programmer", "wizard", "r", "w", "f"})
if (object.(flag))
line = (line + " ") + flag;
endif
endfor
player:notify(line);
if ($wiz_utils:is_builder(player) && ((player.wizard || (player == object.owner)) || object.r))
if (("verbs" in what) && verbs(object))
player:notify("Verb definitions:");
for v in (verbs(object))
$command_utils:suspend_if_needed(0);
player:notify(tostr("    ", v));
endfor
endif
if ("props" in what)
if (properties(object))
player:notify("Property definitions:");
for p in (properties(object))
$command_utils:suspend_if_needed(0);
player:notify(tostr("    ", p));
endfor
endif
all_props = $object_utils:all_properties(object);
if (all_props != {})
player:notify("Properties:");
for p in (all_props)
$command_utils:suspend_if_needed(0);
val = object.(p);
if (val == E_PERM)
STR = "(Permission denied.)";
else
STR = $string_utils:from_value_suspended(val, 1, -1);
endif
player:notify(tostr("    ", p, ": ", STR));
endfor
endif
endif
elseif ($wiz_utils:is_builder(player))
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
.
#58:7
set_task_perms(caller_perms());
object = args[1];
pname = args[2];
if (pname in this.builtin_props)
player:notify(tostr(object, ".", pname));
player:notify("Built-in property.");
else
info = property_info(object, pname);
if (typeof(info) == ERR)
player:notify(tostr(info));
return;
endif
owner = info[1];
perms = info[2];
player:notify(tostr(object, ".", pname));
player:notify(tostr("Owner:        ", valid(owner) ? tostr(owner.name, " (", owner, ")") | "*** NONE ***"));
player:notify(tostr("Permissions:  ", perms));
endif
player:notify(tostr("Value:        ", $string_utils:print(object.(pname))));
.
#58:8
set_task_perms(caller_perms());
object = args[1];
vname = args[2];
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
info = verb_info(object, vname);
if (typeof(info) == ERR)
player:notify(tostr(info));
return;
endif
owner = info[1];
perms = info[2];
names = info[3];
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]));
.
#58:9
if (args[4..5] == {"none", "this"})
return 0;
endif
thisobj = args[1];
verb = args[2];
adobj = args[3];
aprep = args[4];
aiobj = args[5];
prep_part = (aprep == "any") ? "to" | this:short_prep(aprep);
".........`any' => `to' (arbitrary),... `none' => empty string...";
if ((adobj == "this") && (dobj == thisobj))
dobj_part = dobjstr;
iobj_part = ((!prep_part) || (aiobj == "none")) ? "" | ((aiobj == "this") ? dobjstr | iobjstr);
elseif ((aiobj == "this") && (iobj == thisobj))
dobj_part = (adobj == "any") ? dobjstr | ((adobj == "this") ? iobjstr | "");
iobj_part = iobjstr;
elseif (!("this" in args[3..5]))
dobj_part = (adobj == "any") ? dobjstr | "";
iobj_part = (prep_part && (aiobj == "any")) ? iobjstr | "";
else
return 0;
endif
return tostr(verb, dobj_part ? " " + dobj_part | "", prep_part ? " " + prep_part | "", iobj_part ? " " + iobj_part | "");
.
#58:10
"returns the permissions of the current verb (either the owner or the result of the most recent set_task_perms()).";
return caller_perms();
.
#58:11
"returns the object where the current verb is defined.";
return callers()[1][4];
.
#58:12
":verb_documentation([object,verbname]) => documentation at beginning of verb code, if any";
"default is the calling verb";
cp = caller_perms();
set_task_perms(cp.programmer ? cp | $no_one);
if (args)
object = args[1];
vname = args[2];
else
c = callers()[1];
object = c[4];
vname = c[2];
endif
if (typeof(code = verb_code(object, vname)) == ERR)
return tostr(code);
else
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;
endif
.
#58:13
":set_verb_documentation(object,verbname,text)";
"  changes documentation at beginning of verb code";
"  text is either a string or a list of strings";
"  returns a non-1 value if anything bad happens...";
set_task_perms(caller_perms());
if (typeof(code = verb_code(object = args[1], vname = args[2])) == ERR)
return code;
elseif (typeof(vd = $code_utils:verb_documentation(object, vname)) == ERR)
return vd;
elseif (!(typeof(text = args[3]) 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..length(code)]}))) || svc)
"... this shouldn't happen.  I'm not setting this code -d just yet...";
return svc;
else
return 1;
endif
endif
.
#58:14
"$code_utils:parse_propref(string)";
"Parses string as a MOO-code property reference, returning {object-string, prop-name-string} for a successful parse and false otherwise.  It always returns the right object-string to pass to, for example, this-room:match_object.";
s = args[1];
if (dot = index(s, "."))
object = s[1..dot - 1];
prop = s[dot + 1..length(s)];
if ((object == "") || (prop == ""))
return 0;
elseif (object[1] == "$")
object = #0.(object[2..length(object)]);
if (typeof(object) != OBJ)
return 0;
endif
object = tostr(object);
endif
elseif (index(s, "$") == 1)
object = "#0";
prop = s[2..length(s)];
else
return 0;
endif
return {object, prop};
.
#58:15
"$code_utils:parse_verbref(string)";
"Parses string as a MOO-code verb reference, returning {object-string, verb-name-string} for a successful parse and false otherwise.  It always returns the right object-string to pass to, for example, this-room:match_object().";
s = args[1];
if (!s)
return 0;
elseif (colon = index(s, ":"))
object = s[1..colon - 1];
verbname = s[colon + 1..$];
if (!(object && verbname))
return 0;
endif
return {object, verbname};
elseif (((s[1] == "$") && (!index(s, "."))) && (typeof(`#0.(s[2..$]) ! E_PROPNF') != OBJ))
"don't make it a verbref if it's a $propref";
return {"#0", s[2..$]};
else
return 0;
endif
.
#58:16
":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};
.
#58:17
if (server_version() != this._version)
this:_fix_preps();
endif
return this.prepositions;
.
#58:18
":short_prep(p) => shortest preposition equivalent to p";
"p may be a single word or one of the strings returned by verb_args().";
if (server_version() != this._version)
this:_fix_preps();
endif
word = args[1];
word = word[1..index(word + "/", "/") - 1];
if (p = word in this._other_preps)
return this._short_preps[this._other_preps_n[p]];
elseif (word in this._short_preps)
return word;
else
return "";
endif
.
#58:19
if (server_version() != this._version)
this:_fix_preps();
endif
prep = args[1];
if (p = prep in this._short_preps)
return this.prepositions[p];
elseif (p = prep in this._other_preps)
return this.prepositions[this._other_preps_n[p]];
else
return "";
endif
.
#58:20
":get_prep(@args) extracts the prepositional phrase from the front of args, returning a list consisting of the preposition (or \"\", if none) followed by the unused args.";
":get_prep(\"in\",\"front\",\"of\",...) => {\"in front of\",...}";
":get_prep(\"inside\",...)          => {\"inside\",...}";
":get_prep(\"frabulous\",...}       => {\"\", \"frabulous\",...}";
prep = "";
allpreps = {@this._short_preps, @this._other_preps};
rest = 1;
for i in [1..length(args)]
try_ = (i == 1) ? args[1] | tostr(try_, " ", args[i]);
if (try_ in allpreps)
prep = try_;
rest = i + 1;
endif
if (!(try_ in this._multi_preps))
return {prep, @args[rest..length(args)]};
endif
endfor
return {prep, @args[rest..length(args)]};
.
#58:21
":_fix_preps() updates the properties on this having to do with prepositions.";
"_fix_preps should be called whenever we detect that a new server version has been installed.";
orig_args = verb_args(this, verb);
multis = nothers = others = shorts = longs = {};
i = 0;
while (typeof(set_verb_args(this, verb, {"this", tostr(i), "this"})) != ERR)
l = verb_args(this, verb)[2];
all = $string_utils:explode(l, "/");
s = all[1];
for p in (listdelete(all, 1))
if (length(p) <= length(s))
s = p;
endif
endfor
for p in (all)
while (j = rindex(p, " "))
multis = {p = p[1..j - 1], @multis};
endwhile
endfor
longs = {@longs, l};
shorts = {@shorts, s};
others = {@others, @setremove(all, s)};
nothers = {@nothers, @$list_utils:make(length(all) - 1, length(shorts))};
i = i + 1;
endwhile
set_verb_args(this, verb, orig_args);
this.prepositions = longs;
this._short_preps = shorts;
this._other_preps = others;
this._other_preps_n = nothers;
this._multi_preps = multis;
this._version = server_version();
return;
.
#58:22
":find_verb_named(object,name[,n])";
"  returns the *number* of the first verb on object matching the given name.";
"  optional argument n, if given, starts the search with verb n,";
"  causing the first n verbs (0..n-1) to be ignored.";
"  -1 is returned if no verb is found.";
"  This routine does not find inherited verbs.";
object = args[1];
name = args[2];
for i in [{@args, 0}[3]..length(verbs(object)) - 1]
verbinfo = verb_info(object, tostr(i));
if (this:verbname_match(verbinfo[3], name))
return i;
endif
endfor
return -1;
.
#58:23
":find_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 = args[1];
name = args[2];
last = {@args, -1}[3];
if (last < 0)
last = length(verbs(object));
endif
for i in [1..last]
verbinfo = verb_info(object, tostr(last - i));
if (this:verbname_match(verbinfo[3], name))
return last - i;
endif
endfor
return -1;
.
#58:24
":find_callable_verb_named(object,name[,n])";
"  returns the *number* of the first verb on object that matches the given";
"  name and has the x flag set.";
"  optional argument n, if given, starts the search with verb n,";
"  causing the first n verbs (0..n-1) to be ignored.";
"  -1 is returned if no verb is found.";
"  This routine does not find inherited verbs.";
object = args[1];
name = args[2];
for i in [{@args, 0}[3]..length(verbs(object)) - 1]
verbinfo = verb_info(object, tostr(i));
if (index(verbinfo[2], "x") && this:verbname_match(verbinfo[3], name))
return i;
endif
endfor
return -1;
.
#58: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 = args[1];
where = {@args, 0}[2];
count = 0;
if (typeof(where) == NUM)
for i in [where..tonum(max_object())]
if (valid(o = toobj(i)))
count = count + this:_find_verbs_containing(pattern, o);
endif
if ($command_utils:running_out_of_time())
player:notify(tostr("...", o));
suspend(0);
endif
endfor
elseif (typeof(where) == LIST)
for o in (where)
count = count + this:_find_verbs_containing(pattern, o);
endfor
else
"...typeof(where) == OBJ...";
count = this:_find_verbs_containing(pattern, where);
endif
player:notify("");
player:notify(tostr("Total: ", count, " verbs."));
.
#58: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 = args[1];
count = 0;
verbs = verbs(o = args[2]);
if (typeof(verbs) == ERR)
player:notify(tostr("verbs(", o, ") => ", verbs));
return 0;
endif
for vnum in [0..length(verbs) - 1]
if (l = this:_grep_verb_code(pattern, o, vname = tostr(vnum)))
owner = verb_info(o, vname)[1];
player:notify(tostr(o, ":", verbs[vnum + 1], " [", 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;
.
#58: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 = args[1];
where = {@args, 0}[2];
count = 0;
if (typeof(where) == NUM)
for i in [where..tonum(max_object())]
if (valid(o = toobj(i)))
count = count + this:_find_verbs_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, args[2]);
endif
player:notify("");
player:notify(tostr("Total: ", count, " verbs."));
.
#58:28
":_find_verbs_matching(regexp,object)";
"number of verbs in object with code having a line matching regexp";
"prints verbname and offending line to player";
set_task_perms(caller_perms());
pattern = args[1];
count = 0;
verbs = verbs(o = args[2]);
if (typeof(verbs) == ERR)
player:notify(tostr("verbs(", o, ")  => ", verbs));
return 0;
endif
for vnum in [0..length(verbs) - 1]
if (l = this:_egrep_verb_code(pattern, o, vname = tostr(vnum)))
owner = verb_info(o, vname)[1];
player:notify(tostr(o, ":", verbs[vnum + 1], " [", 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;
.
#58: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];
for line in (vc = verb_code(@listdelete(args, 1)))
if (index(line, pattern))
return line;
endif
endfor
return 0;
.
#58:30
":_egrep_verb_code(regexp,object,verbname) => 0 or line number";
"  returns line number of first line matching regexp in object:verbname code";
set_task_perms(caller_perms());
pattern = args[1];
for line in (vc = verb_code(@listdelete(args, 1)))
if (match(line, pattern))
return line;
endif
endfor
return 0;
.
#58:31
"Parse [from <start>] [to <end>] [for <name>].";
"Takes a series of strings, most likely @args with dobjstr removed.";
"Returns a list {NUM start, NUM end, STR name}, or {} if there is an error.";
fail = length(args) % 2;
start = 0;
end = tonum(max_object());
match = "";
while (args && (!fail))
prep = args[1];
if (prep == "from")
if ((start = player.location:match_object(args[2])) >= #0)
start = tonum(start);
else
start = tonum(args[2]);
endif
elseif (prep == "to")
if ((end = player.location:match_object(args[2])) >= #0)
end = tonum(end);
else
end = tonum(args[2]);
endif
elseif (prep == "for")
match = args[2];
else
fail = 1;
endif
args = args[3..length(args)];
endwhile
return fail ? {} | {start, end, match};
.
#58:32
olist = {player, @$object_utils:ancestors(player)};
if (valid(player.location))
olist = {@olist, player.location, @$object_utils:ancestors(player.location)};
endif
dbs = {};
for o in (olist)
h = o.help;
if (typeof(h) == OBJ)
h = {h};
endif
for db in (h)
if ((typeof(db) == OBJ) && (valid(db) && (!(db in dbs))))
dbs = {@dbs, db};
endif
endfor
endfor
return {@dbs, $help};
.
#58: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 = args[1];
dblist = args[2];
topics = {};
help = 1;
for db in (dblist)
if ({what} == (ts = db:find_topics(what)))
return {db, ts[1]};
elseif (ts && (typeof(ts) == LIST))
if (help)
help = db;
endif
for t in (ts)
topics = setadd(topics, t);
endfor
endif
endfor
if (length(topics) > 1)
return {$ambiguous_match, topics};
elseif (topics)
return {help, topics[1]};
else
return {};
endif
.
#58: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];
for p in (properties(#0))
if (#0.(p) == object)
return "$" + p;
endif
endfor
return tostr(object);
.
#58:35
"$code_utils:substitute(string,subs) => new line";
"Subs are a list of lists, {{\"target\",\"sub\"},{...}...}";
"Substitutes targets for subs in a delimited string fashion, avoiding substituting anything inside quotes, e.g. player:tell(\"don't sub here!\")";
s = args[1];
subs = args[2];
lets = "abcdefghijklmnopqrstuvwxyz0123456789";
for x in (subs)
len = length(sub = x[1]);
delimited = index(lets, sub[1]) && index(lets, sub[len]);
prefix = "";
while (i = index(s, sub))
prefix = prefix + s[1..i - 1];
if ((((prefix == "") || ((!delimited) || (!index(lets, prefix[length(prefix)])))) && ((!delimited) || (((i + len) > length(s)) || (!index(lets, s[i + len]))))) && (!this:inside_quotes(prefix)))
prefix = prefix + x[2];
else
prefix = prefix + s[i..(i + len) - 1];
endif
s = s[i + len..length(s)];
endwhile
s = prefix + s;
endfor
return s;
.
#58:36
"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;
.
#58:37
"verb_or_property(<obj>, <name> [, @<args>])";
"Looks for a callable verb or property named <name> on <obj>.";
"If <obj> has a callable verb named <name> then return <obj>:(<name>)(@<args>).";
"If <obj> has a property named <name> then return <obj>.(<name>).";
"Otherwise return E_PROPNF.";
"N.B.: a verb returning E_VERBNF will act like an undefined verb.";
set_task_perms(caller_perms());
return ((val = args[1]:(args[2])(@args[3..length(args)])) == E_VERBNF) ? args[1].(args[2]) | val;
.
#58:38
set_task_perms($no_one);
id = args[1];
return (id == task_id()) || (E_PERM == kill_task(id));
.
#58:39
if (a = $list_utils:assoc(args[1], queued_tasks()))
return a[5];
else
return E_INVARG;
endif
.
#58:40
":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
.
#58:41
":verbname_match(pattern, string)";
"=> TRUE iff 'string' would match a verb defined with name 'pattern'.";
{pattern, s} = args;
result = `call_function("match_verbname", pattern, s) ! E_INVARG';
if (result != E_INVARG)
return result;
endif
verblist = (" " + args[1]) + " ";
if (index(verblist, (" " + (name = args[2])) + " ") && (!(index(name, "*") || index(name, " "))))
"Note that if name has a * or a space in it, then it can only match one of the * verbnames";
return 1;
else
namelen = length(name);
while (star = index(verblist, "*"))
vstart = rindex(verblist[1..star], " ") + 1;
vlast = (vstart + index(verblist[vstart..length(verblist)], " ")) - 2;
if ((namelen >= (star - vstart)) && ((!(v = strsub(verblist[vstart..vlast], "*", ""))) || (index(v, (verblist[vlast] == "*") ? name[1..min(namelen, length(v))] | name) == 1)))
return 1;
endif
verblist = verblist[vlast + 1..length(verblist)];
endwhile
endif
return 0;
.
#58: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_connect_time is shown.  For players in the second list, last_connect_time is shown, no matter whether the player is logged in.";
idles = itimes = offs = otimes = {};
for p in (args[2])
if (!valid(p))
caller:notify(tostr(p, " <invalid>"));
elseif (typeof(t = p.last_connect_time) == NUM)
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_connect_time."));
else
caller:notify(tostr(p.name, " (", p, ") is not a player."));
endif
endfor
for p in (args[1])
if (p in offs)
elseif (!valid(p))
caller: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_connect_time) == NUM)
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_connect_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 = {"Player name", @idles ? {"Connected", "Idle time"} | {"Last login", ""}, "Location"};
total_width = caller:linelen() || 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, length(p.name))], " (", 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
locations = {@locations, wlm};
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(length(tell1), total_width)]);
caller: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[2]), su:from_seconds(lst[1]), locations[i]};
else
lct = offs[i - ilen][3].last_connect_time;
ctime = caller:ctime(lct) || ctime(lct);
l = {names[i], (lct <= time()) ? ctime | "Never", "", locations[i]};
if ((i == (ilen + 1)) && idles)
caller:notify(su:left(su:space(before[2]), before[4] - 2, "-"));
endif
endif
tell1 = l[1];
for j in [2..4]
tell1 = su:left(tell1, before[j]) + l[j];
endfor
caller:notify(tell1[1..min(length(tell1), total_width)]);
if ($command_utils:running_out_of_time())
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, " player", active_str, " been active recently."));
return total;
.
#58:43
":_egrep_verb_code(regexp,object,verbname) => list of lines number";
"  returns list of all lines matching regexp in object:verbname code";
set_task_perms(caller_perms());
pattern = args[1];
lines = {};
for line in (vc = verb_code(@args[2..3], 1, 0))
if (match(line, pattern))
lines = {@lines, line};
endif
endfor
return lines;
.
#58: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 = args[1];
lines = {};
for line in (vc = verb_code(@args[2..3]))
if (index(line, pattern))
lines = {@lines, line};
endif
endfor
return lines;
.
#58:46
":verb_usage([object,verbname]) => usage string at beginning of verb code, if any";
"default is the calling verb";
set_task_perms(caller_perms());
if (args)
object = args[1];
vname = args[2];
else
c = callers()[1];
object = c[4];
vname = c[2];
endif
if (typeof(code = verb_code(object, vname)) == 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
.
#58:47
":args_with_quotes(cmdline_args)";
"Return the arglist with argstr quotes intact.";
oargstr = argstr;
oargs = nargs = args[1];
while (m = match(oargstr, "\"%([^\"]*%)\""))
if ((i = substitute("%1", m) in oargs) && (!match(oargs[i], "^\".*\"$")))
nargs[i] = tostr("\"", oargs[i], "\"");
endif
oargstr = oargstr[1..m[3][1][1] - 2] + oargstr[m[3][1][2] + 2..length(oargstr)];
endwhile
return nargs;
.
#58:48
":verb_id(\"v*erb name\")";
"Return a version of the given verb name that can be fed to and understood by the verb_info function.";
name = args[1];
if (i = index(name, " "))
name = name[1..i - 1];
endif
return strsub(name, "*", "");
.
#58:49
":call_verb(OBJ object, STR verbname[, LIST args])";
"Call the given verb.  Return E_VERBNF if it does not exist or cannot be called, else return the result of the call.";
set_task_perms(caller_perms());
return args[1]:(args[2])(@args[3] || {});
.
#58:50
":get_prop(OBJ object, STR propname)";
"Get the value of the given property.  Return usual errors, !d.";
set_task_perms(caller_perms());
return args[1].(args[2]);
.
#58:51
":notify(user, str)";
set_task_perms(caller_perms());
user = args[1];
if ($recycler:valid(user))
user:notify(args[2]);
else
notify(@args);
endif
.
#58:52
":is_secure_verbcall(obj, vrb, owner)";
"Search all parents of OBJ, returning false if all VRB verbs are now owned by a wizard.  Return false also if OBJ:VRB() does not exist.";
object = args[1];
if (valid(object))
vrb = args[2];
while ((object != #-1) && ((!(i = verb_info(object, vrb))) || i[1].wizard))
f = f || (i && 1);
object = parent(object);
endwhile
return f && (object == #-1);
else
return 0;
endif
.
#58:53
":has_message(obj, str)";
"See $root:has_message for details.  This verb is used in absence of non-overridable methods to avoid security problems.";
"Returns {OBJ prop_location, STR prop_name}, or some false value.";
object = args[1];
string = args[2];
if (object == this)
m = $object_utils:has_message(this, string);
return m ? {this, m} | E_PROPNF;
elseif (m = $object_utils:has_message(object, string))
return {object, m};
elseif (this:is_secure_verbcall(object, "has_message"))
return object:has_message(string);
else
return 0;
endif
.
#58:54
":display_line_verb(object, verbname[, shortprep[, blankargs]])";
{where, q, ?shortprep = 0, ?blankargs = #-1} = args;
set_task_perms(caller_perms());
short = strsub(y = index(q, " ") ? q[1..y - 1] | q, "*", "");
inf = `verb_info(where, short) ! E_PERM, E_VERBNF';
if ((typeof(inf) == LIST) || (inf == E_PERM))
name = (typeof(inf) == LIST) ? inf[3] | q;
name = index(name, " ") ? ("\"" + name) + "\"" | name;
line = $string_utils:left(tostr($string_utils:right(tostr(where), 6), ":", name, " "), 32);
if (inf == E_PERM)
line = line + "   ** unreadable **";
else
owner_column = $string_utils:nn(inf[1]);
line = $string_utils:left(tostr(line, owner_column), 53) + ((i = inf[2] in {"x", "xd", "d", "rd"}) ? {" x", " xd", "  d", "r d"}[i] | inf[2]);
vargs = verb_args(where, short);
if (vargs != blankargs)
if (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
return line;
elseif (inf == E_VERBNF)
return tostr("  ** no such verb, \"", short, "\" **");
endif
.
#58:55
":display_line_prop(object, propname[, max_value_width[, max_list_depth[, truncate_owner_names]]])";
{it, q, ?max = 999, ?depth = 0, ?truncate_owner_names = 0} = args;
set_task_perms(caller_perms());
inf = `property_info(it, q) ! E_PROPNF, E_PERM';
su = $string_utils;
if (inf == E_PROPNF)
if (q in $code_utils.builtin_props)
return tostr(su:left("," + q, 25), "Built in property ", (length(y = $string_utils:from_value(it.(q), 1, depth)) > max) ? y[1..max] + ".." | y);
else
return tostr("  ** property not found, \"", q, "\" **");
endif
endif
if (`q in properties(it) ! E_PERM')
pname = su:left(tostr(".", q, " "), 25);
elseif (`is_clear_property(it, q) ! E_PERM')
pname = su:left(tostr(" ", q, " "), 25);
else
pname = su:left(tostr(",", q, " "), 25);
endif
if (inf == E_PERM)
return tostr(pname, "   ** unreadable **");
endif
oname = inf[1].name;
truncate_owner_names && ((length(oname) > 12) && (oname = oname[1..12]));
oname = tostr(oname, " (", inf[1], ") ");
if (`inf[2][1] ! E_RANGE' != "r")
inf[2][1..0] = " ";
endif
if (`inf[2][2] ! E_RANGE' != "w")
inf[2][2..1] = " ";
endif
v = it.(q);
y = su:abbreviated_value(v, max, depth);
if (length(y) > max)
y = tostr(this:type_name(typeof(v)), "; ", su:to_bytes($quota_utils:value_bytes(v)));
endif
return su:left(tostr(su:left(tostr(pname, oname), 47), inf[2], " "), 54) + y;
.
#58:56
":type_name(INT)";
"Return a string representation of the builtin type variable for the given type.";
return {"INT", "OBJ", "STR", "ERR", "LIST", "", "", "", "", "FLOAT"}[args[1] + 1];
.
#58:57
"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:0
return $player.ownership_quota;
.
#59:1
return $prog.ownership_quota;
.
#59:2
text = pass(@args);
object = player:my_match_object(what = args[1]);
object = valid(object) ? object | $match_utils:literal_object(what);
if (text != E_PROPNF)
return text;
endif
if (ohelp = object:help_msg() || object.help_msg)
"...geez. life is simple if we set -d...";
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
.
#59:3
topiclist = pass(@args);
if (topiclist || (!args))
return topiclist;
endif
what = args[1];
if (valid(o = $match_utils:literal_object(what)))
return {what};
elseif (valid(o = player:my_match_object(what)))
return {what};
else
return {};
endif
.
#59: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;
.
#59:5
hdr = "Available Help Indices";
text = {"", hdr, $string_utils:space(hdr, "-")};
for db in ($code_utils:help_db_list())
if ($object_utils:isa(db, $generic_help))
for p in (properties(db))
if ((h = db.(p)) && ("*index*" in h))
text = {@text, tostr($string_utils:left(p, 14), " -- ", h[2] || db.name, " (", db, ")")};
endif
endfor
endif
endfor
for p in (properties(this))
if (this.(p) && (this.(p)[1] == "*full_index*"))
return {@text, "", tostr($string_utils:left(p, 14), " -- ", "EVERYTHING")};
endif
endfor
return text;
.
#59:6
wizzes = {#2, @$list_utils:randomly_permute(setremove($object_utils:leaves($wiz), #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) ? (" (a.k.a. " + wpi) + ")" | "")};
endfor
return slist;
.
#59:7
if (((text = pass(@args)) != E_PROPNF) || ((!valid(object = player:my_match_object(what = args[1]))) || (!$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
.
#59:8
"Automated help on trustable actions.";
help = {"A complete list of trustable actions:", ""};
for kid in (children($trust_db))
als = setremove(kid.aliases, kid.name);
help = {@help, tostr("-- ", kid.name, als ? (" (aka " + $string_utils:english_list(als)) + ")" | ""), "", @kid:description(), ""};
endfor
help = {@help, "Use '@trust <anyone> to <action> Me' to trust someone to perform one of the above actions."};
return help;
.
#60:0
raw = ctime(this.last_news_time);
"         111111111122222";
"123456789012345678901234";
"Fri Nov 30 14:31:21 1990";
date = (raw[1..10] + ",") + raw[20..24];
return strsub(this.description, "%d", date);
.
#60:1
return pass(@args) || (args[1] in $list_utils:map_prop($object_utils:descendants($wiz), "mail_identity"));
.
#60:2
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
seq = args[1];
this.current_news_going = $seq_utils:intersection(this.current_news, seq);
this.current_news = $seq_utils:contract(this.current_news, seq);
return $mail_agent:(verb)(@args);
.
#60:3
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
seq = $mail_agent:(verb)(@args);
this.current_news = $seq_utils:union(this.current_news_going, $seq_utils:expand(this.current_news, seq));
this.current_news_going = {};
return seq;
.
#60:4
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
this.current_news_going = {};
return $mail_agent:(verb)(@args);
.
#60:5
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
this.current_news = new = args[1];
if (new)
newlast = $seq_utils:last(new);
newlasttime = this:messages_in_seq(newlast)[2][1];
if (newlasttime > this.last_news_time)
"... only notify people if there exists a genuinely new item...";
this.last_news_time = newlasttime;
this:touch();
endif
else
"...flush everything...";
this.last_news_time = 0;
endif
.
#60:6
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
return this:set_current_news($seq_utils:union(this.current_news, args[1]));
.
#60:7
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
return this:set_current_news($seq_utils:intersection(this.current_news, $seq_utils:complement(args[1])));
.
#60:8
":news_display_seq_full(msg_seq) => {cur, last-read-date}";
"Display the given msg_seq as a collection of news items";
set_task_perms(caller_perms());
desc = this:description();
player:notify((typeof(desc) == LIST) ? desc[1] | desc);
player:notify("");
msgs = this:messages_in_seq(args[1]);
for i in [-(n = length(msgs))..-1]
x = msgs[-i];
player:notify_lines(this:to_text(@x[2]));
player:notify("");
$command_utils:suspend_if_needed(0);
endfor
player:notify("(end)");
return {msgs[n][1], msgs[n][2][1]};
.
#60:9
if (!this:ok_write(caller, valid(who = caller_perms()) ? who | player))
player:notify("Permission denied.");
return;
endif
fork (0)
for p in (connected_players())
$command_utils:suspend_if_needed(0);
if ((p:get_current_message(this) || {0, 0})[2] < this.last_news_time)
p:notify("There's a new edition of the newspaper.  Type 'news new' to see the new article(s).");
endif
endfor
endfork
.
#60:10
if ((caller_perms() != #-1) && (caller_perms() != player))
$error:raise(E_PERM);
endif
set_task_perms(player);
if (!this:is_writable_by(player))
player:notify("You can't write the news.");
elseif (typeof(result = this:add_news(args[1..(prepstr in args) - 1], player:get_current_message(this) || {0, 0})) == STR)
player:notify(result);
else
new = this.current_news;
if (new)
player:notify("Current newspaper set.");
this:display_seq_headers(new);
else
player:notify("Current newspaper is now empty.");
endif
endif
.
#60:11
if ((caller_perms() != #-1) && (caller_perms() != player))
$error:raise(E_PERM);
endif
set_task_perms(player);
if (!this:is_writable_by(player))
player:notify("You can't write the news.");
elseif (typeof(result = this:rm_news(args[1..(prepstr in args) - 1], player:get_current_message(this) || {0, 0})) == STR)
player:notify(result);
else
new = this.current_news;
if (new)
player:notify("Current newspaper set.");
this:display_seq_headers(new);
else
player:notify("Current newspaper is now empty.");
endif
endif
.
#60:12
set_task_perms(player);
if (!this:is_writable_by(player))
player:notify("You can't write the news.");
elseif (typeof(seq = this:_parse(strings = args[(prepstr in args) + 1..length(args)], @player:get_current_message(this) || {0, 0})) == STR)
player:notify(seq);
else
old = this.current_news;
if (old == seq)
player:notify("No change.");
else
this:set_current_news(seq);
if (seq)
player:notify("Current newspaper set.");
this:display_seq_headers(seq);
else
player:notify("Current newspaper is now empty.");
endif
endif
endif
.
#60:13
if (!(strings = args[1]))
return "You need to specify a message sequence";
elseif (typeof(pms = this:parse_message_seq(@args)) == STR)
return $string_utils:substitute(pms, {{"%f", "The news"}, {"%<has>", "has"}, {"%%", "%"}});
elseif (typeof(pms) != LIST)
return tostr(pms);
elseif (length(pms) > 1)
return tostr("I don't understand `", pms[2], "'.");
elseif (!(seq = pms[1]))
return tostr("The News (", this, ") has no `", $string_utils:from_list(pms[2][1..used], " "), "' messages.");
else
return seq;
endif
.
#60:14
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.description = "It's the current issue of the News, dated %d.";
this.mail_forward = {};
this.moderated = 1;
this.last_news_time = 0;
.
#60:15
if (!this:ok_write(caller, caller_perms()))
$error:raise(E_PERM);
endif
specs = args[1];
cur = {@args, {0, 0}}[2];
seq = this:_parse(specs, @cur);
if (typeof(seq) == STR)
return seq;
endif
old = this.current_news;
new = $seq_utils:union(old, seq);
if (old == new)
return "Those messages are already in the news.";
endif
this:set_current_news(new);
return 1;
.
#60:16
if (!this:ok_write(caller, caller_perms()))
$error:raise(E_PERM);
endif
specs = args[1];
cur = {@args, {0, 0}}[2];
seq = this:_parse(specs, @cur);
if (typeof(seq) == STR)
return seq;
endif
old = this.current_news;
new = $seq_utils:intersection(old, $seq_utils:complement(seq));
if (old == new)
return "Those messages were not in the news.";
endif
this:set_current_news(new);
return 1;
.
#60:17
player:notify("The following articles are currently in the newspaper:");
this:display_seq_headers(this.current_news);
.
#60:18
set_task_perms(player);
if (this:is_writable_by(player))
this:set_current_news({});
player:notify("Current newspaper is now empty.");
else
player:notify("You can't write the news.");
endif
.
#60:19
":to_text(@msg) -> Message in text form.";
by = args[2][1..index(args[2], "(") - 2];
subject = (args[4] == " ") ? "FLASH" | args[4];
text = args[("" in {@args, ""}) + 1..length(args)];
linelen = player:linelen() || 79;
header = tostr("-----[ ", player:ctime(args[1]) || ctime(args[1]), " ] \"", subject, "\" by ", by);
return {header, "", @text};
.
#60:20
set_task_perms(caller_perms());
if ((player:get_current_message(this) || {0, 0})[2] < this.last_news_time)
player:tell("There is new news.  Type `news` to read all news or `news new` to read just the new news.  Whether you read it or not, you are subject to its announcements.  Ignorance is no excuse.");
endif
.
#62:0
"Return a toad (child of #1, owned by $hacker) from this.contents.  Move it to #-1.  Recreate as a child of args[1], or of #1 if no args are given.  Chown to caller_perms() or args[2] if present.";
what = args ? args[1] | #1;
who = (length(args) == 2) ? args[2] | caller_perms();
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;
.
#62: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))
return $error:raise(E_PERM);
elseif (is_player(item))
return $error: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);
.
#62:2
e = set_task_perms(caller_perms());
if (typeof(e) == ERR)
return e;
else
val = this:_recreate(@args);
return (val == E_NONE) ? $quota_utils:bi_create(@args) | val;
endif
.
#62: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
.
#62: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
.
#62:5
dobj = valid(dobj) ? dobj | player:my_match_object(dobjstr);
if (!valid(dobj))
dobj = (n = tonum(dobjstr)) ? toobj(n) | #-1;
endif
if (!$wiz_utils:is_builder(player))
player:tell("Only programmers can request recycled objects.");
elseif (!$object_utils:isa(dobj, $garbage))
player:tell("Only children of $garbage may be requested.");
elseif (!(dobj in this.contents))
player:tell("Couldn't find ", dobj, " in ", this.name, ".");
else
if (typeof(emsg = this:setup_toad(dobj, player, #1)) != 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
.
#62:6
"this:setup_toad(objnum,new_owner,parent)";
"Called by :_create and :request.";
if (caller != this)
return E_PERM;
endif
potential = args[1];
who = args[2];
what = args[3];
if (!$quota_utils:creation_permitted(who))
return E_QUOTA;
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
.
#62:7
if (caller == this)
this.orphans = setadd(this.orphans, args[1]);
endif
.
#62:8
if (caller == this)
this.orphans = setremove(this.orphans, args[1]);
endif
.
#62:9
"Usage:  valid(object)";
"True if object is valid and not $garbage.";
return valid(args[1]) && (parent(args[1]) != $garbage);
.
#62:10
this.orphans = {};
this.history = {};
pass();
.
#62:11
":trusts_to_create_for(parent, for_whom, caller_perms)";
"Allow programmers to create items for non-privileged players, assuming the prospective parent is either fertile or writable by the programmer.";
{parent, for_whom, cp} = args;
if ($perm_utils:controls(cp, for_whom))
return 1;
elseif ((!parent.f) && (!parent:is_writable_by(cp)))
return 0;
elseif ($wiz_utils:is_builder(for_whom))
return 0;
elseif (cp.programmer)
return 1;
endif
return 0;
.
#62:12
":_create_for(parent, for_whom)";
{parent, for_whom, ?spawn = 1} = args;
if (!this:trusts_to_create_for(parent, for_whom, caller_perms()))
raise(E_PERM);
endif
new = this:_create(parent, for_whom);
if ((!spawn) || (typeof(new) != OBJ))
return new;
endif
if (nn = $code_utils:verb_or_property(parent, "spawned_name"))
new:set_name(nn);
else
new:set_name(parent.name);
endif
if (na = $code_utils:verb_or_property(parent, "spawned_aliases"))
new:set_aliases(na);
else
new:set_aliases(nn ? {nn} | parent.aliases);
endif
return new;
.
#63:0
return ("Garbage object " + tostr(this)) + ".";
.
#63:1
player:tell(this:description());
.
#63:2
return tostr("Recyclable ", this);
.
#63:3
return;
.
#64:0
if (i = args[1] in {"noinclude", "sender"})
return {{{"include", "all"}[i], !args[2]}};
else
return {args};
endif
.
#64:1
"... we'll take anything...";
raw = args[2];
if (raw == 1)
"...+@mail => @mailo=new";
return {args[1], "new"};
else
return args[1..2];
endif
.
#64:2
oname = args[1];
raw = args[2];
if (typeof(raw) == LIST)
if (length(raw) > 1)
return "Too many arguments.";
endif
raw = raw[1];
elseif (typeof(raw) == NUM)
return {oname, raw && ((oname == "manymsgs") ? 20 | 1)};
endif
if ((value = $code_utils:tonum(raw)) == E_TYPE)
return tostr("`", raw, "'?  Number expected.");
endif
return {oname, value};
.
#64:3
oname = args[1];
raw = args[2];
if (typeof(raw) == STR)
raw = $string_utils:explode(raw, ",");
elseif (typeof(raw) == NUM)
return raw ? "You need to give one or more recipients." | {oname, 0};
endif
value = $mail_editor:parse_recipients({}, raw);
if (value)
return {oname, value};
else
return "No valid recipients in list.";
endif
.
#64: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
.
#64: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
.
#64:6
if (value = this:get(@args))
return {"", {tostr("Default message sequence for @mail:  ", (typeof(value) == STR) ? value | $string_utils:from_list(value, " "))}};
else
return {0, {"Default message sequence for @mail:  last:15"}};
endif
.
#64: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
.
#64: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
.
#64: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
.
#67: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";
"      if name is not set, and there is a .default_NAME prop, return that.";
"      otherwise return 0";
if ((name = args[2]) in (options = args[1]))
return 1;
elseif (a = $list_utils:assoc(name, options))
return a[2];
elseif ((what = this:get_default(name)) != E_PROPNF)
return what;
else
return 0;
endif
.
#67:1
":set(optionlist,oname,value) => revised optionlist or string error message.";
"oname must be the full name of an option in .names or .extras.";
"Note that values must not be of type ERR.  ";
"FALSE (0, blank string, or empty list) is always a legal value.";
"If a verb :check_foo is defined on this, it will be used to typecheck any non-false or object-type value supplied as a new value for option `foo'.";
"";
"   :check_foo(value) => string error message or {value to use}";
"";
"If instead there is a property .check_foo, that will give either the expected type or a list of allowed types.";
"Otherwise, the option is taken to be a boolean flag and all non-false, non-object values map to 1.";
"";
options = args[1];
if (!(((oname = args[2]) in this.names) || (oname in this.extras)))
return "Unknown option:  " + oname;
elseif (typeof(value = args[3]) == 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
else
"... value is considered to be boolean...";
if (!value)
"... must be an object.  oops.";
return tostr("Non-object value expected.");
endif
value = 1;
endif
"... We now have oname and a value.  However, if oname is one of the extras,";
"... then we need to call :actual to see what it really means.";
if (oname in this.names)
nvlist = {{oname, value}};
elseif ((typeof(nvlist = this:actual(oname, value)) != LIST) || (!nvlist))
return nvlist || "Not implemented.";
endif
"... :actual returns a list of pairs...";
for nv in (nvlist)
oname = nv[1];
value = nv[2];
default = this:get_default(oname);
if (i = (oname in options) || $list_utils:iassoc(oname, options))
if (value == default)
"don't waste space on default";
options[i..i] = {};
elseif ((!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 == default)
"don't waste space on default";
elseif ((1 || value) || (typeof(value) == OBJ))
options[1..0] = {(value == 1) ? oname | {oname, value}};
endif
endfor
return options;
.
#67:2
":parse(args[,...]) => {oname [,value]} or string error message";
"additional arguments are fed straight through to :parse_* routines.";
" <option> <value>     => {option, value}";
" <option>=<value>     => {option, value}";
" <option> is <value>  => {option, value}";
" +<option>            => {option, 1}";
" -<option>            => {option, 0}";
" !<option>            => {option, 0}";
" <option>             => {option}";
if (!(words = args[1]))
return "";
endif
option = words[1];
words[1..1] = {};
if (flag = option && index("-+!", option[1]))
option[1..1] = "";
endif
if (i = index(option, "="))
rawval = option[i + 1..length(option)];
option = option[1..i - 1];
if (i == 1)
"... =bar ...";
return "Blank option name?";
elseif (flag)
"... +foo=bar";
return "Don't give a value if you use +, -, or !";
elseif (words)
"... foo=bar junk";
return $string_utils:from_list(words, " ") + "??";
endif
elseif (!option)
return "Blank option name?";
elseif (flag)
if (words)
"... +foo junk";
return "Don't give a value if you use +, -, or !";
endif
rawval = (flag - 1) % 2;
else
words && ((words[1] == "is") && (words[1..1] = {}));
rawval = words;
endif
"... do we know about this option?...";
if (!(oname = this:_name(strsub(option, "-", "_"))))
return tostr((oname == $failed_match) ? "Unknown" | "Ambiguous", " option:  ", option);
endif
"... determine new value...";
if (!rawval)
"... `@option foo is' or `@option foo=' ...";
return (rawval == {}) ? {oname} | {oname, 0};
elseif ($object_utils:has_callable_verb(this, pverb = "parse_" + oname))
return this:(pverb)(oname, rawval, args[2..length(args)]);
elseif (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
.
#67:3
":_name(string) => full option name corresponding to string ";
"               => $failed_match or $ambiguous_match as appropriate.";
if (((string = args[1]) in this.names) || (string in this.extras))
return string;
endif
char = (namestr = this._namelist)[1];
if (!(i = index(namestr, char + string)))
return $failed_match;
elseif (i != rindex(namestr, char + string))
return $ambiguous_match;
else
j = index(namestr[i + 1..length(namestr)], char);
return namestr[i + 1..(i + j) - 1];
endif
.
#67: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 = args[1];
isextra = {@args, 0}[2];
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
.
#67: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
.
#67: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 (name in this.extras)
return {name + " not documented (complain)"};
else
value = this:get(args[1], name);
desc = {"not documented (complain)"};
if (typeof(value) in {LIST, STR})
desc[1..0] = $string_utils:print(value);
value = "";
endif
endif
if (value in {0, 1})
which = "-+"[value + 1] + name;
elseif ((typeof(value) in {OBJ, STR, NUM}) && (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;
.
#67: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.";
.
#67: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;
.
#67: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;
.
#67:10
":desc_type(types) => string description of types";
nlist = {};
for t in (types = args[1])
if (typeof(t) == LIST)
if (length(t) > 1)
nlist = {@nlist, tostr("(", this:desc_type(t), ")-list")};
else
nlist = {@nlist, tostr(this:desc_type(t), "-list")};
endif
elseif (t in {NUM, OBJ, STR, LIST})
nlist = {@nlist, {"number", "object", "string", "?", "list"}[t + 1]};
else
return "Bad type list";
endif
endfor
return $string_utils:english_list(nlist, "nothing", " or ");
.
#67:11
":short_name()";
if (i = index(this.name, " "))
return this.name[1..i - 1];
else
return this.name;
endif
.
#67:12
":get_default(oname)";
return $code_utils:verb_or_property(this, "default_" + args[1], args);
.
#68:0
if (i = args[1] in {"numbers"})
return {{{"nonumbers"}[i], !args[2]}};
else
return {args};
endif
.
#68:1
if (o = (name = args[2]) in {"numbers"})
args[2] = {"nonumbers"}[o];
return {@pass(@args), tostr("(", name, " is a synonym for -", args[2], ")")};
else
return pass(@args);
endif
.
#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 ...";
tonum();
.
#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
":full_traceback({@exception})";
"A full, traditional traceback describing the given exception.";
{e} = args;
tbk = {};
prefix = "";
for frame in (e[4])
{ethis, everb, eperms, edefiner, eplayer, eline} = frame;
if (everb)
line = tostr(edefiner, ":", everb, " (this == ", ethis, "), line ", eline);
else
line = tostr(edefiner, ":Input to EVAL, line ", eline);
endif
line = tostr(prefix, line, prefix ? "" | (": " + e[2]));
tbk = {@tbk, line};
prefix = "... called from ";
endfor
tbk = {@tbk, "(End of traceback)"};
return tbk;
.
#69:18
":short_traceback({@exception})";
"A one-liner describing the given exception.";
{e} = args;
top = e[4][1];
return tostr("<<<Error>>> ", top[1], ":", top[2] || "Input to EVAL", ", line ", top[6], ":  ", e[2]);
.
#70:0
if (!caller_perms().wizard)
raise(E_PERM);
endif
pass();
this.mail_forward = {player, this};
this.mail_notify = {player};
this.moderated = 1;
.
#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 = args[1];
insist = (length(args) > 1) && args[2];
notify(this, tostr("cleaning ", object, " task ", task_id(), " insist ", insist));
i = object in this.clean;
if (!i)
return tostr(object, " is not on the ", this.name, "'s cleanup list");
endif
place = this.destination[i];
if (!(($recycler:valid(object) && ($recycler:valid(place) || (place == #-1))) && (!(object.location in this.recycle_bins))))
"object no longer valid (recycled or something), remove it.";
this.clean = listdelete(this.clean, i);
this.requestors = listdelete(this.requestors, i);
this.destination = listdelete(this.destination, i);
return tostr(object) + " is no longer valid, removed from cleaning list";
endif
oldloc = loc = object.location;
if (object.location == place)
"already in its place";
return "";
endif
requestor = this.requestors[i];
if (insist != "up!")
for thing in (object.contents)
if (thing:is_listening())
return ((("Not returning " + object.name) + " because ") + thing.name) + " is inside";
endif
$command_utils:suspend_if_needed(0);
endfor
if (valid(loc) && (loc != $limbo))
if (loc:is_listening())
return ((("Not returning " + object.name) + " because ") + loc.name) + " is holding it";
endif
for y in (loc:contents())
if ((y != object) && y:is_listening())
return (((("Not returning " + object.name) + " because ") + y.name) + " is in ") + loc.name;
endif
$command_utils:suspend_if_needed(0);
endfor
endif
endif
if (valid(place) && (!place:accept(object)))
return (place.name + " won't accept ") + object.name;
endif
requestor:tell("As you requested, the housekeeper tidies ", $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 ", $code_utils:verb_or_property(object, "po") || "it", " away.");
endif
this:moveit(object, place, requestor);
if ((loc = object.location) == oldloc)
return (object.name + " wouldn't go; ") + ((!place:accept(object)) ? (" perhaps " + $string_utils:nn(place)) + " won't let it in" | ((" perhaps " + $string_utils:nn(loc)) + " won't let go of it"));
endif
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
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 = args[1];
if ((what < #1) || (!valid(what)))
return "invalid object";
endif
who = (length(args) > 1) ? args[2] | player;
where = (length(args) > 2) ? args[3] | what.location;
if ($object_utils:isa(who, $guest))
return tostr("Guests can't use the ", this.name, ".");
endif
if (!is_player(who))
return tostr("Non-players can't use the ", this.name, ".");
endif
if (!valid(where))
return tostr("The ", this.name, "doesn't know how to find ", where, " in order to put away ", what.name, ".");
endif
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 = args[1];
who = (length(args) > 1) ? args[2] | player;
if (i = what in this.clean)
if (!this:controls(i, who))
return tostr("You may remove an object from ", this.name, " list only if you own the object, the place it is kept, or if you placed the original cleaning order.");
endif
this.clean = listdelete(this.clean, i);
this.destination = listdelete(this.destination, i);
this.requestors = listdelete(this.requestors, i);
return tostr(what.name, " (", what, ") removed from cleanup list.");
else
return tostr(what.name, " not in cleanup list.");
endif
.
#71:6
"does player control entry I?";
i = args[1];
who = args[2];
if ((who in {this.owner, @this.owners}) || who.wizard)
return "Yessir.";
endif
i = args[1];
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 (this.task)
taskn = this.task;
this.task = 0;
kill_task(taskn);
endif
fork taskn (0)
while (1)
for x in (this.clean)
fork (0)
this:replace(x);
endfork
suspend(this.testing ? 2 | this:time());
endfor
suspend(5);
this:litterbug();
endwhile
endfork
this.task = taskn;
.
#71:8
for room in (this.public_places)
for thingy in (room.contents)
suspend(10);
litter = 0;
if (((thingy.location == room) && this:is_litter(thingy)) && (!this:is_watching(thingy, #-1)))
"if it is litter and no-one is watching";
fork (0)
this:send_home(thingy);
endfork
suspend(0);
endif
endfor
endfor
.
#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
loc:tell("You notice the ", this.name, " sneak in, give you ", litter:title(), " and leave.");
endif
if ($object_utils:has_callable_verb(littering, "announce_all_but"))
littering:announce_all_but({litter}, "The ", this.name, " sneaks in, picks up ", litter:title(), " and rushes off to put it away.");
endif
.
#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.litter = {};
this.public_places = {};
this.requestors = {};
this.destination = {};
this.clean = {};
this.eschews = {};
this.recycle_bins = {};
this.cleaning = #-1;
this.task = 0;
this.owners = {#2};
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, 3600 / length(this.clean));
.
#71:19
return caller == this;
.
#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..length(address)]} | {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..length(site)];
site = site[1..dot - 1];
while (site && (domain in this.large_domains))
if (dot = rindex(site, "."))
domain = tostr(site[dot + 1..length(site)], ".", 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(forwhom = 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)))
else
return E_INVARG;
endif
if (typeof(connection = open_network_connection(address, port)) != ERR)
this.open_connections = {@this.open_connections, connection};
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
boot_player(args[1]);
$login.ignored = setremove($login.ignored, args[1]);
$network.open_connections = setremove($network.open_connections, args[1]);
if (i = $list_utils:iassoc(args[1], $network.connect_connections_to))
$network.connect_connections_to = listdelete($network.connect_connections_to, i);
endif
return 1;
.
#72:4
"sendmail(to, subject, @lines)";
"  sends mail to internet address 'to', with given subject.";
"  It fills in various fields, such as date, from (from player), etc.";
"  lines 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
return this:raw_sendmail(to, "Date: " + $time_utils:ctime_gmt(), ((((("From: \"" + player.name) + "@") + mooname) + "\" <") + this.reply_address) + ">", "To: " + to, "Subject: " + args[2], "X-Mail-Agent: " + mooinfo, @args[3..length(args)]);
.
#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.site = "yoursite";
this.postmaster = "postmastername@yourhost";
this.MOO_name = "YourMOO";
this.maildrop = "localhost";
this.port = 7777;
this.large_domains = {};
this.trusts = {};
this.open_connections = this.connect_connections_to = {};
endif
.
#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
debugging = this.debugging;
address = args[1];
body = listdelete(args, 1);
data = {"HELO " + this.site, ("MAIL FROM:<" + this.postmaster) + ">", ("RCPT TO:<" + address) + ">", "DATA"};
blank = 0;
for x in (body)
$command_utils: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)
return tostr("Cannot open connection to maildrop ", this.maildrop, ": ", target);
endif
"added while() loop below to prevent flushing --961017 Quinn";
fork (0)
for line in (data)
$command_utils:suspend_if_needed(0);
if (debugging)
notify(this.owner, "SEND:" + line);
endif
while (!notify(target, line, 1))
suspend(1);
endwhile
endfor
endfork
expect = {"2", "2", "2", "2", "3", "2"};
while (expect && (typeof(line = read(target)) != ERR))
if (line)
if (debugging)
notify(this.owner, "GET: " + line);
endif
if (line[4] == "-")
elseif (!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.";
address = args[1];
if (!(at = rindex(address, "@")))
return ("'" + address) + "' contains no @";
endif
name = address[1..at - 1];
host = address[at + 1..length(address)];
if (!match(host, $network.valid_host_regexp))
return tostr("'", host, "' doesn't look like a valid internet host");
endif
if (!match(name, $network.valid_email_regexp))
return tostr("'", name, "' doesn't look like a valid user name for internet mail");
endif
return "";
.
#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().";
reason = this:invalid_email_address(args[1]);
if (reason && {@args, 0}[2])
player:tell("Invalid email address: ", reason);
endif
return reason;
"following is code from OpalMOO, not used here";
"Possible situations where the address would be unusable are when the address is invalid or we can't connect to the site to send mail.";
"If <display> is true, error messages are displayed to the player and 1 is returned when address is unuable.  If <display> is false and address is unusable, the error message is returned.  If the address is usable, 0 is always returned.";
if (!this:approved_for_network(caller_perms()))
return E_PERM;
endif
if (!this:valid_email_address(email = args[1]))
msg = tostr("Your email address (", email, ") is not a usable account.");
elseif ((result = this:verify_email_address(email)) == E_INVARG)
msg = tostr("Unable to connect to ", this:parse_address(email)[2], ".");
elseif (typeof(result) == STR)
msg = tostr("The site ", (parse = this:parse_address(email))[2], " does not recognize ", parse[1], " as a valid account.");
else
return 0;
endif
if ({@args, 0}[2])
player:tell(msg);
return 1;
else
return msg;
endif
"Last modified Tue Jun 15 00:19:01 1993 EDT by Ranma (#200).";
.
#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..length(gp)];
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];
.
#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))
help = $code_utils:verb_documentation(this, vrb);
if (help)
all_help = {@all_help, "", (tostr(this) + ":") + verb_info(this, 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("Type \"@add-feature ", this, "\" to add this feature, and \"help ", this, "\" for more information on how to use it.");
.
#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"}});
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)
return E_PERM;
endif
pass();
this.warehouse = #-1;
.
#74:9
":display_msg(viewer, owner)";
"Text to show from the @features command.  0 if it should not appear.";
{?user = caller, ?viewer = player} = args;
return (m = this.display_msg) ? $string_utils:pronoun_sub(m, viewer, user) | $string_utils:nn(this);
.
#75:0
oname = index(args[2], "room") ? "Room" | "Exit";
value = this:get(@args);
if (!value)
return {0, {tostr(oname, " names will be shown in normal print.")}};
elseif (typeof(value) == STR)
where = player:room();
if (oname == "Room")
value = $string_utils:pronoun_sub(value, where);
elseif (x = where:obvious_exits())
value = $string_utils:pronoun_sub(value, $list_utils:random_element(x));
endif
return {"", {tostr(oname, "s shown as: \"", value, "\"")}};
else
return {1, {tostr(oname, " names will be shown in bold print.")}};
endif
.
#75:1
oname = args[1];
type = index(oname, "room") ? "room" | "exit";
raw = args[2];
if (typeof(raw) == STR)
if (!index(raw, "%N"))
return tostr("You need to have a %N in the string, representing the ", type, " name.");
endif
endif
return {oname, raw};
.
#75:2
":parse_charset()";
{oname, raw, @rest} = args;
available = $emu.available_charsets;
if (!(raw in available))
return tostr("Charset must be one of ", $string_utils:english_list(available, "", " or "), ".");
endif
return {oname, raw};
.
#75:3
":show_charset()";
value = this:get(@args);
if (typeof(value) == STR)
return {"", {tostr("Using preferred character set: ", value, ".")}};
else
return {value, {tostr("Using default character set: ", $emu.default_charset, ".")}};
endif
.
#75:4
return $emu.default_charset;
.
#76: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;
endif
return $gender_utils:set(this, args[1]);
.
#76: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
.
#76:2
":verb_sub(verb)";
"Correctly conjugate verb according to this object's gender.";
text = args[1];
subs = this.verb_subs;
if (subs && (a = $list_utils:assoc(text, this.verb_subs)))
return a[2];
else
return $gender_utils:get_conj(text, this);
endif
.
#76:3
"Temporary verb whilst we switch the gender system.";
return (this.(verb) || this.gender:(verb)()) || this.gender.(verb);
.
#76:4
":gender_adj()  -> 'male', 'female'";
":gender_noun() -> 'man', 'woman'";
return tostr((this.gender:(verb)() || this.gender.name) || this.gender);
.
#76:5
":gender_tokenize(str)";
"Take the given string and convert it into something suitable for pronoun_sub.  Ie: Replace this player's name with %N, and all pronouns with their respective subst-strings.";
s = args[1];
q = {};
while (m = match(s, "%(\"[^\"]+\"%)"))
t = s[a = m[3][1][1]..b = m[3][1][2]];
q = {@q, {c = crypt(t), t}};
s[a..b] = c;
endwhile
s = strsub(s, this:name(), "%n");
g = this.gender;
for p in ({{"ppc", "%P"}, {"pp", "%p"}, {"prc", "%R"}, {"pr", "%r"}, {"poc", "%O"}, {"po", "%o"}, {"psc", "%S"}, {"ps", "%s"}, {"pqc", "%Q"}, {"pq", "%q"}})
while (m = match(s, ("%(%<" + g.(p[1])) + "%>%)", 1))
s[m[3][1][1]..m[3][1][2]] = p[2];
endwhile
endfor
for p in (q)
s = strsub(s, @p);
endfor
return s;
.
#76:6
":gender_obj()";
"Just return this.gender; might conceivably be hacked someday.";
return this.gender;
.
#76:7
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
if (!valid(this.gender))
this.gender = $default_gender;
endif
.
#77:0
":Assign_Args(@args)";
if (caller != this)
return E_PERM;
endif
i = 0;
arg_names = this.arg_names;
for arg in (args)
this.(arg_names[i = i + 1]) = arg;
endfor
.
#77:1
":Fetch_Object()";
"Try and get a new event object.  Return true if we failed.  HEH WEIRD HUH.";
if (caller != this)
return E_PERM;
elseif (valid(try_ = this:Find()))
return try_;
elseif (valid(try_ = this:Spawn()))
return try_;
else
return 1;
endif
.
#77:2
":spawn()";
"-> Create a child of this event.";
if (caller != this)
return E_PERM;
elseif (!valid(type = this.type))
return type;
endif
new = $recycler:_create(type);
new:set_name("event");
new:set_aliases({});
return new;
.
#77:3
":find()";
"-> An expired event object of this type.";
"-> $nothing";
for kid in (children(this.type))
if (!$code_utils:task_valid(kid.task_id))
kid:Reset();
return kid;
endif
endfor
return $nothing;
.
#77:4
":New(@args)";
"-> An event object initialized with the given args.  See the various event verbs on $event for details.";
if (caller != $event)
return E_PERM;
endif
event = this:Fetch_Object();
event && $error:raise(E_QUOTA);
"receivers = args[1];";
"args = listdelete(args, 1);";
event.args = args;
event.task_id = task_id();
this:Assign_Args(@args);
return event;
.
#77:5
":Set_Custom_Format(for_whom, pre_formatted_text)";
if (caller != $event)
return E_PERM;
endif
this.custom_format = args;
.
#77:6
":Reset()";
"Prepare this object for use by a new event.";
for arg in ({@this.arg_names, "task_id", "custom_format", "compiled_text"})
clear_property(this, arg);
endfor
.
#77:7
"origin  (%T) -- Object from which the event was broadcast.";
"subject (%N) -- Object (usually a character) which initiated the event.";
"dobj    (%D) -- Object on which the event is acting.";
"iobj    (%I) -- Object with which the subject is acting.";
"other   (%L) -- Any other object or objects.";
"If an argument is given, it is an ALTERNATE actors list.";
"These methods should always be used rather than accessing the ACTORS list directly, as its format may change.";
actor = (args ? args[1] | this.actors)[verb in this.actor_names];
return valid(actor) ? actor | $nothing;
.
#77:8
":Text_For(receiver)";
"Return text to be sent to the receiver's :tell method.";
r = args[1];
if ((cf = this.custom_format) && (cf[1] == r))
return strsub(cf[2], "$phrase", this.phrase);
endif
f = 0;
a = this.actors;
while (i = r in a)
a[f = i] = $you;
endwhile
if (f)
return this:Sub_Template(a);
elseif (t = this.compiled_text)
return t;
else
return this.compiled_text = this:Sub_Template();
endif
.
#77:9
":Sub_Template([actors])";
"Return this event's template, appropriately subbed for an uninvolved receiver.";
"If an argument is given, it will be an alternate set of actors to be used in the substitution.";
if (args)
a = args[1];
dobj = this:Dobj(a);
iobj = this:Iobj(a);
ps = {this:Subject(a), this:Origin(a), this:Other(a)};
else
dobj = this:Dobj();
iobj = this:Iobj();
ps = {this:Subject(), this:Origin(), this:Other()};
endif
return strsub($string_utils:pronoun_sub(this.template, @ps), "$phrase", this.phrase);
.
#78:0
":time()            -> The time according to this player's timezone.";
":ctime([NUM time]) -> Likewise, but ctime().";
"May be hacked by players and player-classes to reflect differences in time-zone.";
return $time_utils:(verb)(this.timezone_msg, @args);
.
#78:1
"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 control this.";
"1        if it works.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
newhome = args[1];
if (!$object_utils:has_callable_verb(newhome, "accept_for_abode"))
return E_TYPE;
elseif (newhome:accept_for_abode(this))
return (typeof(e = this.home = args[1]) != ERR) || e;
else
return E_INVARG;
endif
.
#78:2
"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
.
#78:3
"Copied from generic player (#6):linelen by Hacker (#38) Sun Nov 21 05:39:10 1993 CST";
return abs(this.linelen);
.
#78:4
":tell_contents() -- Tell `player' what this character is carrying.";
c = args ? args[1] | this:contents();
ctype = (length(args) > 1) ? args[2] | this.ctype;
if (!ctype)
"Don't say anything.";
return 0;
elseif (!c)
if (this.wielding)
"don't mentioned emptyhandedness";
elseif (msg = this:emptyhanded_msg())
player:tell(msg);
endif
return E_NONE;
endif
"Remove non-tangibles from vR contents display.  Use 'inventory' to see everything.";
for o in (c)
if (!$object_utils:isa(o, $tangible))
c = setremove(c, o);
endif
endfor
"Try to keep a little compatibility with the `ctype' room protocol.";
if (ctype == 1)
"Columnize.";
l = {};
player:tell("Carrying:");
for thing in (c)
l = {@l, " " + thing:title()};
endfor
player:tell_lines($string_utils:smart_columnize(l, 4));
elseif (ctype == 2)
"English list.";
if (msg = this:carrying_msg(c))
player:tell(msg);
endif
endif
.
#78:5
"Usage: i";
"=> Show a list of everything you wield, wear and carry.";
c = this:contents();
if (!c)
player:tell("You are empty-handed.");
return E_NONE;
endif
text = {};
if (w = this:wielding())
text = {@text, "Wielding:"};
l = {};
for thing in (w)
c = setremove(c, thing);
l = {@l, tostr(" ", thing:title(), " (", thing, ")")};
endfor
text = {@text, @$string_utils:smart_columnize(l, 3)};
endif
l = {};
for ac in (this:clothing())
for thing in (ac)
if (i = thing in c)
c = listdelete(c, i);
l = {@l, tostr(" ", thing:title(), " (", thing, ")")};
endif
endfor
endfor
if (l)
text = {@text, "Wearing:", @$string_utils:smart_columnize(l, 3)};
endif
if (c)
text = {@text, "Carrying:"};
l = {};
for thing in (c)
l = {@l, tostr(" ", thing:title(), " (", thing, ")")};
endfor
text = {@text, @$string_utils:smart_columnize(l, 3)};
endif
player:tell_lines(text);
.
#78:6
return !is_player(args[1]);
.
#78:7
"mu*rmur/wh*isper <player> [=] <message>";
"Both send a private message to the given player.  Murmur will print a message `PlayerA murmurs something to PlayerB.' to the room.";
if (!args)
return player:tell_lines($code_utils:verb_documentation());
elseif (m = match(argstr, "^%(.*[^ =]%) *= *%(.*%)$"))
whostr = substitute("%1", m);
text = substitute("%2", m);
elseif (m = match(argstr, ("^" + args[1]) + " +%(.*%)$"))
whostr = args[1];
text = substitute("%1", m);
else
return player:tell_lines($code_utils:verb_documentation());
endif
who = player:my_match_object(whostr);
what = (verb[1] == "m") ? "murmur" | "whisper";
if ($command_utils:object_match_failed(who, whostr))
return;
endif
who:tell(player:dnamec(), " ", what, "s, \"", text, "\"");
player:tell("You ", what, ", \"", text, "\" to ", who:dname(), ".");
if (what == "murmur")
player:room_announce_all_but({player, who}, tostr(player:dname(), " murmurs something to ", who:dname(), "."));
endif
.
#78:8
":carrying_msg()    -> What you're carrying.  Sub %c for an english list of the contents.  If you don't want your character's inventory shown, `@set char.ctype to 0'.";
"Other subs:  %N -- This, %D -- Player.";
msg = this.(verb);
isp = is_player(this);
if ((!msg) && (!isp))
return msg;
endif
c = args ? args[1] | this:contents();
if (index(msg, "%c", 1))
msg = strsub(msg, "%c", $string_utils:ititle_list(c), 1);
elseif (index(msg, "%C", 1))
msg = strsub(msg, "%C", $string_utils:ititle_listc(c), 1);
elseif (isp)
msg = strsub($character.(verb), "%c", $string_utils:ititle_list(c));
endif
return $string_utils:pronoun_sub(msg, this, this, this.location, player);
.
#78:9
":resolve(skill[, mod[, routine]])";
"skill     -- A string, the name of the skill to roll against.";
"mod       -- Modifier to the roll.";
"routine   -- True if the roll isn't worthy of skill improvement.";
return ((random(100) - 100) + (this.(args[1]) || 0)) + (args[2] || 0);
.
#78:10
":reroll() -- Reroll the attributes of this character.";
"-> Sum of new ranks.";
atts = {"agility", "dexterity", "endurance", "quickness", "strength"};
done = {};
for att in (atts)
done = {@done, this.(att) = $rpg:result("3d6")};
endfor
return $math_utils:sum(@done);
.
#78:11
":random_body_area()";
"-> A random body area object from this character.";
areas = this.body_areas;
tries = length(areas);
rint = random(100);
while (tries)
if (rint < areas[i = random($)].percent_of_body_area)
return areas[i];
endif
tries = tries - 1;
endwhile
return areas[random($)];
"---the old way---";
while ((l = length(areas)) > 1)
i = random(l);
if (random(100) < areas[i].percent_of_body_area)
return areas[i];
endif
areas = listdelete(areas, i);
endwhile
return areas[1];
.
#78:12
":body_areas() -> All body areas of this character.";
return this.body_areas;
.
#78:13
":match_body_area(STR areaname[, NUM no_ambigs]) -> A body area object.";
"If `no_ambigs' is 1, return a random element of all matching objects with `areaname' as an alias.";
"If `no_ambigs' is 2, return a list of all (if any) matching parts.";
areas = this:body_areas();
string = args[1];
if ((length(args) < 2) || (!args[2]))
return $match_utils:match(string, areas);
endif
matched = $match_utils:match_list(string, areas);
if (args[2] == 2)
return matched;
elseif (!matched)
return $failed_match;
elseif (length(matched) == 1)
return matched[1];
endif
while (matched)
area = matched[i = random(length(matched))];
if (string in area.aliases)
return area;
endif
matched = listdelete(matched, i);
endwhile
return $ambiguous_match;
.
#78:14
":hands() -- Does this character have any hands left?";
return $set_utils:intersection(this.body_areas, this.hands);
.
#78:15
":hands_free() -- How many hands does the character have free?";
hands = length(this:hands());
for w in (this:wielding())
if (h = w:hands_required(this))
hands = hands - h;
endif
endfor
return hands;
.
#78:16
":set_hands(LIST hand_objects)";
"`hand_objects' should be a subset of `body_area_objects'--those objects that are considered to be the characters hands.  Or whatever you call what e's grasping with.  Pincers, claws, mandibles, etc.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
return this.(verb[5..length(verb)]) = args[1];
.
#78:17
":speech_msg(STR spoken-text)";
"The format in which a player speaks.  `$text' is substituted for what is being said.  The message is pronoun substituted.";
"Ie: @my speech is \"%N %<growls>, \\\"$text\\\"\"";
msg = this.(verb);
txt = $string_utils:pronoun_quote(args[1]);
if (!msg)
return "";
endif
if ((!index(msg, "%n")) && (!index(msg, this.name)))
msg = tostr(this.name, ": ", msg);
endif
if (!index(msg, "$text"))
msg = tostr(msg, "  (\"", txt, "\")");
else
msg = strsub(msg, "$text", txt);
endif
return msg;
.
#78:18
"Say something in the room.  Allow player-customised speech with :say_msg.";
info = $object_utils:has_callable_verb(where = player.location, "say");
if (info && (info[1] != $room))
where:say(@args);
return;
endif
argstr = this:filter_speech(argstr);
if (said = player:speech_msg(argstr))
$you:say_action(said);
else
player:tell("You say, \"", argstr, "\"");
where:announce(player:inamec(), " says, \"", argstr, "\"");
endif
volume = (argstr[$] == "!") ? 4 | 1;
where:broadcast_event_speech(player, argstr, volume + random(3));
.
#78:19
"give/hand <anything> to <anyone>";
"Transfer an object from you to them.  Wotcha think it does?";
"There are several optional messages that may be called:";
"iobj : give_refused                -- If iobj refused dobj.";
"dobj : give_succeeded/give_failed  -- If dobj didn't wanna go.";
set_task_perms(callers() ? caller_perms() | player);
loc = player.location;
inv = player:matching_contents();
env = loc:environment();
dobj = $match_utils:match_environment(dobjstr, inv);
iobj = $match_utils:match_environment(iobjstr, env);
if ($match_utils:object_match_failed(dobj, dobjstr, inv, "on you"))
return E_INVARG;
elseif (!player:is_holding(dobj))
player:tell(("You aren't holding " + dobj:dname()) + ".");
return E_RANGE;
elseif ($match_utils:object_match_failed(iobj, iobjstr, env))
return E_INVARG;
elseif (!(iobj in env))
player:tell(iobj:inamec() + " isn't here.");
return E_RANGE;
elseif (player == iobj)
player:tell("You've already got it.");
return E_RECMOVE;
elseif (!iobj:acceptable(dobj))
if (!iobj:announce_messages("give_refused"))
player:tell($string_utils:pronoun_sub("%[idnamec] %<i:does> not want that item."));
endif
return E_NACC;
endif
last = dobj.location;
dobj:moveto(iobj);
if ($code_utils:call_verb(dobj, "was_moved", {last, iobj}) || (dobj.location == iobj))
if (!dobj:announce_messages("give_succeeded"))
player:tell($string_utils:pronoun_sub(("You " + verb) + " %[ddname] to %i."));
iobj:tell($string_utils:pronoun_sub(("%N %<" + verb) + "s> you %[diname]."));
iobj:room_announce_all_but({player, iobj}, $string_utils:pronoun_sub(("%N %<" + verb) + "s> %[diname] to %i."));
endif
elseif (!dobj:announce_messages("give_failed"))
player:tell($string_utils:pronoun_sub("%[ddnamec] %<d:does> not want to go."));
endif
.
#78:20
":go_home() -- Sends this character home, sans beeps and whistles.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
this:moveto(this.home);
.
#78:21
":hear_event_exit(player, exit)";
"See if we should be following the exiting person.";
pass(@args);
{who, how} = args;
if (!(who in this.following))
"who cares?";
return E_NONE;
elseif (is_player(this) && (idle_seconds(this) == E_INVARG))
"not connected";
return E_NACC;
elseif ($code_utils:task_valid(this.forked_follow))
"already following someone";
return E_QUOTA;
elseif (typeof(how) == OBJ)
if (!valid(how))
"got away!";
this.following = setremove(this.following, who);
return E_INVIND;
elseif (who.location != how.dest)
"bogus exit";
return E_RANGE;
endif
endif
fork id (0)
this:move_through(how);
clear_property(this, "forked_follow");
endfork
this.forked_follow = id;
return id;
.
#78:22
"follow <character>";
"Attach yourself to the given character, following em whenever e leaves the room through a traditional exit.";
if (player == this)
player:tell("Follow yourself?");
elseif (this.location != player.location)
player:tell("I don't see ", this.name, " here.");
else
player.following = setadd(player.following, this);
$you:say_action("%N %<starts> following %d.");
endif
.
#78:23
"following -- List everyone you're following.";
f = player.following;
if (!$code_utils:task_valid(player.forked_follow))
"Aren't waiting to follow, so take the opportunity to clean up.";
for who in (f)
if ((!$recycler:valid(who)) || (who.location != player.location))
f = setremove(f, who);
endif
endfor
player.following = f;
endif
player:tell("You are currently following ", $string_utils:title_list(f, "nobody"), ".");
.
#78:24
":set_body_areas(LIST appendages)";
"Sets the given list as your new body-parts, in the given order.  Adjusts clothing property so that clothing is worn in the correct slots.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
ou = $object_utils;
ba = $body_area;
newparts = args[1];
for p in (newparts)
if (!ou:isa(p, ba))
return p;
endif
endfor
oldparts = this.body_areas;
oldclothing = this.clothing;
newclothing = $list_utils:make(length(newparts), {});
oldnudity = this.nudity;
newnudity = $list_utils:make(length(newparts), "");
for o in [1..length(oldparts)]
part = oldparts[o];
if (n = part in newparts)
newclothing[n] = oldclothing[o] || {};
newnudity[n] = oldnudity[o] || "";
endif
endfor
this.clothing = newclothing;
this.nudity = newnudity;
this.body_areas = newparts;
return newparts;
.
#78:25
":my_match_room(string) -- Pass to $match_utils by default.";
return $match_utils:match_room(@args);
.
#78:26
"walk to <roomname>";
"Walk from your current location to the given room name.  The destination room must be within your general area (zone) to be located by the mapping processor.";
"";
"walk to <username>";
"Walk to the given user's location.  E must be logged in to be located.";
already = this.walking;
if (!already)
"...not walking...";
elseif (!$code_utils:task_valid(already[1]))
this.walking = 0;
else
player:tell("You're already on your way to ", already[2].name, ".  Type `stop walking' to cancel that journey.");
return E_RECMOVE;
endif
dest = player:my_match_room(iobjstr);
if (valid(dest))
"...ok...";
elseif (valid(who = $match_utils:match_player(iobjstr)))
if (!$login:connected(who))
player:tell("Since ", who.name, " isn't logged in, you can't pinpoint ", who:pp(), " location.");
return;
elseif ($object_utils:isa(dest = who.location, $generic_editor))
dest = (i = who in dest.active) ? dest.original[i] | dest;
endif
endif
start = player.location;
if ($match_utils:room_match_failed(dest, iobjstr))
return;
elseif (dest == start)
player:tell("You are already there.");
return;
endif
oldbrief = this.brief;
this.brief = 1;
if (!this:_walk(dest))
player:tell("You can't walk from here to ", dest:title(), ".");
endif
this.brief = oldbrief;
.
#78:27
":_walk(destination)";
"Find a path to destination and walk there.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
path = this:find_path(player.location, dest = args[1]);
if (path && (typeof(path) == LIST))
this.walking = {task_id(), dest};
this:move_by_exits(path);
this.walking = 0;
endif
return player.location == dest;
.
#78:28
":find_path(start, dest)";
"-> List of exitnames which should lead from start to dest.";
if (args[1] == args[2])
return {};
endif
destination = args[2];
rooms = {args[1]};
paths = {{}};
which = 1;
suspend_msg = tostr("You think of the best route to ", destination.name, "...");
while (which <= length(rooms))
$command_utils:suspend_if_needed(1, suspend_msg);
check = rooms[which];
path = paths[which];
which = which + 1;
for exit in (check:obvious_exits())
if (exit:is_unlocked_for_walker(player))
if ((dest = exit.dest) == destination)
return {@path, exit.name};
elseif (!(dest in rooms))
rooms = {@rooms, dest};
paths = {@paths, {@path, exit.name}};
endif
endif
endfor
endwhile
.
#78:29
"move_by_exits(path)";
path = args[1];
this.location:go(@path);
.
#78:30
"join <user> -- Walk to the given user's location.";
"";
"Note that if the user is not signed in, you won't be able to pinpoint eir location.";
here = player.location;
if ($object_utils:has_callable_verb(here, verb))
here:(verb)(@args);
else
this:walk(iobjstr = argstr);
endif
.
#78:31
"stop ambushing               -- stop ambushing, allowing you to act freely";
"stop blocking [<exit>]       -- stop blocking an exit.";
"stop carrying <character>    -- stop carrying someone/thing.";
"stop following <character>   -- stop following someone.";
"stop guarding|defending <character> -- stop defending someone.";
"stop walking                 -- stop walking somewhere.";
where = player:room();
action = args[1];
if (action == "ambushing")
if (a = this.ambushing)
player:tell("You stop waiting for \"", a, "\" and relax from your ambush position.");
player:room_announce($string_utils:pronoun_sub("%N %<seems> to relax slightly, and %<backs> off from the local exits."));
clear_property(this, "ambushing");
else
player:tell("You aren't waiting to ambush anyone.");
endif
return;
elseif (action == "blocking")
for exit in (where:exits())
if (exit:is_blocked_by(player))
return exit:unblock();
endif
endfor
player:tell("You aren't blocking any exits here.");
return E_NONE;
elseif (action == "following")
this:unfollow(dobjstr = args[2]);
return;
elseif (action in {"guarding", "defending"})
args = listdelete(args, 1);
dobjstr = args[1];
if (!args)
player:tell("Stop defending whom?");
elseif ($match_utils:object_match_failed(dobj = $match_utils:match(dobjstr, this.defending), dobjstr, this.defending, "amongst those you are defending"))
return;
else
this:stop_defending(dobj);
endif
return;
elseif (action == "carrying")
args = listdelete(args, 1);
dobjstr = args[1];
if (!args)
player:tell("Stop carrying whom?");
elseif ($match_utils:object_match_failed(dobj = player:match_contents(dobjstr), dobjstr, player:matching_contents(), "being carried by yourself"))
return;
else
this:stop_carrying(dobj);
endif
return;
elseif (argstr == "walking")
if (walking = this.walking)
kill_task(walking[1]);
player:tell("You stop walking towards ", walking[2]:ititle(), ".");
else
player:tell("You aren't going anywhere.");
endif
return;
endif
dobj = player:my_match_object(dobjstr);
if (typeof(dobj:(verb)(@args)) != ERR)
"...action on dobj...";
elseif (where:(verb)(@args) != E_VERBNF)
"...action on location...";
elseif (argstr)
player:tell("I don't know what to tell you.  You stop ", argstr, ".  Happy now?");
else
player:tell("Stop doing what?");
endif
.
#78:32
"get something";
"Trying to get a character will result in the task being redirected to the :carry command.";
if (dobj == this)
return player:carry(dobjstr);
endif
where = this.location;
if (!valid(where))
player:tell("You can't do that in this hellish $nothingness.");
return E_INVIND;
endif
e = setremove(where:contents(), player);
set_task_perms(callers() ? caller_perms() | player);
dobj = player:my_match_object(dobjstr, e);
if ($match_utils:object_match_failed(dobj, dobjstr, e))
return;
elseif (`dobj:(verb)(@args) ! E_VERBNF' != E_VERBNF)
"...dobj defined a take verb...";
elseif (!$wiz_utils:is_builder(player))
"...don't allow non-programmers to take things without take verbs...";
player:notify("You can't take that.");
else
$last_huh:(verb)(@args);
endif
.
#78:33
if (!valid(here = this.location))
return player:tell("You can't do that in this hellish $nothingness.");
endif
e = player:contents();
set_task_perms(callers() ? caller_perms() | player);
dobj = player:my_match_object(dobjstr, e);
if (dobj == $nothing)
player:tell(("What is it you want to " + verb) + "?");
elseif (dobj == $failed_match)
player:tell(((("You don't have a \"" + dobjstr) + "\" to ") + verb) + ".");
elseif (dobj == $ambiguous_match)
all = $match_utils:match_list(dobjstr, e);
player:tell(((("I don't know whether you want to " + verb) + " ") + $string_utils:name_list(all, "nothing", " or ")) + ".");
elseif (dobj.location != player)
return player:tell("You don't have that.");
elseif (dobj == this)
player:stop_carrying(dobj);
elseif (`dobj:(verb)(@args) ! E_VERBNF' == E_VERBNF)
$last_huh:(verb)(@args);
endif
.
#78:34
":get_message(msgname)";
"Check if the requested message is a body area description.";
if (!this:is_readable_by(caller_perms(), caller))
return E_PERM;
endif
msgname = args[1];
if (msgname[1..6] != "naked_")
return pass(@args);
endif
area = strsub(msgname[7..length(msgname)], "_", " ");
if (!(fnd = this:_match_body_area_exactly(area)))
msg = this.nudity[fnd in this.body_areas];
if (msg)
return msg;
endif
als = fnd.aliases;
gps = this.grouped_body_areas;
for i in [1..length(gps)]
if (gps[i] in als)
return this.grouped_body_areas_nudity[i];
endif
endfor
endif
if (i = area in this.grouped_body_areas)
return this.grouped_body_areas_nudity[i];
endif
return pass(@args);
.
#78:35
":set_message(msgname, newvalue)";
"Check if the requested message is a body area description.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
msgname = args[1];
if (msgname[1..6] != "naked_")
return pass(@args);
endif
area = strsub(msgname[7..length(msgname)], "_", " ");
matched = this:match_body_area(area, 2);
if (!matched)
"...neither partial nor exact match...";
return pass(@args);
endif
new = args[2];
if (length(matched) == 1)
i = matched[1] in this.body_areas;
if (i && (i <= length(this.nudity)))
this.nudity[i] = new;
endif
elseif (i = $list_utils:iassoc(area, grouped = this.grouped_body_areas))
this.grouped_body_areas_nudity[i] = new;
else
"...see if grouped area is defined under another name...";
set = i = 0;
leg = length(grouped);
while ((!set) && ((i = i + 1) <= leg))
if (this:match_body_area(grouped[i], 2) == matched)
this.grouped_body_areas_nudity[i] = new;
set = 1;
endif
endwhile
if (!set)
"...create new grouped area...";
this.grouped_body_areas = {@grouped, area};
this.grouped_body_areas_nudity = {@this.grouped_body_areas_nudity, new};
endif
endif
if (!new)
"CLEAR these big ol' properties if same as mama's.";
ma = parent(this);
if (this.grouped_body_areas_nudity == ma.grouped_body_areas_nudity)
clear_property(this, "grouped_body_areas_nudity");
endif
if (this.nudity == ma.nudity)
clear_property(this, "nudity");
endif
endif
return new ? 1 | 0;
.
#78:36
":naked_<part>_msg() -> Return what the given naked body area looks like.";
if (!this:is_readable_by(caller_perms(), caller))
return E_PERM;
endif
msg = this:get_message(strsub(verb, "_msg", "")) || "";
return msg && $string_utils:pronoun_sub(msg, this, this, this.location, player);
.
#78:37
":clothed_description()";
"=> String describing what player is/isn't wearing. The topmost article of clothing on a body area is always shown.  if `bare' messages are defined, they will be shown when you aren't wearing anything on that location.";
if (!this:is_readable_by(caller_perms(), caller))
return E_PERM;
elseif (0 && (this.clothing_msg == "OFF"))
"...used on lambdamoo to turn clothing off...";
elseif ((type = this.clothing_integration_style) == 0)
return "";
endif
shown = clothes = {};
if (type == 1)
nudity = {};
endif
nude = this:body_areas();
duds = this.clothing;
got_some_nudity = 0;
for x in [1..length(duds)]
area = duds[x];
done = 0;
while ((!done) && area)
item = area[1];
if ((!$object_utils:isa(item, $clothing)) || (!item:worn_by(this)))
this.clothing[x] = setremove(this.clothing[x], item);
else
if (!item:will_reveal(setremove(@area, item)))
done = -1;
endif
if (item in shown)
"...shown...";
elseif (type == 1)
clothes = {@clothes, item};
shown = setadd(shown, item);
else
clothes = {@clothes, item:worn_msg()};
shown = setadd(shown, item);
endif
endif
area = setremove(area, item);
endwhile
add = (!done) && this:(("naked_" + strsub(nude[x].name, " ", "_")) + "_msg")();
if (!add)
continue;
endif
got_some_nudity = 1;
if ((type == 1) && (!(add in nudity)))
nudity = {@nudity, add};
elseif (!(add in clothes))
clothes = {@clothes, add};
endif
endfor
if (got_some_nudity && this:get_option($privacy_options, "paranude"))
this:notify($string_utils:pronoun_sub(">>> %N may have just seen you naked."));
endif
if (type == 1)
clothes = clothes ? tostr(this:title(), " is wearing ", $string_utils:title_list(clothes, "nothing remarkable"), ".", $code_utils:verb_or_property(player, "integration_separation_msg") || "  ") | "";
clothes = nudity ? clothes + $string_utils:from_list(nudity, $code_utils:verb_or_property(player, "look_sep_msg") || "  ") | clothes;
return clothes;
else
return clothes ? $string_utils:from_list(clothes, $code_utils:verb_or_property(player, "integration_separation_msg") || "  ") | "";
endif
.
#78:38
"@nudity";
" Show defined nude messages, and all parts available for description.";
dobjstr = dobjstr || player.name;
dobj = player:my_match_object(dobjstr);
if ($match_utils:object_match_failed(dobj, dobjstr))
return;
elseif (!dobj:is_readable_by(player))
player:tell("You aren't allowed to peek at ", dobj:pp(), " naughty bits.");
return E_PERM;
endif
areas = dobj:body_areas();
bland = {};
nudity = dobj.nudity;
for i in [1..length(areas)]
name = strsub(areas[i].name, " ", "_");
if (desc = nudity[i])
player:tell("@naked_", name, " ", dobjstr, " is \"", desc, "\"");
else
bland = {@bland, name};
endif
endfor
prep_dobj = (dobj == player) ? $you | dobj;
if (bland)
player:tell(prep_dobj:ppc(), " individual areas ", $string_utils:english_list(bland), " ", (length(bland) == 1) ? " is " | "are", " undescribed.");
endif
areas = dobj.grouped_body_areas;
bland = {};
nudity = dobj.grouped_body_areas_nudity;
for i in [1..length(areas)]
name = strsub(areas[i], " ", "_");
if (desc = nudity[i])
player:tell("@naked_", name, " ", dobjstr, " is \"", desc, "\"");
else
bland = {@bland, name};
endif
endfor
if (bland)
player:tell(prep_dobj:ppc(), " grouped areas ", $string_utils:english_list(bland), " ", (length(bland) == 1) ? " is " | "are", " undescribed.");
endif
player:tell("Type `@naked_<area> ", dobjstr, " is \"Whatever it looks like.\"` to describe an area.");
.
#78:39
":_match_body_area_exactly(string)";
"-> The first body area with NAME equal to the given string.";
"-> 1 if no match.";
"No ambigs, no muss, no fuss.  Quick and dirty.";
string = args[1];
for area in (this.body_areas)
if (area.name == string)
return area;
endif
endfor
return 1;
.
#78:40
"Allow GameMasters to control cosmetic aspects of characters.";
return pass(@args) || $rpg:trusted(args[1]);
.
#78:41
":trusts(who[, to-do-what])";
"Does this character trust who to-do-what?  If what is not given, does the character trust who completely?";
{who, ?what = this} = args;
if (what == this)
crit = 0;
elseif (r = this:is_trustable_action(what))
what = r[1];
crit = what:is_critical_action();
else
what = #-1;
crit = 0;
endif
complete = this:get_trustees(this);
if ((!crit) && ($everyone in complete))
"...this object trusts EVERYONE...";
return "everyone";
elseif ((!crit) && (who in complete))
"...who is trusted completely...";
return "complete";
elseif (this:is_writable_by(who))
"...who 'owns' this object...";
return "controls";
elseif (what == this)
return 0;
endif
base = what:trusts(this, who);
if ((!base) && what:do_while_unconscious(who, this))
return 1;
endif
return base;
.
#78:42
":is_trustable_action(what)";
"Can the given action be trusted to be performed by others?  If so, return its index in the trusted_actions property of this character.";
all = this:trustable_actions();
what = args[1];
if (typeof(what) == OBJ)
return (what in all) ? {what} | 0;
else
matched = $match_utils:match(what, all);
return valid(matched) ? {matched} | 0;
endif
.
#78:43
":add_trusted_object(who, to-do-what)";
"Add who as an object trusted to-do-what.";
{who, ?what = this} = args;
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
if (i = $list_utils:iassoc(what, this.trustees))
return this.trustees[i][2] = setadd(this.trustees[i][2], who);
else
new_trust = {what, {who}};
this.trustees = {@this.trustees, new_trust};
return new_trust;
endif
.
#78:44
":add_trusted_object(who, to-do-what)";
"No longer trust the given object to-do-what.";
{who, ?what = this} = args;
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
if (i = $list_utils:iassoc(what, this.trustees))
new_trust = setremove(this.trustees[i][2], who);
if (!new_trust)
this.trustees[i..i] = {};
else
return this.trustees[i][2] = new_trust;
endif
return new_trust;
else
return E_NONE;
endif
.
#78:45
":parse_trust_args(@args) -> Action being trusted.";
i = prepstr in args;
l = length(args);
if ((!i) || (i == l))
return {};
endif
args = args[i + 1..l];
if (args[l] == "Me")
what = $string_utils:from_list(args[i..l - 1]);
if (r = this:is_trustable_action(what))
return r;
endif
endif
for i in [1..l - i]
what = $string_utils:from_list(args[1..i], " ");
if (r = this:is_trustable_action(what))
return r;
endif
endfor
.
#78:46
"@trust/@untrust <who>";
"Trust (or stop trusting) the given character to do anything to you.";
"";
"@trust/@untrust <who> to <action> [me]";
"";
"Trust (or stop trusting) the given character to perform the given action on you.";
"";
"You may use 'Everyone' as 'who' to trust everyone to perform a certain action, but you should be especially careful when doing so.  See 'help trustable-actions' for warnings and full descriptions of all actions.";
if (caller != this)
return E_PERM;
elseif (!args)
player:tell_lines($code_utils:verb_documentation());
player:tell();
player:tell("You may trust someone to ", $string_utils:name_list(this:trustable_actions(), "do nothing", " or "), " you, or just '@trust <who>' to put complete trust in them.");
return E_ARGS;
elseif (!dobjstr)
player:tell(verb, " whom?");
return E_ARGS;
elseif (iobjstr)
what = this:parse_trust_args(@args);
else
what = {};
endif
if (typeof(what) != LIST)
player:tell("You may trust someone to ", $string_utils:name_list(this:trustable_actions(), "do nothing", " or "), " you, or just '@trust <who>' to put complete trust in them.");
return E_INVARG;
endif
who = player:my_match_object(dobjstr);
if (!valid(who))
who2 = $match_utils:match_player(dobjstr);
if (valid(who2))
who = who2;
elseif (who == $ambiguous_match)
$match_utils:object_match_failed(who, dobjstr, $match_utils:match_list(dobjstr, player:environment()));
return E_INVARG;
elseif (who2 == $ambiguous_match)
$command_utils:player_match_failed(who2, dobjstr);
return E_INVARG;
elseif (who2 == $failed_match)
player:tell("The name \"", dobjstr, "\" matches neither a local object nor a player.");
return E_INVARG;
endif
endif
if (!$rpg:is_character(who))
player:tell("The specified trustee isn't a sentient being.  Weirdo.");
return E_INVARG;
elseif (verb == "@trust")
if (this:trusts(who, @what))
player:tell("You already trust ", who:name(), " to do that.");
return E_NONE;
endif
elseif (!this:trusts(who, @what))
player:tell("You don't trust ", who:name(), " to do that.  No need to @untrust ", who:po(), ".");
return E_NONE;
endif
if (verb == "@trust")
this:add_trusted_object(who, @what);
if (what == {})
player:tell("You now put complete trust in ", who:name(), ".");
else
player:tell(who:name(), (" is now trusted to \"" + what[1]:name()) + "\" you.");
endif
else
this:remove_trusted_object(who, @what);
if (what == {})
player:tell("You no longer put complete trust in ", who:name(), ".");
elseif (this:trusts(who))
player:tell("Warning!  While ", who:name(), " has been removed from the list of those who can specifically \"", what[1]:name(), "\" you, you still put complete trust in ", who:po(), ".  Type '@untrust ", who, "' to undo this.");
else
player:tell(who:name(), (" is no longer trusted to \"" + what[1]:name()) + "\" you.");
endif
endif
.
#78:47
"@trusted";
"With no args, show everyone you trust and everything you trust them to do.";
if (caller != this)
return E_PERM;
endif
action_objects = this:trustable_actions();
action_names = $list_utils:map_verb(action_objects, "name");
longest = length($list_utils:longest(action_names)) + 1;
trusted = untrusted = {};
for action in (action_objects)
if (trustees = action:get_trustees(this))
trusted = {@trusted, tostr($string_utils:left(action:name(), longest), " : ", $string_utils:name_list(trustees))};
else
untrusted = {@untrusted, action:name()};
endif
endfor
if (trusted)
player:tell($string_utils:left("Action", longest + 3), "Those you trust to perform it...");
player:tell("-----------------------------------------------------");
player:tell_lines(trusted);
player:tell("-----------------------------------------------------");
endif
RPG = $rpg;
if (complete = this:get_trustees())
player:tell("You put complete trust in ", $string_utils:name_list(complete), ".");
endif
if (untrusted)
player:tell("You trust nobody ", complete ? "else " | "", "to ", $string_utils:english_list(untrusted, "do anything", " or "), " you.");
endif
.
#78:48
":my_call_verb(obj, vrb, args) -> Call obj:(verb)(@args).";
if (!$perm_utils:controls(caller_perms(), this))
return E_PERM;
endif
set_task_perms(this);
$code_utils:call_verb(@args);
.
#78:49
":clothing([area])";
"Return either the raw clothing property, or the clothing worn on the given area.";
"Return E_PROPNF if a body area is given and not one of this character.";
if (!args)
return this.clothing;
elseif (i = args[1] in this.body_areas)
return this.clothing[i];
else
return E_PROPNF;
endif
.
#78:50
":trustable_actions() -> A list of all trust objects.";
return children($trust_db);
.
#78:51
"strip <clothing> from <character>";
"Strips the given piece of clothing from this character.  E must trust you to strip em.  See `help trust` for more information.";
here = player.location;
if (here != this.location)
player:tell("You aren't close enough to ", this:name(), " to do that.");
return E_RANGE;
elseif (player == this)
player:tell("Why don't you just 'remove ", dobjstr, "' instead?");
return E_RECMOVE;
endif
what = this:match_clothing(dobjstr, 1);
if (what == $nothing)
player:tell(this:dnamec(), " is buck nekkid!");
return $nothing;
elseif (!what)
player:tell(this:dnamec(), " isn't wearing anything resembling a \"", dobjstr, "\".");
return $failed_match;
elseif (length(what) > 1)
player:tell(this:dnamec(), " is wearing ", $string_utils:name_list(what), ".  Which do you want to strip from ", this:po(), "?");
return $ambiguous_match;
endif
what = what[1];
if (!what:is_unlocked_for(player))
player:tell(what:dnamec(), " doesn't seem to wanna come off.");
elseif (this:trusts(player, "strip"))
if (what:do_remove())
dobj = iobj;
what:announce_messages("strip");
what:moveto(player);
endif
else
this:tell(player:dnamec(), " tried to strip off your ", what:name(), "!");
player:tell(this:dnamec(), " doesn't want you stripping ", this:po(), ".");
endif
.
#78:52
":match_clothing(string[, list-mode])";
"-> Match on the top layers of clothing on this character.";
"If 'list-mode' is given and true, list results are given instead of the usual $failed_match, etc.";
"$nothing is returned if no clothing is being worn.";
string = args[1];
clothes = {};
if (!string)
return $failed_match;
endif
for a in (this.clothing)
if (a)
clothes = setadd(clothes, a[1]);
endif
endfor
if (!clothes)
return $nothing;
elseif (!args[2])
return $match_utils:match(string, clothes);
else
return $match_utils:match_list(string, clothes);
endif
.
#78:53
":connected_seconds() -> How long this character has been connected.";
return `connected_seconds(this) ! ANY => -1';
.
#78:54
":idle_seconds()";
"-> How long this character has been idle.  -1 if not connected, or error.";
return `idle_seconds(this) ! ANY => -1';
.
#78:55
":namec()";
"-> Maybe a character wants a special capitalization.  Let them set namec.";
return this.namec || $string_utils:capitalize(this:name());
.
#78:56
":is_holding(obj)";
"Is this character holding the given object?";
"Right now, 'holding' just means being located on the character.  In the future, it should mean actually having the object readied.";
return args[1].location == this;
.
#78:57
":is_enclosing(obj)";
"The contents of a character are being held, and can perceive the environment of that character.";
return 0;
.
#78:58
"look/smell/taste/feel/listen [<object>]";
"Perceive something around you.";
where = player:room();
sense = (verb[2] == "i") ? "hear" | ((verb[1] == "l") ? "look" | verb);
sense_verb = sense + "_self";
if ((!dobjstr) && (!prepstr))
if (where:(sense_verb)() == E_VERBNF)
player:tell(("You " + sense) + " nothing special.");
endif
return;
endif
pool = player:environment();
if ((prepstr != "in") && (prepstr != "on"))
if ((!dobjstr) && (prepstr in {"at", "to"}))
dobjstr = iobjstr;
iobjstr = "";
else
dobjstr = dobjstr + (prepstr && ((dobjstr && " ") + prepstr));
dobjstr = dobjstr + (iobjstr && ((dobjstr && " ") + iobjstr));
endif
dobj = player:my_match_object(dobjstr, pool);
if (((!valid(dobj)) && (dobj != $ambiguous_match)) && (detail = where:get_detail(dobjstr)))
player:tell_lines(detail);
elseif (valid(dobj))
if (dobj:(sense_verb)() == E_VERBNF)
player:tell(("You " + sense) + " nothing special.");
endif
elseif (parsed = $match_utils:parse_possessive_reference(dobjstr))
"check for details such as `look at bob's nose'";
dobj = player:my_match_object(parsed[1], pool);
if ($match_utils:object_match_failed(dobj, parsed[1], pool))
"failure";
elseif (detail = dobj:get_detail(parsed[2]))
player:tell_lines(detail);
else
player:tell("You don't notice anything remarkable about ", (player == dobj) ? "your" | (dobj:pp() || "its"), " ", parsed[2], ".");
endif
elseif (!$match_utils:object_match_failed(dobj, dobjstr, pool))
endif
elseif (!iobjstr)
player:tell(verb, " ", prepstr, " what?");
else
if (m = match(dobjstr, "^at +%(.+%)"))
"allow for `look at knob on door', etcetera";
dobjstr = substitute("%1", m);
endif
iobj = player:my_match_object(iobjstr, pool);
if (!$match_utils:object_match_failed(iobj, iobjstr, pool))
if (dobjstr == "")
if (iobj:(sense_verb)() == E_VERBNF)
player:tell(("You " + sense) + " nothing special.");
endif
elseif ((!valid(thing = iobj:match(dobjstr))) && (detail = iobj:get_detail(dobjstr)))
player:tell_lines(detail);
elseif (thing == $failed_match)
player:tell("You don't notice any \"", dobjstr, "\" ", prepstr, " ", iobj.name, ".");
elseif ($match_utils:object_match_failed(thing, dobjstr, iobj:contents()))
"...fancy handling of ambigs...";
else
if (thing:(sense_verb)() == E_VERBNF)
player:tell(("You " + sense) + " nothing special.");
endif
endif
endif
endif
"Quinn 18-NOV-93 1642: Hacked to support $detailed.";
"Quinn 25-AUG-94 0022: Now supports all senses and their appropriate foo_self verbs.  System is still very much visual-oriented though.  Will be hard to shake.  Details are an inconsistency.";
.
#78:59
"Emote something in our current location.";
info = $object_utils:has_callable_verb(where = player.location, "emote");
if (info && (info[1] != $room))
where:emote(@args);
else
if (!argstr)
text = tostr(player:inamec(), " ", argstr);
elseif (argstr[1] == ":")
text = tostr(player:inamec(), argstr[2..length(argstr)]);
elseif (argstr[1] == "'")
text = tostr(player:inamec(), argstr);
else
text = tostr(player:inamec(), " ", argstr);
endif
where:announce_all(text);
endif
"where:broadcast_event_motion(player, text, 5);";
.
#78:60
":get_integration_msg(object) -> Integration info for 'object'.";
o = args[1];
if ((m = o:person_integration_msg(this)) != E_VERBNF)
return m;
elseif ((m = o:look_person_msg(this)) != E_VERBNF)
return m;
else
return "";
endif
.
#78:61
"gibber  <text> -- Say `text' as an english-sounding encryption.";
$gibber_feature:(verb)(@args);
.
#78:62
":available_exits()";
"Return a list of exits which aren't locked or blocked, which this character can go through.";
exits = {};
for exit in (this:room():obvious_exits())
if (exit:is_unlocked_for(this))
exits = {@exits, exit};
endif
endfor
return exits;
.
#78:63
":move_through(exitspec)";
"Move this character through the exit referred to by the given value.";
"An object is taken to be an actual exit object.";
"A string is taken to be a direction that is handled by a verb on the room.";
exit = args[1];
room = this:room();
player = this;
if (typeof(exit) == OBJ)
exit:invoke();
elseif (dir = $exit_utils:match_direction(exit))
room:(dir)(@args);
else
return E_INVARG;
endif
"--WiZARDLY to set player--";
.
#78:64
":get_option(options_pkg, option_name)";
"(Here so some verbs assuming its presence on a puppet don't choke)";
return args[1]:get({}, @listdelete(args, 1));
.
#78:65
"Some actions taken on the MOO should be interpreted as yours--the person typing the commands.  Others should be taken as your character--your representation in the virtual reality.";
"";
"You can make it clear to those around just _who_ you are by using the @ic and @ooc commands.";
"";
"@ic   -- Go `in character', playing your character.";
"@ooc  -- Go `out of character', being you.";
"";
"Adding a ! to the command (@ic!, @ooc!) has one of two effects:";
"";
"If you are not already in the mode associated with the command, you will be switched into that mode and those around you will be informed.";
"If you _are_ in that mode, the room will be reminded.  This is useful if, for instance, your character is being especially cruel and you want to remind everyone that it is not YOU who are strangling that kitten, but Sadistic_Kitten_Strangler.";
if (caller != this)
return E_PERM;
elseif ($rpg:is_npc(this))
player:notify("You are an NPC.  Your sole purpose is to be an in-character plot device.  You can't go OOC.");
return;
elseif ((!argstr) || (argstr in {"yes", "on"}))
new = verb[2] == "i";
elseif (argstr in {"no", "off"})
new = verb[2] == "o";
else
player:notify_lines($code_utils:verb_documentation());
return E_ARGS;
endif
announce = verb[$] == "!";
if (msg = $rpg:maybe_forbid_ic_switch(player, new))
player:notify(msg);
return E_NACC;
endif
if (this.ic == new)
if (!announce)
player:notify("You are already " + (new ? "in character." | "out of character."));
elseif (new)
$you:say_action("( %N %<reminds> everyone that %s %<is> playing in-character, and that %p actions do not necessarily reflect the feelings of the person behind the screen. )");
else
$you:say_action("( %N %<reminds> everyone that %s %<is> not in-character, and that everything said and done is a reflection of the person behind the screen. )");
endif
elseif (new)
player:notify("You go in-character.  Your actions now reflect your character and not necessarily yourself.");
if (announce)
player:room_announce($string_utils:pronoun_sub("( %N %<goes> in-character. %P actions should now be understood as being from %p character, and not necessarily the person behind the screen. )"));
endif
elseif (player:in_combat())
player:notify("You are currently engaged in combat.  Try fleeing or making peace with thine enemies before going @ooc.");
return;
else
player:notify("You go out-of-character.  Your actions should now be taken as your own and not those of your character.");
if (announce)
player:room_announce($string_utils:pronoun_sub("( %N %<goes> out-of-character.  %P actions should now be understood as being from the person behind the screen, and not %p character. )"));
endif
endif
player.ic = new;
.
#78:66
":disfunc(quit?)";
"Clear following list if the player actually @quit or was booted, but NOT if their client disconnected.";
if (!(caller in {this, #0}))
return E_PERM;
elseif (args && args[1])
clear_property(this, "following");
endif
.
#78:67
"grab <something> from <someone>";
"Take something from the given person.  They must either trust you to take it, or be unconscious.";
this:do_grab_from(@args);
.
#78:68
":set_my(property, value)";
"Called by $last_huh:@, this allows for the very English `@my prop is val` syntax.";
set_task_perms(caller_perms());
what = $string_utils:lowercase(args[1]);
value = args[2];
if (what == "name")
result = this:set_name(value);
if (!(value in this.aliases))
this:set_aliases(setadd(this.aliases, value));
endif
elseif (what == "gender")
result = this:set_gender(value);
else
return 0;
endif
if (!result)
player:notify(tostr("You fail to change the ", what, " of ", this:name(), " because of \"", result, "\"."));
return 0;
elseif (player == this)
player:notify(tostr("You set your ", what, " to ", $string_utils:print(value), "."));
else
player:notify(tostr("You set the ", what, " of ", this:name(), " to ", $string_utils:print(value), "."));
endif
return 1;
.
#78:69
":description()";
desc = pass(@args);
if ((player.location == this.location) && (clothed = this:clothed_description()))
return desc ? $string_utils:lines_to_list(desc, clothed) | clothed;
endif
return desc;
.
#78:70
"ooc  bleah";
"Say something out of character.";
"ooc :emote";
"Emote something out of character.";
lv = length(verb);
if ((lv > 3) && (verb[4] == ":"))
argstr = (verb[4..lv] + " ") + argstr;
endif
if (!argstr)
player:notify("Do what out of character?");
return;
endif
if (argstr[1] == ":")
argstr[1..1] = "";
if (argstr && (argstr[1] == "'"))
argstr = ":" + argstr;
endif
if (argstr && (argstr[1] == ":"))
argstr[1..1] = "";
text = tostr("<OOC> ", player:dnamec(), argstr);
else
text = tostr("<OOC> ", player:dnamec(), " ", argstr);
endif
player:room_announce_all(text);
elseif (said = player:speech_msg(argstr))
$you:say_action(tostr("<OOC> ", said));
else
$you:say_action(tostr("<OOC> ", "%(dnamec) %<says>, \"", $string_utils:pronoun_quote(argstr), "\""));
endif
.
#78:71
"abandon <character>";
"Stop following the given character.";
room = player:room();
pool = $set_utils:union(room:matching_contents(), player.following);
dobj = player:my_match_object(dobjstr, pool);
if ($match_utils:object_match_failed(dobj, dobjstr, pool))
return;
endif
if (a = player in dobj.following)
dobj.following = setremove(dobj.following, player);
player:tell("You will lose ", dobj:dname(), " with your next exit.");
endif
if (b = dobj in player.following)
player.following = setremove(player.following, dobj);
$you:say_action("%N %<stops> following %d.");
endif
if ((!a) && (!b))
player:tell($string_utils:pronoun_sub("You aren't following %[ddname] and %[dps] %<d:isn't> following you."));
endif
.
#78:72
"When seated or reclining on furniture, you can talk and pose to those sitting with you.";
"  tt Hey, scoot over.";
"  tt :nudges you in the shoulder.";
"Remember: These actions are visible only to those sitting with you, so you can talk privately.";
lv = length(verb);
if ((0 && (lv > 2)) && (verb[3] == "!"))
"Using `tt!` instead of `tt` will announce to everyone in the room, but still prefix the message.";
to_all = 1;
verb[3..3] = "";
lv = lv - 1;
else
to_all = 0;
endif
if ((lv > 2) && (verb[3] == ":"))
argstr = (verb[3..lv] + " ") + argstr;
endif
room = player:room();
seat = room:find_seat(player);
if (!valid(seat))
player:tell("You aren't seated or reclining here.");
return;
elseif (!argstr)
player:tell_lines($code_utils:verb_documentation());
return;
endif
say_to = to_all ? room:contents() | seat:occupants();
prefix = seat:tt_prefix_msg(player);
if (argstr[1] == ":")
argstr[1..1] = "";
if (argstr && (argstr[1] == "'"))
argstr = ":" + argstr;
endif
if (argstr && (argstr[1] == ":"))
argstr[1..1] = "";
text = tostr(prefix, player:dnamec(), argstr);
else
text = tostr(prefix, player:dnamec(), " ", argstr);
endif
else
said = player:speech_msg(argstr) || tostr("%(dnamec) %<says>, \"", argstr, "\"");
text = tostr(prefix, $string_utils:pronoun_sub(said));
endif
for pc in (say_to)
pc:announce_to(text, {});
endfor
.
#78:73
":sub_name()";
"Return :iname if .use_article is true.";
if (this.use_article)
verb[1..4] = "i";
return this:(verb)(@args);
else
return pass(@args);
endif
.
#78:74
":last_huh(verb, args)";
"Check for a '_last_COMMAND_NAME' verb on this character, and execute it if it exists and matches the current command line.";
set_task_perms(caller_perms());
verb = args[1];
args = args[2];
if ($last_huh:call_social_verb(verb, args))
return 1;
elseif ($match_utils:match_verb(tostr("_last_", verb), this, args))
return 1;
endif
.
#78:75
verb[1..6] = "";
return $pose_feature:(verb)(@args);
.
#78:76
"@ways";
"Show the ways out of this room.";
player:room():tell_exits();
.
#78:77
"carry someone";
"Take someone into your arms and carry them around.  For simplicity's sake, only unconscious characters may be carried, and drop free when they awaken.";
"You may stop carrying someone at any time by typing `stop carrying their_name`.";
dobj = player:my_match_object(dobjstr);
if ($match_utils:object_match_failed(dobj, dobjstr, player:env()))
return;
elseif (!$rpg:is_character(dobj))
player:tell("Don't make such a fuss over ", dobj:iname(), "; just try picking ", dobj:dname(), " up.");
elseif (dobj == player)
player:tell("You don't really want to carry yourself, do you?  That would break certain immutable laws of Physics.");
elseif (!dobj:attempt_pickup(player))
return;
elseif (!dobj:is_carryable_by(player))
dobj:announce_carry_failure(player);
else
"--Wizardly--";
move(dobj, player);
if (dobj.location == player)
dobj:announce_carry_success(player);
else
dobj:announce_carry_failure(player);
endif
endif
.
#78:78
":eject_character(who, reason)";
"Eject 'who' from this character into the containing room.  Provided for easy hacking by those who might want special behaviour.";
"The 'reason' argument is usually the name of the method that called this one.  They'll be listed below as added.";
"wake_up: WHO woke up inside this character, and wriggled onto the ground.";
player = args[1];
room = this:room();
dobj = player.location;
if (dobj != this)
return;
endif
"Don't bother checking the reason, since there's only one right now.";
move(player, room);
$you:say_action("%N %<wriggles> out of %d's grasp and onto the ground.");
.
#78:79
":features()";
"Return the contents of the features property, plus standard features kept on $command_utils.";
return $set_utils:union(this.features || {}, $command_utils:standard_features(this));
.
#78:80
return tostr("carried by ", this:dname(), " in ", this.location:dname());
.
#78:81
":sight_level()";
"Return a general indication of how much this character can see in the its current location, which is actually a percentage (the sight attribute) of the room's light level.";
"The final number will be from 1-100.  If less than 10, some actions may fail.";
return (this.location:light_level() * this:stat_sight()) / 100;
.
#78:82
":receive_event(event)";
E = args[1];
if (E.type_str == "AV")
return this:tell(E:text_for(this));
endif
.
#78:83
"Hook to the central RPG machine, allowing it to prod hearts to act.";
pass(@args);
$rpg:maybe_prod_hearts();
.
#78:84
":cpr()";
"Add us to our heart and start it pumping.";
"This should more properly be on $player and $robot, but I didn't wanna bother with two copies, and it's !d anyway, so...";
this.heart:add(this);
this.heart:pump();
.
#78:85
":look_place_msg()";
"%N -- This  %D -- Player";
msg = this.(verb);
if (!msg)
return "";
endif
msg = $string_utils:pronoun_sub(msg, this, this, this.location, player);
if (is_player(this))
return index(msg, this:name()) ? msg | "";
else
return msg;
endif
.
#78:86
":provoke_mood()";
"Prod this object and surrounding ones to :announce_mood.";
if (this:idle_seconds() > 120)
return;
endif
provokable = {this.location};
"Only announce mood for this character if should_be_moody.  <Quinn;19980816>";
if (this:should_be_moody())
provokable = {@provokable, this, @this:holding()};
endif
for o in (provokable)
if ((random(3) == 1) && o:announce_mood())
suspend(5 + random(5));
endif
endfor
.
#78:87
":do_behave()";
"In here is where you stick some behaviour.  Scenes and such for AHaBs, a default :provoke_mood() for everyone.";
this:provoke_mood();
.
#78:88
":pulse()";
"Perform various bodily functions: healing, bleeding, consciousness checks.";
this:do_metabolism();
this:do_behave();
return pass(@args);
.
#78:89
":take_random_exit()";
"You crazy-ass son of a bitch!  What are you, insane, taking random exits like that??";
exits = args ? args[1] | this:room():obvious_exits();
prev_loc = this.location;
for exit in ($list_utils:randomly_permute(exits))
if (exit:move(this) && (this.location != prev_loc))
return 1;
endif
endfor
return 0;
.
#78:90
":is_carryable()";
":is_carryable_by(user)";
"Return true if this object is carryable[ by user].  By default, only unconscious characters may be carried.";
return this:unconscious() && (!$object_utils:isa(this.location, $generic_editor));
.
#78:91
":announce_carry_success/failure(user_who_carries)";
"Print messages announcing that this character has been picked up to be carried by the given user.";
"Should provide for override by carrying person in the future.";
actor = args[1];
dobj = this;
if (verb == "announce_carry_success")
$you:say_action("%N %<scoops> %d into %p arms and %<slings> %[dpo] over %p shoulders.", actor);
actor:tell(dobj:grammar_sub("(Type `stop carrying %n` when you want to stop carrying %o, or just wait until %s %<wakes> up.)"));
elseif (verb == "announce_carry_failure")
$you:say_action("%N %<tries> to get a good grip on %d, but despite moments of comical contortion and awkward groping, %s %<fails> to pick %[dpo] up.", actor);
else
$you:say_action("%N gently %<releases> %d to the ground.", actor);
endif
.
#78:92
":do_grab_from(@args)";
"Guts method for grab/swipe/take wrapper.";
verb = "grab";
if (!player:is_local_to(this))
player:notify(("You'll have to get closer to " + this:dname()) + " before trying that.");
return E_RANGE;
elseif ($match_utils:object_match_failed(dobj = this:match_contents(dobjstr), dobjstr, this:matching_contents(), "on " + this:po()))
return;
elseif (!this:trusts(player, "take from"))
player:notify(tostr(this:dnamec(), " doesn't want you taking things from ", this:po(), ".  Maybe try asking ", this:po(), " to '@trust ", player.name, " to take from me'."));
return E_PERM;
endif
dobj:moveto(player);
if (player:is_holding(dobj))
$you:say_action(tostr("%N %<", verb, "s> %[diname] from %t."));
else
$you:say_action(tostr("%N %<attempts> to ", verb, " %[diname] from %t, but %[ddname] apparently does not want to go."));
endif
.
#78:93
"Pass speech, sound, and motion events inside this object.";
for o in (this:contents())
o:(verb)(@args);
endfor
"In a perfect world, hearing these events would be *the* method of beign aware of emotes, says, and any other VR action.  It ain't a perfect world.";
.
#78:94
":emptyhanded_msg() -> What to display when you're empty-handed.";
msg = this.(verb);
return msg && $string_utils:pronoun_sub(msg, this, this, this.location, player);
.
#78:95
":filter_speech(STR spoken_text)";
"Do any kind of transmuting of the speech.  Slur when drunk, gibber when insane, make someone mute.  You get the idea.";
return args[1];
.
#78:96
":stop_carrying(OBJ character)";
{dobj} = args;
if (!this:is_holding(dobj))
player:notify(player:grammar_sub("You aren't holding %[diname]."));
return;
elseif (!$rpg:is_character(dobj))
this:drop(dobjstr);
return;
endif
where = this:room();
dobj:moveto(where);
if (dobj.location == where)
dobj:announce_drop_success(player);
else
player:notify(player:grammar_sub("You can't seem to drop %[ddname] here."));
endif
.
#78:97
msg = "";
if (`player:get_option($display_options, "rpg_titles") ! E_VERBNF => 0')
if (is_player(this) || `player:get_option($display_options, "npc_titles") ! E_VERBNF => 0')
msg = `this:title_msg() ! ANY => "!"';
endif
endif
return msg ? tostr(this:name(), " (", msg, ")") | this:name();
.
#78:98
":title_msg()";
if (is_player(this) && (this:connected_seconds(this) < 1))
return "offline";
elseif ((i = this:idle_seconds()) > 60)
states = {"idle"};
else
states = {};
endif
if (!this.ic)
states = {@states, "OOC"};
endif
if (u = this:unconscious())
states = {@states, (u > 0) ? "KO'd" | "asleep"};
endif
NEG = 25;
if (this:stat_injury() > NEG)
states = {@states, "hurt"};
endif
if (this:stat_insanity() > NEG)
states = {@states, "nervous"};
endif
if (this:wielding())
states = {@states, "armed"};
endif
return $string_utils:from_list(states, ", ");
.
#78:99
":set_gm_notes(LIST)";
if (!$rpg:trusted(caller_perms()))
return E_PERM;
endif
{new} = args;
if (typeof(new) != LIST)
return E_TYPE;
endif
this.gm_notes = new;
.
#78:100
":get_gm_notes()";
if (!$rpg:trusted(caller_perms()))
return E_PERM;
endif
return this.gm_notes;
.
#78:101
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
if (this != $char)
for p in (properties($char))
`clear_property(this, p) ! ANY';
endfor
endif
this.home = $nowhere;
this.body_areas = children($humanoid_body_area);
this.nudity = $list_utils:make(length(this.body_areas), "");
$command_utils:suspend_if_needed(0);
this.hands = this:match_body_area("hand", 2);
.
#78:102
examiner = args[1];
isbuilder = $wiz_utils:is_builder(examiner);
if (isbuilder)
return pass(@args);
endif
{OBJUTILS, STRUTILS, TANGIBLE} = {$object_utils, $string_utils, $tangible};
visible = {};
for c in (this:contents())
if (`c:worn_by(this) ! E_VERBNF')
continue;
elseif (!OBJUTILS:isa(c, TANGIBLE))
continue;
else
visible = {@visible, "  " + c:name()};
endif
endfor
return visible ? {"Carrying:", @visible} | {"(Carrying nothing.)"};
.
#78:103
":get_trustees([what])";
"Return everyone trusted by this character to perform the given action.";
"This should probably have some permissions checks, for privacy reasons, but the primary :trusts interface has to be open for programmers anyway...";
{?what = this} = args;
trustees = $list_utils:assoc(what, this.trustees);
trusted = {};
if (trustees)
"Is the assumption that only characters can be trusted valid? <19980804>";
for d in (trustees[2])
if ($rpg:is_char(d))
trusted = {@trusted, d};
endif
endfor
endif
return trusted;
.
#78:104
":set_species(OBJ species)";
{species} = args;
if (caller != species)
raise(E_PERM);
endif
this.species = species;
.
#78:105
":is_wearing(OBJ)";
"Return true if the given object is being worn by this character.";
return args[1] in $list_utils:flatten(this:clothing());
.
#79:0
dobj = this;
return $string_utils:pronoun_sub(this.(verb), @args);
.
#79:1
"Show `olook' message to everyone but the player looking at this object.";
if ((player != this) && (player.location == this.location))
dobj = this;
if (msg = this:watched_msg())
this:tell(msg);
endif
if (0 && (msg = this:owatched_msg()))
player:room_announce_all_but({this}, msg);
endif
endif
pass(@args);
.
#79:2
":look_place_msg()";
"%N -- This  %D -- Player";
return (msg = this.(verb)) && $string_utils:pronoun_sub(msg, this, this, this.location, player);
.
#79:3
":look_person_msg()";
"%N -- Person  %T -- This  %L -- Person's location  %D -- Player";
return (msg = this.(verb)) && $string_utils:pronoun_sub(msg, this.location, this, this.location.location, player);
.
#79:4
"Pronoun-sub it!  Yeah!  Whee!";
return $string_utils:pronoun_sub(pass(@args), this, this, this.location, player);
.
#79:5
":smell_self()";
if (msg = this:aroma_msg())
player:tell(msg);
return;
endif
player:tell("You smell nothing special.");
.
#79:6
":taste_self()";
if (msg = this:taste_msg())
player:tell(msg);
return;
endif
player:tell("You taste nothing special.");
.
#79:7
":feel_self()";
if (msg = this:texture_msg())
player:tell(msg);
return;
endif
player:tell("You feel nothing special.");
.
#79:8
":hear_self()";
if (msg = this:sound_msg())
player:tell(msg);
return;
endif
player:tell("You hear nothing special.");
.
#79:9
":aroma_msg()";
"-> String or list describing the smell of this object.";
":taste_msg()";
"-> String or list describing the taste of this object.";
":texture_msg()";
"-> String or list describing the texture of this object.";
":sound_msg()";
"-> String or list describing the sound emanating from this object.";
return $string_utils:pronoun_sub(this.(verb), this, this, this.location, player);
.
#79:10
":is_bonded_to(char)";
"Anything that is +f, has children, is owned by a programmer and id'd as a generic, or is owned by the given character will stay with em when e dies.";
if ((b = this.bonded) != E_PROPNF)
return (typeof(b) == OBJ) ? b == args[1] | b;
elseif (args[1] == this.owner)
return 1;
elseif ((this.f || children(this)) || (this.owner.programmer && (index(this.name, "generic") || index(this.name, "prototype"))))
return 1;
else
return 0;
endif
.
#79:11
":set_v_size(NUM)";
"Set the virtual size of this object.  Must be a positive integer, or Zero.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
v_size = args[1];
if (typeof(v_size) != NUM)
return E_TYPE;
elseif (v_size < 1)
return E_RANGE;
endif
this.v_size = v_size;
return v_size;
.
#79:12
":attempt_pickup(user)";
"Print messages and return false if this object cannot be picked up by the given user.  Otherwise, be silent and return true.";
"Locking and other pre-VR core checks will be made later; this method should explain only VR difficulties such as 'too heavy', 'nailed down', etc.";
"The @locked_down and @olocked_down messages are provided for easy VR 'locking' of objects that just shouldn't be taken.  Note it will ward only against 'get' and 'take', not @move: If either is set, the object cannot be 'taken'.";
if (this.locked_down_msg || this.olocked_down_msg)
this:announce_messages("locked_down", @args);
return 0;
endif
return 1;
.
#79:13
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
if (this != $tangible)
for p in (properties($tangible))
`clear_property(this, p) ! ANY';
endfor
endif
.
#79:14
":new_effect_id()";
"Return a unique identifier for a new event.";
existing = $list_utils:slice(this.effects, 1);
tmp = task_id();
while (tmp in existing)
tmp = tmp + 1;
endwhile
return tmp;
.
#79:15
":add_effect(OBJ effect[, LIST args])";
"effect -- A child of $effect.";
"eargs  -- Information passed as first argument of each call to the effect.";
{effect, ?eargs = {}} = args;
id = this:new_effect_id();
eargs = effect:added(eargs, this, id);
this.effects = {@this.effects, {id, effect, eargs}};
return id;
.
#79:16
":remove_effect(INT id)";
"Remove the effect with the given id (returned by :add_effect).";
"Return false if no effect has the given id, otherwise return the old id.";
{id} = args;
i = $list_utils:iassoc(id, this.effects);
if (!i)
return 0;
endif
{id, effect, eargs} = this.effects[i];
effect:removed(eargs, this);
this.effects = listdelete(this.effects, i);
return id;
.
#79:17
":integrated_description([@integration-text])";
text = pass(@args);
return $string_utils:enlist(text, this:get_effect_integration());
.
#79:18
":get_effect_integration()";
fx = {};
for f in (this.effects)
fx = {@fx, @f[2]:build_integration_msg(f[3], this)};
endfor
return fx;
.
#79:19
":pulse()";
"Pass pulse to all current effects.";
expired = {};
for i in [1..length(this.effects)]
{id, effect, eargs} = this.effects[i];
n_eargs = effect:pulsed(eargs, this);
if (n_eargs == E_INVIND)
expired = {i, @expired};
else
this.effects[i][3] = n_eargs;
endif
endfor
for i in (expired)
this.effects = listdelete(this.effects, i);
endfor
return `pass(@args) ! E_VERBNF';
.
#79:20
{what} = args;
if (size = `what.v_size ! E_PROPNF')
this:update_bulk(size);
endif
return `pass(@args) ! E_VERBNF';
.
#79:21
{what} = args;
if (size = `what.v_size ! E_PROPNF')
this:update_bulk(-size);
endif
return `pass(@args) ! E_VERBNF';
.
#79:22
":update_bulk(INT size)";
"Update the bulk of this object by the given size, then do the same through all objects which contain this one.";
"Should only be called by itself, when something enters or exits.";
if (caller != this)
return raise(E_PERM);
endif
{size} = args;
this.v_bulk = max(this.v_bulk + size, 0);
container = this;
while (valid(container = container.location) && ((bulk = `container.v_bulk ! E_PROPNF') != E_PROPNF))
container.v_bulk = max(bulk + size, 0);
endwhile
.
#79:23
":dispatch_event_*(@event-args)";
"Relay this event (via :hear_event_*) to any interested objects.  In this default case, only effects are interested.";
if (!this:is_controllable_by(caller_perms(), caller))
raise(E_PERM);
endif
verb[1..8] = "hear";
set_task_perms(this);
for fx in (this.effects)
$object_utils:has_callable_verb(fx, "verb") && fx:(verb)(this, @args);
endfor
.
#79:24
":is_affected_by(OBJ effect)";
"Return true if this object is affected by the given effect.";
{effect} = args;
return $list_utils:iassoc(effect, this.effects, 2);
.
#80:0
":get_detail(STR name)";
"-> Description of detail matching string, or empty string.";
if (!this:is_readable_by(caller_perms(), caller))
return E_PERM;
elseif (!(i = this:match_detail(args[1])))
return "";
else
return this.detail_descs[i];
endif
.
#80:1
":match-detail(STR name)";
"-> Index of first detail with given name in this object's details list.";
string = args[1];
if (!string)
return;
endif
string = ("|" + string) + "|";
for i in [1..length(names = this.detail_names)]
if (index(names[i], string))
return i;
endif
endfor
.
#80:2
":set_detail(STR name, TEXT description)";
"-> Set the description of detail matching name.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
elseif (!(i = this:match_detail(args[1])))
return E_INVIND;
else
return this.detail_descs[i] = args[2];
endif
.
#80:3
":add_detail(LIST aliases, TEXT desc)";
"-> Add detail described as desc, referenced by all strings in aliases.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
names = args[1];
if (typeof(names) == LIST)
names = ("|" + $string_utils:from_list(names, "|")) + "|";
endif
if (i = names in this.detail_names)
this.detail_descs[i] = args[2];
return i;
else
this.detail_names = {@this.detail_names, names};
return length(this.detail_descs = {@this.detail_descs, args[2]});
endif
.
#80:4
":remove_detail(STR name) -> Remove the detail matching name.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
elseif (!(i = this:match_detail(args[1])))
return E_NONE;
else
this.detail_names = listdelete(this.detail_names, i);
return length(this.detail_descs = listdelete(this.detail_descs, i));
endif
.
#80:5
":detail_names/descs() -> Return names or descriptions of all details.";
if (!this:is_readable_by(caller_perms(), caller))
return E_PERM;
else
return this.(verb);
endif
.
#80:6
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.detail_names = this.detail_descs = {};
.
#81:0
"monitor <puppet>";
"Have everything heard by the puppet relayed back to you.";
who = (callers() && (caller != this)) ? caller_perms() | player;
if (this:monitor_ok(who))
this:add_monitor(who);
this:tell(who:inamec(), " begins monitoring.");
else
who:tell(this:dnamec(), " doesn't want you monitoring ", this:po(), ".");
endif
.
#81:1
"ignore <puppet>";
"Stop monitoring the puppet.";
who = (callers() && (caller != this)) ? caller_perms() | player;
if (!(who in this.monitors))
player:tell("You aren't listening to ", this.name, ".");
else
this:tell(who.name, " stops monitoring.");
this:del_monitor(who);
endif
.
#81:2
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
{who} = args;
return this.monitors = setadd(this.monitors, who);
.
#81:3
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
{who} = args;
return this.monitors = setremove(this.monitors, who);
.
#81:4
"Relay text to monitors.";
if (!this:is_listening())
return;
endif
text = tostr(@args);
if (this.monitors)
report = this:monitor_prefix_msg() + text;
for spy in (this.monitors)
if (`connected_seconds(spy) ! E_INVARG => 0')
spy:notify(report);
endif
endfor
endif
return pass(@args);
"--WIZARDLY (to :notify) --";
.
#81:5
"command <puppet> to <cmdline>";
"Command the puppet to `do' the given command line, processing it as if it were a command issued by a player.";
who = (callers() && (caller != this)) ? caller_perms() | player;
if (this:command_ok(who))
text = substitute("%1", match(argstr, ("%<" + prepstr) + "%> *%(.+%)$"));
if (!text)
player:tell("Transmit what to ", this.name, "?");
else
player:tell("You transmit the command \"", text, "\" to ", this.name, ".");
this:do(text);
endif
else
who:tell(this.name, " ignores your command.");
endif
.
#81:6
":do(STR cmdline) -> Parse and perform the given string.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
"Set last-do-time here, since a command was at least ATTEMPTED.";
this.last_do_time = time();
cmdline = args[1];
"This next check should probably be left to $sys:do_command?";
if ((`cmdline[1..4] ! E_RANGE' != "wake") && this:unconscious())
this:tell("You are not conscious.");
return E_NACC;
endif
force_input(this, cmdline);
return 1;
"-- WIZARDLY --";
.
#81:7
":find_verb(object, verbname, [esoteric parse-command shit])";
{where, vrb, ?match = {{}, {}, {}}, ?spec = {}} = args;
if (!$recycler:valid(where))
return E_INVIND;
endif
def = $object_utils:has_callable_verb(where, vrb);
if (!def)
return 0;
endif
{do, pn, io} = spec;
{darg, parg, iarg} = verb_args(def[1], vrb);
if (((darg == "this") && (parg == "none")) && (iarg == "this"))
return 0;
endif
return ((((darg == "this") && (where == do)) || (darg in match[1])) && (parg in match[2])) && (((iarg == "this") && (where == io)) || (iarg in match[3]));
"-- WIZARDLY --";
.
#81:8
"Show all monitors listening to the puppet.";
if (y = setremove(this.monitors, player))
return "is listening for " + $string_utils:name_and_number_list(y);
endif
.
#81:9
":call(who, obj, verb, args[, NOW])";
"This verb should NEVER be called by anything but :call_as_puppet!  It is a required second-step to keep the `player' value set there valid.";
if (!caller_perms().wizard)
return E_PERM;
endif
{who, object, vname, vargs, ?now = 1} = args;
set_task_perms(this);
return object:(vname)(@vargs);
"-- WIZARDLY --";
.
#81:10
":monitor/command_ok(who)";
"May who monitor/command the puppet?  By default pass to :is_controllable_by.";
return this:is_controllable_by(args[1]);
.
#81:11
":initialize()";
"Set home to owner's home, clear monitors list, add the puppet to the owner's `.puppets' list, if any.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
if (this.home == $puppet.home)
this:set_home(this.owner.home);
endif
this.monitors = {};
try
this.owner.puppets = setadd(this.owner.puppets, this);
except (E_PROPNF)
endtry
return pass(@args);
.
#81:12
":parse_command(cmdline-string) Pass to $string_utils by default.";
return $string_utils:parse_command(@args);
.
#81:13
":call_as_puppet(OBJ verbloc, STR verbname, LIST args)";
"Calls the given verb, after setting 'player' to this 'puppet'.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
player = this;
try
result = this:call(this, @args);
return result;
except tbk (ANY)
0 && #2:tell(">>> ", this, " -> ", toliteral(tbk));
this:tell_lines($error:format_traceback(tbk));
endtry
"-- WIZARDLY --";
.
#81:14
":monitor_prefix_msg() -> Msg prepended to text echoed to monitors.";
"Hrm--we'll just strsub(msg, %N, this.name)";
return strsub(this.(verb), "%N", this.name);
"157 ticks right down there, boyo!  For EVERY tell, we had that overhead.";
return $string_utils:pronoun_sub(this.(verb), this, this, this.location, player);
.
#81:15
"Allow shapers all the priveleges of ownership.";
return pass(@args) || this:is_shaper(@args);
.
#81:16
"Return whether the given player a shaper, or a list of all shapers.";
shapers = setadd(this.shapers, this.owner);
return (verb == "is_shaper") ? args[1] in shapers | shapers;
.
#81:17
"Remove the puppet from the owner's `.puppets' list, if any.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
try
this.owner.puppets = setremove(this.owner.puppets, this);
except (E_PROPNF, E_INVIND)
endtry
return pass(@args);
.
#81:18
"Handle the case where the puppet's home is an exit.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
elseif ($object_utils:isa(home = this.home, $exit))
home:move(this);
return 1;
elseif (entrances = `home.entrances ! E_PROPNF, E_PERM')
entrance = $list_utils:random_element(entrances);
entrance:move(this);
if (this.location == home)
return 1;
endif
endif
return pass(@args);
.
#81:19
":do_scene(LIST actions) -- Perform the given list of actions in sequence.";
"The following special frames (actions within a scene) affect the playing of the scene.";
";<evaluation> -- Eval the remainder of the frame after the semicolon.";
"                 If the result is a string, :do that string.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
scene = (typeof(args[1]) == LIST) ? args[1] | args[1..1];
for frame in (scene)
if (frame[1] == ";")
frame[1..1] = "";
v = this:my_eval_d(frame);
if ((!v[1]) || (typeof(v[2]) != STR))
"...do nothing...";
else
this:do(v[2]);
endif
else
this:do(frame);
endif
endfor
.
#81:20
":hear_event_attack(attacker, target)";
":hear_event_heal(healer, patient, kit, result)";
"Pass to the basic combat reactor, to give combat behaviour to lower puppets.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
elseif (this.reactors)
"...temp hack to allow dispatching...";
return pass(@args);
else
this:interpret_reactor_result($rpg.basic_combat_reactor:(verb)(this, @args));
endif
.
#81:21
"@set-home <puppet> to <new-home>";
"Attempt to set this puppet's home to the given new home.";
if ((player != caller) && (caller_perms() != player))
return E_PERM;
endif
home = player:my_match_room(iobjstr);
if ($match_utils:room_match_failed(home, iobjstr))
return;
endif
set_task_perms(player);
result = this:set_home(home);
if (result == 1)
player:tell("The home of ", this:dname(), " is now ", home:dname(), ".");
elseif (result == E_PERM)
player:tell("You aren't allowed to set the home of ", this:dname(), ".");
elseif (result == E_TYPE)
player:tell(home:dnamec(), " doesn't seem to be a valid place to set ones home.");
elseif (result == E_INVARG)
player:tell("The owner of ", home:dname(), " (", home.owner:name(), ") hasn't approved ", this:dname(), " to stay there.");
else
player:tell("Couldn't set ", home:dname(), " as home of ", this:dname(), ": ", result);
endif
return result;
.
#81:22
"How long since the puppet last :did something?";
return min(time() - this.last_do_time, this:connected_seconds());
.
#81:23
":connected_seconds()";
return -1;
.
#81:24
":is_listening()";
"-> True if we have monitors.  Whee.";
return this.monitors && pass(@args);
.
#81:25
":is_unique()";
"A weirdo way of checking if this isn't just another spawned beastie.";
if (pass(@args))
return 1;
endif
kids = setremove(children(parent(this)), this);
return kids ? kids[1].name != this.name | 1;
.
#81:26
":behave()";
if (this.location == this:get_afterlife())
return this:go_home();
endif
info = this.last_attacked_by;
if (((info && valid(agg = info[1])) && (info[2] > (time() - 3600))) && (this.location == agg.location))
if (this:stat_injury() > this:stat_endurance())
this:do("flee");
else
fork (5)
for i in [1..random(5)]
this:do(tostr("attack ", agg));
endfor
endfork
endif
endif
.
#81:27
":hear_event_*()";
"Hear no events while unconscious.";
return (!this:unconscious()) && pass(@args);
.
#81:28
":take_exit(STR direction|OBJ exit)";
"Move in the given direction/through the given exit.";
if (typeof(exit = args[1]) == OBJ)
this:call_as_puppet(exit, "invoke", {});
else
this:do(tostr(exit));
endif
.
#81:29
"Max it at this creature's willpower or endurance, whichever is higher.";
will = this:get_stat("willpower");
end = this:get_stat("endurance");
return min(pass(@args), max(will, end));
.
#81:30
":add_shaper(OBJ pc)";
"Allow the given PC to control this puppet.";
dobj = args[1];
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
return this.shapers = setadd(this.shapers, dobj);
.
#81:31
":remove_shaper(OBJ pc)";
"Revoke shaper privileges for this puppet from the given PC.";
dobj = args[1];
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
return this.shapers = setremove(this.shapers, dobj);
.
#82:0
return $object_utils:isa(args[1], $room);
.
#82:1
":match_room(string) -> Room in this zone matching the given string.";
return $match_utils:match(args[1], this:contents());
.
#82:2
":free_entry([what])";
"-> Are things allowed to teleport into rooms within this zone?";
free = this.free_entry;
return (free == $gm) ? $rpg:trusted(player) | free;
.
#82:3
":disfunc_leave_msg()";
"-> Message printed in room after a disconnected user is taken home from within this zone.";
":disfunc_arrive_msg()";
"-> Message printed in disconnected user's home when taken there.";
return $string_utils:pronoun_sub(this.(verb), @args);
.
#82:4
"This should never be reached since $room:is_enclosing() returns true as defined by $root.";
"Just in case...";
return {};
.
#82:5
"Allow $geomancer to write to all zones.";
return pass(@args) || ($geomancer in args);
.
#82:6
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.free_entry = $gm;
.
#84:0
":oleave_msg(OBJ source_exit|STR direction_name)";
"Build a default `oleave' message for the given exit (or direction name).  The message is in the third person, but with pronoun sub syntax that will easily make it a `leave_msg' if conjugated with $you.";
exit = args[1];
aliases = (typeof(exit) == OBJ) ? exit.aliases | {exit};
for name in (aliases)
if (name = this:match_direction(name))
return this.source_messages[name in this.source_names];
endif
endfor
return "%N %<has> left.";
.
#84:1
":match_direction(STR direction)";
"-> Standard full name of the given direction, or Zero.";
name = args[1];
if (name in this.source_names)
return name;
endif
for aliases in (this.source_aliases)
if (name in aliases)
return aliases[1];
endif
endfor
.
#84:2
":oarrive_msg(OBJ source_exit|STR direction_name)";
"Build a default `oarrive' message for the given exit (or direction name). The message is in the third person, but with pronoun sub syntax that will easily make it an `arrive_msg' if conjugated with $you.";
exit = args[1];
aliases = (typeof(exit) == OBJ) ? exit.aliases | {exit};
for name in (aliases)
if (name = this:match_direction(name))
return this.dest_messages[name in this.source_names];
endif
endfor
return "%N %<has> arrived.";
.
#84:3
":otherside_name(OBJ source_exit|STR direction_name)";
"Find the name of the exit opposite the given.  Better to use the name of $exit:otherside() if it is valid.";
name = (typeof(exit = args[1]) == OBJ) ? exit.name | exit;
if (!(name = this:match_direction(name)))
return "somewhere";
endif
return this.dest_names[name in this.source_names];
.
#84:4
":from_name(OBJ source_exit|STR direction_name)";
"Find a phrase suitable for following `from'.  Eg: the west, above, inside.";
exit = args[1];
aliases = (typeof(exit) == OBJ) ? exit.aliases | {exit};
for name in (aliases)
if (name = this:match_direction(name))
return this.from_names[name in this.source_names];
endif
endfor
return aliases[1];
.
#85:0
"@nn  -- reads the first new message on the first mail_recipient (in .current_message) where new activity exists.";
if (player != caller_perms())
return E_PERM;
endif
set_task_perms(player);
cm = player.current_message;
"...following makes me like a mail_recipient.  cool, huh?";
"...i always wanted to be a mail recipient...";
cm = (length(cm) < 3) ? {@cm, {}} | cm;
cm = {{player, @cm[1..2]}, @cm[3..length(cm)]};
for n in (cm)
if (new = n[1]:length_date_gt(n[3]))
next = (n[1]:length_all_msgs() - new) + 1;
player:set_current_folder(folder = n[1]);
player._mail_task = task_id();
cur = folder:display_seq_full({next, next + 1}, tostr("Message %d", " on ", $mail_agent:name(folder), ":"));
player:set_current_message(folder, @cur);
return;
endif
endfor
player:tell("No new activity on any of your lists.");
.
#85:1
"@refile/@copym*ail <msg-sequence> [on <recipient>] to <recipient>";
"@refile will delete the messages from the source folder.  @copym does not.";
set_task_perms(caller_perms());
if (!(p = player: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 == player) ? "" | 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
.
#85:2
"@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 = player.current_message;
cm[1..2] = {{player, @cm[1..2]}};
player._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 == player) ? "" | (" on " + $mail_agent:name(folder)), ":")))
if (noconfirm)
player:set_current_message(folder, @cur);
player:set_current_folder(folder);
else
new_cms = {@new_cms, {folder, @cur}};
endif
player:notify("");
endif
endif
$command_utils:suspend_if_needed(1);
player._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)
player:set_current_message(@n);
player:set_current_folder(n[1]);
endfor
player:notify("Last-read dates updated.");
else
player:notify("Last-read dates not updated.");
endif
.
#86:0
":is_writable_by(who)";
"Is 'who' allowed to edit this object?";
":is_controllable_by(who)";
"Is `who' allowed to issue commands on this object?";
who = args[1];
return (who.wizard || (who == this.owner)) || this.w;
.
#86:1
":pump()";
"Get the heart a'pumpin'.  Check permissions, queue status, and task status.";
"Anyone is allowed to start pumping the heart, though it's possibly a security problem if someone starts it, thereby gaining permission to kill it at any time.";
if (!this.queue)
return E_NONE;
elseif ($code_utils:task_valid(this.heartbeat_task))
return E_NACC;
endif
fork (0)
this:heartbeat();
endfork
return 1;
.
#86:2
":heartbeat()";
"Run a heartbeat, cycling every `this.interval' and calling :pulse on each object in the queue.";
if ((caller != this) && (!caller_perms().wizard))
return E_PERM;
elseif (this:should_bypass())
return E_NACC;
elseif ($code_utils:task_valid(id = this.heartbeat_task))
kill_task(id);
endif
this.heartbeat_task = task_id();
while (this:aorta())
endwhile
.
#86:3
":remove(object) -> Remove an object from the queue.";
object = args[1];
if ((object != caller) && (!this:is_writable_by(caller_perms(), caller)))
return E_PERM;
elseif (!(object in this.queue))
return E_NONE;
endif
return length(this.queue = setremove(this.queue, object));
.
#86:4
":add(object) -> Add an object to the queue.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
elseif (!$recycler:valid(object = args[1]))
return E_INVARG;
elseif (i = object in this.queue)
return i;
elseif (this:full(object))
return this:transplant(object);
endif
this:set_heart(object, this);
return length(this.queue = setadd(this.queue, object));
.
#86:5
":full(object) -- Can the heart accept `object' into its queue?";
"-> TRUE   if the heart cannot accept the given object.";
"-> FALSE  if it can/will.";
return length(this.queue) >= this.max;
.
#86:6
":transplant() -- Find a new heart.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
elseif (!this:full(object = args[1]))
return E_NONE;
endif
root = this.root;
for kid in ({root, @children(root)})
if (!kid:full(object))
this:set_heart(object, kid);
return kid:add(object);
endif
endfor
if (typeof(heart = this:spawn_heart()) != OBJ)
return heart;
endif
return heart:add(object);
.
#86:7
":spawn_heart() -- Spawn a new child of this heart's root parent.";
"-> E_PERM  if the caller isn't the programmer of this verb.";
"-> E_NACC  if the heart's owner isn't this verb's owner.";
"-> E_QUOTA if the owner can't create any more objects.";
vp = $code_utils:verb_perms();
if (vp.wizard ? caller_perms() != vp | (caller != this))
return E_PERM;
endif
owner = this.owner;
if ((!vp.wizard) && (owner != vp))
return E_NACC;
endif
heart = this.recycler:_create(root = this.root);
if (heart == E_QUOTA)
player:tell("A new heart needs to be created, and ", $string_utils:nn(vp), " seems to be out of quota.  Please inform ", vp:po(), " of the error.");
elseif (typeof(heart) == OBJ)
heart:set_name(name = ($string_utils:english_ordinal(length(children(root)) + 1) + " ") + root.name);
heart:set_aliases({name, @root.aliases});
endif
return heart;
.
#86:8
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
this.queue = {};
this.heartbeat_task = 0;
return pass(@args);
.
#86:9
":call(OBJ object[, @args]) -- Call the given object's :pulse verb.";
"-> E_PERM  if the caller isn't the programmer of this verb.";
"-> E_NACC  if the heart's owner isn't this verb's owner.";
if (caller != this)
return E_PERM;
endif
this.current = what = args[1];
this.last_call_time = time();
fork (0)
"No longer set task perms.  --960723";
player = what;
what:pulse(@listdelete(args, 1));
endfork
"-- WIZARDLY --";
.
#86:10
":call_next()";
"Call the :pulse of the next object in this heart's queue, as determined by to what object the 'current' property points.";
"E_PERM if caller does not control this heart.";
"E_NONE if the queue is empty.";
"E_NACC if it isn't yet time to call the next object.";
"This should be used only if there are no regular heartbeats running, otherwise there may be duplicate calls made.";
"Useful on systems on which continuous tasks eat up inordinate amounts of resources.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
q = this.queue;
if (!q)
return E_NONE;
endif
l = length(q);
i = max(this.interval / l, 1);
if ((time() - this.last_call_time) < i)
"It is not yet time.";
return E_NACC;
endif
c = this.current;
c = valid(c) ? c | q[1];
p = c in q;
o = (p == l) ? q[1] | q[p + 1];
this:call(o);
return o;
.
#86:11
":aorta()";
"Send pulses out to all the objects in the queue.";
if (caller != this)
return E_PERM;
elseif (this:should_bypass())
return E_NACC;
elseif (0 && (!this.queue))
"...do this rather than returning? Nah.";
kill_task(task_id());
endif
i = 0;
while ((i = i + 1) <= (lq = length(q = this.queue)))
o = q[i];
if (this:valid_object(o))
this:call(o);
seconds = max(this.interval / lq, 1);
suspend(seconds);
else
this.queue = setremove(this.queue, o);
endif
endwhile
return this.queue ? 1 | 0;
.
#86:12
":set_heart(object, heart)";
"Set object's heart to the given heart, trying both verb and property.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
object = args[1];
old_heart = object.heart;
heart = args[2];
set = object:set_heart(heart);
if (set == E_VERBNF)
set = object.heart = heart;
endif
ok = typeof(set) != ERR;
if (ok)
old_heart:remove(object);
endif
return ok;
.
#86:13
":valid_object(obj)";
"Is the given object something to which we should be sending a pulse?";
what = args[1];
return $object_utils:has_property(what, "heart") && (what.heart == this);
.
#86:14
":should_bypass()";
"Return true if heart prods should be handled by hooks and not system tasks.";
return $rpg.bypass;
.
#86:15
":shuffle()";
"This method attempts to push some of our queue into the root queue to maximize heart efficiency.";
root = this.root;
if (this == root)
return E_MAXREC;
endif
old_len = length(this.queue);
for o in (this.queue)
root:add(o);
endfor
return old_len - length(this.queue);
.
#86:16
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.root = this;
this.heartbeat_task = 0;
this.recycler = $recycler;
this.current = #-1;
this.last_call_time = 0;
this.queue = {};
.
#87:0
":_start() -> Set the robot's `running' true, and give the heart a pump.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
this.running = 1;
this.last_start_time = time();
"Make sure we're in the heart (puppet may be chparented).";
this:cpr();
return 1;
.
#87:1
":_start() -> Set the robot's `running' false.";
if (!this:is_writable_by(caller_perms()))
return E_PERM;
endif
this.heart:remove(this);
this.last_stop_time = time();
return this.running = 0;
.
#87:2
"@start <robot>   -- Start the robot running.";
"@stop  <robot>   -- Stop it.";
action = strsub(verb, "@", "");
if (!this:is_controllable_by(player))
player:tell("You aren't allowed to ", verb, " ", this.name, ".");
return E_PERM;
endif
this:("_" + action)();
player:tell("You ", action, " ", $string_utils:nn(this), ".");
.
#87:3
":running() -> Should the robot do anything?";
"Hack to make sure it doesn't walk around or spew text when dead.";
return this.running;
.
#87:4
":connected_seconds()";
"Seconds since the robot was last @started.";
start = max($last_restart_time, this.last_start_time);
stop = this.last_stop_time;
if (start && (start >= stop))
return time() - start;
else
return start - stop;
endif
.
#87:5
":do_behave()";
"Wrapped around the puppet's own :behave, which exists primarily because it's older and I don't wanna re-write it all.";
pass(@args);
if (this:running())
return this:behave();
endif
.
#87:6
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.heart = $npc_heart;
this.running = 0;
this.last_start_time = this.last_stop_time = 0;
.
#88:0
":will_wander() -> True if the robot will wander.";
"Hack to prevent wandering away during attacks, etc.";
if (this:unconscious())
return 0;
elseif (this.location == this.afterlife)
return 1;
else
return this.wander_chance > random(100);
endif
.
#88:1
"wander-chance <wanderer> is <percentage>";
"Set the chance of this wanderer taking an exit when its :pulse is called.";
who = (callers() && (caller != this)) ? caller_perms() | player;
if (!this:is_writable_by(who))
player:tell("You aren't allowed to set the ", verb, " of ", $string_utils:nn(this), ".");
return E_PERM;
endif
verb[1..1] = "";
parsed = $string_utils:to_value(iobjstr);
if (!parsed[1])
player:tell("Couldn't parse ", verb, " of \"", iobjstr, "\".");
return E_INVARG;
endif
this.(strsub(verb, "-", "_")) = value = parsed[2];
player:tell("You set the ", verb, " of ", $string_utils:nn(this), " to ", $string_utils:print(value), ".");
.
#88:2
":get_exits() -> List of exits in the current room.";
"Exits may be either strings or objects.  Usually objects, but strings may pop up in metarooms.";
where = this:room();
exits = where:obvious_exits() || {};
if (dirs = where:invokable_directions(this))
exits = {@exits, @dirs};
endif
return exits;
"--WiZARDLY to grab :exits.";
.
#88:3
":ok_exit(exitspec)";
"Is the given direction/exit OK to travel through?";
exit = args[1];
if (!this:ok_destination_terrain(exit))
return 0;
elseif (exit in this.forbidden_exits)
return 0;
elseif (typeof(exit) == STR)
return 1;
elseif (!exit:is_unlocked_for(this))
return 0;
elseif (typeof(dest = exit.dest) != OBJ)
return 1;
elseif (dest in this.forbidden_rooms)
return 0;
endif
zones = this.allowable_zones;
return (!zones) || (dest:zone() in zones);
.
#88:4
":choose_exit() -- Choose somewhere to wander.";
"-> {STR direction|OBJ exit} if a direction/exit was found.";
"-> Some false if no exits found.";
exits = this:get_exits();
if (!exits)
return exits;
endif
while (l = length(exits))
i = random(l);
if (this:ok_exit(exit = exits[i]))
return {exit};
endif
exits = listdelete(exits, i);
endwhile
.
#88:5
":move(STR direction|OBJ exit)";
"Move in the given direction/through the given exit.";
if (typeof(exit = args[1]) == OBJ)
this:call_as_puppet(exit, "invoke", {});
else
this:do(tostr(exit));
endif
.
#88:6
":wander() -> Choose an exit and take it (1), or stay put (0).";
if (exit = this:choose_exit())
this:take_exit(exit[1]);
return 1;
endif
return 0;
.
#88:7
":set_wander_chance(% chance of wandering when behaving)";
":set_forbidden_exits(list of exits nomad is forbidden to move through)";
":set_forbidden_rooms(list of rooms nomad is forbidden to move through)";
":set_allowable_zones(list of zones nomad may travel through)";
"If allowable zones is an empty list (the default), all zones are traversable.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
return this.(verb[5..length(verb)]) = args[1];
.
#88:8
":do_flee([exitspec])";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
if (args)
exit = args[1];
if (this:ok_exit(exit))
return this:take_exit(exit);
endif
endif
if (!this:wander())
player:tell("There is no escape.");
return E_NACC;
endif
.
#88:9
":behave()";
if (this:will_wander())
this:wander();
endif
return pass(@args);
.
#88:10
":my_oleave_msg(mover, exit, @pronoun_subs)";
"Setting your @my_oleave and @my_oarrive messages allow you to customize messages shown to folks when your critter leaves and enters a room. It's difficult to work the actual exit into these generic messages, so try something like this:";
"    %N %<leaves> the room (via `%t`).";
"To incorporate the exit name into eir motion.  Otherwise, players won't be able to know how the NPC left, which is unreal and thus unfair.";
return this.(verb);
.
#88:11
":ok_destination_terrain(dir)";
"May we travel the terrain through the given exit?";
{dir} = args;
if (!this.forbidden_terrain)
return 1;
elseif (typeof(dir) != STR)
"hrm";
return 1;
endif
here = this.location;
try
dest_coords = here:get_destination_coordinates(dir);
dest_terrain = here:get_cell_terrain(dest_coords);
except (E_VERBNF)
"not a terrain room";
return 1;
endtry
for t in (this.forbidden_terrain)
if ($object_utils:isa(dest_terrain, t))
return 0;
endif
endfor
return 1;
.
#88:12
"Go through the standard interface for wandering so we don't go where we shouldn't be.";
return this:wander();
.
#89:0
return (msg = this.(verb)) && $string_utils:pronoun_sub(msg, @args);
.
#89:1
return (msg = this.(verb)) && $string_utils:pronoun_sub(msg, @args);
.
#89:2
return (msg = this.(verb)) && $string_utils:pronoun_sub(msg, @args);
.
#89:3
":sitting/reclining([who]) -> If 'who' is given, return true if e is sitting/reclining.  Else return everyone who is sitting/reclining.";
contents = this.location:contents();
for who in (all = this.(verb))
if (!(who in contents))
this.(verb) = all = setremove(all, who);
endif
endfor
return args ? args[1] in all | all;
.
#89:4
":look_msg() -> What the furniture looks like right now.";
msg = this:occupied_msg(@args);
if (!msg)
return this:placed_on_msg() || this:empty_msg();
else
pon = this:placed_on_msg();
return {{@this:sitting(), @this:reclining()}, msg + (pon && ("  " + pon))};
endif
.
#89:5
":sitting/reclining_msg()";
"-> Message describing those sitting/reclining on the furniture.";
what = strsub(verb, "_msg", "");
if (!(l = length(people = this:(what)())))
return "";
elseif (l == 1)
return $string_utils:pronoun_sub(this.(what + "_alone_msg"), @people);
else
return $string_utils:pronoun_sub(this.(verb), people);
endif
.
#89:6
":space() -> How much space is left on the furniture.";
return max((this.space - length(this:sitting())) - length(this:reclining()), 0);
.
#89:7
"sit on this -- Sit down on the furniture.";
if (player.location != this.location)
player:tell("You can't sit on that from where you are.");
return E_NACC;
elseif (this:sitting(player))
player:tell("You're already sitting on it.");
return E_NONE;
elseif (this:reclining(player))
this:remove_reclining(player);
msg = "sit_from_reclining";
elseif (this:space())
msg = "sit_from_standing";
else
this:announce_messages("no_room");
return E_QUOTA;
endif
this:add_sitting(player);
this:announce_messages(msg);
this.location:broadcast_event_sit(player, this);
.
#89:8
"recline on this -- Lie back on the furniture.";
if (player.location != this.location)
player:tell(("You can't " + verb) + " on that from where you are.");
return E_NACC;
elseif (this:reclining(player))
player:tell("You're already reclining on it.");
return E_NONE;
elseif (this:sitting(player))
this:remove_sitting(player);
msg = "recline_from_sitting";
elseif (this:space())
msg = "recline_from_standing";
else
this:announce_messages("no_room");
return E_QUOTA;
endif
this:add_reclining(player);
this:announce_messages(msg);
this.location:broadcast_event_recline(player, this);
.
#89:9
"stand from this -- Get off the furniture.";
if (player.location != this.location)
player:tell("You're not even in the same room.");
return E_NACC;
elseif (this:sitting(player))
this:remove_sitting(player);
msg = "rise_from_sitting";
elseif (this:reclining(player))
this:remove_reclining(player);
msg = "rise_from_reclining";
else
player:tell("You're not even on it.");
return E_NONE;
endif
this:announce_messages(msg);
this.location:broadcast_event_stand(player, this);
.
#89:10
":add_sitting/reclining(who)";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
else
this.(p = strsub(verb, "add_", "")) = setadd(this.(p), args[1]);
endif
.
#89:11
":remove_sitting/reclining(who)";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
else
this.(p = strsub(verb, "remove_", "")) = setremove(this.(p), args[1]);
endif
.
#89:12
return (msg = this.(verb)) && $string_utils:pronoun_sub(msg, @args);
.
#89:13
":look_self()";
pass(@args);
if (msg = this:occupied_msg())
player:tell(msg);
endif
if (msg = this:placed_on_msg())
player:tell(msg);
endif
.
#89:14
"push any from this -- Push someone off the furniture.";
if (player.location != this.location)
player:tell("You're not even in the same room.");
return E_NACC;
endif
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
return E_INVARG;
elseif (this:sitting(dobj))
this:remove_sitting(dobj);
elseif (this:reclining(dobj))
this:remove_reclining(dobj);
else
player:tell(dobj:title(), " isn't on ", this:title(), ".");
return E_NONE;
endif
this:announce_messages("push");
.
#89:15
"Allow in only those which have been blessed.";
what = args[1];
return ((task_id() == this.place_task[1]) && (what == this.place_task[2])) && (!$rpg:is_character(what));
.
#89:16
":bless_for_placement(what)";
"Bless the object for placement on the furniture.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
return this.place_task = {task_id(), @args};
.
#89:17
"place <object> on <furniture>";
"Place an object on the furniture, which actually puts it inside.";
if (!this:is_local_to(player))
player:tell("You aren't close enough to do that.");
return E_RANGE;
endif
pool = player:contents();
dobj = player:my_match_object(dobjstr, pool);
if ($match_utils:object_match_failed(dobj, dobjstr, pool, "on you"))
return E_INVARG;
endif
if ($rpg:is_character(dobj))
return this:place_person(dobj);
endif
this:bless_for_placement(dobj);
if (!this:acceptable(dobj))
player:tell(this:dnamec(), " doesn't want it.");
return E_NACC;
endif
dobj:moveto(this);
if ($code_utils:call_verb(dobj, "was_moved", {player, this}) || (dobj.location == this))
this:announce_messages("put");
else
player:tell(dobj:dnamec(), " doesn't want to go.");
return E_NACC;
endif
.
#89:18
"remove <object> from <furniture> -- Remove an object from the furniture.";
if (!this:is_local_to(player))
player:tell("You aren't close enough to do that.");
return E_RANGE;
endif
pool = this:contents();
dobj = player:my_match_object(dobjstr, pool);
if ($match_utils:object_match_failed(dobj, dobjstr, pool, "on " + this:dname()))
return E_INVARG;
elseif (!player:acceptable(dobj))
player:tell("You don't really want it.");
return E_NACC;
endif
dobj:moveto(player);
if ($code_utils:call_verb(dobj, "was_moved", {this, player}) || (dobj.location == player))
this:announce_messages("remove");
else
player:tell(dobj:dnamec(), " doesn't want to go.");
return E_NACC;
endif
.
#89:19
":placed_on_msg()";
"-> String describing what is place on the furniture.";
if (c = this:contents())
msg = this.(verb);
return msg && $string_utils:pronoun_sub(msg, player, this, this.location, c);
else
return "";
endif
.
#89:20
":occupied_msg()";
"A description of who is sitting and reclining on the furniture.";
msg = "";
if (sitting_msg = this:sitting_msg())
msg = sitting_msg + ((rm = this:reclining_msg()) ? "  " + rm | rm);
else
msg = this:reclining_msg();
endif
return msg;
.
#89:21
return (msg = this.(verb)) && $string_utils:pronoun_sub(msg, @args);
.
#89:22
":occupants()";
"Return a list of everything/one sitting/lying on this furniture.";
return $set_utils:union(this:contents(), this:sitting(), this:reclining());
.
#89:23
":is_enclosing(obj)";
"Someone sitting on furniture can still perceive others in the room.";
return 0;
.
#89:24
return this.location:(verb)(@args);
.
#89:25
":hear_furniture_event(who, furniture)";
"Check for events on other furniture, remove from this if heard.";
if (args[2] != this)
who = args[1];
this.sitting = setremove(this.sitting, who);
this.reclining = setremove(this.reclining, who);
endif
return pass(@args);
.
#89:26
":announce_to(text, exclude)";
"Relay the given text to those inside.";
exclude = args[2];
for what in (this:matching_contents())
if (!(what in exclude))
what:announce_to(@args);
endif
endfor
return pass(@args);
.
#89:27
":attached_contents()";
return {this, @this:contents()};
.
#89:28
"Remove exiting person from our sitting lists.";
who = args[1];
this.sitting = setremove(this.sitting, who);
this.reclining = setremove(this.reclining, who);
return pass(@args);
.
#89:29
":tt_prefix_msg(sitter)";
"Return text to be prepended to something done with the 'tt' command.";
return $string_utils:pronoun_sub(this.(verb), @args);
.
#89:30
":is_occupied_by(char)";
c = args[1];
return (c in this.sitting) ? 1 | ((c in this.reclining) ? 2 | 0);
.
#89:31
":place_person(person)";
"Place the given person on this furniture.";
su = $string_utils;
person = args[1];
if (!this:space())
player:tell(su:pronoun_sub("There isn't any room for %o on %[tdname].", person));
elseif (this:reclining(person))
player:tell(su:pronoun_sub("%S %<is> already reclining on %[tdname].", person));
elseif (this:sitting(player))
player:tell(su:pronoun_sub("%S %<is> already sitting on %[tdname].", person));
else
here = this.location;
here:bless_for_entry(person);
person:moveto(here);
this:announce_messages("place_person");
this:add_reclining(person);
here:broadcast_event_recline(person, this);
endif
.
#90:0
":roll(dice-spec)";
"Dice spec is in the form: <count>d<sides>";
"3d8  -> Three eight-sided dice.";
"d12  -> 1 twelve-sided die.  (count defaults to 1)";
"3d   -> 3 six-sided dice.    (sides defaults to 6)";
"d    -> One six-sided die.";
"-> E_INVARG if invalid syntax.";
"-> E_RANGE  if count or sides are specified and less than 1.";
"-> {list of die results}";
if (!(m = match(args[1], "%([0-9]*%)d%([0-9]*%)")))
return E_INVARG;
endif
count = (s = substitute("%1", m)) ? tonum(s) | 1;
sides = (s = substitute("%2", m)) ? tonum(s) | 6;
if ((count < 1) || (sides < 1))
return E_RANGE;
endif
results = {};
for i in [1..count]
results = {@results, random(sides)};
endfor
return results;
.
#90:1
":result(dice-spec) -> NUM total of dice roll.";
return $math_utils:sum(@this:roll(@args));
.
#90:2
":match_skill(STR|OBJ spec)";
{spec} = args;
if (typeof(spec) == STR)
skills = this.skills;
if (typeof(`skills.(spec) ! E_PROPNF') == OBJ)
return skills.(spec);
endif
return $match_utils:match(spec, skills:contents());
elseif (typeof(spec) == OBJ)
if ($object_utils:isa(spec, $skill))
return spec;
endif
raise(E_INVARG);
else
raise(E_TYPE);
endif
.
#90:3
":is_char(object)";
"=> Return true if the given object is an RPG character.";
return $object_utils:isa(args[1], $char);
.
#90:4
":trusted(user)";
"-> Is the given user trusted to perform GM actions?";
{user} = args;
return (user.owner == user) && $wiz_utils:is_builder(user);
.
#90:5
":prod_hearts()";
"Prod the next heart in sequence, calling a :pulse somewhere.";
q = this.all_hearts;
if (q)
c = this.last_heart_called;
c = valid(c) ? c | q[1];
p = c in q;
o = (p == length(q)) ? q[1] | q[p + 1];
r = o:call_next();
this.last_heart_called = o;
endif
if ((time() - this.last_heart_update) > this.update_interval)
this.all_hearts = $object_utils:descendants($heart);
endif
return r;
.
#90:6
":spawn(parent)";
"Create a child of the given parent, owned by PackRat.";
"Only allow wizards to do so as of now.";
if (!this:trusts_to_spawn(caller_perms()))
return E_PERM;
endif
mom = args[1];
new = $packrat:_create(mom, $packrat);
if (typeof(new) != OBJ)
return new;
endif
$building_utils:set_spawned_names(new);
return new;
.
#90:7
":att_bonus(NUM)";
"Given an attribute rank, return its bonus applied to skills.";
{stat} = args;
"Another; hopefully the last!  <19980902>";
if (stat < 50)
return ((stat - 60) ^ 3) / 1250;
elseif (stat <= 95)
return ((stat - 50) / 6) * 5;
elseif (stat <= 100)
return (stat - 90) * 10;
else
return min(((stat - 50) ^ 3) / 1000, 1000);
endif
"Yet another tweaked bonus, with the averaged replaced at 50.";
return (stat > 50) ? ((stat - 50) ^ 3) / 1250 | (((stat - 60) ^ 3) / 1250);
"Another tweaked bonus.";
return (stat > 60) ? ((stat - 50) ^ 3) / 1250 | (((stat - 70) ^ 3) / 1250);
"The 'parabolic' bonus, tweaked by Quinn.";
return ((stat - 60) ^ 3) / 1250;
"A parabolic bonus, courtesy of Bishamon.";
return ((stat - 50) ^ 3) / 1250;
"The original bonus.";
return ((args[1] - 50) / 8) * 5;
.
#90:8
":parse_reroll_args(argstr)";
"Parse the arguments of the @reroll command.  Each arg should be of one of the following forms:";
"att att+ att- -att +att att+N att-N";
"A + or - without a number is assumed to be 5.";
"The attributes should be given in their order of importance to the roller.  If any attributes are left out, they'll be tacked oin to the end with no bonuses.";
"Returns {0, failure_msg} or a list of lists of {attribute, bonus}.";
atts = this.atts;
atts_regexp = this.atts_regexp;
parsed = {};
for word in ($string_utils:words(args[1]))
l = length(word);
a = word[1];
b = word[l];
if ((b == "+") || (b == "-"))
bonus = $code_utils:tonum(b + "5");
word[l..l] = "";
elseif (m = match(word, "%([+-][0-9]+%)"))
bonus = $code_utils:tonum(substitute("%1", m));
word[m[3][1][1]..m[3][1][2]] = "";
elseif ((a == "+") || (a == "-"))
bonus = $code_utils:tonum(a + "5");
word[1..1] = "";
else
bonus = 0;
endif
a = "";
b = 0;
l = length(atts);
while ((!a) && ((b = b + 1) <= l))
if (match(word, atts_regexp[b]))
a = atts[b];
atts = listdelete(atts, b);
atts_regexp = listdelete(atts_regexp, b);
endif
endwhile
if (!a)
return {0, tostr("Either \"", word, "\" doesn't match any existing attribute, or you have duplicated an attribute."), parsed};
endif
parsed = {@parsed, {a, bonus}};
endfor
for a in ($list_utils:randomly_permute(atts))
parsed = {@parsed, {a, 0}};
endfor
return {1, parsed};
.
#90:9
"Copied from generic weapon (#327):tell_stats by GameMaster (#1991) Fri Dec  9 03:46:42 1994 CST";
"Show a summary of this weapon's statistics.";
len = length($list_utils:longest(stats = properties($code_utils:verb_loc())));
for stat in (stats)
type = typeof(value = this.(stat));
if (type == OBJ)
value = $string_utils:nn(value);
elseif (type in {LIST, ERR})
value = $string_utils:print(value);
endif
player:tell($string_utils:left(stat, len), " -> ", value);
endfor
.
#90:10
":build_stat_line(obj, property_desc, property_name, value, pd_len, pn_len)";
{object, pd, pn, value, pd_len, pn_len} = args;
return tostr(";;\"", $string_utils:right(pd, -pd_len), "\"; return ", object, ".", $string_utils:left(pn, pn_len), " = ", value);
"old way below";
return tostr($string_utils:right(pd, -pd_len), " | ;", object, ".", $string_utils:left(pn, pn_len), " = ", value);
.
#90:11
":is_combat_action(cmd)";
"Is the given action one which should be queued?  Used primarily from $combatant:rush to determine whether something can be rushed to the top of a player's action queue, and as such should probably be @renamed is_action or is_rushable_action.";
return args[1] in this.actions;
.
#90:12
":is_rushed_task(id)";
"Should the given task be 'rushed' instead of 'queued' into the character's action sequence?";
return (args ? args[1] | task_id()) == this.rushed_task;
.
#90:13
":add_rushed_task(id)";
"Record the task as being 'rushed' and not normally queued.";
this.rushed_task = args ? args[1] | task_id();
.
#90:14
":delete_rushed_task(id)";
"Unmark the given task ID as one to be rushed.";
this.rushed_task = 0;
.
#90:15
":character_match_failed(result, string)";
result = args[1];
string = args[2];
if (valid(result))
if (!$rpg:is_char(result))
player:tell($string_utils:pronoun_sub("%(dnamec) %<is> not an RPG character.  Try again.", result));
return 1;
endif
return 0;
elseif (!string)
player:tell("You must give a string matching a character name or alias.");
elseif (result == $ambiguous_match)
player:tell("You'll have to be more specific; \"", string, "\" refers to more than one character.");
else
player:tell("You don't perceive a character by the name of \"", string, "\" around here.");
endif
return 1;
.
#90:16
":match_character(str)";
"Try to find a character matching the given string.  First search all objects local to the player, then return a :match_player.";
string = args[1];
chars = {};
for o in (player:env())
if (this:is_char(o))
chars = {@chars, o};
endif
endfor
try_1 = try_2 = #-1;
if (chars)
try_1 = $match_utils:match_object(string, chars);
endif
if (valid(try_1))
return try_1;
else
try_2 = $match_utils:match_player(string);
endif
if (valid(try_2))
return try_2;
elseif (try_1 == $ambiguous_match)
return try_1;
elseif (try_2 == $ambiguous_match)
return try_2;
else
return try_1;
endif
.
#90:17
":is_violent_action(action_name)";
"Return true if the given action is considered a violent one.";
return args[1] in this.violent_actions;
.
#90:18
":maybe_prod_hearts()";
"If this.bypass is true, and a random check succeeds, then prod the next heart in the global queue.";
return (this.bypass && (random(5) > 2)) && this:prod_hearts();
.
#90:19
":match_global_room(str)";
"Return a match of string against all global locations.";
string = args[1];
if (string == "home")
return player.home;
elseif (string == "start")
return $player_start;
endif
return $match_utils:match(string, this:global_locations());
.
#90:20
":is_global_loc(obj[, user])";
"True if given object is a global location.";
g = this:global_locations();
if (length(args) > 1)
return args[1] in {@g, args[2].home};
else
return args[1] in g;
endif
.
#90:21
":global_locations()";
"Return all rooms considered OK for shortcut movement, such as @join by non-progs.";
return setadd(this.global_locations, $player_start);
.
#90:22
":faux_pedest(user, dest)";
"Fake the user exiting eir current location and entering the destination location as if e were using an exit.";
"The destination must have entrances unlocked for the user and be unlocked itself.";
set_task_perms(caller_perms());
{player, dest} = args;
here = player:room();
if (dest == here)
player:notify(tostr("Why bother?  You're already there."));
return E_NONE;
elseif (!dest:is_unlocked_for(player))
player:notify(tostr("You can't go in there; ", dest:dnamec(), " is @locked."));
return E_NACC;
endif
entrances = dest:entrances() || {};
while (entrances && (!(e = entrances[1]):is_unlocked_for_walker(player)))
entrances = listdelete(entrances, 1);
endwhile
if (!entrances)
player:notify(tostr("You can't fake a pedestrian entrance into ", dest:dname() || "that location", "; It has no entrances.", $wiz_utils:is_builder(player) ? tostr("  You might try `@move me to ", dest, "` instead.") | ""));
return E_ARGS;
endif
exits = here:obvious_exits(player) || {};
while (exits && (!(x = exits[1]):is_unlocked_for_walker(player)))
exits = listdelete(exits, 1);
endwhile
if (!exits)
$you:say_action("%N %<walks> through a heretofore unseen exit which promptly disappears behind %o.");
else
if (m = x:leave_msg())
player:tell(m);
endif
here:announce(x:oleave_msg());
endif
"Do a straight move instead, since the player really isn't coming in from the entrance's source.";
"e:invoke_for_walker();";
player:moveto(dest);
if (m = e:arrive_msg())
player:tell(m);
endif
dest:announce(e:oarrive_msg());
return 1;
.
#90:23
":char_stat_line(char, stat[, with_bonus])";
su = $string_utils;
{char, stat, ?withbonus = 0} = args;
stat = su:capitalize(stat);
base = char.(stat);
curr = char:("stat_" + stat)();
line = tostr(su:left(stat, -12), " -> ", su:right(base, -4), "  (", su:right(curr, -4), ")");
if (withbonus)
bone = char:total(stat);
line = tostr(line, "  ", (bone < 0) ? "-" | ((bone == 0) ? " " | "+"), su:right(abs(bone), 4));
endif
return line;
.
#90:24
":delete_stat(OBJ stat)";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
stat = args[1];
prop = stat.property;
delete_property(this.skills, prop);
delete_property($skilled, prop);
.
#90:25
":wts_code(damage)";
"Return the Welby Trauma Scale code for the given damage degree.";
inj = args[1];
if (inj < 1)
i = "NON";
elseif (inj < 10)
i = "NEG";
elseif (inj < 40)
i = "MIN";
elseif (inj < 70)
i = "MOD";
elseif (inj < 100)
i = "SER";
elseif (inj < 130)
i = "SVR";
else
i = "CRT";
endif
return i;
.
#90:26
":ic_ctime([ic_time])";
"A string representing the time according to the role-playing universe.";
{?when = this:ic_time()} = args;
asctime = ctime(when);
postfix = asctime[21..$];
asctime = asctime[1..20];
yearint = toint(postfix[1..4]) + this.YEAR_OFFSET;
return tostr(asctime, yearint);
.
#90:27
":ic_time([rl_time])";
"The time according to the role-playing universe.";
{?when = time()} = args;
return toint(tofloat(when - this.BASE_TIME) * this.TIME_RATE);
.
#90:28
":log_stat_change(stat, val)";
if (!player.programmer)
return;
endif
GOD = #2;
if (player == GOD)
return;
endif
{stat, val} = args;
c = callers();
m = tostr(ctime()[5..16], " ", player.name, " (", player, "): ", caller, ".", stat, "=", val, " \"", c[$][2], " ", argstr, "\"");
if (0 && `connected_seconds(GOD) ! ANY')
notify(#2, ">>> " + m);
elseif (0)
this.stat_changes = {@this.stat_changes, m};
endif
.
#90:29
":user_desig(user)";
"Return a three-character code describing the given user in the context of MOO society.";
{user} = args;
if (!is_player(user))
return "NPC";
elseif (user == #2)
return "Wiz";
elseif (user.programmer)
return "Tec";
elseif ($wiz_utils:is_builder(user))
return "Geo";
elseif ($justice:is_criminal(user))
return "CON";
else
msg = $rpg:is_npc(user) ? "NPC" | " PC";
if (user.pk)
msg[$] = "K";
endif
return msg;
endif
.
#90:30
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.last_heart_called = #-1;
this.skills = $skill_db;
this.corpse = $corpse;
this.crystal = $crystal;
this.basic_combat_reactor = $combat_reactor;
this.att_object = $attribute;
this.all_hearts = children($heart);
this.trusted_spawners = {};
this.global_locations = {$player_start, $pec};
.
#90:31
":is_npc(OBJ what)";
"Return true if the given object is an NPC.";
what = args[1];
return `what.ic ! ANY => 0' < 0;
.
#90:32
":is_ic_neutral_location(OBJ dest[, INT new_ic])";
"Return true if the given room is friendly to both IC and OOC folks.  If new_ic is passed, also return true if it equals the room's IC tendancies.";
{where, ?new_ic = $maxint} = args;
wic = `where.ic ! E_PROPNF => -1';
return (wic < 0) || (wic == new_ic);
.
#90:33
":forbid_strict_ic_room_entry(OBJ what, OBJ dest)";
if (!this.strict_ic)
return E_NONE;
endif
{what, dest} = args;
what_ic = `what.ic ! ANY';
dest_ic = `dest.ic ! ANY';
if ($rpg:trusted(what))
return E_NONE;
elseif (this:is_ic_neutral_location(dest))
return E_NONE;
elseif (what_ic && (!dest_ic))
return tostr("That exit leads to an OOC (out-of-character) area, and you are currently IC (in-character).  If you still want to enter the area, use `@ooc` to switch out of character before doing so.  Note that any attempt to role-play in an OOC area is probably futile.");
elseif ((!what_ic) && dest_ic)
return tostr("That exit leads to an IC (in-character) area, and you are currently OOC (out-of-character).  Your presence might disrupt the role-playing going on in that location.  Use `@ic` to switch before entering.");
else
return E_NONE;
endif
.
#90:34
":maybe_forbid_ic_switch(OBJ character, INT ic)";
if (!this.strict_ic)
return E_NONE;
endif
{char, new_ic} = args;
where = char.location;
if ($rpg:trusted(char))
return E_NONE;
elseif (this:is_ic_neutral_location(where, new_ic))
"same or neutral";
return E_NONE;
elseif (where == char.home)
"aw, let 'em in the privacy of their own homes";
return E_NONE;
elseif (`where:occupants() ! E_VERBNF' == {char})
"alone in a room, could be abused, but risk that for now";
return E_NONE;
else
return tostr("You may toggle your IC/OOC state only in IC-neutral locations (designated as such in their titles), your home, or rooms in which you are the sole occupant.  Find a room which meets one of those requirements, then try again.");
endif
.
#90:35
":notify(OBJ char, STR text)";
"Tell the given character the given string.  This should be used for verbs such as combat messages, where it is important that they not be ignored by social mechanisms such as @gag.";
"This doesn't really :notify (as of this writing), but $player:tell checks for trusted caller and if so avoids the gagging.";
if (!$rpg:trusted(caller_perms()))
return raise(E_PERM);
endif
{char, text} = args;
char:tell(text);
.
#90:36
":_match_stat(OBJ result, STR spec)";
{result, spec} = args;
if (valid(result))
return result;
elseif (result == $ambiguous_match)
all = $match_utils:match_list(spec, $skill_db:contents());
if (length(all) > 1)
return tostr("\"", spec, "\" could match any one of ", $string_utils:name_list(all, "several skills, but we can't come up with any right now", " or "), ".");
else
return tostr("There is more than one primary attribute or skill matching \"", spec, "\"; try to be more specific.");
endif
elseif (result == $failed_match)
return tostr("There is no primary attribute or skill matching \"", spec, "\".");
elseif (!spec)
return tostr("You must give the name of some primary attribute or skill.");
else
return tostr("Match on \"", spec, "\" failed.");
endif
.
#90:37
":_ic_to_ooc_ctime(STR)";
{?asctime = this:ic_ctime()} = args;
postfix = asctime[21..$];
asctime = asctime[1..20];
yearint = toint(postfix[1..4]) - this.YEAR_OFFSET;
return tostr(asctime, yearint);
.
#90:38
":ic_ctime_diff(STR this_ctime, STR minus_this_ctime)";
"=> Seconds between this_ctime and minus_this_ctime.";
{big, little} = args;
return $time_utils:from_ctime(this:_ic_to_ooc_ctime(big)) - $time_utils:from_ctime(this:_ic_to_ooc_ctime(little));
.
#90:39
":allow_player_stat_change(OBJ gm, OBJ pc)";
"Is player gm allowed to change the stats of player pc?";
{gm, pc} = args;
if (((!gm.programmer) && (!$rpg:is_npc(pc))) && (!pc:is_writable_by(gm)))
"Non-programmers may not set stats of non-NPC players to whom";
"they do not have write permission.  <Quinn;19981231>";
return E_PERM;
endif
"Only a special set of characters may change stats.";
return 1;
.
#90:40
":is_newbie(char)";
"Return true if the character can be considered a game newbie.";
pc = args[1];
"only players can be newbies";
if ((!valid(pc)) || (!is_player(pc)))
return 0;
endif
"anyone under two weeks old is a newbie";
MAX_NEWBIE_AGE = $time_utils.week * 2;
rerolled = pc.last_reroll;
if ((time() - rerolled) < MAX_NEWBIE_AGE)
return 1;
endif
"anyone with a skill over 100 is _not_ a newbie";
"this may be incorrect with new character creation";
"rules which allow high startup stats";
MAX_NEWBIE_RANK = 100;
for sk in (pc:all_skills())
if (pc.(sk) > MAX_NEWBIE_RANK)
return 0;
endif
endfor
return 1;
.
#90:41
":_indent_skill(OBJ character, OBJ skill[, STR indent[, LIST prev[, STR indent_space]]])";
{pc, skill, ?indent = "", ?prev = {}, ?spaces = " "} = args;
prev = {@prev, tostr(indent, $string_utils:capitalize(skill.property))};
newindent = indent + spaces;
for kid in (children(skill))
prev = this:_indent_skill(pc, kid, newindent, prev, spaces);
endfor
return prev;
.
#90:42
":indented_skill_sheet(OBJ character[, STR indent_space])";
{pc, ?spaces = " "} = args;
tops = children($skill);
sheet = {};
for kid in (tops)
if (kid == $attribute)
continue;
endif
sheet = this:_indent_skill(pc, kid, "", sheet, spaces);
endfor
statcol = length($list_utils:longest(sheet));
display = {};
for each in (sheet)
stat = strsub(each, " ", "");
base = pc.(stat);
bone = pc:total(stat);
line = tostr($string_utils:right(base, -4), "  ", (bone < 0) ? "-" | ((bone == 0) ? " " | "+"), $string_utils:right(abs(bone), 4));
display = {@display, tostr($string_utils:left(each, statcol), " ", line)};
endfor
return display;
.
#90:43
":trusts_to_spawn(OBJ perms)";
cp = args[1];
return cp.wizard || (cp in {$gm, $packrat, @this.trusted_spawners});
.
#91:0
":resolve(skill[, mod[, trivial]])";
"skill     -- A string, the name of the skill to roll against, or a skill object.";
"mod       -- Modifier to the roll.";
"trivial   -- True if the roll isn't worthy of skill improvement.";
{skstr, ?skmod = 0, ?trivial = 0} = args;
if (!valid(skobj = this:match_skill(skstr)))
raise(E_INVARG);
endif
return skobj:resolve(this, skmod, trivial);
.
#91:1
":reroll([LIST priorities[, INT total_reroll]])";
"Re-Roll this character's attributes.  This is currently just an internal verb for the @reroll command; more options will perhaps be available later.";
if (!this:is_writable_by(caller_perms(), caller))
raise(E_PERM);
endif
{?result = $rpg:parse_reroll_args("")[2], ?clear = 0} = args;
"------";
"Resets";
"------";
`this.last_reroll = time() ! E_PROPNF';
if (!clear)
"rerolls should probably _always_ clear";
return;
endif
skills = this:all_skills();
reset = {@skills, "pot", "total_deaths", "total_power", "total_kills"};
reset = {@reset, "upper_mobility", "lower_mobility", "pk"};
for att in (reset)
clear_property(this, att);
endfor
"------";
"Attributes";
"------";
for att in ($rpg.atts)
skobj = this:match_skill(att);
if (valid(skobj))
this.(att) = skobj:starting_rank_for(this);
else
clear_property(this, att);
endif
endfor
"------";
"Senses";
"------";
senses = $rpg.senses;
for sense in (senses)
this.(sense) = (7 + $rpg:result("3d6")) * 4;
endfor
"------";
"Skills";
"------";
for att in (skills)
skobj = this:match_skill(att);
if (valid(skobj))
this.(att) = skobj:starting_rank_for(this);
endif
endfor
"------";
"Species";
"------";
$species_feature:set_character_species(this, $species_human);
"------";
"Features";
"------";
for fo in (this:features())
try
$object_utils:has_callable_verb(fo, "player_rerolled") && fo:player_rerolled(this);
except e (ANY)
player:notify($error:short_traceback(e));
endtry
$command_utils:suspend_if_needed(0);
endfor
"------";
"Sheet";
"------";
if (this.sheet)
$mail_agent:send_message($gm, {$reroll_log}, {tostr("@reroll ", this.name)}, {player:grammar_sub(tostr("%d (%[#d]) was rerolled by %n (%#).  %[dppc] character was ", this.last_reroll ? $string_utils:from_seconds(time() - this.last_reroll) + " old" | "never re-rolled before", ".  Previous sheet follows.")), "-----", @this.sheet});
this.sheet = {};
endif
.
#91:2
":is_stat(STR statname)";
"-> Does the given string refer to a statistic of this character?";
stat = args[1];
return `property_info($skilled, stat) ! ANY' ? 1 | 0;
.
#91:3
":stat_<statname>()";
"-> Return the value of the given stat.";
stat = verb[6..$];
return this:get_stat(stat);
.
#91:4
":penalty([skill]) -> Total penalty from all condition monitors.";
return -(((this.injury / 2) + (this.insanity / 4)) + (this.fatigue / 5));
.
#91:5
"By default, pass to $rpg.  Might wanna add in specialty skills here.";
return $rpg:(verb)(@args);
.
#91:6
":total(skill) -> Total bonus for this character using `skill'.";
skill = this:match_skill(args[1]);
if (!valid(skill))
return 0;
endif
return skill:total(this);
.
#91:7
"Reroll a character upon creation.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
if (is_player(this))
this:reroll();
endif
return pass(@args);
.
#91:8
":all_skills() -> Names of all skills defined on the $skilled character.";
return $set_utils:difference(properties($skilled), $rpg.atts, $rpg.monitors, $rpg.senses, {"pot", "should_improve"});
.
#91:9
":set_stat_<statname>(value)";
"Set the given statname to the given value.  Here so that progs can use the @set verb to set stats.";
"Example: @set npc.stat_quickness to 68";
"         Would set NPC's 'quickness' stat to 68.";
if (!$rpg:trusted(caller_perms()))
return E_PERM;
endif
verb[1..9] = "";
set_task_perms(caller_perms());
return this:set_stat(verb, @args);
.
#91:10
":get_stat(stat)";
"Get the value of the given RPG statistic.";
if (!this:is_stat(stat = args[1]))
return E_INVARG;
endif
return this.(stat);
.
#91:11
":set_stat(stat, value)";
"Set the value of the given RPG statistic.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
{stat, value} = args;
if (is_player(this))
$rpg:log_stat_change(@args);
endif
return this.(stat) = value;
.
#91:12
":mod_stat(stat, change[, min[, max]])";
"Add the given value to the given RPG statistic.";
{stat, mod, ?min_rank = 0, ?max_rank = $maxint} = args;
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
elseif (!this:is_stat(stat))
return E_INVARG;
endif
now_rank = this.(stat);
if (typeof(mod) != INT)
raise(E_TYPE);
endif
new_rank = now_rank + mod;
return this:set_stat(stat, max(min(new_rank, max_rank), min_rank));
.
#91:13
":maybe_reset_stats()";
"Decide whether or not the stats for this character should be reset to parent values.  Useful for curbing the improvement of generic creatures, so that we don't have beastly fidos with Unarmed skills of 1000.";
"Should only be called at birth, by the :birth_effects method.";
if (this:is_unique())
return 0;
endif
for pn in ({@this:all_skills(), @$rpg.atts, @$rpg.senses})
clear_property(this, pn);
endfor
return 1;
.
#91:14
":set_potential(num)";
"Gamemasters may use this verb (or `@set char.pot to N`) to reward players for good role-play, completion of adventures, etc.  Rewarding more than 10 pot at a time should be rare and well-justified.";
if (!$rpg:trusted(caller_perms()))
raise(E_PERM);
elseif (typeof(n = args[1]) != FLOAT)
raise(E_TYPE);
else
return this.pot = n;
endif
.
#91:15
":tell_stats([BOOL atts[, BOOL skills[, BOOL senses[, BOOL monitors]]]])";
"Print stats of this character.";
{?show_atts = 1, ?show_skills = 1, ?show_senses = 1, ?show_monitors = 1} = args;
RPG = $rpg;
SU = $string_utils;
LU = $list_utils;
info = att_info = id_info = mon_info = sense_info = skill_info = {};
"primary attributes";
if (show_atts)
atts = RPG.atts;
for att in (atts)
att_info = {@att_info, RPG:char_stat_line(this, att, 1)};
endfor
"identity: species, gender, etc?";
if ("finger header needs perms set to caller")
id_info = {};
else
id_info = `this:finger_header() ! E_VERBNF => {}';
endif
endif
"status monitors";
if (show_monitors)
for mon in (RPG.monitors)
mon = SU:capitalize(mon);
mon_info = {@mon_info, SU:left(mon + ": ", -12) + SU:right(this.(mon), 3)};
endfor
mon_info = {@mon_info, ""};
mon_info = {@mon_info, tostr("Upper Mobility: ", SU:right(this.upper_mobility, 3), "%")};
mon_info = {@mon_info, tostr("Lower Mobility: ", SU:right(this.lower_mobility, 3), "%")};
endif
"senses, perception skill";
if (show_senses)
sense_info = {};
for sense in (RPG.senses)
sense_info = {@sense_info, RPG:char_stat_line(this, sense)};
endfor
endif
"skills";
if (show_skills)
skill_info = {};
if (1)
for skill in (this:all_skills())
skill_info = {@skill_info, RPG:char_stat_line(this, skill, 1)};
endfor
width = length(LU:longest(skill_info));
skill_info = SU:columnize(skill_info, player:linelen() / width);
else
"this one is still kinda ugly";
skill_info = {@skill_info, @RPG:indented_skill_sheet(player)};
endif
endif
"the whole shebong";
if (id_info)
info = {@info, @id_info, ""};
endif
if (mon_info)
info = {@info, @LU:merge({att_info, mon_info}, "     "), "", @sense_info, "", @skill_info};
else
info = {@info, @att_info, "", @sense_info, "", @skill_info};
endif
"pretty borders, the wind up, and the pitch";
tborder = SU:left("--" + this.name, player:linelen(), "-");
bborder = SU:right(this.name + "--", player:linelen(), "-");
info = {tborder, @info, bborder};
player:tell_lines(info);
.
#91:16
":filter_speech(STR spoken_text)";
{?text = ""} = args;
if (!this.ic)
return text;
endif
ins = $rpg.gibber_threshold;
if (ins && (this.insanity > (ins + random(ins))))
gibcore = $gibber_feature;
return gibcore:to_gibberish(text, gibcore:get_seed_for_user(player));
endif
return text;
.
#91:17
":should_improve(OBJ skill)";
"Should this character be allowed to improve the given skill?  By default, return true if this.should_improve and the character is IC.";
return this.should_improve && this.ic;
.
#92:0
":status()";
"-> STR of what this character is doing.  Ie: `Flo is attacking Boojum.'";
":result()";
"-> NUM result of last attack resolve.";
return this.(verb);
.
#92:1
":current_weapon()";
"-> OBJ weapon this character is currently using.";
what = this.current_weapon;
if ((what in this:wielding()) || this:is_natural_weapon(what))
return what;
else
return this.current_weapon = this:random_weapon();
endif
.
#92:2
":weapons() -> LIST of all weapons wielded.";
return this.wielding;
.
#92:3
":attacking([character])";
"-> LIST of who this character is attacking, or {} if nobody.";
"-> NUM  true/false if this character is attacking given character.";
":dodging([character])";
"-> LIST of who this character is actively dodging, or {} if nobody.";
"-> NUM  true/false if this character is actively dodging given character.";
return args ? args[1] in this.(verb) | this.(verb);
.
#92:4
":add_action(STR what, STR|LIST action, LIST args, NUM slowness)";
"-- Add the given action to the queue.";
"what       - String describing the action.";
"action     - Verbname to call, or list of {object, verbname}.";
"args       - Args to verbcall.";
"slowness   - Time required for the attack.";
"-> NUM length of new queue.";
"-> E_PERM if illegal caller or obj is specified and not owned by caller.";
"-> E_NACC if the action is not permitted.";
"If a custom object is given as element one in 'action', the calling permissions MUST control it for it to be added to the queue.";
cp = caller_perms();
if (!this:is_controllable_by(cp, caller))
return E_PERM;
elseif (!(q = this.queue))
this.slowness = args[4];
endif
spec = args[2];
if (typeof(spec) != LIST)
action = listinsert(args, this, 2);
elseif (0 && (!spec[1]:is_writable_by(cp)))
return E_PERM;
else
action = {args[1], @spec, args[3], args[4]};
endif
if ($rpg:is_rushed_task())
this.queue = {action, @q};
total = 1;
$rpg:delete_rushed_task();
else
total = length(this.queue = {@q, action});
endif
where = this.location;
where:add_combatant(this);
where:birth_combat();
return total;
.
#92:5
":remove_action(NUM n) -- Remove the action indexed by `n' from the queue.";
"-> NUM length of new queue.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
elseif (!(q = this.queue))
return E_NONE;
endif
q = listdelete(q, args[1]);
this.slowness = q ? q[1][5] | 0;
return length(this.queue = q);
.
#92:6
":cancel_action() -- Cancel the last action in the queue.";
"-> NUM length of new queue.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
elseif (!(l = length(q = this.queue)))
return E_NONE;
endif
return this:remove_action(l);
.
#92:7
":act() -- Do the first action inthe queue.";
"The action will be called with player and caller_perms() of this object.";
"-> NUM Action points expended.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
elseif (this:death_pending())
return E_NACC;
elseif (unc = this:unconscious())
"Will heal unconsciousness down to 1, minimum.  No valid reason.";
if (unc > 1)
this.unconscious = this.unconscious - 1;
endif
this.queue = {};
return 0;
endif
q = this.queue;
if (!q)
return 0;
endif
action = q[1];
"----- This should perhaps be moved into each :do_* verb -----";
ok = this:attempt_action(action[3], action[4]);
if (typeof(ok) != NUM)
this:remove_action(1);
return 0;
endif
"-----";
this.pending_actions = {@this.pending_actions, action};
this:remove_action(1);
fork (0)
if (this:unconscious() || this:death_pending())
return 0;
endif
player = this;
dobj = iobj = $nothing;
this:my_call_verb(action[2], "do_" + action[3], action[4]);
endfork
return action[5];
.
#92:8
":cleanup() -- End of round cleanup.";
if (!this:is_controllable_by(caller_perms()))
return E_PERM;
endif
for p in (this.pending_actions)
p[2]:("cleanup_" + p[3])(@p[4]);
this.pending_actions = setremove(this.pending_actions, p);
endfor
this.status = "";
this.result = 0;
.
#92:9
":do_attack(OBJ target, INT nice[, OBJ body_area])";
"Perform an attack on target's body_area with a converse lethality of 'nice'.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
{dobj, nice, ?area = $nothing} = args;
ipt = `dobj:is_possible_target(this) ! E_VERBNF => 0';
if (!ipt)
$you:say_action("Breath ragged and braced for combat, %n %<scans> the area for %[ddname].  %S %<relaxes/relax> %p stance, realizing %[dps] %<d:is> no longer a threat.");
return E_NONE;
elseif (typeof(ipt) == STR)
$you:say_action(ipt);
return E_NONE;
elseif (dobj:death_pending())
$you:say_action("%N %<beats> senselessly at the bloody ground, cursing the lifeless shadow of %[ddname,former].");
return E_MAXREC;
endif
where = this.location;
this:add_attacker(dobj);
"get weapon, its skill, and the attacker's total proficiency in it";
weapon = this:current_weapon();
skill = weapon:skill(this, dobj);
total = this:total(skill);
"tag the 'niceness' of this attack (converse of lethality)";
this.nice = nice;
"allow the weapon a chance to handle the attack";
if (!weapon:attempt_attack(this, dobj, area))
return;
endif
"set the base bonus to the last attack -10, so people can ride their success/failure";
bonus = this.result / 10;
"get weapon attack modifier";
mod = weapon:attack_mod(this, dobj);
bonus = bonus + mod;
"apply a called shot penalty if it's a called shot";
if (valid(area))
mod = area:called_shot_mod(dobj, this, weapon);
else
mod = 0;
area = weapon:random_hit_location(this, dobj);
endif
bonus = bonus + mod;
"apply a massive bonus for attacking an unconscious target";
if (unc = dobj:unconscious())
bonus = bonus + 100;
endif
"roll the attack and store it as last result";
this.result = attack = this:resolve(skill, bonus);
"announce the attempt";
weapon:announce_swing(this, dobj, area);
"now process the attack!";
if (weapon:critical_miss(this, dobj, area, attack))
"weapon critical miss handled everything";
elseif (attack < ((total < 0) ? 0 | (-min(total, 100))))
"automatic miss if total is less than zero,";
"or attack roll is less than zero minus total,";
"but anything less than -100 is always a miss";
weapon:announce_miss(this, dobj, area);
elseif (dobj:do_defense(this, area))
"successful dodge; messages handled in do_defense";
elseif (weapon:critical_hit(this, dobj, area, attack))
"weapon critical hit handled everything";
else
dmg = weapon:damage(this, dobj);
if (unc)
dmg = dmg * 3;
endif
level = dobj:receive_damage(dmg, weapon:lethality(this, dobj), this, area);
if (level == $maxint)
weapon:announce_kill(this, dobj, area, level);
elseif (level == $minint)
weapon:announce_knockout(this, dobj, area, level);
elseif (level)
weapon:announce_hit(this, dobj, area, level);
else
"no damage, messages printed";
endif
endif
weapon:finished_attack(this, dobj, area);
where:broadcast_event_attack(this, dobj, area, attack);
"19980829: Big re-write to handle possible float modifiers from weapon:attack_mod and area:called_shot_mod, scaled automatic miss to function of attacker's actual skill, fixed long-standing bug where a FALSE return from weapon:critical_miss was construed as it handling the miss! <Quinn>";
.
#92:10
":do_defense(attacker[, body_area])";
"Handle defensive action against an attack by the given character to the given body area.";
"Return -1 if the attack was dodged, 1 if it was parried, 2 if guarded against by someone else.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
elseif (this:unconscious())
return 0;
endif
"initialization";
{who, ?area = $nothing} = args;
against = who:current_weapon();
if (!valid(area))
area = against:random_body_area(this);
endif
this.last_attacked_by = {who, time()};
"--- DEFEND ---";
if (this.defenders)
d = $list_utils:random_element(this.defenders);
r = d:attempt_guard(this, who, area);
if (typeof(r) == ERR)
"remove the defender if an error is returned";
"in the confusion, lose a round of defense";
this.defenders = setremove(this.defenders, d);
elseif (r)
return 2;
endif
endif
"----- DODGE -----";
dodge = this:attempt_dodge(who, area);
if (!dodge)
return 0;
elseif (dodge > 0)
return -1;
endif
"----- PARRY -----";
if (this:attempt_parry(who, area))
return 1;
endif
return 0;
"19980829: Big re-write to use new float-aware parry_with and parry_against modifiers on weapons, and to fix the long-standing defense loop bug which rendered it USELESS.  <Quinn>";
"19981229: Remove defender if :attempt_guard returns an ERR value.  <Quinn>";
.
#92:11
":do_flee([exitspec])";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
exits = this.location:obvious_exits();
if (args)
exit = args[1];
elseif (!exits)
exit = #-1;
elseif ($object_utils:has_callable_verb(this, "ok_exit"))
exit = #-1;
for x in (exits)
if (this:ok_exit(x))
exit = x;
break;
endif
endfor
else
exit = $list_utils:random_element(exits);
endif
if (!$object_utils:isa(exit, $exit))
player:tell(exits ? "You can't go that way." | "There is no escape!");
return E_INVIND;
endif
exit:invoke();
.
#92:12
":do_dodge([@who])";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
dodgem = $set_utils:intersection(args || this:attacking(), this:room():combatants());
whostr = $string_utils:name_list(dodgem, "nobody");
if (dodgem)
$you:say_action(((("%N %<prepares> for " + ((length(dodgem) == 1) ? "an attack" | "attacks")) + " from ") + whostr) + ".", this);
else
$you:say_action("%N %<assumes> a full defensive posture.");
endif
base = $rpg:att_bonus((this:stat_quickness() + this:stat_agility()) / 2);
this.defense_bonus = max(base, 10);
this.dodging = dodgem;
.
#92:13
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
"$you:say_action(\"%N %<relaxes> %p stance.\", this);";
this.defense_bonus = 0;
this.dodging = {};
.
#92:14
":receive_damage(NUM dam, NUM lethality[, OBJ weapon|attacker[, OBJ area]])";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
elseif (this:death_pending())
return E_MAXREC;
endif
{damage, ?lethality = 0, ?aggressor = $nothing, ?area = this:random_body_area()} = args;
if (!valid(aggressor))
attacker = caller;
weapon = `attacker:current_weapon() ! E_VERBNF => $nothing';
elseif ($rpg:is_char(aggressor))
attacker = aggressor;
weapon = `attacker:current_weapon() ! E_VERBNF => $nothing';
else
attacker = player;
weapon = aggressor;
endif
"Set this AGAIN just in case the attack didn't go through :do_defense.";
this.last_attacked_by = {attacker, time()};
weapon = valid(weapon) ? weapon | $weapon;
if (valid(area))
damage = this:absorb_damage(damage, area, weapon, attacker);
endif
if (!damage)
"..absorbed by armour, messages printed...";
return E_NONE;
endif
inj = this.injury + damage;
end = this:stat_endurance();
nice = `attacker:nice(this) ! ANY => 0';
level = (damage / (end / 5)) || 1;
"Apply the injury here, to avoid missing it on unconsciousness and";
"death in the iffy structure below.";
this.injury = inj;
"A single hit worth twice endurance, or a total injury greater than";
"four times endurance will automatically kill/knockout.";
if (((damage < (end + 50)) && (inj <= (end * 3))) && (this:resolve("shock") > (-end)))
"...survived...";
elseif (((random(100) + nice) > lethality) && (!this:is_mortally_wounded()))
"...knockout...";
if (this:unconscious())
"...don't do it again, apply damage...";
else
alt = this:alt_unconsciousness(area, damage, attacker);
if (alt == 1)
return 0;
elseif (!alt)
fork (0)
this:unconsciousness(attacker, damage, area);
endfork
return $minint;
endif
endif
else
alt = this:alt_death(area, damage, attacker);
if (alt == 1)
return 0;
elseif (!alt)
fork tid (0)
this:death(attacker, damage, area);
endfork
this.dt = tid;
return $maxint;
endif
endif
return level;
.
#92:15
"dodge [<combatant>]";
"dodge             -- Dodge everyone.  Less of a defensive bonus.";
"dodge <combatant> -- Dodge combatant.  Full defensive bonus.";
if (callers() && (!this:is_controllable_by(caller_perms(), caller)))
return E_PERM;
elseif (!args)
return this:queue_action("dodge everyone", "dodge", {}, 20 - this:quickness());
endif
where = this:room();
who = $match_utils:match_environment(dobjstr, where:contents());
if (who == $failed_match)
player:tell("You don't see any \"", dobjstr, "\" here to dodge.");
elseif (who == $ambiguous_match)
all = $match_utils:match_list(dobjstr, where:contents());
player:tell("You'll have to choose between ", $string_utils:name_list(all), ", or just 'dodge' to evade everyone.");
elseif ((!$object_utils:isa(who, $combatant)) && (who:(verb)(@args) != E_VERBNF))
"Object is a non-combatant with a dodge verb defined.";
elseif (who == player)
player:tell("Dodge yourself?  Are you feeling OK?");
else
this:queue_action("dodge " + who:name(), "dodge", {who}, 20 - this:quickness());
endif
.
#92:16
":add_attacker(who)";
"Add `who' as a character in combat with this one.";
if (!this:is_controllable_by(caller_perms()))
return E_PERM;
endif
who = args[1];
this.location:add_combatant(this, who);
return this.attacking = setadd(this.attacking, who);
.
#92:17
":remove_attacker(who)";
"Remove `who' as a character in combat with this one.";
if (!this:is_controllable_by(caller_perms()))
return E_PERM;
endif
who = args[1];
this.location:remove_combatant(who);
if (!(attacking = setremove(this.attacking, who)))
this.location:remove_combatant(this);
endif
return this.attacking = attacking;
.
#92:18
"stun/kill             -- Stun the first person in your attacking list.";
"stun/kill <combatant> -- Stun the given combatant.";
"stun/kill ... with <weapon> -- Use the given weapon to do the above.";
"`Stun' is like `kill' except that you are attempting to knock the given combatant unconscious, NOT kill em.";
"In a `stun' attack damage is tken as normal, but when it comes time for the victim to die there is a greater chance e will simply fall unconscious.";
if (callers() && (!this:is_controllable_by(caller_perms(), caller)))
return E_PERM;
endif
weapon = this:my_match_wielding(iobjstr);
if (this:wielding_match_failed(weapon, iobjstr))
return;
endif
this:set_current_weapon(weapon);
action = "attack";
nice = (verb in {"attack", "kill"}) ? (-random(50)) - 25 | this:total(weapon.skill);
if (!args)
dobj = this:resolve_null_target();
return valid(dobj) && this:queue_action((verb + " ") + dobj:name(), action, {dobj, nice}, (20 - this:quickness()) + weapon:slowness(this, dobj));
endif
match = this:my_match_target(dobjstr);
if (this:target_match_failed(match))
return;
endif
match = match[2];
dobj = match[1];
if (!$object_utils:isa(dobj, $combatant))
"970405-1800: Support for non-combatant attacks?";
if (`dobj:receive_attack_request(this, weapon, @args) ! E_VERBNF')
return;
endif
this:tell(dobj:grammar_sub("%[dtitlec] %<d:isn't> worth the trouble."));
return;
elseif (this == dobj)
this:tell("Kill yourself?  aW...things aren't that bad.  Buck up, lil camper.");
return;
endif
if (length(match) == 1)
this:queue_action((verb + " ") + dobj:name(), action, {dobj, nice}, (20 - this:quickness()) + weapon:slowness(this, dobj));
else
area = match[2];
this:queue_action((((verb + " ") + dobj:name()) + "'s ") + area:name(), action, {dobj, nice, area}, (30 - this:quickness()) + weapon:slowness(this, dobj));
endif
this:add_attacker(dobj);
.
#92:19
"flee [<direction>]";
"flee             -- Flee in any direction.  Quicker.";
"flee <direction> -- Flee in the given direction.";
if (callers() && (!this:is_controllable_by(caller_perms(), caller)))
return E_PERM;
endif
where = this.location;
if (!args)
this:maybe_queue_action("flee anywhere", "flee", {}, 30 - this:quickness());
elseif (valid(exit = where:match_exit(dobjstr)))
this:maybe_queue_action("flee " + exit:name(), "flee", {exit}, 50 - this:quickness());
elseif (exit == $ambiguous_match)
all = $match_utils:match_list(dobjstr, where:obvious_exits());
player:tell("You'll have to choose between ", $string_utils:name_list(all), ", or just 'flee' to run like Hell.");
else
player:tell("You can't go that way.  (", dobjstr, ")");
endif
.
#92:20
pass(@args);
this:co();
"this:tell_health();";
.
#92:21
":tell_health() -- Show this character's condition.";
inj = args ? args[1] | this:stat_injury();
rat = inj / (this:stat_endurance() / 3);
if (callers()[1][2] == "look_self")
who = this;
psc = "%S";
elseif (this == player)
who = $you;
psc = "%(titlec)";
else
who = this;
psc = "%(titlec)";
endif
b = this.bleeding;
if ((!b) && (!inj))
msg = psc + " %<is> uninjured.";
elseif (rat < 1)
msg = psc + " %<has> a few minor ouchies.";
elseif (rat < 3)
msg = psc + " %<has> a few scratches and bruises.";
elseif (rat < 6)
msg = psc + " %<has> some nasty wounds.";
elseif (rat < 9)
msg = psc + " %<is> barely held together by tattered flesh and bone stained with blood.";
else
msg = psc + " %<is> on the brink of death.";
endif
if (!b)
"no bleeding";
elseif (b < 10)
msg = tostr(msg, "  A few of %p wounds are bleeding lightly.");
elseif (b < 30)
msg = tostr(msg, "  Fresh blood wets several of %p wounds.");
elseif (b < 60)
msg = tostr(msg, "  %P wounds leak copious life.");
else
msg = tostr(msg, "  Blood gushes freely from multiple wounds.");
endif
fat = this:stat_fatigue();
if (fat < 10)
"";
elseif (fat < 60)
msg = tostr(msg, "  %S %<shows> signs of minor fatigue.");
elseif (fat < 120)
msg = tostr(msg, "  Sweat coats %p brow, and %s %<appears> somewhat out of breath.");
else
msg = tostr(msg, "  %S %<is> suffering from severe fatigue.  %P tongue lolls out, dry from breathless panting, and %p bare skin glimmers with a sheen of sweat.");
endif
ins = this:stat_insanity();
if (ins < 10)
"";
elseif (ins < 55)
msg = tostr(msg, "  %P eyes are bloodshot, and %s %<blinks> and %<twitches> with mild emotional distress.");
elseif (ins < 110)
msg = tostr(msg, "  %S %<is> visibly nervous, flesh crawling with involuntary fidgets and facial twitches.");
else
msg = tostr(msg, "  %P eyes dart back and forth towards the slightest motion, and a line of drool dangles from %p lower lip.");
endif
u = this:unconscious();
m = this.upper_mobility;
if (u)
"...no telling mobility...";
elseif (m < 25)
msg = tostr(msg, "  %P hands are gnarled bunches of digits.  %S %<has> trouble holding things, and joints pop grotesquely when %s %<moves> %p arms.");
elseif (m < 60)
msg = tostr(msg, "  %P arms move with difficulty, joints rigid from badly healed wounds.");
endif
m = this.lower_mobility;
if (u)
"...ditto...";
elseif (m < 25)
msg = tostr(msg, "  %S %<is> having trouble standing.  %P legs are gnarled and weak.");
elseif (m < 60)
msg = tostr(msg, "  %S %<appears> to have a bit of a limp.");
endif
if (u > 0)
msg = tostr(msg, "  %N %<is> unconscious.");
elseif (u < 0)
msg = tostr(msg, "  %N %<is> sleeping.");
endif
m = this.bleeding;
player:tell($string_utils:pronoun_sub(msg, who));
.
#92:22
":slowness() -> NUM slowness penalty for current round.";
"The minimum slowness for a character is 0.";
return max(this.(verb) || 0, 0);
.
#92:23
"  wield <object>";
"Wield the given object.  Currently only kids of $wieldable can be wielded.";
"";
"  wield <object> with <x> hands";
"Wield an object with <x> of your hands, possibly causing more damage.";
dobj = player:my_match_object(dobjstr, c = player:contents());
if ($match_utils:object_match_failed(dobj, dobjstr, c, "on your person"))
return E_INVARG;
endif
if (dobj in this:wielding())
player:tell("Warning!  You're already wielding that.");
endif
this:maybe_queue_action("wield " + dobj:name(), "wield", {dobj}, 25 - this:quickness());
.
#92:24
"sheathe <object> -- Stop wielding the given object.";
if (callers() && (!this:is_controllable_by(caller_perms(), caller)))
return E_PERM;
endif
dobj = player:my_match_object(dobjstr, c = player:wielding());
if ($match_utils:object_match_failed(dobj, dobjstr, c, "in your hands"))
return E_INVARG;
endif
this:maybe_queue_action("unwield " + dobj:name(), "unwield", {dobj}, 10 - this:quickness());
.
#92:25
":exitfunc(what) -- Remove it from wielding.";
"No security-- programmers should be trusted as GMs.";
this.wielding = setremove(this.wielding, args[1]);
return `pass(@args) ! E_VERBNF';
.
#92:26
":natural_weapon()";
"-> OBJ what the character uses when not wielding anything.  If the property's value is a list, choose a random weapon from that list.";
":natural_weapons()";
"-> Return a list of all natural weapons.";
value = this.natural_weapon;
if (verb == "natural_weapons")
return $string_utils:lines_to_list(value);
elseif (typeof(value) == LIST)
return $list_utils:random_element(value);
else
return value;
endif
.
#92:27
"heal <pc> [with <item>]";
"Attempt to heal this character.  Note that inept practictioning could make your patient's condition worse.";
"You may attempt to use an item to aid in the healing, but beware: It may be inadequate for the task and make your patient worse.";
if (valid(caller_perms()) && (!this:is_controllable_by(caller_perms(), caller)))
return E_PERM;
endif
where = this.location;
dobj = dobjstr ? player:my_match_object(dobjstr) | player;
if (dobj == $nothing)
player:tell("Who do you want to heal?");
return;
elseif (dobj == $failed_match)
player:tell("There's no \"", dobjstr, "\" here to revive.");
return;
elseif (dobj == $ambiguous_match)
all = $match_utils:match_list(dobjstr, where:environment());
player:tell("You'll have to choose between ", $string_utils:name_list(all), ".");
return;
endif
what = {};
if (iobjstr)
what = player:my_match_object(iobjstr);
if ($match_utils:object_match_failed(what, iobjstr, $match_utils:match_list(iobjstr, player.location:environment())))
return;
endif
what = {what};
endif
if (`dobj:receive_heal_request(this, what ? what[1] | $nothing, @args) ! E_VERBNF')
return;
elseif (!$object_utils:isa(dobj, $combatant))
player:tell(dobj:name(), " is beyond your help.");
return;
endif
if (!$rpg:trusted(player))
if (!this.ic)
player:tell("You aren't allowed to heal while out of character.  Type `@ic` first.");
return E_NACC;
elseif (!`dobj.ic ! E_PROPNF')
player:tell(dobj:grammar_sub(tostr("Wouldn't it be more exciting healing %n while %s %<is> vulnerable to attack?  You should tell %o to go @ic and find out!")));
return E_NACC;
endif
endif
if (!dobj:stat_injury())
player:tell("Warning!  ", dobj:name(), " is not injured at this time.");
endif
if (!dobj:trusts(this, "heal"))
player:tell("Warning!  ", dobj:name(), " does not trust you to heal ", dobj:po(), ".");
endif
this:maybe_queue_action(("heal " + dobj:name()) + (what ? " using " + what[1]:iname() | ""), "heal", {dobj, @what}, 50 - this:quickness());
.
#92:28
":death(killer, damage, area) -- Kill the poor chap.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
clear_property(this, "dt");
killer = args[1];
killer = valid(killer) ? killer | $nothing;
weapon = killer:current_weapon();
weapon = valid(weapon) ? weapon | $nothing;
if (typeof(this:death_effects(killer, weapon)) == ERR)
return;
endif
"Tell yer friends!";
where = this.location;
if (this:unconscious())
this:notify(tostr("You have been killed by ", killer:dname(), "!"));
endif
"personalized announcement is now checked in $weapon:announce_kill";
this:announce_messages("death", this, weapon, where, killer);
"boot_player(this);";
this:deposit_remains(killer, weapon);
this:afterlife(killer, weapon);
where:broadcast_event_death(this, killer);
.
#92:29
"actions -- List your queued actions.";
actions = player.queue;
if (!actions)
player:tell("You have no actions queued.");
return E_NONE;
endif
for i in [1..length(actions)]
player:tell($string_utils:right(i, 2), " -> ", actions[i][1]);
endfor
.
#92:30
"ht <character> -- Show the given character's degree of injury.";
dobj = dobjstr ? player:my_match_object(dobjstr) | this;
if ($command_utils:object_match_failed(dobj, dobjstr))
"...no match...";
elseif (!$object_utils:isa(dobj, $combatant))
player:tell("Pshaw!  ", dobj:title(), " is hardly capable of combat!");
else
dobj:tell_health();
endif
.
#92:31
"cancel [num]";
"Cancel your last action queued, or your last NUM actions.";
"cancel ALL";
"Cancel all of your actions.";
if (valid(cp = caller_perms()) && (!this:is_writable_by(cp, caller)))
return E_PERM;
endif
total = 0;
if (dobjstr == "ALL")
count = $maxint;
else
count = max(dobjstr ? tonum(dobjstr) | 1, 1);
endif
while (count)
result = this:cancel_action();
if (result == E_NONE)
count = 0;
else
total = total + 1;
count = count - 1;
endif
endwhile
if (total)
player:tell(total, " ", $english:pluralize("action", total), " removed.  ", result ? tostr("(", result, ") left.") | "None left.");
else
player:tell("You have no actions queued.");
endif
.
#92:32
"eq <character> -- Show what the given character is wielding.";
dobj = dobjstr ? player:my_match_object(dobjstr) | this;
if ($command_utils:object_match_failed(dobj, dobjstr))
"...no match...";
elseif (!$object_utils:isa(dobj, $combatant))
player:tell("Pshaw!  ", dobj:title(), " is hardly capable of combat!  What would ", dobj:ps() || "it", " wield?");
else
dobj:tell_equipped();
endif
.
#92:33
"co <character> -- Show the condition of the given character, including weapons wielded and degree of injury.";
dobj = dobjstr ? player:my_match_object(dobjstr) | this;
if ($command_utils:object_match_failed(dobj, dobjstr))
"...no match...";
elseif (!$object_utils:isa(dobj, $combatant))
player:tell(dobj:title(), " is a lily-livered non-combatant whose condition is inconsequential.");
else
dobj:ht();
dobj:eq();
endif
.
#92:34
":nice(who)";
"The lethality of an attack against `who', usually in a range of -100 to 100.";
return this.nice;
.
#92:35
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
clear_property(this, "nice");
.
#92:36
":unconsciousness(OBJ killer, INT damage)";
"Knock this character unconscious.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
{killer, damage, ?area = #-1} = args;
this.queue = {};
this.unconscious = this.unconscious + max(damage / 2, 1);
where = this:room();
where:broadcast_event_knockout(this, @args);
.
#92:37
":unconscious() -> Is this character unconscious?";
return this.unconscious;
.
#92:38
":do_revive(who)";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
dobj = args[1];
where = this.location;
if (dobj.location != where)
this:tell("Looks like ", dobj:name(), " woke up and wandered off.");
return E_RANGE;
endif
severity = dobj:unconscious();
if (!severity)
this:tell("Looks like ", dobj:name(), " doesn't need your help anymore.");
return E_NONE;
endif
"If the person is just sleeping (negative unconsciousness), this gives";
"a big bonus.";
resolve = this:resolve("Medic", (-severity) * 3, random(100) < 2);
$you:say_action("%N %<attempts> to wake %[ddname].");
if (resolve < -25)
$you:say_actioN("%N %<remains> unconscious.", dobj);
elseif (resolve < 0)
$you:say_action("%N %<turns> restlessly, but %<does> not regain consciousness.", dobj);
dobj:notify("You feel a presence at the borders of your unconsciousness: A warm fire in cold darkness, a knock on a cell door, the light over water viewed by a drowning man.");
dobj:notify("However, you're still out cold.");
else
dobj:wake_up(1);
$you:say_action("%N %<has> revived %d.");
endif
.
#92:39
"revive <character>";
"Try to bring a character back to consciousness.";
if (valid(caller_perms()) && (!this:is_controllable_by(caller_perms(), caller)))
return E_PERM;
endif
where = this.location;
who = $match_utils:match_environment(dobjstr, where:environment());
if (who == $nothing)
player:tell("Who do you want to revive?");
elseif (who == $failed_match)
player:tell("You don't see any \"", dobjstr, "\" to revive.");
elseif (who == $ambiguous_match)
all = $match_utils:match_list(dobjstr, where:environment());
player:tell("You'll have to choose between ", $string_utils:name_list(all), ".");
elseif (!$object_utils:isa(who, $combatant))
player:tell(who:name(), " is beyond help.");
elseif (!who:unconscious())
player:tell(who:name(), " doesn't need your help.");
elseif (who == player)
player:tell("If you could revive yourself, you wouldn't need to.");
else
this:maybe_queue_action("revive " + who:name(), "revive", {who}, 30 - this:quickness());
endif
.
#92:40
":set_<property>(value) -> Set the given property.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
prop = verb[5..$];
return this.(prop) = args[1];
.
#92:41
":defense_bonus(attack-object) -> NUM defense bonus against the given attacker.";
"Modified depending on who the character is actively defending against.";
if (this:unconscious())
"...big hit time baby oh yeah...";
return -100;
endif
dodging = this:dodging(@args);
if (!dodging)
return 0;
elseif (args)
return this.defense_bonus / dodging;
else
return this.defense_bonus / length(dodging);
endif
.
#92:42
":queue_action(what, action, args, slowness)";
"Add the given action, printing messages for any errors which might occur.";
"Return the position of the new action if successful, false otherwise.";
if (!this:attempt_queue(@args))
return;
endif
set_task_perms(cp = caller_perms());
action = args[2];
result = this:add_action(@args);
if (typeof(result) == NUM)
this:tell("Action \"", args[1], "\" (", result, ") queued.");
elseif (result == E_PERM)
cp:tell("Permission denied to add action ", $string_utils:print(args), " to queue of ", $string_utils:nn(this), ".");
this:tell("Action \"", args[1], "\" NOT queued: ", result);
elseif (result == E_NACC)
this:tell("Action \"", args[1], "\" not permitted in this area.");
else
this:tell("Action \"", args[1], "\" NOT queued: ", result);
endif
return result;
.
#92:43
":do_heal(who)";
"Attempt to heal the given character.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
who = args[1];
where = player.location;
inj = who:stat_injury();
if (!player:is_local_to(who))
player:tell(who:grammar_sub("%N %<isn't> here anymore."));
return E_NONE;
elseif (!who:trusts(player, "heal"))
player:tell("It looks as though your help isn't wanted by ", who:name(), ".  Ask ", who:po(), " to type `@trust ", player.name, " to Heal Me` or `@options combat +trust_healers`.");
return E_NACC;
elseif (this:hands_free() < (n = min(length(this:hands()), 2)))
player:tell("You'll need at least ", n, " hand", (n == 1) ? "" | "s", " free before you can attempt first aid.");
return E_QUOTA;
elseif (who.bleeding)
dobj = who;
$you:say_action("%N %<manages> to stop %d's wounds from bleeding.");
who.bleeding = 0;
return 1;
elseif (!inj)
player:tell(who:grammar_sub("%N %<doesn't> need your help anymore."));
return E_NONE;
endif
what = (length(args) > 1) ? args[2] | $nothing;
if (who == player)
who:tell("You attempt to tend your own wounds", valid(what) ? " with " + what:dname() | "", ".");
where:announce_all_but({who}, (player:name() + " attends to ") + player:pp(), " wounds", valid(what) ? " with " + what:iname() | "", ".");
else
dobj = who;
iobj = what;
$you:say_action(("%N %<rushes> over to %d and %<attends> to %[dpp] wounds" + (valid(what) ? " with " + ((iobj.location == player) ? "%p %i" | "%[idname]") | "")) + ".", player);
endif
if (valid(what))
bonus = what:attempt_heal(player, who) || 0;
else
bonus = 0;
endif
time = time();
info = who.last_healed_by;
last = info[2];
WAIT = 60;
if (time < (last + WAIT))
player:tell("Nothing has changed for ", (player == who) ? "you" | who:po(), " in the past ", $time_utils:english_time(time - last), " to improve your chances.  All you can do now is wait.");
return;
endif
roll = player:resolve("Medic", ((-(inj / 5)) - ((who == player) * 10)) + bonus);
if (((player == info[1]) && (roll < info[3])) && (time < (info[2] + 30)))
if (who == player)
player:tell("You can't do anything more for yourself right now.  Take a break to clear your head and hope for a change in your condition.");
else
player:tell("You can't do anything more for ", who:po(), " right now.  Take a break to clear your head and hope for a change in ", who:pp(), " condition.");
who:tell(player:psc(), " can't do anything more for you at this time.");
endif
success = $minint;
elseif (roll < (-max((inj / 5) * 4, 50)))
if (who == player)
player:tell("Yeeouch!  You've made yourself worse.  Perhaps you should find someone ELSE to butcher you?");
else
player:tell("You've made ", who:pp(), " condition worse!");
who:tell("UGH--You feel worse!");
endif
who:set_stat("injury", inj + random(max(inj / 3, 1)));
success = -1;
elseif (roll < 0)
if (who == player)
player:tell("Damn!  You didn't improve your condition.");
else
player:tell("You did not help ", who:pp(), " condition.");
who:tell("You don't feel any better.");
endif
success = 0;
elseif (roll < inj)
if (who == player)
player:tell("Your self-healing touch has improved your condition.");
else
player:tell(who:namec(), " is improving.");
who:tell("You feel somewhat better under ", player:pp(), " healing touch.");
endif
who:set_stat("injury", inj - random(max(inj / 5, 1)));
success = 1;
else
if (who == player)
player:tell("Physician, you have healed thyself!");
else
player:tell("Splendid work;  ", who:namec(), " is healed!");
who:tell("Praise be!  You've been healed!");
endif
who:set_stat("injury", 0);
success = $maxint;
endif
if (success > $minint)
who.last_healed_by = {player, time, roll};
endif
where:broadcast_event_heal(player, who, what, success);
.
#92:44
":mod_damage(damage, area, weapon, attacker)";
"Return the adjusted damage for a hit to 'area' by 'attacker' wielding 'weapon'.";
"Armour will print a clang message if all damage was absorbed.";
area = args[2];
damage = area:absorb_damage(@listset(args, this, 2));
armour = this:clothing(area);
if (!armour)
return damage;
endif
armargs = listdelete(args, 1);
for foo in (armour)
damage = foo:absorb_damage(damage, @armargs);
if (damage < 1)
return 0;
endif
endfor
return damage;
.
#92:45
":quickness()";
"This combatant's speed, on an ascending roughly 1-25 scale.";
return this:stat_quickness() / 4;
.
#92:46
":random_weapon()";
"-> Choose what weapon this character should use when none is specified on the 'attack' line.";
weapons = this:wielding();
return weapons ? weapons[1] | this:natural_weapon();
.
#92:47
":absorb_damage(damage, area, weapon, attacker)";
"Maybe some natural armour to deflect hits.  Return modified damage.";
dam = this:mod_damage(@args);
if (!dam)
return;
endif
dam = dam - this.natural_protection;
if (dam < 1)
dam = 0;
dobj = this;
$you:say_action(this.oabsorbed_hit_msg, player, args[3]);
endif
return dam;
.
#92:48
":attempt_action(STR action, LIST action_args)";
"Signal that this character is attempting the given action.";
"Return a numeric modifier to the action if it is acceptable, a non-numeric value if not.";
"Print messages if necessary.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
elseif (this:unconscious())
this:notify("The stars in your blackened field of vision spell out the words \"You are unconscious.\"");
return E_NACC;
endif
RPG = $rpg;
SU = $string_utils;
{action, action_args} = args;
violent = RPG:is_violent_action(action);
trusted = RPG:trusted(this);
{?target = 0, @rest} = action_args;
if (trusted)
"";
elseif (action == "heal")
if (!this.ic)
this:notify("You aren't allowed to heal while out of character.  Type `@ic` first.");
return E_NACC;
elseif (!`target.ic ! E_PROPNF')
this:notify(target:grammar_sub(tostr("Wouldn't it be more exciting healing %n while %s %<is> vulnerable to attack?  You should tell %o to go @ic and find out!")));
return E_NACC;
endif
elseif (violent)
if (!this.ic)
this:notify("You aren't allowed to perform violent actions while out of character.  Type `@ic` first.");
return E_NACC;
elseif (!`target.ic ! E_PROPNF')
this:notify(target:grammar_sub(tostr("It wouldn't be very sporting to attack %n while %s %<is> out of character.")));
return E_NACC;
endif
endif
target_isa_player = `is_player(target) ! ANY';
if (target_isa_player && $rpg.npc_are_invulnerable)
if ($rpg:is_npc(this) && (!target:trusts(this, "act on")))
this:notify(target:grammar_sub(tostr("%(dnamec) %<does> not trust you to perform actions on %o.  As an NPC, you are restricted to acting only upon those who consent.  Ask %o to `@trust ", this.name, " to act on me`, or leave %o alone.")));
return E_PERM;
elseif ($rpg:is_npc(target) && (!this:trusts(target, "act on")))
this:notify(target:grammar_sub(tostr("NPC are restricted to acting only upon those who consent.  Conversely, you must consent to their actions if you wish to act upon them.  Type `@trust %n to act on me`, or leave %o alone.")));
return E_PERM;
endif
endif
if (target_isa_player && (!trusted))
ima_player = is_player(this);
if (ima_player && ($alt_player_db:is_alt(target) == {this}))
this:notify("Due to cheating by some players, characters are no longer allowed to perform actions on their alts.");
return E_MAXREC;
endif
if (!violent)
"foo";
elseif ((!$object_utils:connected(target)) && ((time() - target.last_disconnect_time) > RPG.disconnect_safety_threshold))
this:notify(target:grammar_sub(tostr("It wouldn't be very sporting to attack someone who's been gone from the conscious world so long.  Wait until %(dname) %<comes> back.")));
return E_NACC;
elseif (`idle_seconds(target) ! ANY => -1' > RPG.idle_safety_threshold)
this:notify(target:grammar_sub(tostr("It wouldn't be very nice to attack someone who has slipped into a coma, would it?  Be fair and wait until %(dname) %<comes> back to the waking world.")));
return E_NACC;
elseif (!ima_player)
"foo";
elseif ((i = index(this_host = this.last_connect_place, ",")) && (this_host[1..i] == `target.last_connect_place[1..i] ! ANY'))
this:notify(tostr("Due to circumstances beyond your understanding, you are not allowed to attack ", target.name, "."));
return E_MAXREC;
endif
endif
"Check if action is allowed in the character's location.";
where = this.location;
return where:attempt_action(this, @args);
.
#92:49
":is_natural_weapon(weapon)";
"Is the given weapon an appendage, etc of this character?";
what = args[1];
return (this.natural_weapon == what) || (what in this.natural_weapon);
.
#92:50
":do_wield(object[, hands])";
"Wield the given object with the given number of hands.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
dobj = args[1];
if (dobj.location != this)
this:tell("You aren't even holding ", dobj:dname(), ".");
return E_RANGE;
endif
weapons = this:wielding();
if (dobj in weapons)
this:tell("You're already wielding ", dobj:dname(), ".");
return E_NONE;
endif
r = $code_utils:call_verb(dobj, "attempt_wield", {this});
if (!r)
this:tell("You can't seem to wield ", dobj:dname(), ".");
return E_INVIND;
endif
req = dobj:hands_required(this);
hands = (length(args) > 1) ? args[2] | req;
if (this:hands_free() < req)
this:tell("You need at least ", $string_utils:english_number(req), " hand", (req == 1) ? "" | "s", " free to ready ", dobj:iname(), ".");
return E_NACC;
endif
this.wielding = setadd(weapons, dobj);
this:set_current_weapon(dobj);
if (dobj:announce_messages("wield", this))
return;
endif
wield_msg = dobj.wield_msg || "";
owield_msg = dobj.owield_msg || "%N %<readies> %p %t.";
if (wield_msg)
this:tell($string_utils:pronoun_sub(wield_msg, this, dobj));
this:room_announce($string_utils:pronoun_sub(owield_msg, this, dobj));
else
$you:say_action(owield_msg, this, dobj);
endif
.
#92:51
":do_unwield(object)";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
dobj = args[1];
weapons = this:wielding();
if (!(dobj in weapons))
player:tell("You're not wielding ", dobj:dname(), ".");
return E_NONE;
endif
r = $code_utils:call_verb(dobj, "attempt_unwield", {player});
if (!r)
return E_INVIND;
endif
player.wielding = setremove(weapons, dobj);
if (dobj:announce_messages("sheathe"))
return;
endif
sheathe_msg = dobj.sheathe_msg || "";
osheathe_msg = dobj.osheathe_msg || "%N %<puts away> %p %t.";
if (sheathe_msg)
player:tell($string_utils:pronoun_sub(sheathe_msg, player, dobj));
player:room_announce($string_utils:pronoun_sub(osheathe_msg, player, dobj));
else
$you:say_action(osheathe_msg, player, dobj);
endif
.
#92:52
"False if the combatant is unconscious.";
return !this:unconscious();
.
#92:53
":is_wielding(OBJ)";
"Return true if the given object is being wielded by this character.";
return args[1] in this:wielding();
.
#92:54
":get_afterlife()";
"Where does this character go when dead?";
if (!is_clear_property(this, "afterlife"))
return this.afterlife;
endif
return `this.pk ! E_PROPNF' ? $hell | $heaven;
.
#92:55
":afterlife(killer, weapon)";
"Send this character to their afterlife.";
where = this:get_afterlife();
if ($object_utils:has_callable_verb(where, "receive_dead"))
where:receive_dead(this);
else
this:moveto(where);
endif
.
#92:56
":birth()";
"Just rot the corpse for now.";
if ($object_utils:isa(c = this.corpse, $rpg.corpse))
c:rot();
endif
this:birth_effects();
.
#92:57
":deposit_remains(killer, weapon)";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
{killer, weapon} = args;
c = this.corpse;
"allow creators to specify that a creature should not leave a corpse";
if (typeof(c) != OBJ)
return c;
endif
if ($object_utils:isa(c, $rpg.corpse))
c:setup_for(this, killer, weapon);
this.corpse = c;
return c;
endif
c = $rpg.corpse:spawn(this, killer, weapon);
if ((typeof(c) != OBJ) || (!$recycler:valid(c)))
return c;
endif
this.corpse = c;
return c;
.
#92:58
":is_stat(stat)";
"The two stats here should maybe be moved down to $skilled, but seem more appropriate here.";
stat = args[1];
return pass(stat) || (`property_info($combatant, stat) ! ANY' ? 1 | 0);
.
#92:59
":stat_quickness()";
"This stat is affected by the lower_mobility monitor, which is decremented by fatal damage to a lower-body extremity.";
return (pass(@args) * this.lower_mobility) / 100;
.
#92:60
":stat_quickness()";
"This stat is affected by the average of both the lower and upper mobility monitors, which are decremented by fatal damage to an extremity.";
return (pass(@args) * ((this.lower_mobility + this.upper_mobility) / 2)) / 100;
.
#92:61
":stat_dexterity()";
"This stat is affected in part by the upper_mobility monitor, which is decremented by fatal damage to an upper-body extremity.";
if (!this.upper_mobility)
return 0;
else
return (pass(@args) * min(this.upper_mobility + 25, 100)) / 100;
endif
.
#92:62
":alt_unconsciousness(area, damage, attacker)";
":alt_death(area, damage, attacker)";
"Should a normally stunning or fatal or hit with the given characteristics actually cause knockout or death, or should it do something other, such as crippling?";
"Return 0 if unconsciousness or death should occur as normal, 1 if the hit was handled, and -1 if damage should be applied but unconsciousness/death should not result.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
"Added below to prevent an NPC from dying if $rpg.immortal_npc is true. <Quinn;980401>";
if ($rpg:is_npc(this) && $rpg.npc_are_immortal)
return -1;
endif
{area, ?damage = 0, ?attacker = #-1} = args;
return `area:(verb)(this, @listdelete(args, 1)) ! ANY => 0';
.
#92:63
":birth_effects()";
"Heal the character and do other things need done at birth.";
this.injury = this.insanity = this.fatigue = this.unconscious = 0;
"the total power reset is now done in :death_effects";
"this.total_power = 1;";
this.upper_mobility = this.lower_mobility = 100;
this:maybe_reset_stats();
.
#92:64
":death_effects(killer, weapon)";
"Exact the consequences of death on this character.  If the return value is an ERR, then the death does not take place!  Agh!";
if (!this:is_controllable_by(caller_perms(), caller))
return raise(E_PERM);
endif
{killer, @rest} = args;
"this.last_death_time = time();  <Quinn;19980922>";
this.last_killed_by = {killer, time(), is_player(this) ? callers(1) | {}};
leaked_power = this.total_power;
this.total_power = 1;
this.total_deaths = this.total_deaths + 1;
this.unconscious = 0;
this.queue = {};
try
killer.total_kills = killer.total_kills + 1;
killer.total_power = killer.total_power + leaked_power;
except (ANY)
endtry
return 0;
.
#92:65
"sleep";
"Drift into the sleepy dreamworld, accelerating certain types of healing.";
player:announce_messages("sleep");
player.unconscious = -1;
.
#92:66
"wake up";
"Return to consciousness after a voluntary sleep.";
unc = player:unconscious();
if (!unc)
player:notify("You aren't even unconscious.");
elseif ((unc < 0) || (player:resolve("willpower", 25 - unc) > 0))
player:wake_up();
this:announce_messages("wake_up");
else
player:notify("It isn't that easy, bub.  You'll have to hope someone tries to revive you.");
endif
"--Wizardly--";
.
#92:67
":disfunc(quit?)";
"Make sure the character doesn't disconnect while voluntarily asleep.";
if (!(caller in {this, #0}))
return E_PERM;
elseif (this.unconscious < 1)
this.unconscious = 0;
endif
return pass(@args);
.
#92:68
":asterisk()";
"Use ANSI colour to display various states of injury.";
return "";
"Temporarily disabled as I fix ANSI.";
if (!player.ANSI)
return "";
endif
EMU = $emu;
inj = this.injury;
deg = inj / (this:stat_endurance() / 3);
if (!inj)
if (this.wielding)
pre = EMU:tokenize("green");
else
return "";
endif
elseif (deg < 1)
pre = EMU:tokenize("bold", "yellow");
elseif (deg < 3)
pre = EMU:tokenize("yellow");
elseif (deg < 7)
pre = EMU:tokenize("red");
elseif (deg < 10)
pre = EMU:tokenize("flashing", "red");
elseif (deg < 15)
pre = EMU:tokenize("flashing", "magenta");
else
pre = EMU:tokenize("flashing", "blue");
endif
return tostr(pre, "*", EMU:tokenize("normal"));
.
#92:69
":crystal_worth()";
"Crystals are a function of a creature's life force, not a hardcoded reward for a difficult kill.  The .crystal_worth property value should never be above fifty.  NEVER.";
"Total_Power ('earned' life force) can give extraordinarily high crystal yields, even for creatures (such as robots) who would normally give none.";
"Do not bypass the 40-50 crystal limit in this verb.  It is a crime against game balance and will be punished if discovered.";
if (!`player.ic ! E_PROPNF')
return 0;
endif
if (typeof(c = `this.crystal_worth ! E_PROPNF') == NUM)
amt = c;
else
amt = max((this:stat_endurance() + this:stat_willpower()) / 20, 2);
amt = (amt / 2) + random(amt / 2);
endif
if (this.total_power > amt)
return this.total_power;
else
return min(amt, 50 - random(10));
endif
.
#92:70
":death_pending()";
"Is a fork queued to spawn our :death process?";
return $code_utils:task_valid(this.dt);
.
#92:71
":maybe_queue_action(what, action, args, slowness)";
"Check if the person is involved in a situation that would require the action to be queued.  If so, then queue it.  Else, just run the :do_foo right here.";
"In simpler terms: If combat is not already in effect, don't start one.";
if (this:in_combat())
set_task_perms(caller_perms());
return this:queue_action(@args);
endif
set_task_perms($gm);
spec = args[2];
if (typeof(spec) == LIST)
return spec[1]:("do_" + spec[2])(@args[3]);
else
return this:("do_" + spec)(@args[3]);
endif
.
#92:72
":receive_fatigue(NUM)";
"Apply the given amount of fatigue to this character.";
new = args[1];
this.fatigue = this.fatigue + new;
if (this:should_suffer_fatigue(new))
fork (0)
this:suffer_fatigue(new);
endfork
endif
.
#92:73
":receive_insanity(NUM)";
"Apply the given amount of emotional stress to this character.";
new = args[1];
this.insanity = this.insanity + new;
if (this:should_suffer_insanity(new))
fork (0)
this:suffer_insanity(new);
endfork
endif
.
#92:74
":wake_up([brief])";
"Make this character conscious.  No security checks; assume all programmers are trusted.";
"In addition to normal wake-up procedures, check if the character is being carried, and if so--move em to the room.";
player = this;
this.unconscious = 0;
if ($rpg:is_char(dobj = this.location))
dobj:eject_character(this, "wake_up");
else
this:room():look_self();
endif
"--WiZARDLY-- To change player.";
.
#92:75
"queue action [args]";
"Place the action as next in your queue.  This is the same as typing the action and its arguments alone.";
"rush action [args]";
"Rush the given action to the top of your queue, performing it on your next turn.";
parsed = $string_utils:first_word(argstr);
cmd = parsed ? $string_utils:trimr(parsed[1]) | "";
if (!$rpg:is_combat_action(cmd))
player:tell("Available actions: ", $string_utils:english_list($list_utils:map_arg($string_utils, "print", $rpg.actions)));
return E_INVARG;
endif
if (verb == "rush")
$rpg:add_rushed_task();
endif
argstr = parsed[2];
args = $string_utils:words(argstr);
dobjstr[1..length(cmd)] = "";
dobjstr = $string_utils:triml(dobjstr);
set_task_perms(valid(cp = caller_perms()) ? cp | player);
player:(cmd)(@args);
.
#92:76
":attempt_queue(@queue_args)";
"Attempt to queue an action described by the given args.  Return false if that action should not be allowed to be queued.  Such a case usually results when the character is 'busy', as when e is ambushing someone.";
"Messages should be printed if the queue is not to be allowed.";
if (a = this.ambushing)
this:tell("You are lying in ambush, waiting for ", $english:indef_art(a), " \"", a, "\", and thus cannot queue any actions.  To stop ambushing, type `stop ambushing`.");
elseif (d = this.defending)
this:tell("Your efforts are focused on defending ", $string_utils:name_list(d), "; use the `stop defending` command to abandon them.");
elseif (this:queue_length() >= (m = $rpg.max_queue_length))
this:tell("The system allows a maximum of ", m, " actions to be queued. You'll have to wait for them those you have (`actions`) to fire, or cancel some (`cancel <#>`).");
else
return 1;
endif
return 0;
.
#92:78
"ambush victim_name";
"Lie in waiting for the given victim name.  Each time someone enters the room, e is matched against the given string.  If the match succeeds, you attack.";
"Note that once you ambush someone, you will not be able to queue many actions.  To abort your ambush, type `stop ambushing`.";
if (!dobjstr)
player:tell("You must give the name of someone for whom you wish to lie in waiting.");
elseif ((valid(dobj = player:my_match_object(dobjstr)) && $rpg:is_char(dobj)) && (dobj:matches(dobjstr) > 0))
player:tell("There's already someone in here matching \"", dobjstr, "\": ", dobj:name(), ".  Try typing `attack ", dobjstr, "` instead.");
elseif (player.queue)
player:tell("You have some actions queued.  You'll have to type `cancel all` before being able to ambush.");
else
old = player.ambushing;
player.ambushing = dobjstr;
if (old)
player:tell("You stop waiting for \"", old, "\" and keen your senses towards \"", dobjstr, "\"--your new target.");
return;
endif
player:tell("You crouch down, senses keen and body poised to strike at any \"", dobjstr, "\" that might enter the room.  [Type `stop ambushing` to stop waiting for your prey.]");
player:room_announce($string_utils:pronoun_sub("%N %<crouches> down, senses keen and body poised to strike."));
endif
.
#92:79
"Check if we are waiting to ambush the entering object.";
if (((a = this.ambushing) && $rpg:is_char(w = args[1])) && w:matches(a))
this.ambushing = 0;
this:add_action(tostr("attack ", w:name()), "attack", {w, 0}, 0);
else
return pass(@args);
endif
.
#92:80
":in_combat()";
"Return true if the given character is involved in structured combat.";
room = this:room();
task = $code_utils:get_prop(room, "combat_task") || 0;
comb = $code_utils:call_verb(room, "combatants");
return $code_utils:task_valid(task) && (comb != {this});
.
#92:81
":add_defender(OBJ defender)";
":remove_defender(OBJ defender)";
"Update defenders list on this character, and defending list on the defender.";
c = args[1];
d = this.defenders;
if (verb[1] == "A")
this.defenders = setadd(d, c);
c.defending = setadd(c.defending, this);
else
this.defenders = setremove(d, c);
c.defending = setremove(c.defending, this);
endif
.
#92:82
"guard character";
"Defend the given character in combat.  You will become the target of anyone attacking the character, IF the attacks occur after your place in combat.";
"If your defense results in a parry, the guarded character is spared the attack.  Otherwise, e must defend eirself.";
if (callers() && (!this:is_controllable_by(caller_perms(), caller)))
return E_PERM;
elseif (!args)
player:tell("You must give the name of a character to ", verb, ".");
return;
endif
where = this:room();
who = $match_utils:match_environment(dobjstr, where:contents());
if (who == $failed_match)
player:tell("You don't see any \"", dobjstr, "\" here to defend.");
elseif (who == $ambiguous_match)
all = $match_utils:match_list(dobjstr, where:contents());
player:tell("You'll have to choose between ", $string_utils:name_list(all), ".");
elseif (((!$object_utils:isa(who, $combatant)) && $object_utils:has_callable_verb(who, verb)) && (who:(verb)(@args) != E_VERBNF))
"Object is a non-combatant with a guard/defend verb defined.";
elseif (who == player)
return this:queue_action("dodge everyone", "dodge", {}, 20 - this:quickness());
else
this:queue_action("guard " + who:name(), "guard", {who}, 20 - this:quickness());
endif
.
#92:83
":do_guard(char)";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
this:start_defending(@args);
.
#92:84
":clear_corpse()";
"Allow the corpse itself and any writer of this object to clear the corpse.";
if ((caller != this.corpse) && (!this:is_writable_by(caller_perms(), caller)))
return E_PERM;
endif
clear_property(this, "corpse");
.
#92:85
":possible_targets()";
e = {};
RPG = $rpg;
loc = this:room();
if (0)
"970405-1803: Maybe add an RPG:is_attackable()";
for c in (loc:env())
if (RPG:is_character(c))
e = {@e, c};
endif
endfor
else
e = loc:env();
endif
for x in (loc:exits())
if (typeof(l = x:blocked_by(this)) == LIST)
e = {@e, @l};
endif
endfor
return e;
.
#92:86
":my_match_target(str)";
"{1, {targ}}          -> Success.";
"{1, {targ, area}}    -> Success and body area for called shot.";
"{0, {targ, area_str}}    -> Found target, but not body area.";
"{0, {str}}               -> Failure.";
"{0, {str, {@ambigs}}     -> Failure due to given ambiguous matches.";
s = args[1];
p = $match_utils:parse_possessive_ref(s);
if (p)
s = p[1];
a = p[2];
else
a = "";
endif
e = this:possible_targets();
m = this:my_match_object(s, e);
if (m == $ambiguous_match)
m = $match_utils:match_list(s, e);
if (!m)
return {0, {s}};
elseif (this:get_option($combat_options, "first_ord"))
m = m[1];
else
return {0, {s, m}};
endif
elseif (!valid(m))
return {0, {s}};
endif
if (!a)
return {1, {m}};
elseif (!valid(b = `m:match_body_area(a, 1) ! ANY => #-1'))
return {0, {m, a}};
else
return {1, {m, b}};
endif
.
#92:87
":target_match_failed(result)";
"If 'result' is not given, it's this:my_match_target(str).";
r = args[1];
if (r[1])
return 0;
endif
r = r[2];
s = r[1];
if (typeof(s) == OBJ)
this:tell("You can't find any \"", r[2], "\" on ", s:dname(), " on which to call a shot.");
else
r = listdelete(r, 1);
if (!r)
this:tell("You find nothing nearby matching \"", s, "\".");
else
r = r[1];
if (length($list_utils:remove_duplicates($list_utils:map_prop(r, "name"))) == 1)
this:tell("There are ", length(r), " nearby targets with that name.  Try an ordinal match (\"first ", s, "\", \"second ", s, "\", etc), or do `@options combat +first_ord` to always choose the first match.");
else
this:tell("There is more than one target nearby that might match \"", s, "\".  Be more specific as to which you mean: ", $string_utils:name_list(r, "", " or "), ".");
endif
endif
endif
return 1;
.
#92:88
":my_match_wielding(str)";
s = args[1];
if (!s)
return this:current_weapon();
endif
nats = this.natural_weapon;
weps = this.wielding;
if (typeof(nats) == LIST)
wielding = {@weps, @nats};
else
wielding = {@weps, nats};
endif
return this:my_match_object(s, wielding);
.
#92:89
":wielding_match_failed(obj, str)";
w = args[1];
if (valid(w))
return 0;
endif
s = args[2];
if (w != $ambiguous_match)
player:tell("You aren't wielding any \"", s, "\".");
else
player:tell("You'll have to be more specific about which weapon you would like to wield.");
endif
return 1;
.
#92:90
":tell_equipped()";
dobj = this;
if (eq = dobj:wielding())
player:tell($string_utils:pronoun_sub(("%N %<is> wielding " + $string_utils:ititle_list(eq)) + ".", (player == dobj) ? $you | dobj));
elseif (this:get_option($combat_options, "hide_nat"))
player:tell($string_utils:pronoun_sub("%N %<isn't/aren't> wielding anything.", (player == dobj) ? $you | dobj));
else
player:tell($string_utils:pronoun_sub("%N %<isn't/aren't> wielding anything, armed only with %p %[ttitle].", (player == dobj) ? $you | dobj, this:current_weapon()));
endif
.
#92:91
":is_possible_target(attacker)";
"Return true if this character is a viable target for the given attacker, or return false if not, or a string for a reason why not suitable for $you:say_action with attacker as player and this as dobj.";
attacker = args[1];
return (attacker.location == this.location) || (this in attacker:possible_targets());
.
#92:92
":queue_length()";
"Return the total number of actions in this combatant's queue.";
return length(this.queue);
.
#92:93
":armour_worn()";
w = {};
for c in (this.clothing)
w = $set_utils:union(w, c);
endfor
return w;
.
#92:94
":resolve_null_target()";
"Find the target for a blank 'kill', 'shoot', etc command.  Print messages to this if no target is found.";
a = {@this:attacking(), this.attacked_by[1]};
if (!a)
this:tell("You aren't engaged in combat.  You'll have to specify a target.");
return $nothing;
endif
e = this:environment();
while (a)
if (a[1] in e)
return a[1];
endif
a = listdelete(a, 1);
endwhile
this:tell("All your targets seem to have fled the area.  You'll have to specify a new one.");
return $nothing;
.
#92:95
":do_metabolism()";
"Update damage conditions, drug effects, etcetera.  Should be called";
" once per :pulse.";
this:update_damage_monitors();
this:update_bleeding();
.
#92:96
":update_damage_monitors()";
"Update this character's physical condition.";
if ((caller != this) && (!$rpg:trusted(caller_perms())))
return raise(E_PERM);
endif
"Total power affects regaining consciousness and healing by itself";
" divided by 200k, up to a maximum of 50%.  Ie. 100,000 power or more";
" means a 50% heal/unc bonus per pulse.  It is applied AFTER natural";
" healing takes place.";
"Subject to change if unbalancing.  Right now the highest total_power";
" is 31k.  <Quinn;19980816>";
pow_pct = min(tofloat(this.total_power) / 200000.0, 0.5);
end = this:stat_endurance();
unc = this.unconscious;
if (unc > 0)
unc = this.unconscious = max(unc - (end / 20), 0);
unc = this.unconscious = max(unc - toint(tofloat(unc) * pow_pct), 0);
if (!this.unconscious)
this:wake_up(1);
$you:say_action("%N %<regains> consciousness.", this);
endif
"why does this return?  let them heal while KO'd.  <Quinn;19980816>";
"this.fatigue = max(this.fatigue - 5, 0);";
"return pass(@args);";
endif
stg = this:stat_strength();
wil = this:stat_willpower();
data = {{"fatigue", ((end + stg) + wil) / 25, 4}, {"insanity", (wil / 15) + 1, 2.5}, {"injury", (end / 20) + 1, 1.5}};
for set in (data)
{statname, rate, uncmod} = set;
current = this.(statname);
if (!current)
continue;
endif
if (unc)
rate = toint(tofloat(rate) * uncmod);
endif
current = this.(statname) = max(current - rate, 0);
pow_bonus = toint(tofloat(current) * pow_pct);
current = this.(statname) = max(current - pow_bonus, 0);
endfor
.
#92:97
":do_behave()";
"Not if you're unconscious, you don't!";
return (!this:unconscious()) && pass(@args);
.
#92:98
":unarmed_msg()";
iobj = this:current_weapon();
return this:grammar_sub(this.unarmed_msg);
.
#92:99
"forgive";
"List all those from whom you should be seeking forgiveness.";
"forgive <character>";
"Forgive someone for killing you.";
if (caller != this)
$error:raise(E_PERM);
endif
RPG = $rpg;
"-----validate the pk list-----";
FORGIVE_FORGET_TIME = time() - $rpg.forgive_forget_time;
pk = removed = {};
for p in (this.pk)
if (p == this)
"suidice is a guiltless crime";
elseif (!`is_player(p) ! ANY')
"not a player";
elseif (p.last_disconnect_time < FORGIVE_FORGET_TIME)
"dead?  near-reaping?";
removed = setadd(removed, p);
else
pk = {@pk, p};
endif
endfor
if (removed)
player:notify(tostr("The spectral faces of ", $string_utils:iname_list($list_utils:remove_duplicates(removed)), " cast a final damning glace at you before fading entirely from your conscience."));
endif
this.pk = pk;
"-----on with our regularly scheduled program-----";
if (!dobjstr)
if (this.pk)
player:notify(tostr("You feel the need to seek the forgiveness of ", $string_utils:iname_list($list_utils:remove_duplicates(this.pk)), "."));
else
player:notify("Your conscience is clear.");
endif
return;
endif
dobj = $match_utils:match_player(dobjstr);
if ($match_utils:object_match_failed(dobj, dobjstr))
return;
endif
pk = dobj.pk;
if (player == dobj)
gag = {"Cheer up.", "Don't be so down on yourself.", "You've nothing of which to be ashamed.  And your hair looks great!", "Hey, now!  You're a nice, smart, attractive individual.  Enough of that stinkin' thinkin'!", "You feel as if all self-doubt and angst has been lifted from your soul.", "What is this, a Woody Allen movie?"};
player:notify($list_utils:random_element(gag));
elseif (player in pk)
dobj.pk = $list_utils:setremove_all(dobj.pk, player);
dobj:tell(tostr("A chronic ache of guilt over your actions towards ", player:iname(), " is suddenly lifted.  You have been forgiven."));
player:notify(tostr("You forgive ", dobj:dname(), "."));
else
player:notify(dobj:grammar_sub("%(dnamec) %<d:has> done nothing against you."));
endif
.
#92:100
if (this:unconscious())
if (random(10) == 1)
this:notify("You hear the distant voices of those in the waking world.");
endif
return;
endif
return pass(@args);
.
#92:101
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
if (this != $combatant)
for p in (properties($combatant))
`clear_property(this, p) ! ANY';
endfor
endif
this.natural_weapon = this.current_weapon = $fists;
this.blood = $blood;
this.afterlife = #-1;
.
#92:102
":all_attackers()";
"Return a list of everyone attacking this character.";
all = {};
for c in (`this.location:combatants() ! E_VERBNF => {}')
if (`c:attacking(this) ! E_VERBNF')
all = {@all, c};
endif
endfor
return all;
.
#92:103
":multiple_attacker_penalty()";
"Return a penalty based on how many are attacking this character.";
attackers = this:all_attackers();
count = max(length(attackers) - 1, 0);
if (count)
0 && `#2:_dnotify(tostr(">>> ", count, " attackers for ", this.name, " <", this, ">")) ! ANY';
endif
return count * 10;
.
#92:104
return pass(@args) - this:multiple_attacker_penalty();
.
#92:105
":should_be_moody()";
"Return true if this character should have random `moods' announced.";
return this.ic && (!this:unconscious());
.
#92:106
"See :should_suffer_fatigue, but substitute 'fatigue' with 'insanity'.";
return 0;
.
#92:107
":should_suffer_fatigue(INT new_amount)";
"True if :suffer_fatigue should be invoked. Called as :suffer_fatigue, the subject does various things if tired beyond a certain point.  Usually called from :receive_fatigue, `new_amount' is the amount of fatigue received in that call.";
"As of this time, only $players will suffer any fatigue effects.  Folded into a single verb so the code that acts is in the same place as the code that determines whether or not we should act.";
return 0;
.
#92:108
"Copied from generic combatant (#314):do_attack by GameMaster (#1991) Sat Aug 29 09:15:38 1998 CDT";
":do_attack(who, nice[, body_area])";
"who           -- Target.";
"nice          -- Negative expression of lethality.";
"body_area     -- Area aimed for.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
{dobj, nice, ?area = $nothing} = args;
ipt = `dobj:is_possible_target(this) ! E_VERBNF => 0';
if (!ipt)
$you:say_action("Breath ragged and braced for combat, %n %<scans> the area for %[ddname].  %S %<relaxes/relax> %p stance, realizing %[dps] %<d:is> no longer a threat.");
return E_NONE;
elseif (typeof(ipt) == STR)
$you:say_action(ipt);
return E_NONE;
elseif (dobj:death_pending())
$you:say_action("%N %<beats> senselessly at the bloody ground, cursing the lifeless shadow of %[ddname,former].");
return E_MAXREC;
endif
where = this.location;
this:add_attacker(dobj);
weapon = this:current_weapon();
this.nice = nice;
bonus = (this.result / 10) + weapon:attack_mod(this, dobj);
if (valid(area))
bonus = bonus + area:called_shot_mod(dobj, this, weapon);
else
area = weapon:random_hit_location(this, dobj);
endif
if (!weapon:attempt_attack(this, dobj, area))
return;
endif
if (unc = dobj:unconscious())
bonus = bonus + 100;
endif
this.result = attack = this:resolve(weapon:skill(this, dobj), bonus);
weapon:announce_swing(this, dobj, area);
if (attack < -25)
if (!weapon:critical_miss(this, dobj, area, attack))
"...weapon critical miss handled everything...";
weapon:announce_miss(this, dobj, area);
endif
weapon:finished_attack(this, dobj, area);
return where:broadcast_event_attack(this, dobj, area, attack);
elseif (dobj:do_defense(this, area))
weapon:finished_attack(this, dobj, area);
return where:broadcast_event_attack(this, dobj, area, attack);
endif
if (weapon:critical_hit(this, dobj, area, attack))
"...weapon critical hit handled everything...";
weapon:finished_attack(this, dobj, area);
return where:broadcast_event_attack(this, dobj, area, attack);
endif
dmg = weapon:damage(this, dobj);
if (unc)
dmg = dmg * 3;
endif
level = dobj:receive_damage(dmg, weapon:lethality(this, dobj), this, area);
if (level == $maxint)
weapon:announce_kill(this, dobj, area, level);
elseif (level == $minint)
weapon:announce_knockout(this, dobj, area, level);
elseif (level)
weapon:announce_hit(this, dobj, area, level);
else
"...no damage, messages printed...";
endif
weapon:finished_attack(this, dobj, area);
where:broadcast_event_attack(this, dobj, area, attack);
.
#92:109
"Copied from generic combatant (#314):do_defense by GameMaster (#1991) Sat Aug 29 09:15:54 1998 CDT";
":do_defense(attacker[, body_area])";
"Handle defensive action against an attack by the given character to the given body area.";
"First checked is whether we dodged the attack.  A dodge is rare.  If the dodge roll succeeds, but is not enough to completely dodge, parrying is checked for each weapon wielded.";
"Return -1 if the attack was dodged, 1 if it was parried.";
"If :get_defender returns a list of defenders, then the :do_parry of each of those characters is called in turn, and the body of THIS :do_parry is called only if all of them dodge.  (Currently only ONE defender is returned per attack.)";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
elseif (this:unconscious())
return 0;
endif
who = args[1];
"--- defender code below, might be buggy ---";
for d in (this:get_defender())
r = d:do_parry(who);
if (r > -1)
return r;
endif
endfor
"--- end defender code ---";
against = who:current_weapon();
this.last_attacked_by = {who, time()};
area = (length(args) > 1) ? args[2] | against:random_body_area(this);
attack = who:result();
dodge = this:resolve("dodge", this:defense_bonus(who) + against:dodge_mod());
if (dodge > attack)
if (!this:announce_dodge(who, this, area))
against:announce_dodge(who, this, area);
endif
return -1;
elseif (dodge < 0)
return 0;
endif
penalty = against:parry_against(who, this) - attack;
for weapon in (setadd(this:wielding(), this:current_weapon()))
if (weapon:attempt_parry(this, who, area))
mod = weapon:parry_with(this, who);
resolve = this:resolve(weapon:skill(this, who), mod + penalty);
if (resolve > 0)
weapon:announce_parry(this, who, area, against);
return 1;
endif
weapon:finished_parry(this, who, area);
endif
endfor
return 0;
.
#92:110
":attempt_guard(victim, attacker[, area])";
"Attempt to guard an attack on victim's area by attacker.  Return true on success, false for simple failure, E_RANGE if the attacker is out of range, and E_MAXREC if the attacker is the defender.  For error conditions, the calling verb should remove this character from the list of defenders.";
{victim, attacker, ?area = $nothing} = args;
if (attacker == this)
return E_MAXREC;
elseif (!attacker:is_possible_target(this))
return E_RANGE;
endif
return this:attempt_parry(attacker, area) ? 1 | 0;
.
#92:111
":attempt_parry(attacker[, area])";
"Final line of a victim's defense in the combat sequence.  Return true if success.";
{who, ?area = $nothing} = args;
against = who:current_weapon();
if (!valid(area))
area = against:random_body_area(this);
endif
attack = who:result();
for weapon in (setadd(this:wielding(), this:current_weapon()))
if (!weapon:attempt_parry(this, who, area))
continue;
endif
"compute the penalty for parrying against the attacker's weapon";
parry_against = against:parry_against(who, this, weapon, attack);
"compute the target number for the attack to succeed";
if (attack <= 0)
parry_beat = 0;
else
"parry_beat = toint(tofloat(attack) * 1.25);";
parry_beat = attack;
endif
"compute the bonus for parrying with the current weapon";
parry_with = weapon:parry_with(this, who, against);
resolve = this:resolve(weapon:skill(this, who), parry_against + parry_with);
0 && #2:_dnotify(tostr(">>> ", this.name, "/", weapon.name, "(with=", parry_with, ") [resolve=", resolve, "] vs. ", who.name, "/", against.name, "(against=", parry_against, ") [beat=", parry_beat, "]; ", weapon.name, (resolve > parry_beat) ? " PARRIED " | " failed to parry ", against.name, ";"));
if (resolve > parry_beat)
weapon:announce_parry(this, who, area, against);
endif
weapon:finished_parry(this, who, area);
if (resolve > parry_beat)
return 1;
endif
endfor
return 0;
.
#92:112
":attempt_dodge(attacker[, area])";
"After guards have a go at parrying the attack, the victim gets a chance to dodge it.  Return greater than zero if the dodge was successful, negative if it failed but a parry attempt can still be made, and zero if it failed completely and no further defense can be attempted.";
{who, ?area = $nothing} = args;
against = who:current_weapon();
if (!valid(area))
area = against:random_body_area(this);
endif
attack = who:result();
"Retrieve and parse dodge modifier from the attacking weapon.";
dodge_mod = against:dodge_mod(who, this);
"Roll the dodge resolution with defense bonus and the dodge_mod calculated above.";
dodge = this:resolve($skill_db.dodge, this:defense_bonus(who) + dodge_mod);
0 && #2:_dnotify(tostr(">>> ", $string_utils:nn(this), " dodging ", $string_utils:nn(who), " with a ", $string_utils:nn(against), " => ", dodge));
if (dodge > attack)
"allow for a possible announce_dodge on this creature";
"should probably do a :has_callable_verb instead of trapping E_VERBNF";
if (!`this:announce_dodge(who, this, area) ! E_VERBNF')
against:announce_dodge(who, this, area);
endif
return 1;
elseif (dodge > 0)
return -1;
else
return 0;
endif
.
#92:113
":start_defending(OBJ)";
if (!this:is_controllable_by(caller_perms(), caller))
return raise(E_PERM);
endif
{dobj} = args;
dobj:add_defender(this);
$you:say_action("%N %<rushes> to %d's side, valiantly defending %[dpo].", this);
.
#92:114
":start_defending(OBJ)";
if (!this:is_controllable_by(caller_perms(), caller))
return raise(E_PERM);
endif
{dobj} = args;
dobj:remove_defender(this);
$you:say_action("%N %<stops> defending %d.", this);
.
#92:115
":melee_damage_bonus(weapon, target)";
"Return the amount of extra damage inflicted by this creature when effectively wielding the given weapon against the given target.";
"By default, the strength bonus min/maxed to a range of 1-50, then adjusted by size.  It is not recommended that this be raised.";
{weapon, target} = args;
raw_base = this:total("strength");
"raise the max to 100 once we're sure no natural weapons are already inflating damage";
sane_base = max(min(raw_base, 25), 1);
"adjust proportional to size differences";
size_adjusted = toint(tofloat(sane_base) * (tofloat(this.v_size) / tofloat(target.v_size)));
return size_adjusted;
.
#92:116
":is_mortally_wounded()";
"Return true if the cumulative damage taken is enough to be fatal.";
return this.injury > (this:stat_endurance() * 20);
.
#92:117
":update_bleeding()";
if (caller != this)
return raise(E_PERM);
elseif ((bleeding = this.bleeding) <= 0)
return;
endif
if ((!this.injury) || (this.unconscious > 0))
"if no longer injured, add some to get us started";
"passed out, possibly from blood loss.  bleeding is now added as injury";
this:receive_injury(this.bleeding);
else
"while the character is conscious, bleeding is added as fatigue";
this:receive_fatigue(this.bleeding);
endif
this:tell("You are bleeding.");
if (valid(this.blood))
new = this.blood:spawn(this.bleeding);
if ((typeof(new) == OBJ) && valid(new))
new:moveto(this.location);
if (new.location == this.location)
"MAYBE PRINT SOME BLEEDING MESSAGE HERE";
else
new:moveto(this);
endif
endif
endif
.
#93:0
":skill(wielder, target)";
"=> OBJ describing skill controlling the use of this weapon.";
":slowness(wielder, target)";
"=> NUM relative slowness of use.";
":lethality(wielder, target)";
"=> INT percentage chance of this weapon delivering a kill rather than a knockout.";
":penetration(wielder, target)";
"=> INT percentage matched against an armour's deflection rating to determine if that armour deflected the attack, avoiding all damage for the wearer";
return this.(verb);
.
#93:1
":announce_swing/miss/dodge(attacker, defender, location)";
":announce_parry(attacker, defender, location, opposing_weapon)";
":announce_hit/knockout/kill(attacker, defender, location, severity)";
"Announce combat messages.  Default behaviour is to pass to the weapon's message set.";
return this:combat_messages(@args):(verb)(this, @args);
.
#93:2
":random_hit_location(attacker, target) -> A body area object.";
"Returns a random body area of the target.  By default, much greater chance to hit higher areas of the body.  Possible future hacks to take into account size difference of combatants.";
return args[2]:random_body_area();
.
#93:3
":critical_miss(attacker, target, hit-location, attack-result)";
"Possibly do some nasty fumbling stuff.  Return true if this verb has handled all messages and miss results.";
"Cosmetic fumbles should usually happen only if the attack-result is less than -25.  Backfires and really bad mistakes should be reserved for much worse rolls.";
return 0;
.
#93:4
":critical_hit(attacker, target, hit-location, attack-result)";
"Possibly do some nasty maiming and incapacitation, etcetera.  Return true if this verb has handled all messages and damage application.";
return 0;
.
#93:5
":announce_parry(attacker, defender, location, opposing_weapon)";
"Announce combat messages.  Default behaviour is to pass to the weapon's message set.";
return this:combat_messages(@args):(verb)(args[4], @listset(args, this, 4));
.
#93:6
":attempt_attack/parry(wielder, target[, area])";
"Signal the weapon that wielder is attempting to use it in the given capacity.";
"Return false if the use will not succeed, and print any necessary messages.";
"For example, an unloaded missile weapon would check ammo, informing the wielder if the weapon is empty.  Ammo should not, however, be drained via this method.  See :finished_attack/parry.";
return 1;
.
#93:7
":finished_attack(attacker, defender[, area])";
"Signal the weapon that attacker has used it.  Called after everything but the attack event broadcast.  That is, when the attack is finished.";
"Useful for depleting ammo/energy, perhaps breakage rolls.  May print any necessary messages.  Return value discarded for the time being.";
wielder = args[1];
exerted = this:exertion(@args);
if (exerted)
wielder:receive_fatigue(exerted);
endif
.
#93:8
":combat_messages()";
"-> The message set to be used by this weapon.";
return this.combat_messages;
.
#93:9
":damage(wielder, target)";
"=> NUM raw damage applied to target when hit.  A strength bonus is added to the raw property value.";
{wielder, target} = args;
return this.damage + this:melee_damage_bonus(wielder, target);
.
#93:10
":exertion(wielder, target)";
"Fatigue incurred by the average use of this weapon.  Fatigue applied is a function of the wielder's strength.  See $combatant:suffer_fatigue for how endurance affects fatigue.";
wielder = args[1];
strength = wielder:stat_strength();
mod = 1.0 / (tofloat(strength) / 50.0);
return toint(tofloat(this.exertion) * mod);
.
#93:11
player:tell(this:name());
player:tell("---------------------:");
O = $rpg;
V = "build_stat_line";
player:tell(O:(V)(this, "Attack Bonus", "attack_mod", this.attack_mod, 20, 16));
player:tell(O:(V)(this, "Dodge Bonus", "dodge_mod", this.dodge_mod, 20, 16));
player:tell(O:(V)(this, "Parry-With Bonus", "parry_with", this.parry_with, 20, 16));
player:tell(O:(V)(this, "Parry-Against Bonus", "parry_against", this.parry_against, 20, 16));
player:tell(O:(V)(this, "DAMAGE", "damage", this.damage, 20, 16));
player:tell(O:(V)(this, "Penetration", "penetration", this.penetration, 20, 16));
player:tell(O:(V)(this, "Lethality", "lethality", this.lethality, 20, 16));
player:tell(O:(V)(this, "Slowness", "slowness", this.slowness, 20, 16));
player:tell(O:(V)(this, "Exertion", "exertion", this.exertion, 20, 16));
player:tell(O:(V)(this, "Skill", "skill", (typeof(s = this.skill) == STR) ? ("\"" + $string_utils:capitalize(s)) + "\"" | tostr(s, " (", s:name(), ")"), 20, 16));
player:tell(O:(V)(this, "Message Set", "combat_messages", tostr(s = this.combat_messages, " (", s:name(), ")"), 20, 16));
.
#93:12
":set_stat(stat, value)";
"Set the value of the given RPG statistic.";
cp = caller_perms();
ok = this:is_unique() ? this:is_writable_by(cp, caller) | $rpg:trusted(cp);
if (!ok)
$error:raise(E_PERM, "Caller was denied write access to this weapon.");
endif
{stat, value} = args;
return this.(stat) = value;
.
#93:13
":mod_stat(stat, change[, min[, max]])";
"Add the given value to the given RPG statistic.";
cp = caller_perms();
ok = this:is_unique() ? this:is_writable_by(cp, caller) | $rpg:trusted(cp);
if (!ok)
$error:raise(E_PERM, "Caller was denied write access to this weapon.");
elseif (typeof(mod = args[2]) != NUM)
return E_TYPE;
endif
{stat, mod, ?min_rank = 0, ?max_rank = $maxint} = args;
now_rank = this.(stat);
new_rank = now_rank + mod;
return this:set_stat(stat, max(min(new_rank, max_rank), min_rank));
.
#93:14
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.skill = $skill_db:match("melee");
this.combat_messages = $combat_message_set;
.
#93:15
"this will include it, but it has to be found by :init_for_core";
return {this.combat_messages};
.
#93:16
":finished_parry(defender, attacker[, area])";
"Signal the weapon that defender has parried with it.  Called immediately after the skill resolve and :announce_parry.";
"May print any necessary messages.  Return value discarded for the time being.";
return 0;
.
#93:17
":attack_mod(attacker, target)";
"INT modifier when attacking with this weapon";
"FLOAT percentage effectiveness of attacker's skill";
"  (min 0.0, negates any skill bonus; 1.0 == 100%, no mod; 2.0 double effectiveness)";
"=> final additive modifier";
{wielder, target, @rest} = args;
mod = this.(verb);
if (typeof(mod) != FLOAT)
return mod;
endif
total = wielder:total(this:skill(wielder, target));
if (total < 0)
return 0;
endif
mod = max(mod, 0.0);
mod = toint(tofloat(total) * mod) - total;
return mod;
.
#93:18
":dodge_against(wielder, target)";
"INT modifier when dodging this weapon";
"FLOAT percentage effectiveness of target's dodge skill";
"  (min 0.0, negates any skill bonus; 1.0 == 100%, no mod; 2.0 double effectiveness)";
"=> final additive modifier";
{wielder, target, @rest} = args;
mod = this.(verb);
if (typeof(mod) != FLOAT)
return mod;
endif
total = target:total($skill_db.dodge);
if (total < 0)
return 0;
endif
mod = max(mod, 0.0);
mod = toint(tofloat(total) * mod) - total;
return mod;
.
#93:19
":parry_with(parry-er, attacker, attacker_weapon)";
"INT modifier when parrying opposing_weapon with this weapon.";
"FLOAT percentage effectiveness of the weapon skill of the one parrying with this weapon";
"  (min 0.0, negates any skill bonus; 1.0 == 100%, no mod; 2.0 double effectiveness)";
"=> final additive modifier";
{wielder, target, against, @rest} = args;
mod = this.(verb);
if (typeof(mod) != FLOAT)
return mod;
endif
total = wielder:total(this:skill(wielder, target));
if (total < 0)
return 0;
endif
mod = max(mod, 0.0);
mod = toint(tofloat(total) * mod) - total;
return mod;
.
#93:20
":parry_against(attacker, defender, defender_weapon, attack_roll)";
"INT modifier when parrying against this weapon with defender_weapon";
"FLOAT percentage of the attacker's attack roll (negafied)";
"  (min 0.0, no penalty; 1.0 == penalty equal to attack roll; 2.0 double attack roll)";
{wielder, target, ?against = target:current_weapon(), ?attack = attacker.result} = args;
mod = this.(verb);
if (typeof(mod) != FLOAT)
return mod;
endif
"return a bonus of the negafied result if <= zero";
if (attack <= 0)
return -attack;
endif
mod = max(mod, 0.0);
mod = toint(tofloat(attack) * mod);
return -mod;
.
#93:21
":melee_damage_bonus(wielder, target)";
"Pass to the wielder to retrieve their bonus.  This should be disabled for thrown weapons, ammo, or any other item which should not convey damages based on raw strength or size differences between combatants.";
{wielder, target} = args;
return wielder:melee_damage_bonus(this, target);
.
#94:0
"Is the weapon wielded?";
return (this in this.location:wielding()) ? {this.location} | 0;
.
#94:1
"Show `wielded_msg' when wielded, and `sheathed_msg' when not.";
return $string_utils:pronoun_sub(this:wielded() ? this.wielded_msg | this.sheathed_msg, this.location);
.
#94:2
":hands_required(who) -- How many hands needed to wield this thing?";
"Hack to make weaker people use two hands, perhaps?";
return this.hands_required;
.
#94:3
"dub this as <title>";
"Add the alias 'title' to this weapon, allowing the player to easily refer to multiple weapons of the same name.";
who = callers() ? caller_perms() | player;
if (this.location != who)
who:tell("Wouldn't it be more dramatic if you were holding ", this:dname(), "?");
elseif (!this:is_writable_by(who))
"960426: forbade non-owners to dub weapons...";
who:tell("Its composition is impervious to your vandal scratchings.");
elseif (iobjstr)
this.title_msg = iobjstr;
aliases = $set_utils:union(parent(this):spawned_aliases(), {iobjstr, this.name});
this:set_aliases(aliases);
who:tell(this:dnamec(), " now bears the name \"", iobjstr, "\"");
else
clear_property(this, "title_msg");
who:tell("You scratch off the title of ", this:dname(), ".");
endif
.
#94:4
if (title = this.title_msg)
return ((this:name() + " named \"") + title) + "\"";
else
return pass(@args) || "";
endif
.
#94:5
"Remind everyone that the weapon is wielded, if it is and the user has no actions queued.";
wielder = this:wielded();
if (!wielder)
return pass(@args);
endif
wielder = wielder[1];
if (wielder:unconscious() || (is_player(wielder) && (!$object_utils:connected(wielder))))
return pass(@args);
elseif (!wielder.attacking)
$you:say_action(this.omood_idle_msg, wielder);
return 1;
else
return 0;
endif
.
#94:6
":attempt_wield(char)";
"Character is attempting to wield this weapon.  Return false and print messages describing why e can't, or return true.";
return 1;
.
#94:7
":attempt_unwield(char)";
"Character is attempting to unwield this weapon.  Return false and print messages describing why e can't, or return true.";
return 1;
.
#95:0
":att_bonus_for(who)";
"Total bonus given by who's attributes when using this skill.";
atts = this.attributes;
if (!atts)
return 0;
endif
who = args[1];
total = 0;
for att in (atts)
total = total + `who:("stat_" + att)() ! E_VERBNF => 0';
endfor
total = total / length(atts);
return $rpg:att_bonus(total);
.
#95:1
":unskilled() -> Penalty for untrained use of this skill.";
return this.unskilled;
.
#95:2
":total(who)";
"The inherited skill bonus.";
who = args[1];
base = this:total_skill_bonus_for(who) + this:att_bonus_for(who);
return base;
.
#95:3
":resolve(OBJ pc, NUM mod[, ANY trivial])";
"Resolve a test of this skill for the given PC, with the given modifier.  If `trivial' is passed, the test will never result in an improved skill rank.";
"A higher mod makes the test easier, a lower more difficult.  If `mod' is a LIST, the first element is a simple integer modifier and the second is a percentage of the character's total skill.  For example: {0, 0.5} means no hard modifier but use only half of the player's actual skill when determining success or failure.";
{pc, ?mod = 0, ?trivial = 0} = args;
"Start with player's total skill.";
total = this:total_skill_bonus_for(pc);
if (typeof(mod) == LIST)
{intmod, ?pct = 1.0} = mod;
total = toint(tofloat(total) * max(pct, 0.0));
mod = intmod;
endif
"Split skill rank total into hard and soft bonuses.";
if (1)
hard = min(total, 100);
soft = max(total - hard, 0);
else
hard = toint(tofloat(total) * 0.8);
soft = max(total - hard, 0);
endif
"Add the player's current penalty to the modifier.";
pen = this:penalty(pc);
mod = mod + pen;
"Roll 'em!";
result = ((((-75 + random(50)) + mod) + hard) + (soft ? random(soft) | 0)) + this:att_bonus_for(pc);
if ((!trivial) && this:should_improve(pc, result, mod))
this:improve(pc, this.trickle_chance);
endif
return result;
"19980925: A maximum of 100 hard points of the skill total is used.  A random portion of the remainder is then applied.  This is good, but does not work with the twisted implementation of FLOAT weapon modifiers.  Will have to add an optional float (proportial to skill) modifier arg for the resolve call.";
"19990112: Re-activated the 100 max hard points, replaced pot with trickle_chance as second arg to :improve.";
"19990114: Att bonus added _after_ hard 100 is capped.";
.
#95:4
":improve(OBJ char, NUM trickle_chance)";
"If trickle_chance > d100, increment the given character's rank in this skill by random(this.pot).";
if ((caller != this) && (parent(caller) != this))
return raise(E_PERM);
endif
{who, trickle_chance} = args;
"check the trickle chance";
if (random(100) > trickle_chance)
"ran out of energy";
return E_QUOTA;
endif
"halve it for passing to the parent";
trickle_chance = trickle_chance / 2;
"only improve if there is potential";
if (this.pot)
add = random(this.pot);
who:mod_stat(this.property, add);
this:record_improvement(who);
if (msg = this:improved_msg(who))
who:tell(msg);
endif
is_player(who) && `#2:_dnotify(tostr(">>> ", ctime(), ": ", who.name, ".", this.property, " += ", add)) ! ANY';
endif
"come to papa";
if ((papa = parent(this)) != $skill)
papa:improve(who, trickle_chance);
endif
.
#95:5
":improve_msg(character)";
"A message informing the character that e has improved in this area.";
":macrospar_msg(character)";
"A message informing the character that e may as well stop the macro cuz that stat ain't going anywhere.";
m = this.(verb);
if (typeof(m) == LIST)
return $list_utils:random_element(m);
else
return m;
endif
.
#95:6
":should_improve(char, roll, mod)";
"Should who's skill in this area be improved with a roll of 'roll' and a modifier of 'mod'?";
{char, roll, mod} = args;
if (!char:should_improve(this))
return E_PROPNF;
endif
"Pass the initial random test of learning difficulty.";
l = this.learn;
if ((!l) || (random(l) != l))
return E_NACC;
endif
"Check that it's been long enough since the last improvement.";
time_left = this:improved_recently(char);
0 && `#2:_dnotify(tostr(">>> ", ctime(), ": ", char.name, ".", this.property, " ... ", time_left)) ! ANY';
if (time_left)
return E_QUOTA;
endif
"20% chance to skip the range requirements.";
if ((random(100) < 80) && ((roll < this.improve_range[1]) || (roll > this.improve_range[2])))
return E_RANGE;
endif
return 1;
"1998-09-25: Nuked previous comments.  Haha!";
.
#95:7
player:tell("Name                 : ", this:id());
player:tell("Property on $skilled : ", this.property);
player:tell("Governing Attributes : ", $string_utils:english_list(this.attributes, "None"));
player:tell("Learn Range          : ", this.improve_range[1], "..", this.improve_range[2]);
player:tell("Learn Chance         : 1 in ", this.learn);
player:tell("Improvement Pacing   : ", $string_utils:from_seconds(this.wait));
player:tell("Potential Disbursed  : 1 to ", this.pot);
player:tell("Trickle Chance       : ", this.trickle_chance, "% (top level)");
{minr, maxr} = this.average_range;
{t, l, a, h} = this.census;
player:tell("Valid Average Range  : ", minr, "-", maxr);
player:tell("Min/Avg/Max          : ", l, "/", a, "/", h);
player:tell("Census Interval      : ", $string_utils:from_seconds(this.census_interval), " [", player:ctime(t), "]");
.
#95:8
":penalty(who)";
"Penalty on any resolution of this skill by the given character.";
return args[1]:penalty(this);
.
#95:9
":disallow_boost_by(OBJ character, INT pts)";
"Assuming he has enough potential, may the given (new) character use it to boost this skill to the given number of points? Return a reason why NOT if he shouldn't be allowed, else return false.  Note that for skills this applies only to characters editing themselves initially after a reroll, but applies to attributes at all times.";
{who, pts} = args;
now = who:get_stat(this.property);
"A negative max_boost_rank uses the skill's current average.  A zero means";
"there is no limit.  Otherwise uses the given positive integer.";
max = this.max_boost_rank;
if (max < 0)
max = this:average_rank();
endif
if (pts > max)
why_msg = ((i = max - now) > 0) ? tostr(", or boost it by ", i, " or less points") | ", since your current rank already exceeds that max";
return tostr("You aren't allowed to boost \"", this:name(), "\" to a rank higher than ", max, ".  You'll have to improve it through use", why_msg, ".");
endif
return 0;
.
#95:10
":recycle()";
if (!this:is_writable_by(caller, caller_perms()))
return E_PERM;
endif
$rpg:delete_stat(this);
return pass(@args);
.
#95:11
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
this.last_boosted = {};
return pass(@args);
.
#95:12
":boost_cost(new_rank)";
"Return the cost (in FLOAT potential points) of raising this stat to the given level.";
{new_rank} = args;
return ceil(tofloat(new_rank) * this.boost_cost_pct);
.
#95:13
":average_rank()";
"Return the average PC rank for this skill.";
census = this.census;
"{t, min, avg, max} = this.census";
t = census[1];
if (is_clear_property(this, "census"))
"";
elseif ((t == -1) || (t > (time() - this.census_interval)))
return census[3];
endif
fork (0)
this:compile_census(@this.average_range);
endfork
this.census[1] = -1;
return census[3];
.
#95:14
":compile_census([low, high])";
"Find min, max, averages of PC ranks for this skill.";
{?lo = this.average_range[1], ?hi = this.average_range[2]} = args;
RPG = $rpg;
MEAN_WAY = this.mean;
TOO_LONG = $time_utils.week;
TOO_YOUNG = $time_utils.week;
orig_min = min_r = $maxint;
orig_max = max_r = 0;
"mean or median average?";
total = count = 0;
all_r = {};
"the big loop";
stat = this.property;
for p in (players())
rank = p.(stat);
if (RPG:trusted(p))
"exclude gms";
elseif (RPG:is_npc(p))
"exclude npc";
elseif ((rank < lo) || (rank > hi))
"rank minmaxes";
elseif (0 && (((time() - `p.last_disconnect_time ! E_ANY => 0') > TOO_LONG) || ((time() - `p.first_connect_time ! E_ANY => 0') < TOO_YOUNG)))
"haven't connected in a while, or are very young";
else
min_r = min(min_r, rank);
max_r = max(max_r, rank);
total = total + rank;
count = count + 1;
if (!MEAN_WAY)
all_r = {@all_r, rank};
endif
endif
$command_utils:suspend_if_needed(0);
endfor
if (count >= 2)
avg = MEAN_WAY ? total / count | all_r[count / 2];
else
min_r = max_r = lo;
avg = max(this.base_average, lo);
endif
return this.census = {time(), min_r, avg, max_r};
.
#95:15
":raw_rank_for(pc)";
"Get the raw (unmodified by damage, crippling, etc.) skill rank for the given player.";
{pc} = args;
return pc:get_stat(this.property);
.
#95:16
":current_rank_for(pc)";
"Get the current (possibly modified by damage, crippling, etc.) skill rank for the given player.";
{pc} = args;
return pc:("stat_" + this.property)();
.
#95:17
":death_adjustment_for(char)";
"If post-mortem skill adjustments are justified, return the new skill rank for the given character.  Return 0 if no changes should be made.";
who = args[1];
now = who:get_stat(this.property);
modified = 0;
avg = this:average_rank();
if (now > (avg * 2))
diff = now - avg;
modified = now - random(diff / 10);
endif
return modified;
.
#95:18
if (args[1] != $skill_db)
raise(E_PERM);
endif
return pass(@args);
.
#95:19
":improved_recently(char)";
"Return the time remaining until the given character will be allowed to improve again, or 0 if they can do so immediately.";
{char} = args;
"Don't record non-players.";
if (!is_player(char))
return 0;
endif
"Get the character's current rank, and the average rank.";
rank = char:get_stat(this.property);
average = this:average_rank();
"Adjust the definition of 'recent' according to the subject's aptitude.";
period = this.wait;
if (average)
period = toint(tofloat(period) * (tofloat(rank) / tofloat(average)));
endif
"If below average, set the period to the minimum of that already calculated or 1 minute.  <19980928>";
"Switched to 2 minutes.  <19980928>";
if (rank < average)
period = min(period, 120);
endif
"Check for a recorded time of last improvement.";
i = $list_utils:iassoc(char, this.last_boosted);
if (!i)
return 0;
endif
info = this.last_boosted[i];
"If the current time is greater than the time last boosted plus the minimum number of seconds one must wait before boosting again, then this dude is allowed to boost.";
boost_again_time = info[2] + period;
if (time() > boost_again_time)
0 && #2:_dnotify(tostr(">>> Removing ", $string_utils:nn(char), " from ", $string_utils:nn(this), "..."));
this.last_boosted[i..i] = {};
return 0;
endif
"Otherwise, possibly print macro sparring message...";
if (msg = this:macrospar_msg(char))
char:tell(msg);
endif
"...then return seconds more they must wait.";
return boost_again_time - time();
.
#95:20
":record_improvement(player)";
"Note the current time as the last time the given character has improved their skill.";
{char} = args;
if (!this:is_writable_by(caller_perms(), caller))
return raise(E_PERM);
elseif (!is_player(char))
return;
endif
TOO_LONG = 100;
if (length(this.last_boosted) > TOO_LONG)
this.last_boosted = this.last_boosted[TOO_LONG / 2..$];
endif
this.last_boosted = {@this.last_boosted, {char, time()}};
.
#95:21
"Copied from generic skill (#494):_total by GameMaster (#1991) Tue Jan 12 17:52:50 1999 CST";
"Copied from generic skill (#494):$skill:_total(kind) by GameMaster (#1991) Tue Jan 12 17:50:40 1999 CST";
"Compute inherited bonuses/penalties for this skill.";
who = args[1];
base = who:("stat_" + this.property)();
if (!base)
base = this:unskilled();
elseif (base > 100)
base = (((40 + 75) + 100) + 15) + ((base - 100) / 2);
elseif (base > 85)
base = ((40 + 75) + 100) + (base - 85);
elseif (base > 35)
base = (40 + 75) + ((base - 35) * 2);
elseif (base > 10)
base = 40 + ((base - 10) * 3);
else
base = base * 4;
endif
all = 1;
papa = this;
while (valid(papa = parent(papa)) && (papa != $skill))
base = base + (who:("stat_" + papa.property)() || 0);
all = all + 1;
endwhile
return base / all;
.
#95:22
":skill_bonus_for(OBJ char)";
"Compute a skill bonus for the given character.  Does not factor in parent skills or bonuses bestowed by governing attributes.";
who = args[1];
base = this:current_rank_for(who);
if (base < 1)
base = this:unskilled();
elseif (base > 1000)
"1000+ /10";
base = ((((((40 + 75) + 100) + 15) + 75) + 125) + 250) + ((base - 1000) / 10);
elseif (base > 500)
"501-1000 /4 = 125-250";
base = (((((40 + 75) + 100) + 15) + 75) + 166) + ((base - 500) / 4);
elseif (base > 250)
"251-500 /3 = 83-166";
base = ((((40 + 75) + 100) + 15) + 75) + ((base - 250) / 3);
elseif (base > 100)
"101-250 /2 = 50-75";
base = (((40 + 75) + 100) + 15) + ((base - 100) / 2);
elseif (base > 85)
"86-100 x1 = 1-15";
base = ((40 + 75) + 100) + (base - 85);
elseif (base > 35)
"36-85 x2 = 1-100";
base = (40 + 75) + ((base - 35) * 2);
elseif (base > 10)
"11-35 x3 = 1-75";
base = 40 + ((base - 10) * 3);
else
"1-10 x4 = 1-40";
base = base * 4;
endif
return base;
.
#95:23
":total_skill_bonus_for(who)";
"Total skill bonus for the given character, averaging in bonuses of all inherited skills.";
who = args[1];
base = this:skill_bonus_for(who);
if (1)
"average this total and all parent totals";
if ((papa = parent(this)) != $skill)
base = (base + max(papa:skill_bonus_for(who), 0)) / 2;
endif
else
"average all parent totals, add this one";
count = total = 0;
papa = this;
while (valid(papa = parent(papa)) && (papa != $skill))
total = total + max(papa:skill_bonus_for(who), 0);
count = count + 1;
endwhile
if (count)
base = base + (total / count);
endif
endif
return base;
.
#95:24
":starting_rank_for(OBJ character)";
"Return the rank at which the given character should begin the game, or to which their skill should be reset after a reroll. If this.starting_rank is non-negative, use its value.  Otherwise, the rank is based on this skill's average, and the player's attributes in relation to this skill's governing attributes.";
{who} = args;
"if a prop is >= 0, use it";
if (this.starting_rank >= 0)
return this.starting_rank;
endif
avg = this:average_rank();
att_bonus = this:att_bonus_for(who);
"if the total attribute bonus is negative, 1/5 average";
"otherwise, start at 1/3 average";
return (att_bonus > 0) ? avg / 5 | (avg / 10);
.
#96:0
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this:set_gender("plural");
.
#97:0
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this:set_gender("unknown");
.
#98:0
"tease <clothing>";
"Hike up your skirt, straighten your stockings, bare some shoulder.  C'mon, we're talking about your job here.  No.  We're talking about sexual harassment.  And I don't have to take it.";
if (player != this.worn)
player:tell("You aren't wearing that.");
return E_INVARG;
elseif (!this:visible())
player:tell("You adjust your underwear.");
player.location:announce(player.name, " adjusts ", player:pp(), " underwear.");
else
this:announce_messages("tease");
endif
.
#98:1
"wear -- wears this clothing";
if ((who = this.worn) == player)
player:tell("You're already wearing ", this.name, ".");
elseif (valid(who))
player:tell(this:title(), " is already being worn by ", who:title(), ".");
elseif (this.location != player)
player:tell("You don't have ", this.name, ".");
elseif (missing = this:appendage_check(player))
player:tell("Hmm.. you seem to be missing the required body parts to wear this clothing.  Try again when you've grown a ", $string_utils:title_list(missing), ".");
elseif (this:do_wear(player))
this:announce_messages("wear");
endif
.
#98:2
"If worn and there is a message defined specific to the wearer's gender, return that message.  Else return the default unisex worn message.";
who = this:worn();
if (!who)
return this:get_message("worn");
endif
who = who[1];
if (msg = this:get_message(who:gender_adj()))
"..:gender_adj()-specific message...";
else
msg = this:get_message("worn");
endif
return $string_utils:pronoun_sub(msg, who, this, who.location, player);
.
#98:3
"This verb handles messages printed when clothing is manipulated.";
"%N -- Who is doing the dressing.";
"%D -- Who is being dressed (Same as %N when dressing yourself)";
"%T -- The duds.";
if (!this:is_readable_by(caller_perms(), caller))
return E_PERM;
endif
msg = this:get_message(verb[1..length(verb) - 4]);
msg = $string_utils:pronoun_sub(msg, player, this, player.location, dobj);
return msg;
.
#98:4
"remove -- removes your own clothing";
if (this.worn != player)
player:tell("You aren't wearing ", this.name, ".");
elseif (this:do_remove())
this:announce_messages("remove");
endif
.
#98:5
"do_remove -- does the actual removal of clothing";
who = this:worn();
if (!who)
return E_NONE;
elseif (!this:caller_ok(caller, caller_perms(), who = who[1]))
return E_PERM;
elseif (takem = this:covered_by())
player:tell("You'll have to remove ", (player == who) ? "your " | (who.name + "'s "), $string_utils:title_list(takem), " first.");
return E_NACC;
endif
this:remove_from(who);
for garment in (this:contents())
`garment:tuck_in() ! E_VERBNF';
endfor
if (!$object_utils:contains(who, this))
this.move_task = task_id();
this:moveto(who);
endif
return !(this.worn = #-1);
.
#98:6
":do_wear(OBJ new_wearer) -- does the actual wearing of clothing";
if (!valid(who = args[1]))
return E_INVIND;
elseif (!this:caller_ok(caller, caller_perms(), who))
return E_PERM;
elseif (!this:is_wearable_by(who))
player:tell((player == who) ? "You" | who.name, " can't wear that.");
return E_NACC;
elseif (!this:where(who))
this:tell_coverage(this:get_body_parts(who));
return E_NACC;
endif
if (this.location != who)
this:moveto(who);
endif
this:add_to(this.worn = who);
this:tuck_in();
this:arrange_clothing(who);
return this:worn() ? 1 | 0;
.
#98:7
":get_clothing_object(OBJ who)";
{who} = args;
if (`who.clothing ! E_PROPNF' == E_PROPNF)
return #-1;
endif
return who;
.
#98:8
":caller_ok(caller, caller_perms, clothing_object)";
"-> True if given caller specs can manipulate the clothing.";
args = {@args, args[1], args[1]};
from = args[1];
perm = args[2];
what = args[3];
if (from in {this, what})
"...caller is either this or the clothing fo, or the object being clothed.";
return 1;
elseif ($perm_utils:controls(perm, what))
"...caller_perms controls the clothing object.";
return 1;
endif
.
#98:9
":moveto()";
who = this:worn();
if ((!who) || (task_id() == this.move_task))
"ok";
elseif (player == who[1])
this:do_remove();
elseif (this:is_controllable_by(player))
this:unconditional_remove();
else
return E_PERM;
endif
return pass(@args);
.
#98:10
"Removes this from the player's .clothing property.";
cp = caller_perms();
if ((caller != this) && (!$perm_utils:controls(cp, this)))
return E_PERM;
elseif ((who = this:worn()) && this:caller_ok(caller, cp))
this:remove_from(who[1]);
endif
return pass(@args);
.
#98:11
":worn() -> {who's wearing it}.";
"If this.worn is not valid, set it to $nothing.  If the clothing is not at the location of the person supposedly wearing it, then it is unworn and this.worn is set to $nothing.";
"Return an empty list if the clothing is unworn.";
if (!$recycler:valid(who = this.worn))
"...bad bad bad...";
elseif ($object_utils:contains(who, this))
return {who};
else
this:remove_from(who);
endif
this.worn = #-1;
return {};
.
#98:12
":get_message(msgname)";
if (!this:is_readable_by(caller_perms(), caller))
return E_PERM;
endif
return pass(strsub(args[1], "_msg", ""));
.
#98:13
":is_configurable_by(who, caller, action)";
"-> True if who/caller can do something to this object.  Action is either COVERAGE (set body areas covered by the clothing), NAME (set_name or set_aliases), MESSAGE or DESCRIPTION.";
{cp, c, ?what = ""} = args;
return this:is_writable_by(cp, c);
.
#98:14
":get_body_parts([who]) -> Return object ids of all valid body parts.";
who = this:get_clothing_object(args ? args[1] | player);
return $code_utils:verb_or_property(who, "body_areas") || this.body_parts;
.
#98:15
":arrange_clothing(who)";
" Puts all stray clothing on `who' into whatever is covering it.";
{pc} = args;
for thing in (pc:contents())
`thing:tuck_in() ! E_VERBNF';
endfor
return;
"970624: Why check if it's a this.root?  Just try a :tuck_in.  Probably a bad idea.";
.
#98:16
":tuck_in()";
"Tucks the clothing into some other piece.";
if (0 && (duds = this:covered_by()))
this.move_task = task_id();
this:moveto(duds[length(duds)]);
elseif (this:worn() && (this.location != this.worn))
this.move_task = task_id();
this:moveto(this.worn);
endif
.
#98:17
"Allow other garments inside this one.";
return $object_utils:isa(args[1], this.root);
.
#98:18
":where(who) -> Return positions in .clothing list.";
where = {};
parts = this:get_body_parts(@args);
for area in (this.worn_on)
if (pos = area in parts)
where = setadd(where, pos);
endif
endfor
return where;
.
#98:19
":visible() -> True if clothing is visible on who is wearing it.";
who = this:worn();
if (!who)
return 1;
endif
who = this:get_clothing_object(who[1]);
if (!valid(who))
return 1;
endif
visible = 0;
clothing = who.clothing;
while (clothing && (!visible))
area = clothing[1];
visible = area && (area[1] == this);
clothing = listdelete(clothing, 1);
endwhile
return visible;
.
#98:20
":covered_by() -> clothing atop this.";
who = this:worn();
if (!who)
return {};
endif
who = this:get_clothing_object(who[1]);
if (!valid(who))
return {};
endif
covering = {};
clothing = who.clothing;
for w in (this:where(who))
if ((pos = this in (worn = clothing[w])) != 1)
for c in (worn[1..pos - 1])
covering = setadd(covering, c);
endfor
endif
endfor
return covering;
.
#98:21
":add_to(who) => Adds this to who's clothing property.";
if (!this:caller_ok(caller, caller_perms(), who = args[1]))
return E_PERM;
elseif (!valid(object = this:get_clothing_object(who = args[1])))
return E_INVIND;
else
clothing = object.clothing;
for w in (this:where(@args))
clothing[w] = listinsert(clothing[w], this);
endfor
object.clothing = clothing;
endif
.
#98:22
":remove_from(who) => Removes this from who's clothing property.";
if (!this:caller_ok(caller, caller_perms(), who = args[1]))
return E_PERM;
elseif (!valid(object = this:get_clothing_object(who = args[1])))
return E_INVIND;
else
clothing = object.clothing;
for w in (this:where(@args))
clothing[w] = setremove(clothing[w], this);
endfor
object.clothing = clothing;
endif
.
#98:23
"@coverage <clothing>                   -- Show body areas covered.";
"@coverage <clothing> is ?              -- Show covered, plus valid areas.";
"@coverage <clothing> is area[, area]*  -- Set the body areas covered.";
"";
"Areas are the same as those listed by the @nudity command.  Some examples are:";
"  @coverage boots is feet";
"  @coverage boxers is groin,thighs";
"  @coverage shirt is torso,upper arms";
"";
"Clothing cannot be worn without first setting its coverage.  You must remove a piece of clothing before re-setting its coverage.";
who = this:worn();
if (iobjstr && (iobjstr != "?"))
if (who)
who = who[1];
player:tell("You'll have to remove ", $string_utils:nn(this), (who == player) ? "" | tostr(" from ", $string_utils:nn(who)), " before changing its body areas.");
return E_NACC;
endif
given = $string_utils:explode(iobjstr, ",");
given = $list_utils:map_arg($string_utils, "trim", given);
given = $list_utils:remove_duplicates(given);
else
given = {};
endif
if (!given)
this:tell_coverage(prepstr && this:get_body_parts(@who));
return;
elseif (!this:is_configurable_by(((cp = caller_perms()) != #-1) ? cp | player, caller, "coverage"))
player:tell("You aren't allowed to change the @coverage of ", $string_utils:nn(this), ".");
return E_PERM;
endif
areas = bogus = {};
for part in (given)
if (found = this:match_body_part(part, player))
areas = $set_utils:union(areas, found);
else
bogus = setadd(bogus, part);
endif
endfor
if (bogus)
player:tell("You don't have a ", $string_utils:english_list(bogus, "nothing", "or"), ".");
endif
if (this.worn_on = areas)
player:tell($string_utils:nn(this), " is now worn on the ", $string_utils:name_list(areas), ".");
else
player:tell($string_utils:nn(this), " cannot be worn until its coverage is set.");
endif
.
#98:24
"Return nothing if it isn't worn, or if the location is being handled by a clothing object.  Else return the :worn_msg.";
if (!this:worn())
return (verb == "look_person_msg") ? pass(@args) | "";
elseif (valid(this:get_clothing_object(this.location)))
"...invisible...";
return 0;
else
return this:worn_msg();
endif
.
#98:25
":appendage_check(who)";
"-> Does `who' have all appendages required to wear this item?  If not, return a list of the missing extremities.  Return 0 if all parts are present.";
who = this:get_clothing_object(args[1]);
nude = this:get_body_parts(who);
missing = {};
for area in (this:get_coverage(who))
if (!(area in nude))
missing = setadd(missing, area);
endif
endfor
return missing || 0;
.
#98:26
"strip <clothing> from <character>";
"Dresses the given character in this clothing.  E must first trust you to dress em.  See 'help trust' for more information.";
dobj = player:my_match_object(dobjstr);
if ($match_utils:object_match_failed(dobj, dobjstr))
"...failed match...";
elseif (who = this:worn())
who = who[1];
if (who == dobj)
player:tell(dobj:dnamec(), " is already wearing that.");
elseif (who == player)
player:tell("You're already wearing that.");
else
player:tell(this:dnamec(), " is already being worn by ", who:dname(), ".");
endif
elseif (this.location != player)
player:tell("You don't have ", this:dnamec(), ".");
elseif (!dobj:trusts(player, "dress"))
player:tell(dobj:dnamec(), " doesn't want you dressing ", dobj:po(), ".");
elseif (this:do_wear(dobj))
this:announce_messages("dress");
endif
.
#98:27
":set_worn_on(LIST bodyparts)";
if (!this:is_configurable_by(caller_perms(), caller, "coverage"))
return E_PERM;
endif
return this.worn_on = args[1];
.
#98:28
"Allow those other than the owner to change name, description, messages.";
if ((verb == "set_name") || (verb == "set_aliases"))
action = "name";
elseif (verb == "set_description")
action = "description";
elseif (verb == "set_message")
action = "message";
else
action = "";
endif
if (!this:is_configurable_by(caller_perms(), caller, action))
return E_PERM;
endif
return pass(@args);
.
#98:29
":coverage([clothing-object]);";
"-> Areas this clothing is worn on.  If clothing-object is given, pass to :my_coverage, which should return body parts appropriate to it.";
"For example, if the player has a 'breasts' part defined...";
":my_coverage({#chest}) -> {#chest, #breasts}";
"Basically this is for players who want to define more specific parts than the default, yet still allow 'normal' clothing to fit over them.";
base = this.worn_on;
if (args)
return args[1]:my_coverage(base) || base;
else
return base;
endif
.
#98:30
":is_readable_by(who, caller) -> May the given perms peek at privied props?";
"By default allows the owner, the author of this verb, and this object itself read the naughty bits.  Note that hacking this in a haphazard or paranoid way may disrupt certain clothign features.";
return ((this.r || (this.owner in args)) || (this in args)) || (args[1] == $code_utils:verb_perms());
.
#98:31
":tell_coverage([body_areas])";
"-> Formatted display of what body areas this clothing covers.";
"Optional `body_areas' args should be a list of all the valid body areas for the player.  If given and true, they are displayed with a usage message.";
if (worn_on = this:get_coverage(player))
msg = tostr($string_utils:nn(this), " is worn on ", $string_utils:title_list(worn_on), ".");
else
msg = tostr("The @coverage for ", $string_utils:nn(this), " is unset.");
endif
if (args && args[1])
player:tell(msg, "  Body areas available on your character include:");
valid = $string_utils:columnise($list_utils:map_prop(args[1], "name"), 3);
for line in (valid)
player:tell("  ", line);
endfor
player:tell("Use `@coverage ", this.name, " is area[,area, ... ]' to set the areas covered by your clothing.");
else
player:tell(msg);
endif
.
#98:32
":match_body_part(string, player)";
"-> List of body area objects on player matching the given string.";
return $match_utils:match_list(args[1], this:get_body_parts(args[2]));
.
#98:33
":is_wearable_by(who) -> Can who wear these duds?";
return $object_utils:isa(args[1], $character);
.
#98:34
":max_damage([INT damage[, OBJ area[, OBJ weapon[, OBJ attacker]]]])";
"Return the maximum amount of damage this armour will absorb.";
":deflection([INT damage[, OBJ area[, OBJ weapon[, OBJ attacker]]]])";
"Return the deflection rating of this armour: a % compared against a weapon's penetration factor to determine if the attack is effective on this armour.  The higher, the better.";
":absorption([INT damage[, OBJ area[, OBJ weapon[, OBJ attacker]]]])";
"Return the absorption rating of this armour: The % of damage it will absorb.";
{?damage = 0, ?area = #-1, ?weapon = $noweapon, ?attacker = #-1} = args;
return (dt = `weapon.damage_type ! E_PROPNF => ""') ? `this.((verb + "_") + dt) ! E_PROPNF => this.(verb)' | this.(verb);
.
#98:35
":mod_damage(damage[, area, weapon, attacker])";
"Return damage adjusted for this armour's protection.";
{dam, @rest} = args;
mod = (dam * this:absorption(dam, @rest)) / 100;
return dam - mod;
.
#98:36
":absorb_damage(damage, area, weapon, attacker)";
"Return modified damage.  Will print clang messages if all damage is absorbed.";
{dam, ?area, ?weapon = $nothing, ?attacker = $nothing} = args;
"961211-1156: hacked penetration/deflection below <quinn>";
if ($recycler:valid(weapon))
p = `weapon:penetration(attacker, this:worn_by()) ! E_VERBNF => $weapon.penetration';
else
p = $weapon.penetration;
endif
if (p < 1)
"no penetration; never gets through: The attack is ineffective.";
return 0;
endif
d = this:deflection(dam, area, weapon, attacker);
if (d > 0)
if (random(d) > random(p))
this:maybe_announce_deflect(0, area, weapon, attacker);
return 0;
endif
endif
adj = this:mod_damage(@args);
mod = dam - adj;
max = this:max_damage(@args);
if (max && (mod > max))
"...too much damage, perhaps trash armour...";
dam = dam - min(mod, max);
else
dam = adj;
endif
dam = max(dam, 0);
this:maybe_announce_absorb(dam, @listdelete(args, 1));
return dam;
.
#98:37
":announce_clang(damage, area, weapon, attacker)";
$you:say_action("%P %t absorbs the full force of the blow.", this.location);
.
#98:38
":set_deflection(num)";
"-> A percentage chance of this armour deflection a blow, compared against weapon.penetration.  Max should be around 50.";
":set_absorption(num)";
"-> The percentage of damage this armour will absorb.";
":set_max_damage(num)";
"-> The maximum amount of damage this armour will absorb in a single blow.";
cp = caller_perms();
if (!$rpg:trusted(cp))
return E_NACC;
elseif ($rpg:trusted(this.owner) && (!this:is_controllable_by(cp, caller)))
return E_PERM;
else
prop = verb[5..$];
return this.(prop) = args[1];
endif
.
#98:39
if (w = this:worn())
c = this:get_coverage(w = w[1]);
m = tostr(this:dnamec(), " is worn by ", w:dname(), " on ", $string_utils:dname_list(c), ".");
else
c = this:get_coverage(player);
m = tostr(this:dnamec(), " is to be worn on ", $string_utils:dname_list(c), ".");
endif
m = tostr(m, "  Clothing or body areas beneath it ", this.revealing ? "will" | "will not", " be shown.");
m = {m, tostr("It has an absorption rating of ", this.absorption, "% for up to ", this.max_damage, " points of damage.")};
player:tell_lines(m);
.
#98:40
":unconditional_remove()";
"Get this clothing OFFA ME!  No excuses!";
who = this:worn();
if (!who)
return;
elseif (!this:caller_ok(caller, caller_perms(), who = who[1]))
return E_PERM;
endif
this:remove_from(who);
for garment in (this:contents())
`garment:tuck_in() ! E_VERBNF';
endfor
this.worn = #-1;
return 1;
.
#98:41
":worn_by()      -> #char";
":worn_by(#char) -> BOOLEAN";
w = this:worn();
return w ? args ? w[1] == args[1] | w[1] | $nothing;
.
#98:42
":will_reveal({@underclothes})";
"Return true if this garment, when worn, reveals the given clothing beneath it.";
return this.revealing;
.
#98:43
"@reveal under this";
"Allow clothing or body parts beneath this worn item to show their messages.";
"@reveal nothing under this";
"Prevent anything beneath this item of clothing from being seen when it is being worn.";
if ((caller != player) || (!this:is_writable_by(player)))
player:tell("You aren't allowed to set attributes of ", $string_utils:nn(this), ".");
return E_PERM;
endif
if (dobjstr == "nothing")
player:tell("When worn, nothing beneath ", $string_utils:nn(this), " will show.");
this.revealing = 0;
else
player:tell("Clothing or body areas beneath ", $string_utils:nn(this), " will show their messages.");
this.revealing = 1;
endif
.
#98:44
":maybe_announce_clang(NUM adjusted_damage, @args)";
"Announce a clang if all damage was absorbed.";
{dam, area, weapon, attacker} = args;
if (dam < 1)
this:announce_clang(@args);
return 1;
endif
return 0;
.
#98:45
":maybe_announce_deflect/absorb(damage)";
"If args[1] is < 1, announce the complete deflection of an attack, or the absorption of all its damage.";
{dam, area, weapon, attacker} = args;
if (dam > 0)
return;
elseif (verb == "maybe_announce_deflect")
$you:say_action("%P %t deflects the strike.", this:worn_by());
else
$you:say_action("%P %t absorbs the full force of the blow.", this:worn_by());
endif
.
#98:46
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.root = $clothing;
this.worn = #-1;
this.worn_on = {};
.
#99:0
":sound_permeability/transparency(@args) -> Vary values for closedness.";
return this:is_opened() ? pass(@args) | this:("closed_" + verb)(@args);
.
#99:1
":closed_sound_permeability/transparency(@args)";
"-> How well sound/motion travels through the door.";
return this.(verb);
.
#99:2
":is_opened()";
":is_locked() -> Get the opened/locked status of the door.";
return this.(verb[4..length(verb)]);
.
#99:3
":set_opened()";
":set_locked() -> Set the opened/locked status of the door.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
this.(verb[5..length(verb)]) = args[1];
otherside = this:otherside();
if (caller != otherside)
otherside:(verb)(@args);
endif
.
#99:4
":is_key(object) -> Is the given object a key for this door?";
if (keys = this.keys)
ancs = {args[1], $object_utils:ancestors(args[1])};
for key in (keys)
if (key in ancs)
return 1;
endif
endfor
elseif (caller != (os = this:otherside()))
return os:(verb)(@args);
else
return 0;
endif
.
#99:5
":is_lockable_by(who)";
"-> Can `who' lock/unlock this door? (with the given key?)";
who = args[1];
if (!this.lockable)
return 0;
elseif (this.keys)
return this:user_has_key(who);
else
return 1;
endif
.
#99:6
":user_has_key(OBJ who) -> Does who have a key for this door?";
for what in (args[1]:contents())
if (this:is_key(what))
return {what};
endif
endfor
.
#99:7
return $string_utils:pronoun_sub(this.(verb), @args);
.
#99:8
"knock on door -- knock knock";
"Broadcasts .knock_sound_msg to the otherside.";
this:announce_messages("knock");
this:hear_event_sound(player, this.knock_sound_msg, 5);
.
#99:9
if (this:is_opened())
this:announce_messages("already_opened");
elseif (this:is_locked())
this:announce_messages("locked");
else
this:set_opened(1);
this:announce_messages("open");
iobj = this:otherside();
this:hear_event_motion(player, this.open_motion_msg, 5);
endif
.
#99:10
if (!this:is_opened())
this:announce_messages("already_closed");
else
this:set_opened(0);
this:announce_messages("close");
iobj = this:otherside();
this:hear_event_motion(player, this.close_motion_msg, 5);
endif
.
#99:11
if (this:is_locked())
this:announce_messages("already_locked");
return E_NONE;
elseif (this:is_opened())
player:tell("Close ", this:dname(), " before trying to lock it.");
return;
elseif (!this.lockable)
this:announce_messages("not_lockable");
return E_NONE;
elseif (!prepstr)
"ok...";
elseif (!iobjstr)
player:tell(verb, " ", prepstr, " what?");
return;
elseif (!(prepstr in {"with", "using"}))
player:tell("Try `", verb, " with ", iobjstr, "` next time.");
return;
elseif ($command_utils:object_match_failed(iobj = player:my_match_object(iobjstr), iobjstr))
return;
elseif (!(iobj in player:contents()))
player:tell("You don't have that.");
return;
elseif (!this:is_key(iobj))
this:announce_messages("bad_key");
return;
else
this:set_locked(1);
this:announce_messages("lock");
return;
endif
if (key = this:is_lockable_by(player))
dobj = (typeof(key) == LIST) ? key[1] | $nothing;
this:set_locked(1);
this:announce_messages("lock");
else
this:announce_messages("cannot_lock");
endif
.
#99:12
if (!this:is_locked())
this:announce_messages("already_unlocked");
return E_NONE;
elseif (!this.lockable)
this:announce_messages("not_lockable");
return E_NONE;
elseif (!prepstr)
"ok...";
elseif (!iobjstr)
player:tell(verb, " ", prepstr, " what?");
return;
elseif (!(prepstr in {"with", "using"}))
player:tell("Try `", verb, " with ", iobjstr, "' next time.");
return;
elseif ($command_utils:object_match_failed(iobj = player:my_match_object(iobjstr), iobjstr))
return;
elseif (!(iobj in player:contents()))
player:tell("You don't have that.");
return;
elseif (!this:is_key(iobj))
this:announce_messages("bad_key");
return;
endif
if (key = this:is_unlockable_by(player))
dobj = (typeof(key) == LIST) ? key[1] | $nothing;
this:set_locked(0);
this:announce_messages("unlock");
else
this:announce_messages("cannot_unlock");
endif
.
#99:13
if (this:is_opened())
return pass(@args);
endif
this:announce_messages("closed");
.
#99:14
pass(@args);
if (this:is_opened())
player:tell(this:opened_append_msg());
else
player:tell(this:closed_append_msg());
endif
.
#99:15
"If the walker encounters a closed door; open it, walk through, then close it from the other side.";
if (this:is_opened())
return pass(@args);
endif
this:open();
pass(@args);
otherside = this:otherside();
if (player.location == otherside.source)
otherside:close();
endif
.
#99:16
":is_unlocked_for(who[, is_walker])";
"Disallow passage if the door is closed.  If the player is walking through automatically, just return the passed lock result.";
if (args[2])
return pass(@args);
else
return pass(@args) && this:is_opened();
endif
.
#99:17
":pick_attempt_action(character, lockpick)";
"Announce messages describing the pick attempt.  Return an error value if you want the pick to announce its messages as well.";
"If you want to handle the entire lockpick action yourself, define a :_lockpick verb expecting args same as this verb.  It's return result is ignored.";
"If you simply want to change the difficulty of picking this lock, just set the .pickable property or define a :pickable(char, pick) verb.";
return E_NONE;
.
#99:18
":pick_failure_action(character, lockpick)";
"Handle what happens when the lockpick fails.  Return an error value if you want the pick to announce its messages as well.";
iobj = args[2];
this:announce_messages("pick_failed", args[1]);
.
#99:19
":pick_success_action(character, lockpick)";
"Handle what happens when the lock is picked.  Return an error value if you want the pick to announce its messages as well.";
iobj = args[2];
this:announce_messages("pick_succeeded", args[1]);
this:set_locked(0);
.
#99:20
":set_pickable(num)";
"Set the modifier to a lockpick attempt on this door.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
elseif (typeof(mod = args[1]) != NUM)
return E_TYPE;
else
return this.pickable = mod;
endif
.
#99:21
"@add-key <keyobj> to door";
if (!this:is_writable_by(player))
player:tell(this:grammar_sub("You aren't allowed to add or remove keys from %[tdname]."));
return E_PERM;
endif
dobj = player:my_match_object(dobjstr);
if ($match_utils:object_match_failed(dobj, dobjstr))
return;
endif
"this should probably be using an add/remove_key accessor";
if (dobj in this.keys)
player:tell(dobj:grammar_sub("%(dname) %<is> already a key for %[tdname]."));
else
this.keys = setadd(this.keys, dobj);
player:tell(dobj:grammar_sub("Added %(dname) as a key for %[tdname]."));
endif
.
#99:22
"@remove-key <keyobj> from door";
if (!this:is_writable_by(player))
player:tell(this:grammar_sub("You aren't allowed to add or remove keys from %[tdname]."));
return E_PERM;
endif
dobj = player:my_match_object(dobjstr, this.keys);
if ($match_utils:object_match_failed(dobj, dobjstr, this.keys))
return;
endif
"this should probably be using an add/remove_key accessor";
if (!(dobj in this.keys))
player:tell(dobj:grammar_sub("%(dname) %<is> not a key for %[tdname]."));
else
this.keys = setremove(this.keys, dobj);
player:tell(dobj:grammar_sub("Removed %(dname) as a key for %[tdname]."));
endif
.
#101:0
":trusts(who, what)";
"Trust those trusted by the RPG to chown over collective objects.";
user = args[1];
what = args[2];
if (r = this:is_trustable_action(what))
if (r[1]:matches("chown-to") && $rpg:trusted(user))
return 1;
endif
endif
return pass(@args);
.
#102:0
":indefinite_article(word)";
"-> Which indefinite article precedes the given word?";
noun = args[1];
if (!noun)
return "";
elseif (!index("aeiou", noun[1]))
return this:is_nonvowel_exception(noun) ? "an" | "a";
else
return this:is_vowel_exception(noun) ? "a" | "an";
endif
.
#102:1
":is_vowel_exception(word)";
"-> Should the given word use 'a' instead of 'an' as an indef article?";
word = args[1];
if (match(word, this.vowel_exceptions))
return 1;
elseif (match(word, "^uni%([aeioubcghqwyz]%|[a-z][aeiouy]%)"))
return 1;
else
return 0;
endif
.
#102:2
":is_nonvowel_exception(word)";
"-> Should the given word use 'an' instead of 'a' as an indef article?";
word = args[1];
return match(word, this.nonvowel_exceptions) ? 1 | 0;
.
#102:3
":pluralize(STR noun[, NUM quantity])";
"-> The plural version of the given noun (if quantity merits a plural).";
string = args[1];
if (args[2] == 1)
return string;
endif
vowels = "aeiou";
consonants = "bcdfghjklmnpqrstvwxyz";
len = length(string);
last = string[len];
next = string[len - 1];
if ((next + last) in {"ss", "sh", "ch", "x", "zz"})
return string + "es";
elseif (last == "s")
return string + "ses";
elseif (last == "y")
return (index(vowels, next) || (string[len - 2..len - 1] == "qu")) ? string + "s" | (string[1..len - 1] + "ies");
elseif (last == "o")
return index(vowels, next) ? string + "s" | (string[1..len - 1] + "es");
else
return string + "s";
endif
.
#102:4
":degenericize(str)";
"Removes all traces of geneirc synonyms from a string.";
"'generic foo'   -> 'foo'";
"'bar prototype' -> 'bar'";
base = args[1];
if (m = match(base, "^generic +%(.+%)"))
return substitute("%1", m);
endif
if (m = match(base, "%(.+%) +prototype$"))
return substitute("%1", m);
endif
return base;
.
#103:0
":add_reactor(object)";
"Add the given reactor to this AHaB.  Perhaps warn player to add their own child if the reactor is fertile.";
"-> E_PERM If the AHaB is not writable by the caller.";
"-> E_NACC If the reactor does not WANT to be added.";
"-> {list} List of current reactors if the addition was a success.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
reactor = args[1];
if (!reactor:add_ok(this))
return E_NACC;
endif
return this.reactors = setadd(this.reactors, reactor);
.
#103:1
":remove_reactor(object)";
"Remove the given reactor to this AHaB.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
reactor = args[1];
if (!(reactor in this.reactors))
return E_NONE;
endif
return this.reactors = setremove(this.reactors, reactor);
.
#103:2
":reactors() -> All of this puppet's reactor objects.";
return this.reactors;
.
#103:3
":my_call_verb(obj, vrb, args) -> Call obj:(verb)(@args).";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
set_task_perms(this);
{object, vname, ?vargs = {}} = args;
if ($object_utils:has_callable_verb(object, vname))
return object:(vname)(@vargs);
endif
raise(E_PERM, tostr("Verb ", object, ":", vname, " not found"));
.
#103:4
":my_eval(string) -> Evaluate the given string of MOO-code.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
set_task_perms($no_one);
exp = args[1];
if (exp[1] != ";")
return eval(tostr("this=", caller, "; return ", exp, ";"));
else
return eval(tostr("this=", caller, ";", exp, ";"));
endif
.
#103:5
":my_eval_d(string)";
"-> Evaluate the given string of MOO-code as if it were a !d verb.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
set_task_perms(this);
exp = args[1];
if (exp[1] != ";")
return $code_utils:eval_d(tostr("this=", caller, "; return ", exp, ";"));
else
return $code_utils:eval_d(tostr("this=", caller, ";", exp, ";"));
endif
.
#103:6
":is_addressd_text(string)";
"-> Addressed text, or 0 if string does not match a address format.";
"An 'address' is considered any text directed via speech to the AHaB.  The following forms are recognised:";
"";
"  [to AHaB]: Anything.";
"  Player says to you, \"Anything.\"";
"  Player says, \"Anything.\" to you.";
"  AHaB, Anything.";
"  Anything, AHaB.";
"";
"Note that a page is text PRIVATELY addressed, and is a separate event.";
text = args[1];
aliases = tostr("%(", this.regexp_aliases, "%)");
if (m = match(text, tostr("^", aliases, "[,;:-]+%W*%(.+%)$")))
return substitute("%2", m);
elseif (m = match(text, tostr("^%(.+%)[,;:-]+%W*", aliases, "%W*$")))
return substitute("%1", m);
endif
return 0;
"Below is the more robust (tho tick-hungry) code for parsing addresss.";
if (m = match(text, tostr("says to %(you%|", aliases, "%)%W+\"%(.+%)\"$")))
return substitute("%3", m);
elseif (m = match(text, tostr("says%W+\"%(.+%)\" to %(you%|", aliases, "%)%W*$")))
return substitute("%1", m);
endif
if (m = match(text, tostr("%[to ", aliases, "%]%W+%(.+%)")))
return substitute("%2", m);
elseif (m = match(text, tostr("\"", aliases, "[,;:-]+%W*%(.+%)\"")))
return substitute("%2", m);
elseif (m = match(text, tostr("\"%(.+%)[,;:-]+%W*", aliases, "%W*\"")))
return substitute("%1", m);
endif
return 0;
.
#103:7
":hear_event_speech(speaker, text, volume[, spoken_to])";
"Check if the speech is addressed to this AHaB in particular.";
"Note that an 'address' is not a traditional event.  It is an event private to the one being spoken to--An AHaB, in most cases.";
if ((caller != this.location) && (!this:is_controllable_by(caller_perms(), caller)))
return E_PERM;
endif
if (args[4] == this)
this:dispatch_event_address(@args);
elseif (a = this:is_addressed_text(args[2]))
this:dispatch_event_address(@listset(args, a, 2));
else
this:dispatch_event_speech(@args);
endif
.
#103:8
":dispatch_event_*(@event-args) -- Hear, and possibly act on, an event.";
"Each reactor object in this puppet's reactors list is given a chance to process the given event.  The return value is either a list of four elements:";
"Priority -> Importance of the reaction, in descending order. (1 == DO NOW)";
"Object   -> Object on which to call the verb below.";
"Method   -> Verb to call.";
"Arguments-> Args for that verb.";
"...OR some false value indication the event was ignored.";
if (!this:is_controllable_by(caller_perms(), caller))
raise(E_PERM);
endif
todo = {};
verb[1..8] = "hear";
vargs = {this, @args};
for rx in (this:reactors())
do = this:my_call_verb(rx, verb, vargs);
if (!do)
elseif (typeof(do[1]) == LIST)
todo = {@todo, @do};
else
todo = {@todo, do};
endif
endfor
"If there are things to do, interpret what they are and queue them.";
todo && this:interpret_reactor_result(todo);
"Pass to parents.  ($tangible defines some effects dispatching.)";
return todo ? 1 | pass(@args);
.
#103:9
"@add-reactor <reactor> to ahab";
"Add a reactor to this AHaB's reactor list.  The player must have write permission to the AHaB, and the reactor must allow the addition.";
if (valid(cp = caller_perms()) && (cp != player))
player:tell("You are very naughty.");
return E_PERM;
elseif (!this:is_writable_by(player))
player:tell("You aren't allowed to modify that AHaB's reactors.");
return E_PERM;
endif
reactor = player:my_match_object(dobjstr);
if ($match_utils:object_match_failed(reactor, dobjstr))
return E_INVARG;
endif
set_task_perms(player);
result = this:add_reactor(reactor);
if (result == E_PERM)
player:tell("You don't have permission to add reactors to ", this:dname(), ".");
elseif (result == E_NACC)
player:tell($string_utils:nn(reactor), " does not want to be added to ", this:dname(), ".");
elseif (!result)
player:tell("Could not add ", $string_utils:nn(reactor), " as a reactor for ", this:dname(), ": ", result);
else
player:tell("Added ", reactor:dname(), " as a reactor for ", this:dname(), ".");
endif
return result;
.
#103:10
"@remove-reactor <reactor> from ahab";
"Remove a reactor from this AHaB's reactor list.  The player must have write permission to the AHaB.";
if (valid(cp = caller_perms()) && (cp != player))
player:tell("You are very naughty.");
return E_PERM;
elseif (!this:is_writable_by(player))
player:tell("You aren't allowed to modify that AHaB's reactors.");
return E_PERM;
endif
allofem = this:reactors();
reactor = player:my_match_object(dobjstr, allofem);
if (reactor == $failed_match)
player:tell(this:dnamec(), " doesn't have a reactor by that name.");
return E_INVARG;
elseif ($match_utils:object_match_failed(reactor, dobjstr, allofem))
return E_INVARG;
endif
set_task_perms(player);
result = this:remove_reactor(reactor);
if (result == E_PERM)
player:tell("You don't have permission to remove reactors from ", this:dname(), ".");
elseif (result == E_NONE)
player:tell(reactor:dnamec(), " is not a reactor of ", this:dname(), ".");
elseif (typeof(result) != LIST)
player:tell("Could not remove ", $string_utils:nn(reactor), " as a reactor of ", this:dname(), ": ", result);
else
player:tell("Removed ", reactor:dname(), " as a reactor of ", this:dname(), ".");
endif
return result;
.
#103:11
":my_huh(verb, args)";
"This version of my_huh for $ahabs checks for verbs on reactors, akin to feature objects on humans.";
if ((caller != this) && (!this:is_controllable_by(caller_perms(), caller)))
"Standard permissions check.";
return E_PERM;
endif
"verb - obvious                 pass - would be args";
"plist - list of prepspecs that this command matches";
"dlist and ilist - likewise for dobjspecs, iobjspecs";
verb = args[1];
pass = args[2];
plist = {"any", prepstr ? $code_utils:full_prep(prepstr) | "none"};
dlist = dobjstr ? {"any"} | {"none", "any"};
ilist = iobjstr ? {"any"} | {"none", "any"};
for reactor in (this:features())
if (valid(loc = $object_utils:has_callable_verb(reactor, verb)[1]))
vargs = verb_args(loc, verb);
if ((vargs[2] in plist) && ((vargs[1] in dlist) && (vargs[3] in ilist)))
this:call_as_puppet(reactor, 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 reactors.  Parsing your command runs out of ticks while checking ", reactor.name, " (", reactor, ").");
return 1;
endif
endfor
.
#103:12
":is_controllable_by(cp, caller)";
if (pass(@args))
return 1;
elseif (length(args) > 1)
return args[2] in this:reactors();
else
return 0;
endif
.
#103:13
":update_regexp_aliases()";
"Set the .regexp_aliases property to a regular expression matching the current aliases of this AHaB.";
return this.regexp_aliases = tostr("%<", $string_utils:from_list(this.aliases, "%>%|%<"), "%>");
.
#103:14
":set_aliases(list)";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
passed = pass(@args);
if (passed)
this:update_regexp_aliases();
endif
return passed;
.
#103:15
"pending for AHaB";
"Display to player the reactions awaiting execution by this AHaB.";
"Useful in times of lag to prevent repeated queries for tasks already spawned.";
todo = this.pending_reactions;
if (!todo)
player:tell(this:dnamec(), " has no reactions pending.");
elseif ($rpg:trusted(player))
for t in (todo)
player:tell("o -> ", $string_utils:print(t));
endfor
else
player:tell(this:dnamec(), " ", this:verb_sub("has"), " ", l = length(todo), " ", $english:plural("reaction", l), " pending.");
endif
.
#103:16
":features()";
"Add all our reactors to the feature list.";
return {@pass(), @this.features};
.
#103:17
"Cure an NPC BEFORE e enters the afterlife.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
this.injury = this.insanity = this.fatigue = this.unconscious = 0;
this.upper_mobility = this.lower_mobility = 100;
return pass(@args);
.
#103:18
":set_features(LIST)";
"Set the features of this AHaB to the given list of objects.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
this.features = args[1];
.
#103:19
":interpret_reactor_result(reactor_return_value)";
"Carry out the instructions returned by a $reactor.  See $reactor for the return value format.  Queue multiple tasks for later execution.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
todo = args[1];
if (!todo)
return 0;
endif
if (typeof(todo[1]) != LIST)
todo = {todo};
endif
this:add_pending_reactions(todo);
if ($code_utils:task_valid(this.todo_task))
return length(todo);
endif
fork tid (0)
for do in ($list_utils:sort_alist(this.pending_reactions))
if (i = do in this.pending_reactions)
this.pending_reactions = listdelete(this.pending_reactions, i);
this:my_call_verb(@do[2..4]);
endif
endfor
endfork
this.todo_task = tid;
return length(todo);
.
#103:20
":do_pause(NUM seconds)";
if (caller != this)
return E_PERM;
endif
suspend(args[1]);
.
#103:21
":expire_pending_reactions(VALUE)";
"Remove all pending reactions identified as VALUE.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
id = args[1];
pending = {};
for p in (this.pending_reactions)
if (p[5] != id)
pending = {@pending, p};
endif
endfor
this.pending_reactions = pending;
"`p[5] ! E_RANGE'";
.
#103:22
":add_pending_reaction(LIST todo_list)";
if (caller != this)
return raise(E_PERM);
endif
{todo} = args;
"This could probably use a better value.";
MAXPEND = $rpg.max_queue_length * 2;
if ((lenpend = length(this.pending_reactions)) > MAXPEND)
this.pending_reactions[1..lenpend - MAXPEND] = {};
endif
this.pending_reactions = {@this.pending_reactions, @todo};
.
#103:23
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
"it's good for unattended npc to randomly select weapons.  this should probably be an rpg-option, but non-players don't have options right now, and moving it to the $npc is an easier fix to the problem of players not being able to select a persistent primary natural weapon.  <Quinn;19980825>";
now = this.current_weapon;
nat = this:natural_weapons();
if ((now in nat) && ({now} != nat))
this.current_weapon = this:random_weapon();
endif
.
#104:0
":match_args(template, argslist)";
"Each element in TEMPLATE is matched against the corresponding element in the ARGSLIST.";
"If the two elements do not match as tested by :match_element, ZERO is returned to indicate failure.";
"If TEMPLATE is longer than ARGSLIST, then the match as a whole fails unless all the extra elements in TEMPLATE are tokens which match anything.";
"The bulk of the thinking for this routine is done in :match_element.  See that method for details on how each element of the template is interpreted.";
template = args[1];
argslist = args[2];
len = length(argslist);
matched = {};
for i in [1..length(template)]
pattern = template[i];
if ((len < i) && (pattern != E_NONE))
return 0;
elseif (result = this:match_element(pattern, argslist[i]))
matched = {@matched, @result};
else
return 0;
endif
endfor
return matched;
.
#104:1
":match_element(pattern, value[, literal-list])";
"Test if the given PATTERN describes the given VALUE.  The interpretations of the pattern are given below:";
"";
"PATTERN/TYPE  INTERPRETATION";
"------------  --------------";
"'E_NONE'      Matches anything.";
"';STR'        Re-match on the result of evaluating STR.";
" STR          A regular expression to be matched against a string.";
" LIST         Succeeds if value matches any pattern in the LIST.";
" ...          All other types of patterns match their value literally.";
"";
"To literally match a list, include it inside a list.  The 'literal-list' argument is used internally to match on those lists within lists.";
"If the match fails, return 0.";
"If the value is a string, return a list of one element--the return value of the match() function.";
"Otherwise, return a list of one element--the given value.";
value = args[2];
template = args[1];
type = typeof(template);
literal_list = args[3];
if (template == E_NONE)
return {value};
elseif (type == STR)
if (((template && (template[1] == ";")) && (!(template[1..1] = ""))) && (e = $no_one:eval_d(template))[1])
return this:match_element(value, e[2]);
elseif (result = match(value, template))
return {result};
else
return 0;
endif
elseif (literal_list || (type != LIST))
return (value == template) ? {value} | 0;
else
for elm in (template)
if (result = this:match_element(value, elm, 1))
return result;
endif
endfor
return 0;
endif
.
#104:2
":reactions() -> Return all reactions defined on this object.";
"Each reaction is a list of six elements, explained below.";
"";
"# ELEMENT      TYPE(S)        EXPLANATION";
"  -------      ---------      -----------";
"1 Event        STR,{STR,...}  Type of events in which the reaction is";
"                              interested.";
"2 Template     LIST           An argument template to match against the";
"                              arguments of the event.";
"";
"             ----- The Reaction List (RX-List) -----";
"";
"3 Priority     NUM            Priority of the reaction.";
"4 Object       OBJ,STR        Object on which...";
"5 Method       STR            ...this verb should be called...";
"6 Args         LIST           ...with these arguments.";
"";
"o See `help event-types` for a list of all EVENTs and their arguments.";
"o See :match_args and :match_element for information on the structure";
"  of the TEMPLATE.";
"o See :reaction_ELEMENT on this object for information on how each of";
"  the final four elements (the 'RX-List') are parsed.";
"";
"You can use the value editor (`@value-edit #REACTOR.reactions`) to easily edit your reactions.";
if (!this:is_readable_by(caller_perms(), caller))
return E_PERM;
endif
return this.reactions;
.
#104:3
":hear_event_EVENT_TYPE(NPC, @event-args)";
"This is the communication point between the NPC and its reactors.  Here we determine if we care about the event and if so: We tell the NPC what to do in reaction to it.";
"A list must always be returned.  If the reactor is not interested, we return an empty list.  Otherwise, we return either a single reaction packet, or a list of them.";
{npc, @eargs} = args;
if ((caller != npc) && (!this:is_controllable_by(caller_perms(), caller)))
return E_PERM;
endif
verb[1..11] = "";
npc = args[1];
args = listdelete(args, 1);
for RX in (this:reactions())
type = RX[1];
if ((verb != type) && (!(verb in type)))
"...not interested...";
elseif (result = this:match_args(RX[2], args))
return this:reaction_packet(npc, RX, args, result);
endif
endfor
return {};
.
#104:4
":reaction_priority(npc, rx-list, event-args, args-match-result)";
"This method 'parses' the priority of the event.  There really is no true parsing involved.  The value is returned.";
"The priority is a numeric value representing a descending order of importance for the reaction:";
"1       -> Call reaction method ASAP.";
"100+    -> Just call it.  Whenever.";
return args[2][3];
.
#104:5
":reaction_object(npc, rx-list, event-args, args-match-result)";
"This method parses where the reaction method is to be called.";
"If the rx-object is itself an object, use that object.";
"If it is the string '$this', use this reactor.";
"If it is the string '$npc', use the object which received the event.";
rx = args[2];
os = rx[4];
if (typeof(os) == OBJ)
return os;
elseif (os == "$this")
return this;
elseif (os == "$npc")
return args[1];
else
return this;
endif
.
#104:6
":reaction_method(npc, rx-list, event-args, args-match-result)";
"Method to be called by the given reaction list.";
return args[2][5];
.
#104:7
":reaction_args(npc, rx-list, event-args, args-match-result)";
"This method is the core of the reactor's intelligence.  It performs sophisticated parsing on each ELEMENT of the args that are to be passed to the reaction method.";
"The patterns which the parser recognizes are explained below, but some syntax notes for the table are in order first:";
"  (a) By default, a PATTERN is part of the string.  If shown in single";
"      -quote marks ('), then the pattern must be the entire element.";
"  (b) TYP is the type of value for which the pattern is substituted.";
"  (c) THIS is the dbref of this reactor.";
"  (d) NPC  is the dbref of the object which received the event.";
"";
"# PATTERN       TYP  EXPLANATION";
"- -------       ---  -----------";
"1  %N,%D,etc    STR  Standard pronoun substitutions.";
"2  %0-9         STR  Result of substitute() from the match() result on the";
"                     string element of the original event arguments.";
"3  $[A|B|...]    STR  One of strings A, B, and so on are chosen at random.";
"4 '$this/$npc'  OBJ  Set the element as THIS or the NPC.";
"5 '$N'          ANY  Set the element to the N'th event argument.";
"6  $this/$npc   STR  Substitue tostr(THIS/NPC) into the element.";
"7 ';STR'        ANY  Set the element to the result of the evaluated STR.";
"- -------       ---  -----------";
"The parsing is performed in the order listed above, so substitutions higher on the table will be available for later processing.  This is especially useful for $this and $npc in a later eval.";
"A full-element substitution (4, 5, 7) will not fall through to subsequent parsings.";
npc = args[1];
rx_list = args[2];
rx_args = rx_list[6] || {};
event_args = args[3];
match_result = args[4];
if (!rx_args)
return {};
endif
if (this:is_text_reaction(rx_list))
rx_args = this:substitute_match(rx_args, match_result[2]);
endif
b = {};
l = length(event_args);
for e in (rx_args)
e = this:parse_brackets(e);
if (e == "$this")
e = this;
elseif (e == "$npc")
e = npc;
elseif ((e[1] == "$") && ((n = tonum(e[2])) <= l))
e = event_args[n];
elseif (e[1] == ";")
e[1..1] = "";
e = strsub(e, "$this", tostr(this));
e = strsub(e, "$npc", tostr(npc));
"...should probably insert printed $N arg substitution here...";
v = npc:my_eval_d(e);
e = v[1] ? v[2] | e;
endif
b = {@b, e};
endfor
return b;
.
#104:8
":reaction_packet(npc, rx-list, event-args, args-match-result)";
"Here we compile the list that will be returned and interpreted to the NPC.";
"These are the last 4 arguments in the reaction, commonly called the 'reaction list' or 'rx-list'.  They are:";
"       RX_PRIORITY   The importance of the reaction.";
"       RX_OBJECT     Where the RX_METHOD is located.";
"       RX_METHOD     The name of the verb to be called on the RX_OBJECT.";
"       RX_ARGS       The arguments to the verb call above.";
"Simple, eh?  See the appropriate routine for more detailed information on the parsing of each.";
p = this:reaction_priority(@args);
o = this:reaction_object(@args);
m = this:reaction_method(@args);
a = this:reaction_args(@args);
return {p, o, m, a};
.
#104:9
":is_text_reaction(rx-list[, match-result])";
"-> True if the given rx-list is a text-based reaction, such as motion, speech, sound, address, etc.";
return args[1][1] in {"motion", "sound", "speech", "address", "page"};
.
#104:10
":subsititute_match(reaction-args, match()-result)";
"Pronoun_Sub first, then substitute() each STR element of the reaction args.";
a = {};
m = args[2];
for e in (args[1])
if (typeof(e) != STR)
a = {@a, e};
else
e = $string_utils:pronoun_sub(e);
if (s = substitute(e, m))
a = {@a, s};
else
a = {@a, e};
endif
endif
endfor
return a;
.
#104:11
":add_ok(what)";
"Is it okay to add this reactor to the given object?";
return 1;
.
#104:12
":strsub_list(LIST string_list, LIST sub_pairs)";
l = {};
subs = args[2];
for e in (args[1])
for sub in (subs)
e = strsub(e, @sub);
endfor
l = {@l, e};
endfor
return l;
.
#104:13
":parse_brackets(string)";
"Return the given string, after parsing for the following:";
"$[option|option|option]";
"       Choose one option of the several delimited by pipe-bars between";
"       square brackets.";
e = args[1];
while (((f = index(e, "$[")) && (l = index(e, "]") || rindex(e, "]"))) && ((f + 3) <= l))
if (c = $string_utils:explode(e[f + 2..l - 1], "|"))
c = $list_utils:random_element(c);
else
c = "";
endif
e[f..l] = c;
endwhile
return e;
"!d -- For speed, ignoring type of argument.";
.
#105:0
"WIZARDLY";
if ((caller != $value_editor) || (caller_perms() != $value_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]);
endif
return this:text_from_value(text);
.
#105:1
"WIZARDLY";
if ((caller != $value_editor) || (caller_perms() != $value_editor.owner))
return E_PERM;
endif
set_task_perms(player);
value = this:value_from_text(args[2]);
if (value[1])
value = value[2];
else
player:tell("Couldn't parse text into a value: ", value[2]);
return E_INVARG;
endif
if (typeof(spec = args[1]) == OBJ)
return spec:set_text(value);
else
return spec[1].(spec[2]) = value;
endif
.
#105:2
":text_from_value(value)";
"Return a string or list of strings describing the given value.";
text = $string_utils:print(args[1]);
parts = {};
while (text && (m = match(text, "%(E_[A-Z]+%|\"%|}%|#[0-9]+%|^[ 0-9]+%)%(,%)", 1)))
i = m ? m[3][2][1] | 0;
if (i)
parts = {@parts, text[1..i]};
endif
text = text[i + 1..length(text)];
endwhile
return parts ? {@parts, text} | text;
.
#105:3
":value_from_text(text)";
"Returns the value given text describes.";
text = args[1];
if (typeof(text) == LIST)
value = $string_utils:to_value(tostr(@text));
else
value = $string_utils:to_value(text);
endif
return value;
.
#105:4
"display";
"View the value, with #xxxx instances replaced with the object's ID.";
text = this:text(player in this.active);
if (typeof(text) == LIST)
for line in (text)
while (m = match(line, "%(#%([1-9][0-9]*%)%)"))
o = toobj(substitute("%1", m));
if ($recycler:valid(o))
line[m[3][1][1]..m[3][1][2]] = tostr("$", $string_utils:lowercase(strsub(o.name, " ", "_")), ":", substitute("%2", m));
else
line[m[3][1][1]..m[3][1][2]] = tostr("$", "invalid:", substitute("%2", m));
endif
endwhile
player:tell(line);
endfor
else
player:tell("Text unreadable:  ", text);
endif
player:tell("--------------------------");
.
#106:0
this:do_consume(player, 1);
.
#107:0
r = max(toint(tofloat(this.remaining_portions) * 0.1), 1);
return this:do_consume(player, r);
.
#107:1
r = max(toint(tofloat(this.remaining_portions) * 0.3), 1);
return this:do_consume(player, r);
.
#107:2
return this:do_consume(player, 1);
.
#107:3
r = this.remaining_portions;
return this:do_consume(player, r);
.
#108:0
":relative_volume(current, total)";
"-> A string describing the fraction of 'total' that is 'current'.";
{cur, max} = args;
if (!cur)
return "none";
elseif ((max / cur) == 2)
return "half";
elseif (cur == max)
return "all";
endif
div = tofloat(cur) / tofloat(max);
if (div > 0.8)
return "almost all";
elseif (div > 0.7)
return "about three-quarters";
elseif (div > 0.6)
return "about two-thirds";
elseif (div > 0.5)
return "more than half";
elseif (div > 0.4)
return "about half";
elseif (div > 0.3)
return "about one-third";
elseif (div > 0.2)
return "about one-quarter";
else
return "almost none";
endif
.
#108:1
"A phrase describing how much of the consumable remains.";
return $string_utils:capitalize(this:relative_volume(this.remaining_portions, this.total_portions) + " of it remains.");
.
#108:2
"Standard message preparation.  $left is substituted with a phrase describing how much of the consumable remains.";
msg = this:get_message(verb[1..length(verb) - 4]);
if (!msg)
return "";
endif
return $string_utils:pronoun_sub(msg, @args);
.
#108:3
"consume this -- Eat it, baby.  Drink it, mama.  Consume.";
this:do_consume(player, 1);
.
#108:4
":empty_action(who)";
"What happens when who tries to consume and nothing remains.";
":consume_action(who)";
"What happens when who successfully consumes some of the object.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
return this:announce_messages(strsub(verb, "_action", ""));
.
#108:5
":finish_action(who)";
"What happens when who finishes off the consumable.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
this:announce_messages("finish");
if (this.destroy_when_finished)
return this:destroy();
endif
aliases = this:finished_aliases();
this:set_name(aliases[1]);
this:set_aliases(aliases);
.
#108:6
":destroy() -- Destroy this object IF:";
"perms are secure, of course";
".destroy_when_finished is true";
"this object does not have children, and is not fertile, has no verbs, etc";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
elseif (!this.destroy_when_finished)
return E_NACC;
elseif (((this.f || children(this)) || verbs(this)) || properties(this))
return E_NACC;
else
return $recycler:_recycle(this);
endif
.
#108:7
":_consume(who, portions)";
"Reduce the portions left by the given portions consumed, and queue or report any effects.";
"If there is nothing left, return E_NONE.  Otherwise, return the portions remaining.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
remains = this.remaining_portions;
if (!remains)
return E_NONE;
endif
gobbler = args[1];
gobbles = min(args[2], remains);
return this.remaining_portions = remains - gobbles;
.
#108:8
if (!this:is_readable_by(caller_perms(), caller))
return E_PERM;
endif
msg = pass(@args);
if (typeof(msg) == STR)
msg = strsub(msg, "$left", this:left_msg());
endif
return msg;
.
#108:9
":set_remaining_portions(NUM) -> Duh.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
return this.remaining_portions = args[1];
.
#108:10
"@set-portions consumable to TOTAL-PORTIONS";
"@set-portions consumable to REMAINING-PORTIONS/TOTAL-PORTIONS";
"The portions should be positive integers.  First syntax sets total portions, and replenishes remaining portions to that amount.  Second syntax sets both remaining and total portions.";
if (!this:is_writable_by(player))
player:tell("You aren't allowed to set the portions of ", this:dname(), ".");
elseif (m = match(iobjstr, "^%([0-9]+%)%(/%([0-9]+%)%)?$"))
one = substitute("%1", m);
two = substitute("%3", m);
t = two ? tonum(two) | tonum(one);
r = two ? tonum(one) | t;
this.total_portions = t;
this.remaining_portions = r;
if (r == t)
this:_replenish();
endif
player:tell(this:dnamec(), " now has ", $string_utils:english_number(t), " total portions.  There ", (r == 1) ? "is" | "are", " ", $string_utils:english_number(r), " remaining.");
else
player:tell_lines($code_utils:verb_documentation());
endif
.
#108:11
"If no portions remain, return the finished_msg instead of the normal description.";
if (!this.remaining_portions)
return this:finished_description(@args);
endif
desc = pass(@args);
desc = (typeof(desc) == LIST) ? desc | {desc};
left = this:left_msg();
for i in [1..length(desc)]
desc[i] = strsub(desc[i], "$left", left);
endfor
return desc;
.
#108:12
"What the object looks like when no portions remain.";
return this.(verb);
.
#108:13
"What the object is called when no portions remain.";
return this.(verb) || this.aliases;
.
#108:15
"Record the 'not finished aliases' for when the consumable is replenished.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
aliases = args[1];
if (((!this.remaining_portions) && (aliases == this.finished_aliases)) && (this.aliases != aliases))
this.not_finished_aliases = this.aliases;
endif
return pass(@args);
.
#108:16
"@set-finished-aliases consumable to \"Name,Alias,Alias\"";
"Set the names to which this object will be changed when no portions remain.";
if (!this:is_writable_by(player))
player:tell("You aren't allowed to set the finished names of ", this:dname(), ".");
elseif (!iobjstr)
player:tell("You must give a set of names separated by commas.");
else
names = $string_utils:explode(iobjstr, ",");
names = $list_utils:remove_duplicates(names);
names = $list_utils:map_arg($string_utils, "trim", names);
this.finished_aliases = names;
player:tell(this:id(), " will be renamed to ", names[1], " ", listdelete(names, 1) ? ("(aka " + $string_utils:english_list(listdelete(names, 1))) + ")" | "", " when no portions remain.");
endif
.
#108:17
":_replenish()";
"Set remaining portions equal to total portions, and perform any transmutations that need be done.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
this.remaining_portions = this.total_portions;
aliases = this:not_finished_aliases();
this:set_name(aliases[1]);
this:set_aliases(aliases);
.
#108:18
"@set-finished-description consumable to \"What this consumable looks like when no portions remain.\"";
if (!this:is_writable_by(player))
player:tell("You aren't allowed to set the finished description of ", this:dname(), ".");
else
this.finished_description = iobjstr;
player:tell(this:id(), " will appear as \"", iobjstr, "\" when no portions remain.");
endif
.
#108:19
"@replenish -- Restore all portions to this consumable.";
if (!this:is_controllable_by(player))
player:Tell("You can't do that.");
else
this:_replenish();
player:tell($string_utils:nn(this), " now has all its portions.");
endif
.
#108:20
":do_consume(consumer, portions)";
{?consumer = player, ?portions = 1} = args;
if (this.location != consumer)
consumer:tell("You don't have ", this:dname(), ".");
return E_RANGE;
elseif (this.f)
consumer:tell("You can't consume that one!");
return E_NACC;
endif
result = this:_consume(consumer, portions);
if (result == E_NONE)
this:empty_action(consumer, portions);
elseif (result)
this:consume_action(consumer, portions);
else
this:finish_action(consumer, portions);
endif
.
#109:0
":damage_mod(damage[, victim, weapon, attacker])";
"-> Percent of raw damage that will be felt as the result of a hit to this area.";
return this.damage_mod;
.
#109:1
":mod_damage(damage[, victim, weapon, attacker])";
"-> Actual damage received from a hit to this body area.";
dam = args[1];
return (dam * (this:damage_mod(@args) || 1)) / 100;
.
#109:2
":max_damage(damage[, victim, weapon, attacker])";
"-> Maximum damage that can be taken by this body area.  For example: Even fully amputating a hand will not usually cause a mortal wound.";
return this.max_damage;
.
#109:3
":absorb_damage(damage, victim, weapon, attacker)";
"Return damage modified for local severity, maximum damage for a body area.";
"Special effects and messages may be inflicted: For example, a hit to the head may temporarily blind a character.";
dam = this:mod_damage(@args);
max = this:max_damage(@args);
if (max && (dam > max))
"...too much damage, possible DISMEMBERMENT...";
dam = max;
endif
return max(dam, 1);
.
#109:4
":called_shot_mod([body, attacker, weapon])";
"Modifier applied to a shot aimed at this area, on the given body, by the given attacker, with the given weapon.  The lower the modifier, the harder the area is to hit.";
"If the modifier is a FLOAT value, then the final modification is a function of that percentage of the attacker's actual skill.";
if (this.called_shot_mod == $body_area.called_shot_mod)
"derive a new one";
mod = -(((100 - this.percent_of_body_area) / 10) * 3);
else
mod = this.called_shot_mod;
endif
{target, attacker, weapon} = args;
"amplify the modifier for size differences";
minsize = min(attacker.v_size, target.v_size);
maxsize = max(attacker.v_size, target.v_size);
sizemod = tofloat(maxsize) / tofloat(minsize);
if (typeof(mod) != FLOAT)
mod = toint(tofloat(mod) * sizemod);
return mod;
else
mod = mod / sizemod;
endif
total = attacker:total(weapon:skill(attacker, target));
if (total < 0)
return 0;
endif
mod = max(mod, 0.0);
mod = toint(tofloat(total) * mod) - total;
return mod;
.
#109:5
":is_fatal_strike(victim, damage, attacker)";
"Is the attack described by the args a fatal one, when received by this body area?";
"Note that rarely will a single strike to (for example) a hand result in death.  That injury will perhaps _lead_ to death, but the injury itself will not result in an instantaneous demise.";
"This verb is called only when death would normally result.  Therefore, the .fatality_chance should be a chance of any mortal wound resulting in instant death.";
"So (for example), if a head body area has a fatality_chance of 100, not EVERY strike to the head will result in death.";
return this.fatality_chance > random(100);
.
#109:6
":is_knockout_strike(victim, damage, attacker)";
"See is_fatal_strike.  Same, but with knockouts.";
return this.knockout_chance > random(100);
.
#109:7
":alt_unconsciousness(victim, damage, attacker)";
"See same verb on $combatant for purpose and return values.";
if (this:is_knockout_strike(@args))
return 0;
else
return -1;
endif
.
#109:8
":alt_death(victim, damage, attacker)";
"See same verb on $combatant for purpose and return values.";
if (this:is_fatal_strike(@args))
return 0;
elseif (p = this.affects_upper_mobility)
c = args[1];
m = c:mod_stat("upper_mobility", -((args[2] * p) / 100), 0);
msg = tostr("%P body does not react well to the strike, submitting to a ", (m < 25) ? "major " | ((m > 75) ? "minor " | "moderate "), "crippling of the upper extremities.");
if (w = c:wielding())
room = c.location;
for i in (w)
i:moveto(room);
endfor
msg = tostr(msg, "  The sudden upper body trauma causes %n to drop %p ", $string_utils:name_list(w), ".");
endif
$you:say_action(msg, c);
return 1;
elseif (p = this.affects_lower_mobility)
c = args[1];
m = c:mod_stat("lower_mobility", -((args[2] * p) / 100), 0);
msg = tostr("%N %<staggers> with the force of the strike, body struggling with a ", (m < 25) ? "major " | ((m > 75) ? "minor " | "moderate "), "crippling of the lower extremities.");
$you:say_action(msg, c);
return 1;
else
return -1;
endif
.
#109:9
player:tell(this:name());
player:tell("----------------:");
O = $rpg;
V = "build_stat_line";
player:tell(O:(V)(this, "Damage Modifier", "damage_mod", this.damage_mod, 15, 21));
player:tell(O:(V)(this, "Maximum Damage", "max_damage", ((d = this.max_damage) == $maxint) ? "$maxint" | d, 15, 21));
player:tell(O:(V)(this, "Called Shot Mod", "called_shot_mod", this.called_shot_mod, 15, 21));
player:tell(O:(V)(this, "Body Area", "percent_of_body_area", tostr(this.percent_of_body_area, "%"), 15, 21));
player:tell(O:(V)(this, "Fatality Chance", "fatality_chance", tostr(this.fatality_chance, "%"), 15, 21));
player:tell(O:(V)(this, "Knockout Chance", "knockout_chance", tostr(this.knockout_chance, "%"), 15, 21));
player:tell(O:(V)(this, "Mobility Affect", "affects_upper_mobility", tostr(this.affects_upper_mobility, "%"), 15, 21));
player:tell(O:(V)(this, "", "affects_lower_mobility", tostr(this.affects_lower_mobility, "%"), 15, 21));
.
#109:10
":bleeding_mod([INT bleeding[, OBJ victim, OBJ weapon, OBJ attacker]])";
"Return a float describing a modification to the base bleeding added by a weapon for a hit to this body area.";
return this.bleeding_mod;
.
#109:11
":mod_bleeding(INT bleeding[, OBJ victim, OBJ weapon, OBJ attacker])";
"Return a float describing a modification to the base bleeding added by a weapon for a hit to this body area.";
bleeding = args[1];
return toint(tofloat(bleeding) * this:bleeding_mod());
.
#110:0
":get_trustees(who)";
"Return a list of everyone trusted by who to perform this trust db's action.";
{truster} = args;
return truster:get_trustees(this);
.
#110:1
":trusts(object, actor)";
"Does object trust actor to do whatever action this trust db governs?";
trustees = this:get_trustees(args[1]);
return ((args[2] in trustees) || ($everyone in trustees)) ? 1 | 0;
.
#110:2
":add_trusted_object(truster, trustee)";
"Trust trustee to perform this trust-db action upon truster.";
{truster, trustee} = args;
if (!truster:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
return truster:add_trusted_object(trustee, this);
.
#110:3
":remove_trusted_object(truster, trustee)";
"No longer trust trustee to perform this trust-db action upon truster.";
{truster, trustee} = args;
if (!truster:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
return truster:remove_trusted_object(trustee, this);
.
#110:4
":is_virtual_action()";
"Is this action virtually real, or some manipulation of a technical nature?";
"If it IS real, return the complexity (or obviousness) of the action.";
"Else just return false.";
return this.virtual;
.
#110:5
":do_while_unconscious(actor, subject)";
"Is the action able to be performed while the subject is unconscious?  Print any necessary messages and return true if execution continues, or false if it is to halt.";
"This method assumes that subject does not already trust actor.";
"Return 0 if this is not a 'virtual action'!  Only virtual actions should be affected by conscious state!";
{actor, victim} = args;
unc = victim:unconscious();
virtual = this:is_virtual_action();
if (!virtual)
return 0;
elseif (unc < 0)
if ((unc + virtual) > 0)
"...Oops!  They woke up!";
$you:say_action(("%N %<tries> to " + this.name) + " %[tpo], but %<wakes> %[tpo] up in the process!", @args);
victim.unconscious = 0;
return 0;
endif
return 1;
elseif (unc > 0)
"...No chance of waking.";
return 1;
else
"...Huh?  Why the Hell are you calling this?";
return 0;
endif
.
#110:6
":is_critical_action()";
"A critical action is one that must be trusted implicitly, and cannot be trusted as part of a 'trust everyone'.";
return this.critical_action;
.
#110:7
if ($code_utils:verb_loc() != this)
return {};
endif
return children(this);
.
#110:8
if (caller_perms().wizard)
pass();
for p in (properties(this))
if (`p[1..2] ! E_RANGE => 0' == "_#")
delete_property(this, p);
endif
endfor
endif
.
#111:0
":set_amount(NUM amt)";
"-> Set the quantity of this collective, also modifying its name and aliases.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
elseif (this == this.root)
return E_NACC;
endif
amt = args[1];
this:set_name(this:cname(amt));
this:set_aliases(this:caliases(amt));
this.amount = amt;
if (!amt)
this:destroy();
endif
return amt;
.
#111:1
":description()";
"-> If the root's description is set, allow the verb to pass.";
"-> Else, return the substituted :cdesc.";
if (is_clear_property(this.root, "description"))
return this:cdesc();
else
return this:_csubs(pass(@args));
endif
.
#111:2
":set_cname(string) -> Set the name template of this collective.";
":set_cdesc(string) -> Set the description for descendants with a clear root description.";
"$amount is replaced with the current quantity.";
"$what is replaced with the 'denomination'.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
return this.(verb[5..length(verb)]) = args[1];
.
#111:3
":set_cregex(string)";
"-> A regular expression by which to match this collective.";
"$amount is replaced with the current quantity.";
"$what is replaced with the 'denomination'.";
"The regex amount should be the first parenthesized expression.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
elseif (typeof(match(this.name, args[1])) == ERR)
return E_INVARG;
endif
return this.cregex = args[1];
.
#111:4
":matches(string) -> Does the string refer to this collective?";
if (passed = pass(@args))
return passed;
endif
amt = this:amount_from_string(args[1]);
return (amt == this.amount) ? 1 | ((amt < this.amount) ? -1 | 0);
.
#111:5
":cregex()";
"A regular expression describing this collective's name and aliases.";
"It is set to a regexp representation of aliases in :set_aliases.";
return this.cregex || tostr("%(", this.what, "%)");
.
#111:6
":cname([INT amount])";
"-> Name of this object, adjusted for current (or given) amount.";
":cdesc([INT amount])";
"-> Description of this object, adjusted for current (or given) amount.";
return this:_csubs(this.(verb), @args);
.
#111:7
":name()";
"If the collective is in motion, transit information will be stored in this.pending so that :title calls are correct.";
pending = this.pending;
if (!pending)
"...not moving...";
elseif (pending[1] != task_id())
this.pending = 0;
else
return this:cname(pending[2]);
endif
return pass(@args);
.
#111:8
":amount_from_string(string)";
"=> How much of the collective does string refer to?";
string = args[1];
if (string == this.what)
"a singular one of the collective";
return 1;
elseif (index(string, this.what) == 1)
"all of them";
return this.amount;
elseif (amount = this:parse_vague_amount(string))
return amount;
endif
return E_INVARG;
.
#111:9
":moveto_transfer(dest)";
"Using the value of dobjstr, find out how much of the collective is being moved, then call :transfer to do the work.";
"Return true if there's no need to move the object.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
amt = this:amount_from_string(dobjstr);
if (!amt)
"...assume it's the whole amount, moved by some programmatical means...";
return this:transfer(this.amount, args[1]);
else
return this:transfer(amt, args[1]);
endif
.
#111:10
":find_like_collective(where)";
"Attempt to find a collective of the same denomination in the given location.";
"Return the valid collective if found, else return $failed_match.";
for o in (args[1]:contents())
if ((parent(o) == this.root) && (o != o.root))
return o;
endif
endfor
return $failed_match;
.
#111:11
":transfer(amount, dest)";
"Move 'amount' portion of this collective to the given destination.";
"-> If the amount is greater than what this collective contains, return E_RANGE.";
"-> If this object is its own root, return E_RECMOVE.";
"-> If the dest will not accept this object, return E_NACC.";
"-> If there is no like collective of like kind in the destination, create a new one of the given amount.  If all of the amount is being transferred, just move the object.";
"-> If there is a collective, add the the transferred amount to its total quantity.  If all of this object is being transferred, fold the dest object's amount into this amount, and recycle THAT object.";
"If a new object is needed and cannot be created, raise an E_QUOTA traceback.";
"";
"If the payment succeeds 'payment' event is sent to the destination, with the following args:";
"  ;dest:hear_event_payment(old_loc, new_loc, amount, root)";
"Where: old_loc is where this item is NOW (before the move),";
"       new_loc is where it is after (its destination),";
"       amount  is the amount transferred,";
"   and root    is this root collective of this object.";
"I realize 'payment' is not the best term for this transaction, but it _is_ the most compact I can think of at this time.";
here = this.location;
dest = args[2];
amt = args[1];
if (amt > this.amount)
return E_RANGE;
elseif (this == this.root)
return E_RECMOVE;
endif
root = this.root;
if (!valid(like = this:find_like_collective(dest)))
if (amt == this.amount)
this:_move(dest);
if (this.location != dest)
$error:raise(E_NACC);
endif
this.pending = {task_id(), amt};
"return 1;";
elseif (typeof(new = root:spawn(amt)) == OBJ)
this:set_amount(this.amount - amt);
new:_move(dest);
if (new.location != dest)
new:destroy();
$error:raise(E_NACC);
endif
this.pending = {task_id(), amt};
"return 1;";
else
player:tell(new);
$error:raise(E_QUOTA);
endif
else
if (amt == this.amount)
this:set_amount(this.amount + like.amount);
like:destroy();
this:_move(dest);
else
like:set_amount(like.amount + amt);
this:set_amount(this.amount - amt);
endif
this.pending = {task_id(), amt};
"return 1;";
endif
dest:hear_event_payment(here, dest, amt, root);
return 1;
.
#111:12
":moveto(dest)";
"Adjust the amount of this and any destination object, using current value of dobjstr to determine how much was moved.";
dest = args[1];
if ((this != this.root) && this:moveto_transfer(dest))
"...:moveto_transfer returned true, thus handled the move...";
else
return pass(@args);
endif
.
#111:13
"Cross wires to the succeeded messages if the collective WAS actually transferred.";
if (this:in_transit())
return this:(strsub(verb, "failed", "succeeded"))(@args);
else
return pass(@args);
endif
.
#111:14
"Cross wires to the succeeded messages if the collective WAS actually transferred.";
if (this:in_transit())
return this:(strsub(verb, "failed", "succeeded"))(@args);
else
return pass(@args);
endif
.
#111:15
return $string_utils:pronoun_sub(this.(verb), @args);
.
#111:16
"Cross wires back to the iobj if the collective WAS actually transferred.";
if (this:in_transit())
return iobj:(strsub(verb, "_refused", ""))(@args) || "";
else
return iobj:(strsub(verb, "refused", "fail"))(@args) || "";
endif
.
#111:17
":caliases([amt])";
"Aliases for the collective, derived from :cname(@args).";
return $string_utils:aliases_from_name(this:cname(@args));
.
#111:18
":_move(dest)";
"Perform a wizardly move to the destination.";
"Moves are allowed only by wizards or $scrooge.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
dest = args[1];
"dest:bless_for_entry(this);";
move(this, dest);
"--WiZARDLY--";
.
#111:19
":spawn([new_amount])";
"Spawn a child of this root, owned by the owner of this verb's location.";
"Wizardly to circumvent any fertility problems.";
cp = caller_perms();
if (!$rpg:trusted(cp))
return E_PERM;
endif
new = $recycler:_create(this.root, $collective.owner);
if (args)
new:set_amount(tonum(args[1]));
endif
return new;
"--WiZARDLY--";
.
#111:20
":destroy()";
"Destroy this collective object, if it isn't unique.";
if ((caller != this) && (caller_perms() != $code_utils:verb_perms()))
return E_PERM;
elseif ((((this == this.root) || this.f) || children(this)) || verbs(this))
return E_NACC;
endif
return $recycler:_recycle(this);
.
#111:21
":in_transit()";
"Return true if the object is being transferred.";
return this.pending && $code_utils:task_valid(this.pending[1]);
.
#111:22
":was_moved(old_loc, new_loc)";
"Always return true.  Shouldn't be much of a problem, even if we're wrong.";
return 1;
.
#111:23
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
"Every corified collective should have itself as its root.";
this.root = this;
.
#111:24
"Copied from crystal (#2383):do_throw by Hacker (#38) Thu Feb 18 08:36:20 1999 CST";
"Disable throwing until it can be fitted to the collective.";
player:tell("You can't throw that.");
.
#111:25
":_csubs([INT amount])";
{text, ?amt = this.amount} = args;
if (typeof(text) == LIST)
done = {};
for s in (text)
done = {@done, this:(verb)(s)};
endfor
return done;
endif
return strsub(strsub(text, "$what", $string_utils:pluralize(this.what, amt)), "$amount", tostr(amt));
.
#111:26
":parse_vague_amount(string)";
"=> Parse a possibly nonspecific amount referenced of this collective.";
string = args[1];
m = match(string, "^%(%([1-9][0-9]*%)%( units%)?%|all%|half%)%( of %)?%(%(the%|a%|an%) %)? *" + this:cregex());
if (!m)
return E_INVARG;
elseif (m[3][2][1])
return toint(substitute("%2", m));
elseif (m[3][1])
amtstr = substitute("%1", m);
amtnow = this.amount;
if (amtstr == "half")
return amtnow / 2;
else
return amtnow;
endif
else
return E_INVARG;
endif
.
#111:27
if (!($perm_utils:controls(caller_perms(), this) || (this == caller)))
return E_PERM;
elseif (typeof(aliases = args[1]) != LIST)
return E_TYPE;
elseif (!(result = pass(@args)))
return result;
else
aliases = {};
for a in (setadd(this:spawned_aliases(), this:spawned_name()))
if (!index("0123456789", a[1]))
"don't include aliases beginning with a number";
a = $string_utils:regexp_quote(a);
if (a[$] == "s")
"cheap way of allowing the singular";
a = a + "?";
endif
aliases = {@aliases, a};
endif
endfor
cregex = tostr("%(", $string_utils:from_list(aliases, "%|"), "%)");
if (cregex != this.cregex)
this.cregex = cregex;
endif
return result;
endif
.
#112:0
":all_global()";
"Return a two-element list of all properties (readable by the caller) on $sys and their values.";
set_task_perms(caller_perms());
what = args[1];
g = v = {};
for pname in (properties($sys))
if ((value = $sys.(pname)) != E_PERM)
g = {@g, pname};
v = {@v, value};
endif
endfor
return {g, v};
.
#112:1
":is_global_object(what)";
"Can what be referenced as a $property on $sys?";
set_task_perms(caller_perms());
what = args[1];
sys = $sys;
for p in (properties(sys))
if (sys.(p) == what)
return p;
endif
endfor
.
#112:2
":dbref(obj)";
"Return either #XXX, or $XXX if the object is a system global.";
o = args[1];
g = $core_utils:is_global_object(o);
return g ? "$" + g | tostr(o);
.
#112:3
if (!caller_perms().wizard)
raise(E_PERM, "Only a wizard may perform a core extraction.");
endif
if (player != $owner)
return E_PERM;
endif
saved = {$sys, $owner};
saved_props = {"sys", "owner"};
wizards = {$owner};
"----------------------------------------";
for p in (properties(#0))
$command_utils:suspend_if_needed(0);
v = #0.(p);
if ((typeof(v) != OBJ) || (!valid(v)))
continue;
endif
saved = {@saved, v};
saved_props = {@saved_props, p};
if (v.wizard)
wizards = setadd(wizards, v);
endif
endfor
"----------------------------------------";
for o in (saved)
$command_utils:suspend_if_needed(0);
for p in (`o:include_with_core() ! ANY => {}')
if ((typeof(p) == OBJ) && valid(p))
saved = setadd(saved, p);
endif
endfor
endfor
"----------------------------------------";
for o in (saved)
$command_utils:suspend_if_needed(0);
p = parent(o);
while (valid(p))
saved = setadd(saved, p);
p = parent(p);
endwhile
for p in (`o:include_with_core() ! ANY => {}')
if ((typeof(p) == OBJ) && valid(p))
saved = setadd(saved, p);
endif
endfor
endfor
"----------------------------------------";
for p in ({"mcd_pos", "mcd_saved", "mcd_wizards"})
`delete_property(this, p) ! E_PROPNF';
endfor
add_property(this, "mcd_pos", #0, {player, "r"});
add_property(this, "mcd_saved", {saved, saved_props}, {player, "r"});
add_property(this, "mcd_wizards", wizards, {player, "r"});
.
#112:4
if (!caller_perms().wizard)
raise(E_PERM, "Only a wizard may perform a core extraction.");
endif
if (player != $owner)
return E_PERM;
endif
for t in (queued_tasks())
kill_task(t[1]);
endfor
.
#112:5
if (!caller_perms().wizard)
raise(E_PERM, "Only a wizard may perform a core extraction.");
endif
if (player != $owner)
return E_PERM;
endif
for i in [1..length(verbs(player))]
delete_verb(player, "0");
endfor
for p in (properties(player))
delete_property(player, p);
endfor
chparent(player, $wiz);
for p in ($object_utils:all_properties(player))
player.(p) = $wiz.(p);
endfor
player.name = "God";
player.aliases = {"Wizard", "God"};
player.description = "";
player.key = 0;
player.password = 0;
$quota_utils:set_quota(player, 10000);
$gender_utils:set(player, "royal");
.
#112:6
if (!caller_perms().wizard)
raise(E_PERM, "Only a wizard may perform a core extraction.");
endif
if (player != $owner)
return E_PERM;
endif
{saved, saved_props} = this.mcd_saved;
max_obj = max_object();
for o in [#0..max_obj]
if ($command_utils:running_out_of_time())
player:notify(tostr("... preparing doomed object ", o, " of ", max_obj, " ..."));
suspend(0);
endif
if ((!valid(o)) || (o in saved))
continue;
endif
while (`delete_verb(o, "recycle") ! E_VERBNF' != E_VERBNF)
endwhile
while (`delete_verb(o, "exitfunc") ! E_VERBNF' != E_VERBNF)
endwhile
endfor
.
#112:7
if (!caller_perms().wizard)
raise(E_PERM, "Only a wizard may perform a core extraction.");
endif
if (player != $owner)
return E_PERM;
endif
fork (0)
this:mcd_reap_doomed();
endfork
start = this.mcd_pos;
{saved, saved_props} = this.mcd_saved;
max_obj = max_object();
count = 0;
for o in [start..max_obj]
this.mcd_pos = o;
if ((!valid(o)) || (o in saved))
continue;
endif
if ($command_utils:running_out_of_time())
notify(player, tostr("*** recycling ", o, " of ", max_obj, " ***"));
return;
endif
recycle(o);
endfor
this.mcd_pos = #-1;
.
#112:8
if (!caller_perms().wizard)
raise(E_PERM, "Only a wizard may perform a core extraction.");
endif
if (player != $owner)
return E_PERM;
endif
{saved, saved_props} = this.mcd_saved;
alist = {};
"----------------------------------------";
for p in (saved_props)
$command_utils:suspend_if_needed(0);
if (pair = $list_utils:assoc_suspended(#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)) && (!$list_utils:assoc_suspended(o, alist)))
renumber(o);
endif
endfor
"----------------------------------------";
reset_max_object();
.
#112:9
if (!caller_perms().wizard)
raise(E_PERM, "Only a wizard may perform a core extraction.");
endif
if (player != $owner)
return E_PERM;
endif
max_obj = max_object();
for o in [#0..max_obj]
suspend(0);
try
move(o, (o == #2) ? $player_start | #-1);
except e (ANY)
player:notify_lines($error:format_traceback(e));
endtry
if ($object_utils:has_callable_verb(o, "init_for_core"))
try
o:init_for_core();
except e (ANY)
player:notify_lines($error:format_traceback(e));
endtry
else
player:notify(tostr("*** ", o.name, " <", o, "> has no :init_for_core verb. ***"));
endif
if ($command_utils:running_out_of_time())
notify(player, tostr("*** Done initializing #", o, " of #", max_obj, " ..."));
endif
endfor
.
#112:10
if (!caller_perms().wizard)
raise(E_PERM, "Only a wizard may perform a core extraction.");
endif
if (player != $owner)
return E_PERM;
endif
for p in ({"mcd_pos", "mcd_saved", "mcd_wizards"})
`delete_property(this, p) ! E_PROPNF';
endfor
$player_class = $player;
$wiz_utils:initialize_owned();
for p in (players())
`$quota_utils:set_quota(p, 10000000) ! E_PROPNF';
endfor
$quota_utils:measure_all_users(0);
.
#112:11
if (!caller_perms().wizard)
raise(E_PERM, "Only a wizard may perform a core extraction.");
endif
if (player != $owner)
return E_PERM;
endif
GOD = player;
HACKER = $hacker;
{saved, saved_props} = this.mcd_saved;
wizards = this.mcd_wizards;
for o in (saved)
owner = o.owner;
if (owner in wizards)
owner = GOD;
elseif (owner in saved)
"we're OK";
else
owner = HACKER;
endif
o.owner = owner;
old_verbs = {};
for vnum in [1..length(verbs(o))]
if ((seconds_left() < 2) || (ticks_left() < 2000))
suspend(0);
player:notify(tostr("... processing verbs of core object ", o, " ..."));
endif
{owner, perms, names} = verb_info(o, vnum);
if (owner in wizards)
owner = GOD;
elseif (owner in saved)
"we're OK";
else
owner = HACKER;
endif
set_verb_info(o, vnum, {owner, perms, names});
if (index(names, "(old)"))
old_verbs = {vnum, @old_verbs};
endif
endfor
for vnum in (old_verbs)
delete_verb(o, vnum);
endfor
for p in ($object_utils:all_properties(o))
if ((seconds_left() < 2) || (ticks_left() < 2000))
suspend(0);
player:notify(tostr("... processing properties of core object ", o, " ..."));
endif
{owner, perms} = property_info(o, p);
if (owner in wizards)
owner = GOD;
elseif (owner in saved)
"we're OK";
else
owner = HACKER;
endif
set_property_info(o, p, {owner, perms});
endfor
endfor
.
#112:12
":install_bare_init_for_core(OBJ)";
"If an :init_for_core doesn't already exist on the given object, install a basic skeleton.";
if (!caller_perms().wizard)
return raise(E_PERM);
endif
{core_object} = args;
verbname = "init_for_core";
if (verbname in verbs(core_object))
return E_MAXREC;
endif
add_verb(core_object, {#2, "rxd", verbname}, {"this", "none", "this"});
set_verb_code(core_object, verbname, {"if (!caller_perms().wizard) return raise(E_PERM); endif pass();"});
return 1;
.
#114:0
":integration_separation_msg() -> A string to seperate look messages.";
return "  ";
.
#114:1
":integrating_contents()";
"-> A list of objects to possibly be integrated.";
return this:matching_contents();
.
#114:2
":integrators([object-list])";
"Returns either the empty list, or a list of {integrators, @text}.";
"'integrators' are objects accounted for by 'text'.  All info is from the :look_msgs (or appropriate integration verbname) of everything in the room.  A :look_msg should return any of the following:";
"If 'object-list' is given, it is used instead of this:integrating_contents().";
"0           -- Object is invisible.";
"text        -- A single string representing the object's appearance.";
"{@text}     -- A list of strings representing the object's appearance.";
"{{integrators}, @text}";
"      This last format is most useful when integrating objects such as furniture which 'contain' one or more players.  The first element is a list of all objects handled by the second element, which should describe the object and its occupants.";
contents = args ? args[1] | this:integrating_contents();
objects = {};
strings = {};
sep_msg = this:integration_separation_msg();
for o in (contents)
type = typeof(look_msg = this:get_integration_msg(o));
if (o in objects)
"Already handled.";
elseif (!this:integration_ok(o))
"Don't integrate it.";
elseif (look_msg == 0)
objects = {@objects, o};
elseif (!look_msg)
"Don't integrate it.";
elseif (type == STR)
if (look_msg && match(look_msg, "[A-Z]"))
objects = {@objects, o};
strings = {@strings, @strings ? {sep_msg} | {}, look_msg};
endif
elseif (type != LIST)
"Skip it.";
elseif (typeof(stuff = look_msg[1]) == LIST)
objects = {@objects, o, @stuff};
strings = {@strings, @strings ? {sep_msg} | {}, @listdelete(look_msg, 1)};
else
objects = {@objects, o};
strings = {@strings, @strings ? {sep_msg} | {}, @look_msg};
endif
endfor
return {objects, @strings};
.
#114:3
":integration_ok(object) -> True if object is allowed to integrate.";
return 1;
.
#114:4
":tell_description([desc])";
"Return contents taken care of by integration.";
i = this:integrators();
d = this:integrated_description(@listdelete(i, 1));
if (d)
player:tell_lines(d);
endif
return $set_utils:diff(this:contents(), i[1]);
.
#114:5
":look_self([brief])";
"Tell player what e sees when inside the room.";
this:tell_title();
if ((!args) || (!args[1]))
c = this:tell_description();
else
c = this:contents();
endif
this:tell_contents(setremove(c, player), this.ctype);
.
#114:6
":get_integration_msg(object) -> Integration info for 'object'.";
"This needs to be defined on ancestors, to determine what object property or verb needs to be referenced for its integration purposes.";
return "";
.
#114:7
":integrated_description([@integration-text])";
desc = this:description();
text = args || listdelete(this:integrators(), 1);
if (!text)
return desc;
elseif (typeof(desc) == LIST)
return {@desc, tostr(@text)};
elseif (this.integration_style == LIST)
return desc ? {desc, tostr(@text)} | {tostr(@text)};
else
return tostr(desc, this:integration_separation_msg(), @text);
endif
.
#114:8
":announce_mood()";
"See 'help mood' and 'help @mood' for details on mood.";
if (!this.mood)
return E_NONE;
endif
room = this:room();
people = room:occupants();
if (!people)
return E_NONE;
endif
mood = this:choose_mood();
if (!mood)
return;
elseif (index(mood, "%N") || index(mood, "%D"))
if (things = $set_utils:difference(this:contents(), people))
dobj = $list_utils:random_element(things);
else
dobj = $list_utils:random_element(people);
endif
$you:say_action(mood, $list_utils:random_element(people));
else
mood = $string_utils:pronoun_sub(mood, $list_utils:random_element(people));
room:announce_all(mood);
endif
.
#114:9
":tell_title()";
player:tell(this:titlec());
.
#114:10
":choose_mood()";
"Return a mood message to be displayed.";
return this.mood && $list_utils:random_element(this.mood);
.
#116:0
oname = args[1];
raw = args[2];
if (typeof(raw) == STR)
what = player:my_match_object(raw);
elseif (typeof(raw) == NUM)
return raw ? "You need to specify an object." | {oname, 0};
elseif (typeof(raw) == LIST)
return "This option must be an OBJECT.";
else
what = raw;
endif
option = verb[7..length(verb)];
parent = #0.(option);
if ($command_utils:object_match_failed(what, tostr(raw)))
return "";
elseif (!$object_utils:isa(what, parent))
return ("This option must be set to a descendant of $" + option) + ".";
else
return {oname, what};
endif
.
#116:1
what = verb[6..length(verb)];
value = this:get(@args);
default = (typeof(default = this.("default_" + what)) != E_PROPNF) ? default | #0.(what);
if ((typeof(value) == OBJ) && (value != default))
return {"", {$string_utils:nn(value), ("is the " + what) + " used by @dig."}};
else
return {0, {((("Custom " + what) + " unset; use ") + $string_utils:nn(default)) + "."}};
endif
.
#116:2
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
.
#116:3
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
.
#116:4
if (value = this:get(@args))
return {"", {tostr("@created objects initialized to \"", value, "\".")}};
else
return {0, {tostr("Custom @create flags unset; use \"", this.default_object_flags, "\".")}};
endif
.
#116:5
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.default_zone = $default_zone;
this.default_room = $rpg_room;
this.default_exit = $rpg_exit;
.
#117:0
":do_ANSI_test()";
"Display all available ANSI tokens to the player.";
if (!caller_perms().wizard)
return E_PERM;
endif
tokes = this.ANSI_Tokens;
codes = this.ANSI_Translations;
names = this.Token_Names;
reset = this.escape + "[0m";
for i in [1..length(tokes)]
notify(player, tostr(this.ANSI_Prefix, tokes[i], this.ANSI_Postfix, " -> ", codes[i], names[i], reset));
endfor
"WIZARDLY";
.
#117:1
":chr(ASCII_code)";
"Return the character associated with the given ASCII code.";
c = args[1];
return `call_function("chr", c) ! E_INVARG => this.ANSI_printables[c]';
.
#117:2
":entity(STR entity_name[, STR charset])";
"Return a (possibly multi-charcter) string representing the given entity.";
c = this:entity_code(@args);
if (typeof(c) == STR)
return c;
elseif (typeof(c) != INT)
raise(E_TYPE);
elseif (c >= 0)
return this:chr(c);
else
return this:chr_altpage(-c);
endif
.
#117:3
":strip_special(str[, HAG_SUB])";
"Return string with all HAGs changed to spaces and all mode controls changed to empty strings.";
"If the HAG_SUB string is given, HAGs will be replaced with that string instead of a space.";
"If HAG_SUB is a numeric '1', then leave all HAGS (~NNN) in that format.";
string = strsub(args[1], "~0", "");
while (m = match(string, "[^~]?%(~[!*%RGYBMCW]%)"))
i = m[3][1];
string[l = i[1]..i[2]] = "";
endwhile
hagsub = (length(args) > 1) ? args[2] | " ";
if (hagsub != 1)
while (m = match(string, "[^~]?%(~[0-9][0-9][0-9]%)"))
i = m[3][1];
string[l = i[1]..i[2]] = hagsub;
endwhile
endif
return string;
.
#117:4
":_length(str)";
"Return the final length of the given string, on the player's screen, after all emulation has been processed on their end.";
string = args[1];
if ((typeof(string) != STR) || (!index(string, "~")))
return length(string);
endif
return length(this:strip_special(string));
.
#117:5
":tokenize(STR code)";
s = "";
for code in (args)
if (code[1] == "/")
code[1..1] = "";
endif
if (i = code in this.Token_Names)
s = s + this.ANSI_Tokens[i];
endif
endfor
return s && ((this.ANSI_Prefix + s) + this.ANSI_Postfix);
.
#117:6
":get_entity_code(STR entity_name[, STR charset])";
"=>  INT   The code representing the character.";
"=> -INT   The character's code in the alternate codepage.";
"=>  STR   The character itself, rather than the code.";
{c, ?charset = this.default_charset} = args;
i = args[1] in this.entity_names;
if (!i)
raise(E_PROPNF);
endif
propname = tostr("entity_codes_", charset);
return this.(propname)[i];
.
#117:7
":chr_altpage(ASCII_code)";
"Return the character associated with the given ASCII code, wrapped in";
"characters which switch the active codepage.";
c = args[1];
"these codes shouldn't be hardcoded, and i'm not even sure it's the";
"proper way to switch codepages, of if `codepage switching' is the";
"proper term for this trick.";
return tostr(this:chr(14), this:chr(c), this:chr(15));
.
#119:0
"load weapon with ammo";
if (!player:is_holding(this))
player:tell(tostr("You aren't holding ", this:dname(), "."));
return E_RANGE;
endif
iobj = player:match_contents(iobjstr);
if ($match_utils:object_match_failed(iobj, iobjstr, player:contents()))
return;
elseif (!player:is_holding(iobj))
player:tell(tostr("You aren't holding any ", iobj:dname(), "."));
return E_RANGE;
elseif (!$object_utils:has_callable_verb(iobj, "do_load"))
player:tell(tostr(iobj:dnamec(), " doesn't wanna be loaded into ", this:dname(), "."));
return E_TYPE;
elseif (this:is_full())
player:tell(tostr("Warning!  ", this:dnamec(), " cannot hold more ammo."));
endif
player:queue_action((("load " + this:dname()) + " with ") + iobjstr, {this, "reload"}, {iobj, iobjstr}, 50 - player:quickness());
.
#119:1
":range_mod(shooter, target, exits)";
return length(args[3]) * -10;
.
#119:2
"Prevent the attack if rounds_shot and !ammo_remaining.";
if ((!this.rounds_shot) || (this:ammo_remaining() > 0))
return 1;
endif
this.rounds_shot = 0;
this:announce_messages("not_loaded", args[1]);
.
#119:3
"fire weapon at [<direction>:]target";
if (!player:is_wielding(this))
player:tell(tostr("You aren't wielding ", this:dname(), "."));
return;
elseif (this:ammo_remaining() < 1)
player:tell(tostr("Warning!  ", this:dnamec(), " is unloaded!"));
endif
if (parsed = $match_utils:parse_possessive_reference(iobjstr))
who_str = parsed[1];
loc_str = parsed[2];
else
who_str = iobjstr;
loc_str = "";
endif
t_info = this:match_target(who_str);
if (this:target_match_failed(t_info, who_str))
return;
endif
iobj = t_info[1];
r_mod = this:range_mod(player, iobj, t_info[4]);
nice = 0;
if (!loc_str)
player:queue_action((((verb + " ") + this:dname()) + " at ") + iobj:iname(), "shoot", {iobj, nice}, (20 - player:quickness()) + this:slowness(this, iobj));
elseif (valid(area = iobj:match_body_area(loc_str, 1)))
player:queue_action((((((verb + " ") + this:dname()) + " at ") + iobj:iname()) + "'s ") + area:name(), "shoot", {iobj, nice, area}, (30 - player:quickness()) + this:slowness(this, iobj));
else
player:tell(tostr("You can't find anything resembling ", $english:indef_art(loc_str), " \"", loc_str, "\" on ", iobj:dname(), "."));
return;
endif
this.rounds_shot = 1;
player:add_attacker(iobj);
.
#119:4
":match_target(target_spec)";
"Return a list of:";
"  OBJ   Match result.";
"  STR   Target spec.";
"  STR   Direction spec.";
"  LIST  Exits leading to the target object.  (if the match succeeded)";
target_spec = args[1];
if (i = index(target_spec, ":"))
dir = target_spec[1..i - 1];
tag = target_spec[i + 1..length(target_spec)];
else
dir = "";
tag = target_spec;
endif
exits = {};
where = this:room();
while (valid(where))
who = where:match_object(tag);
if (valid(who) || (!dir))
break;
endif
exit = where:match_exit(dir);
if (!valid(exit))
break;
endif
where = exit:dest();
exits = {@exits, exit};
endwhile
return {who, tag, dir, exits};
.
#119:5
":target_match_failed(target_match_result)";
result = args[1];
target = result[1];
if (target == $ambiguous_match)
player:tell(tostr("There are several targets", result[3] ? " in that direction " | " ", "matching the given description.  You'll have to be more specific."));
elseif (!valid(target))
player:tell(tostr("You can't get a bead on anything matching that description."));
elseif (!$object_utils:isa(target, $combatant))
player:tell(tostr(target:dnamec(), " isn't worth the trouble."));
elseif (player == target)
player:tell("Shoot yourself?  Aw...things aren't that bad.  Buck up, lil camper.");
else
return 0;
endif
return 1;
.
#119:6
":ammo_stat(stat_name[, @args])";
"-> The given weapon statistic for loaded (or default) ammo.";
if (valid(ammo = this:ammo_loaded()))
return $no_one:call_verb(ammo, args[1], listdelete(args, 1));
else
return this.(args[1]);
endif
.
#119:7
":weapon_stat()";
"Return the stats for loaded ammo if shots have been fired.";
if (this.rounds_shot && valid(this:ammo_loaded()))
return this:ammo_stat(verb, @args);
else
return pass(@args);
endif
.
#119:8
":combat_messages()";
"Return the message set of this weapon's ammo if it has been fired.";
if (this.rounds_shot)
return this:ammo_stat("combat_messages", @args);
else
return pass(@args);
endif
.
#119:9
":acceptable(what)";
"Return true only if the given object is of the correct ammo type.";
what = args[1];
if (!$object_utils:has_property(what, "ammo_type"))
return 0;
endif
return this:is_accepted_ammo_type(what.ammo_type);
.
#119:10
":is_accepted_ammo_type(STR|OBJ ammo_type)";
"Return true if this weapon will accept the ammo of the given type.";
"Hacked 5/10/96 to accept both string and object ammo types, and recognize accepted_ammo as possibly a list.";
ammo = args[1];
accepted = $string_utils:enlist(this.accepted_ammo);
if (typeof(ammo) == OBJ)
ancestors = $object_utils:ancestors(ammo);
for e in (accepted)
if (e in ancestors)
return 1;
endif
endfor
ammo = $code_utils:get_property_value(ammo, "ammo_type");
endif
return ammo in accepted;
.
#119:11
":ammo_loaded()";
"-> The ammo currently loaded in this weapon.";
if (c = this.contents)
return c[1];
else
return $nothing;
endif
.
#119:12
"Call the appropriate verbs on this weapon's loaded ammo object.";
if (valid(ammo = this:ammo_loaded()))
return $no_one:call_verb(ammo, "deplete", args);
else
"...do nothing, return -1 if no ammo loaded...";
return -1;
endif
.
#119:13
"Call the appropriate verbs on this weapon's loaded ammo object.";
if (valid(ammo = this:ammo_loaded()))
return $no_one:call_verb(ammo, "remaining", args);
else
return 0;
endif
.
#119:14
":finished_attack(wielder, target, area)";
left = this:deplete_ammo(this.rounds_shot);
this.rounds_shot = 0;
if (left)
return;
endif
"Maybe print some outta ammo messages here?";
.
#119:15
":is_full()";
"Is this weapon filled to capacity with ammo?";
loaded = 0;
for i in (this.contents)
loaded = loaded + ($object_utils:has_property(i, "amount") ? i.amount | 1);
endfor
return loaded >= capacity;
.
#119:16
":do_reload(ammo, round_spec)";
if (!player:is_holding(this))
player:tell(tostr("You aren't holding ", this:dname(), "."));
return E_RANGE;
endif
iobj = args[1];
iobjstr = args[2];
if (!player:is_holding(iobj))
player:tell(tostr("You aren't holding any ", iobj:dname(), "."));
return E_RANGE;
elseif (this:is_full())
player:tell(tostr(this:dnamec(), " cannot hold more ammo."));
return E_QUOTA;
endif
result = this:is_accepted_ammo_type(iobj) && iobj:do_load(this, iobjstr);
if (result)
this:announce_messages("load");
else
this:announce_messages("load_failed");
endif
.
#119:17
":::NOTE:::";
"Puppets will not be able to use this verb, as it has the same name as the earlier, preferred `SHOOT GUN AT TARGET`.  Use that syntax instead.";
temp = dobjstr;
dobjstr = iobjstr;
iobjstr = temp;
i = "with" in args;
if (!i)
return this:(verb)("at", @args[i + 1..length(args)]);
else
return this:(verb)(@args[1..i - 1], "at", @args[i + 1..length(args)]);
endif
.
#119:18
"unload weapon";
"unload ammo from weapon";
if (!player:is_holding(this))
player:tell(this:grammar_sub("You aren't holding %(dname)."));
return E_RANGE;
elseif (!this:is_unloadable_by(player))
player:tell(this:grammar_sub("You can't seem to unload %(dname)."));
return E_NACC;
endif
dobj = dobjstr ? this:match_contents(dobjstr) | this:ammo_loaded();
if ($match_utils:object_match_failed(dobj, dobjstr, this:contents(), "in " + this:dname()))
return;
elseif (!$object_utils:has_callable_verb(dobj, "do_unload"))
player:tell(tostr(dobj:dnamec(), " doesn't wanna be ejected from ", this:dname(), "."));
return E_TYPE;
endif
player:queue_action((("unload " + dobj:dname()) + " from ") + this:dname(), {this, "unload"}, {dobj, dobjstr}, 40 - player:quickness());
.
#119:19
":do_unload(ammo, round_spec)";
if (!player:is_holding(this))
player:tell(tostr("You aren't holding ", this:dname(), "."));
return E_RANGE;
endif
dobj = args[1];
dobjstr = args[2];
result = dobj:do_unload(this, dobjstr);
if (result)
this:announce_messages("unload");
else
this:announce_messages("unload_failed");
endif
.
#119:20
"Show how much ammo is left.  Temporary verb.";
passed = pass(@args);
loaded = this:ammo_loaded();
if (!player:is_wielding(this))
return passed;
elseif (valid(loaded))
iobj = loaded;
player:tell(this:grammar_sub("%(dnamec) %<looks> to be loaded with %[iiname]."));
else
player:tell(this:grammar_sub("%(dnamec) %<is> unloaded."));
endif
.
#119:21
":set_rounds_shot()";
"Set how many rounds were shot at the last activation.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
this.rounds_shot = args[1];
.
#119:22
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.skill = $skill_db:match("blunt");
.
#119:23
":is_unloadable_by(pc)";
"Return true if this weapon can be unloaded by the given character.  Useful for items such as wands ('loaded' with magic), or high-tech items which might require some skill to unload.";
return this.unloadable;
.
#120:0
":is_configurable_by(who, caller, STR mode)";
"-> True if who can set messages and body areas on this object.";
if ("coverage" in args)
c = 1;
args = setremove(args, "coverage");
else
c = 0;
endif
if (c && (!$rpg:trusted(args[1])))
return 0;
endif
return pass(@args);
.
#120:1
who = args[1];
if (!$object_utils:isa(who, $character))
return E_INVARG;
else
clothing = $set_utils:union(@who:clothing());
armor = {};
for thing in (clothing)
if ($object_utils:isa(thing, $armour))
armor = {@armor, thing};
endif
endfor
return armor;
endif
.
#120:2
who = args[1];
armor = this:worn_armor(who);
for piece in (armor)
if ($set_utils:intersection(piece.worn_on, this.worn_on) != {})
msg = (player == who) ? "You cannot wear %[tdname] over your %d." | "%N cannot wear %[tdname] over %p %d.";
player:tell($string_utils:pronoun_sub(msg, who, this, who.location, dobj = piece));
return 0;
endif
endfor
return pass(@args);
.
#121:0
":add(alt_character, primary_character)";
"Add the given character as an alternate to the given primary.";
"-> E_MAXREC: Alt is either a primary character, or already an alternate.";
"-> E_NACC  : Given primary is an alternate.";
"-> E_INVARG: It just didn't work.";
"-> If succeeded, return a list of all alternate characters.";
if (!caller_perms().wizard)
return E_PERM;
endif
who = args[2];
alt = args[1];
alt_info = this:verify_alt_info(alt);
if (alt_info != 0)
return E_MAXREC;
endif
who_info = this:verify_alt_info(who);
if (who_info == 0)
who.alt_info = {alt};
elseif (typeof(who_info) == OBJ)
return E_NACC;
elseif (typeof(who_info) == LIST)
who.alt_info = setadd(who_info, alt);
else
return E_INVARG;
endif
alt.alt_info = who;
return who.alt_info;
.
#121:1
":all_alternates(character)";
"Return all alternate characters of the given character.";
if (!this:can_peek(caller_perms()))
return E_PERM;
endif
return this:verify_alt_info(args[1]) || {};
.
#121:2
":is_alternate_char(character)";
"Is the given character an alternate one?  If so, return a list of one element: the primary character.  Else return 0.";
if (!caller_perms().wizard)
return E_PERM;
endif
alt = args[1];
alt_info = this:verify_alt_info(alt);
return (typeof(alt_info) == OBJ) ? {alt_info} | 0;
.
#121:3
":delete(alt_character[, primary_character])";
"Remove the given character as a alternate of the given primary.";
"=> E_NONE: Given alt is not an alternate character.";
"=> E_INVARG: Given alt is not an alternate character of the given primary.";
"=> {OBJ}: The previous primary character.";
if (!caller_perms().wizard)
return E_PERM;
endif
{alt, ?primary = #-1} = args;
alt_info = this:verify_alt_info(alt);
if (typeof(alt_info) != OBJ)
return E_NONE;
elseif (valid(primary) && (alt_info != primary))
return E_INVARG;
else
primary = alt_info;
endif
clear_property(alt, "alt_info");
who_info = this:verify_alt_info(primary);
if (typeof(primary) == LIST)
primary.alt_info = setremove(who_info, alt);
endif
return {primary};
.
#121:4
":find(user)";
return pass(tostr(args[1]), @listdelete(args, 1));
.
#121:5
":primary_character_for(user)";
"Return the given user's primary character.";
if (!caller_perms().wizard)
return E_PERM;
endif
who = args[1];
who_info = this:verify_alt_info(who);
return (typeof(who_info) == OBJ) ? who_info | who;
.
#121:6
":new_alternate_ok_for(user)";
"May the given user create another alternate character?";
if (!caller_perms().wizard)
return E_PERM;
endif
user = args[1];
return length(this:all_alternates(user)) < this:max_alts_for(user);
.
#121:7
":max_alts_for(user)";
"How many alternate characters may the given user have?";
mod = $rpg:trusted(args[1]) ? 3 | 1;
return this.maximum_alternate_characters * mod;
.
#121:8
":verify_alt_info(who)";
"Verify the alternate character info for the given player.";
if (!caller_perms().wizard)
return E_PERM;
endif
who = args[1];
who_info = who.alt_info;
if (typeof(who_info) != LIST)
return who_info;
endif
for alt in (who_info)
alt_info = alt.alt_info;
if (alt_info == E_PROPNF)
who_info = setremove(who_info, alt);
elseif (alt_info != who)
who_info = setremove(who_info, alt);
endif
endfor
who.alt_info = who_info;
return who_info;
.
#121:9
":can_peek(who)";
"Return true if who has read access to alt information.";
{who} = args;
return who.wizard || (who in {$quota});
.
#123:0
":print_call(args[, to_whom])";
set_task_perms(cp = caller_perms());
args = {@args, cp};
args_str = substitute("%1", match($string_utils:print(args[1]), "^{%(.*%)}$"));
args[2]:notify(tostr("--", player.name, "--> ", caller, ":", callers()[1][2], "(", args_str, ")"));
.
#124:0
"Return the proposition appropriate for this gender.";
return this.(verb);
.
#124:1
":gender_adj()  -> 'male', 'female'";
":gender_noun() -> 'man', 'woman'";
"Should be hacked later.  Perhaps rename each gender object to the appropriate noun?";
return this.verb || this.name;
.
#124:2
":is_feminine/masculine/neuter()";
"Used for determining titles, conjugation.  For example, a process may want to know whether or not to print 'master' or 'mistress'.  This would help.";
return `this.(verb) ! E_PROPNF => 0';
.
#124:3
if ($code_utils:verb_loc() != this)
return {};
endif
return children(this);
.
#126:0
":audio(receivers, LIST actors, STR phrase, NUM intensity, STR template)";
"Actors    : {origin-%T, subject-%N, dobj-%D, iobj-%I, other-%L}";
"Phrase    : 'Howdy!', 'beep', 'AROOOOO'";
"Intensity : 0-whisper, pindrop; 50-normal speech;100-scream, siren";
"Template  : '%N %<says>: $phrase', 'From %(tdname): $phrase'";
"Receivers is a list of either objects or lists of {OBJ, STR} where STR is the special pre-formatted text to be used in place of 'template' for OBJ.";
receivers = args[1];
args = listdelete(args, 1);
event = this.audio:new(@args);
for rec in (receivers)
if (typeof(rec) == LIST)
event:set_custom_format(@rec);
rec[1]:receive_event(event);
else
rec:receive_event(event);
endif
endfor
.
#126:1
":video(receivers, LIST actors, STR phrase, NUM intensity, STR template)";
"Actors    : {origin-%T, subject-%N, dobj-%D, iobj-%I, other-%L}";
"Phrase    : 'grins his ass off.'";
"Intensity : 0-facial twitch, dust settling; 50-wave, cat scurrying; 100-seizure, building collapsing";
"Template  : '%N grins %p ass off.";
"Receivers is a list of either objects or lists of {OBJ, STR} where STR is the special pre-formatted text to be used in place of 'template' for OBJ.";
receivers = args[1];
event = this.video:new(@listdelete(args, 1));
for rec in (receivers)
if (typeof(rec) == LIST)
event:set_custom_format(@rec);
rec[1]:receive_event(event);
else
rec:receive_event(event);
endif
endfor
.
#127:0
value = this:get(@args);
if (value == "zone")
return {"", {"Your general location (zone) is public."}};
elseif (value)
return {"", {"Your exact location is public."}};
else
return {0, {"Your location is hidden."}};
endif
.
#127:1
{oname, raw, @rest} = args;
if (typeof(raw) == NUM)
return {oname, raw ? 1 | 0};
elseif (raw == "zone")
return {oname, raw};
else
return "This option is either on/off or \"zone\".";
endif
.
#128:0
"Not a 'real' room--nobody should be allowed in.";
return 0;
.
#129:0
return $wiz_utils:is_builder(args[1]);
.
#129:1
"@verbs <object>                                @verbs for <object>";
"List all readable verbs on the given object.";
"@properties <object>                           @properties for <object>";
"List all properties on the given object.";
whatstr = prepstr ? iobjstr | dobjstr;
what = player:my_match_object(whatstr);
if ($match_utils:object_match_failed(what, whatstr, player:env()))
return;
endif
if (verb == "@verbs")
mod = "verbs";
set = {};
cnt = -1;
set_task_perms(caller_perms());
while ((i = verb_info(what, tostr(cnt = cnt + 1))) != E_VERBNF)
set = listappend(set, i ? i[3] | "E_PERM");
endwhile
else
mod = "properties";
set = properties(what);
endif
player:tell(";", mod, "(", what, ") => ", $string_utils:print(set));
.
#129:2
"@find <object-spec>   -- Find an object's location.";
"@find <player-name>   -- Find a  player's location.";
"@find :<verbname>     -- Find a  verb's defining objects.";
if (!dobjstr)
player:tell_lines($code_utils:verb_documentation());
return;
elseif (dobjstr[1] == ":")
name = dobjstr[2..length(dobjstr)];
where = $match_utils:find_verb(name);
if (where)
player:tell("The verb \"", name, "\" is defined on ", $string_utils:name_and_number_list(where), ".");
else
player:tell("The verb \"", name, "\" is nowhere to be found.");
endif
elseif ((dobjstr[1] == "#") && ((o = $code_utils:toobj(dobjstr)) != E_TYPE))
if (!$command_utils:object_match_failed(o, dobjstr))
player:tell($string_utils:nn(o), " is at ", $string_utils:nn(o.location), ".");
endif
else
o = player:my_match_object(dobjstr);
if (o == $ambiguous_match)
player:tell("I don't know which \"", o, "\" you mean.");
return o;
elseif (!valid(o))
o = $string_utils:match_player(dobjstr);
endif
if (!$command_utils:player_match_failed(o, dobjstr))
player:tell($string_utils:nn(o), " is at ", $string_utils:nn(o.location), ".");
endif
endif
.
#129:3
"@opaque object.propname";
"Shows all descendants of `object' whose `propname' property is not clear.";
"@opaque object:verbname";
"Shows all descendants of `object' which define a `verbname' verb.";
"@defined shows all kids with the object or property defined and opaque, regardless of whether or not it exists on the parent.";
if (player != caller_perms())
return raise(E_PERM);
endif
set_task_perms(caller_perms());
if (index(dobjstr, ":") && (pref = $code_utils:parse_verbref(dobjstr)))
mode = "verb";
elseif (pref = $code_utils:parse_propref(dobjstr))
mode = "property";
else
player:notify(tostr("Usage: ", verb, " object.property|object:verb"));
return E_INVARG;
endif
what = player:my_match_object(pref[1]);
if ($match_utils:object_match_failed(what, pref[1]))
return what;
endif
name = pref[2];
if ((verb == "@opaque") && (!$object_utils:("has_" + mode)(what, name)))
player:notify(tostr($string_utils:nn(what), " has no \"", name, "\" ", mode, "."));
return E_PROPNF;
endif
unreadable = 0;
spaces = $string_utils:space(linelen = player:linelen());
suspend_msg = $string_utils:center((("suspending " + verb) + " ") + argstr, linelen, "-");
border = (verb + " ") + argstr;
player:notify(border = ("---" + border) + strsub(spaces[1..(linelen - length(border)) - 4], " ", "-"));
property_mode = mode == "property";
vallen = (linelen - 32) - 5;
cmu = $command_utils;
cdu = $code_utils;
stu = $string_utils;
for kid in (descendants = $object_utils:descendants_suspended(what))
cmu:suspend_if_needed(2, suspend_msg);
try
if (property_mode)
if (!is_clear_property(kid, name))
player:notify_lines(this:property_desc(kid, name));
endif
elseif (verb_info(kid, name))
player:notify_lines(this:verb_desc(kid, name));
endif
except (E_PERM)
unreadable = unreadable + 1;
except (E_PROPNF, E_VERBNF)
continue;
endtry
endfor
if (unreadable)
player:notify($string_utils:centre(tostr("skipped ", unreadable, " unreadable propert", (unreadable - 1) ? "ies" | "y"), linelen, "-"));
endif
player:notify(border);
.
#129:4
"  @locations <obj>";
"Show recursive locations of the given object.";
"  @locations ;eval_returning_list_of_objects";
"Show locations of the referred list of objects.";
if (dobjstr && (dobjstr[1] == ";"))
dobjstr[1..1] = "";
result = $no_one:eval(dobjstr);
if (!result[1])
return player:tell_lines(result[2]);
endif
result = result[2];
if (typeof(result) != LIST)
return player:tell("That result (", $string_utils:print(result), ") isn't of type LIST.");
endif
for kid in (result)
if (typeof(kid) == OBJ)
c = $code_utils:get_prop(loc = kid.location, "zone_coordinates");
w = valid(loc) ? tostr(loc, ":", loc:name(), c ? tostr(" (", $string_utils:from_list(c, ","), ")") | "") | "*nowhere*";
player:tell($string_utils:left(tostr(kid, ":", kid:name()), -24), " -> ", w);
endif
endfor
player:tell("-- Done `", verb, " ", dobjstr, "`");
return;
endif
dobj = player:my_match_object(dobjstr);
if ($match_utils:object_match_failed(dobj, dobjstr, player:env()))
return;
endif
player:tell($string_utils:left(dobj, 8), ": ", dobj:name());
for loc in (all = $object_utils:locations(dobj))
c = $code_utils:get_prop(loc, "zone_coordinates");
player:tell($string_utils:left(loc, 8), ":   ", loc:name(), c ? tostr(" (", $string_utils:from_list(c, ","), ")") | "");
endfor
if (!all)
player:tell("            *nowhere*");
endif
.
#129:5
"'@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
if ($object_utils:has_verb(dobj, "contents"))
contents = dobj:contents();
else
contents = dobj.contents;
endif
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
.
#129:6
"@parents       All parent()s.";
"@kids          First generation of children().";
"@branches      All children with children themselves.";
"@leaves        All children without children.";
"@descendants   All children, recursively.  Use with caution.";
" with columns       displays results in columns.";
" with matching <string>  displays only results with a name or alias matching string.";
TROUBLE_COUNT = 100;
perms = (caller_perms() == #-1) ? player | caller_perms();
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
return;
endif
set_task_perms(perms);
if (verb == "@kids")
info = children(dobj);
elseif (verb[1..4] == "@par")
verb = "@parents";
info = $object_utils:ancestors(dobj);
elseif (verb == "@branches")
info = $object_utils:branches_suspended(dobj);
elseif (verb == "@leaves")
info = $object_utils:leaves_suspended(dobj);
elseif (verb == "@descendants")
info = $object_utils:descendants_suspended(dobj);
endif
match_spec = "";
if (i = ("matching" in args) || ("named" in args))
match_spec = args[i + 1];
info = $match_utils:match(match_spec, info, LIST);
endif
tail_text = tostr(verb[2..$], match_spec ? tostr(" matching \"", match_spec, "\".") | ".");
if (info)
prolog = dobj:grammar_sub(tostr("%N (%#) %<has> ", length(info), " ", tail_text));
if ((length(info) > TROUBLE_COUNT) && (!$command_utils:yes_or_no(prolog + "  That's an awful lot of objects.  Are you sure you want to list them all?")))
player:notify("Aborted.");
return;
else
player:notify(prolog);
endif
if ((prepstr == "with") && index(iobjstr, "columns"))
pretty = $string_utils:smart_columnize($list_utils:map_arg($string_utils, "nn", info));
player:notify_lines(pretty);
else
player:notify($string_utils:nn(info));
endif
else
player:notify(dobj:grammar_sub(tostr("%N (%#) %<has> no ", tail_text)));
endif
.
#129:7
":property_desc(obj, name)";
set_task_perms(caller_perms());
{object, refname} = args;
maxobj = length(tostr(max_object()));
prefix = tostr($string_utils:right(object, maxobj), ".", refname);
try
{owner, perms} = property_info(object, refname);
except e (ANY)
return {tostr(prefix, " => <", toliteral(e[1]), ">")};
endtry
cutlen = (maxobj + length(refname)) + 5;
line = $string_utils:left(prefix, cutlen);
"name";
maxname = 16;
oname = object.name;
if (length(oname) >= maxname)
oname[maxname..$] = "*";
endif
line = tostr(line, " ", $string_utils:left(("(" + oname) + ")", maxname + 2));
"value";
MAX = 255;
v = object.(refname);
y = $string_utils:abbreviated_value(v, MAX, 3);
return {tostr(line, " => ", y)};
.
#129:8
":verb_desc(obj, name)";
set_task_perms(caller_perms());
{object, refname} = args;
maxobj = length(tostr(max_object()));
prefix = tostr($string_utils:right(object, maxobj), ":", refname);
try
{owner, perms, names} = verb_info(object, refname);
except e (ANY)
return {tostr(prefix, " => <", toliteral(e[1]), ">")};
endtry
if (names != refname)
"note the refname is one of many, possibly a wildcard";
prefix = strsub(prefix, ":" + refname, (":" + refname) + "*");
endif
cutlen = (maxobj + length(refname)) + 5;
line = $string_utils:left(prefix, cutlen);
"name";
maxname = 36;
oname = object.name;
if (length(oname) >= maxname)
oname[maxname..$] = "*";
endif
line = tostr(line, " ", $string_utils:left(("(" + oname) + ")", maxname + 2));
"owner";
owner_str = $string_utils:left(owner.name, 12);
line = tostr(line, " ", owner_str);
"perms";
perms_str = (i = perms in {"x", "xd", "d", "rd"}) ? {" x", " xd", "  d", "r d"}[i] | perms;
line = tostr(line, perms_str);
if (0)
"args";
{dspec, pspec, ispec} = verb_args(object, refname);
if (!(pspec in {"any", "none"}))
pspec = $code_utils:short_prep(pspec);
endif
args_str = tostr(dspec, " ", pspec, " ", ispec);
line = tostr(line, " ", args_str);
endif
"done";
return {line};
.
#129:9
":object_desc(obj)";
set_task_perms(caller_perms());
{object} = args;
maxobj = length(tostr(max_object()));
prefix = tostr($string_utils:right(object, maxobj));
cutlen = maxobj + 1;
line = $string_utils:left(prefix, cutlen);
"name";
maxname = (80 - maxobj) - 10;
oname = object.name;
if (length(oname) >= maxname)
oname[maxname..$] = "*";
endif
line = tostr(line, " ", oname);
return {line};
.
#130:0
"Not a 'real' room--nobody should be allowed in.";
return 0;
.
#131:0
":set_blocked_by(list)";
"Set the list of those objects blocking passage through this exit.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
return this.blocked_by = args[1];
.
#131:1
":blockers() -> An english list of those blocking the exit.";
blocked_by = this:blocked_by();
this_side = that_side = {};
source = this.source;
dest = this.dest;
for who in (blocked_by)
if (who.location == source)
this_side = setadd(this_side, who);
else
that_side = setadd(that_side, who);
endif
endfor
vrb = (verb == "blockersc") ? "iname_listc" | "iname_list";
msg = "";
if (this_side)
msg = $string_utils:(vrb)(this_side, "nobody") + " on this side";
endif
if (!that_side)
return msg;
elseif (msg)
return ((msg + " and ") + $string_utils:(vrb)(that_side, "nobody")) + " from the other";
else
return $string_utils:(vrb)(that_side, "nobody") + " from the other side";
endif
.
#131:2
":is_blocked_by(what) -> Is the given object blocking this exit?";
return args[1] in this:blocked_by();
.
#131:3
":is_blocked_for(moved[, mover])";
"Is moved's passage through the exit by mover blocked?  'mover' defaults to 'player'.";
{what, ?mover = #-1} = args;
mover = valid(mover) ? mover | ($rpg:is_char(what) ? what | player);
blocked_by = setremove(this:blocked_by(), mover);
if (!blocked_by)
return {};
else
return setremove(blocked_by, what);
endif
.
#131:4
":is_blocking_other_exit(what)";
"Check if what is blocking another exit in the source location.";
{what} = args;
for exit in (`this.source:exits() ! E_INVIND, E_VERBNF => {}')
if (`exit:is_blocked_by(what) ! E_VERBNF')
return {exit};
endif
endfor
.
#131:5
"block <exit> -- Block exit with yourself.";
"Blocking an exit prevents anyone or thing from travelling through it.";
player:maybe_queue_action("block " + this:dname(), {this, "block"}, {}, 40 - player:quickness());
.
#131:6
"Watch for blockers exiting, so's we can remove them.";
"Should be security here for an insecure MOO.";
this.blocked_by = setremove(this.blocked_by, args[1]);
.
#131:7
"Check if the exit is blocked for the given object.";
what = args[1];
return pass(what) && (!this:is_blocked_for(what));
.
#131:8
"Print appropriate messages if blocked.";
what = args[1];
if (all = this:is_blocked_for(what))
this:announce_messages("nogo_blocked");
for blocker in (all)
if (blocker.location != what.location)
blocker:tell(what:grammar_sub("%(inamec) %<is> trying to enter from behind you."));
endif
endfor
return E_NACC;
endif
"Print appropriate messages if ic states do not match.";
if (msg = $rpg:forbid_strict_ic_entry(what, this:dest(player)))
player:tell(msg);
return E_NACC;
endif
return pass(@args);
.
#131:9
"block <exit> -- Block exit with yourself.";
"Blocking an exit prevents anyone or thing from travelling through it.";
player:maybe_queue_action("unblock " + this:dname(), {this, "unblock"}, {}, 20 - player:quickness());
.
#131:10
"Messages associated with throwing something through this exit.";
"  The following are announced to the room from which the object was thrown.";
"{o,}throw_through";
"{o,}throw_through_fail";
"  The following are announced to the room to which the object was thrown.  Note that the message used in this case is the one on the OTHERSIDE of the exit through which the object was actually thrown.";
"thrown_through";
"thrown_through_failed";
msg = this.(verb);
return msg && $string_utils:pronoun_sub(msg);
.
#131:11
"throw <anything> through <exit>";
"Throw an object from this room into another.  Could have very interesting possibilities, such as tossing a beeping communicator out, or the classic last-minute grenade hoist!";
source = this.source;
if (player.location != source)
player:tell("You're in no position to do that.");
return E_RANGE;
endif
what = valid(dobj) ? dobj | source:match_object(dobjstr);
if ($command_utils:object_match_failed(what, dobjstr))
return what;
elseif (what.location != player)
player:tell("You don't have ", what:dname(), ".");
return E_RANGE;
elseif (this:is_blocked_for(what, player))
player:tell("That exit is being blocked by ", this:blockers(), ".");
return E_NACC;
elseif (!this:may_throw_through(what, player))
player:tell("It wouldn't make it through the exit anyway.");
return E_NACC;
endif
dest = this.dest;
otherside = this:otherside();
dest:bless_for_entry(what);
unlocked = this:is_unlocked_for(what) && dest:acceptable(what);
set_task_perms(caller_perms());
if (unlocked)
what:moveto(dest);
endif
if (what.location != dest)
"  source:bless_for_entry(what);";
"  what:moveto(source);";
this:announce_messages("throw_through_fail");
"-- Need some code here for announcing failure to other side? --";
return E_NACC;
endif
this:announce_messages("throw_through");
if (msg = otherside:thrown_through_msg())
dest:announce_all(msg);
endif
.
#131:12
":blocked_by()";
"A list of all objects blocking this exit.  Do maintenance here to remove those who aren't really here, or are not connected/conscious.";
base = this.blocked_by;
source = this.source;
for blocker in (base)
if (!$rpg:is_char(blocker))
base = setremove(base, blocker);
elseif (((blocker.location != source) || blocker:unconscious()) || (is_player(blocker) && ((!$object_utils:connected(blocker)) || (idle_seconds(blocker) > 120))))
base = setremove(base, blocker);
endif
endfor
this.blocked_by = base;
otherside = this:otherside();
if ((caller != otherside) && valid(otherside))
base = {@base, @`otherside:blocked_by() ! E_VERBNF => {}'};
endif
return base;
.
#131:13
"Messages associated with the blocking feature.";
return $string_utils:pronoun_sub(this.(verb), @args);
.
#131:14
pass(@args);
if (this:blocked_by())
player:tell(this:blocked_msg());
endif
.
#131:15
":may_throw_through(obj, thrower)";
"Can thrower throw obj through this exit?";
OBJ = args[1];
if (!this:transparency())
return 0;
elseif (!this:sound_permeability())
return 0;
elseif (this:blocked_by())
return 0;
elseif ($object_utils:isa(OBJ, $room))
return 0;
else
return 1;
endif
.
#131:16
":do_block()";
if (player.location != this.source)
player:tell("You can't do that from where you are.");
return E_RANGE;
elseif (this:is_blocked_by(player))
player:tell("You're already blocking ", this:dname(), ".");
return E_NONE;
elseif (info = this:is_blocking_other_exit(player))
player:tell("You're already blocking ", info[1]:dname(), ".");
return E_QUOTA;
endif
this.blocked_by = setadd(this.blocked_by, player);
this:announce_messages("block");
.
#131:17
":do_unblock()";
if (!this:is_blocked_by(player))
player:tell("You're not blocking ", this:dname(), ".");
return E_NONE;
endif
this.blocked_by = setremove(this.blocked_by, player);
this:announce_messages("stop_blocking");
.
#132:0
":add_combatant(@who)";
"Add all the given characters to this room's combatants list.";
combatants = this.combatants;
for who in (args)
combatants = setadd(combatants, who);
endfor
this.combatants = combatants;
this:birth_combat();
return combatants;
.
#132:1
":remove_combatant(@who)";
"Remove all the given characters from this room's combatants list.";
combatants = this.combatants;
for who in (args)
combatants = setremove(combatants, who);
endfor
return this.combatants = combatants;
.
#132:2
":sort_combatants() -> A list of combatants sorted by slowness.";
"If the fastest combatant's slowness is less than half that of the next fastest combatant, e receives the divisor in extra actions, represented by extra entries of eir character at the end of the sorted combatants list.";
"These extra actions will occur after all other combatants have acted.";
if (length(all = this.combatants) < 2)
return all;
endif
sorted = {c = all[1]};
speeds = {c:slowness()};
rest = listdelete(all, 1);
"Juggle the combatants list, preventing repetitive results with tied speeds.";
this.combatants = {@rest, c};
for c in (rest)
s = c:slowness();
i = s in speeds;
p = 0;
l = length(sorted);
while ((!i) && ((p = p + 1) <= l))
if (s < speeds[p])
i = p;
endif
endwhile
sorted = i ? listinsert(sorted, c, i) | {@sorted, c};
speeds = i ? listinsert(speeds, s, i) | {@speeds, s};
endfor
if (speeds[1] && ((x = (speeds[2] / speeds[1]) - 1) > 0))
c = sorted[1];
for i in [1..min(x, 3)]
sorted = {@sorted, c};
endfor
endif
return sorted;
.
#132:3
"turn_wait   -- Suspend between combatant's turns.";
"round_wait  -- Suspend between combat rounds.";
seconds = this.(verb);
if (seconds > -1)
suspend(seconds);
endif
.
#132:4
":birth_combat()";
"Start a combat task if there is none.";
if ($code_utils:task_valid(this.combat_task))
return E_NACC;
endif
fork id (0)
`this.combatants ! E_PROPNF, E_INVIND' && this:_combat();
endfork
this.combat_task = id;
.
#132:5
":kill_combat()";
"Clear the combatants list and kill the current combat task.";
if (!$code_utils:task_valid(id = this.combat_task))
return E_NONE;
endif
"Clear the `attacking' list of everyone involved, then clear combatants.";
for c in (this.combatants)
`c.attacking = {} ! E_PROPNF';
endfor
this:announce_to_combatants("The battle ends");
this.combatants = {};
return kill_task(id);
.
#132:6
":combatants() -> A list of characters in combat here.";
return this.combatants;
.
#132:7
"Clear the attacking list and the action queue of the exiting object.";
{what} = args;
try
what.attacking = what.queue = {};
except (E_INVIND, E_PROPNF)
"foo";
endtry
this.combatants = setremove(this.combatants, what);
return pass(@args);
.
#132:8
":is_forbidden_action(action)";
"Is the given action forbidden in this room?";
return args[1] in this.forbidden_actions;
.
#132:9
":attempt_action(OBJ character, STR action, LIST action_args)";
"Is the given character allowed to perform the given action in this room?";
"Return a numeric modifier to the action if so, a non-numeric value if not.";
"Print messages if necessary.";
zone = this:zone();
base = valid(zone) ? `zone:(verb)(@args) ! E_VERBNF' | E_VERBNF;
if ((base != E_VERBNF) && (typeof(base) != NUM))
return base;
endif
{who, action, @rest} = args;
if (msg = this:is_forbidden_action(action))
if (typeof(msg) == STR)
who:tell($string_utils:pronoun_sub(msg, who));
else
who:tell("You can't seem to perform ", $english:indef_art(action), " \"", action, "\" here.");
endif
return E_NACC;
endif
return 0;
.
#132:10
":do_combat(sorted-combatants, max-idle-rounds)";
"Loop through the given list of combatants, allowing each of them to :act.";
"Kill combat if nothing happens and max-idle-rounds is one, else decrement that number and return it.";
{sorted, active} = args;
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
action = 0;
for who in (sorted)
points = `who:act() ! E_VERBNF => 0';
if (points)
action = 1;
try
this:turn_wait();
except (E_VERBNF)
return;
endtry
endif
endfor
for who in (sorted)
`who:cleanup() ! E_VERBNF';
endfor
if ((active = action ? active | (active - 1)) < 1)
this:kill_combat();
endif
return active;
.
#132:11
":announce_to_combatants(msg)";
"Announce a message to all combatants.  Eventually we'll have RPG options to toggle these.";
msg = "";
comopt = $combat_options;
for c in (this:combatants())
c:get_option(comopt, "round_display") && c:announce_to(msg || (msg = $string_utils:center(args[1], 78, "-")), {});
endfor
.
#132:12
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.combatants = {};
.
#132:13
"Allow up to 5 rounds of inactivity before killing the combat task.";
if (caller != this)
return E_PERM;
endif
roundc = 0;
active = 3;
while (sorted = `this:sort_combatants() ! E_VERBNF')
this:announce_to_combatants(tostr("A new round (", roundc = roundc + 1, ") begins"));
active = this:do_combat_round(sorted, active);
if (`this:round_wait() ! E_VERBNF' == E_VERBNF)
return;
endif
endwhile
`this.combat_task = 0 ! E_PROPNF';
.
#132:14
"Append the room's neutral status, if any.";
add = $rpg:is_ic_neutral_location(this) ? $rpg.neutral_location_title_tag_msg | "";
return tostr(pass(@args), add);
.
#133:0
"@describe ...";
player:notify("You have shown yourself unworthy of creative freedom here, so your privelege of using the ", verb, " command has been revoked.");
.
#133:1
"home ...";
player:notify("You have abused your manipulation of our reality, so your privelege of using the ", verb, " command has been revoked.");
.
#133:2
"page ...";
player:notify("You have lost the right to use OOC communication commands.");
.
#133:3
":title()";
return tostr(this:name(), " [ABUSIVE]");
.
#133:4
"@quit";
boot_player(player);
.
#134:0
":tell(@stuff)";
"Redirect to :notify().";
return this:notify(tostr(@args));
.
#134:1
":notify(str)";
"Capture the string as output for later retrieval.";
this:record_output(@args);
.
#134:2
":record_output(str)";
"Store the given string in the element appropriate to the current task.";
s = args[1];
if (i = task_id() in this.master_tasks)
this.task_output[i] = listappend(this.task_output[i], s);
endif
"Else drop it.";
.
#134:3
":capture_task(obj, method, args)";
"Call obj:method(@args), capturing all input and returning that text.  No methods on THIS object may be called.";
object = args[1];
if (object == this)
return E_INVARG;
endif
this:register_task(caller_perms(), @args);
this:call_task(@args);
id = task_id();
output = this:get_output(id);
this:clear_task(id);
return output;
.
#134:4
":call_task(obj, method, args)";
"Setup the call to the args-described task by setting player.";
if (caller != this)
return E_PERM;
endif
player = this;
return this:_call_task(@args);
.
#134:5
":_call_task(obj, method, args)";
"Call the args-described task with the permissions of ABSOLUTELY NOBODY.";
if (caller != this)
return E_PERM;
endif
return args[1]:(args[2])(@args[3]);
.
#134:6
":get_output(id)";
"Return output accumulated thus far for the given task id.";
if (caller != this)
return E_PERM;
endif
id = args[1];
if (i = id in this.master_tasks)
return this.task_output[i];
else
return {};
endif
.
#134:7
":clear_task(id)";
"Stop listening to the given task, clear all output and registry info.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
id = args[1];
if (i = id in this.master_tasks)
this.masters = listdelete(this.masters, i);
this.master_tasks = listdelete(this.master_tasks, i);
this.task_output = listdelete(this.task_output, i);
return id;
else
return E_NONE;
endif
.
#134:8
":register_task(caller_perms, obj, method, args)";
if (caller != this)
return E_PERM;
endif
master = args[1];
if (i = master in this.masters)
this:clear_task(this.master_tasks[i]);
endif
this.masters = listappend(this.masters, master);
id = task_id();
this.master_tasks = listappend(this.master_tasks, id);
this.task_output = listappend(this.task_output, {});
return id;
.
#135:0
":maybe_warn_*(user)";
"Warn non-programmers about misleading object customization.";
user = args[1];
what = verb[12..length(verb)];
if (!$rpg:trusted(user))
user:notify(this.(what + "_warning_msg"));
endif
.
#135:1
":*_warning(user)";
"Return a note concerning misleading object customization, appropriate for the given user.  For trusted users, return an empty string.";
user = args[1];
return $rpg:trusted(user) ? "" | this.(verb + "_msg");
.
#136:0
":is_key(key)";
key = args[1];
keys = this.key_objects;
if (key in keys)
return;
endif
while (`valid(key = parent(key)) ! E_INVARG')
if (key in keys)
return 1;
endif
endwhile
return 0;
.
#136:1
":is_unlockable_by(what, char)";
return args[1]:is_controllable_by(args[2]);
.
#136:2
":is_lockable_by(what, char)";
return args[1]:is_controllable_by(args[2]);
.
#136:3
":do_lock(what, by_whom[, with_what])";
dobj = args[1];
whom = args[2];
iobj = {@args, $nothing}[3];
0 && this:announce_messages("attempt_lock", whom);
if (this:lock_succeeds(dobj, whom, iobj))
dobj:set_lock_status(1);
this:announce_lock_messages("lock", whom);
else
this:announce_lock_messages("lock_failed", whom);
endif
.
#136:4
":lock_succeeds(what, by_whom, with_what|$nothing)";
dobj = args[1];
whom = args[2];
iobj = args[3];
if (this.detect_owner && this:is_lockable_by(dobj, whom))
"...lock uses fingerprints, retinal scans, etc...";
return 1;
elseif (this:is_key(iobj))
return 1;
else
return 0;
endif
.
#136:5
":do_unlock(what, by_whom[, with_what])";
dobj = args[1];
whom = args[2];
iobj = {@args, $nothing}[3];
0 && this:announce_messages("attempt_unlock", whom);
if (this:unlock_succeeds(dobj, whom, iobj))
dobj:set_lock_status(0);
this:announce_lock_messages("unlock", whom);
else
this:announce_lock_messages("unlock_failed", whom);
endif
.
#136:6
":do_pick(what, by_whom, with_what)";
dobj = args[1];
whom = args[2];
iobj = {@args, $nothing}[3];
0 && this:announce_messages("attempt_pick", whom);
if (this:pick_succeeds(dobj, whom, iobj))
dobj:set_lock_status(0);
this:announce_lock_messages("pick", whom);
else
this:announce_lock_messages("pick_failed", whom);
endif
.
#136:7
":pick_succeeds(what, by_whom, with_what)";
dobj = args[1];
whom = args[2];
iobj = args[3];
pick_mod = $code_utils:call_verb(iobj, "pick_bonus", {dobj, whom}) || 0;
pick_mod = pick_mod + this.pick_mod;
result = whom:resolve("lockpick", pick_mod, args in this.pick_attempts);
this:register_pick_attempt(@args);
return (result > 0) && result;
.
#136:8
":announce_lock_messages(result-name[, @pronoun-sub-args])";
if ((caller != this) && (!this:is_controllable_by(caller_perms())))
return E_PERM;
endif
su = $string_utils;
result_name = args[1];
i = index(result_name, "_");
if (i)
attempt_name = "oattempt_" + result_name[1..i - 1];
else
attempt_name = "oattempt_" + result_name;
endif
ps_args = listdelete(args, 1) || {};
who = ps_args ? ps_args[1] | player;
msg_name = result_name + "_msg";
base_msg = this.(attempt_name + "_msg");
if (msg = this.(msg_name))
who:tell((su:pronoun_sub(base_msg, @listset(ps_args, $you, 1)) + "  ") + su:pronoun_sub(msg, @ps_args));
endif
if (!(omsg = this:get_message("o" + args[1])))
"...silence...";
elseif (msg)
who:room_announce_all_but({who}, su:pronoun_sub((base_msg + "  ") + omsg, @ps_args));
else
$you:say_action((base_msg + "  ") + omsg, @ps_args);
endif
return msg ? omsg ? 3 | 1 | (omsg ? 2 | 0);
.
#136:9
":unlock_succeeds(what, by_whom, with_what|$nothing)";
dobj = args[1];
whom = args[2];
iobj = args[3];
if (this.detect_owner && this:is_unlockable_by(dobj, whom))
"...lock uses fingerprints, retinal scans, etc...";
return 1;
elseif (valid(iobj) && this:is_key(iobj))
return 1;
else
return 0;
endif
.
#136:10
":register_pick_attempt(object, thief, lockpick)";
"Register the attempt to prevent massive scripting stat boosts.";
if (caller != this)
return E_PERM;
endif
LOG_MAX = 12;
attempts = this.pick_attempts;
this.pick_attempts = (length(attempts) > LOG_MAX) ? {@listdelete(attempts, 1), args} | {@attempts, args};
.
#137:0
":get_lock_type()";
return this.lock ? this.lock[1] | $nothing;
.
#137:1
":get_lock_status()";
return this.lock ? this.lock[2] | 0;
.
#137:2
":set_lock_status(BOOLEAN)";
lock = this:get_lock_type();
if ((caller != lock) && (!this:is_writable_by(caller_perms(), caller)))
return E_PERM;
elseif (!valid(lock))
return E_INVIND;
else
this.lock[2] = args[1];
endif
.
#137:3
":set_lock_type(#lock_interface|$nothing)";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
lock = args[1];
if (lock == $nothing)
this.lock = lock;
return 0;
elseif (!$object_utils:isa(lock, $lock_interface))
return E_INVARG;
endif
if (this.lock)
this.lock[1] = lock;
else
this.lock = {lock, 0};
endif
return 1;
.
#137:4
":lockable()";
"Return true if this object can be locked/unlocked/picked.";
return this.lock ? 1 | 0;
.
#137:5
":do_*(what, by_whom[, with_what])";
"Pass to the lock.";
if (!this:lockable())
return E_INVIND;
elseif ((verb == "do_lock") && this:is_opened())
args[2]:tell(this:grammar_sub("%(dnamec) %<d:is> open; close %[dpo] first."));
elseif ((locked = this:get_lock_status()) ? verb == "do_lock" | (verb != "do_lock"))
args[2]:tell(this:grammar_sub(("%(dnamec) %<d:is> already " + (locked ? "locked" | "unlocked")) + "."));
else
return this:get_lock_type():(verb)(@args);
endif
.
#137:6
"lock this";
"lock this with key";
"unlock this";
"unlock this with key";
"pick this";
"pick this with lockpick";
if (!this:lockable())
player:tell(this:grammar_sub("%(dnamec) %<does> not have a lock."));
return E_INVIND;
elseif (!this:is_local_to(player))
player:tell(this:grammar_sub("You're not close enough to %(dname) to manipulate %p lock."));
return E_RANGE;
endif
if (!iobjstr)
return this:("do_" + verb)(this, player);
endif
if (prepstr != "with")
player:tell(tostr("Try `", verb, " ", dobjstr, " WITH ", iobjstr, "` instead."));
return E_INVARG;
endif
iobj = player:my_match_object(iobjstr, e = player:matching_contents());
if ($match_utils:object_match_failed(iobj, iobjstr, e, "on your person"))
return $failed_match;
endif
return this:("do_" + verb)(this, player, iobj);
.
#137:7
":examine_verb_ok(what, i, info, syntax)";
names = args[3][3];
if ((((!index(names, "_")) && index(names, "lock")) && index(names, "pick")) && index(names, "unlock"))
return this:lockable();
endif
return pass(@args);
.
#137:8
":is_opened()";
"Return true if our sleuthing discovers that this object is open.  Can (and should) be overridden by child methods.";
v = $code_utils:get_property_value(this, "opened");
if (v == E_PROPNF)
v = $code_utils:get_property_value(this, "open");
endif
return v;
.
#137:9
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.lock = #-1;
.
#138:0
"install this in lockable_object";
if (!player:is_holding(this))
player:tell(tostr("You should be holding ", this:dname(), " when you do that."));
return;
endif
iobj = player:my_match_object(iobjstr, e = player:env());
if ($match_utils:object_match_failed(iobj, iobjstr, e))
return;
elseif (!$object_utils:isa(iobj, $lockable))
player:tell(iobj:grammar_sub("%(dnamec) %<doesn't> support any kind of lock."));
return;
endif
result = player:resolve(this.install_skill, this.install_mod);
if (result < 1)
player:tell(tostr("You attempt to install ", this:dname(), " into ", iobj:dname(), " and fail.  Try again."));
return;
endif
iobj:set_lock_type(this.lock_interface);
this:announce_messages("install");
this:_destroy();
"WIZARDLY: To allow setting of lock type.  Bad form, I know.";
.
#138:1
":_destroy()";
"Recycle this lock dummy after it's been installed.";
if (caller != this)
return E_PERM;
elseif ((verbs(this) || properties(this)) || children(this))
return E_NACC;
else
$recycler:_recycle(this);
endif
.
#139:0
":purge()";
"Remove all of the willed stuff from this character.";
if (!this:is_writable_by(caller_perms(), caller))
$error:raise(E_PERM);
endif
i = 0;
recycler = $recycler;
cu = $command_utils;
for o in (setremove(this.owned_objects, this))
`recycler:_recycle(o) ! ANY';
i = i + 1;
cu:suspend_if_needed(0);
endfor
"should probably be more selective in my error trapping";
return i;
.
#140:0
":skill(wielder, target)";
"-> STR|OBJ describing skill controlling the use of this weapon.";
":attack_mod(wielder, target)";
"-> NUM modifier when attacking with this weapon.";
":dodge_mod(wielder, target)";
"-> NUM modifier when dodging this weapon.";
":parry_with(wielder, target, opposing_weapon)";
"-> NUM modifier when parrying opposing_weapon with this weapon.";
":parry_against(wielder, target, opposing_weapon)";
"-> NUM modifier when parrying against this weapon with opposing_weapon.";
":damage(wielder, target)";
"-> NUM raw damage applied to target when hit.";
":slowness(wielder, target)";
"-> NUM relative slowness of use.";
":lethality(wielder, target)";
"-> NUM chance of this weapon delivering a kill rather than a knockout.";
return this.(verb);
.
#140:1
":combat_messages()";
"-> The message set to be used by this weapon.";
return this.combat_messages;
.
#140:2
":deplete(rounds)";
"Deplete the given number of rounds from this ammo object.";
rounds = this.amount - min(args[1], this.amount);
this:set_amount(rounds);
return rounds;
.
#140:3
":remaining()";
"Return the rounds remaining in this ammo object.";
return this.amount;
.
#140:4
":do_load(weapon, round_spec)";
"Load the given number of rounds into the given weapon.  Print all messages, return the number of rounds loaded.";
"Round_Spec, for the collective ammo object, is the iobjstr referring to the object.";
weapon = args[1];
round_spec = args[2];
if (typeof(round_spec) == STR)
rounds = this:amount_from_string(round_spec) || this.amount;
else
rounds = tonum(round_spec);
endif
rounds = min(weapon.ammo_capacity - weapon:ammo_remaining(), this.amount, rounds);
if (rounds > 0)
this:transfer(rounds, weapon);
endif
return rounds;
.
#140:5
":do_unload(weapon, round_spec)";
"Unload the given number of rounds from the given weapon.";
"Round_Spec, for the collective ammo object, is the dobjstr referring to the object.";
weapon = args[1];
round_spec = args[2];
if (typeof(round_spec) == STR)
rounds = this:amount_from_string(round_spec) || this.amount;
else
rounds = tonum(round_spec);
endif
rounds = min(this.amount, rounds);
if (rounds > 0)
this:transfer(rounds, player);
endif
"Always return successful.";
return 1;
.
#140:6
player:tell(this:name());
player:tell("---------------------:");
O = $rpg;
V = "build_stat_line";
player:tell(O:(V)(this, "Ammo Type", "ammo_type", this.ammo_type, 20, 16));
player:tell(O:(V)(this, "Attack Bonus", "attack_mod", this.attack_mod, 20, 16));
player:tell(O:(V)(this, "Dodge Bonus", "dodge_mod", this.dodge_mod, 20, 16));
player:tell(O:(V)(this, "Parry-With Bonus", "parry_with", this.parry_with, 20, 16));
player:tell(O:(V)(this, "Parry-Against Bonus", "parry_against", this.parry_against, 20, 16));
player:tell(O:(V)(this, "DAMAGE", "damage", this.damage, 20, 16));
player:tell(O:(V)(this, "Penetration", "penetration", this.penetration, 20, 16));
player:tell(O:(V)(this, "Lethality", "lethality", this.lethality, 20, 16));
player:tell(O:(V)(this, "Slowness", "slowness", this.slowness, 20, 16));
player:tell(O:(V)(this, "Skill", "skill", (typeof(s = this.skill) == STR) ? ("\"" + $string_utils:capitalize(s)) + "\"" | tostr(s, " (", s:name(), ")"), 20, 16));
player:tell(O:(V)(this, "Message Set", "combat_messages", tostr(s = this.combat_messages, " (", s:name(), ")"), 20, 16));
.
#140:7
"Copied from generic weapon (#327):set_stat by GameMaster (#1991) Tue Jan 28 15:16:16 1997 CST";
":set_stat(stat, value)";
"Set the value of the given RPG statistic.";
cp = caller_perms();
ok = this:is_unique() ? this:is_writable_by(cp, caller) | $rpg:trusted(cp);
if (!ok)
$error:raise(E_PERM, "Caller was denied write access to this weapon.");
endif
{stat, value} = args;
return this.(stat) = value;
.
#140:8
"Copied from generic weapon (#327):mod_stat by GameMaster (#1991) Tue Jan 28 15:16:42 1997 CST";
":mod_stat(stat, change[, min[, max]])";
"Add the given value to the given RPG statistic.";
cp = caller_perms();
ok = this:is_unique() ? this:is_writable_by(cp, caller) | $rpg:trusted(cp);
if (!ok)
$error:raise(E_PERM, "Caller was denied write access to this weapon.");
elseif (typeof(mod = args[2]) != NUM)
return E_TYPE;
endif
{stat, mod, ?min_rank = 0, ?max_rank = $maxint} = args;
now_rank = this.(stat);
new_rank = now_rank + mod;
return this:set_stat(stat, max(min(new_rank, max_rank), min_rank));
.
#140:9
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.skill = $skill_db:match("shooting");
kids = $object_utils:descendants($combat_message_set);
poss = {};
for alias in (this.aliases)
poss = {@poss, @$match_utils:match(alias, kids, LIST)};
endfor
this.combat_messages = poss ? poss[1] | $combat_message_set;
.
#140:10
"this will include it, but it has to be found by :init_for_core";
return {this.combat_messages};
.
#141:0
":spawn(coords)";
"reate an unembellished child of this room, set its coordinates to those given.";
cp = caller_perms();
vp = $code_utils:verb_perms();
if ((!cp.wizard) && (cp != vp))
return E_PERM;
endif
kid = $recycler:_create(this);
if (typeof(kid) != OBJ)
player:tell(vp:dnamec(), " cannot create a new room!  Please inform ", vp:po(), " or a wizard.");
return E_QUOTA;
endif
kid:moveto(this:zone());
kid:set_zone_coordinates(args[1], kid);
kid.destroy_when_empty = 1;
kid:set_name(this:spawned_name());
kid:set_aliases(this:spawned_aliases());
return kid;
.
#141:1
":exitfunc(obj)";
"If the room is now empty and has no exits or entrances, recycle it.";
what = args[1];
where = what.location;
if ((i = what in where.active) && (where.original[i] == this))
"should be back";
player:tell("Be forewarned that ", this:dtitle(), " may not be there when you get back.  It's best to @edit from solid OOC ground, or leave something of yours in the room to keep it occupied as you @edit.");
elseif (this:should_be_destroyed())
fork (0)
this:maybe_destroy();
endfork
endif
return pass(@args);
.
#141:2
":destroy()";
"Destroy this room if it is unimportant (!verbs, !props, !kids, etc).";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
try
if ((((!is_clear_property(this, "description")) || verbs(this)) || properties(this)) || children(this))
return E_NACC;
endif
except (E_PERM)
return E_PERM;
endtry
return `$recycler:_recycle(this) ! E_PERM';
.
#141:3
":desc_noun() -> A random noun describing this room.";
":desc_adj()  -> A random adjective describing this room.";
"The 'i' and 'd' variations prepend an indefinite or definite article to the string.";
if (verb[2] == "d")
artvrb = (verb[1] == "i") ? "indef_art" | "def_art";
verb[1..1] = "";
endif
string = $list_utils:random_element(this.(verb));
if (artvrb)
art = $english:(artvrb)(string);
string = art ? (art + " ") + string | string;
endif
return string;
.
#141:4
"Args for all verbs are (OBJ who, OBJ other_room, STR direction)";
":[o]leave_msg()  Print to player [room] when leaving this room.";
":[o]arrive_msg() Print to player [room] when entering this room.";
":[o]nogo_msg()   Print to player [room] when access to this room is denied.";
"Other room is subbed as %L, this room is subbed as %T.";
"In leave and nogo message, $dir is the direction in which player is leaving.";
"In arrive messages, $dir is the direction from which player is arriving.";
"All compass directions sub as 'the <dir>'.  $tdir prepends a 'to', and $fdir prepends a 'from'.  Up and down ALWAYS sub to 'above' and 'below', respectively.";
"The following verbs make for useful subs:";
"%[xdesc_noun] -> marsh, swamp, bog, quagmire";
"%[xdesc_adj]  -> swampy, reeking, bug-infested, muddy";
"An example leave_msg: %N %<trek> out of the %[tdesc_noun] towards a %[ldesc_adj] region to $dir.";
"             Subs to: You trek out of the marsh towards a swampy region to the north.";
who = args[1];
loc = args[2];
dir = args[3];
if (i = dir in {"up", "down"})
dir = tdir = {"above", "below"}[i];
fdir = "from " + dir;
else
dir = "the " + dir;
fdir = "from " + dir;
tdir = "to " + dir;
endif
msg = this.(verb);
msg = strsub(msg, "$dir", dir);
msg = strsub(msg, "$tdir", tdir);
msg = strsub(msg, "$fdir", fdir);
return $string_utils:pronoun_sub(msg, who, loc);
.
#141:5
":get_dest(dir)";
"Find a room in the given direction.  If one does not exist, create it.";
here = this.zone_coordinates;
dest = this:new_coordinates(here, args[1]);
if (!dest)
return E_NONE;
endif
info = this:get_cell_info(dest);
if (!info)
terrain = this:get_cell_terrain(dest);
if (!terrain:impassable(player))
room = terrain:spawn(dest);
else
"...it won't be passed through anyway...";
return terrain;
endif
else
room = info[1];
endif
return room;
.
#141:6
":fake_exit_move(obj, dest, to-direction)";
"Move the given object from this room into the given destination, going in to-direction.";
what = args[1];
dest = args[2];
tdir = args[3];
fdir = $exit_utils:otherside_name(tdir);
leave = this:leave_msg(what, dest, tdir);
oleave = this:oleave_msg(what, dest, tdir);
dest:bless_for_entry(what);
if (!dest:accept_from_cell(what, this))
"...destination does not accept entry from cells...";
"...will have already printed its own messages...";
return "Destination doesn't accept cell transit.";
endif
if (!dest:accept(what))
nogo = dest:nogo_msg(what, dest, tdir);
onogo = dest:onogo_msg(what, dest, tdir);
if (nogo)
what:tell(nogo);
endif
if (onogo)
this:announce_all_but({what}, onogo);
endif
return "Not accepted.";
endif
if (leave)
what:tell(leave);
endif
what:moveto(dest);
if (what.location != dest)
nogo = dest:nogo_msg(what, dest, tdir);
onogo = dest:onogo_msg(what, dest, tdir);
if (nogo)
what:tell(nogo);
endif
if (onogo)
this:announce_all_but({what}, onogo);
endif
return "Move failed.";
endif
if (oleave)
this:announce_all_but({what}, oleave);
endif
arrive = this:arrive_msg(what, this, fdir);
oarrive = this:oarrive_msg(what, this, fdir);
if (arrive)
what:tell(arrive);
endif
if (oarrive)
dest:announce_all_but({what}, oarrive);
endif
.
#141:7
"Attempt to travel in the given direction <dir>.";
"If a 'real' exit of <dir> leads out of this room into an existing room, go there.";
"If there is an existing room on the underlying zone grid in <dir>, go there.";
"If there is no room in <dir>, create one.";
if ((!this.zone_coordinates) && (pass(@args) != E_VERBNF))
"If not zoned, don't bother attempting coordinate operations.";
return;
endif
exit = this:match_exit(dir = verb);
if (valid(exit))
exit:invoke();
return "Valid exit found and taken.";
endif
dir = verb;
names = this.dir_names;
aliases = this.dir_aliases;
if (i = dir in aliases)
dir = names[i];
elseif (!(i = dir in names))
player:tell("You can't go that way.");
return ("Direction (" + dir) + ") not found in direction names or aliases.";
endif
dest = this:get_dest(dir);
if (typeof(dest) != OBJ)
player:tell("Some unusual force prevents you from going in that direction.");
return ("Direction (" + dir) + ") does not parse to a room object.";
endif
this:fake_exit_move(player, dest, dir);
.
#141:8
":spawned_name()";
"The name to be assigned to a spawned kid of this room.";
return this.(verb) || strsub(this.name, "generic ", "");
.
#141:9
":spawned_aliases()";
"The aliases to be assigned to a spawned kid of this room.";
return this.(verb) || {this:spawned_name()};
.
#141:10
"Pass to verbs on this room's zone.";
return this:zone():(verb)(@args);
.
#141:11
":recycle() -- Clear zone coordinates!";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
pass(@args);
this:update_cell_info(this.zone_coordinates);
.
#141:12
":tell_exits()";
"Attempt to show what's all around you in the terrain room.";
if (!this.zone_coordinates)
return pass(@args);
endif
type = `player:get_option($display_options, "exit_sentence") ! E_VERBNF => 0';
if (!type)
pass(@args);
return this:tell_open_directions();
endif
ddirs = descs = odirs = occupants = {};
eximp = this.exclude_impassable;
for dir in (this.dir_names)
dest = this:new_coordinates(this.zone_coordinates, dir);
terr = this:get_cell_terrain(dest);
if (!$object_utils:has_verb(terr, "adjacent_desc"))
continue;
endif
if ((!eximp) || (!terr:impassable(player)))
desc = terr:adjacent_desc();
if (i = desc in descs)
ddirs[i] = setadd(ddirs[i], dir);
else
ddirs = {@ddirs, {dir}};
descs = {@descs, desc};
endif
if (o = terr:occupants())
odirs = {@odirs, dir};
occupants = {@occupants, o};
endif
endif
endfor
if (odirs)
msg = {};
for i in [1..length(occupants)]
all = occupants[i];
msg = {@msg, ((($string_utils:iname_listc(all) + " ") + ((length(all) == 1) ? "is" | "are")) + " to the ") + odirs[i]};
endfor
occupants = ("  " + $string_utils:english_list(msg)) + ".";
else
occupants = "";
endif
ddir_len = length(ddirs);
real_exits = this:obvious_exits();
if ((!eximp) && (ddir_len == 1))
msg = ("You see " + descs[1]) + " on all sides.";
elseif (((!eximp) && (ddir_len == 2)) && ((length(ddirs[1]) < 5) || (length(ddirs[2]) < 5)))
if (length(ddirs[1]) < 5)
f = 2;
r = 1;
else
f = 1;
r = 2;
endif
msg = tostr("You see ", descs[f], " on all sides except ", $string_utils:english_list(ddirs[r]), ", where there is ", descs[r], ".");
elseif (ddirs)
msg = {};
for i in [1..length(descs)]
msg = {@msg, (descs[i] + " to the ") + $string_utils:english_list(ddirs[i])};
endfor
msg = ("You see " + $string_utils:english_list(msg)) + ".";
else
return pass(@args);
endif
if (real_exits)
msg = tostr(msg, "  ", $string_utils:dname_listc(real_exits), " exit", (length(real_exits) == 1) ? "s" | "", " to markedly unique terrain.");
endif
player:tell(msg + occupants);
.
#141:13
":audience_for_announce()";
"-> Everyone in surrounding cells can here what is said.";
return pass(@args);
"Well, later they'll be able to.  Right now we'll stick with the old way.";
all = {};
for dir in (this.dir_names)
dest = this:new_coordinates(this.zone_coordinates, dir);
terr = this:get_cell_terrain(dest);
all = {@all, @terr:contents()};
endfor
return all;
.
#141:14
"Pass to the holding zone, with our zone coordinates as first argument.";
return this:zone():(verb)(this.zone_coordinates, @args);
.
#141:15
":accept_from_cell(obj, cell_room)";
"Will we accept entry of obj from zoned cell_room?  Of course we will!";
"This verb should be hacked to provide messages upon failure.  :acceptable*, to which it passes, should only return a value.";
return this:acceptable_from_cell(@args);
.
#141:16
":adjacent_desc()";
"Return the phrase that should appear in the surroundings list of adjacent rooms.";
if (info = this.adjacent_desc_msg)
return info;
elseif ((info = this.persistent_adjacent_desc) && (info[1] == task_id()))
return info[2];
else
info = {task_id(), (this:idesc_adj() + " ") + this:desc_noun()};
this.persistent_adjacent_desc = info;
return info[2];
endif
.
#141:17
":maybe_transmit_announce(text, exclude)";
"Called by :announce_all_but, this method determines if the given sound should be transmitted to adjacent rooms.";
"The current criterion for this is simply a trailing exclamation point.";
if (!this.zone_coordinates)
"no coords, no transmission";
return 0;
endif
text = args[1];
if (!(m = match(text, "%(.+[^!]%)!+%(\"?%)$")))
return 0;
endif
text = substitute("%1", m);
if (match(text, "[a-z]$"))
text = text + "!";
endif
this:transmit_announcement(text + substitute("%2", m), @listdelete(args, 1));
return 1;
.
#141:18
"Maybe transmit the noise to adjacent cells.";
pass(@args);
this:maybe_transmit_announcement(tostr(@listdelete(args, 1)), args[1]);
.
#141:19
":get_detail()";
"Show what lies in the cardinal directions.";
if (passed = pass(@args))
return passed;
endif
dir = args[1];
directions = this.dir_names;
i = (dir in directions) || (dir in this.dir_aliases);
if (!i)
return "";
endif
sights = this:visible_in_direction(dir = directions[i], player:stat_sight() || 1);
range = 0;
for sight in (sights)
if (sight)
"...unremarkable terrain...";
range = range + 1;
else
if (range)
pfx = ((("(" + $string_utils:capitalize($string_utils:english_number(range))) + " units ") + dir) + "...)";
else
pfx = ("(Immediately " + dir) + "...)";
endif
if (c = sight:contents())
c = {(($string_utils:iname_listc(c) + " ") + ((length(c) == 1) ? "is" | "are")) + " there."};
endif
return $string_utils:lines_to_list(pfx, sight:description(), @c);
endif
endfor
terr = this:get_cell_terrain(this:new_coordinates(this.zone_coordinates, dir));
return $string_utils:lines_to_list("(For as far as you can see...)", terr:description());
.
#141:20
":icon()";
"Return the icon for this square, for use in maps of the grid.";
return this.icon_msg;
.
#141:21
":invokable_directions([character])";
who = args ? args[1] | player;
dirs = {};
here = this.zone_coordinates;
"return if no coordinates  --Quinn, 961105";
if (!here)
return {};
endif
for dir in (this.dir_names)
cell = this:new_coordinates(here, dir);
terr = this:get_cell_terrain(cell);
if (!terr:impassable(who))
dirs = {@dirs, dir};
endif
endfor
return dirs;
.
#141:22
":impassable(who)";
"Is this terrain impassable for the given object?";
return this.solid;
.
#141:23
":opaque(who)";
"Is this terrain opaque for the given viewer?";
"Perhaps adjust if viewer has infrared gogs, etc.";
return this.opaque;
.
#141:24
":should_be_destroyed()";
"True if destroy_when_empty is true and we're empty and have no exits or entrances.";
"Actually, perhaps I should even add exits and entrances on the fly?";
return ((this.destroy_when_empty && (!this.contents)) && (!this.exits)) && (!this.entrances);
.
#141:25
":maybe_destroy()";
"Destroy the room if it should be destroyed.";
if (!this:should_be_destroyed())
return 0;
endif
return this:destroy() || 1;
.
#141:26
":acceptable_from_cell(obj, cell_room)";
"Will we accept entry of obj from zoned cell_room?  Of course we will!  No messages should be printed.";
return !this:impassable(@args);
.
#141:27
"So generic terrain rooms don't have to be in their preferred zones.";
if (valid(z = this.default_zone))
return z;
else
return pass(@args);
endif
.
#141:28
":take_exit(@args)";
"If the third arg of 'just move' is given, find the dest and attempt the move.";
quiet = (length(args) > 2) && args[3];
if ((!quiet) || (typeof(exit = args[1]) != STR))
return pass(@args);
endif
char = args[2];
dest = this:get_dest(exit);
char:moveto(dest);
room = player:room();
if (room != exit.dest)
return E_NACC;
endif
return {room};
.
#141:29
":tell_open_directions()";
"Show a list of open directions and who's there.";
ddirs = descs = odirs = occupants = {};
su = $string_utils;
coords = this.zone_coordinates;
for dir in (this.dir_names)
dest = this:new_coordinates(coords, dir);
terr = this:get_cell_terrain(dest);
desc = `terr:adjacent_desc() ! E_VERBNF => ""';
if ((!desc) || `terr:impassable(player) ! E_VERBNF => 1')
continue;
endif
if (o = `terr:occupants() ! E_VERBNF => {}')
desc = tostr(desc, "; ", su:pronoun_sub("%n %<is> there", (length(o) == 1) ? o[1] | o));
endif
if (i = desc in descs)
ddirs[i] = setadd(ddirs[i], dir);
else
ddirs = {@ddirs, {dir}};
descs = {@descs, desc};
endif
endfor
player:tell("Open directions:");
if (!ddirs)
player:tell(" (None)");
return;
endif
bold = player:get_option($display_options, "bold_exits");
prefix = bold ? $emu.ANSI_Line_Prefix | "";
bold_on = bold ? $emu:tokenize("bold") | "";
bold_off = bold ? $emu:tokenize("normal") | "";
for i in [1..length(descs)]
line = tostr(prefix, " ", bold_on, su:english_list(ddirs[i]), bold_off, " (", descs[i], ")");
player:tell(line);
endfor
.
#142:0
":update_cell_info(coordinates[, room[, @info]])";
"Set information on the given cell.  Right now, info is just one element:";
"room -- The $room occupying the given cell.";
"Information is stored in properties on the zone object, each one with a name of the format 'x.y.z' where x, y, and z are the coordinates of the relevant cell.";
"If no info is given, delete the cell property.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
{xyz, @info} = args;
if (!xyz)
return E_INVARG;
endif
prop = this:coordinate_propname(xyz);
if (!info)
if (`this.(prop) ! E_PROPNF')
delete_property(this, prop);
endif
return 0;
elseif (`this.(prop) ! E_PROPNF')
this.(prop) = info;
return -1;
else
add_property(this, prop, info, {$code_utils:verb_perms(), "R"});
return 1;
endif
"--WiZARDLY--";
.
#142:1
":get_cell_info(coordinates)";
"Get recorded cell info for the given coordinates, if any.";
"=> {#room} Cell info on success.";
"=> E_PROPNF if no info";
"=> E_INVARG if info since invalid and now deleted";
xyz = args[1];
info = `this.(this:coordinate_propname(xyz)) ! E_PROPNF';
if (info != E_PROPNF)
if (!this:is_valid_cell_info(info))
this:update_cell_info(xyz);
return E_INVARG;
endif
return info;
endif
info = this:is_grouped_cell(xyz);
if (info)
return info[1..1];
endif
return E_PROPNF;
.
#142:2
":get_cell_terrain(coordinates)";
"Return the generic terrain room suitable for the given coordinates.";
c = args[1];
if (info = this:get_cell_info(c))
return info[1];
elseif (info = this:info_from_terrain_map(c))
return info[1];
elseif (info = this:terrain_zone_for(c))
return info[1];
else
return this.default_terrain;
endif
.
#142:3
":get_direction_offset(dir)";
"Return a list of coordinates, representing the relative change in position of an object moving in the given direction.";
if (i = args[1] in this.directions)
return this.offsets[i];
else
return {0, 0, 0};
endif
.
#142:4
":add_coordinates(coords, add-coords)";
"The result of adding add-coords to coords.";
{now, add} = args;
for i in [1..3]
now[i] = now[i] + add[i];
endfor
return now;
.
#142:5
":new_coordinates(current-coordinates, direction[, x])";
"Return the resultant destination after moving 'x' spaces (defaults to 1) in 'direction' from 'current-coordinates'.";
old = args[1];
dir = args[2];
dst = args[3] || 1;
add = this:get_direction_offset(dir);
new = old;
while (dst)
new = this:add_coordinates(new, add);
dst = dst - 1;
endwhile
return new;
.
#142:6
":coordinate_propname(coords)";
return $string_utils:from_list(args[1], ".");
.
#142:7
":grouped_cell_propname(grouped_cell_object)";
return tostr("_", args[1]);
.
#142:8
":is_grouped_cell(coords)";
"Are the given coordinates included in an existing grouped cell object?";
"If so, return a list consisting of the handler for that cell's group and all the contained cells.";
xyz = args[1];
for cell in (this.grouped_cells)
prop = this:grouped_cell_propname(cell);
xyzs = this.(prop);
if (xyz in xyzs)
return {cell, xyzs};
endif
endfor
return E_PROPNF;
.
#142:9
":verify_prop(name)";
"Does a property with the given name exist on this object?";
return this.(args[1]) != E_PROPNF;
.
#142:10
":adjacent_cells(coords[, range])";
"Return a list of coordinates around the given center.";
"'range' should be how far to extend in each direction.";
center = args[1];
range = (length(args) > 1) ? args[2] | 1;
coords = {};
for set in (this.offsets)
c = center;
for itr in [1..range]
coords = {@coords, c = this:add_coordinates(c, set)};
endfor
endfor
return coords;
.
#142:11
":adjacent_rooms(coords[, range])";
"Return a list of actual rooms around the given center.  Will not return uninhabited terrain, but only special or occupied cells.";
"'range' should be how far to extend in each direction.";
center = args[1];
range = (length(args) > 1) ? args[2] | 1;
places = {};
for set in (this.offsets)
c = center;
r = range;
while (r > 0)
if (i = this:get_cell_info(c = this:add_coordinates(c, set)))
places = {@places, i[1]};
r = -1;
else
r = r - 1;
endif
endwhile
endfor
return places;
.
#142:12
":add_cell_group(OBJ handler[, @cells])";
"Add the given object as a handler for a group of cells (optionally given).";
"This is very useful for 'filling' an area in a coordinate zone, to prevent characters from walking through a certain area.";
"It could represent a 'real' structure (group of conventional $rooms), a field of high-radiation, a lava flow, a bottomless pit, etcetera.";
":accept_from_cell(object, cell) will be called on the handler when someone tries to enter from outside terrain.  You should handle the movement in that verb.  Either refuse entry and print messages or redirect them somewhere else.";
"If the given handler already exists and 'cells' is given, set its cells to that new list.";
"Each cell should be either a list of coordinates {x,y,z}, or a list of lists of coordinates, which will be interpreted as the coordinates forming lines between those points.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
cell = args[1];
info = this:parse_cell_list(@listdelete(args, 1)) || 0;
prop = this:grouped_cell_propname(cell);
if (!this:verify_property(prop))
add_property(this, prop, info || {}, {$code_utils:verb_loc().owner, "R"});
this.grouped_cells = setadd(this.grouped_cells, cell);
return 1;
elseif (info != 0)
this.(prop) = info;
return -1;
else
return E_NONE;
endif
"--WiZARDLY--";
.
#142:13
":del_cell_group(OBJ handler)";
"Delete the given object as a cell group handler, freeing all cells grouped within it.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
cell = args[1];
prop = this:grouped_cell_propname(cell);
if (!this:verify_property(prop))
return E_NONE;
elseif (typeof(r = delete_property(this, prop)) == ERR)
return r;
else
this.grouped_cells = setremove(this.grouped_cells, cell);
return 1;
endif
"--WiZARDLY--";
.
#142:14
":add_grouped_cell(handler, @cells)";
"Add the given cells to the domain of the given handler.";
"See :add_cell_group for details on how cells are interpreted.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
cell = args[1];
prop = this:grouped_cell_propname(cell);
if (!this:verify_property(prop))
return E_PROPNF;
else
this.(prop) = $set_utils:union(this.(prop), this:parse_cell_list(@listdelete(args, 1)));
return 1;
endif
.
#142:15
":add_grouped_cell(handler, @cells)";
"Remove the given cell(s) from the domain of the given handler.";
"See :add_cell_group for details on how cells are interpreted.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
cell = args[1];
prop = this:grouped_cell_propname(cell);
if (!this:verify_property(prop))
return E_PROPNF;
else
this.(prop) = $set_utils:diff(this.(prop), this:parse_cell_list(@listdelete(args, 1)));
return 1;
endif
.
#142:16
":connect_points(xyz, xyz, ...)";
"Return a list of coordinates, all those connecting the given points.";
"Note only one plane (out of x, y, and z) must differ between each point.";
last = args[1];
line = {last};
for point in (listdelete(args, 1))
if (point != last)
d = 0;
while (point[d = d + 1] == last[d])
endwhile
plus = last[d] < point[d];
while ((next = listset(last, plus ? last[d] + 1 | (last[d] - 1), d)) != point)
line = {@line, next};
last = next;
endwhile
last = next;
line = {@line, point};
endif
endfor
return line;
.
#142:17
":parse_cell_list(@cells)";
"Each cell should be either a coordinate reference {x,y,z}, or a list of them.  A list of coordinates will be interpreted as all points between each coordinate.";
cells = {};
for cell in (args)
if (typeof(cell[1]) == NUM)
cells = {@cells, cell};
else
cells = {@cells, @this:connect_points(@cell)};
endif
endfor
return cells;
.
#142:18
":visible_in_direction(LIST coords, STR dir[, NUM range])";
"Return a list of 'range' elements, each element containing either a 1 if nothing unique is in that cell, or an object (the unique thingie).";
if (!(i = args[2] in this.directions))
return E_PROPNF;
endif
point = args[1];
offset = this.offsets[i];
range = (length(args) > 2) ? args[3] | 1;
sights = {};
for itr in [1..range]
if (valid(i = this:get_cell_terrain(point = this:add_coordinates(point, offset))))
cell = i;
if (!`cell:opaque(player) ! E_VERBNF => 1')
sights = {@sights, cell};
else
"...the cell is opaque, last in line of sight...";
return {@sights, cell};
endif
else
sights = {@sights, 1};
endif
endfor
return sights;
.
#142:19
":transmit_announce(coords, text, exclude[, range])";
"coords   -- Speaker's location.";
"text     -- Text to be announced.";
"exclude  -- Those to exclude from the announcement.";
"range    -- Cell distance in each direction to announce.";
center = args[1];
o_text = args[2];
ex = args[3];
range = (length(args) > 3) ? args[4] | 1;
directions = this.directions;
othersides = this.othersides;
offsets = this.offsets;
for i in [1..length(directions)]
c = center;
r = range;
while (r > 0)
if (info = this:get_cell_info(c = this:add_coordinates(c, offsets[i])))
text = (("(" + othersides[i]) + ") ") + o_text;
for what in (info[1]:occupants())
if (!(what in ex))
what:announce_to(text, ex);
endif
endfor
endif
r = r - 1;
endwhile
endfor
.
#142:20
":draw_map(center, radius)";
center = args[1];
radius = args[2];
x_beg = center[1] - radius;
x_end = center[1] + radius;
y = center[2] + radius;
z = center[3];
i = radius * 2;
map = {};
for i in [1..(radius * 2) + 1]
icons = {};
for x in [x_beg..x_end]
here = {x, y, z};
icon = this:get_cell_terrain(here):icon(player) || "   ";
if (center == here)
icon[2] = "X";
endif
icons = {@icons, icon};
endfor
map = {@map, tostr(@icons)};
y = y - 1;
endfor
return map;
.
#142:21
":point_in_line(coordinates, line)";
"Coordinates: Standard 2/3-element list of integers.";
"       Line: List of two coordinates.";
"The Z coordinate may be omitted if the zone is not 3-D.";
c = args[1];
l = args[2];
a = l[1];
b = l[2];
if ((c[1] < a[1]) || (c[1] > b[1]))
return 0;
elseif ((c[2] < a[2]) || (c[2] > b[2]))
return 0;
elseif (!this.is_3d)
return 1;
elseif ((c[3] < a[3]) || (c[3] > b[3]))
return 0;
endif
return 1;
.
#142:22
":terrain_zone_for(coordinates)";
"If the given coordinates are included inside a zone of homogenous terrain, return a list of one element: the generic terrain room representing that zone.";
c = args[1];
for set in (this.terrain_zones)
for line in (listdelete(set, 1))
if (this:point_in_line(c, line))
return set[1..1];
endif
endfor
endfor
return 0;
.
#142:23
":info_from_terrain_map(coordinates)";
"Scan the ASCII terrain map for the given coordinates.  The terrain_maps property is a list of lists or strings.  Each list is like so...";
"Element 1: ANCHOR coordinates specifying the northwest corner of the MAP.";
"Element 2: ICON_INFO, an associated list of {'CHARACTER', #TERRAIN_OBJECT}.";
"Element 3: MAP, a list of strings of characters in ICON_INFO which.  For MAP[y][z], y and z are added to ANCHOR and the translated string in that spot specifies the terrain for those coordinates.";
"If the element is a string, it points to properties on this object 'mapdata_foo' (containing a list of elements 1-2 above) and 'maptext_foo' (containing element 3 above--the actual map).";
{x, y, z} = args[1];
for d in (this.terrain_maps)
if (typeof(d) == STR)
try
data = this.("mapdata_" + d);
map = this.("maptext_" + d);
except (E_PROPNF)
continue;
endtry
else
data = d;
map = data[3];
endif
{tl_x, tl_y, tl_z} = data[1];
if (((z != tl_z) || (y > tl_y)) || (x < tl_x))
continue;
endif
idx_a = tl_y - (y - 1);
idx_b = x - (tl_x - 1);
icon = `map[idx_a][idx_b] ! E_RANGE => 0';
if (icon)
info = $list_utils:assoc(icon, data[2]);
return info && listdelete(info, 1);
endif
endfor
return 0;
.
#142:24
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.default_terrain = $terrain_room;
.
#142:25
":is_valid_cell_info(LIST info)";
"Return true if the given info on this cell (taken from a property on this object) has any business describing coordinates in this zone.  The `info' generally consists of one significant element: a room object reference.";
{info} = args;
return (info && $recycler:valid(info[1])) && $object_utils:isa(info[1], $room);
.
#143:0
if (!this:ok_write(caller, caller_perms()))
return E_PERM;
endif
"This is the quick and dirty way to kill messages, dammit.";
all = length(this.messages);
if ((cull = this.autodelete_threshold - all) < 0)
this.messages[1..abs(cull)] = {};
endif
return pass(@args);
.
#144:0
":compute_score(xbegin, ybegin, put_tiles_return_value, board_image)";
"=> {{{word, position, bonuses, score} ... }, totalscore}";
events = {};
xbegin = args[1];
ybegin = args[2];
limage = args[3][4];
bimage = args[4];
where = player in this.players;
cols = rows = {};
events = {};
for i in [1..length(limage)]
cols = setadd(cols, limage[i][1]);
rows = setadd(rows, limage[i][2]);
endfor
totalscore = 0;
for i in (rows)
mult = 1;
score = 0;
image = bimage[i];
image = $string_utils:substitute(image, {{"-", " "}, {"=", " "}, {"'", " "}, {"\"", " "}, {"*", " "}});
start = rindex(image[1..xbegin], " ") + 1;
end = (index(image[xbegin..length(image)], " ") + xbegin) - 2;
if (end == (xbegin - 2))
end = 15;
endif
word = image[start..end];
if (length(word) > 1)
for ii in [start..end]
isblank = {i, ii} in this.blank;
if ({ii, i} in limage)
if (!isblank)
if (this.board_layout[i][ii] == "'")
score = score + (this:do_value(bimage[i][ii]) * 2);
elseif (this.board_layout[i][ii] == "\"")
score = score + (this:do_value(bimage[i][ii]) * 3);
else
score = score + this:do_value(bimage[i][ii]);
endif
endif
if ((this.board_layout[i][ii] == "-") || (this.board_layout[i][ii] == "*"))
mult = mult * 2;
elseif (this.board_layout[i][ii] == "=")
mult = mult * 3;
endif
elseif (!isblank)
score = score + this:do_value(bimage[i][ii]);
endif
endfor
score = score * mult;
totalscore = totalscore + score;
events = {@events, {word, tostr("across ", i, "abcdefghijklmno"[start]), this:get_word_mult_msg(mult), score}};
endif
endfor
for ii in (cols)
mult = 1;
score = 0;
image = "";
for foo in [1..15]
image = image + bimage[foo][ii];
endfor
image = $string_utils:substitute(image, {{"-", " "}, {"=", " "}, {"'", " "}, {"\"", " "}, {"*", " "}});
start = rindex(image[1..ybegin], " ") + 1;
end = (index(image[ybegin..length(image)], " ") + ybegin) - 2;
if (end == (ybegin - 2))
end = 15;
endif
word = image[start..end];
if (length(word) > 1)
for i in [start..end]
isblank = {i, ii} in this.blank;
if ({ii, i} in limage)
if (!isblank)
if (this.board_layout[i][ii] == "'")
score = score + (this:do_value(bimage[i][ii]) * 2);
elseif (this.board_layout[i][ii] == "\"")
score = score + (this:do_value(bimage[i][ii]) * 3);
else
score = score + this:do_value(bimage[i][ii]);
endif
endif
if ((this.board_layout[i][ii] == "-") || (this.board_layout[i][ii] == "*"))
mult = mult * 2;
elseif (this.board_layout[i][ii] == "=")
mult = mult * 3;
endif
elseif (!isblank)
score = score + this:do_value(bimage[i][ii]);
endif
endfor
score = score * mult;
totalscore = totalscore + score;
events = {@events, {word, tostr("down ", "abcdefghijklmno"[ii], start), this:get_word_mult_msg(mult), score}};
endif
endfor
return {events, totalscore};
.
#144:1
"place <word> across|down [a-o][1-15]";
"      <word> at [a-o][1-15]  (place a word down)";
"      <word> at [1-15][a-o]  (place a word across)";
if (!this.started)
player:tell("The game hasn't started yet.");
return E_NACC;
elseif (!(where = player in (players = this.players)))
player:tell("You can't place tiles -- you aren't even playing.");
return E_PERM;
elseif (this.turn != player)
player:tell("You can't place tiles -- It isn't your turn.");
return E_PERM;
elseif (!(ppinfo = this:parse_placement(argstr)))
"...bad args...";
return ppinfo;
elseif (!(info = this:put_tiles(word = ppinfo[1], across = ppinfo[4], xbegin = ppinfo[2], ybegin = ppinfo[3])))
"...bad placement...";
return info;
endif
this.board = info[1];
this.tiles[where] = info[2];
this.blank = info[3];
blank = info[5];
usedtiles = info[6];
this.lastmove = listset(this.lastmove, info[4], where);
this:announce_all(player.name, " takes ", player:pp(), " turn:");
if (blank != {})
this:announce_all(player:psc(), " used ", (length(blank) == 1) ? "a" | "both", " blank tile", (length(blank) == 1) ? " " | "s ", "as the letter", (length(blank) == 1) ? " " | "s ", $string_utils:english_list(blank), ".");
endif
cs_args = {xbegin, ybegin, info, this.board};
info = this:compute_score(@cs_args);
events = info[1];
totalscore = info[2];
number_events = length(events);
this:announce_all(player:psc(), " places the word ", events[1][1], " ", events[1][2], " ", events[1][3], "for ", events[1][4], " points.");
score = this.scores[where];
this:do_history(player, (("place " + events[1][1]) + " ") + events[1][2], events[1][4], score = score + events[1][4]);
for event in (listdelete(events, 1))
this:announce_all(player:psc(), " places the word ", event[1], " ", event[2], " ", event[3], "for ", event[4], " points.");
this:do_history(0, (("      " + event[1]) + " ") + event[2], event[4], score = score + event[4]);
endfor
if (usedtiles == 7)
this:announce_all(player.name, " used all seven of ", player:pp(), " tiles! ", player:psc(), " gets 50 bonus points!");
number_events = number_events + 1;
totalscore = totalscore + 50;
this:do_history(0, "BONUS: Used all tiles!", 50, totalscore);
endif
extra = this:custom_score(totalscore, events, cs_args);
if (extra)
for line in (extra[1])
this:announce_all(line);
endfor
for line in (extra[2])
this:do_history(@line);
endfor
totalscore = extra[3];
endif
if (number_events > 1)
this:announce_all("...for a total of ", totalscore, " point", (totalscore == 1) ? "." | "s.");
endif
this.scores[where] = this.scores[where] + totalscore;
this.lastscore[where] = totalscore;
this.lastpicked[where] = "";
if ((this.tiles[where] == {}) && (this.bag == {}))
this:announce_all(player.name, " placed ", player:pp(), " last tile.  Players may type endgame to tally final scores.");
endif
this:nextturn();
.
#144:2
data = args[1];
wanted = args[2];
output = "";
for letter in [1..length(data)]
ltr = data[letter];
if (index(wanted, ltr))
output = output + ltr;
endif
endfor
return output;
.
#144:3
"value[!]|preview[!] <letter or word>";
"                    <word> across|down [a-o][1-15]";
"                    <word> at [a-o][1-15]  (place a word down)";
"                    <word> at [1-15][a-o]  (place a word across)";
"";
"Value simply shows the events and score for the play.  Preview shows what the board would look like if the play was actually made.";
"Using ! forces the placer to ignore whether or not you actually have the given tiles.";
if (!argstr)
player:tell_lines($code_utils:verb_documentation());
return E_ARGS;
elseif (length(args) == 1)
word = args[1];
msg = "";
if (length(word) > 1)
msg = "  -> ";
for i in [1..length(word)]
msg = (((msg + word[i]) + "=") + tostr(this:do_value(word[i]))) + " ";
endfor
endif
player:tell("Value of ", word, ": ", this:do_value(word), msg);
return 1;
endif
info = this:parse_placement(argstr);
if (!info)
return info;
endif
word = info[1];
across = info[4];
xbegin = info[2];
ybegin = info[3];
ignore = verb[length(verb)] == "!";
info = this:put_tiles(word, across, xbegin, ybegin, ignore ? this.letter_layout | 0);
if (!info)
return info;
endif
where = player in this.players;
bimage = info[1];
timage = info[2];
player:tell("The results of that move would:");
usedtiles = info[6];
if (info[5])
player:tell(tostr(length(info[5])), " blank tile", (length(info[5]) == 1) ? "" | "s ", " in use as ", $string_utils:english_list(info[5]), ".");
endif
cs_args = {xbegin, ybegin, info, bimage};
info = this:compute_score(@cs_args);
events = info[1];
totalscore = info[2];
number_events = length(events);
for event in [1..length(events)]
player:tell("Form the word ", events[event][1], " ", events[event][2], " ", events[event][3], "for ", events[event][4], " points.");
endfor
if (usedtiles == 7)
player:tell("...Use all seven tiles for a 50-point bonus.");
number_events = number_events + 1;
totalscore = totalscore + 50;
endif
extra = this:custom_score(totalscore, events, cs_args);
if (extra)
for line in (extra[1])
player:tell(line);
endfor
totalscore = extra[3];
endif
if (number_events > 1)
player:tell("...Total value: ", totalscore, " point", (totalscore == 1) ? "." | "s.");
endif
if (verb[1] == "p")
board = this:compile_board(bimage, ignore ? this.tiles | listset(this.tiles, timage, where), ignore ? this.scores | listset(this.scores, this.scores[where] + totalscore, where));
player:tell_lines(board);
endif
.
#144:4
if (this.finished)
player:tell("Game over, man--game over!  Reset if you want to start a new game.");
return;
elseif (length(this.players) >= this.max_players)
player:tell("Sorry, you can't join the game now - there are already the maximum number of players.");
this:announce(player.name, " tried to join the game, but can't. There are already the maximum number of players.");
elseif (player in this.players)
player:tell("You're already in the game, silly!");
elseif (this.locked)
$you:say_action("%N %<pounds> in vain at a locked strongbox marked `TILE RACKS', eying the Word Game Elite with obvious malice.");
else
this:do_play(player);
endif
.
#144:5
":reset -- Reset the game.";
"Send a true arg to reset from inside w/out prompts.";
who = (callers() && (caller != this)) ? caller_perms() | player;
if ((!(players = this.players)) || (((caller == this) && args) && args[1]))
"no questions asked";
elseif ((!(who in this.players)) && (!$set_utils:equal(this:contents(), $set_utils:diff(this:contents(), players))))
who:tell("It appears that a game is in progress, and you aren't in it.  Sorry, I better not reset the game now.");
return E_PERM;
elseif (((this.board != this.board_layout) && this.started) && (!$command_utils:yes_or_no("It appears that a game is in progress. If you are SURE you want to reset the game, type `yes'.")))
player:tell("Game reset aborted.");
return E_NACC;
endif
this.turn = #-1;
this.board = this.board_layout;
this.bag = this.letter_layout;
this.players = this.scores = this.tiles = this.lastmove = this.lastscore = this.task_id = this.dots = this.blank = this.order = this.notify = this.lastpicked = this.board_message = {};
this.history = {{who, "reset", "N/A", 0}};
this:announce_all(player.name, " resets ", this.name, ".");
this.started = this.locked = this.finished = 0;
.
#144:6
"scores -- List score and number of tiles for each player.";
if (!this.players)
player:tell("There is no one playing.");
return E_NONE;
endif
lines = {};
player:tell("Scores:");
for i in [1..length(this.players)]
message = $string_utils:right(tostr(this.scores[i]), 4);
message = (message + ": ") + $string_utils:left(this.players[i].name, 25);
message = (message + " (holding ") + tostr(length(this.tiles[i]));
message = message + " tile";
message = message + ((length(this.tiles[i]) == 1) ? ".)" | "s.)");
lines = {@lines, message};
endfor
player:tell_lines($list_utils:reverse($list_utils:sort(lines)));
.
#144:7
if (!(players = this.players))
player:tell("You ogle the empty tile racks.");
return E_NONE;
elseif (player in players)
player:tell("Everyone hides their tiles from your naughty eyes.");
return E_PERM;
else
tiles = this.tiles;
for i in [1..length(players)]
t = tiles[i];
player:tell($string_utils:left((who = players[i]).name, 18), " ", tostr("[ ", $string_utils:left(this:get_player_info(who, "peer_ok") ? tostr(@t) | tostr(@$list_utils:make(length(t), "-")), 7), " ]"));
endfor
endif
.
#144:8
"turn [on|off]";
"If no args; Whose turn is it?  Else, toggle turn notification on/off.";
where = player in this.players;
if (!this.started)
player:tell("The game hasn't started yet.");
return;
endif
if (!this.players)
player:tell("Nobody is playing.");
return;
endif
if ((!valid(this.turn)) || (!(this.turn in this.players)))
if (valid(p = this.prevturn))
if ((po = p in (o = this.order)) == length(o))
this.turn = o[1];
else
this.turn = o[po + 1];
endif
else
this.turn = this.order[1];
endif
endif
if ((!(player in this.players)) || (!argstr))
if (where = player in this.players)
if (this.turn == player)
player:tell("It's currently your turn!");
return;
endif
endif
player:tell("It is ", this.turn.name, "'s turn.");
elseif ($string_utils:lowercase(argstr) == "on")
this.notify[where] = 1;
player:tell("Turn notification turned on.");
elseif ($string_utils:lowercase(argstr) == "off")
this.notify[where] = 0;
player:tell("Turn notification turned off.");
else
player:tell("Usage: notify [<on|off>]");
endif
.
#144:9
"start [[player_order][ max_players=x]]";
" Start the game with given parameters.";
" player_order is a list of players, the order you'd like the game to be played.  If not given, the order is random.";
" max_players is how many players you wish to limit the game to.  By default, this is set to the amount of current players.  Raise it if you'd like to allow new players to join in later.";
if (!(player in (players = this.players)))
player:tell("You aren't even playing!");
return E_PERM;
elseif (this.started)
player:tell("The game has already started.  Use `order player player ... ' to change the order of play.");
return E_NONE;
elseif (m = match(argstr, "%(max_players%|max%|mp%|max%) *= *%([0-9]?%)"))
if ((max = tonum(substitute("%2", m))) < 1)
player:tell("A maximum of Zero players?  Try a number between 1-9.");
return E_INVARG;
elseif (max < length(players))
player:tell("That's less players than are already playing.  Try again.");
return E_INVARG;
else
this:announce_all(player.name, " restricts the game to ", max, " players.");
this.max_players = max;
argstr = strsub(argstr, m[4], "");
endif
else
"Leave it like it is.";
endif
this.order = order = argstr ? this:parse_order(players, argstr) | $list_utils:randomly_permute(players);
this:announce_all(player.name, " starts the game.");
this.turn = order[1];
this.started = 1;
this:announce_all(argstr ? player.name + " sets the order of play as: " | "The order of play is: ", $string_utils:title_list(order, "", " then "), ".");
.
#144:10
"order [player player ... ] -- With no args, show the order of play.  If given a list of players, change the order to the given sequence.";
if (!this.started)
player:tell("The game hasn't begun yet.");
return E_NACC;
elseif (argstr)
if ((this.board == this.board_layout) || $command_utils:yes_or_no("The game has already started.  Are you SURE you want to change the order?"))
this.order = this:parse_order(this.players, argstr);
endif
endif
player:tell("The order of play is: ", $string_utils:title_list(this.order, "", " then "), ".");
.
#144:11
if (caller != this)
return E_PERM;
endif
this.prevturn = this.turn;
if ((this.turn in this.order) == length(this.order))
this.turn = this.order[1];
else
this.turn = this.order[(this.turn in this.order) + 1];
endif
for x in [1..length(this.players)]
if (this.notify[x])
if (this.players[x] == this.turn)
this.players[x]:tell("It is now your turn.");
else
this.players[x]:tell("It is now ", this.turn.name, "'s turn.");
endif
endif
endfor
this:announce_all_but(this.players, "It is now ", this.turn.name, "'s turn.");
.
#144:12
"pass";
"Pass your turn to the next player.";
if (!(caller in {player, this}))
return E_PERM;
endif
where = player in this.players;
if (!where)
player:tell("You aren't even playing!");
elseif (this.turn != player)
player:tell("It's not even your turn!");
else
this:announce_all(player.name, " declines to do anything during ", player:pp(), " turn.");
this:do_history(player, "pass");
this.lastmove[where] = {"pass"};
this:nextturn();
endif
.
#144:13
if (!(player in this.players))
player:tell("You're not in the game - you can't quit.");
else
this:do_quit(player);
this:recompute_order(player);
endif
.
#144:14
where = player in this.players;
if (!where)
player:Tell("Since you aren't playing, you can't pick tiles.");
return E_PERM;
elseif (length(this.bag) == 0)
player:tell("There are no more tiles in the bag to take!");
this:announce(player.name, " reaches for the bag of tiles and finds it empty.");
return E_INVIND;
elseif ((howmany = length(this.tiles[where])) >= 7)
player:tell("You already have ", howmany, " tiles.");
return E_NACC;
endif
this.lastpicked[where] = "";
msg = {};
picked = 0;
this:do_history(player, "pick " + $string_utils:space(7 - howmany, "-"));
for i in [1..7 - howmany]
if (this.bag)
grabbed = random(length(this.bag));
grabbedtile = this.bag[grabbed];
this.bag = listdelete(this.bag, grabbed);
this.tiles[where] = listappend(this.tiles[where], grabbedtile);
this.lastpicked[where] = this.lastpicked[where] + grabbedtile;
msg = listappend(msg, grabbedtile);
picked = picked + 1;
endif
endfor
info = ((tostr(picked) + " ") + ((picked == 1) ? "tile " | "tiles ")) + "from the bag of letters";
this:announce(player.name, " picks ", info, ".");
player:tell("You pick ", info, ": ", $string_utils:english_list(msg));
if (length(this.bag) == 0)
this:announce(player.name, " took the last tile.");
player:tell("You took the last tile.");
endif
.
#144:15
"undo";
"Undo your last turn.  Or, at least, have the game attempt to do so.";
if (!(caller in {player, this}))
return E_PERM;
endif
where = player in this.players;
if (!where)
player:tell("You're not playing!");
return;
elseif (player != this.prevturn)
player:tell("You did not make the last move.");
return;
elseif (this.lastmove[where] == {})
player:tell("You haven't moved yet - you can't take back any moves.");
return;
elseif (this.lastmove[where] == {"undo"})
player:tell("You already undid your last move.");
return;
elseif (this.lastmove[where] == {"pass"})
this:announce(player.name, " undoes ", player:pp(), " last turn.");
player:tell("You undo your last turn.");
this.lastmove[where] = {"undo"};
this:prevturn();
return;
endif
if (this.lastpicked[where] != "")
this:discard_tiles(this.lastpicked[where]);
this.lastpicked[where] = "";
endif
if (this.lastmove[where][1] == "discard")
retrieved = {};
for tile in (msg = this.lastmove[where][2])
this.tiles[where] = listappend(this.tiles[where], tile);
retrieved = listappend(retrieved, tile);
endfor
picked = length(msg);
info = ((tostr(picked) + " ") + ((picked == 1) ? "tile " | "tiles ")) + "from the bag of letters";
this:announce(player.name, " retrieves ", info, ".");
player:tell("You retrieve ", info, ": ", $string_utils:english_list(retrieved));
this:announce(player.name, " undoes ", player:pp(), " last turn.");
player:tell("You undo your last turn.");
this.lastmove[where] = {"undo"};
this:prevturn();
return;
endif
for i in [1..length(this.lastmove[where])]
x = this.lastmove[where][i][1];
y = this.lastmove[where][i][2];
this.tiles[where] = listappend(this.tiles[where], this.board[y][x]);
temp = this.board[y];
this.board[y] = this.board[y][1..x - 1];
this.board[y] = this.board[y] + this.board_layout[x][y];
this.board[y] = this.board[y] + temp[x + 1..length(temp)];
if (isablank = {y, x} in this.blank)
this.blank = listdelete(this.blank, isablank);
this.tiles[where][length(this.tiles[where])] = "_";
endif
endfor
this.scores[where] = this.scores[where] - this.lastscore[where];
this.lastscore[where] = 0;
this.lastmove[where] = {"undo"};
this:announce(player.name, " undoes ", player:pp(), " last turn.");
player:tell("You undo your last turn.");
while ((h = this.history) && (h[l = length(h)][1] == player))
this.history = listdelete(this.history, l);
endwhile
this:prevturn();
.
#144:16
"show <something>";
"Show various board attributes.  Type `show` alone to see a list.";
where = player in this.players;
if (dobjstr == "board")
this:do_show_board();
elseif (dobjstr == "tiles")
this:do_show_tiles();
elseif (argstr in {"letter", "letters", "dist", "distribution", "letter distribution", "values", "tile values", "letter values"})
this:do_show_distribution();
elseif (dobjstr in {"players", "player", "scores", "score"})
this:score();
elseif (argstr == "bag")
this:do_show_bag();
elseif (dobjstr == "legend")
this:do_show_legend();
elseif (dobjstr == "history")
this:history();
elseif ((dobjstr == "games") || (dobjstr == "saved"))
this:do_show_saved();
else
player:tell("Usage: show <board, tiles, letters, score, bag, legend, history, saved>");
endif
.
#144:17
":do_value(STR word_or_letter) => NUM value of word.";
value = 0;
word = args[1];
values = this.values;
letters = this.letters;
fromvalue = ((c = callers())[length(c)][2] == "value") && (c[1][2] == "compute_score");
for i in [1..length(word)]
if (p = (w = word[i]) in letters)
if ((!fromvalue) || (!strcmp(w, letters[p])))
value = value + values[p];
endif
endif
endfor
return value;
.
#144:18
":do_quit(OBJ player)";
"Remove the given player from the game, adjusting score for unplayed tiles.";
"=> {value of remaining tiles, final score}";
if (!this:is_writable_by(caller_perms(), caller))
raise(E_PERM);
endif
{who} = args;
where = who in this.players;
lost = 0;
this:announce_all(who.name, " quits the game. ", who:psc(), " had ", this.scores[where], (this.scores[where] == 1) ? " point." | " points.");
if (this.tiles[where] != {})
lost = 0;
for i in (this.tiles[where])
this.bag = listappend(this.bag, i);
this.scores[where] = this.scores[where] - this:do_value(i);
lost = lost + this:do_value(i);
endfor
this:announce_all(who:ppc(), " ", length(this.tiles[where]), " unused ", (length(this.tiles[where]) == 1) ? "tile" | "tiles", " go back in the bag... ", who:psc(), " loses ", lost, (lost == 1) ? " point" | " points", " for left-over tiles.");
this:announce_all(who:ppc(), " final score is: ", this.scores[where], ".");
endif
this:do_history(who, "quit", "", "N/A", this.scores[where]);
if (this.board_message != {})
msg = (who.name + ": ") + tostr(this.scores[where]);
this.board_message = {@this.board_message, msg};
endif
score = this.scores[where];
this.players = listdelete(this.players, where);
this.scores = listdelete(this.scores, where);
this.tiles = listdelete(this.tiles, where);
this.lastmove = listdelete(this.lastmove, where);
this.lastscore = listdelete(this.lastscore, where);
this.dots = listdelete(this.dots, where);
this.notify = listdelete(this.notify, where);
this.lastpicked = listdelete(this.lastpicked, where);
this.task_id = listdelete(this.task_id, where);
return {lost, score};
.
#144:19
pass(@args);
if ((what = args[1]) in this.players)
this:announce_all(what.name, " leaves the board.  If ", what:ps(), " does not return, use `boot ", what.name, "' to expel ", what:po(), " from the game.");
what:tell("Hey! You left during a game.  You'd better return soon, or you might be booted.");
endif
.
#144:20
":do_history(player, actions, points)";
"Record the given event in the game history.";
"player  -- Object # of player making the play.  Duh.";
"actions -- Action, or colon-delimited actions, made.";
"points  -- Points scored.";
if (caller != this)
return E_PERM;
endif
who = (typeof(args[1]) == OBJ) ? args[1] | player;
if (!(i = who in this.players))
"...no score data...";
elseif (length(args) < 3)
args = {@args, "N/A", this.scores[i]};
elseif (length(args) < 4)
args = {@args, this.scores[i]};
endif
this.history = listappend(this.history, args);
.
#144:21
"history [num]";
"Show all of the history record, or just the last [num] lines.";
hst = this.history;
len = length(hst);
if (argstr)
x = tonum(argstr);
if ((x > len) || (x < 1))
x = len;
endif
else
x = len;
endif
player:tell($string_utils:left("Player", -12), " ", $string_utils:left("Play", -33), " ", "Score", " ", "Total");
div = tostr($string_utils:space(12, "-"), " ", $string_utils:space(33, "-"), " ", "-----", " ", "-----");
player:tell(div);
for i in [(len + 1) - x..len]
info = hst[i];
name = $string_utils:left((info[1]:name() || info[1].name) || "", -12);
play = $string_utils:left(info[2], -32) + " ";
argn = info[3];
if (typeof(argn) == OBJ)
argn = argn:name() || argn[3].name;
endif
0 && (play = play + $string_utils:left(argn || "", -8));
player:tell(name, " ", play, " ", $string_utils:right(info[3] || "", -5), " ", $string_utils:right(info[4] || "", -5));
endfor
player:tell(div);
.
#144:22
player:tell_lines(this.help_msg);
.
#144:23
"sort/alphabetize";
"       Arrange tiles in alphabetical order.";
"mix/scramble";
"       Scramble tiles.  If mix! is used, give many scrambled versions.";
where = player in this.players;
if (!where)
player:tell("You aren't playing!");
return;
endif
if (length(verb) < 5)
max = length(verb);
else
max = 5;
endif
if ((verb == "sort") || (verb[1..max] == "alpha"))
this.tiles[where] = $list_utils:sort(this.tiles[where]);
player:tell("You sort your tiles: ", $string_utils:from_list(this.tiles[where]));
elseif (verb[length(verb)] != "!")
foo = this.tiles[where];
this.tiles[where] = $list_utils:randomly_permute(foo);
player:tell("You scramble your tiles: ", $string_utils:from_list(this.tiles[where]));
else
dobjstr = tostr(@this.tiles[where]);
player:tell("----- Mixed ", dobjstr, ":");
message = $string_utils:from_list(this:scramble_letters(dobjstr, 36), " ");
for line in (player:linesplit(message, player:linelen()))
player:tell($string_utils:triml(line));
endfor
player:tell("-----");
endif
.
#144:24
"dots -- Pass the old toggle to :options.";
this:options("dots", @args);
.
#144:25
if (this.players == {})
player:tell("There is no one playing - you can't save the game!");
return;
endif
if (!(player in this.players))
player:tell("You are not one of the players in the game - you can't save it.");
return;
endif
if (!this.started)
player:tell("Why save the game? It hasn't even started yet!");
return;
endif
where = "empty" in this.saved;
if (!where)
this.saved = listappend(this.saved, {});
where = length(this.saved);
endif
this.saved[where] = {this.players, this.blank, this.dots, this.history, this.lastscore, this.lastmove, this.scores, this.tiles, this.board, this.bag, this.notify, this.order, time(), this.turn, this.lastpicked};
this:announce_all(player.name, " saves the game in progess. Restore it later using `restore ", where, "'. Use 'show saved' to see saved games.");
this:reset(1);
.
#144:26
if (length(args) != 1)
player:tell("Usage: restore <number>");
player:tell("Use `show saved' to see saved games.");
return;
endif
where = tonum(args[1]);
l = length(this.saved);
if ((where < 1) || (l < where))
player:tell("Sorry, but there's no such save slot.");
return E_RANGE;
elseif (this.saved[where] == "empty")
player:tell("Sorry, but that save slot is empty.");
return E_RANGE;
endif
if ((!(player in this.saved[where][1])) && (player != this.owner))
player:tell("Sorry, you aren't one of the players in that game. You can't restore it.");
return;
endif
if (this.players != {})
player:tell("There seems to be a game in progress. The board needs to be reset before a game can be restored.");
return;
endif
this:reset(1);
this.players = this.saved[where][1];
this.blank = this.saved[where][2];
this.dots = this.saved[where][3];
this.history = this.saved[where][4];
this.lastscore = this.saved[where][5];
this.lastmove = this.saved[where][6];
this.scores = this.saved[where][7];
this.tiles = this.saved[where][8];
this.board = this.saved[where][9];
this.bag = this.saved[where][10];
this.notify = this.saved[where][11];
this.order = this.saved[where][12];
this.turn = this.saved[where][14];
this.lastpicked = this.saved[where][15];
this.task_id = {};
this.board_message = {};
names = {};
for i in (this.players)
names = {@names, i.name};
this.task_id = listappend(this.task_id, {});
endfor
this:announce_all(player.name, " restores game #", tostr(where), ".");
this.saved[where] = "empty";
this.started = 1;
order = {};
for who in (this.order)
order = {@order, who.name};
endfor
this:announce_all("The order of play is: ", $string_utils:english_list(order, "", " then "), ". It is currently ", this.turn.name, "'s turn.");
.
#144:27
this.board_message = {"Final Scores:"};
winner = 0;
scores = $list_utils:make(length(players = this.players));
for i in [1..length(players)]
if (length(this.tiles[i]) == 0)
winner = this.players[i];
endif
endfor
if (winner != 0)
while (length(this.players) > 1)
if ((winner in this.players) != 1)
who = 1;
else
who = 2;
endif
this:announce_all();
info = this:do_quit(this.players[who]);
gained = info[1];
scores[who] = info[2];
this.scores[winner in this.players] = this.scores[winner in this.players] + gained;
endwhile
this:announce_all();
this:announce_all(winner.name, " gets the points for other players' leftover tiles...");
scores[winner in players] = this:do_quit(winner)[2];
else
this:announce_all("No one was out of tiles...");
for i in [1..length(players)]
scores[i] = this:do_quit(players[i])[2];
this:announce_all();
endfor
endif
this.started = 0;
this.finished = 1;
for line in (this.board_message)
this:announce_all(line);
endfor
this:update_rankings(players, scores);
.
#144:28
if ((!iobjstr) || (iobjstr == ""))
player:tell("Usage: surrender to <player>");
return;
endif
who = player:my_match_object(iobjstr);
$command_utils:object_match_failed(who, iobjstr);
if (who < #1)
return;
endif
if (!is_player(who))
player:tell("Thats not even a player.");
return;
endif
if (who == player)
player:tell("You can't surrender your game to yourself!");
return;
elseif (who in this.players)
player:tell(who.name, " is already in the game.");
return;
elseif (who.location != this)
player:tell(who.name, " isn't here.");
return;
endif
this.players[player in this.players] = who;
this.order[player in this.order] = who;
if (this.turn == player)
this.turn = who;
endif
player:tell("You surrender your game to ", who.name, ". You are now out of the game.");
this:announce_all_but({player, who}, player.name, " surrenders ", player:pp(), " game to ", who.name, ". ", player.name, " has left the game.");
who:tell(player.name, " surrenders ", player:pp(), " to you. You are now in the game.");
this:do_history(player, "surrender to " + who.name);
.
#144:29
":parse_placement(command_line) => {word, horz, vert, across, down}";
if (!(m = match(input = args[1], "^%([a-z_]+%) +%(a%|d%|across%|down%|across *at%|down *at%|at%) +%([a-o0-9]+%)$")))
player:tell("Usage: ", cmd = callers()[1][2], " <word> across|down [a-o][1-15]");
player:tell("       ", $string_utils:space(lcmd = length(cmd)), " <word> at [a-o][1-15]  (place a word down)");
player:tell("       ", $string_utils:space(lcmd), " <word> at [1-15][a-o] (place a word across)");
return E_INVARG;
endif
lw = length(word = substitute("%1", m));
direction = substitute("%2", m);
if (pm = match(position = substitute("%3", m), "^%([a-o]?%)%([0-9]%|[0-1][0-5]?%)$"))
across = (direction == "at") ? !(down = 1) | ((direction[1] == "a") ? !(down = 0) | (!(down = 1)));
horz = index("abcdefghijklmno", substitute("%1", pm));
vert = tonum(substitute("%2", pm));
elseif (pm = match(position, "^%([0-9]%|[0-1][0-5]?%)%([a-o]?%)$"))
across = (direction == "at") ? !(down = 0) | ((direction[1] == "a") ? !(down = 0) | (!(down = 1)));
horz = index("abcdefghijklmno", substitute("%2", pm));
vert = tonum(substitute("%1", pm));
else
player:tell("When specifying a vertical play use letter-number.  When playing horizontally, use number-letter.  Letters are between a-o, numbers 1-15.");
return E_INVARG;
endif
if ((across && ((horz + lw) < 17)) || (down && ((vert + lw) < 17)))
"...ok move...";
else
player:tell("You can't place that word there: it would go off the ", across ? "right" | "bottom", " side of the board.");
return E_RANGE;
endif
return {word, horz, vert, across, down};
.
#144:30
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
pass(@args);
this:blow_away();
.
#144:31
"boot <player>";
"Boot the given player from the game.";
players = this.players;
if (!(player in players))
player:tell("You can't boot a player because you aren't playing. If you want to join and someone has left or is idle, have a player boot him or her.");
return E_PERM;
elseif (!players)
player:tell("No one is playing!");
return E_NONE;
elseif ($match_utils:player_match_failed(who = $match_utils:match(dobjstr, players), dobjstr))
return E_INVARG;
elseif (who == player)
player:tell("Don't boot yourself. Type quit.");
return E_RECMOVE;
elseif (!(who in players))
player:tell(who.name, " doesn't seem to be playing.");
return E_INVIND;
endif
this:announce_all(player.name, " boots ", who.name, " from the game!");
this:do_history(player, "boot " + who.name);
this:do_quit(who);
this:recompute_order(who);
.
#144:32
if (!this.started)
player:tell("The game is already over.");
return;
endif
if ((!player) in this.players)
player:tell("You're not even playing!");
return;
endif
this:announce_all(player.name, " officially ends the game.");
this:do_history(player, "endgame");
this:do_endgame();
.
#144:33
if (caller != this)
return E_PERM;
endif
if ((this.prevturn in this.order) == 1)
this.prevturn = this.order[length(this.order)];
else
this.prevturn = this.order[(this.prevturn in this.order) - 1];
endif
if ((this.turn in this.order) == 1)
this.turn = this.order[length(this.order)];
else
this.turn = this.order[(this.turn in this.order) - 1];
endif
for x in [1..length(this.players)]
if (this.notify[x])
if (this.players[x] == this.turn)
this.players[x]:tell("It's now your turn.");
else
this.players[x]:tell("It's now ", this.turn.name, "'s turn.");
endif
endif
endfor
.
#144:34
who = args[1];
where = who in this.order;
this.order = setremove(this.order, who);
if (this.order)
if (this.turn == who)
if (where < length(this.order))
this.turn = this.order[where];
else
this.turn = this.order[1];
endif
this:announce_all("It is now ", this.turn.name, "'s turn.");
endif
endif
.
#144:35
if (length(args))
who = args[1];
if (where = who in (players = this.players))
return this.name + (this.started ? ((" (" + tostr(score = this.scores[where])) + " point") + ((score == 1) ? ")" | "s)") | " [waiting for players]");
elseif (players)
return this.name + " (not playing)";
else
return this.name + " [waiting for players]";
endif
else
pass(@args);
endif
.
#144:36
":tell_contents()";
"Pass to the $room tell_contents behaviour, then print players, if any.";
pass(@args);
p = this.players;
if (!p)
return;
endif
msg = me = un_msg = {};
o = this.order ? $set_utils:intersection(p, this.order) | p;
scores = this.scores;
for c in (o)
if (c.location == this)
msg = {@msg, tostr(c:dname(), " (", score = scores[c in p], " ", $english:pluralize("point", score), ")")};
else
un_msg = {@un_msg, tostr(c:dname(), " (", score = scores[c in p], " ", $english:pluralize("point", score), ")")};
endif
endfor
msg = {@msg, @me};
len = length(msg);
msg = tostr($string_utils:english_list(msg, "Nobody"), (len < 2) ? " is" | " are", " gathered around the board.");
len = length(un_msg);
if (len)
msg = tostr(msg, "  ", $string_utils:english_list(un_msg, "Nobody"), (len < 2) ? " is" | " are", " playing right now, but ", (len == 1) ? "seems" | "seem", " to have left the game indefinitely.");
endif
t = this.turn;
player:tell($string_utils:capitalize(msg), "  ", this.started ? tostr("It is ", (t == player) ? "your" | (t:dname() + "'s"), " turn.") | "The game hasn't started yet.");
.
#144:37
":put_tiles(word, across, xbegin, ybegin[, timage[, bimage]])";
"timage and bimage may be passed and used instead of the current values of the 'tiles' and 'board' properties.";
"=> {LIST newboard, LIST newrack, LIST blankcoords, LIST playcoords, INT blanksused, INT tilesused, LIST tilesused}";
across = args[2];
x = args[3];
y = args[4];
limage = blank = {};
blankimage = this.blank;
usedtiles = {};
connects = 0;
letters = this.letters;
timage = args[5] || this.tiles[player in this.players];
bimage = args[6] || this.board;
for p in [1..length(word = args[1])]
letter = word[p];
connects = connects || this:connects(x, y);
if (((spot = bimage[y][x]) in letters) && (spot != letter))
player:tell("You cannot place the letter ", letter, " at ", y, letters[x], ": there is already a ", bimage[y][x], " there.");
return E_NACC;
elseif (spot == letter)
"...placing across a played tile...";
elseif (!(letter in letters))
player:tell(letter, " isn't valid.  Stick with the alphabet.");
return E_INVARG;
elseif (letter in timage)
usedtiles = {@usedtiles, letter};
if (letter != "_")
timage = setremove(timage, letter);
bimage[y][x] = letters[letter in letters];
limage = listappend(limage, {x, y});
elseif ("_" in timage)
"Player explicitly used a blank '_' in eir move.";
blankletter = $command_utils:read("the letter you wish the blank tile to represent");
timage = setremove(timage, "_");
bimage[y][x] = $string_utils:lowercase(blankletter);
blank = listappend(blank, letters[blankletter in letters]);
limage = listappend(limage, {x, y});
blankimage = {@blankimage, {y, x}};
else
player:tell("You don't have an extra blank tile to do that!");
return E_INVARG;
endif
elseif ("_" in timage)
timage = setremove(timage, "_");
usedtiles = {@usedtiles, letter};
bimage[y][x] = $string_utils:lowercase(letter);
blank = listappend(blank, letters[letter in letters]);
limage = listappend(limage, {x, y});
blankimage = {@blankimage, {y, x}};
else
player:tell("You don't have the ", letter, " required to make that word.");
return E_INVARG;
endif
across ? x = x + 1 | (y = y + 1);
endfor
if (connects)
return {bimage, timage, blankimage, limage, blank, length(usedtiles), usedtiles};
else
player:tell((this.board == this.board_layout) ? "The first play must cross the center square (*)." | "That word doesn't connect with any previously played tiles.");
endif
.
#144:38
":connects(x, y[, board_image]) => True if a play on the given spot connects to an existing word or the center square (*).";
x = args[1];
y = args[2];
board = (length(args) > 2) ? args[3] | this.board;
if (board[y][x] == "*")
return 1;
endif
for pair in ({{0, 0}, {0, -1}, {1, 0}, {0, 1}, {-1, 0}})
if (((spot = board[ty = y + pair[2]][tx = x + pair[1]]) != E_RANGE) && match(spot, "^[a-z]$"))
return 1;
endif
endfor
.
#144:39
if (!(where = player in (players = this.players)))
if (players)
$you:say_action("%N %<ogles> ", players[1].name, "'s tiles with hands clenched; aching to mix them up.");
else
player:tell("You itch to mess with someone's tiles.");
endif
elseif ($set_utils:diff(tiles = this.tiles[where], new = $string_utils:char_list(argstr)) || (length(tiles) != length(new)))
player:tell("You may arrange only the tiles you have; no more, no less.");
else
this.tiles[where] = new = $list_utils:map_arg($string_utils, "capitalise", new);
player:tell("You sort your tiles: ", @new);
endif
.
#144:40
"Show all saved games and when they were saved.";
"Delete games older than .saved_gamed_expire_period.";
printed_anything = 0;
time = time() - this.saved_game_expire_period;
for i in [1..length(saved = this.saved)]
if ((game = saved[i]) == "empty")
"skip it";
elseif ((when = game[13]) < time)
this.saved[i] = "empty";
else
names = {};
turn = saved[i][14];
for id in (this.saved[i][1])
if (valid(id) && is_player(id))
if (id == turn)
names = {@names, id.name + "(*)"};
else
names = {@names, id.name};
endif
else
names = {@names, "the former " + tostr(id)};
endif
endfor
timestr = $time_utils:time_sub("$T-$n-$Y $H:$M", player:time(when));
player:tell("Game #", $string_utils:right(i, 3), ": ", timestr, " -> ", $string_utils:english_list(names));
printed_anything = 1;
endif
endfor
if (!printed_anything)
player:tell("No saved games.");
endif
.
#144:41
player:tell("Legend:");
player:tell("* -center of board");
player:tell("' -double letter score");
player:tell("\" -triple letter score");
player:tell("- -double word score");
player:tell("= -triple word score");
.
#144:42
x = length(this.bag);
if (x == 0)
msg = "completely empty";
elseif (x < 5)
msg = "nearly empty";
elseif (x < 15)
msg = "low on tiles";
elseif (x < 30)
msg = "about 20% full";
elseif (x < 40)
msg = "a little less than half full";
elseif (x < 60)
msg = "about half full";
elseif (x < 75)
msg = "a little over half full";
elseif (x < 90)
msg = "very full";
elseif (x <= 100)
msg = "full";
else
msg = "made of ugly green vinyl";
endif
player:tell("The bag of tiles appears to be ", msg, ".");
.
#144:43
msg = this:compile_board();
player:tell_lines(msg);
.
#144:44
":do_show_dist()";
"Show tile distribution and point values.";
su = $string_utils;
lu = $list_utils;
letters = this.letters;
values = this.values;
tiles = this.letter_layout;
output = {};
for i in [1..length(letters)]
l = letters[i];
output = {@output, su:format("%1: %-2 at %-2 pts", l, lu:count(l, tiles), values[i])};
endfor
player:tell_lines(su:columnize(output, 4));
.
#144:45
"onboard <tile(s)> -- How many of given tile(s) are on the board.";
if (!match(dobjstr, "[a-z_]+"))
player:tell("You must specify one or more valid tiles.");
return E_INVARG;
endif
board = tostr(@$list_utils:flatten(this.board));
board = strsub(strsub(strsub(board, " ", ""), "-", ""), "=", "");
board = strsub(strsub(board, "'", ""), "\"", "");
count = 0;
stats = {};
for i in [1..length(dobjstr)]
count = this:_onboard(tile = dobjstr[i], board);
total = this:_distribution(tile);
stats = {@stats, tostr(count, "/", total, " ", (tile == "_") ? "blanks" | (("\"" + tile) + "\" tiles"))};
endfor
player:tell("There are ", $string_utils:english_list(stats), " on the board.");
this:announce(player.name, " silently counts the tiles on the board.");
.
#144:46
player:tell_lines(this.features_msg);
.
#144:47
"shake bag -- jumbles all tiles in the tile bag";
if (dobjstr != "bag")
player:tell("You shake ", dobjstr, ", then seriously consider mental help.");
elseif (this.bag)
this.bag = $list_utils:randomly_permute(this.bag);
$you:say_action("%N %<shakes> the bag of tiles.");
else
$you:say_action("%N %<crumples> the empty tile bag into a little ball.");
endif
.
#144:48
":set_prop(STR propname, value)";
return this:secure(caller, caller_perms()) ? this.(args[1]) = args[2] | E_PERM;
.
#144:49
":clear_prop(STR propname)";
return this:secure(caller, caller_perms()) ? clear_property(this, args[1]) | E_PERM;
.
#144:50
":get_prop(STR propname)";
return this:secure(caller, caller_perms()) ? this.(args[1]) | E_PERM;
.
#144:51
":secure(caller, caller_perms())";
return (args[1] == this) || $perm_utils:controls(args[2], this);
.
#144:52
player:tell("The score database is currently offline.");
.
#144:53
"Same as `show board'...";
this:do_show_board();
.
#144:54
return $string_utils:pronoun_sub(this.description, this.players);
.
#144:55
where = player in this.players;
if (!where)
player:tell("You have no tiles to look at -- you aren't playing.");
elseif (this:get_player_info(player, "graphics"))
bmsg = "";
tmsg = pmsg = "";
tiles = this.tiles[where];
for p in [1..length(tiles)]
bmsg = tostr(bmsg, " --- ");
tmsg = tostr(tmsg, "|", l = tiles[p], "  |");
pmsg = tostr(pmsg, "|", $string_utils:right(tostr(this:do_value(l)), 3), "|");
endfor
player:tell_lines({"  Your tiles: ", bmsg, tmsg, pmsg, bmsg});
else
player:tell("Your tiles: ", @this.tiles[where]);
endif
.
#144:56
if (caller != this)
"...use new `exchange' command...";
return E_PERM;
elseif (!(player in this.players))
player:tell("You aren't playing -- you can't discard tiles.");
elseif ((!argstr) && (length(args) == 0))
player:tell("Usage: discard <tile or tiles to put back in the bag>");
else
count = 0;
argstr = args[1];
for i in [1..length(argstr)]
if (argstr[i] in this.tiles[player in this.players])
this.tiles[player in this.players] = setremove(this.tiles[player in this.players], argstr[i]);
count = count + 1;
this.bag = listappend(this.bag, this.letters[argstr[i] in this.letters]);
player:tell("...you discard the letter ", argstr[i], ".");
endif
endfor
if (count)
this:announce_all(player.name, " discards ", tostr(count), " tile", (count != 1) ? "s." | ".");
endif
endif
.
#144:57
if (!(where = player in this.players))
player:tell("You can't discard -- you aren't playing.");
return;
elseif (this.turn != player)
player:tell("You can't discard -- it isn't your turn.");
return;
elseif ((!argstr) && (length(args) == 0))
player:tell("Usage: ", verb, " <tile or tiles to put back in the bag>");
return;
elseif (length(this.bag) < 7)
player:tell("Uh-uh.  You ain't gonna get rid of those tiles that easily.");
return;
endif
discarded = {};
for i in [1..length(argstr)]
if (argstr[i] in this.tiles[where])
this.tiles[where] = setremove(this.tiles[where], argstr[i]);
discarded = {@discarded, argstr[i]};
player:tell("...you set aside the letter ", argstr[i], ".");
else
player:tell("You don't have the letter `", argstr[i], "' to ", verb, ".");
endif
endfor
if (!discarded)
return;
endif
count = length(discarded);
this:announce_all(player.name, " lays out ", tostr(count), " tile", (count != 1) ? "s" | "", " for discard.");
this:pick();
letters = this.letters;
for tile in (discarded)
this.bag = listappend(this.bag, letters[tile in letters]);
endfor
this.lastmove[where] = {"discard", discarded};
this:announce_all(player.name, " discards ", tostr(count), " tile", (count != 1) ? "s." | ".");
this:do_history(player, "exchange " + $string_utils:space(count, "-"));
this:nextturn();
.
#144:58
"lock/unlock -- Prevent (or allow again) new players into the game.";
if (!(player in this.players))
player:tell("You are given a painful shock as you attempt to tamper with the locked strongbox.");
return E_PERM;
endif
key = (verb in {"unlock", "lock"}) - 1;
if (this.locked == key)
player:tell("The strongbox is already ", verb, "ed.");
return E_NONE;
elseif (key == 1)
$you:say_action("%N %<locks> the tile racks into an iron strongbox, preventing any new players from joining the game.");
else
$you:say_action("%N %<unlocks> the iron strongbox, pulling several tile racks from it and tossing them into the room shouting, \"Be free!  Play with me!\"");
endif
this.locked = key;
.
#144:59
if (index(verb, "options") && (!args))
player:tell("Your custom game options:");
for on in (this.options_db.options)
player:tell($string_utils:left(on, 14), ": ", this:get_player_info(player, on) ? "On" | "Off");
endfor
elseif (!((option = args[1]) in (options = this.options_db.options)))
player:tell("Game options available: ", $string_utils:english_list(options));
elseif (length(args) < 2)
player:tell("Your \"", option, "\" option is ", this:get_player_info(player, option) ? "ON" | "OFF", ".");
elseif ((flag = args[2]) in {"1", "On", "Yes"})
this:set_player_info(player, option, 1);
player:tell("Option \"", option, "\" turned ON.");
elseif (flag in {"0", "Off", "No"})
this:set_player_info(player, option, 0);
player:tell("Option \"", option, "\" turned OFF.");
else
player:tell("Huh?  You may set an option to either 1/On/Yes or 0/Off/No.");
endif
.
#144:60
":parse_order(players, string) => New order as given by a `player player...' string.";
"Players not included in the string are tacked onto the end of the order.";
players = args[1];
string = args[2];
order = {};
for whostr in ($string_utils:words(string))
if (valid(who = $match_utils:match(whostr, players)))
order = setadd(order, who);
endif
endfor
return {@order, @$set_utils:diff(players, order)};
.
#144:61
"HaHaHa what a stupid name for a verb.";
"Returns the message to give for a certain bonus word-score.";
mult = args[1];
if (mult == 2)
return "over a double-word score ";
elseif (mult == 3)
return "over a triple-word score ";
elseif (mult == 4)
return "over TWO double-word scores ";
elseif (mult == 9)
return "over TWO TRIPLE-WORD SCORES ";
else
return "";
endif
.
#144:62
"max-players <number> -- Restrict the game to `number' players.";
if (!(player in (players = this.players)))
player:tell("You aren't even playing!");
return E_PERM;
elseif ((max = tonum(dobjstr)) < 1)
player:tell("A maximum of Zero players?  Try a number between 1-9.");
return E_INVARG;
elseif (max < length(players))
player:tell("That's less players than are already playing.  Try again.");
return E_INVARG;
else
this:announce_all(player.name, " restricts the game to ", max, " players.");
this.max_players = max;
endif
.
#144:63
"show the board if you're playing";
pass(@args);
if (this.board == this.board_layout)
player:tell("No words have been played.  Type `board` or `b` to see the board.");
elseif (0 && (player in this.players))
this:do_show_board();
else
player:tell("Type `board` or `b` to see the board.");
endif
.
#144:64
":_onboard(tile[, board_image])";
"How many of the given tile are on the board?";
"Board image may be passed, else this.board is used.";
tile = $string_utils:capitalise(args[1]);
count = 0;
is_blank = tile == "_";
board = args[2];
if (!board)
board = tostr(@$list_utils:flatten(this.board));
board = strsub(strsub(strsub(board, " ", ""), "-", ""), "=", "");
board = strsub(strsub(board, "'", ""), "\"", "");
endif
for i in [1..length(board)]
spot = board[i];
if (is_blank && strcmp(spot, $string_utils:capitalise(spot)))
count = count + 1;
elseif (!strcmp(tile, board[i]))
count = count + 1;
endif
endfor
return count;
.
#144:65
":to_percent(NUM cur[, NUM max]) => percent of cur/max";
"If max is Zero, just return cur.";
cur = args[1];
max = (length(args) > 1) ? args[2] | 100;
return max ? ((cur * 10000) / max) / 100 | cur;
.
#144:66
":_distribution(tile) -> Total amount of the given tile.";
return $list_utils:count(args[1], this.letter_layout);
.
#144:67
":_chance_of_drawing(tile)";
"An educated guess as to ones chances of drawing the given tile from the bag, taking into account only those tiles on the board.";
tile = args[1];
used = this:_onboard(tile);
bag = length(this.bag);
total = this:_distribution(tile);
if (!bag)
return E_NONE;
elseif (used == total)
return E_QUOTA;
elseif (bag <= used)
return E_RANGE;
else
chance = this:to_percentage(used || total, bag);
return chance;
endif
.
#144:68
"draw-chance <tile>";
"An educated guess as to ones chances of drawing the given tile from the bag, taking into account only those tiles on the board.";
if ((length(argstr) != 1) || (!match(argstr, "[a-z_]+")))
player:tell("You must specify a single tile.  For example, 'draw-chance E'.");
return E_INVARG;
endif
chance = this:_chance_of_drawing(argstr);
if (chance == E_NONE)
player:tell("The tile bag is empty.");
elseif (chance == E_RANGE)
player:tell("There aren't many tiles in there.  Chances are probably pretty good.");
elseif (chance)
player:tell("You estimate a ", chance, "% chance of finding that tile in the bag.");
else
player:tell("There's no chance of drawing that tile.  All of them have been played.");
endif
.
#144:69
":bag_graphic()";
total_bag_icons = 20;
bag_graphic_div = 5;
total = length(this.letter_layout);
left = $math_utils:to_percentage(length(this.bag), total);
left = this.bag ? max(left / bag_graphic_div, 1) | 0;
remove = total_bag_icons - left;
graphic = $list_utils:flatten(this.bag_graphic);
while (remove && (i = "=" in graphic))
remove = remove - 1;
graphic[i] = " ";
endwhile
return $string_utils:chop(tostr(@graphic), length(this.bag_graphic[1]));
.
#144:70
":compile_board([board, tiles, scores])";
argc = length(args);
{?board = this.board, ?tiles = this.tiles, ?scores = this.scores} = args;
dots = this:get_player_info(player, "dots");
graphics = this:get_player_info(player, "graphics");
EMU = $emu;
charset = player:get_option($display_options, "charset") || $emu.default_charset;
"";
"--Grab all the graphics characters--";
"";
{chr_hl, chr_vl, chr_tlc, chr_trc, chr_blc, chr_brc, chr_dot} = {EMU:entity("sh_line", charset), EMU:entity("sv_line", charset), EMU:entity("shsv_tl_corner", charset), EMU:entity("shsv_tr_corner", charset), EMU:entity("shsv_bl_corner", charset), EMU:entity("shsv_br_corner", charset), EMU:entity("center_dot", charset)};
top_line = (chr_tlc + $string_utils:space(29, chr_hl)) + chr_trc;
bottom_line = (chr_blc + $string_utils:space(29, chr_hl)) + chr_brc;
"";
"--Substitute board icons with appropriate graphics--";
"";
sub = "'\"-=*";
rep = sub;
for i in [1..length(board)]
raw = dots ? strsub(board[i], " ", chr_dot) | board[i];
len = length(raw);
row = "";
for p in [1..len]
if (r = index(sub, raw[p]))
row = tostr(row, rep[r]);
else
row = tostr(row, raw[p]);
endif
if (p != len)
row = tostr(row, " ");
endif
endfor
board[i] = tostr($string_utils:right(i, 2), chr_vl, row, chr_vl, $string_utils:left(i, 2));
endfor
display = {"   a b c d e f g h i j k l m n o"};
display = {@display, "  " + top_line, @board, "  " + bottom_line};
display = {@display, "   a b c d e f g h i j k l m n o"};
players = this.players;
where = player in players;
tilestr = where ? tostr(@tiles[where]) | "";
bagstr = tostr(this:to_percentage(length(this.bag), length(this.letter_layout)), "%");
if (!where)
display[6] = tostr(display[6], "  Bag: ", bagstr);
elseif (graphics)
tbmsg = bbmsg = tmsg = pmsg = "";
my_tiles = tiles[where];
tile_top = tostr(chr_tlc, chr_hl, chr_hl, chr_hl, chr_trc);
tile_bottom = tostr(chr_blc, chr_hl, chr_hl, chr_hl, chr_brc);
tile_side = chr_vl;
for p in [1..length(my_tiles)]
tbmsg = tostr(tbmsg, tile_top);
tmsg = tostr(tmsg, tile_side, l = my_tiles[p], "  ", tile_side);
pmsg = tostr(pmsg, tile_side, $string_utils:right(tostr(this:do_value(l)), 3), tile_side);
bbmsg = tostr(bbmsg, tile_bottom);
endfor
display[3] = tostr(display[3], "    Bag: ", bagstr, "  Your tiles:");
display[4] = tostr(display[4], "  ", tbmsg);
display[5] = tostr(display[5], "  ", tmsg);
display[6] = tostr(display[6], "  ", pmsg);
display[7] = tostr(display[7], "  ", bbmsg);
else
display[6] = tostr(display[6], "  Bag: ", bagstr, "  Your tiles: ", tilestr);
endif
bmsg = this.board_message;
if (bmsg)
for line in [1..length(bmsg)]
display[11 + line] = (display[11 + line] + "  ") + bmsg[line];
endfor
return display;
endif
start = 8;
turn = this.turn;
countdown = length($list_utils:flatten(this.tiles)) < (length(players) * 7);
peer_display = (!where) && this:get_player_info(player, "peer_display");
order = this.order;
for who in (order)
if (pos = who in players)
t = tiles[pos];
base = tostr(display[start = start + 1], "  ", (who == turn) ? "+ (" | "  (", $string_utils:right(tostr(scores[pos]), 3), ") ", $string_utils:left(who.name, 18));
if (peer_display)
display[start] = tostr(base, " [ ", $string_utils:left(this:get_player_info(who, "peer_ok") ? tostr(@t) | tostr(@$list_utils:make(length(t), "-")), 7), " ]");
else
display[start] = tostr(base, countdown ? ("[ " + $string_utils:left(tostr(@$list_utils:make(length(t), "-")), 7)) + " ]" | "");
endif
endif
endfor
start = start + 2;
LAST_USABLE_ROW = 16;
usable = LAST_USABLE_ROW - start;
if (tilestr && (usable > 0))
"show some mixed-up tiles";
available_width = (player:linelen() - length(display[start])) - 1;
count = (usable * available_width) / length(tilestr);
message = $string_utils:from_list(this:scramble_letters(tilestr, count), " ");
for line in (player:linesplit(message, available_width, 0))
display[start] = tostr(display[start], " ", line);
start = start + 1;
endfor
endif
if ((0 && graphics) && this:get_player_info(player, "bag"))
bag = this:bag_graphic();
for line in [1..length(bag)]
lold = length(old = display[13 + line]);
if (lold > 64)
display[13 + line] = old[1..64] + bag[line];
else
display[13 + line] = (old + $string_utils:space(64 - length(old))) + bag[line];
endif
endfor
endif
return display;
.
#144:71
":custom_score(total_score, events, compute_score_data)";
"Use this verb to calculate special bonuses or penalties.  Print any explanatory messages.  Return 0 or {{extra_msgs}, {history_lines}, total_score}.";
return 0;
.
#144:72
":scramble_letters(STR letters[, NUM count])";
"-> COUNT [18] random combinations of given letters.";
letters = $string_utils:char_list(args[1]);
count = min((length(args) < 2) ? 18 | args[2], $math_utils:factorial(length($list_utils:remove_duplicates(letters))));
words = {};
while (length(words) < count)
words = setadd(words, tostr(@$list_utils:randomly_permute(letters)));
endwhile
return words;
.
#144:73
if (!caller_perms().wizard)
return E_PERM;
endif
this:blow_away();
this.options_db = $word_game_options;
pass();
.
#144:74
":blow_away()";
"Used by :initialize and :init_for_core.";
if (caller != this)
return E_PERM;
endif
this.saved = {};
this.players = this.scores = this.tiles = this.lastmove = this.lastscore = this.task_id = this.dots = this.blank = this.order = this.notify = this.lastpicked = this.board_message = {};
this.board = this.board_layout;
this.bag = this.letter_layout;
this.history = {};
this.started = this.turn = 0;
this.order = {};
.
#144:75
"bored";
"Show an ASCII representation of the game board which is less likely to be detected by snooping bosses.";
here = this;
board = {};
hzb = "   A.b.c.d.e.f.g.H.i.j.k.l.m.n.O";
hzb = tostr(hzb, " ", hzb);
i = 1;
board = {@board, hzb};
for line in (here.board)
lineno = $string_utils:right(i, 2, "0");
line = strsub(line, " ", " ");
line_list = $string_utils:char_list(line);
line = $string_utils:from_list(line_list, ".");
board = {@board, tostr(lineno, " ", line, " ", lineno, " ", line, " ", lineno)};
i = i + 1;
endfor
board = {@board, hzb};
scores = (p = player in here.players) ? tostr(@here.tiles[p], ": You = ", here.scores[p], ";") | "";
for i in [1..length(here.players)]
if (here.players[i] != player)
scores = tostr(scores, " ", here.players[i].name, " = ", here.scores[i], ";");
endif
endfor
scores = tostr(scores, " Bag: ", this:to_percentage(length(this.bag), length(this.letter_layout)), "%");
board = {@board, scores};
values = "";
for i in [1..length(here.tiles[p])]
values = tostr(values, here.tiles[p][i], "(", this:do_value(here.tiles[p][i]), ") ");
endfor
board = {@board, values};
player:tell_lines(board);
this:("mix!")();
.
#144:76
"Pass to this.options_db.";
if (caller != this)
raise(E_PERM);
endif
return this.options_db:set_player_info(@args);
.
#144:77
"Pass to this.options_db.";
return this.options_db:get_player_info(@args);
.
#144:78
":allow_fame()";
"May the current game be saved to the Hall of Fame by the current player?";
return this.allow_fame;
.
#144:79
"save-fame -- Saves the board and final score to a Hall of Fame.";
if (this.players)
player:tell("This game is still in progress.");
elseif ($list_utils:iassoc(board = this.board, hall = this.hall_of_fame))
player:tell("This game has already been saved.");
elseif (!this:allow_fame())
player:tell("The owner of this board doesn't want to keep a Hall of Fame, probably due to quota restraints.");
else
"...stop saving history-- it's just too damned long and isn't used...";
this.hall_of_fame = {@hall, {board, this.board_message, {}, time()}};
this:announce_all(player:title(), " produces a tube of Epoxy and glues this most memorable tile configuration to the board, preserving it for future generations.");
endif
.
#144:80
"list-fame";
" Lists all games recorded in this board's Hall of Fame.";
if (hall = this.hall_of_fame)
player:tell($string_utils:nn(this), " has the following games in its Hall of Fame:");
for g in [1..length(hall)]
game = hall[g];
player:tell(" [", $string_utils:right(g, 2), "]: ", player:ctime(game[4]), " (", $string_utils:from_list(listdelete(game[2], 1), ", "), ")");
endfor
else
player:tell($string_utils:nn(this), " has no games in its Hall of Fame.");
endif
.
#144:81
"show-fame <game-index>";
" View the board and final scores of a Hall of Fame game.";
" Use `list-fame' to see a list of notable games saved for posterity.";
if (((index = tonum(dobjstr)) > length(hall = this.hall_of_fame)) || (index < 1))
player:tell_lines($code_utils:verb_documentation($word_game, verb));
return E_INVARG;
endif
game = hall[index];
board = game[1];
message = game[2];
display = {};
where = player in this.players;
display = listappend(display, "   a b c d e f g h i j k l m n o");
display = listappend(display, "   _____________________________ ");
for row in [1..length(board)]
display = listappend(display, $string_utils:right(tostr(row), 2) + "|");
for column in [1..length(board[row])]
x = board[row][column];
if ((where && (x == " ")) && this.dots[where])
x = ".";
endif
display[row + 2] = (display[row + 2] + x) + ((column == length(board[row])) ? "|" | " ");
endfor
endfor
display = listappend(display, "   ----------------------------- ");
for line in [1..length(message)]
display[11 + line] = (display[11 + line] + "  ") + message[line];
endfor
player:tell_lines(display);
player:tell("Saved: ", player:ctime(game[4]));
.
#144:82
"purge-fame [game#|ALL]";
"Purge the given game from the Hall of Fame.";
"If ALL is given, purge all the games.";
if (!args)
player:tell_lines($code_utils:verb_documentation());
return E_ARGS;
endif
fame = this.hall_of_fame;
lenf = length(fame);
if (!lenf)
player:tell("There are no games in this board's Hall of Fame.");
return E_NONE;
elseif (args[1] == "ALL")
answer = $command_utils:yes_or_no(tostr("Are you SURE you want to purge all ", lenf, " games from ", this.name, "?"));
if (!answer)
player:tell("Purging aborted.");
return 0;
else
this.hall_of_fame = {};
player:tell("All games purged.");
return $maxint;
endif
endif
i = $code_utils:tonum(args[1]);
if (i == E_TYPE)
player:tell("You must given either the numeric index of a game, or ALL for all games.");
return E_TYPE;
elseif (!fame[i])
player:tell("There is no game in that slot.  Use 'list-fame' to see all games.");
return E_RANGE;
else
this.hall_of_fame = listdelete(this.hall_of_fame, i);
player:tell("Game ", i, " purged from ", this.name, ".");
return i;
endif
.
#144:83
":do_play(OBJ player)";
"Add the given player to this game.";
if (!this:is_writable_by(caller_perms(), caller))
raise(E_PERM);
endif
{who} = args;
this.players = listappend(this.players, who);
this.scores = listappend(this.scores, 0);
this.tiles = listappend(this.tiles, {});
this.lastmove = listappend(this.lastmove, {});
this.lastscore = listappend(this.lastscore, 0);
this.task_id = listappend(this.task_id, 0);
this.dots = listappend(this.dots, 1);
this.notify = {@this.notify, 1};
this.lastpicked = {@this.lastpicked, ""};
this:do_history(who, "join");
this:announce(who.name, " walks up to the table, sits in front of the game board, and takes a tile rack and some tiles.");
who:tell("Type `instructions' for help, and `features' for a list of special features.");
who:tell("You step up the the the game board and take a tile rack.");
if ((this.started && this.players) && this.order)
who:tell("Your turn will be after ", this.order[length(this.order)].name, "'s.");
this:announce(who.name, "'s turn will be after ", this.order[length(this.order)].name, "'s.");
this.order = {@this.order, who};
endif
this:pick();
.
#145:0
":set_player_info(player, infoname, value) => Sets given player info to `value'.";
if (!(caller_perms() in {who = args[1], this.owner}))
return E_PERM;
elseif ((old = this.(propname = "i_" + args[2])) == E_PROPNF)
return E_PROPNF;
elseif (!((value = args[3]) in old[1]))
return E_INVARG;
elseif (index = $list_utils:iassoc(who, data = old[2]))
return {this.(propname)[2][index][2] = value};
else
return {this.(propname)[2] = {@data, {who, value}}};
endif
.
#145:1
":get_player_info(player, infoname) => Gets a given player's settings.";
if (!(caller_perms() in {who = args[1], this.owner}))
return E_PERM;
elseif ((old = this.("i_" + args[2])) == E_PROPNF)
return E_PROPNF;
elseif (index = $list_utils:iassoc(who, data = old[2]))
return data[index][2];
else
return old[1][1];
endif
.
#145:2
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
for p in (this.options)
this.("i_" + p) = {};
endfor
.
#146:0
":set(ANY <key>, ANY <value>)";
"=> set the value associated to <key> to <value>, insert the new key if needed.";
{table, set_key, set_value} = args;
try
{id, keys, values} = table;
except (E_TYPE)
return `pass(@args) ! E_VERBNF => raise(E_TYPE)';
endtry
if (id != this.id)
return `pass(@args) ! E_VERBNF => raise(E_TYPE)';
endif
if (i = set_key in keys)
values[i] = set_value;
else
keys = {set_key, @keys};
values = {set_value, @values};
endif
return {id, keys, values};
.
#146:1
":get($table <table>, ANY <key>)";
"=> <value> associated to <key>, raise E_RANGE if <key> is not in <table>.";
{table, get_key} = args;
try
{id, keys, values} = table;
except (E_ARGS)
return `pass(@args) ! E_VERBNF => raise(E_TYPE)';
endtry
if (id != this.id)
return `pass(@args) ! E_VERBNF => raise(E_TYPE)';
endif
return values[get_key in keys];
.
#146:2
":keys($table <table>) => LIST keys that are in <table>";
{table} = args;
try
{id, keys, values} = table;
except (E_ARGS)
return `pass(@args) ! E_VERBNF => raise(E_TYPE)';
endtry
if (id != this.id)
return `pass(@args) ! E_VERBNF => raise(E_TYPE)';
endif
return keys;
"Last modified Sat Aug  3 18:28:43 1996 EDT by Richard (#200@TappedIn).";
.
#146:3
{table} = args;
`{id, keys, values} = table ! E_ARGS => raise(E_TYPE)';
(id == this) || raise(E_TYPE);
return $hash:new(keys, values);
.
#146:4
":true($table <table>) => TRUE if <table> is not empty, FALSE otherwise";
{table} = args;
`{id, @data} = table ! E_ARGS => raise(E_TYPE)';
if (id != this.id)
return pass(@args);
else
return table != this:new();
endif
.
#146:5
":get_multiple($table <table>, {ANY <key 1>, ANY <key 2>, ..., ANY <key n>} [, BOOLEAN <don't raise> [, ANY <default value>]])";
"=> {<value 1>, <value 2>, ..., <value N>} the values in <table> associated to <key 1>, ..., <key N> (respectively. raise E_RANGE if <key 1> or <key 2> or ... or <key N> is not in <table> and <don't raise> is FALSE or not provided, otherwise if <key X> is not in <table>, set <value X> to <default value> or E_RANGE.";
{table, get_keys, ?silent = 0, ?default = E_RANGE} = args;
try
{id, keys, values} = table;
except (E_ARGS)
return `pass(@args) ! E_VERBNF => raise(E_TYPE)';
endtry
if (id != this.id)
return `pass(@args) ! E_VERBNF => raise(E_TYPE)';
endif
if (typeof(get_keys) != LIST)
raise(E_INVARG);
endif
data = {};
if (silent)
for k in (get_keys)
data = {@data, `values[k in keys] ! E_RANGE => default'};
endfor
else
for k in (get_keys)
data = {@data, values[k in keys]};
endfor
endif
return data;
.
#146:6
":set_multiple($table <table>, {ANY <key 1>, ANY <key 2>, ..., ANY <key N>}, {ANY <value 1>, ANY <value 2>, ..., ANY <value N>})";
"=> new $table, which is a copy of <table> which ... blahblah, like :set but with multiple key/values.";
{table, set_keys, set_values} = args;
try
{id, keys, values} = table;
except (E_TYPE)
return `pass(@args) ! E_VERBNF => raise(E_TYPE)';
endtry
if (id != this.id)
return `pass(@args) ! E_VERBNF => raise(E_TYPE)';
endif
if (`(lk = length(set_keys)) != length(set_values) ! E_TYPE => 1')
raise(E_INVARG);
endif
for i in [1..lk]
k = set_keys[i];
v = set_values[i];
try
values[k in keys] = v;
except (E_RANGE)
keys = {k, @keys};
values = {v, @values};
endtry
endfor
return {id, keys, values};
"Last modified Sat Aug  3 18:28:42 1996 EDT by Richard (#200@TappedIn).";
.
#146:7
":delete($table <table>, ANY <key>)";
"=> return a $table, which is a copy of <table> with the <key> entry deleted. Raise E_RANGE if <key> is not in <table>.";
{table, del_key} = args;
try
{id, keys, values} = table;
except (E_TYPE)
return `pass(@args) ! E_VERBNF => raise(E_TYPE)';
endtry
if (id != this.id)
return `pass(@args) ! E_VERBNF => raise(E_TYPE)';
endif
index = del_key in keys;
return {id, listdelete(keys, index), listdelete(values, index)};
.
#146:8
":delete_multiple($table <table>, {ANY <key 1>, ANY <key 2>, ..., ANY <key n>} [, BOOLEAN <don't raise>])";
"=> return a $table, which is a copy of <table> with <key 1>, <key 2>, ... entries deleted. Raise E_RANGE ifone of the keys is not in table unless <don't raise> is provided and TRUE, in which case missing entries are just ignored.";
{table, del_keys, ?silent = 0} = args;
try
{id, keys, values} = table;
except (E_TYPE)
return `pass(@args) ! E_VERBNF => raise(E_TYPE)';
endtry
if (id != this.id)
return `pass(@args) ! E_VERBNF => raise(E_TYPE)';
endif
if (silent)
for k in (del_keys)
try
index = k in keys;
keys = listdelete(keys, index);
values = listdelete(values, index);
except (E_RANGE)
endtry
endfor
else
for k in (del_keys)
index = k in keys;
keys = listdelete(keys, index);
values = listdelete(values, index);
endfor
endif
return {id, keys, values};
.
#146:9
":new()  => new empty <$table>";
":new(ANY <key>, ANY <value>) => new <$table> with <value> associated to <key>";
":new_multiple({ANY <key 1>, ANY <key 2>, ..., ANY <key N>}, {ANY <value 1>, ANY <value 2>, ..., ANY <value N>})";
"=> like above but with multiple keys/values";
new = {this.id, {}, {}};
return args ? this:("set" + verb[4..$])(new, @args) | new;
.
#147:0
return $object_utils:isa(args[1], $skill);
.
#147:1
if ($code_utils:verb_loc() != this)
return {};
endif
return this.contents;
.
#147:2
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
for sk in ($object_utils:descendants($skill))
for al in (sk.aliases)
if (`this.(al) ! E_PROPNF' != E_PROPNF)
this.(al) = sk;
break;
endif
endfor
"19980610: Move 'em into this so $rpg:match_skill works.";
move(sk, this);
endfor
.
#147:3
return $object_utils:isa(args[1], $skill);
.
#148:0
{new_name} = args;
if (!$perm_utils:controls(caller_perms(), this))
raise(E_PERM);
endif
this.id = $string_utils:strhash(new_name);
return this.id_name = new_name;
.
#148:1
raise(E_TYPE);
"this method need to be overriden to define a proper type";
.
#149:0
"@jail <pc> for \"reason\"";
"Send the PC to this .jail, move eir things there, set eir home there.  The reason is required and will be sent both to a log of imprisonments and to the accused.  (If it is not given, the jailer will be prompted for a longer explanation.)";
if (!this:is_warden(player))
$error:raise(E_PERM, "You aren't allowed to jail people.");
endif
dobj = $match_utils:match_player(dobjstr);
if ($match_utils:player_match_failed(dobj, dobjstr))
return;
endif
if (iobjstr)
why = {iobjstr};
else
msg = tostr("Please enter a reason for the imprisonment of ", $string_utils:nn(dobj), ".  It will be sent both to *", this.prison_log:mail_name(), " and to ", dobj:dname(), ".");
player:tell(msg);
why = $command_utils:read_lines();
endif
if (!why)
player:tell("You must give a reason for the imprisonment.");
return;
endif
if (this:do_jail_user(dobj))
$mail_agent:send_message(this, {this.prison_log, dobj}, {tostr("Imprisonment of ", $string_utils:nn(dobj))}, {tostr($string_utils:nn(player), " imprisoned ", $string_utils:nn(dobj), " using the ", verb, " command.  Reason follows."), "-----", @why});
else
player:tell("Failed to ", verb, " ", $string_utils:nn(dobj), ".");
endif
.
#149:1
":is_warden(user)";
return $rpg:trusted(args[1]);
.
#149:2
":jail_user(pc)";
"Very heavily wizzed upon.";
if (caller != this)
return E_PERM;
endif
{dobj} = args;
if ($wiz_utils:is_builder(dobj))
return E_INVARG;
endif
old_room = dobj.location;
prison = this.prison;
dobj:notify($string_utils:pronoun_sub(this.convict_msg));
move(dobj, prison);
if (dobj.location != prison)
return E_NACC;
endif
old_room:announce_all_but({dobj, player}, $string_utils:pronoun_sub(this.convict_leave_msg));
dobj:room_announce_all_but({dobj, player}, $string_utils:pronoun_sub(this.convict_arrive_msg));
cu = $command_utils;
ou = $object_utils;
tangible = $tangible;
for o in (dobj.owned_objects)
cu:suspend_if_needed(0);
if ((!ou:isa(o, tangible)) || ou:contains(dobj, o))
continue;
endif
try
move(o, dobj);
if (o.location != dobj)
move(o, prison);
endif
except (ANY)
"...foo...";
endtry
endfor
dobj.home = prison;
this:register_criminal(dobj);
player:notify($string_utils:pronoun_sub(this.jailer_msg));
return 1;
.
#149:3
":is_criminal(who)";
"Return {date_imprisoned, #prison} if the given object has its criminal bit set.";
{who} = args;
return `who.criminal ! E_PROPNF';
.
#149:4
":register_criminal(who)";
"Register the given object as a 'criminal' in the eyes of 'the law'.  Set their criminal property to the current time and the prison object (which can be queried to determine movement allowances, etc).  Will bomb on non-$chars, as well it should.";
if (!this:is_warden(caller_perms()))
raise(E_PERM);
endif
{who} = args;
who.criminal = {time(), this.prison};
.
#149:5
":register_criminal(who)";
"Clear the given criminal, making them just in the eyes of 'the law'.";
if (!this:is_warden(caller_perms()))
raise(E_PERM);
endif
{who} = args;
clear_property(who, "criminal");
.
#149:6
"@unjail <pc> for \"reason\"";
"Reverse charges against the given PC, removing them from Jail and unsetting their 'criminal' bit.";
if (!this:is_warden(player))
$error:raise(E_PERM, "You aren't allowed to release people from prison.");
endif
dobj = $match_utils:match_player(dobjstr);
if ($match_utils:player_match_failed(dobj, dobjstr))
return;
endif
if (!this:is_criminal(dobj))
player:notify(dobj:grammar_sub("%N %<is> not registered as a criminal."));
return;
endif
if (iobjstr)
why = {iobjstr};
else
msg = dobj:grammar_sub(("Please enter a reason for releasing %n (%#) from prison.  It will be sent both to *" + this.prison_log:mail_name()) + " and to %(dname).");
player:tell(msg);
why = $command_utils:read_lines();
endif
if (!why)
player:tell("You must give a reason for releasing.");
return;
endif
this:clear_criminal(dobj);
`dobj.home = $player_start ! ANY';
dobj:moveto($player_start);
$mail_agent:send_message(this, {this.prison_log, dobj}, {tostr("Release of ", $string_utils:nn(dobj))}, {tostr($string_utils:nn(player), " released ", $string_utils:nn(dobj), " using the ", verb, " command.  Reason follows."), "-----", @why});
player:notify(dobj:grammar_sub("%N %<has> been cleared of %p crime.  %S %<has> been moved to $player_start and %p home set there."));
.
#149:7
return $rpg:trusted(args[1]);
.
#152:0
return $list_utils:(verb)(@args);
.
#152:1
return $code_utils:(verb)(@args);
.
#152:2
return $rpg:(verb)(@args);
.
#152:3
":match_object_exactly(string)";
"Like :match_object, but accepts exact matches on a name or  alias only.";
{string, ?where = player.location} = args;
for dude in (where:contents())
if (string in {dude.name, @dude.aliases})
return dude;
endif
endfor
return $failed_match;
.
#152:4
"Pose is more powerful (and prettier) than emote because it conjugates your message for every interested party in the room.  Enter your pose as if you were typing in a first-person voice.";
"";
"-- Pose recognises the following:";
"";
"Names    -- Exact matches on the aliases of gendered objects in the room.";
"Pronouns -- Pronouns will match for the last player mentioned.";
"";
"Additional verbs you wish conjugated to your gender should be prefixed with a period.";
"";
"-- For example:";
"";
"Typed------> .kick chuck, bruising him on his shin as I .laugh madly.";
"You see    | You kick chuck, bruising him on his shin as you laugh madly.";
"Chuck sees | Quinn kicks you, bruising you on your shin as he laughs madly.";
"Others see | Quinn kicks Chuck, bruising him on his shin as he laughs madly.";
"";
"Typing `.kick chuck, bruising chuck on chuck's shin...` would produce the same, except 'him' would translate to 'chuck', since a literal name was specified.";
"";
"-- Special subs:";
"";
"/all /everybody /everyone -> A list of every $player in the room.";
if ((l = length(verb)) < 2)
player:tell("The syntax \"", verb, argstr ? " " + argstr | "", "\" is invalid.  Type `help ", this, ":pose` for instructions on usage of the pose verb.");
return;
elseif (((verb == "pose") && args) && (args[1][1] != "."))
args[1] = "." + args[1];
else
args = {verb, @args};
endif
here = player.location;
la = length(args);
MAX_TITLED_ALL = 6;
if (pos = (("/all" in args) || ("/everyone" in args)) || ("/everybody" in args))
contents = setremove(this:occupants_in(here), player);
if (length(contents) > MAX_TITLED_ALL)
args[pos] = args[pos][2..$];
else
titles = $string_utils:words($string_utils:name_list(contents));
if (pos == 1)
args = (pos == la) ? titles | {@titles, @args[pos + 1..la]};
elseif (pos == la)
args = {@args[1..pos - 1], @titles};
else
args = {@args[1..pos - 1], @titles, @args[pos + 1..la]};
endif
endif
endif
parsed = this:parse_pose_args(@this:args_with_quotes(args));
"messages = this:build_messages(@parsed);";
"msg = messages[1];";
"omsg = messages[2];";
msg = omsg = "";
for w in (msglist = parsed[2])
if (w == "(ps)")
msg = tostr(msg, "you", " ");
omsg = tostr(omsg, player:ps(), " ");
elseif (w == "(pr)")
msg = tostr(msg, "yourself", " ");
omsg = tostr(omsg, player:pr(), " ");
elseif (w == "(po)")
msg = tostr(msg, "you", " ");
omsg = tostr(omsg, player:po(), " ");
elseif (((lw = length(w)) > 1) && ((w[1] == ":") || (w[1] == ".")))
aux_verb = w[2..lw];
if (m = match(aux_verb, "%(.*[A-Z0-9]%)%([^A-Z0-9]+%)$"))
aux_verb = substitute("%1", m);
aux_punc = substitute("%2", m);
else
aux_punc = "";
endif
msg = tostr(msg, $gender_utils:get_conj(aux_verb, $you), aux_punc, " ");
omsg = tostr(omsg, $gender_utils:get_conj("/" + aux_verb, player), aux_punc, " ");
else
msg = tostr(msg, w, " ");
omsg = tostr(omsg, w, " ");
endif
endfor
msg = tostr("You ", msg[1..length(msg) - 1]);
omsg = tostr(player:name(), " ", omsg[1..length(omsg) - 1]);
msg = tostr(msg, match(msg, "[a-z0-9)]$") ? "." | "");
omsg = tostr(omsg, match(omsg, "[a-z0-9)]$") ? "." | "");
subs = {};
for dp in [1..length(involved = parsed[1])]
dude = involved[dp];
subs = {@subs, {tostr("(", dp, ")"), dude:name()}, {tostr("(", dp, "s)"), dude:ps()}, {tostr("(", dp, "p)"), dude:pp()}, {tostr("(", dp, "o)"), dude:po()}, {tostr("(", dp, "r)"), dude:pr()}};
endfor
already_told_player = 0;
for dp in [1..length(involved)]
dude = involved[dp];
msg_subs = subs;
spa = ((dp - 1) * 5) + 1;
spb = ((dp - 1) * 5) + 5;
msg_subs[spa..spb] = {{tostr("(", dp, ")"), "you"}, {tostr("(", dp, "s)"), "you"}, {tostr("(", dp, "p)"), "your"}, {tostr("(", dp, "o)"), "you"}, {tostr("(", dp, "r)"), "yourself"}};
if (dude == player)
tell = $string_utils:substitute(msg, msg_subs);
already_told_player = 1;
else
tell = $string_utils:substitute(omsg, msg_subs);
endif
tell = strsub(tell, "you's", "your", 1);
tell = strsub(tell, "You's", "Your", 1);
"previously also subbed you' with your, but that doesn't make sense, does it?";
dude:tell(tell);
endfor
if (!already_told_player)
player:tell($string_utils:substitute(msg, subs));
endif
player.location:announce_all_but(setadd(involved, player), $string_utils:substitute(omsg, subs));
.
#152:5
":parse_pose_args(cmd-line-args)";
message = args = args || $string_utils:words(argstr);
vi = player in (involved = {player});
pron_props = {"s", "p", "o", "r"};
last_pronouns = {player:ps(), player:pp(), player:po(), player:pr()};
all_preps = $code_utils._all_preps;
for wp in [1..length(args)]
ls = length(string = args[wp]);
match = $string_utils:strip_chars(string, "!.,;:?");
poss = 0;
if (!string)
"...don't bother...";
elseif (match == "me")
message[wp] = "(po)";
elseif (match == "myself")
message[wp] = "(pr)";
elseif (match == "I")
message[wp] = "(ps)";
elseif (p = match in last_pronouns)
"pronoun referring to a matched character";
message[wp] = tostr("(", tostr(vi), pron_props[p], ")");
elseif ((m = match(match, "%(%w+%)s$")) && (p = substitute("%1", m) in last_pronouns))
"reflexive possessive referring to a matched character";
"ie: \"That's his.\" should parse to \"That's yours.\" for `him'.";
message[wp] = tostr("(", tostr(vi), pron_props[p], ")s");
else
if (poss = string in {"my", "mine"})
victim = player;
elseif (poss = $match_utils:parse_possessive_reference(match))
victim = this:match_object_exactly(poss[1]);
else
victim = this:match_object_exactly(match);
endif
if (this:is_character(victim))
ovi = victim in involved;
vi = victim in (involved = setadd(involved, victim));
if (!poss)
"It isn't possessive.";
word = tostr("(", vi, ")");
elseif (string == "mine")
"mine -> yours, hers, etc";
"grr.. this'll screw up outwardly for pps ending in 's'";
word = tostr("(", vi, "p)", "s" || (match(victim:pp(), "s$") ? "" | "s"));
elseif (ovi || (typeof(poss) != LIST))
"The possessive pronoun has a clear antecedent.";
word = tostr("(", vi, "p)");
else
"Antecedent was found by parsing for a possessive.";
"Use the victim's name, suffixed with possessive punctuation.";
word = tostr("(", vi, ")", strsub(match, poss[1], ""));
endif
punctuation = (m = match(string, "%(%W*%)$")) ? substitute("%1", m) | "";
message[wp] = word + punctuation;
last_pronouns = {victim:ps(), victim:pp(), victim:po(), victim:pr()};
endif
endif
endfor
return {involved, message};
.
#152:6
":occupants_in(OBJ room)";
{room} = args;
o = {};
for what in (room:env())
if ($rpg:is_character(what))
o = {@o, what};
endif
endfor
return o;
.
#153:0
"gibber  <text>";
"Say `text' as an english-sounding encryption.";
if (argstr)
argstr = this:to_gibberish(argstr, this:get_seed_for_user(player));
endif
$you:say_action(tostr("%N %<gibbers>, \"", argstr, "\""));
.
#153:1
":gibber_seed()";
vowels = tmp = this.vowels;
v_pool = "";
while (tmp)
v_pool = v_pool + tmp[i = random($)];
tmp[i..i] = "";
endwhile
consonants = tmp = this.consonants;
c_pool = "";
while (tmp)
c_pool = c_pool + tmp[i = random($)];
tmp[i..i] = "";
endwhile
return v_pool + c_pool;
.
#153:2
":to_gibberish(text, seed)";
{text, ?seed = this:gibber_seed()} = args;
key = this.vowels + this.consonants;
UC = this.UPPERCASE;
for i in [1..length(text)]
old = text[i];
if (p = index(key, old))
sub = seed[p];
else
sub = old;
endif
text[i] = index(UC, old, 1) ? UC[index(UC, sub)] | sub;
endfor
return text;
.
#153:3
":get_seed_for_user(OBJ user)";
{user} = args;
if (e = $list_utils:assoc(user, this.user_seeds))
return e[2];
endif
maxlen = this.max_seed_record;
if (length(this.user_seeds) > maxlen)
this.user_seeds[1..maxlen / 2] = {};
endif
seed = this:gibber_seed();
this.user_seeds = listappend(this.user_seeds, {user, seed});
return seed;
.
#153:4
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.user_seeds = {};
.
#154:0
"eat crystal";
"Eating a life crystal is said to invigorate the consumer, resulting in anything from a healthy glow to miraculous healing.";
if (!player:is_holding(this))
player:tell("You aren't holding ", this:dnamec(), ".");
return;
endif
player:maybe_queue_action(tostr("eat ", dobjstr), {this, "eat"}, {dobjstr}, 80 - player:quickness());
.
#154:1
":eaten_by(user, amount)";
"Deal with amount crystals in the mouth of user.";
{user, amount} = args;
if (!amount)
player:tell("You feel nothing.");
return;
endif
remaining = amount;
msg = tostr("The ", amount, " ", $english:pluralize("crystal", amount), " ", (amount == 1) ? "dissolves" | "dissolve", " quickly in your mouth, melting into a sweet tasting liquid.  ", (amount == 1) ? "Its" | "Their", " purity flows through you.");
user:tell(msg);
health = mobility = boosts = {};
msg = "";
perfect_health = 1;
"----- (1) HEALTH MONITORS-----";
info = {{"fatigue", 20, "completely rested"}, {"insanity", 3, "thoroughly sane"}, {"injury", 1, "in perfect health"}};
for set in (info)
{prop, rate, adj} = set;
stat = user.(prop);
if (stat)
perfect_health = 0;
full = max(stat / rate, 1) + 1;
used = min(remaining, full);
stat = max(stat - (used * rate), 0);
user.(prop) = stat;
if (!stat)
health = {@health, adj};
endif
"remaining = remaining - used";
endif
endfor
if (health)
msg = tostr("You feel ", $string_utils:english_list(health), ".");
endif
"----- (2) MOBILITY-----";
mobility = {};
if (remaining && ((um = user.upper_mobility) < 100))
perfect_health = 0;
used = min(100 - um, remaining);
user.upper_mobility = um + used;
if (user.upper_mobility == 100)
mobility = {@mobility, "arms"};
endif
"remaining = remaining - used";
endif
if (remaining && ((lm = user.lower_mobility) < 100))
perfect_health = 0;
used = min(100 - lm, remaining);
user.lower_mobility = lm + used;
if (user.lower_mobility == 100)
mobility = {@mobility, "legs"};
endif
"remaining = remaining - used";
endif
if (mobility)
msg = tostr(msg, msg && "  ", "A youthful litheness returns to your ", $string_utils:english_list(mobility), ".");
endif
"----- (3) ATTRIBUTE BOOSTS-----";
if (!msg)
"Only boost if the character is already in perfect health.";
att = $list_utils:random_element($rpg.atts);
old = user:get_stat(att);
new = old + 1;
used = this:stat_cost(new);
if (remaining >= used)
remaining = remaining - used;
boosts = {@boosts, att};
user:set_stat(att, old + random(3));
endif
endif
if (boosts)
msg = tostr(msg, msg && "  ", "You are overwhelmed by rushes of ", $string_utils:english_list(boosts), ".");
endif
if (!msg)
msg = tostr("You feel ", perfect_health ? "invigorated" | "somewhat better", ".");
endif
this:set_amount(this.amount - amount);
user:tell(msg);
"19980916-1818: Rewrote to allow health and mobility improvements to occur concurrently.  Att boosts still require perfect health.  Better messages.";
.
#154:2
"feed crystal to someone";
"Eating a life crystal is said to invigorate the consumer, resulting in anything from a healthy glow to miraculous healing.";
if (!player:is_holding(this))
player:tell("You aren't holding ", this:dnamec(), ".");
return;
endif
iobj = player:my_match_object(iobjstr);
if ($match_utils:object_match_failed(iobj, iobjstr, player:env()))
return;
elseif (!$rpg:is_char(iobj))
player:tell(iobj:dnamec(), " isn't interested in eating anything.");
return;
elseif (!iobj:trusts(player, "feed"))
player:tell(iobj:dnamec(), " doesn't trust you to feed ", iobj:po(), ".");
return;
endif
if (iobj == player)
this:eat();
else
player:maybe_queue_action(tostr("feed ", dobjstr, " to ", iobj:dname()), {this, "feed"}, {dobjstr, iobj}, 100 - player:quickness());
endif
.
#154:3
":stat_cost(new_stat_rank)";
"How many crystals must be eaten to rise to the given new rank?  It requires increasingly more, squaring the rank over 100.  Consider it the penalty of tolerance to the crystals.";
r = args[1];
if (r < 50)
return r * 5;
elseif (r < 75)
return r * 10;
elseif (r < 90)
return r * 15;
elseif (r <= 100)
return r * 30;
else
return (r * r) * (r - 100);
endif
"96-08-23: Square rank cost above 100.";
"97-09-11: Times again by (r - 100).";
.
#154:4
":do_eat(dobjstr)";
if (!player:is_holding(this))
player:tell("You aren't holding ", this:dnamec(), ".");
return;
endif
{dobjstr} = args;
amt = this:amount_from_string(dobjstr);
if (typeof(amt) == ERR)
player:tell("Eat how many?");
return;
endif
amt = min(amt, this.amount);
if (amt < 1)
player:tell("You eat none of them.");
return;
endif
player:room_announce($string_utils:pronoun_sub(tostr("%N %<tosses/toss> ", $string_utils:english_number(amt), " ", $english:pluralize("crystal", amt), " into %p mouth.")));
this:eaten_by(player, amt);
.
#154:5
":do_feed(dobjstr, iobj)";
if (!player:is_holding(this))
player:tell("You aren't holding ", this:dnamec(), ".");
return;
endif
{dobjstr, iobj} = args;
amt = this:amount_from_string(dobjstr) || this.amount;
amt = min(amt, this.amount);
if (amt < 1)
player:tell("You feed none of the crystals to ", iobj:dname(), ".  Happy?");
return;
endif
if (iobj:unconscious())
$you:say_action(tostr("%N %<takes> %i in %p arms and gently %<places> ", $string_utils:english_number(amt), " ", $english:pluralize("crystal", amt), " in %[ipp] mouth.  %S %<moves> %[ipp] jaws and %<massages> %[ipp] throat to induce swallowing."));
else
$you:say_action(tostr("%N %<tosses/toss> ", $string_utils:english_number(amt), " ", $english:pluralize("crystal", amt), " into the waiting jaws of %[idname]."));
endif
this:eaten_by(iobj, amt);
.
#154:6
if (!$rpg:trusted(caller_perms()))
raise(E_PERM);
endif
return pass(@args);
"19981013: Allow any GM to destroy a pile of crystals.";
.
#155:0
":acceptable(what)";
"Allow an object in iff it is located on the victim.";
return valid(v = this.victim) ? $object_utils:contains(v, args[1]) | pass(@args);
.
#155:1
":setup_for(victim, killer, weapon)";
"Set the victim property, name, aliases description from the desc_template message, move all contents of the victim to the corpse, and finally move the corpse to the victim's location.";
"WIZARDLY move() to save time.";
{victim, killer, weapon} = args;
if (valid(this.victim) && (this.victim != victim))
return E_NACC;
endif
this.victim = victim;
name = tostr("corpse of ", victim:iname());
this:set_name(name);
this:set_aliases({"corpse", name});
desc = $string_utils:pronoun_sub(this.desc_template_msg, victim);
this:set_description(desc);
c = $code_utils:call_verb(victim, "crystal_worth", {killer, weapon});
this.crystal_worth = max(c || 0, 0);
ou = $object_utils;
"record everything (*recursively*) carried by the victim";
everything = ou:all_contents(victim);
"move the immediate contents to the corpse";
for i in (victim:contents())
i:moveto(this);
endfor
"now go through the previously recorded items, bringing back the bonded";
for i in (everything)
if (i:is_bonded_to(victim) && (!ou:contains(victim, i)))
i:moveto(victim);
endif
endfor
"--now no contained items should have been 'spilled' into the corpse";
this.tod = time();
where = victim:room();
where:bless_for_entry(this);
this:moveto(where);
.
#155:2
$you:say_action("%N %<reaches> around %[tdname], attempting to take %[tpo] into %p arms.");
return this:rot();
"--- we'll be nice for now ---";
m = "%N %<cackles> with glee, hefting %[tdname] into %p arms with a wild fire of sick sexual excitment in %p eyes.  %N suddenly %<becomes> aware of disgusted eyes staring at %o and gently %<lowers> %[tdname] back to %[tpp] place of death, ashamed.";
$you:say_action(m);
.
#155:3
"loot corpse";
"Take all you can from the corpse.  Equivalent to `get all from corpse`.";
iobjstr = dobjstr;
this:remove(dobjstr = "all", prepstr = "from", iobjstr);
.
#155:4
"get <loot> from corpse";
"Get a specific object from the corpse.";
"";
"get everything from corpse";
"Take all you can from the dearly departed.";
"";
"This attempts to find 'generic' objects and prevent them from being taken, so GMs don't have unnecessary pains at death.";
if (dobjstr in {"all", "everything"})
loot = this:matching_contents();
else
dobj = this:match_contents(dobjstr);
if ($match_utils:object_match_failed(dobj, dobjstr, this:matching_contents(), tostr("in ", this:dname("mangled"))))
return;
endif
loot = {dobj};
endif
v = this.victim;
keep = {};
for dobj in (loot)
if (((!$object_utils:isa(dobj, $tangible)) || dobj.f) || children(dobj))
keep = setadd(keep, dobj);
else
dobj:moveto(player);
endif
endfor
loot = $set_utils:diff(loot, keep);
if (keep)
player:tell(v:dnamec(), " won't give up ", $string_utils:iname_list(keep), ".  Perhaps you should show a just a little respect for the dead, you vile graverobber.");
endif
if (!loot)
if (keep)
return;
endif
player:tell("There doesn't seem to be anything of worth left on ", this:dname(), ".");
endif
dobj = loot;
$you:say_action("%N %<pries> %[diname] from the death grip of %[tdname], snickering with morbid glee.", player, this.victim);
.
#155:5
pass(@args);
c = this:contents();
if (c)
player:tell($string_utils:iname_listc(c), " ", (length(c) < 2) ? "is" | "are", " strewn about the body.");
else
player:tell($string_utils:pronoun_sub("%S %<seems> to have long since been stripped of %p worldly possessions.", this.victim));
endif
.
#155:6
":destroy()";
"Recycle this corpse object.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
elseif (((verbs(this) || properties(this)) || this.f) || children(this))
return E_NACC;
endif
r = this:room();
for o in (this:contents())
o:moveto(r);
endfor
if ($perm_utils:controls($code_utils:verb_perms(), this))
$code_utils:call_verb(this.victim, "clear_corpse", {});
$recycler:_recycle(this);
else
this:moveto($nothing);
endif
return !$recycler:valid(this);
.
#155:7
"rot corpse";
"Cause the corpse to rot and blow away in the wind.";
if ((!this:is_controllable_by(player)) && (!player:is_local_to(this)))
player:tell("You'd have to be closer to give that corpse the touch of death.");
return E_PERM;
endif
victim = this.victim;
if (c = this.contents)
sub = $string_utils:iname_list(c);
else
sub = "";
endif
amt = this.crystal_worth;
new = amt && $rpg.crystal:spawn(amt);
if ((typeof(new) == OBJ) && valid(new))
new:moveto(r = this:room());
if (new.location == r)
sub = tostr("a glittering collection of ", $string_utils:english_number(amt), " ", $english:pluralize("crystal", amt), sub && (" scattered over " + sub));
else
new:destroy();
endif
endif
sub = sub || "a handful of dust dispersing in a chill phantom wind";
msg = $string_utils:pronoun_sub(strsub(this.rot_msg, "$loot", sub), this);
this:room_announce_all_but({this}, msg);
return this:destroy();
.
#155:8
":spawn(victim, killer, weapon)";
"Spawn a corpse for victim, killed by killer wielding weapon.";
if ((caller != this) && (!$rpg:trusted(caller_perms())))
return E_PERM;
endif
new = $rpg:spawn(this.root);
if (typeof(new) != OBJ)
return new;
endif
new:setup_for(@args);
return new;
.
#155:9
":is_writable_by(cp, caller)";
return pass(@args) || (this.victim in args);
.
#155:10
":is_controllable_by(cp, caller)";
return pass(@args) || $rpg:trusted(args[1]);
.
#155:11
":set_crystal_worth(num)";
if (typeof(w = args[1]) != NUM)
return E_TYPE;
endif
return this.crystal_worth = w;
.
#155:12
"kick corpse";
"Alternative to taking it.";
$you:say_action("%N %<twists> an indignant snarl at %[tdname], then %<swings> back %p foot, sending it crashing through the lifeless shell.");
this:rot();
.
#155:13
"bash corpse with weapon";
iobj = player:my_match_object(iobjstr, c = player:matching_contents());
if ($match_utils:object_match_failed(iobj, iobjstr, c, "on you"))
return;
endif
$you:say_action("%N %<belches> forth a sadistic howl then %<bears> down on %[tdname] with %p %i, sinking the weapon into the bloated cadaver.");
this:rot();
.
#155:14
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.root = this;
.
#156:0
":hear_event_attack(npc, attacker, target)";
" -> [priority, obj, method, args]";
npc = args[1];
args = listdelete(args, 1);
debug = this.debug;
if (args[2] != npc)
debug && npc:room_announce_all(npc.name, "-> Defender (", args[2].name, ") does not equal npc.  Aborting.");
return;
endif
npc_inj = npc:stat_injury();
end = npc:stat_endurance();
att = args[1];
if (npc_inj > (end + random(end)))
debug && npc:room_announce_all(npc.name, "-> Sending flee action.");
return {1, npc, "do", {"flee"}};
elseif ((npc_inj > ((end / 5) * 3)) && (random(5) == 1))
debug && npc:room_announce_all(npc.name, "-> Sending dodge action.");
return {1, npc, "do", {tostr("dodge ", att)}};
endif
room = npc:room();
if ((0 && (att:stat_injury() > (att:stat_endurance() / 2))) && (length(exits = room:obvious_exits()) == 1))
exit = exits[1];
if (!exit:is_blocked_by(npc))
return {1, npc, "do", {tostr("block ", exit)}};
endif
endif
debug && npc:room_announce_all(npc.name, "-> Sending attack action.");
weapon = npc:weapon();
return {1, npc, "do", {tostr("attack ", att)}};
.
#156:1
":hear_event_heal(npc, doctor, patient, medkit, success)";
" -> [priority, obj, method, args]";
this.debug && this.owner:tell("-> ", this, ":", verb, "(", $string_utils:print(args), ")");
npc = args[1];
args = listdelete(args, 1);
if ((npc in args) > 1)
return;
endif
attacking = npc:attacking();
if (args[2] in attacking)
"...was attacking doctor...";
who = args[2];
elseif (args[3] in attacking)
"...was attacking patient...";
who = args[3];
else
return;
endif
end = npc:stat_endurance();
att = who;
if (npc.injury > (end + random(end)))
return {1, npc, "do", {"flee"}};
elseif ((npc.injury > ((end / 5) * 3)) && (random(10) == 1))
return {1, npc, "do", {tostr("dodge ", att)}};
else
weapon = npc:weapon();
return {1, npc, "do", {tostr("attack ", att)}};
endif
"Replaced these queue actions with the :do above.  <Quinn; 970322-1416>";
{1, npc, "queue_action", {"flee", "flee anywhere", {}, 30 - npc:stat_quickness()}};
{1, npc, "queue_action", {"dodge", "dodge " + att.name, {att}, 20 - npc:stat_quickness()}};
{1, npc, "queue_action", {"attack", "attack " + att.name, {att, -100}, (20 - npc:stat_quickness()) + weapon:slowness(npc, who)}};
.
#156:2
":hear_event_spell(npc, attacker, target, spellname, violent?)";
npc = args[1];
args = listdelete(args, 1);
attacking = npc:attacking();
if (((args[2] == npc) && args[4]) || (args[1] in attacking))
att = args[1];
elseif (args[2] in attacking)
att = args[2];
else
return;
endif
npc_inj = npc:stat_injury();
end = npc:stat_endurance();
if (npc_inj > (end + random(end)))
"flee, too injured.";
return {1, npc, "do", {"flee"}};
endif
room = npc:room();
if ((0 && (att:stat_injury() > (att:stat_endurance() / 2))) && (length(exits = room:obvious_exits()) == 1))
exit = exits[1];
if (!exit:is_blocked_by(npc))
return {1, npc, "do", {tostr("block ", exit)}};
endif
endif
"Well, attack em!";
weapon = npc:weapon();
return {1, npc, "do", {tostr("attack ", att)}};
.
#157:0
"Attributes should not improve naturally greater than 100.";
char = args[1];
if (char:get_stat(this.property) >= 100)
return 0;
else
return pass(@args);
endif
.
#157:1
"No skill bonus for attributes.";
return 0;
.
#157:2
"Att bonus is this attribute's bonus.";
pc = args[1];
total = this:current_rank_for(pc);
return $rpg:att_bonus(total);
.
#158:0
inc = {};
for kid in (children(this))
if (!children(kid))
inc = {@inc, kid};
endif
endfor
return inc;
.
#159:0
":call(obj)";
"Check if the player has been unconnected for more than a week.  If so, remove them from the heart.  They'll be added again in :confunc if and when they reconnect.";
if (caller != this)
return E_PERM;
endif
object = args[1];
laston = $code_utils:get_prop(object, "last_connect_time");
if (laston < (time() - this.max_disconnected_seconds))
return this:remove(object);
else
return pass(@args);
endif
.
#161:0
":severity_string(NUM damage_level) -> STR description of said damage.";
strings = this.severity_strings;
return strings[min(args[1], length(strings))];
.
#161:1
":swing/miss/dodge(weapon, attacker, defender, location)";
"%N -- Attacker  %D -- Defender  %T -- Weapon  %L -- Body location";
message = $list_utils:random_element(this.(verb));
return {message, args[2], args[1], args[4], args[3]};
.
#161:2
":parry(weapon, attacker, defender, location, opposing_weapon)";
"%N -- Attacker  %D -- Defender  %T -- Weapon  %L -- Body location";
"%I -- Opposing Weapon";
message = $list_utils:random_element(this.parry);
return {message, args[3], args[1], args[4], args[2], args[5]};
.
#161:3
":hit(weapon, attacker, defender, location, severity)";
"%N -- Attacker  %D -- Defender  %T -- Weapon  %L -- Body location";
"%severity -- Severity.";
severity = this:severity_string(args[5]);
if (this.(severity))
message = $list_utils:random_element(this.(severity));
else
message = $list_utils:random_element(this.hit);
message = strsub(message, "%severity", severity);
endif
return {message, args[2], args[1], args[4], args[3]};
.
#161:4
":kill/knockout(weapon, attacker, defender, location, severity)";
"%N -- Attacker  %D -- Defender  %T -- Weapon  %L -- Body location";
"Severity passed in case you might wanna disintegrate someone.";
message = $list_utils:random_element(this.(verb));
return {message, args[2], args[1], args[4], args[3]};
.
#161:5
":announce(message, @actors)";
msg = args[1];
a = listdelete(args, 1);
told = {};
tokes = {"%n", "%d"};
su = $string_utils;
y = $you;
for i in [1..length(a)]
if (tokes && $rpg:is_char(c = a[i]))
toke = tokes[1];
tokes = listdelete(tokes, 1);
if ((is_player(c) && (!(c in told))) && (idle_seconds(c) != E_INVARG))
$rpg:notify(c, su:pronoun_sub(y:fixpos(msg, toke), @listset(a, y, i)));
told = {@told, c};
endif
endif
endfor
a[1]:room_announce_all_but(told, su:pronoun_sub(msg, @a));
.
#161:6
"upload [<message>] to set";
"-- Upload combat messages into the set.";
"Message is one of the following: swing, miss, dodge, parry, hit, knockout, kill";
"If no message is given, accept them one each into the above.";
lp = length(props = {"swing", "miss", "dodge", "parry", "hit", "knockout", "kill"});
if (!this:is_writable_by(w = callers() ? caller_perms() | player))
w:tell("You don't have write-permission to ", $string_utils:nn(this), ".");
elseif (!dobjstr)
player:tell("Enter ", $string_utils:english_number(lp), " lines with a message each for ", $string_utils:english_list(props), " respectively.");
if (lines = $command_utils:read_lines())
for i in [1..h = min(length(lines), lp)]
this.(props[i]) = {lines[i]};
endfor
player:tell("Messages ", $string_utils:english_list(props[1..h]), " set.");
else
player:tell("No lines entered.  Message upload aborted.");
endif
elseif (!(dobjstr in props))
player:tell("You may choose from one of the following: ", $string_utils:english_list(props), ".");
else
player:tell("Enter lines as messages for \"", dobjstr, "\".");
if (lines = $command_utils:read_lines())
this.(dobjstr) = lines;
player:tell("\"", dobjstr, "\" messages set.");
else
player:tell("No lines entered.  Message upload aborted.");
endif
endif
.
#161:7
"view -- View all combat messages on this object.";
props = {"swing", "miss", "dodge", "parry", "hit", "knockout", "kill", @this.severity_strings};
player:tell("-- Combat Messages on ", $string_utils:nn(this), " --");
for p in (props)
if (messages = this.(p))
player:tell(":: ", p);
for line in (messages)
player:tell("   ", line);
endfor
endif
endfor
player:tell("-- Done --");
.
#161:8
":kill(weapon, attacker, defender, location, severity)";
"%N -- Attacker  %D -- Defender  %T -- Weapon  %L -- Body location";
"Severity passed in case you might wanna disintegrate someone.";
where = args[4];
victim = args[2];
if (this.head_kills && (where:matches("head") || where:matches("neck")))
message = $list_utils:random_element(this.head_kills);
elseif (this.torso_kills && (where:matches("chest") || where:matches("abdomen")))
message = $list_utils:random_element(this.torso_kills);
elseif (this.groin_kills && ((victim:gender_adj() == "male") && where:matches("groin")))
message = $list_utils:random_element(this.groin_kills);
else
message = $list_utils:random_element(this.kill);
endif
return {message, args[2], args[1], args[4], args[3]};
.
#161:9
":announce_swing/miss/dodge(weapon, attacker, defender, location)";
":announce_parry(weapon, attacker, defender, location, opposing_weapon)";
if (message = this:(verb[10..length(verb)])(@args))
this:announce(@message);
endif
.
#161:10
":announce_hit/knockout/kill(weapon, attacker, defender, location, severity)";
"Check if the defender has a custom message routine.";
def = args[3];
if ($code_utils:call_verb(def, "my_" + verb, args) != E_VERBNF)
return;
elseif (message = this:(verb[10..length(verb)])(@args))
this:announce(@message);
endif
.
#164:0
return this.damage * 5;
.
#166:0
"@reap info username";
" Show reaping information for the given user.";
"@reap next";
" Like @reap info <name of next user eligible for reaping>";
"@reap protect username";
" Protect the given user from reaping.";
"@reap kill username";
" Kill the user; grant all their items to $executor and recycle them.";
if (!caller_perms().wizard)
raise(E_PERM, "Only a wizard may use the @reap command.");
endif
{?cmd = 0, @args} = args;
if ((!cmd) || (cmd == "next"))
user = this:next_reapable();
if (!valid(user))
player:notify("Nobody is up for reaping.");
else
this:do_show_info(user);
endif
return;
elseif (cmd == "purge")
this:do_purge();
return;
elseif (cmd == "info")
if (!args)
"fail";
elseif (!$match_utils:player_match_failed(user = $match_utils:match_player(args[1]), args[1]))
this:do_show_info(user);
return;
else
return;
endif
elseif (cmd == "protect")
if (!args)
"fail";
elseif (!$match_utils:player_match_failed(user = $match_utils:match_player(args[1]), args[1]))
this:do_protect(user);
return;
else
return;
endif
elseif (cmd == "skip")
if (!args)
"fail";
elseif (!$match_utils:player_match_failed(user = $match_utils:match_player(args[1]), args[1]))
this:do_skip(user);
return;
else
return;
endif
elseif (cmd == "kill")
if (!args)
"fail";
elseif (!$match_utils:player_match_failed(user = $match_utils:match_player(args[1]), args[1]))
this:do_kill(user);
return;
else
return;
endif
endif
player:notify("Usage: @reap [info|next|protect|kill] [username]");
return E_ARGS;
.
#166:1
":is_morbidly_inactive(user)";
"Return true if the user meets the inactivity requirements for reaping.";
{user} = args;
return (t = `user.last_disconnect_time ! E_INVIND, E_PROPNF') && (t < (time() - this.reapable_age));
.
#166:2
":is_immortal(user)";
"Return true if the user is exempt from reaping.";
{user} = args;
return $wiz_utils:is_builder(user) || (user in this.protected);
.
#166:3
":next_reapable(INT reapability_factor)";
{?minr = this.default_reapability_factor} = args;
for user in (players())
if (user in this.skipped)
continue;
endif
reapability = this:is_reapable(user);
if (reapability && (reapability >= minr))
return user;
endif
endfor
return #-1;
.
#166:4
":do_show_info(user)";
if (caller != this)
return E_PERM;
endif
{user} = args;
LWIDTH = 16;
border = $string_utils:space(player:linelen(), "-");
player:notify(border);
"-----------------------------";
msg = tostr($string_utils:nn(user), " <", user.email_address, ">");
player:notify(($string_utils:left("User", LWIDTH) + ": ") + msg);
"-----------------------------";
msg = player:ctime(user.last_disconnect_time);
player:notify(($string_utils:left("Last On", LWIDTH) + ": ") + msg);
"-----------------------------";
{qt, qu, ql, qo} = user.size_quota;
msg = tostr(`length(user.owned_objects) ! ANY => 0', " objects; ", $string_utils:to_bytes(qu));
player:notify(($string_utils:left("Usage", LWIDTH) + ": ") + msg);
"-----------------------------";
if (main = $alt_player_db:is_alt(user))
msg = tostr("alternate character of ", $string_utils:nn(main[1]));
elseif (alts = $alt_player_db:all_alternates(user))
msg = tostr("has alt(s) ", $string_utils:nn(alts));
else
msg = "None";
endif
player:notify(($string_utils:left("Alt Info", LWIDTH) + ": ") + msg);
"-----------------------------";
player:notify(border);
player:notify_lines(`user:description() ! ANY => "<buggy :description>"');
player:notify(border);
"-----------------------------";
player:notify_lines($string_utils:smart_columnize($list_utils:map_arg($string_utils, "nn", user.owned_objects)));
player:notify(border);
"-----------------------------";
if (hints = this:reap_hints(user))
i = 0;
for hint in (hints)
player:notify(tostr(i = i + 1, ": ", hint));
endfor
player:notify(border);
endif
.
#166:5
":do_protect(user)";
if (caller != this)
return E_PERM;
endif
{user} = args;
this.protected = {@this.protected, user};
player:notify(user:grammar_sub("%N (%#) %<is> now exempt from reaping."));
.
#166:6
":is_immortal(user)";
"Return true if the user is a candidate for reaping.  If return integer is positive, the user is almost certainly reapable.  If negative, the user is possibly reapable, but there may be complicating circumstances such as alt concerns.";
{user} = args;
if (!this:is_controllable_by(caller_perms(), caller))
raise(E_PERM);
elseif (this:is_immortal(user) || (!this:is_morbidly_inactive(user)))
return 0;
endif
if ($alt_player_db:is_alt(user) || $alt_player_db:all_alternates(user))
return -1;
endif
if (is_clear_property(user, "description"))
return 5;
endif
"Return true unless something above warrants otherwise.";
return 1;
"--WIZARDLY to access alt info.---";
.
#166:7
":do_kill(user)";
if (!caller_perms().wizard)
return E_PERM;
endif
{user} = args;
if (!this:is_reapable(user))
player:notify(user:grammar_sub("%N (%#) %<is> not reapable.  Use @zap if you're sure you want to reap %o."));
return E_NACC;
endif
dead_name = $string_utils:nn(user);
for o in (setremove(user.owned_objects, user))
player:notify(tostr("***** granting ", $string_utils:nn(o), " *****"));
$wiz_utils:grant_object(o, $executor);
$command_utils:suspend_if_needed(0, "..... suspending grants to $executor .....");
endfor
"all this needs to be in $player:recycle...gah!";
"same with registration info...";
alts = user.alt_info;
if (typeof(alts) == OBJ)
`alts.alt_info = setremove(alts.alt_info, user) ! ANY';
elseif (alts)
new_primary = alts[1];
alts = listdelete(alts, 1);
new_primary.alt_info = alts;
for alt in (alts)
`alt.alt_info = new_primary ! ANY';
endfor
endif
$wiz_utils:unset_player(user);
$recycler:_recycle(user);
this.skipped = setremove(this.skipped, user);
player:notify(tostr("***** recycled ", dead_name, " *****"));
player:notify(tostr(dead_name, " reaped.  R.I.P."));
.
#166:8
":do_skip(user)";
if (caller != this)
return E_PERM;
endif
{user} = args;
this.skipped = {@this.skipped, user};
player:notify(user:grammar_sub("%N (%#) will be skipped when calculating the next candidate for reaping."));
.
#166:9
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.protected = this.skipped = {};
.
#166:10
":reap_hints(user)";
if (caller != this)
return E_PERM;
endif
{user} = args;
hints = {};
last_on = user.last_disconnect_time;
inactive = time() - last_on;
pct = (tofloat(inactive) / tofloat(this.reapable_age)) * 100.0;
if (pct < 100.0)
hints = {@hints, tostr("WARNING: ", floatstr(pct, 2), "% of reapable age!")};
else
hints = {@hints, tostr(floatstr(pct, 2), "% of reapable age")};
if ((year = player:ctime(last_on)[21..25]) != player:ctime()[21..25])
hints = {@hints, "hasn't been online since " + year};
endif
endif
if (user.description == parent(user).description)
hints = {@hints, "no description"};
endif
if (main = $alt_player_db:is_alt(user))
hints = {@hints, tostr("CAUTION: an alternate character of ", $string_utils:nn(main[1]))};
elseif (alts = $alt_player_db:all_alternates(user))
hints = {@hints, tostr("CAUTION: has ", length(alts), " alternate character(s): ", $string_utils:nn(alts))};
else
hints = {@hints, "no alternate character concerns"};
endif
{quota, usage, last_measured, unmeasured} = user.size_quota;
if (usage > (quota / 2))
hints = {@hints, tostr(user:grammar_sub("using more than half %p quota"), " (", $string_utils:to_bytes(usage), ")")};
endif
return hints;
.
#166:11
return args[1].wizard;
.
#166:12
":do_purge(user)";
if (caller != this)
return E_PERM;
endif
count = $executor:purge();
if (count)
player:notify($executor:grammar_sub(tostr("Recycled ", count, " object(s) owned by %N <%#>.")));
else
player:notify($executor:grammar_sub("%N <%#> %<owns> nothing."));
endif
.
#166:13
"@sweap";
"Iterate through all those up for reaping, showing their reap info and a prompt to kill, skip, protect.";
"@sweap!";
"If @sweap! is used, auto-delete those with clear descriptions.";
if (!caller_perms().wizard)
raise(E_PERM);
endif
nuclears = verb[$] == "!";
while (valid(user = this:next_reapable()))
if (nuclears && (this:is_reapable(user) >= 5))
this:do_show_info(user);
this:do_kill(user);
suspend(0);
else
this:do_show_info(user);
response = $command_utils:read("`kill', `protect', `skip'");
if (response == "kill")
this:do_kill(user);
elseif (response == "protect")
this:do_protect(user);
elseif (response == "skip")
this:do_skip(user);
endif
endif
endwhile
player:notify("Nobody is up for reaping.");
.
#169:0
"uM...maybe hack this for a security check.";
return pass(@args);
.
#169:1
":match_parent(string)";
"-> Return the replicatable parent matching he given string.";
return $match_utils:match(args[1], this.parents);
.
#169:2
":_replicate(dobj, player)";
"Attempt to create a child of the given parent.";
"If this.owned_by_player is false, make all replications owned by $gm.";
if (!caller_perms().wizard)
return E_PERM;
endif
dobj = args[1];
if ((!dobj.f) && (!dobj:is_writable_by(this.owner)))
return E_NACC;
elseif (!this:owned_by_player(args[1]))
args[2] = this.alt_owner;
endif
object = $recycler:_create(@args);
if (typeof(object) != OBJ)
return object;
endif
what = args[1];
object:set_name(what:spawned_name());
object:set_aliases(what:spawned_aliases());
object:moveto(this);
return object;
.
#169:3
":_destroy(dobj) -- Attempt to destroy the given object.";
what = args[1];
if (!caller_perms().wizard)
return E_PERM;
elseif ((is_player(what) || verbs(what)) || properties(what))
return E_NACC;
endif
return $recycler:_recycle(what);
.
#169:4
":add_parent(obj)";
"Add the object as something to be replicated in this machine.";
"=> E_PERM if not allowed";
"=> E_NACC if parent is not fertile and not writable by this owner";
"=> E_RANGE if parent is larger than the capacity of this replicator";
{p} = args;
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
elseif ((!p.f) && (!p:is_writable_by(caller_perms(), caller)))
return E_NACC;
elseif (p.v_size > this.v_size)
return E_RANGE;
endif
return this.parents = setadd(this.parents, args[1]);
.
#169:5
":remove_parent(obj)";
"Remove the object as something to be replicated in this machine.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
return this.parents = setremove(this.parents, args[1]);
.
#169:6
"menu <replicator>";
"";
"Display a list of items available for replication.";
this:tell_menu();
.
#169:7
"Catalogue the replicator's parentables.";
player:tell("A digital display lists permitted parentables:");
for mom in (this.parents)
player:tell(" ", mom.name, (a = setremove(mom.aliases, mom.name)) ? (" (aka " + $string_utils:english_list(a)) + ")" | "");
endfor
.
#169:8
"Show the menu if the replicator's look_self_menu property is true.";
pass(@args);
if (this.look_self_menu)
this:tell_menu();
endif
.
#169:9
":do_noise(msg, sound)";
room = this:room();
msg = args[1];
sound = args[2];
volume = 5;
room:announce_all(this:namec(), ": *", sound, "*  \"", msg, "\"");
room:broadcast_event_speech(this, msg, volume / 2);
room:broadcast_event_sound(this, sound, volume);
.
#169:10
"replicate parent in replicator";
"Direct the replicator to access the creation code for 'parent' and attempt to construct it.";
if (caller != player)
return E_PERM;
endif
this:do_replication(@args);
.
#169:11
"replicate parent with replicator";
"Direct the replicator to access the creation code for 'parent' and attempt to construct it.";
if (caller != player)
return E_PERM;
endif
this:do_replication(@args);
.
#169:12
":replication_ok(user, item)";
"Is the machine ready to be used?  If not, print messages to user and return false.  Else return true.";
user = args[1];
item = args[2];
if (this.opened)
this:do_noise("Door is ajar.", "beep");
else
return 1;
endif
return 0;
.
#169:13
"Main code for the VR replication process, called by various syntax routers.";
if ((!caller_perms().wizard) && (caller != player))
return E_PERM;
elseif (!player:is_local_to(this))
player:tell("You'd have to be closer to ", this:dnamec(), " to use it.");
return;
endif
this:do_autoclose();
where = player:room();
$you:say_action("%N %<keys> in %p request on %[tdname].");
if ((dobj = this:match_parent(dobjstr)) == $nothing)
return this:do_noise("No template specified.", "beep");
elseif (dobj == $ambiguous_match)
return this:do_noise("Ambiguous template specification.", "beep");
elseif (!valid(dobj))
return this:do_noise("Unknown template specification.", "beep");
elseif (!this:replication_ok(player, dobj))
return;
endif
what = this:_replicate(dobj, player);
if (typeof(what) == OBJ)
this:do_noise("Replication successful.", "bing");
elseif (what == E_QUOTA)
this:do_noise("Replication allotment exceeded.", "beep");
else
this:do_noise("Replication failed.", "beep");
endif
.
#169:14
"destroy object in replicator";
"Dis-integrate the components of the given object with the replicator.";
if (caller != player)
return E_PERM;
endif
this:do_disintegration(@args);
.
#169:15
"destroy object in replicator";
"Dis-integrate the components of the given object with the replicator.";
if (caller != player)
return E_PERM;
endif
this:do_disintegration(@args);
.
#169:16
":disintegration_ok(user, item)";
user = args[1];
item = args[2];
if (this.opened)
this:do_noise("Door is ajar.", "beep");
elseif (0 && setremove(this.contents, item))
this:do_noise("Foreign objects in disintegration chamber.", "BRAAAP");
elseif (!item:is_writable_by(user))
this:do_noise("Given object not controlled by activator.", "BRAAAP");
elseif ((verbs(item) || properties(item)) || children(item))
this:do_noise("Modifications to object prohibit disintegration.", "BRAAAP");
else
return 1;
endif
return 0;
.
#169:17
"Main code for the VR disintegration process, called by various syntax routers.";
if ((!caller_perms().wizard) && (caller != player))
return E_PERM;
elseif (!(this.location in {player, player.location}))
player:tell("You'd have to be closer to ", this:dnamec(), " to use it.");
return;
endif
this:do_autoclose();
if ((dobj = this:match_contents(dobjstr)) == $nothing)
player:tell("You read the fine print:  As an additional precaution, ", this:dname(), " requires you to specify the id of the object you wish to destroy.");
return;
elseif ($match_utils:object_match_failed(dobj, dobjstr, this:contents(), "in " + this:dname()))
return;
endif
where = player:room();
$you:say_action("%N %<keys> in %p request on %[tdname].");
if (!valid(dobj))
player:tell("You notice there is no \"", dobjstr, "\" inside ", this:dname(), ".");
return where:announce("Nothing happens.");
elseif (!this:disintegration_ok(player, dobj))
return;
endif
msg = $string_utils:pronoun_sub("%N %<has> been disintegrated.", dobj);
this:_destroy(dobj);
if ($recycler:valid(dobj))
this:do_noise("Disintegration failed.", "beep");
else
this:do_noise(msg, "bing");
endif
.
#169:18
":do_autoclose()";
"Have the current player close the container.";
if (this.opened)
dobj = this;
this:close(dobjstr = this.name);
endif
.
#169:19
":owned_by_player(item)";
"Return true if the given object should be owned by the player, rather than by this.alt_owner.";
"If this.owned_by_player is a non-Zero integer, then all objects are owned by the player.  If it is Zero, they're owned by alt_owner.  If it is a list, then the objects in that list are owned by the player, all others by alt_owner.";
info = this.owned_by_player;
if ((!info) || (typeof(info) != LIST))
return info;
else
return args[1] in info;
endif
.
#169:20
":_purge()";
"Return the list of objects inside this which were moved to the trash bin.";
all = {};
for o in (this.contents)
o:moveto(this.trash_bin);
all = {@all, o};
endfor
return all;
.
#169:21
"Move all objects in the replicator to its associated trash bin.";
dobj = this:_purge();
if (!dobj)
player:tell("There's nothing there to clean out.");
else
this:announce_messages("cleanout");
endif
.
#169:22
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.parents = {};
this.owned_by_player = 1;
this.alt_owner = $packrat;
"trash bin should probably be something else";
this.trash_bin = $nowhere;
.
#170:0
":takemsg(@pronoun_sub_args)";
"Ah and now we mimic the behaviour of the generic thing!";
return (m = this.(verb)) && $string_utils:pronoun_sub(m, @args);
.
#170:1
"get portable room";
set_task_perms(callers() ? caller_perms() | player);
if (player:is_holding(this))
player:notify("You're already holding that!");
return E_NONE;
elseif (!this:is_local_to(player))
player:notify("I don't see that here.");
return E_RANGE;
elseif (who = this:occupants())
player:notify(this:grammar_sub("You can't pick up %(dname) while there are people inside of %o."));
return E_NACC;
endif
this:moveto(player);
if (player:is_holding(this))
player:notify_lines(this:take_succeeded_msg() || (("You take " + this:dname()) + "."));
(msg = this:otake_succeeded_msg()) && player:room_announce(msg);
else
player:notify_lines(this:take_failed_msg() || (("You can't pick " + this:dname()) + " up."));
(msg = this:otake_failed_msg()) && player:room_announce(msg);
endif
.
#170:2
"drop portable room";
set_task_perms(callers() ? caller_perms() | player);
if (!player:is_holding(this))
player:tell("You aren't holding that.");
return E_RANGE;
endif
where = player.location;
if (!where:acceptable(this))
player:tell("You can't drop that here.");
return E_NACC;
endif
this:moveto(where);
if (this.location == where)
player:notify_lines(this:drop_succeeded_msg() || (("You drop " + this:dname()) + "."));
(msg = this:odrop_succeeded_msg()) && where:announce(msg);
else
player:notify_lines(this:drop_failed_msg() || "You can't seem to drop that here.");
(msg = this:odrop_failed_msg()) && where:announce(msg);
endif
.
#170:3
player:tell(this:title());
if (!(args && args[1]))
c = this:tell_description();
else
c = this:contents();
endif
if (player:room() == this)
this:tell_exits(this:obvious_exits());
this:tell_contents(setremove(c, player), this.ctype);
endif
.
#172:0
":is_busy(player) -- Is the feature busy for the given player?";
"-> STR message describing why the feature is busy.";
"-> 0   Player is free to use the feature.";
{user} = args;
if (this.last_used < (time() - 60))
this.pending = {};
return 0;
elseif (length(pending = this.pending) > this.max)
return tostr($string_utils:title_listc(pending), " are currently using the ", this:title(), ".  You'll have to wait until one of them is finished.");
endif
info = $list_utils:assoc(user, this.pending);
if (!info)
return 0;
elseif (`idle_seconds(info[2]) ! E_INVARG' == E_INVARG)
this.pending = setremove(this.pending, info);
return 0;
endif
return tostr("You're already waiting for \"", info[3], "\" from ", $string_utils:nn(this), ".  Try again when that request is finished.");
.
#172:1
":register_request(player, connection, word)";
if (caller != this)
return E_PERM;
endif
{who, how, what} = args;
i = $list_utils:iassoc(who, this.pending);
if (i && (this.pending[i][3] == what))
this.pending[i][2] = how;
else
this.pending = {@this.pending, args};
endif
this.last_used = time();
.
#172:2
if (caller != this)
return E_PERM;
endif
verb[1..4] = "";
return $network:(verb)(@args);
.
#172:3
if (caller != this)
return E_PERM;
endif
return `read(@args) ! ANY';
.
#172:4
if (caller != this)
return E_PERM;
endif
return `notify(@args) ! ANY';
.
#172:5
"@define <word>";
"Definition of the given word, according to Webster's.";
"@paste-define <word>";
"Paste to your location the definition of the given word.";
if (!args)
player:tell("usage: ", verb, " <word>");
return E_ARGS;
elseif (msg = this:is_busy(player))
player:tell(msg);
return E_NACC;
endif
word = $string_utils:uppercase(argstr);
prefix = tostr(verb, " ", word, " -> ");
player:tell(tostr(prefix, "Checking..."));
this.last_used = time();
result = this:word_query(word);
if (typeof(result) == ERR)
player:tell(tostr(prefix, " *** Connection Error! *** <", result, ">"));
return result;
elseif (!result)
player:tell(tostr(prefix, ": No definition."));
return 0;
endif
pasted = verb[1..2] == "@p";
border = $string_utils:centre(((verb + " ") + word) + (pasted ? " from " + player.name | ""), player:linelen(), "-");
result = {border, @result, border};
where = player.location;
for who in (pasted ? where:contents() | {player})
who:tell_lines(result);
endfor
.
#172:6
"@word";
"Same, but with the preferred Word-Dot-List (WORD.LST).";
if (!args)
player:tell("usage: ", verb, " <possible-word>");
return E_ARGS;
elseif (msg = this:is_busy(player))
player:tell(msg);
return E_NACC;
endif
if (verb[2] == "p")
verb[1..7] = "";
public = 1;
else
verb[1..1] = "";
public = 0;
endif
word_string = $string_utils:uppercase(argstr);
prefix = tostr("@", verb, " ", word_string, " -> ");
output = tostr(prefix, "Checking ...");
if (public)
player.location:emote(argstr = "| " + output);
else
player:tell(output);
endif
words = $string_utils:words(word_string);
result = this:perform_word_check(player, words);
for line in (result)
if (public)
player.location:emote(argstr = ("| " + prefix) + line);
else
player:tell(prefix + line);
endif
endfor
.
#172:7
":finish_request(player)";
if (caller != this)
return E_PERM;
endif
{user, @rest} = args;
if (i = $list_utils:iassoc(user, this.pending))
this.pending = listdelete(this.pending, i);
endif
.
#172:8
":explode(subject [, break])";
{subject, ?delim = " "} = args;
lb = length(delim);
subject = subject + delim;
parts = {};
while (subject)
if ((i = index(subject, delim)) > 1)
parts = {@parts, subject[1..i - 1]};
endif
subject = subject[i + lb..length(subject)];
endwhile
return parts;
.
#172:9
":word_query_raw(STR word)";
if (caller != this)
raise(E_PERM);
endif
{word} = args;
{host, port, request} = this.define_request;
word = $string_utils:uppercase(strsub(word, " ", "+"));
this:register_request(player, #-1, word);
open = this:net_open(host, port);
this.debug && notify(@this.debug, tostr(">>> open = <", toliteral(open), ">"));
if (typeof(open) == ERR)
return open;
endif
this:register_request(player, open, word);
for line in (request)
this:net_write(open, strsub(line, "$word", word));
endfor
result = {};
in_content = 0;
while ((lt = typeof(line = this:net_read(open))) != ERR)
this.debug && notify(@this.debug, tostr(">>> receiving \"", line, "\""));
if (in_content)
result = {@result, line};
elseif (!line)
in_content = 1;
endif
this.debug && notify(@this.debug, tostr(">>> idle_seconds(", open, ") => ", `idle_seconds(open) ! ANY'));
endwhile
this.debug && notify(@this.debug, tostr(">>> last received <", toliteral(line), ">"));
this:net_close(open);
this:finish_request(player, open, word);
return result;
.
#172:10
":word_query(STR word)";
{word} = args;
if (result = this:cache_get(word))
return {@result, @this.define_credit};
endif
raw = this:word_query_raw(word);
if (typeof(raw) == ERR)
return raw;
endif
patterns = this.define_regions;
stage = 1;
result = {};
for line in (raw)
m = match(line, patterns[stage]);
if (stage == 1)
if (m)
result = {@result, substitute("%1", m)};
stage = 2;
endif
continue;
elseif (!m)
result = {@result, line};
else
result = {@result, substitute("%1", m)};
break;
endif
endfor
if (!result)
return 0;
endif
rendered = this:render_markup(result);
this:cache_add(word, rendered);
return {@rendered, @this.define_credit};
.
#172:11
":cache_add(STR word, STR content)";
"Add the given word and definition to the cache.";
if (caller != this)
return E_PERM;
endif
this:validate_cache();
{word, content} = args;
i = $list_utils:iassoc(word, this.cache);
if (!i)
this.cache = {@this.cache, {word, content}};
else
this.cache[i][2] = content;
endif
.
#172:12
":validate_cache(word)";
"Return true if the given word can be cached.  Do any cleanup here.";
if (length(this.cache) > this.cache_entries)
this.cache[1..$ - this.cache_entries] = {};
endif
return 1;
.
#172:13
":cache_get(STR word)";
"Retrieve content for the given word from the cache.";
{word} = args;
cache = this.cache;
i = $list_utils:iassoc(word, cache);
if (!i)
return 0;
endif
def = cache[i];
if (i != length(cache))
this.cache = {@listdelete(cache, i), def};
endif
return def[2];
.
#172:14
":render_markup(LIST content)";
"Strip HTML from the given list of strings.";
{content} = args;
result = {};
aline = tostr(@content);
for bline in (this:explode(aline, "<br>"))
while (m = match(bline, "%(<[^>]+>%)"))
bline[m[3][1][1]..m[3][1][2]] = "";
endwhile
while (m = match(bline, "%(&%([a-zA-Z]+%);%)"))
entity = substitute("%2", m);
if (entity == "amp")
new = "&";
elseif (entity == "lt")
new = "<";
elseif (entity == "gt")
new = ">";
else
new = "";
endif
bline[m[3][1][1]..m[3][1][2]] = new;
endwhile
result = {@result, bline};
endfor
return result;
.
#172:15
":allow_query_from(caller-perms, caller)";
"Return true if the given caller specs may make a raw query.";
return this:is_controllable_by(@args);
.
#172:16
":ospd_query(STR)";
"Return true if the word is valid, false if not.";
if (!this:allow_query_from(caller_perms(), caller))
return E_PERM;
endif
send = tostr("ospd -remote ", args[1]);
open = this:net_open(@this.scrabble_server);
if (typeof(open) == ERR)
return open;
endif
this:net_write(open, send);
while ((typeof(line = this:net_read(open)) == STR) && (line != "[BEGIN]"))
endwhile
result = {};
while ((typeof(line = this:net_read(open)) == STR) && (line != "[END]"))
result = {@result, line};
endwhile
this:net_close(open);
if (result)
return match(result[1], "%(is|are%) acceptable") ? 1 | 0;
else
return E_NACC;
endif
.
#172:17
if (!caller_perms().wizard)
return E_PERM;
endif
pass();
this.cache = this.pending = {};
this.last_used = this.debug = 0;
.
#172:18
"@word-cache";
"Show all words which have been cached, and display the definition of one of them.";
prefix = tostr(verb, " -> ");
cached = this.cache;
if (!cached)
player:tell(prefix, "The word cache is empty.");
return;
endif
words = $list_utils:slice(cached, 1);
if (verb == "@popword")
word = words[random($)];
border = $string_utils:centre(verb, player:linelen(), "-");
result = {border, @this:word_query(word), border};
player:tell_lines(result);
else
player:tell(prefix, $string_utils:english_list(words));
endif
.
#172:19
":perform_word_check(OBJ player, LIST words)";
"Return a list of strings suitable for output to the player requesting validity results form the given list of words.";
if (0 && (caller != this))
raise(E_PERM);
endif
{player, words} = args;
good_template = "The word \"%s\" is acceptable.";
bad_template = "The word \"%s\" is unacceptable.";
local_template = "The word \"%s\" is acceptable on this MOO.";
undone_words = result = {};
for word in (words)
r = this:fetch_word_result(word);
if (r < 0)
undone_words = {@undone_words, word};
elseif (r == 2)
result = {@result, strsub(local_template, "%s", word)};
elseif (r)
result = {@result, strsub(good_template, "%s", word)};
else
result = {@result, strsub(bad_template, "%s", word)};
endif
endfor
if (!undone_words)
return result;
endif
word_string = $string_utils:from_list(undone_words, " ");
send = tostr("word -remote ", word_string);
open = this:net_open(@this.word_server);
if (typeof(open) == ERR)
return {@result, tostr(" *** Connection Error! *** <", open, ">")};
endif
out = this:net_write(open, send);
this.debug && notify(@this.debug, tostr("out: ", out));
while ((typeof(line = this:net_read(open)) == STR) && (line != "[BEGIN]"))
this.debug && notify(@this.debug, tostr("in: ", line));
endwhile
net_result = {};
if (typeof(line) != STR)
net_result = {@net_result, tostr(" *** Read Error! *** <", line, ">")};
endif
while ((typeof(line = this:net_read(open)) == STR) && (line != "[END]"))
net_result = {@net_result, line};
endwhile
this:net_close(open);
this:cache_word_result(net_result);
return {@result, @net_result};
.
#172:20
":cache_word_result(LIST output)";
" Parses the given server output and places the results into the cache.";
if (caller != this)
raise(E_PERM);
endif
{output} = args;
results = {{}, {}};
pattern = "The word \"%([^\"]+%)\" is %(%(un%)?acceptable%).";
for line in (output)
m = match(line, pattern);
if (!m)
"blah";
elseif (substitute("%3", m))
results[2] = {@results[2], substitute("%1", m)};
else
results[1] = {@results[1], substitute("%1", m)};
endif
endfor
for i in [1..2]
if (results[i])
for word in (results[i])
if (p = word in this.word_cache[i])
this.word_cache[i][p..p] = {};
endif
this.word_cache[i] = {word, @this.word_cache[i]};
endfor
if (length(this.word_cache[i]) > this.word_cache_top[i])
this.word_cache[i][this.word_cache_top[i]..$] = {};
endif
endif
endfor
.
#172:21
":fetch_word_result(STR word)";
{word} = args;
if (word in this.dialect)
return 2;
endif
for w in [1..2]
if (x = word in this.word_cache[w])
break;
endif
endfor
if (!x)
return -1;
endif
this.word_cache[w][x..x] = {};
this.word_cache[w] = {word, @this.word_cache[w]};
return !(w - 1);
.
#172:22
"@dialect";
"  Display words not acceptable according to the official WORD list, but accepted on this MOO.";
"@dialect add word1 word2 wordN";
"@dialect remove word1 word2 wordN";
"  Add or remove words from the dialect.  Requires write perms.";
if (!argstr)
player:tell("Local dialect includes ", $string_utils:english_list($list_utils:sort(this.dialect), "no additional words"), ".");
return;
endif
if (!this:is_writable_by(player))
player:tell("You aren't allowed to modify this MOO's dialect.");
return E_PERM;
endif
words = $list_utils:sort(listdelete(args, 1));
words = $list_utils:map_arg($string_utils, "UPPERCASE", words);
if (args[1] == "add")
vterm = "added to";
for word in (words)
this.dialect = setadd(this.dialect, word);
endfor
elseif (args[1] == "remove")
vterm = "removed from";
for word in (words)
this.dialect = setremove(this.dialect, word);
endfor
else
player:tell("Use @dialect add|remove to add or remove words from the local dialect.");
return E_INVARG;
endif
player:tell($string_utils:english_list(words, "No words"), " ", vterm, " the local dialect.");
.
#173:0
pass(@args);
this:tell_screen();
.
#173:1
"type <unique_patient_ID> on scanner";
"Access BioTEK database records for the given subject.";
if (player:stat_ElecTech() < (1 + random(5)))
"Added arbitrary rank 5-10 skill check for remote access.  <970111-1608;Quinn>";
player:tell("It looks as if the database uplink access protocols have changed, and you don't have the appropriate technical knowledge to find your way around them.");
return;
endif
$you:say_action("%N %<keys> in a request on %p %t.");
dobj = $string_utils:match_player(dobjstr);
this:new_subject(dobj, dobjstr);
.
#173:2
"read bioscanner";
"Read the display of your bioscanner.";
this:tell_screen();
.
#173:3
":tell_subject_stats(who)";
who = args[1];
EMU = $emu;
ANSI = player:get_option($display_options, "color");
charset = player:get_option($display_options, "charset") || EMU.default_charset;
line_prefix = ANSI ? EMU.ANSI_Line_Prefix | "";
"----- subject name -----";
line = tostr($string_utils:left(who:name(), -40));
lines = {line};
"----- damage monitor display -----";
line = tostr(line_prefix, "  ");
inj = who:stat_injury();
line = tostr(line, " Injury: ", this:injury_adj(inj, ANSI), " (", inj, ")");
fat = who:stat_fatigue();
line = tostr(line, ", Fatigue: ", this:injury_adj(fat, ANSI), " (", fat, ")");
ins = who:stat_insanity();
line = tostr(line, ", Insanity: ", this:injury_adj(ins, ANSI), " (", ins, ")");
lines = {@lines, line};
"----- main scanner display elements -----";
wts = $rpg:wts_code(inj);
nrm = ANSI ? EMU:tokenize("normal") | "";
pre = ANSI ? this:wts_to_ansi(wts) | "";
max_char = EMU:entity("lt_fill", charset);
now_char = EMU:entity("dk_fill", charset);
vrt_line = EMU:entity("sv_line", charset);
hrz_line = EMU:entity("sh_line", charset);
"----- the guts -----";
row = 0;
lines = {@lines, ""};
for att in ($rpg.atts)
max_rank = who:get_stat(att);
now_rank = min(who:("stat_" + att)(), 100);
blocks = min(max(max_rank, now_rank), 100) / 5;
blanks = 20 - blocks;
bar = $string_utils:space(blocks, max_char);
fill = max(now_rank / 5, 1);
bar[1..fill] = $string_utils:space(fill, now_char);
line = tostr(line_prefix, $string_utils:left($string_utils:uppercase(att), 12), pre, bar, nrm, $string_utils:space(blanks), vrt_line);
lines = {@lines, line};
endfor
start = 3;
lines[start + 1] = tostr(lines[start + 1], "   MOBILITY:");
lines[start + 2] = tostr(lines[start + 2], " Upper ", $string_utils:space(c = (m = who:get_stat("upper_mobility")) / 5, hrz_line), (m == 100) ? vrt_line | ">", $string_utils:space(20 - c), " ", $string_utils:right(m, 3), "%");
lines[start + 3] = tostr(lines[start + 3], " Lower ", $string_utils:space(c = (m = who:get_stat("lower_mobility")) / 5, hrz_line), (m == 100) ? vrt_line | ">", $string_utils:space(20 - c), " ", $string_utils:right(m, 3), "%");
lines = {@lines, ""};
return lines;
.
#173:4
":injury_adj(NUM inj[, BOOL color])";
{inj, ?color = 0} = args;
msg = $rpg:wts_code(inj);
return color ? tostr(this:wts_to_ansi(msg), msg, $emu:tokenize("normal")) | msg;
.
#173:5
"scan <living_organism> with scanner";
"scan ALL with scanner";
"The first syntax scans an individual.  The second scans everyone in the room with you.";
if (!player:is_holding(this))
player:tell("You've got to be holding ", this:dname(), " in order to scan someone with it.");
return;
elseif (dobjstr in {"all", "everyone", "everything", "everybody", "here", "area", "room", "vacinity"})
return this:do_scan_all();
endif
dobj = player:my_match_object(dobjstr);
if ($match_utils:object_match_failed(dobj, dobjstr))
return;
endif
if (dobj == player)
$you:say_action("%N %<points> %p %t at %r and %<sweeps> its sensors up and down %p body.");
elseif ($rpg:is_char(dobj))
$you:say_action("%N %<points> %p %t at %[ddname] and slowly %<sweeps> its sensors up and down %[dpp] body.");
else
$you:say_action("%N %<points> %p %t at %[ddname] and %<sweeps> the sensors up and down %[dpp] surface, eyes mad with a lust for forbidden knowledge.");
endif
this:new_subject(dobj, dobjstr);
.
#173:6
":new_subject(obj, string)";
who = args[1];
string = args[2];
if (!string)
player:tell(this:name(), ": *BEEP* <null entry>");
return E_ARGS;
elseif (who == $ambiguous_match)
player:tell(this:name(), ": *BEEP* <ambiguous subject>");
return E_INVARG;
elseif (who == $failed_match)
player:tell(this:name(), ": *BEEP* <invalid subject>");
return E_INVARG;
elseif (!$rpg:is_character(who))
player:tell(this:name(), ": *BEEP* <non-living subject>");
return E_INVARG;
else
player:tell(this:name(), ": *BING* <processing...>");
this.screen = this:stat_text(who);
this:tell_screen();
endif
.
#173:7
":scan_all()";
"Scan everyone in the room.";
o = player:room():occupants();
$you:say_action("%N %<waves> %p %t around the room, sweeping its scanners across every living being in sight.");
lines = {};
player:tell("--Scanning");
color = player:get_option($display_options, "color");
line_prefix = color ? $emu.ANSI_Line_Prefix | "";
for c in (o)
lines = {@lines, tostr(line_prefix, this:injury_adj(i = c:stat_injury(), color), " (", $string_utils:right(i, 3), ") ", c:name())};
endfor
player:tell_lines(this.screen = lines);
player:tell("--Done");
.
#173:8
":tell_screen()";
if (s = this.screen)
line = $string_utils:space(player:linelen(), "-");
player:tell(line);
player:tell_lines(s);
player:tell(line);
else
player:tell("The screen is blank.");
endif
.
#173:9
":wts_to_ansi(wts_code)";
EMU = $emu;
codes = {"NON", "NEG", "MIN", "MOD", "SER", "SVR", "CRT"};
colors = {"", EMU:tokenize("bold", "yellow"), EMU:tokenize("yellow"), EMU:tokenize("red"), EMU:tokenize("flashing", "red"), EMU:tokenize("flashing", "magenta"), EMU:tokenize("flashing", "blue")};
return (i = args[1] in codes) ? colors[i] | "";
.
#173:10
if (!caller_perms().wizard)
return raise(E_PERM);
endif
this.screen = {};
pass();
.
#174:0
"Copied from staff (#2560):is_usable_by by GameMaster (#1991) Mon Nov 29 17:19:47 1999 CST";
return $rpg:trusted(args[1]);
.
#175:0
":sentence_for(user)";
"How long should the given user be kept in HELL?";
{user} = args;
return this.default_sentence * max(`length(user.pk) ! ANY => 1', 1);
.
#175:1
":exitfunc(DAMNED_USER)";
what = args[1];
if ((!$rpg:is_char(what)) || (what.location == this))
return pass(@args);
endif
remaining = this:time_left_for(what);
if (!remaining)
this:announce_messages("release");
what:birth();
return pass(@args);
endif
this:announce_messages("preyank");
this:bless_for_entry(what);
what:moveto(this);
this:announce_messages("postyank");
.
#175:2
":receive_dead(user)";
"Move the user in and print delivery messages.";
user = args[1];
this:bless_for_entry(user);
user:moveto(this);
this:announce_all_but({user}, $string_utils:pronoun_sub(this.delivery_msg, user));
.
#175:3
":time_left_for(char)";
"Return seconds remaining in the given character's stay in the afterlife.";
who = args[1];
sentence = this:sentence_for(who);
{killer, last_death, ?stack = {}} = who.last_killed_by;
if (last_death < 1)
return 0;
endif
elapsed = time() - last_death;
return max(sentence - elapsed, 0);
.
#175:4
":eng_time_left";
"Intended to be called from pronoun-subbed messages, this returns a string describing how much time the  player has left in the afterlife.";
remaining = this:time_left_for(player);
return remaining ? $time_utils:english_time(remaining) | "no time";
.
#175:5
"Don't give the damned a free ride if they disconnect.";
return E_PERM;
.
#176:0
":post_info([@msgs])";
"Return the sender, date, subject, etc information for all posts [or the given post numbers].";
if (!this:is_readable_by(caller_perms(), caller))
return E_PERM;
endif
if (!args)
return this.post_info;
endif
posts = {};
if (args)
for p in (this.post_info)
if (p[5] in args)
posts = {@posts, p};
endif
endfor
endif
return posts;
.
#176:1
"write to:someone subj:something on this";
if (player != caller)
return E_PERM;
elseif (!this:is_local_to(player))
player:tell("You'd have to be near ", this:dname(), " to ", verb, " on it.");
return;
endif
dobjstr = argstr[1..rindex(argstr, " on ") - 1];
post = this:process_post_args(dobjstr);
if (!post)
player:tell($string_utils:pronoun_sub(this.failed_post_msg));
else
this:add_post(player, @post);
this:announce_messages("post");
endif
.
#176:2
":process_post_args(string)";
s = args[1];
recipient = subject = "";
if (m = match(s, "%( *to:\"?%([^ \"]+%)\"? +%)"))
recipient = substitute("%2", m);
s[m[3][1][1]..m[3][1][2]] = "";
endif
if (m = match(s, "%( *subje?c?t?:\"?%([^\"]+%)\"? +%)"))
subject = substitute("%2", m);
s[m[3][1][1]..m[3][1][2]] = "";
endif
return {recipient, subject, {$string_utils:trim(s, "\"")}};
.
#176:3
":readable_text()";
"Straight text from :post_info() to be piped to :tell_lines().";
if (!this:is_readable_by(caller_perms(), caller))
return E_PERM;
endif
return this:read_text();
"---OLD CODE DELETE DELETE---";
i = 0;
text = {};
for info in (this:post_info())
recp = info[2] && tostr("  To: \"", info[2], "\"");
subj = info[3] && tostr("  Subject: \"", info[3], "\"");
text = {@text, "----------", tostr("Msg #", i = i + 1, "  From: \"", info[1]:name(), "\"", recp, subj), "", @info[4]};
endfor
return {@text, text ? "----------" | tostr("(No posts on ", this:dname(), ".)")};
.
#176:4
":add_post(from, to, subj, {lines});";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
this.post_info = {@this.post_info, {@args, this:new_post_num()}};
this.new_post_num = this.new_post_num + 1;
return 1;
.
#176:5
":clear_posts()";
"Clear all posts from this board.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
this.post_info = {};
this.new_post_num = 1;
.
#176:6
"erase this";
"Erase all messages from the board.";
if (player != caller)
return E_PERM;
elseif (!this:is_local_to(player))
player:tell("You'd have to be near ", this:dname(), " to ", verb, " messages from it.");
elseif (!this:is_writable_by(player))
player:tell("You wave your hand ineffectually over ", this:dname(), ".");
else
this:clear_posts();
this:announce_messages("erase");
endif
.
#176:7
":message_is_deletable_by(who, msg)";
who = args[1];
return this:is_writable_by(who) || (this:post_info(args[2])[1][1] == who);
.
#176:8
":delete_post(deleter, @msgs)";
"Delete the given post or post numbers from the board.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
u = args[1];
d = {};
posts = this.post_info;
nums = listdelete(args, 1);
info = this:post_info(@nums);
for msg in (info)
n = msg[5];
if (this:message_is_deletable_by(u, n))
d = {@d, n};
posts = setremove(posts, msg);
endif
endfor
this.post_info = posts;
return d;
.
#176:9
"delete msgs from board";
"Delete the given messages from the board.  The messages returned can be somewhat deceiving.  If NO messages are successfully deleted, you get the fail message.  If even one is deleted, you get the succeed.";
if (player != caller)
return E_PERM;
endif
nums = this:parse_msg_seq(dobjstr);
d = this:delete_posts(player, @nums);
this:announce_messages(d ? "delete" | "failed_delete");
.
#176:10
":parse_msg_sequence(str)";
nums = {};
maxn = this:last_post_num();
for e in ($string_utils:explode(args[1]))
if (m = match(e, "%([0-9]+%)-%([0-9]+%|%$%)?"))
a = substitute("%1", m);
a = max(tonum(a), 1);
b = substitute("%2", m);
b = b ? (b == "$") ? maxn | min(max(tonum(b), a), maxn) | maxn;
for i in [a..b]
nums = setadd(nums, i);
endfor
elseif (a = tonum(e))
nums = setadd(nums, min(max(a, 1), maxn));
endif
endfor
return nums;
.
#176:11
":scan_text(@msg_nums)";
su = $string_utils;
rule = "%-4 %12 %8 %50";
text = su:format(rule, " Num", "From", "To", "Subject");
line = su:space(player:linelen(), "-");
text = {text, line};
for msg in (this.post_info)
i = msg[5];
if ((!args) || (i in args))
text = {@text, su:format(rule, i, `msg[1]:name() ! ANY => "???"', msg[2] || "All", msg[3] || msg[4][1])};
endif
endfor
return {@text, line};
.
#176:12
":read_text(@msg_nums)";
if (!this:is_readable_by(caller_perms(), caller))
return E_PERM;
endif
su = $string_utils;
rule = "%-4 %12 %8 %50";
text = su:format(rule, " Num", "From", "To", "Message");
line = su:space(player:linelen(), "-");
text = {text, line};
for msg in (this.post_info)
i = msg[5];
if ((!args) || (i in args))
body = player:linesplit(tostr(@msg[4]), 50);
text = {@text, su:format(rule, i, `msg[1]:name() ! ANY => "???"', msg[2] || "All", body[1])};
for b in (listdelete(body, 1))
text = {@text, su:format(rule, "", "", "", b)};
endfor
endif
endfor
return {@text, line};
.
#176:13
"read board";
"Read all the messages on this board.";
this:read_on();
.
#176:14
"scan board";
"Scan all the messages on this board.";
this:scan_on();
.
#176:15
"read [msgs] on board";
"Scan all [or the given number sequence of] messages on the board.";
if (!this:is_local_to(player))
player:tell("You'd have to be near ", this:dname(), " to read it.");
return;
endif
nums = this:parse_msg_seq(dobjstr);
this:announce_messages("read");
player:tell_lines(this:read_text(@nums));
.
#176:16
"scan [msgs] on board";
"Scan all [or the given number sequence of] messages on the board.";
if (!this:is_local_to(player))
player:tell("You'd have to be near ", this:dname(), " to scan it.");
return;
endif
nums = this:parse_msg_seq(dobjstr);
this:announce_messages("scan");
player:tell_lines(this:scan_text(@nums));
.
#176:17
":new_post_num()";
"Return the number of the next post on the board.";
return this.new_post_num;
.
#176:18
":last_post_num()";
"Return the message number of the last post on the board.";
posts = this.post_info;
return posts ? posts[length(posts)][5] | 0;
.
#177:0
":set_description(desc)";
"Set the 'inside_description' if player is inside this room.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
elseif ($object_utils:contains(this, player))
this.inside_description = args[1];
return 1;
else
return pass(@args);
endif
"--WIZARDLY--";
.
#177:1
":description()";
"Return 'inside_description' if player is located in this room.";
if ($object_utils:contains(this, player))
return $string_utils:pronoun_sub(this.inside_description);
else
return pass(@args);
endif
.
#177:2
":invoke()";
"Mimic an exit's behaviour.";
player:maybe_queue_action(tostr("enter ", this:dname()), {this, "move"}, {player}, 50 - player:quickness());
.
#177:3
":outside_nameverb()";
"Return the appropriate name verb for this room's location.";
set_task_perms($no_one);
return this.location:(verb[9..length(verb)])(@args);
"--WIZARDLY--";
.
#177:4
":exitmsg_msg(what)";
"Basically same as the messages on $exit.";
return (m = this.(verb)) && $string_utils:pronoun_sub(m, @args);
.
#177:5
":move(what)";
"Move the given object inside of the room.";
what = args[1];
mode = (what.location == this) ? "_from_inside" | "";
if (caller != this)
return E_PERM;
elseif (what.location != (mode ? this | this.location))
player:tell((("You aren't in" + (mode ? "side " | " the same place as ")) + this:dname()) + ".");
return E_RANGE;
endif
if (`this:get_lock_status() ! E_VERBNF => 0')
"locked";
this:announce_messages("nogo" + mode);
return E_NACC;
endif
dest = mode ? this.location | this;
if (!$recycler:valid(dest))
move(this, this.campground);
dest = this.campground;
endif
`dest:bless_for_entry(what) ! ANY';
if (!dest:acceptable(what))
this:announce_messages("nogo" + mode);
return E_NACC;
endif
this:announce_messages("leave" + mode);
what:moveto(dest);
if (what.location != dest)
this:announce_messages("nogo" + mode);
return E_NACC;
endif
this:announce_messages("arrive" + mode);
"--WIZARDLY--";
.
#177:6
":set_hatch(exit)";
"Set the default method of egress for this room.  If invalid, :move is used.";
"-> -1      If the exit was cleared (set invalid).";
"-> E_NACC  If :add_exit(exit) failed.";
"->  1      If exit was successfully added.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
exit = args[1];
if (!valid(exit))
this.hatch = exit;
return -1;
endif
this:add_exit(exit);
if (exit in this.exits)
this.hatch = exit;
return 1;
else
return E_NACC;
endif
.
#177:7
"enter portaroom";
"";
"Mosey on inside the \"portable\" room.  Messages printed are akin to those printed by an $exit.";
if (player.location != this.location)
player:tell("You aren't in any position to do that.");
else
return this:invoke();
endif
.
#177:8
"exit";
"";
"Mosey on OUT of the \"portable\" room.  Messages printed are akin to those printed by an $exit.";
if (player.location != this)
player:tell("Why bother?  You aren't inside.");
elseif (valid(hatch = this.hatch))
hatch:invoke();
else
return this:invoke();
endif
.
#177:9
":look_person_msg()";
"%N -- Person  %T -- This  %L -- Person's location  %D -- Player";
return (msg = this.(verb)) && $string_utils:pronoun_sub(msg, this.location, this, this.location.location, player);
.
#177:10
":look_place_msg()";
"%N -- This  %D -- Player  %I -- Owner of this";
if (is_clear_property(this, verb))
return "";
endif
return (msg = this.(verb)) && $string_utils:pronoun_sub(msg, this, this, this.location, player, this.owner);
.
#177:11
"Show this information only if player is inside.";
if (!$object_utils:contains(this, player))
return;
elseif (!this:obvious_exits())
player:tell("Your only egress seems to be 'exit'.");
return;
else
return pass(@args);
endif
.
#177:12
"Show this information only if player is inside.";
if (!$object_utils:contains(this, player))
return;
else
return pass(@args);
endif
.
#177:13
"For the benefit of queued actions.";
this:move(@args);
.
#177:14
":integrated_description()";
"Return 'inside_description' if player is located in this room.";
if ($object_utils:contains(this, player))
return pass(@args);
else
return this:description();
endif
.
#177:15
"added by me (QUINN) 17-nov-95";
what = args[1];
return $object_utils:contains(this, what) || pass(what);
.
#177:16
if (!caller_perms().wizard)
raise(E_PERM);
endif
this.hatch = #-1;
"set the place to relocate if moved to #-1";
this.campground = $campground;
return pass(@args);
.
#179:0
"Return a very negative penalty unless the opposing weapon is also a natural weapon.  Should probably penalize the broader case of the attack not being the SAME natural weapon.";
{wielder, target, against, @rest} = args;
if (!$object_utils:isa(against, $natural_weapon))
return -1000;
endif
return pass(@args);
.
#180:0
if (!caller_perms().wizard)
return raise(E_PERM);
endif
this.task_data = {};
return pass(@args);
.
#180:1
if ($code_utils:task_valid(this.keepalive_task))
return E_MAXREC;
endif
fork tid (0)
while (1)
this:prod();
suspend(this.keepalive_interval);
endwhile
endfork
this.keepalive_task = tid;
.
#180:2
"prod $taskmaster";
"Go through all maintained objects, calling a kind of 'keepalive' method on each which should restart their perpetual tasks.";
squawk = caller != this;
for data in (this.task_data)
r = `this:do_prod_for(data) ! ANY';
if (squawk)
player:notify(tostr(":", toliteral(data[2]), " on ", $string_utils:nn(data[1])));
player:notify(tostr("=> ", r));
endif
$command_utils:suspend_if_needed();
endfor
.
#180:3
":do_prod_for(LIST data)";
"Interpret and execute the given set of task data according to the follow rules.";
"{";
" LIST|OBJ location,   -- Either a single object or a list of them.";
" STR|INT  method,     -- The method to call on each location.";
"}";
"The method should simply check if a task needs restarted, and do so.  It could possibly be called many consecutive times.  For an example of a prodded verb's structure, see this:keepalive.";
"";
{location, method} = args[1];
if (typeof(location) != LIST)
location = {location};
endif
set_task_perms(this);
for object in (location)
"fork in case the daemon doesn't return";
fork (0)
object:(method)();
endfork
"suspend to prevent a lot of queued forks";
suspend(0);
endfor
return 1;
.
#182:0
":input(STR text)";
"Input data into the comm.  By default, just send it to the current channel.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
channel = this:active_channel();
channel:output(@args, this);
.
#182:1
":output(STR text[, OBJ from[, NUM raw_text]])";
"Output data from the comm.  By default, just tell it to the location.";
"If raw_text is given and true, do not run the text through output_format.";
if ((!this:is_active_channel(caller)) && (!this:is_controllable_by(caller_perms(), caller)))
return E_PERM;
endif
args = {@args, this};
if ((length(args) > 2) && args[3])
text = args[1];
else
text = this:output_format_msg(@args);
endif
"Check volume to decide to how many to announce.";
this:tell_listeners({this.location}, text);
.
#182:2
"Remove from old location's features, and add to new location's.";
this.location:remove_feature(this);
pass(@args);
this.location:add_feature(this);
this:update_user();
.
#182:3
"transmit <text> -- Speak into the comm.";
if (!player:is_holding(this))
player:tell(("You've got to be holding " + this:dname()) + " to do that.");
return E_RANGE;
endif
if (msg = this:speak_msg(argstr))
player:tell(msg);
endif
if (match(argstr, "!$"))
player:room_announce(this:ospeak_msg(argstr));
else
player:room_announce(this:omumble_msg(argstr));
endif
this:input(argstr);
.
#182:4
":match_channel(str) -> Channel object matching `str'.";
":channel_match_failed(obj, str) -> What went wrong?";
return this.channel_server:(verb)(@args);
.
#182:5
"switch comm to <channel> -- Switch your comm to another channel.";
if (!player:is_holding(this))
player:tell(("You've got to be holding " + this:dname()) + " to do that.");
return E_RANGE;
endif
channel = this:match_channel(iobjstr);
if ((!valid(channel)) && valid(ch = this:match_comm(iobjstr)))
channel = ch;
endif
if (this:channel_match_failed(channel, iobjstr))
return E_INVARG;
endif
if (this:is_active_channel(channel))
player:tell(("Tuning in " + channel:callname()) + " as transmitting channel...");
endif
$you:say_action(this.otune_msg);
this:change_channel(channel);
.
#182:6
":change_channel(OBJ channel)";
"Tune in the given channel.  If it is invalid ($nothing), then disconnect from all channels.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
channel = args[1];
if (!valid(channel))
for ch in (this:active_channels())
this:disconnect_from(ch);
endfor
return 1;
endif
approved = channel:approved(this);
if (!approved)
this:output(approved);
else
this:connect_to(channel);
endif
return approved;
.
#182:7
"Allow this as a feature for the bearer.";
return $object_utils:contains(args[1], this);
.
#182:8
":is_direct_link([OBJ channel])";
"Is a link to the the curren (given) channel a direct one?  Ie: Is it a comm-to-comm link?";
ch = args ? args[1] | this.channel;
return $object_utils:isa(ch, this.root);
.
#182:9
"call <comm_number> with comm";
"";
"Call the communicator with the given number.";
if (this == dobj)
"...Need this to get around matching difficulties...";
iobj = player:my_match_object(iobjstr);
if (iobj != this)
if (!$match_utils:object_match_failed(iobj, iobjstr))
iobj:(verb)(@args);
endif
return;
endif
endif
if (!player:is_holding(this))
player:tell(("You've got to be holding " + this:dname()) + " to do that.");
return E_RANGE;
endif
comm = this:match_comm(dobjstr);
if (this:comm_match_failed(comm, dobjstr))
this:output("Searching FoneNet Database...");
if (c = this:lookup(dobjstr))
comm = c[1];
else
return E_INVARG;
endif
endif
if (this:call_in_progress())
player:tell("You already have a call in progress.  You'll have to hangup first.");
return E_MAXREC;
elseif (comm:is_busy())
this:output("Line busy.", this);
return E_NACC;
elseif (this.incoming == comm)
this:set_incoming($nothing);
return E_NONE;
endif
player:tell(this:dial_msg(comm));
player:room_announce($string_utils:pronoun_sub(this:odial_msg(comm)));
d = 10;
waiting_msg = this:outgoing_msg(comm);
this:set_outgoing(comm);
comm:set_incoming(this);
while (((d = d - 1) && (this.outgoing == comm)) && (comm.incoming == this))
this:output(waiting_msg, this, 1);
comm:do_incoming_notification();
suspend(this.ring_interval);
endwhile
comm:set_incoming($nothing);
if ((!d) || (this.outgoing != comm))
this:output("Call aborted.");
else
this:change_channel(comm);
endif
this:set_outgoing($nothing);
.
#182:10
":match_comm(STR number)";
"-> A communicator with the given number.";
target = toobj(args[1]);
if ($object_utils:isa(target, this.root))
return target;
else
return $failed_match;
endif
.
#182:11
":comm_match_failed(obj, str) -- What went wrong?";
if ($recycler:valid(cm = args[1]))
return 0;
elseif (cm == $nothing)
caller:output("No comm reference given.");
elseif (cm == $ambiguous_match)
caller:output("More than one comm matches given reference.");
else
caller:output("No comm matching given reference.");
endif
return 1;
.
#182:12
":set_incoming(OBJ comm)";
"-> Set this comm as receiving a call from the given comm.";
":set_outgoing(OBJ comm)";
"-> Set this comm as requesting a call to the given comm.";
comm = args[1];
if ((caller != comm) && (!this:is_controllable_by(caller_perms(), caller)))
return E_PERM;
endif
return this.(verb[5..length(verb)]) = comm;
.
#182:13
"answer comm -- Answer the ringing communicator.";
if (!player:is_holding(this))
player:tell(("You've got to be holding " + this:dname()) + " to do that.");
return E_RANGE;
elseif (!this:is_receiving_call())
this:output("No incoming call.");
else
this:_connect(this.incoming);
this:set_incoming($nothing);
endif
.
#182:14
"Add the communicator as a feature.";
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return E_PERM;
endif
fork (0)
this.location:add_feature(this);
endfork
return pass(@args);
.
#182:15
":_disconnect(comm)";
comm = args[1];
this:remove_active_channel(comm);
this:output("*DISCONNECT*", comm);
comm:output("*DISCONNECT*", this);
.
#182:16
"hangup COMM";
"Cancel an outgoing call on your communicator, or disconnect from the active channel.";
"hangup! COMM";
"Disconnect from all channels.";
if (!player:is_holding(this))
player:tell(("You've got to be holding " + this:dname()) + " to do that.");
return E_RANGE;
endif
c = this:active_channel();
if (this:call_in_progress())
this:set_outgoing($nothing);
elseif (!valid(c))
"this was disabled.  no idea why.  <Quinn;980310>";
player:tell("You aren't connected to anything, so there's no channel to disconnect.");
return E_NONE;
endif
if (verb[$] == "!")
this.incoming = #-1;
for ch in (this:active_channels())
this:disconnect_from(c);
endfor
else
this:disconnect_from(c);
endif
$you:say_action(this.ohangup_msg);
.
#182:17
":user()";
"-> Who's using the comm?  Default to this.user.";
return $recycler:valid(u = this.user) ? u | this:update_user();
.
#182:18
":_connect(comm)";
comm = args[1];
this:add_active_channel(comm);
this:output("*CONNECT*", comm);
`comm:output("*CONNECT*", this) ! E_VERBNF';
.
#182:19
":active_channel()";
"-> The primary channel to which input is routed by default.";
":active_channels()";
"-> All active channels, from which we'll accept input.";
channels = this.active_channels;
if (!channels)
return (verb[length(verb)] == "S") ? {} | $nothing;
else
return (verb[length(verb)] == "S") ? channels | channels[1];
endif
.
#182:20
":is_active_channel(OBJ channel)";
"Is this communicator tuned in to the given channel?";
return args[1] in this.active_channels;
.
#182:21
":add_active_channel(OBJ channel) -> Tune in the given channel.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
ch = args[1];
this.active_channels = listinsert(setremove(this.active_channels, ch), ch);
return 1;
.
#182:22
":remove_active_channel(OBJ channel) -> Tune out the given channel.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
ch = args[1];
this.active_channels = setremove(this.active_channels, ch);
return 1;
.
#182:23
":is_receiving_call()";
"-> Is there a call incoming to this communicator?";
return $recycler:valid(this.incoming);
.
#182:24
":output_format_msg(text, origin_channel)";
"Return how output from this communicator should appear.  Substitute $text in the message with the text of the transmission.";
format = strsub(this.(verb), "$text", args[1]);
if (length(args) > 1)
dobj = args[2];
endif
return $string_utils:pronoun_sub(format);
.
#182:25
":{o,}dial_msg(text)";
"Return the given message, substituting $number for the number dialed.";
return strsub(this.(verb), "$number", tostr(args[1]));
.
#182:26
":username()";
"Return the name of the user of this communicator, or NOBODY.";
user = this:user();
return valid(user) ? user:name() | "nobody";
.
#182:27
":approved(comm)";
"Is the given communicator approved to connect to this one?";
return 1;
.
#182:28
":incoming_msg(comm)";
"-> Message displayed when receiving a call request from the given comm.";
":outgoing_msg(comm)";
"-> Message displayed when calling the given comm.";
"$number is substituted in both messages as the number being dialed, which is of course THIS comm in the first case.";
dobj = args[1];
msg = strsub(this.(verb), "$number", tostr(dobj));
return $string_utils:pronoun_sub(msg);
.
#182:29
":is_busy() -> Is this line busy?";
try
if (this.incoming.outgoing == this)
return 1;
endif
if (this:refusing_calls())
return 1;
endif
except (ANY)
this.incoming = #-1;
endtry
return 0;
.
#182:30
"speak <text> into communicator";
if (!player:is_holding(this))
player:tell(("You've got to be holding " + this:dname()) + " to do that.");
return E_RANGE;
endif
argstr = dobjstr;
this:transmit(@args[1..max(("into" in args) - 1, 1)]);
.
#182:31
":call_in_progress()";
"Is this communicator requesting an answer from another comm?";
return $recycler:valid(this.outgoing);
.
#182:32
"Show the comm's number.";
pass(@args);
EMU = $emu;
line_prefix = EMU.ANSI_Line_Prefix;
player:tell("Etched into the case and highlighted in bright neon blue is the call-number for ", this:dname(), ": ", this, ".  A large red button reads PANIC.");
if (!this:is_local_to(player))
return;
endif
if (this:refusing_calls())
player:tell(this:grammar_sub("%(dnamec) %<is> set to refuse all incoming calls."));
endif
channels = this:active_channels();
player:tell("A live display, backlit green, lists active channels:");
bold = EMU:tokenize("bold");
norm = EMU:tokenize("normal");
ctext = {};
current = this:active_channel();
for ch in (channels)
if ($object_utils:has_callable_verb(ch, "callname"))
cname = $string_utils:right(ch:callname(), -24);
if (ch == current)
ctext = {@ctext, tostr(line_prefix, " -> ", bold, cname, norm, " <-")};
else
ctext = {@ctext, tostr("    ", cname)};
endif
else
this:remove_active_channel(ch);
endif
endfor
if (ctext)
player:tell_lines(ctext);
else
player:tell("  (Not Connected.)");
endif
if (this:is_receiving_call())
player:tell(line_prefix, "[ ", EMU:tokenize("magenta", "flashing"), "Incoming Call", norm, ": Type `answer ", this.name, "` to receive. ]");
else
player:tell("[ No messages. ]");
endif
.
#182:33
":callnum()";
"Return the number of this communicator/channel (as a string).";
return tostr(this);
.
#182:34
":callname()";
"Return the callname for this communicator; it defaults to the callnumbers.";
user = this:user();
return tostr(this:callnum(), valid(user) ? "/" + user.name | "");
.
#182:35
"lookup <user> on comm";
"Find the communicator ID of the given user.";
string = args[1];
if (c = this:lookup_user(string, "... Searching FoneNet Database."))
this:output(tostr($string_utils:left(c[2]:name(), -24, "."), c[1]));
return c;
endif
this:output(tostr("No communicator ID for \"", string, "\" found."));
return $failed_match;
.
#182:36
"press PANIC on comm";
"Press the PANIC button, sending a distress signal with your name and location to the 911 channel.";
if (!(dobjstr in {"PANIC", "DISTRESS", "HELP", "911", "EMERGENCY"}))
player:tell("You PANIC and press any button you can find...");
endif
if (this:channel_match_failed(ch = this:match_channel("911")))
return;
endif
this:announce_messages("panic");
ch:input(tostr("!*beep*!HELP--", player.name, "@\"", player.location.name, "\"--HELP!*beep*!"));
.
#182:37
":lookup_user(str[, suspend_msg])";
"Return {OBJ comm, OBJ user}, or false.";
string = args[1];
msg = (length(args) > 1) ? tostr(args[2]) | "";
for c in ($object_utils:descendants(this.root))
if ($command_utils:running_out_of_time())
msg && this:output(msg);
suspend(2);
endif
if (valid(u = c:user()) && u:matches(string))
return {c, u};
endif
endfor
.
#182:38
":tell_listeners(LIST people STR text)";
"Does the final notification of an incoming msg.";
"Sub a *beep* for an actual beep.";
m = strsub(args[2], "*beep*", $emu.beep);
for c in (args[1])
c:tell(m);
endfor
.
#182:39
":connect_to(new_channel)";
ch = args[1];
this:add_active_channel(ch);
this:output("*CONNECT*", ch);
`ch:_connect(this) ! E_VERBNF';
.
#182:40
":disconnect_from(old_channel)";
ch = args[1];
this:remove_active_channel(ch);
this:output("*DISCONNECT*", ch);
`ch:_disconnect(this) ! E_VERBNF, E_INVIND';
.
#182:41
":[o]speak_msg(STR msg, @ps_args)";
"Message printed to player [room] when e speaks into this.  Blank it if you want no message shown.";
":omumble_msg(STR msg, @ps_args)";
"Message printed when an un-exclaimed message is spoken into this communicator.";
"Replace $text in both with the spoken text.";
msg = strsub(this.(verb), "$text", args[1]);
return $string_utils:pronoun_sub(msg, @listdelete(args, 1));
.
#182:42
":do_incoming_notification()";
user = this:user();
loc = this.location;
if (loc != user)
while (valid(loc.location) && (loc.location != user))
loc = loc.location;
endwhile
endif
if (valid(room = this:room()))
room:announce_all_but({user}, $string_utils:pronoun_sub(this.oincoming_msg, user, this, loc));
endif
this:output(this:incoming_msg(this.incoming), this.incoming, 1);
.
#182:43
":update_user()";
"Find who's using this comm, and set the user property to their dbref.";
u = #-1;
l = this;
rpg = $rpg;
while ((!valid(u)) && valid(l = l.location))
u = rpg:is_char(l) ? l | u;
endwhile
return this.user = u;
.
#182:44
"refuse all with comm";
"Refuse all calls.  Your comm will never ring again.";
"refuse none with comm";
"Stop refusing calls.";
if (dobjstr == "all")
player:tell("You turn on your communicator's call refusal.");
this.refusals = {1};
elseif (dobjstr == "none")
player:tell("You turn off your communicator's call refusal.");
this.refusals = {};
else
player:tell("Right now you can only 'refuse all' or 'refuse none'.");
endif
.
#182:45
":refusing_calls()";
"Return true if this.refusals is true.";
return this.refusals ? 1 | 0;
.
#183:0
":match_channel(str) -> Channel object matching `str'.";
all = $object_utils:leaves(this.generic_channel);
return $match_utils:match(args[1], all);
.
#183:1
":title() -> Show a funky techie kinda name.";
return tostr("server.", this);
.
#183:2
":channel_match_failed(obj, str) -- What went wrong?";
if ($recycler:valid(ch = args[1]))
return 0;
elseif (ch == $nothing)
caller:output("No channel reference given.");
elseif (ch == $ambiguous_match)
caller:output("More than one channel by that name.");
else
caller:output("No channel by that name.");
endif
return 1;
.
#183:3
"Hold da channels.";
return $object_utils:isa(args[1], this.generic_channel);
.
#183:4
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
this.generic_channel = $comm_channel;
.
#184:0
":subscribes([OBJ comm])";
"If no args, return all subscribers.  Else, return true if comm is subscribed to this channel.";
if (!this:is_readable_by(caller_perms(), caller))
return E_PERM;
endif
return args ? args[1] in this.subscribers | this.subscribers;
.
#184:1
":input(msg) -> Broadcast to all subscribers.";
all = this:online_subscribers();
if ((!(caller in all)) && (!this:is_controllable_by(caller_perms(), caller)))
return E_PERM;
endif
msg = args[1];
for comm in (setremove(all, caller))
try
comm:output(msg, this);
except (E_VERBNF)
this:remove_subscriber(comm);
endtry
endfor
.
#184:2
":approved(OBJ comm)";
"Is the given comm allowed to subscribe to this channel?";
return 1;
.
#184:3
":add_subscriber(OBJ comm)";
"Add the given communicator to this channel.";
comm = args[1];
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
this.subscribers = setadd(this.subscribers, comm);
return 1;
.
#184:4
":remove_subscriber(OBJ comm)";
"Remove the given communicator from this channel.";
comm = args[1];
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
this.subscribers = setremove(this.subscribers, comm);
return 1;
.
#184:5
":_connect(comm) -- Notify this channel of an attempted connection to it.";
comm = args[1];
if ((caller != comm) && (!this:is_controllable_by(caller_perms(), caller)))
return E_PERM;
endif
this:add_subscriber(comm);
this:output("*CONNECT*", comm);
.
#184:6
":_disconnect(comm) -- Notify this channel of a disconnection from it.";
comm = args[1];
if ((caller != comm) && (!this:is_controllable_by(caller_perms(), caller)))
return E_PERM;
endif
this:remove_subscriber(comm);
this:output("*DISCONNECT*", comm);
.
#184:7
":callname()";
return this:title();
.
#184:8
":callnum()";
return this.name;
"Return the number of this communicator/channel (as a string).";
return tostr(this);
.
#184:9
":output(msg, origin)";
msg = args[1];
if (this:do_channel_command(caller, @args))
return;
endif
this:input(@args);
.
#184:10
":do_channel_command(caller, msg, origin)";
{comm, msg, origin} = args;
if (msg in {"w", "who"})
this:cmd_who(@args);
return 1;
endif
return 0;
.
#184:11
":cmd_who(caller, msg, origin)";
"Output a string describing all those listening to this channel.";
{comm, msg, origin} = args;
output = tostr($string_utils:iname_listc($set_utils:intersection($login:connected(), $list_utils:map_verb(this:subscribes(), "user"))), ".");
comm:output(tostr(msg, "-> ", output));
.
#184:12
":online_subscribers()";
"Return only the communicators of those users connected.";
online = {};
for c in (this.subscribers)
try
if (connected_seconds(c:user()))
online = {@online, c};
endif
except (E_VERBNF)
this.subscribers = setremove(this.subscribers, c);
except (E_INVARG)
"...nothing, just catch it...";
endtry
endfor
return online;
.
#184:13
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
this.subscribers = {};
.
#185:0
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#185:1
{cp, ?c = cp} = args;
return (c == this) || $rpg:trusted(cp);
.
#185:2
{who} = args;
return this:trusted(who);
.
#185:3
":match_species(STR)";
"Return an object describing the species named by STR.";
{spec} = args;
return $match_utils:match(spec, $object_utils:descendants($species));
.
#185:4
":set_character_species(OBJ char, OBJ species)";
"Set char's species to the given species, after attempting to 'remove' their former species, if defined.";
{char, species} = args;
if (!this:trusted(caller_perms(), caller))
raise(E_PERM);
endif
if (valid(old = char.species))
this:unset_character_species(char, species);
endif
result = species:convert_character(char);
return result;
.
#185:5
":unset_character_species(OBJ char, OBJ species)";
"Attempting to undo the attributes of the given species from the given character.";
{char, species} = args;
if (!this:trusted(caller_perms(), caller))
raise(E_PERM);
endif
result = species:revert_character(char);
return result;
.
#185:6
"@species <char> is <species-name>";
if (player != caller_perms())
return raise(E_PERM);
endif
if (!$rpg:trusted(player))
return raise(E_PERM);
endif
dobj = $rpg:match_character(dobjstr);
if ($rpg:character_match_failed(dobj, dobjstr))
return;
endif
iobj = this:match_species(iobjstr);
if ($match_utils:object_match_failed(iobj, iobjstr))
return;
endif
result = this:set_character_species(dobj, iobj);
"this REALLY NEEDS WORK, heh";
if (result)
player:tell("Set the species of ", $string_utils:nn(dobj), " to ", $string_utils:nn(iobj), ".");
else
player:tell("FAILED to set the species of ", $string_utils:nn(dobj), " to ", $string_utils:nn(iobj), ".  This might be a problem with this feature, or a misconfiguration of the species object.");
endif
.
#186:0
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#186:1
if (!(caller in {this, #0}))
return raise(E_PERM);
endif
try
user = this.last_user;
"... try to set this options to the same as the user options ...";
for p in ({"pagelen", "linelen", "eval_subs", "eval_time", "eval_ticks", "eval_env"})
`this.(p) = user.(p) ! E_PROPNF';
endfor
for p in (user:option_packages())
for o in ($set_utils:union(user:get_options(p), this:get_options(p)))
this:set_option(p, o, user:get_option(p, o));
endfor
endfor
finally
return pass(@args);
endtry
.
#187:0
if (!caller_perms().wizard)
return E_PERM;
endif
this.interface = $species_feature;
.
#187:1
{cp, ?c = cp} = args;
return ((cp == $gm) || (c == this)) || (c == this.interface);
.
#187:2
":check_eligibility(OBJ char[, INT adjust])";
"Return true if the given character is suitable for this species.  Otherwise raise an error with its arg as the rejection notice.";
"If the adjust argument is given and true, adjust the character so that e is eligible, if possible.";
return 1;
.
#187:3
":new_body_areas_for(OBJ char)";
"Return a list of body areas for the given character if e were to become this species.";
{char} = args;
"Might be nice if the areas were stored relative to former areas, or as strings, to facilitate corification.";
return this.body_areas;
.
#187:4
":new_natural_weapons_for(OBJ char)";
"Return a list of natural weapons for the given character if e were to become this species.";
{char} = args;
return this.natural_weapons;
.
#187:5
":convert_character(OBJ char)";
if (!this:trusted(caller_perms(), caller))
raise(E_PERM);
endif
{char} = args;
"First check that the character is eligible for the species.";
if (!this:check_eligibility(char))
return E_NACC;
endif
"Attempt to set the character's body areas, after backing up the old.";
if (new_areas = this:new_body_areas_for(char))
old_areas = char:body_areas();
try
char:set_body_areas(new_areas);
except (ANY)
char:set_body_areas(old_areas);
return 0;
endtry
endif
"Attempt to set the character hand objects.";
if (new_hands = this:new_hands_for(char))
char:set_hands(new_hands);
endif
"Try to set natural weapons.  Failure here isn't as drastic, so no recovery.";
if (new_weapons = this:new_natural_weapons_for(char))
char:set_natural_weapon(new_weapons);
endif
char:set_natural_protection(this:new_natural_protection_for(char));
`this:do_custom_conversions() ! ANY';
char:set_species(this);
this:add_features_to(char);
return 1;
.
#187:6
":revert_character(OBJ char)";
"Undo changes made when the given character became this species.";
if (!this:trusted(caller_perms(), caller))
raise(E_PERM);
endif
{char} = args;
char:set_body_areas($character:body_areas());
char:set_hands($character.hands);
clear_property(char, "species");
char:set_natural_weapon($combatant:natural_weapons());
char:set_natural_protection($combatant.natural_protection);
`this:undo_custom_conversions() ! ANY';
this:remove_features_from(char);
return 1;
.
#187:7
":new_features_for(OBJ char)";
"Return a list of features for the given character if e were to become this species.";
{char} = args;
return this.features;
.
#187:8
":new_natural_protection_for(OBJ char)";
"Return the natural protection afforded members of this species.  At the time of this writing, generic clothing gives 5 points of damage absorption.  A tough hide would give at least that, but most animal skins are probably less.  Natural protection is subtracted directly from damage taken, so be very careful with these values!  Heavy damage ranges between 20-30; even the strongest of natural armor shouldn't absorb that much.  Consider also that the character may be wearing armor which further protects them.  Eg. Armadillo ~12, Rhino ~10, Bear ~6-7, Dog ~3-5, Chinchilla 0.";
{char} = args;
return this.natural_protection;
.
#187:9
tborder = $string_utils:left("--" + this.name, player:linelen(), "-");
bborder = $string_utils:right(this.name + "--", player:linelen(), "-");
player:tell(tborder);
player:tell("Unique Body Areas: ", $string_utils:nn($set_utils:difference(this.body_areas, $character.body_areas)));
player:tell("Natural Weapons: ", $string_utils:nn(this.natural_weapons));
player:tell("Natural Protection: ", this.natural_protection);
player:tell("Features: ", $string_utils:nn(this.features, ", ", "None"));
player:tell(bborder);
.
#187:10
":new_hands_for(OBJ char)";
"Return a list of `hand objects' for the given character if e were to become this species.";
{char} = args;
return this.hands;
.
#187:11
":remove_features_from(OBJ player)";
"Remove this species' features from the given character.";
if (caller != this)
return raise(E_PERM);
endif
char = args[1];
if (new_features = this:new_features_for(char))
for f in (new_features)
`char:remove_feature(f) ! ANY';
endfor
endif
"Wizardly!";
.
#187:12
":add_features_to(OBJ player)";
"Add this species' features to the given character.";
if (caller != this)
return raise(E_PERM);
endif
char = args[1];
if (new_features = this:new_features_for(char))
for f in (new_features)
`char:add_feature(f) ! ANY';
endfor
endif
"Wizardly!";
.
#188:0
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#188:1
":build_integration_msg(LIST eargs, OBJ affected)";
"Return text describing the effect this object has upon 'affected'.";
"Must always return a list, even if an empty one.";
return {};
.
#188:2
":added(LIST eargs, OBJ affected, INT id)";
"Called when this effect is added to 'affected'.  Initialize any effects on the object.  For example, a steroid drug might increase strength now.  (And decrease it appropriately in :removed.)";
"Return eargs, possibly modified.";
{eargs, affected, id} = args;
eargs = this:earg_set(eargs, "id", id);
try
seconds = this:earg_get(eargs, "duration");
token = this:_setup_removal(affected, id, seconds);
eargs = this:earg_set(eargs, "removal_token", token);
except (E_VARNF)
endtry
return eargs;
.
#188:3
":removed(LIST eargs, OBJ affected)";
"Called when this effect is removed from 'affected'.  Remove any effects from the object.  For example, a steroid drug might return strength to normal.";
"Return eargs, possibly modified.";
{eargs, affected} = args;
try
removal_token = this:earg_get(eargs, "removal_token");
this:_cancel_removal(removal_token);
eargs = this:earg_delete(eargs, "removal_token");
except (E_VARNF)
endtry
return eargs;
.
#188:4
":pulsed(LIST eargs, OBJ affected)";
"Called when the affected object receives a pulse.  For example, an 'on fire' effect might inflict additional fire damage and change eargs to reflect the condition of the fire.  (Later to be picked up in build_integration_msg and reflected in the affected object's description.)";
"Return eargs, possibly modified, or E_INVIND if the effect should be removed immediately after this pulse.";
{eargs, affected} = args;
return eargs;
.
#188:5
":earg_get(LIST eargs, ANY key)";
"Return the value if the key exists in the list, else return E_VARNF.";
{eargs, key} = args;
v = $list_utils:assoc(key, eargs);
if (v)
return v[2];
else
raise(E_VARNF, "Key not found in eargs");
endif
.
#188:6
":earg_get(LIST eargs, ANY key, ANY value)";
"Return the new list.  Add the {key, value} pair if it does not exist.";
{eargs, key, val} = args;
i = $list_utils:iassoc(key, eargs);
if (i)
eargs[i][2] = val;
else
eargs = {@eargs, {key, val}};
endif
return eargs;
.
#188:7
":_setup_removal(OBJ affected, INT effect_id, INT seconds)";
if (caller != this)
return raise(E_PERM);
endif
{affected, id, seconds} = args;
return $scheduler:add_to_queue(seconds, "remove", affected, id);
.
#188:8
":_cancel_removal(OBJ affected, INT effect_id, INT seconds)";
if (caller != this)
return raise(E_PERM);
endif
{removal_token} = args;
return $scheduler:remove_from_queue(removal_token);
.
#188:9
":scheduler_message(OBJ affected, INT effect_id)";
if (caller != $scheduler)
return raise(E_PERM);
endif
{action, affected, id} = args;
if (action == "remove")
affected:remove_effect(id);
endif
.
#188:10
":earg_delete(LIST eargs, ANY key)";
"Deletes the given key in 'eargs'.  Returns the modified eargs list, or raises E_VARNF if it doesn't exist.";
{eargs, key} = args;
i = $list_utils:iassoc(key, eargs);
if (!i)
return raise(E_VARNF, "Key not found in eargs");
endif
return listdelete(eargs, i);
.
#189:0
{delay, @data} = args;
if (delay < this.min_queue_delay)
raise(E_INVARG);
elseif (!valid(caller))
raise(E_INVARG);
endif
info = callers(1)[1];
time = time() + delay;
token = task_id();
while (token in this.queue_tokens)
$command_utils:suspend_if_needed(0);
token = 1 + random(time);
endwhile
$command_utils:suspend_if_needed(0);
if (time <= time())
fork (0)
caller:jscheduler_message(@data);
endfork
return 0;
else
index = $list_utils:find_insert(this.queue_times, time);
this.queue_users = listinsert(this.queue_users, caller, index);
this.queue_tokens = listinsert(this.queue_tokens, token, index);
this.queue_times = listinsert(this.queue_times, time, index);
this.queue_data = listinsert(this.queue_data, data, index);
this.queue_info = listinsert(this.queue_info, info, index);
if ((index == 1) || (!$code_utils:task_valid(this.queue_task)))
fork (0)
this:run();
endfork
endif
return token;
endif
.
#189:1
{token} = args;
$command_utils:suspend_if_needed(0);
index = token in this.queue_tokens;
if (!index)
return 0;
endif
user = this.queue_users[index];
if (((!(caller in {user, this})) && (!$perm_utils:controls(caller, user))) && (!$perm_utils:controls(caller_perms(), this)))
raise(E_PERM);
else
this.queue_users = listdelete(this.queue_users, index);
this.queue_tokens = listdelete(this.queue_tokens, index);
this.queue_times = listdelete(this.queue_times, index);
this.queue_data = listdelete(this.queue_data, index);
this.queue_info = listdelete(this.queue_info, index);
if ((index == 1) || (!$code_utils:task_valid(this.queue_task)))
fork (0)
this:run();
endfork
endif
return token;
endif
.
#189:2
if (((caller != this) && (caller != $taskmaster)) && (!$perm_utils:controls(caller_perms(), this)))
return raise(E_PERM);
endif
$command_utils:suspend_if_needed(0);
if (this.queue_task && $code_utils:task_valid(this.queue_task))
`kill_task(this.queue_task) ! ANY';
endif
this.queue_task = 0;
this:cleanup_queue();
if (`time = this.queue_times[1] ! E_RANGE')
this.queue_task = task_id();
suspend(max(0, time - time()));
this.queue_task = 0;
this:run();
endif
.
#189:3
if (caller != this)
raise(E_PERM);
elseif (!this.queue_times)
return;
endif
index = 1;
$command_utils:suspend_if_needed(0);
while (`this.queue_times[index] <= time() ! E_RANGE')
user = this.queue_users[index];
data = this.queue_data[index];
fork (0)
"// need to fork, otherwise we can get screwed if user:jscheduler_message suspend";
user:jscheduler_message(@data);
endfork
index = index + 1;
endwhile
if (index > 1)
if (index > length(this.queue_times))
this.queue_users = {};
this.queue_tokens = {};
this.queue_times = {};
this.queue_data = {};
this.queue_info = {};
else
this.queue_users = this.queue_users[index..$];
this.queue_tokens = this.queue_tokens[index..$];
this.queue_times = this.queue_times[index..$];
this.queue_data = this.queue_data[index..$];
this.queue_info = this.queue_info[index..$];
endif
$command_utils:suspend_if_needed(0);
endif
.
#189:4
"Usage:  @status OBJ <what> on <this>";
this:secure_call(caller, caller_perms());
if (dobjstr)
if (dobjstr == "all")
dobj = $nothing;
else
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
return;
elseif ((!$perm_utils:controls(player, this)) && (!$perm_utils:controls(player, dobj)))
return player:tell(E_PERM);
endif
endif
else
dobj = player;
endif
tasks = this:queued_tasks(dobj);
"// I'm a lazy bastard, code borrowed from $prog:@forked";
if (tasks)
su = $string_utils;
player:tell("Queue ID    Start Time            Owner         Verb (Line) [This]");
player:tell("--------    ----------            -----         ------------------");
now = time();
for task in (tasks)
q_id = task[1];
start = task[2];
time = (start >= now) ? ctime(start)[5..24] | su:left((start == -1) ? "Reading input ..." | tostr(now - start, " seconds ago..."), 20);
owner = task[5];
owner_name = valid(owner) ? owner.name | tostr("Dead ", owner);
vloc = task[6];
vname = task[7];
lineno = task[8];
this = task[9];
player:tell(tostr(su:left(tostr(q_id), 10), "  ", time, "  ", su:left(owner_name, 12), "  ", vloc, ":", vname, " (", lineno, ")", (this != vloc) ? tostr(" [", this, "]") | ""));
endfor
else
player:tell("No tasks.");
endif
.
#189:5
"Usage:  @cancel INT <queue token> on <scheduler>";
this:secure_call(caller, caller_perms());
token = $code_utils:tonum(dobjstr);
if (token == E_TYPE)
player:tell("Usage:  @cancel INT <queue token> on <scheduler>");
return;
endif
if (!(index = token in this.queue_tokens))
player:tell("Invalid task ID ", token, ".");
else
user = this.queue_users[index];
if ((!$perm_utils:controls(player, this)) && (!$perm_utils:controls(player, user)))
player:tell("Soory, you are not allowed to cancel task ID ", token, ".");
else
if (this:remove_from_queue(token))
player:tell("Cancelled task ID ", token, ".");
else
player:tell("Couldn't cancel task ID ", token, ".");
endif
endif
endif
.
#189:6
if (caller != this)
raise(E_PERM);
endif
{what} = args;
tasks = {};
for i in [1..length(this.queue_users)]
$command_utils:suspend_if_needed(0);
user = this.queue_users[i];
if (((what == $nothing) || (what == user)) || $perm_utils:controls(what, user))
time = this.queue_times[i];
token = this.queue_tokens[i];
{junk, vname, junk, vloc, junk, lineno} = this.queue_info[i];
tasks = {@tasks, {token, time, "#3#", "#4#", user.owner, vloc, vname, lineno, user}};
endif
endfor
return tasks;
.
#189:7
{task_token} = args;
return task_token && (!(!(task_token in this.queue_tokens)));
.
#189:8
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#190:0
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#190:1
if (!args)
player:tell("Edit what?");
else
this:invoke(argstr, verb);
endif
.
#190:2
"save";
"Save the stats to a character.";
if ((caller != player) && (caller_perms() != player))
return E_PERM;
elseif (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
endif
object = this.objects[who];
result = this:apply_stats(object);
if (typeof(result) == ERR)
player:tell("Failed to write stats back to ", $string_utils:nn(object.original), ".");
return E_PERM;
endif
player:tell("Wrote game stats to ", $string_utils:nn(object.original), ".");
this:set_changed(who, 0);
.
#190:3
"Return the 'name (#num)' of the character being edited.";
{who} = args;
return ((this:ok(who) && $recycler:valid(what = this.objects[who])) && $object_utils:isa(what, $effigy)) ? $string_utils:nn(what.original) | "???";
.
#190:4
"Initialize the session for editing a given character.";
{who, object} = args;
if (!this:ok(who))
return;
endif
this:load(who, {});
this.objects[who] = object;
this.active[who]:tell("Now editing ", this:working_on(who), ".");
.
#190:5
":parse_invoke(string, verbname)";
" => {object, stats} or error";
{string, verbname} = args;
source = $rpg:match_character(string);
if ($rpg:character_match_failed(source, string))
return;
endif
object = this:fetch_stats(source);
if (typeof(object) == ERR)
player:tell("Error (", object, ") trying to load game statistics for ", $string_utils:nn(source), ".");
return object;
endif
return {object};
.
#190:6
":fetch_stats(object)";
"Return a new effigy object representing the given character to be edited.";
if (caller != this)
return raise(E_PERM);
endif
{object} = args;
return $effigy:clone(object);
.
#190:7
":apply_stats(object)";
if (caller != this)
return raise(E_PERM);
endif
{object} = args;
return object:save();
.
#190:8
":_mod_stat(who, stat, mod)";
{who, stat, mod} = args;
if (!this:ok(who))
return raise(E_PERM);
endif
if (typeof(stat) == OBJ)
stat = stat.property;
endif
object = this.objects[who];
return object:mod_stat(stat, mod);
.
#190:9
":_set_stat(who, stat, val)";
{who, stat, val} = args;
if (!this:ok(who))
return raise(E_PERM);
endif
if (typeof(stat) == OBJ)
stat = stat.property;
endif
object = this.objects[who];
return object:set_stat(stat, val);
.
#190:10
":_set_stat(who, stat)";
{who, stat} = args;
if (!this:ok(who))
return raise(E_PERM);
endif
if (typeof(stat) == OBJ)
stat = stat.property;
endif
object = this.objects[who];
return object:get_stat(stat);
.
#190:11
"Syntax: boost <stat> by <pts>";
"";
"Raises the specified stat by the given number of points.";
if ((caller != player) && (caller_perms() != player))
return E_PERM;
elseif (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
endif
if (m = match(argstr, " +by +%([0-9]+%)"))
stat_spec = argstr[1..m[1] - 1];
pts = toint(substitute("%1", m));
else
player:tell("Usage: boost <stat> by <points>");
return;
endif
"match the stat";
{r, stat} = this:stat_match_result(stat_spec);
if (!r)
player:tell(stat);
return;
endif
"check for valid (>0) points";
if (toint(pts) <= 0)
player:tell("Points changed must be greater than zero.");
return E_QUOTA;
endif
"check that we have enough potential";
pot = tofloat(this:_get_stat(who, "pot"));
required_pts = tofloat(pts);
if (this:_is_attribute(who, stat))
required_pts = required_pts * tofloat(this.attribute_cost);
endif
if (pot < required_pts)
player:tell("That would require ", toint(required_pts), " potential points; this character doesn't have enough.");
return E_QUOTA;
endif
old_stat = this:_get_stat(who, stat);
new_stat = old_stat + pts;
if (msg = this:forbid_stat_boost(who, stat, new_stat))
player:tell(msg);
return E_NACC;
endif
result = this:_set_stat(who, stat, new_stat);
if (typeof(result) == ERR)
player:tell("Failed (", result, ") to set ", stat.name, ".");
return;
endif
new_pot = this:charge_pot(who, required_pts);
this:set_changed(who, 1);
player:tell("Raised ", stat.name, " by ", pts, " to ", result, ".  ", toint(required_pts), " potential points used; ", toint(new_pot), " remaining.");
.
#190:12
"Return true if the character has rerolled since the last time their character was edited.";
{who} = args;
char = this.objects[who].original;
try
{editor, when} = char.last_edited_by;
except (ANY)
return -1;
endtry
if (!char.last_reroll)
return 0;
endif
return char.last_reroll >= when;
.
#190:13
":_is_attribute(INT who, STR|OBJ stat)";
"Return true if the given stat is an primary attribute.";
{who, stat} = args;
if (typeof(stat) == OBJ)
return $object_utils:isa(stat, $attribute);
endif
return stat in $rpg.atts;
.
#190:14
":_is_skill(INT who, STR|OBJ stat)";
"Return the position in the skills list if the given stat is an skill.";
{who, stat} = args;
if (typeof(stat) == OBJ)
return $object_utils:isa(stat, $attribute) && (!$object_utils:isa(stat, $attribute));
endif
char = this.objects[who].original;
return stat in char:all_skills();
.
#190:15
"Syntax: trade <att/skill> for [num] <att/skill>";
"";
"Raises the specified stat by the given number of points.";
"Attribute:Attribute => 1:1";
"Skill:Skill         => 1:1";
"Attribute:Skill     => 1:10";
"";
"If `sacrifice' is used instead of `trade', ALL points are swapped.";
if ((caller != player) && (caller_perms() != player))
return E_PERM;
elseif (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
elseif (!this:first_time(who))
"maybe allow GMs to override this?";
player:tell("A character may only trade attributes and skills the first time they are edited after a reroll.");
return;
endif
"parse it";
if (m = match(iobjstr, "^%([0-9]+%) +"))
pts = toint(substitute("%1", m));
to_stat_spec = iobjstr[m[2] + 1..$];
elseif (verb == "sacrifice")
pts = -1;
to_stat_spec = iobjstr;
else
pts = 1;
to_stat_spec = iobjstr;
endif
from_stat_spec = dobjstr;
{r, from_stat} = this:stat_match_result(from_stat_spec);
if (!r)
player:tell(from_stat);
return;
endif
{r, to_stat} = this:stat_match_result(to_stat_spec);
if (!r)
player:tell(to_stat);
return;
endif
"check for points available for trading";
available_pts = this:_get_stat(who, from_stat);
if (pts == -1)
pts = available_pts;
endif
"check for valid (>0) points";
if (pts <= 0)
player:tell("Points exchanged must be greater than zero.");
return E_QUOTA;
endif
"derive conversion rate";
{required_pts, pts} = this:conversion_rate(who, from_stat, to_stat, pts);
if (available_pts < required_pts)
player:tell("There are only ", available_pts, " available in ", from_stat.name, "; ", required_pts, " are required for the trade.");
return;
endif
"possibly forbid restricted boosts";
old_stat = this:_get_stat(who, to_stat);
new_stat = old_stat + pts;
if (msg = this:forbid_stat_boost(who, to_stat, new_stat))
player:tell(msg);
return E_NACC;
endif
"do the setting";
to_result = this:_mod_stat(who, to_stat, pts);
if (typeof(to_result) == ERR)
player:tell("Failed (", to_result, ") to set ", to_stat.name, ".");
return;
endif
from_result = this:_mod_stat(who, from_stat, -required_pts);
if (typeof(from_result) == ERR)
this:_mod_stat(who, to_stat, -pts);
player:tell("Failed (", from_result, ") to set stat ", from_stat.name, ".");
return;
endif
this:set_changed(who, 1);
player:tell("Raised ", to_stat.name, " by ", pts, " to ", to_result, "; lowered ", from_stat.name, " by ", required_pts, " to ", from_result, ".");
.
#190:16
":loaded(player)";
"Return the index of the given player in the editor, if they're working on something.";
{user} = args;
return user in this.active;
.
#190:17
"list";
"";
"List the character in its current state of edit.";
if ((caller != player) && (caller_perms() != player))
return E_PERM;
elseif (!(who = this:loaded(player)))
player:tell(this:nothing_loaded_msg());
return;
endif
char = this.objects[who];
char:tell_stats(1, 1, 0, 0);
player:tell("Potential Remaining: ", toint(this:_get_stat(who, "pot")));
.
#190:18
"WIZARDLY";
if ((caller != this) && (!caller_perms().wizard))
return raise(E_PERM);
endif
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
pop = length(this.active);
i = pop;
while (i)
this:kill_session(i);
i = i - 1;
endwhile
return pop || 1;
.
#190:19
{who} = args;
if (!this:ok(who))
return 0;
endif
what = this.objects[who];
if ($object_utils:isa(what, $effigy))
what:destroy();
endif
for p in ({@this.stateprops, {"original"}, {"active"}, {"times"}})
this.(p[1]) = listdelete(this.(p[1]), who);
endfor
return who;
.
#190:20
"WIZARDLY";
{who} = args;
if (!this:ok(who))
return 0;
endif
"reset recycles the effigy, too";
this.objects[who]:destroy();
for p in (this.stateprops)
this.(p[1])[who] = p[2];
endfor
this.times[who] = time();
return who;
.
#190:21
":forbid_stat_boost(INT who, OBJ stat, INT pts)";
"Possibly forbid boosting attributes, or skills past a certain level if this is the first time a character has been edited.  Of course, someone can easily cheat by saving and re-entering, but then they lost the ability to trade.";
{who, stat, pts} = args;
char = this.objects[who];
if (this:_is_attribute(who, stat) || this:first_time(who))
return stat:disallow_boost_by(char, pts);
endif
return 0;
.
#190:22
":_match_stat(STR|OBJ)";
return $rpg:match_stat(@args);
.
#190:23
":conversion_rate(OBJ from_stat, OBJ to_stat, INT pts)";
{who, from, to, to_pts} = args;
from_type = this:_is_attribute(who, from) ? 1 | 0;
to_type = this:_is_attribute(who, to) ? 1 | 0;
SKILL_ATT_RATE = this.attribute_cost;
if (from_type == to_type)
required_pts = to_pts;
elseif (to_type > from_type)
required_pts = to_pts * SKILL_ATT_RATE;
elseif (to_pts < SKILL_ATT_RATE)
"must raise at least SKILL_ATT_RATE when trading an att for a skill";
to_pts = SKILL_ATT_RATE;
required_pts = 1;
else
required_pts = to_pts / SKILL_ATT_RATE;
endif
return {required_pts, to_pts};
.
#190:24
":stat_match_result(STR)";
{spec} = args;
result = this:_match_stat(spec);
msg = $rpg:stat_match_failed(result, spec);
if (msg)
return {0, msg};
endif
return {1, result};
.
#190:25
return $string_utils:pronoun_sub(this.description);
.
#190:26
":charge_pot(who, points)";
"Charge the given amount of potential points to the given character.";
{who, points} = args;
if (!this:ok(who))
raise(E_PERM);
endif
effigy = this.objects[who];
old_pot = effigy:get_stat("pot");
if (typeof(old_pot) != FLOAT)
old_pot = tofloat(old_pot);
endif
if (old_pot < points)
raise(E_QUOTA);
endif
new_pot = old_pot - points;
return effigy:set_stat("pot", new_pot);
.
#190:27
"Return a list of all uncoded skill/advantage objects.";
return $object_utils:descendants($uncoded);
.
#190:28
":match_uncoded(STR spec)";
{spec} = args;
return $match_utils:match(spec, $object_utils:leaves($uncoded));
.
#190:29
":uncoded_match_result(STR spec)";
{spec} = args;
result = this:match_uncoded(spec);
"uh...under construction";
msg = $rpg:stat_match_failed(result, spec);
if (msg)
return {0, msg};
endif
return {1, result};
.
#190:30
":uncoded_match_failed(OBJ result, STR spec)";
{result, spec} = args;
if (valid(result))
return result;
elseif (result == $ambiguous_match)
all = $match_utils:match_list(spec, $skill_db:contents());
if (length(all) > 1)
return tostr("\"", spec, "\" could match any one of ", $string_utils:name_list(all, "several skills, but we can't come up with any right now", " or "), ".");
else
return tostr("There is more than one primary attribute or skill matching \"", spec, "\"; try to be more specific.");
endif
elseif (result == $failed_match)
return tostr("There is no primary attribute or skill matching \"", spec, "\".");
elseif (!spec)
return tostr("You must give the name of some primary attribute or skill.");
else
return tostr("Match on \"", spec, "\" failed.");
endif
.
#190:31
"Don't allow folks to keep things in here.";
return 0;
.
#190:32
"Nothing for now.  To be for editing sheets?";
player:tell("You can't say or emote anything here.");
.
#191:0
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#191:1
":check_learn(OBJ char, STR subclass, INT level, LIST current_uncoded)";
"Called when the given character wishes to add this advantage with the given subclass, at the given starting level.";
"=> {1, INT point_cost}";
"=> {0, STR failure_reason}";
{char, subclass, level, uncoded} = args;
last_reroll = `char.last_reroll ! E_PROPNF';
min_reroll_age = this.min_reroll_age;
if (last_reroll && ((time() - last_reroll) < min_reroll_age))
return {0, this:grammar_sub(tostr("%n may not be added by a character re-rolled (or created) less than ", $string_utils:from_seconds(min_reroll_age), " ago."))};
endif
first_connect = char.first_connect_time;
min_login_age = this.min_login_age;
if (first_connect && ((time() - first_connect) < min_login_age))
return {0, this:grammar_sub(tostr("%n may not be added by a player whose first connection was less than ", $string_utils:from_seconds(min_reroll_age), " ago."))};
endif
max_initial_level = this.max_initial_level;
if (level > this.max_initial_level)
return {0, this:grammar_sub(tostr("The maximum initial level for %n is ", max_initial_level, ".  Try starting with a level at or below that."))};
endif
msg = this:forbid_level(char, level, uncoded);
if (msg)
return {0, msg};
endif
msg = this:forbid_subclass(char, subclass, uncoded);
if (msg)
return {0, msg};
endif
"Something here should check incompatabilities with the passed uncoded.";
"We'll let it go for now so I can get this installed.  <Quinn;19981107>";
try
return {1, this.level_costs[level + 1]};
except (E_RANGE)
return {0, this:grammar_sub(tostr("The maximum level for %n is ", max_initial_level, ".  The max_initial_level warning on %n should have preceded this error; please inform ", this.owner.name, " of the error."))};
endtry
.
#191:2
":check_advance(OBJ char, STR subclass, INT level, LIST current_uncoded)";
"Called when the given character wishes to advance this advantage with the given subclass, to the given new level.";
"=> {1, INT point_cost}";
"=> {0, STR failure_reason}";
{char, subclass, level, uncoded} = args;
msg = this:forbid_level(char, level, uncoded);
if (msg)
return {0, msg};
endif
"Something here should check incompatabilities with the passed uncoded.";
"We'll let it go for now so I can get this installed.  <Quinn;19981107>";
try
return {1, this.level_costs[level + 1]};
except (E_RANGE)
return {0, this:grammar_sub(tostr("The maximum level for %n is ", max_initial_level, "."))};
endtry
.
#191:3
":forbid_level(OBJ char, INT level, LIST current_uncoded)";
"Return a STR describing why the given character cannot obtain the given level of mastery of this advantage.  The uncoded list is their current array of advantages, for use in resolving conflicts.";
{char, level, uncoded} = args;
if ((level + 1) < length(this.level_costs))
return this:grammar_sub(tostr("The maximum level for %n is ", max_initial_level, "."));
endif
return 0;
.
#191:4
":forbid_subclass(OBJ char, STR subclass, LIST current_uncoded)";
"Return a STR describing why the given character cannot learn this skill with the given subclassing.  (Eg. A subclass of 'Language' is 'Latin'.)  The uncoded list is their current array of advantages, for use in resolving conflicts.";
{char, subclass, uncoded} = args;
approved = this.preapproved_subclasses;
if (subclass in approved)
return 0;
endif
return this:grammar_sub(tostr("Allowed subclasses for %n include ", $string_utils:english_list(approved), ".  Contact a GM if you feel your subclass \"", subclass, "\" should be allowed."));
.
#192:0
return $match_utils:match_player(@args);
.
#192:1
{s} = args;
u = this:match_user(s);
return $match_utils:player_match_failed(u, s) ? 1 | u;
.
#192:2
":_invite(whostr)";
if (caller != this)
raise(E_PERM);
endif
if (!this:is_op(player))
return "Only operators can invite users.  You are not an operator.";
endif
{whostr} = args;
dobj = this:user_match_failed(whostr);
if (dobj)
return;
endif
try
this:toggle_user_access(dobj, 1);
except e (ANY)
return tostr("Invite failed: (", e[2], ")");
endtry
this:broadcast(this:invite_msg());
return 0;
.
#192:3
":_op(whostr)";
if (caller != this)
raise(E_PERM);
endif
if (!this:is_op(player))
return "Only operators can add operators.  You are not an operator.";
endif
{whostr} = args;
dobj = this:user_match_failed(whostr);
if (dobj)
return;
endif
try
this:toggle_op_status(dobj, 1);
except e (ANY)
return tostr("Op failed: (", e[2], ")");
endtry
this:broadcast(this:op_msg());
return 0;
.
#192:4
":_op(whostr)";
if (caller != this)
raise(E_PERM);
endif
if (!this:is_op(player))
return "Only operators can remove operators.  You are not an operator.";
endif
{whostr} = args;
dobj = this:user_match_failed(whostr);
if (dobj)
return;
endif
if ($perm_utils:controls(dobj, this))
return "A channel owner is implicitly an operator.";
endif
try
this:toggle_op_status(dobj, 0);
except e (ANY)
return tostr("Deop failed: (", e[2], ")");
endtry
this:broadcast(this:deop_msg());
return 0;
.
#192:5
":_ban(whostr)";
if (caller != this)
raise(E_PERM);
endif
if (!this:is_op(player))
return "Only operators can ban users.  You are not an operator.";
endif
{whostr} = args;
dobj = this:user_match_failed(whostr);
if (dobj)
return;
endif
if (this:is_op(dobj))
return "You cannot ban another operator.  Try using /deop first.";
endif
try
this:toggle_user_access(dobj, 0);
except e (ANY)
return tostr("Ban failed: (", e[2], ")");
endtry
this:broadcast(this:ban_msg());
return 0;
.
#192:6
":_kick(whostr)";
if (caller != this)
raise(E_PERM);
endif
if (!this:is_op(player))
return "Only operators can kick users.  You are not an operator.";
endif
{whostr} = args;
dobj = this:user_match_failed(whostr);
if (dobj)
return;
endif
if (this:is_op(dobj))
return "You cannot kick another operator.  Try using /deop first.";
endif
try
`this:broadcast(msg = this:kick_msg()) ! ANY';
this:remove_listener(dobj);
except e (ANY)
return tostr("Kick failed: (", e[2], ")");
endtry
return 0;
.
#192:7
":listeners()";
return this.listeners;
.
#192:8
":is_op(user)";
{user} = args;
return ((user == this) || $perm_utils:controls(user, this)) || (user in this.ops);
.
#192:9
":welcome_user(user)";
"True if user is allowed access to this channel; false otherwise.";
{user} = args;
if (this:is_op(user))
return 1;
elseif (`user in this.deny ! E_TYPE')
return 0;
elseif (`user in this.allow ! E_TYPE')
return 1;
elseif (typeof(this.allow) == LIST)
return 0;
else
return this.allow;
endif
.
#192:10
":broadcast(raw[, users])";
if (caller != this)
raise(E_PERM);
endif
prefix = this:prefix_msg();
{raw, ?set = 0} = args;
text = tostr(prefix, raw);
connected = connected_players();
for user in (this.listeners)
if (((!set) || (user in set)) && (user in connected))
user:notify(user:process_notified_text(text));
endif
endfor
.
#192:11
":do_command(cmd, argstr)";
if (player != caller_perms())
raise(E_PERM);
endif
{cmd, argstr} = args;
if ((!cmd) || (cmd[1] != "/"))
method = this.default_method;
else
method = "_" + cmd[2..$];
endif
if ($object_utils:has_verb(this, method))
return this:(method)(argstr);
else
return "invalid command";
endif
.
#192:12
return $string_utils:pronoun_sub(this.(verb));
.
#192:13
":_speak(text)";
if (caller != this)
raise(E_PERM);
endif
if (!this:welcome_user(player))
return "You aren't allowed in that channel.";
endif
{text} = args;
if (action = this:is_emoted_text(text))
raw = tostr(this:emoting_msg(), @action);
else
raw = tostr(this:speaking_msg(), text);
endif
this:broadcast(raw);
return 0;
.
#192:14
":_join()";
if (caller != this)
raise(E_PERM);
endif
try
this:add_listener(player);
this:broadcast(msg = this:join_msg());
except e (E_NONE)
player:tell("You switch your active channel to ", this:title(), ".");
"pass through to allow caller to set as current channel";
except e (ANY)
return tostr("Join failed: (", e[2], ")");
endtry
return 0;
.
#192:15
":_part()";
if (caller != this)
raise(E_PERM);
endif
try
`this:broadcast(msg = this:part_msg()) ! ANY';
this:remove_listener(player);
except e (ANY)
return tostr("Part failed: (", e[2], ")");
endtry
return 0;
.
#192:16
":add_listener(user)";
{user} = args;
if (caller != this)
raise(E_PERM);
elseif (!this:welcome_user(user))
raise(E_NACC, "Not invited");
elseif (user in this.listeners)
raise(E_NONE, "Already listening");
else
this.listeners = setadd(this.listeners, user);
return 1;
endif
.
#192:17
":remove_listener(user)";
{user} = args;
if ((caller != this) && (caller != $channel_feature))
raise(E_PERM);
elseif (user in this.listeners)
this.listeners = setremove(this.listeners, user);
return 1;
else
raise(E_NONE, "Not listening");
endif
.
#192:18
":toggle_op_status(user, bool)";
if (caller != this)
raise(E_PERM);
endif
{user, bool} = args;
opped = this:is_op(user);
if (bool)
if (opped)
raise(E_NONE, "Already an operator");
endif
if (this.ops == 0)
this.ops = {user};
elseif (typeof(this.ops) == LIST)
this.ops = setadd(this.ops, user);
endif
else
if (!opped)
raise(E_NONE, "Not an operator");
endif
if (this.ops == 0)
this.ops = {user};
elseif (typeof(this.ops) == LIST)
this.ops = setremove(this.ops, user);
endif
endif
.
#192:19
":toggle_user_access(user, bool)";
if (caller != this)
raise(E_PERM);
endif
{user, bool} = args;
welcomed = this:welcome_user(user);
if (bool)
if (welcomed)
raise(E_NONE, "Already welcome");
endif
`this.deny = setremove(this.deny, user) ! E_TYPE';
if (this.allow == 0)
this.allow = {user};
elseif (typeof(this.allow) == LIST)
this.allow = setadd(this.allow, user);
endif
else
if (!welcomed)
raise(E_NONE, "Already unwelcome");
endif
`this.allow = setremove(this.allow, user) ! E_TYPE';
if (this.deny == 0)
this.deny = {user};
elseif (typeof(this.deny) == LIST)
this.deny = setadd(this.deny, user);
endif
endif
.
#192:20
":_who()";
if (caller != this)
raise(E_PERM);
endif
player:tell(tostr("Publicly connected listeners on ", this:title(), ": ", $string_utils:name_list($set_utils:intersection(this.listeners, $login:connected()))));
return 0;
.
#192:21
":is_emoted_text(text)";
"=> LIST of text to be tacked after the emoting_msg if emoted text.";
"=> false, otherwise.";
{text} = args;
if ((!text) || (text[1] != ":"))
return 0;
endif
if (`text[2] ! E_RANGE' == ":")
text[1..2] = "";
else
text[1..1] = " ";
endif
return {text};
.
#192:22
":_me(text)";
if (caller != this)
raise(E_PERM);
endif
if (!this:welcome_user(player))
return "You aren't allowed in that channel.";
endif
{text} = args;
raw = tostr(this:emoting_msg(), " ", text);
this:broadcast(raw);
return 0;
.
#192:23
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#192:24
if (!this:is_writable_by(caller_perms(), caller))
raise(E_PERM);
endif
{aliases} = args;
return $channel_feature:channel_name_conflicts(aliases, this) ? E_NACC | pass(@args);
.
#192:25
if (!this:is_writable_by(caller_perms(), caller))
raise(E_PERM);
endif
return $channel_feature:channel_name_conflicts(args, this) ? E_NACC | pass(@args);
.
#192:26
":is_heard_by(user)";
"Return true if the given user is listening to this channel.  (No accounting for connection status.)";
{user} = args;
return user in this.listeners;
.
#192:27
":_desc(str)";
if (caller != this)
raise(E_PERM);
endif
if (!this:is_op(player))
return "Only operators can change a channel description.  You are not an operator.";
endif
{desc} = args;
if (!desc)
return "Use the @describe command to clear a description.";
endif
if (!(r = this:set_description(desc)))
return tostr("Channel description failed: ", r);
endif
this:broadcast(this:desc_msg());
return 0;
.
#192:28
":_destroy()";
if (caller != this)
raise(E_PERM);
endif
if (!this:is_op(player))
return "Only operators can destroy a channel.  You are not an operator.";
endif
player:tell(this:grammar_sub("Use `@recycle %#` if you're sure you want to recycle %n."));
return 0;
.
#193:0
":get_current_channel_for(user)";
{user} = args;
d = $list_utils:assoc(user, this.current_channel);
return d ? d[2] | #-1;
.
#193:1
":set_current_channel_for(user)";
{user, ch} = args;
i = $list_utils:iassoc(user, this.current_channel);
if (i)
this.current_channel[i][2] = ch;
else
this.current_channel = {@this.current_channel, {user, ch}};
endif
.
#193:2
":all_channels()";
return $object_utils:leaves($channel);
.
#193:3
":channel_match_failed(str[, LIST channel_pool])";
{string, ?leaves = this:all_channels()} = args;
if (!string)
return "No channel name was given.  Use `@x/list` to view available channels.";
endif
if (toobj(string) in leaves)
return toobj(string);
endif
matched = $match_utils:match_list(string, leaves);
if (!matched)
return tostr("No channels answer to the name \"", string, "\".");
elseif (length(matched) == 1)
return matched[1];
else
return tostr($string_utils:nn(matched), " all answer to the name \"", string, "\".  Please be more specific.");
endif
.
#193:4
"@x <text>            -- Speak on the current channel.";
"";
"@x/join <channel>";
"@x/switch <channel>  -- Set a channel as your active (speaking) channel.";
"";
"@x/part              -- Stop listening to a [the current] channel.";
"";
"@x/invite <user>     -- Allow user to join the current channel.";
"";
"@x/list [channel]    -- List all [or the given] channel listeners.";
"@x/who               -- List those listening on the current channel.";
"@x/kick   <user>     -- Kick the user from the current channel.";
"@x/ban    <user>     -- Ban the user from the current channel.";
"";
"@x/op   <user>       -- Add user as an operator of current channel.";
"@x/deop <user>       -- Remove user as an operator of current channel.";
"";
"@x/create <name>     -- Create a new channel.";
"@x/desc <text>       -- Describe a channel with the given text.";
"@x/destroy           -- Instructions to destroy a channel.";
"";
"@x/help              -- Get help on this feature.";
"";
"@x-foo will regard a channel matching 'foo' as your current channel.";
if (player != caller_perms())
raise(E_PERM);
endif
if ($justice:is_criminal(player))
player:tell("Being a criminal, you're not allowed to use channels while serving your sentence.");
return E_PERM;
endif
set_task_perms(caller_perms());
cmd = verb[3..$];
if (cmd && (cmd[1] == "-"))
if (i = index(cmd, "/"))
chspec = cmd[2..i - 1];
cmd = cmd[i..$];
else
chspec = cmd[2..$];
cmd = "";
endif
ch = this:channel_match_failed(chspec, this:all_channels_heard_by(player));
if (ch)
player:tell(ch);
return;
endif
else
ch = this:get_current_channel_for(player);
endif
if (cmd == "/part")
if (argstr)
r = this:channel_match_failed(argstr);
elseif (valid(ch))
r = ch;
elseif (valid(nextch = this:first_channel_heard_by(player)))
r = nextch;
else
r = #-1;
endif
if (r)
player:tell(r);
elseif (!valid(r))
player:tell("You aren't listening to any channels.");
elseif (result = r:do_command(cmd, argstr))
player:tell("Command failed: ", result);
elseif (r == ch)
"recalculate next channel now that current is gone";
nextch = this:first_channel_heard_by(player);
this:set_current_channel_for(player, nextch);
endif
elseif ((cmd == "/join") || (cmd && (index("/switch", cmd) == 1)))
cmd = "/join";
r = this:channel_match_failed(argstr);
if (r)
player:tell(r);
elseif (result = r:do_command(cmd, argstr))
player:tell("Command failed: ", result);
else
this:set_current_channel_for(player, r);
endif
elseif (cmd == "/create")
this:do_create_channel(argstr);
elseif (cmd == "/list")
this:do_list_channels();
elseif (cmd == "/help")
player:tell_lines($code_utils:verb_documentation());
elseif (valid(ch))
if (result = ch:do_command(cmd, argstr))
player:tell("Command failed: ", result);
endif
else
player:tell("You aren't connected to any channels.  Type `@x/list` for a list of those you may `@x/join`.");
endif
.
#193:5
":do_list_channels()";
b = $string_utils:left("-----[available channels]", player:linelen(), "-");
player:tell(b);
now = this:get_current_channel_for(player);
for ch in (this:all_channels())
if (!ch:welcome_user(player))
continue;
endif
visible_listeners = $set_utils:intersection($login:connected(), ch:listeners());
if (ch == now)
icon = "+";
elseif (ch:is_heard_by(player))
icon = ")";
else
icon = " ";
endif
count = $string_utils:right(tostr(length(visible_listeners)), -3);
title = $string_utils:left(ch:title(), -20);
blurb = $string_utils:abbreviated_value(tostr(@ch:description()), 50);
player:tell(" ", icon, " ", count, " ", title, " ", blurb);
endfor
player:tell(b);
.
#193:6
"Remove the player from all channels.";
{user, @args} = args;
if ((caller != this) && (caller != user))
return raise(E_PERM);
elseif (!$object_utils:isa(user, $guest))
return E_NONE;
endif
this:set_current_channel_for(user, #-1);
for ch in (this:all_channels())
`ch:remove_listener(user) ! E_NONE';
endfor
.
#193:7
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
this.current_channel = {};
.
#193:8
":channel_name_conflicts(LIST names[, OBJ channel])";
"Return a list containing the current channel with one of the given names, or 0 if the name is free.  If channel is given, don't count it when determining conflicts.";
{names, ?omit = #-1} = args;
if (typeof(names) != LIST)
names = {names};
endif
for ch in (this:all_channels())
if (ch == omit)
continue;
endif
for name in (names)
if (name in {ch.name, @ch.aliases})
return {ch};
endif
endfor
endfor
.
#193:9
":first_channel_heard_by(user)";
"Return the first channel to which the given user is listening.";
{user} = args;
for ch in (this:all_channels())
if (ch:is_heard_by(user))
return ch;
endif
endfor
return #-1;
.
#193:10
":all_channels_heard_by(user)";
"Return a list of all channels heard by the given user.";
{user} = args;
channels = {};
for ch in (this:all_channels())
if (ch:is_heard_by(user))
channels = {@channels, ch};
endif
endfor
return channels;
.
#193:11
":do_create_channel(STR name)";
"Attempt to create a channel with the given name or names (separated by commas).";
{names} = args;
set_task_perms(caller_perms());
ch = $recycler:_create($channel);
if (typeof(ch) != OBJ)
player:tell("Channel creation failed: ", ch);
return;
endif
{name, aliases} = $building_utils:parse_names(names);
if ((!(r = ch:set_name(name))) || (!(r = ch:set_aliases(aliases))))
$recycler:_recycle(ch);
player:tell("Channel christening failed: ", r);
return r;
endif
player:tell(ch:grammar_sub("%N (%#) created.  Use `@describe %# as \"whatever\"` to describe it, and `@recycle %#` to destroy it."));
return ch;
.
#194:0
":min_skill(doc, patient)";
"Minimum skill required to successfully operate the medical aide.";
return this.min_skill;
.
#194:1
":attempt_heal(doctor, patient)";
doc = args[1];
pat = args[2];
med = doc:total("medic");
if (med > this:min_skill(@args))
doc:tell("You grab some scalpels and bandages, determined to do your best with the instruments at hand.");
doc:room_announce_all_but({doc}, ((((((doc:dnamec() + " gets out ") + doc:pp()) + " ") + this:name()) + " to assist in ") + doc:pp()) + " healing.");
half = this.bonus_range / 2;
return half + random(half);
elseif (random(5) == 1)
doc:tell("Eyoo.  You really don't know what you're doing with that kit.  You might hurt someone.");
pat:tell(doc:dnamec(), " fumbles some sharp instruments clumsily.  To your horror, ", doc:ps(), " appears to be preparing to use them on you!");
return -this.bonus_range;
else
doc:tell("You don't have enough medical skill to know what the stuff in ", this:dname(), " is used for.  Better get some more field experience before messing with sharp instruments.");
endif
.
#194:2
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#195:0
pass(@args);
this:tell_display();
.
#195:1
"Show our coordinates, if possible.";
where = player:room();
if (c = where.zone_coordinates)
player:tell(tostr("The LCD display reads: x", c[1], " y", c[2], " z", c[3]));
else
player:tell("The LCD display reads: COORDINATES UNKNOWN");
endif
if (h = this.holo_display)
player:tell("");
player:tell(("Hovering above " + this:dname()) + " is a holographic display:");
player:tell_lines(h);
endif
.
#195:2
"read <unit>";
"Show your coordinates according to the global positioning satellites.";
this:tell_display();
.
#195:3
"map with gps";
"Show a holographic display of your area.";
if ($code_utils:task_valid(this.drawing_map))
this:output("COMPILE IN PROGRESS");
return E_QUOTA;
endif
map = this:draw_map(player);
if (!map)
this:output("HOLOMAP DATA UNAVAILABLE");
return E_INVARG;
endif
this.drawing_map = task_id();
this:output("COMPILING HOLOMAP");
suspend(random(3));
this.holo_display = map;
who = this.location;
who:tell(("Your " + this:name()) + " echoes vibrations through the air, attracting particles which coalesce into a three-dimensional small-scale map of your locale:");
who:room_announce_all_but({who}, tostr("The air around ", who:dname(), " vibrates, tiny particles coalescing into an abstract form above ", who:pp() || "its", " ", this:name(), "."));
this:output(map);
this:output(("\"cancel map on " + this.name) + "\" TO DISSOLVE HOLOMAP");
.
#195:4
":draw_map(who[, radius])";
"Pass to whose zone, drawing a map with the given radius if available.";
who = args[1];
rad = min(args[2] || 3, 8);
loc = who:room();
xyz = loc.zone_coordinates;
raw = $code_utils:call_verb(who:zone(), "draw_map", {xyz, rad});
if (raw)
"ok";
elseif (exits = loc:exits())
raw = this:_draw_map(exits);
else
return raw;
endif
map = {};
h_border = (" " + $string_utils:center(("(" + $string_utils:from_list(xyz, "/")) + ")", length(raw[1]), "-")) + " ";
map = {h_border};
for line in (raw)
map = {@map, ("|" + line) + "|"};
endfor
return {@map, h_border};
.
#195:5
"cancel <map|holo|display> on gps";
"Cancel the holographic display on the GPS Unit.";
if (((dobjstr == "map") || (dobjstr == "display")) || (dobjstr[1..4] == "holo"))
player:tell("The holographic display dissipates like a sudden rainfall, or sand blowing away across glass.");
this.holo_display = 0;
else
this:output(("CANCEL MODE " + dobjstr) + " UNKNOWN");
endif
.
#195:6
":output(data)";
"By default, just :tell data to location.";
where = this.location;
output = args[1];
prefix = ("(" + this:name()) + "): ";
for line in ((typeof(output) == LIST) ? output | {output})
where:tell(prefix + line);
endfor
.
#195:7
":_draw_map(exits)";
"Attempt to draw a map of the local area, which is assumed NOT to be a terrain room.";
map = this.blank_local_map;
exits = args[1];
if (!exits)
return map;
endif
info = this.local_map_info;
marked = this.marked_local_map;
for x in (exits)
names = {x.name, @x.aliases};
for set in (info)
if (set[1] in names)
y1 = set[3][1];
y2 = set[3][2];
for x in [set[2][1]..set[2][2]]
map[x][y1..y2] = marked[x][y1..y2];
endfor
endif
endfor
endfor
return map;
.
#195:8
if (!caller_perms().wizard)
return raise(E_PERM);
endif
this.drawing_map = this.holo_display = 0;
pass();
.
#196:0
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#196:1
"boost stat_name";
"Boost your 'stat_name' statistic by one point.";
"";
"";
"";
"";
player:tell("You feel an urge to type `@charedit' instead of using this horrible method of spending your potential.");
return;
"";
"";
"";
"";
.
#197:0
"install <device> into PC";
"";
"Install a peripheral device (a disk is considered a device) into the PC.";
if (!player:is_local_to(this))
player:tell("You aren't close enough to do that.");
return E_RANGE;
endif
dobj = player:my_match_object(dobjstr);
if ($match_utils:object_match_failed(dobj, dobjstr, player:env()))
return;
endif
u_base = tostr("You attempt to ", verb, " ", dobj:dname(), " ", prepstr, " ", this:dname());
o_base = tostr(player:dnamec(), " attempts to ", verb, " ", dobj:iname(), " ", prepstr, " ", player:pp(), " ", this:name());
install_args = dobj:install_info(this);
result = install_args ? this:dev_install(player, @install_args) | E_NACC;
if (result)
player:tell(u_base + ".  It fits perfectly.");
player:room_announce(o_base + ".  It fits perfectly.");
elseif (result == E_NACC)
player:tell(u_base + ", but it doesn't seem to fit.");
player:room_announce(o_base + ", but it doesn't seem to fit.");
elseif (result == E_PROPNF)
player:tell(tostr(u_base, ", but ", this:dname(), " rejects it.  The device seems to reference an invalid port."));
player:room_announce(tostr(o_base, ", but ", this:dname(), " rejects the device."));
elseif (typeof(result) == OBJ)
player:tell(tostr(u_base, ", but ", this:dname(), " rejects it.  That port is occupied by ", result:iname(), "; you'll have to uninstall it first."));
player:room_announce(o_base + ", but the port is occupied.");
else
player:tell(tostr(u_base, ", but ", this:dname(), " rejects it."));
player:room_announce(tostr(o_base, ", but ", this:dname(), " rejects the device."));
endif
.
#197:1
":acceptable(obj)";
"Reject everything except devices being installed.";
return this.install_task == task_id();
.
#197:2
":dev_install(user, device, port)";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
this.install_task = task_id();
port = "port_" + args[3];
curr = this.(port);
if (curr == E_PROPNF)
return E_PROPNF;
endif
if ($recycler:valid(curr))
return curr;
endif
device = args[2];
device:moveto(this);
if (device.location != this)
return E_NACC;
endif
this.(port) = device;
return 1;
.
#197:3
"uninstall <device> from PC";
"";
"Remove a peripheral device from the PC.";
if (!player:is_local_to(this))
player:tell("You aren't close enough to do that.");
return E_RANGE;
endif
dobj = this:match_contents(dobjstr);
if ($match_utils:object_match_failed(dobj, dobjstr, this:matching_contents()))
return;
endif
u_base = tostr("You attempt to ", verb, " ", dobj:dname(), " ", prepstr, " ", this:dname());
o_base = tostr(player:dnamec(), " attempts to ", verb, " something ", prepstr, " ", player:pp(), " ", this:name());
uninstall_args = dobj:uninstall_info(this);
result = uninstall_args ? this:dev_uninstall(player, @uninstall_args) | E_NACC;
if (result)
player:tell(u_base + ".  It slides out smoothly.");
player:room_announce(tostr(o_base, ".  ", dobj:inamec(), " slides out smoothly."));
elseif (result == E_NONE)
player:tell(ubase + ".  It slides out easily, and you notice that it wasn't even in use.");
player:room_announce(tostr(o_base, ".  ", dobj:inamec(), " slides out smoothly."));
elseif (result == E_NACC)
player:tell(u_base + ", but it seems to be stuck.");
player:room_announce(o_base + ", but whatever it is seems to be stuck.");
elseif (result == E_PROPNF)
player:tell(u_base + ", but can't find it inside.");
player:room_announce(o_base + ", but can't seem to find it.");
elseif (result == E_INVIND)
player:tell(tostr(u_base, ", but can't find it inside.  However, ", this:dname(), " seemed to think it was connected.  You correct the problem."));
player:room_announce(tostr(o_base, ", but can't seem to find it.  ", player:psc(), " jiggles the hardware in desperation."));
elseif (typeof(result) == OBJ)
player:tell(tostr(u_base, ", but find ", result:iname(), " where it ought to be."));
player:room_announce(o_base + ", but can't seem to find it.");
else
player:tell(u_base + ", but it seems to be stuck.");
player:room_announce(o_base + ", but whatever it is seems to be stuck.");
endif
.
#197:4
":exitfunc(obj)";
"Move the object back in if this is not the uninstall task.";
OBJ = args[1];
if (task_id() != this.uninstall_task)
OBJ:moveto(this);
endif
if (OBJ.location != this)
return pass(@args);
endif
.
#197:5
":dev_uninstall(user, device, port)";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
this.uninstall_task = task_id();
user = args[1];
device = args[2];
here = device.location == this;
port = "port_" + args[3];
curr = this.(port);
if (curr != device)
if (here)
device:moveto(user);
return (device.location == user) ? E_NONE | E_NACC;
else
return $recycler:valid(curr) ? curr | E_PROPNF;
endif
endif
this.(port) = $nothing;
if (here)
device:moveto(user);
return (device.location == user) ? 1 | E_NACC;
else
return E_INVIND;
endif
.
#197:6
":set_dt_current(STR fn)";
"Set the active desktop file.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
fn = args[1];
if (fn in this.dt_files)
return this.dt_current = fn;
else
return E_PROPNF;
endif
.
#197:7
":read_port(port, filename)";
"Read data referenced as filename from the given port.";
"Will always return either an error or a list.";
if (!this:is_readable_by(caller_perms(), caller))
return E_PERM;
endif
port = "port_" + args[1];
if (this.(port) == E_PROPNF)
return E_PROPNF;
endif
result = this:(port)("R", args[2]);
return result && $string_utils:lines_to_list(result);
.
#197:8
":write_port(port, filename[, @data])";
"Write data to filename through the given port.";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
port = "port_" + args[1];
if (this.(port) == E_PROPNF)
return E_PROPNF;
endif
return this:(port)("W", @listdelete(args, 1));
.
#197:9
":port_IN(mode, filename[, @data])";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
if (args[1] != "W")
return E_INVARG;
endif
for line in (args[3..length(args)])
this:parse_input(line);
endfor
.
#197:10
"type \"anything\" onto PC";
"";
"Enter a line of text into the computer, sending it to port_IN.";
if (!player:is_local_to(this))
player:tell("You aren't close enough to do that.");
return E_RANGE;
endif
if (0)
"...disable this, since command echoes onto screen anyway...";
player:tell(tostr("You enter \"", dobjstr, "\" onto ", this:dname(), "."));
endif
this:write_port("IN", "Keyboard_Input", dobjstr);
player:room_announce(tostr(player:dnamec(), " taps some keys on ", player:pp(), " ", this:name(), "."));
.
#197:11
":parse_input(line)";
"Parse the given line of input, interpreting it with respect to what programs are active on the computer.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
"Validate the desktop buffer.";
this:save_buffer();
line = args[1];
this:write_port("OUT", "Command_Echo", this:prompt_msg() + line);
if (!line)
return;
endif
cmd_info = $string_utils:first_word(line);
cmd = cmd_info[1];
cmd_args = $string_utils:words(cmd_info[2]);
argstr = cmd_info[2];
if (info = this:parse_editor_command(cmd, line))
if (typeof(info[1]) != STR)
this:write_port("OUT", "Error_Msg", @listdelete(info, 1));
else
set_task_perms($no_one);
this:(info[1])(@listdelete(info, 1));
endif
elseif (where = this:find_command(cmd))
set_task_perms($no_one);
where[1]:("_" + cmd)(@cmd_args);
else
this:write_port("OUT", "Error_Msg", cmd + "? Not found.");
endif
"--WiZARDLY--";
.
#197:12
":screen_prefix_msg()";
return $string_utils:pronoun_sub(this.screen_prefix_msg, @args);
.
#197:13
":port_OUT(mode, filename[, @data])";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
if (args[1] != "W")
return E_INVARG;
endif
device = this.(verb);
if (valid(device) && (device != this))
return device:write(@listdelete(args, 1));
endif
data = args[3..length(args)];
prefix = this:screen_prefix_msg();
"added this dubious feature, which echoes to the player if the location isn't a character.  hate relying on 'player', but it's easier than rewriting the whole protocol, and arguably better than echoing output to the entire room (as commented out below) <Quinn;19980419>";
user = $rpg:is_char(this.location) ? this.location | player;
if ($rpg:is_char(user))
for line in (data)
user:tell(prefix + line);
endfor
elseif (0)
where = this:room();
for line in (data)
where:announce_all(prefix + line);
endfor
endif
"Pad empty lines on each side of output, for easy screen reading.";
screen = {@this.screen, @data};
top = length(screen) - this.screen_height;
if (top > 1)
screen = screen[top..length(screen)];
endif
this.screen = screen;
return 1;
.
#197:14
":port_DSK(mode, filename[, @data])";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
device = this.(verb);
if (!$recycler:valid(device))
return E_INVIND;
endif
mode = args[1];
if (mode == "R")
return device:read(@listdelete(args, 1));
elseif (mode == "W")
return device:write(@listdelete(args, 1));
else
return E_INVARG;
endif
.
#197:15
":port_PRN(mode, filename[, @data])";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
device = this.(verb);
if (!$recycler:valid(device))
return E_INVIND;
endif
mode = args[1];
if (mode == "R")
"...allow a printer to double as a scanner...";
return device:read(@listdelete(args, 1));
elseif (mode == "W")
return device:write(@listdelete(args, 1));
else
return E_INVARG;
endif
.
#197:16
":port_COM(mode, filename[, @data])";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
device = this.(verb);
if (!$recycler:valid(device))
return E_INVIND;
endif
mode = args[1];
if (mode == "R")
return device:read(@listdelete(args, 1));
elseif (mode == "W")
return device:write(@listdelete(args, 1));
else
return E_INVARG;
endif
.
#197:17
":port_RAM(mode, filename[, @data])";
if (!this:is_writable_by(caller_perms(), caller))
return E_PERM;
endif
mode = args[1];
fn = args[2];
if (mode == "R")
i = fn in this.dt_files;
if (i)
return this.dt_data[i];
else
return E_PROPNF;
endif
elseif (mode == "W")
i = fn in this.dt_files;
if (!i)
this.dt_files = {@this.dt_files, fn};
i = length(this.dt_data = {@this.dt_data, {}});
endif
return this.dt_data[i] = args[3..length(args)];
else
return E_INVARG;
endif
.
#197:18
"Show what's on the computer screen right now.";
"Later to show a more graphical representation, with borders, windows, etc.";
"Also, perhaps make perception checks, and notify room if a player other than the one using the laptop is looking at it.";
if (!this.screen)
player:tell("The screen is blank.");
return E_NONE;
endif
fn = this.dt_current;
if (fn)
fn = tostr("#", fn in this.dt_files, "/", fn);
endif
screen = this:draw_screen({this.screen, fn, "", this.screen_width, this.screen_height / 2});
player:tell_lines(screen);
.
#197:19
":draw_screen(screen_info+)";
"'screen_info' is in the following format:";
"  {text, file-name, note, x-len, y-len, screen-name, horz-bdr, vert-bdr}";
info = args[1];
dlen = length(data = info[1] || {});
fname = info[2] || "#0/Null";
note = info[3] || "";
linelen = info[4] || player:linelen();
pagelen = ((info[5] || player:pagelen()) || 24) - 2;
sname = info[6] || (this.name + " screen");
hbor = info[7] || "-";
vbor = info[8] || "|";
linelen = max(linelen, length((sname + note) + fname) + 2);
inlen = linelen - 4;
space = $string_utils:space(linelen);
header = footer = $string_utils:left("---" + sname, linelen, hbor);
header[(length(header) - length(fname)) - 3..length(header) - 4] = fname;
if (note)
insert = (length(header) / 2) - ((ln = length(note)) / 2);
header[insert..(insert + ln) - 1] = note;
endif
screen = {};
for i in [max(dlen - pagelen, 1)..dlen]
line = data[i];
for subline in (player:linesplit(line, inlen))
screen = {@screen, tostr((((vbor + " ") + (subline + space)[1..inlen]) + " ") + vbor)};
endfor
endfor
return {header, @screen, footer};
.
#197:20
passed = pass(@args);
this:tell_screen();
return passed;
.
#197:21
":prompt_msg()";
"String to preceed commands echoed onto the computer screen.  Is run throught both $string_utils:pronoun_sub() and $time_utils:time_sub().";
msg = this.(verb);
msg = $time_utils:time_sub(msg, $rpg:ic_time());
msg = $string_utils:pronoun_sub(msg);
return msg;
.
#197:22
"desktop";
"View desktop files.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
this:write_port("OUT", "Desktop_Directory", "Desktop: " + this:name());
file_names = this.dt_files;
if (!file_names)
this:write_port("OUT", "Desktop_Directory", "  (No files.)");
return;
endif
used = 0;
curr = this.dt_current;
for i in [1..length(file_names)]
fn = file_names[i];
data = this:read_port("RAM", fn);
if (typeof(data) == ERR)
line = $string_utils:format(" %0 %-2: %24 **corrupted**", (fn == curr) ? "*" | " ", i, fn);
else
size = $quota_utils:value_bytes(data);
line = $string_utils:format(" %0 %-2: %24 %-6u", (fn == curr) ? "*" | " ", i, fn, size);
used = used + size;
endif
this:write_port("OUT", "Desktop_Directory", line);
endfor
this:write_port("OUT", "Desktop_Directory", tostr("Total: ", i, " file(s), ", used, " units."));
.
#197:23
":match_desktop_file(filespec)";
"-> The name of the desktop file matching the given spec.";
fs = args[1];
dt_files = this.dt_files;
if (!dt_files)
return E_NONE;
endif
if (i = fs in dt_files)
return fs;
endif
i = $code_utils:tonum(tostr(fs));
if (i == E_TYPE)
return 0;
elseif ((i < 1) || (i > length(dt_files)))
return E_RANGE;
else
return dt_files[i];
endif
.
#197:24
":desktop_file_match_failed(dtfm_result, string, cmdname)";
result = args[1];
string = args[2];
cmd = args[3];
if (result)
return 0;
endif
if (result == E_NONE)
msg = tostr(cmd, "? The desktop is empty.");
elseif (result == E_RANGE)
msg = tostr(cmd, "? Desktop index (", string, ") out of range.");
else
msg = tostr(cmd, "? Desktop filespec \"", string, "\" unmatched.");
endif
this:write_port("OUT", "Failed_Desktop_Match", msg);
return 1;
.
#197:25
"fg <desktop-file>";
"Put a desktop file in the foreground, opening it for changes.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
if (!args)
this:write_port("OUT", "Error_Msg", "fg! No desktop file specified.");
return E_ARGS;
endif
fn = this:match_desktop_file(args[1]);
if (this:desktop_file_match_failed(fn, args[1], "fg"))
return;
endif
this:dump_buffer();
this.dt_current = fn;
data = this:read_port("RAM", fn);
if (typeof(data) == LIST)
this.dt_buffer = data;
this:write_port("OUT", "Status_Msg", tostr("fg: Loaded \"", fn, "\" into foreground."));
else
this:write_port("OUT", "Error_Msg", tostr("fg! Error reading data: ", data));
endif
.
#197:26
"load <disk-filename>";
"Load a file from disk into the desktop, and put it in the foreground.";
"";
"load <disk-filename> as <desktop-filename>";
"Load a file from disk, storing on in the desktop as a different name.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
if (!args)
this:write_port("OUT", "Error_Msg", "load! No file specified.");
return E_ARGS;
endif
fs = args[1];
fn = ((i = "as" in args) && args[i + 1]) || fs;
if (this:match_desktop_file(fn))
this:write_port("OUT", "Error_Msg", tostr("load! Desktop file \"", fn, "\" already exists."));
return E_NACC;
endif
data = this:read_port("DSK", fs);
if (data == E_PROPNF)
this:write_port("OUT", "Error_Msg", tostr("load! File \"", fs, "\" does not exist."));
return data;
elseif (data == E_PERM)
this:write_port("OUT", "Error_Msg", tostr("load! File \"", fs, "\" is unreadable."));
return data;
elseif (typeof(data) != LIST)
this:write_port("OUT", "Error_Msg", "load! Error reading file.");
return data;
endif
this.dt_files = listappend(this.dt_files, fn);
this.dt_data = listappend(this.dt_data, data);
this:_fg(fn);
.
#197:27
"save <desktop-filename>";
"Save a file to disk from the desktop.";
"";
"save <desktop-filename> as <disk-filename>";
"Save a file to disk, storing it as the given name.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
if (!args)
this:write_port("OUT", "Error_Msg", "save! No file specified.");
return E_ARGS;
endif
fn = this:match_desktop_file(args[1]);
if (this:desktop_file_match_failed(fn, args[1], "save"))
return;
endif
fs = ((i = "as" in args) && args[i + 1]) || fn;
data = this:read_port("RAM", fn);
if (typeof(data) != LIST)
this:write_port("OUT", "Error_Msg", "save! Desktop data is corrupt.");
return data;
endif
result = this:write_port("DSK", fs, 1, @data);
if (result)
this:write_port("OUT", "Status_Msg", tostr("save: File \"", fs, "\" saved."));
return result;
elseif (result == E_PROPNF)
this:write_port("OUT", "Error_Msg", tostr("save! File \"", fs, "\" cannot be accessed or created."));
return result;
elseif (result == E_PERM)
this:write_port("OUT", "Error_Msg", tostr("save! File \"", fs, "\" is unwritable."));
return result;
else
this:write_port("OUT", "Error_Msg", "save! Error writing file.");
return result;
endif
.
#197:28
"time/date";
"Show the time and date.";
this:write_port("OUT", "Time", $rpg:ic_ctime());
.
#197:29
":all_ports()";
"Return the full names (port_XXX) of all ports on the computer.";
ports = {};
for p in (properties($code_utils:verb_loc()))
if (p[1..5] == "port_")
ports = {@ports, p};
endif
endfor
return ports;
.
#197:30
":all_devices()";
"Return a list of all devices connected to the computer.";
devices = {};
for p in (this:all_ports())
if (valid(dev = this.(p)))
devices = {@devices, dev};
endif
endfor
return devices;
.
#197:31
"help";
"Show a list of all valid commands.";
"help cmdname";
"Show help on a particular command.";
if (args)
info = this:find_command(args[1]);
if (!info)
this:write_port("OUT", "Error_Msg", "help! Command not found.");
elseif (doc = $code_utils:verb_documentation(info[2], "_" + args[1]))
this:write_port("OUT", "Command_Help", @doc);
else
this:write_port("OUT", "Error_Msg", "help! No help available.");
endif
return;
endif
cmds = this:all_commands();
cmds = $string_utils:columnize(cmds, 4, 60);
this:write_port("OUT", "Command_List", @cmds);
.
#197:32
"view";
"Show data in foreground buffer.";
"list";
"Same as view, but with line numbers.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
verb[1..1] = "";
curr = this.dt_current;
if (!curr)
this:write_port("OUT", "Error_Msg", verb + "! No active desktop file.");
return E_NONE;
endif
LIST = verb == "list";
data = this:dt_buffer();
for i in [1..length(data)]
if (LIST)
data[i] = ($string_utils:right(i, 2) + ": ") + data[i];
else
data[i] = "  " + data[i];
endif
endfor
data = data || {"  (Buffer empty.)"};
this:write_port("OUT", "Report_Current_Buffer", curr + ":", @data);
.
#197:33
":dt_buffer()";
"Return the text of the active buffer, removing all internal tokens (such as the __INS__ insertion point marker.";
return setremove(this.dt_buffer, "__INS__");
.
#197:34
":dump_buffer()";
"Clear the buffer after saving it into the current desktop file.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
this:save_buffer();
this.dt_buffer = {};
.
#197:35
":insertion_point()";
"Return the insertion point in the active desktop buffer.";
"It is marked by the line __INS__ in the data, which is removed when the buffer is dumped to its file.";
"If __INS__ is not present in the current buffer, add it to the end.";
data = this.dt_buffer;
if (i = "__INS__" in data)
return i;
else
this.dt_buffer = listappend(data, "__INS__");
return length(data) + 1;
endif
.
#197:36
"clear <desktop-file>";
"Clear the given file from the desktop.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
if (!args)
this:write_port("OUT", "Error_Msg", "clear! No file specified.");
return E_ARGS;
endif
fn = this:match_desktop_file(args[1]);
if (this:desktop_file_match_failed(fn, args[1], "clear"))
return;
endif
if (this.dt_current == fn)
this:dump_buffer();
this.dt_current = "";
endif
dt_files = this.dt_files;
i = fn in dt_files;
this.dt_files = listdelete(dt_files, i);
this.dt_data = listdelete(this.dt_data, i);
this:write_port("OUT", "Status_Msg", ("clear: File \"" + fn) + "\" cleared from desktop.");
.
#197:37
"new";
"Create a new file on the desktop.";
"";
"new <new-desktop-filename>";
"Create a new file on the desktop, with the given name.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
if (!args)
i = 1;
fn = "Untitled";
while (this:match_desktop_file(fn))
fn = tostr("Untitled-", i + 1);
endwhile
else
fn = args[1];
if (this:match_desktop_file(fn))
this:write_port("OUT", "Error_Msg", tostr("new! Desktop file \"", fn, "\" already exists."));
return E_NACC;
endif
endif
this.dt_files = listappend(this.dt_files, fn);
this.dt_data = listappend(this.dt_data, {});
this:_fg(fn);
.
#197:38
":save_buffer()";
"Save the buffer into the current desktop file.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
endif
data = this:dt_buffer();
if (curr = this.dt_current)
this:write_port("RAM", curr, $string_utils:lines_to_list(data));
endif
.
#197:39
":os_commands()";
"Return a list of all commands available via the operating system.";
all_verbs = verbs(this);
curr = this;
while ((curr = parent(curr)) != parent($code_utils:verb_loc()))
all_verbs = {@all_verbs, @verbs(curr)};
endwhile
"(Eventually may search for commands on devices as well)";
cmds = {};
for vrb in (all_verbs)
if (vrb[1] == "_")
for name in ($string_utils:words(vrb))
name[1..1] = "";
cmds = {@cmds, name};
endfor
endif
endfor
return cmds;
.
#197:40
":find_command(cmd)";
"Return a list of the device defining the given command, and the location of the verb itself.";
"If not found, return some false value.";
cmd = tostr("_", args[1]);
for device in (listinsert(this:all_devices(), this))
if (info = $object_utils:has_callable_verb(device, cmd))
return {device, @info};
endif
endfor
.
#197:41
":input(@data)";
"Write data to the IN port of the computer.";
"Caller must be a valid device.  Others can use :write_port directly.";
if (caller in this:all_devices())
this:write_port("IN", "Device_Input", @args);
else
return E_PERM;
endif
.
#197:42
":output(@data)";
"Write data to the OUT port of the computer.";
"Caller must be a valid device.  Others can use :write_port directly.";
if (caller in this:all_devices())
this:write_port("OUT", "Device_Output", @args);
else
return E_PERM;
endif
.
#197:43
":parse_editor_command(cmd, cmdline)";
"An editor command is in one on the following format:";
"<line>[,<line>]<cmd_character>[/<text>[/<text>]";
cmd = args[1];
if (this.inserting)
line = args[2];
if (line == ".")
this.inserting = 0;
return {E_NONE, "ed: Data entered."};
else
return {"ed_add_line", line, this.inserting};
endif
endif
len = length(cmd);
if (!len)
return;
endif
i = index(cmd, "/");
if (i && (i != len))
arg = $string_utils:explode(cmd[i + 1..len], "/");
cmd = cmd[1..i - 1];
len = i - 1;
else
arg = {};
endif
verbs = {"ed_numbered", "ed_append", "ed_insert", "ed_delete", "ed_find", "ed_sub"};
dat = length(this:dt_buffer());
ins = min(this:insertion_point(), dat);
if (i = index("naidfs", cmd))
return {verbs[i], {ins, ins}, @arg};
elseif (!index("0123456789.$", cmd[1]))
return;
endif
i = index("naidfs", cmd[len]);
if (!i)
return;
endif
vrb = verbs[i];
if (len == 1)
return {vrb, {ins, ins}, @arg};
endif
rng = $string_utils:explode(cmd[1..len - 1], ",");
len = length(rng);
if (len > 2)
return {E_INVARG, "ed? Too many commas in range spec."};
endif
for i in [1..len]
if (rng[i] == "$")
rng[i] = dat;
elseif (rng[i] == ".")
rng[i] = ins;
elseif ((n = $code_utils:tonum(rng[i])) == E_TYPE)
return {E_TYPE, tostr("ed? Cannot parse range spec: ", rng[i])};
elseif (n < 1)
return {E_RANGE, tostr("ed? Range spec below floor: ", rng[i])};
elseif (n > dat)
return {E_RANGE, tostr("ed? Range spec above ceiling: ", rng[i])};
else
rng[i] = n;
endif
endfor
if (len < 2)
rng = {@rng, rng[1]};
endif
if (rng[1] > rng[2])
return {E_DIV, tostr("ed? Bottom range is greater than top range.")};
endif
return {vrb, rng, @arg};
.
#197:44
":ed_add_line(line, mode)";
"Silently add the given line of text to the active desktop buffer.";
"If mode is -1, insert.  Else append.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
elseif (!this.dt_current)
return E_INVIND;
endif
line = args[1];
mode = args[2];
ins_point = this:insertion_point();
dt_buffer = this:dt_buffer();
if (mode == -1)
dt_buffer[ins_point..ins_point] = {line, "__INS__"};
else
dt_buffer = listappend(dt_buffer, line, ins_point);
dt_buffer = listappend(dt_buffer, "__INS__", ins_point + 1);
endif
this.dt_buffer = dt_buffer;
.
#197:45
":set_insertion_point(line_number)";
"Set the insertion point in the active desktop buffer.";
"It is marked by the line __INS__ in the data, which is removed when the buffer is dumped to its file.";
data = setremove(this.dt_buffer, "__INS__");
i = args[1];
if (i > length(data))
data = listappend(data, "__INS__");
elseif (i < 1)
data = listinsert(data, "__INS__");
else
data[i] = "__INS__";
endif
this.dt_buffer = data;
.
#197:46
":ed_numbered({range})";
"List lines in the given range from the foreground buffer.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
elseif (!this.dt_current)
return E_INVIND;
endif
raw = this:dt_buffer();
buf = {};
for i in [args[1][1]..args[1][2]]
buf = {@buf, ($string_utils:right(i, 2) + ": ") + raw[i]};
endfor
this:write_port("OUT", "Editor_Output", @buf);
.
#197:47
":ed_append({range})";
"Enter editor append mode, in which everything typed is appended to the foreground buffer.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
elseif (!this.dt_current)
return E_INVIND;
endif
this:write_port("OUT", "Editor_Output", "Begin appending text; type '.' alone to finish.");
this.inserting = 1;
this:set_insertion_point(args[1][1] + 1);
.
#197:48
":ed_insert({range})";
"Enter editor insert mode, in which everything typed is appended to the foreground buffer.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
elseif (!this.dt_current)
return E_INVIND;
endif
this:write_port("OUT", "Editor_Output", "Begin inserting text; type '.' alone to finish.");
this.inserting = -1;
this:set_insertion_point(args[1][1] - 1);
.
#197:49
":ed_delete({range})";
"Delete all lines in the given range from the foreground buffer.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
elseif (!this.dt_current)
return E_INVIND;
endif
raw = this:dt_buffer();
beg = args[1][1];
end = args[1][2];
raw[args[1][1]..args[1][2]] = {};
this.dt_buffer = raw;
this:write_port("OUT", "Editor_Output", tostr("Lines ", beg, "-", end, " deleted."));
.
#197:50
":ed_find({range}, search_string)";
"Find search_string in the given range of lines in the foreground buffer.";
if (!this:is_controllable_by(caller_perms(), caller))
return E_PERM;
elseif (!this.dt_current)
return E_INVIND;
elseif (length(args) < 2)
this:write_port("OUT", "Editor_Output", "ed! No search string given.");
return E_ARGS;
endif
sub = args[2];
raw = this:dt_buffer();
fnd = 0;
for i in [args[1][1]..args[1][2]]
if (index(raw[i], sub))
fnd = 1;
this:ed_numbered({i, i});
endif
endfor
if (!fnd)
this:write_port("OUT", "Editor_Output", tostr("ed: Text \"", sub, "\" not found."));
endif
.
#197:51
":_editing_help()";
this:write_port("OUT", "Editor_Output", @this.editing_help);
.
#197:52
":all_commands()";
"Return all commands in the OS (this PC), and all connected devices.";
":all_commands_associated()";
"As above, but return value is a list of lists of commands for each device.";
assoc = verb != "all_commands";
cmds = this:os_commands();
if (assoc)
cmds = {{this, cmds}};
endif
for dev in (this:all_devices())
ac = dev:all_commands();
if (!ac)
"...skip...";
elseif (assoc)
cmds = {@cmds, {dev, ac}};
else
cmds = {@cmds, @ac};
endif
endfor
return cmds;
.
#197:53
"commands";
"Show all commands on this PC, listed by device location.";
cmds = this:all_commands_associated();
output = {};
for set in (cmds)
curr = {tostr("Serial ", set[1], " (\"", set[1]:name(), "\")")};
for c in (set[2])
curr = {@curr, "  " + c};
endfor
output = {@output, @curr};
endfor
this:write_port("OUT", "Command_Output", @output);
.
#197:54
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#198:0
":install_info(computer)";
"Return a list of information needed to install on the given machine.";
return {this, this.port};
.
#198:1
":uninstall_info(computer)";
"Return a list of information needed to uninstall from the given machine.";
return {this, this.port};
.
#198:2
":all_commands()";
"Return a list of all commands available on this device.";
all_verbs = verbs(this);
curr = this;
while ((curr = parent(curr)) != parent($code_utils:verb_loc()))
all_verbs = {@all_verbs, @verbs(curr)};
endwhile
"(Eventually may search for commands on devices as well)";
cmds = {};
for vrb in (all_verbs)
if (vrb[1] == "_")
cmds = {@cmds, @$string_utils:words(strsub(vrb, "_", ""))};
endif
endfor
return cmds;
.
#198:3
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#199:0
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#199:1
"Account for the strange grammatic nature of the transputty name.";
{?amt = this.amount} = args;
base = pass(amt);
if (amt == 1)
return strsub(base, "handfuls", "handful");
endif
return base;
.
#199:2
"Add putty to the aliases.";
{?amt = this.amount} = args;
return setadd(pass(@args), "putty");
.
#199:3
"Special-case for the singular nature of the transputty.";
string = args[1];
if (string == this.what)
"a singular one of the collective";
return this.amount;
endif
return pass(@args);
.
#199:4
":match_mantra(OBJ sculptor, STR mantra)";
"Find a notion which responds to the given mantra.";
{sculptor, mantra} = args;
for kid in ($object_utils:descendants($notion))
if (kid:verify_mantra_for(sculptor, mantra))
return kid;
endif
endfor
return $nothing;
.
#199:5
"resonate <mantra> with transputty";
dobj = this:match_mantra(player, dobjstr);
player:maybe_queue_action((verb + " ") + argstr, {this, "sculpt"}, {player, dobj, dobjstr}, valid(dobj) ? dobj:slowness() | $notion.slowness);
.
#199:6
":do_sculpt(OBJ sculptor, OBJ notion, STR mantra)";
if (player != caller)
return raise(E_PERM);
endif
{sculptor, dobj, dobjstr} = args;
if (player != sculptor)
return raise(E_PERM);
endif
this:announce_messages("sculpt_attempt");
if (!valid(dobj))
this:announce_messages("mantra_failed");
return;
endif
"this needs to be able to check for more than just a single static";
"quantity, in the case of different outcomes from sculpting a notion";
amt = this.amount;
req = dobj:amount_required();
if (amt < req)
this:announce_messages("not_enough");
return;
endif
"check willpower or whatever; :attempt_sculpture will print fail messages";
if (!dobj:attempt_sculpture())
this:set_amount(amt - req);
return;
endif
new = dobj:make_notion_reality(player);
if ((typeof(new) != OBJ) || (!valid(new)))
if (new == E_QUOTA)
player:receive_fatigue(req);
this:announce_messages("equota");
else
player:tell("Failed to create sculpture (", new, ") from notion.");
player:tell("Please contact a wizard.");
endif
return;
endif
this:place_sculpture(player, new);
dobj:announce_success(new);
"debit the collective; it'll disappear if <= 0";
this:set_amount(amt - req);
.
#199:7
":place_sculpture(OBJ sculptor, OBJ sculpture)";
"Place the sculpture where it should be after being created by the sculptor.  For smaller items, put it in their contents.  For larger, in their location.";
if (caller != this)
return raise(E_PERM);
endif
{sculptor, sculpture} = args;
if (sculpture.v_size <= sculptor.v_size)
sculpture:moveto(sculptor);
endif
if (sculpture.location != sculptor)
sculpture:moveto(sculptor:room());
endif
return sculpture.location;
.
#199:8
":mantra_failed_msg()";
return $string_utils:pronoun_sub(strsub(this.(verb), "$mantra", dobjstr));
.
#200:0
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#200:1
":slowness()";
"=> INT slowness rating to sculpt this notion; should be 100+ for anything";
"   but the most trivial items (eg. a pebble)";
":amount_required()";
"=> INT units of transputty required to sculpt this notion";
return this.(verb);
.
#200:2
":make_notion_reality(OBJ sculptor)";
"Create whatever this notion becomes when sculpted.";
if (!caller_perms().wizard)
return raise(E_PERM);
endif
sculptor = args[1];
mama = this.prototype;
if (!valid(mama))
return mama;
endif
new = $recycler:_create(mama, sculptor);
$building_utils:set_spawned_names(new);
return new;
.
#200:3
":attempt_sculpture()";
"Return 1 if the player should succeed in creating this notion.  Elsewise,";
"print appropriate failure messages to them (and possibly their location),";
"and return false.";
result = player:resolve("willpower", this.willcheck_mod);
if (result < 0)
this:announce_failure();
return 0;
endif
return 1;
.
#200:4
":announce_failure()";
"Announce failure to sculpt this notion, due to insufficent skill.";
this:announce_messages("failure");
.
#200:5
":announce_success(OBJ sculpture)";
"Announce the successful sculpture of this notion.";
{dobj} = args;
this:announce_messages("success");
.
#200:6
":mantra_seed()";
"Return the base integer for the mantra generator.";
if (!this:is_readable_by(caller_perms(), caller))
return raise(E_PERM);
elseif (!this.mantra_seed)
this.mantra_seed = random(toint(this) + toint(this.owner));
endif
return this.mantra_seed;
.
#200:7
":mantra_for(OBJ sculptor)";
"Return the mantra to be chanted by sculptor when sculpting this notion.";
if (!this:is_readable_by(caller_perms(), caller))
return raise(E_PERM);
endif
sculptor = args[1];
seed = this:mantra_seed();
base = crypt(tostr(seed * toint(sculptor)), tostr(seed));
base = base[3..8];
mantra = "";
ASCII = $string_utils.ascii;
letters = "acehilorsuwxyz";
for i in [1..5]
mantra = mantra + letters[((index(ASCII, base[i]) * i) % $) + 1];
endfor
return mantra;
.
#200:8
":verify_mantra_for(OBJ sculptor, STR mantra)";
"Return true iof the given mantra is valid for the given scultpor.";
{sculptor, mantra} = args;
return mantra == this:mantra_for(sculptor);
.
#200:9
":mantra_failed_msg()";
return $string_utils:pronoun_sub(strsub(this.(verb), "$chant", dobjstr));
.
#201:0
":clone(OBJ)";
if (!this:is_writable_by(caller_perms(), caller))
return raise(E_PERM);
endif
{source} = args;
"relies on player variable, which means anyone able to create one of these";
"can circumvent security.  this is fine, since programmers are explicitly";
"trusted to edit stats";
"player-based perms checks here, :load_from, and :save_to";
if (!source:is_readable_by(player))
return E_PERM;
endif
proto = $code_utils:verb_location();
new = $recycler:_create(proto);
new:load(source);
new:set_name(source:grammar_sub("Effigy of %n"));
new:set_description(source:grammar_sub("A clone of %n used as a scratch pad for the character editor."));
return new;
.
#201:1
":destroy()";
if (!this:is_writable_by(caller_perms(), caller))
return raise(E_PERM);
elseif (((this == $code_utils:verb_location()) || verbs(this)) || properties(this))
return raise(E_INVARG);
endif
$recycler:_recycle(this);
return 1;
.
#201:2
":load(OBJ)";
if (!this:is_writable_by(caller_perms(), caller))
return raise(E_PERM);
endif
{source} = args;
result = this:load_from(source);
if (result)
this.original = source;
endif
return result;
.
#201:3
":reload()";
if (!this:is_writable_by(caller_perms(), caller))
return raise(E_PERM);
endif
if (!$recycler:valid(this.original))
return E_INVIND;
endif
return this:load_from(this.original);
.
#201:4
":load_from(OBJ)";
if (!this:is_writable_by(caller_perms(), caller))
return raise(E_PERM);
endif
{source} = args;
"relies on player variable, which means anyone able to create one of these";
"can circumvent security.  this is fine, since programmers are explicitly";
"trusted to edit stats";
if (!source:is_readable_by(player))
return E_PERM;
endif
"primary attributes";
for pn in ($rpg.atts)
this:set_stat(pn, source:get_stat(pn));
endfor
"skills";
for pn in (source:all_skills())
this:set_stat(pn, source:get_stat(pn));
endfor
if (0)
"character sheet; disable editing for now";
this.sheet = this:import_sheet(source);
endif
"potential";
this:set_stat("pot", source:get_stat("pot"));
"done!";
return 1;
.
#201:5
":is_writable_by(perms, caller)";
"Return true if the given user may alter or destroy this object.";
{perms, object} = args;
return pass(@args) || (object == $character_editor);
.
#201:6
":save_to(OBJ)";
if (!this:is_writable_by(caller_perms(), caller))
return raise(E_PERM);
endif
{dest} = args;
"relies on player variable, which means anyone able to create one of these";
"can circumvent security.  this is fine, since programmers are explicitly";
"trusted to edit stats";
if (!dest:is_writable_by(player))
return E_PERM;
endif
"primary attributes";
for pn in ($rpg.atts)
dest:set_stat(pn, this:get_stat(pn));
endfor
"skills";
for pn in (this:all_skills())
dest:set_stat(pn, this:get_stat(pn));
endfor
if (0)
"character sheet; disable for now";
sheet = {};
for line in (this.sheet)
if ((!line) || (line[1] != "["))
"malformed";
continue;
endif
sheet = {@sheet, line};
endfor
dest.sheet = this:export_sheet();
endif
"potential";
dest:set_stat("pot", this:get_stat("pot"));
"book-keeping, assume player is correct";
this:record_edit(dest, player);
"done!";
return 1;
.
#201:7
":save()";
if (!this:is_writable_by(caller_perms(), caller))
return raise(E_PERM);
endif
if (!$recycler:valid(this.original))
return E_INVIND;
endif
return this:save_to(this.original);
.
#201:8
":import_sheet(OBJ)";
if (!this:is_writable_by(caller_perms(), caller))
return raise(E_PERM);
endif
{source} = args;
sheet = {};
for line in (source.sheet)
if ((!line) || (line[1] != "["))
"malformed";
continue;
endif
sheet = {@sheet, line};
endfor
return {@sheet, this.uncoded_end_marker};
.
#201:9
":is_existing_uncoded(STR)";
{spec} = args;
for data in (this.sheet)
if (data == this.uncoded_end_marker)
break;
endif
if (index(data, spec))
return data;
endif
endfor
return 0;
.
#201:10
":export_sheet(OBJ)";
sheet = this.sheet;
i = this.uncoded_end_marker in sheet;
if (i)
sheet[i..i] = {};
endif
return sheet;
.
#201:11
":record_edit(OBJ dest, OBJ player)";
"Write a record of the given player editing the given character.";
if (caller_perms() != $code_utils:verb_perms())
raise(E_PERM);
endif
{dest, editor} = args;
try
dest.last_edited_by = {editor, time()};
except (E_PROPNF, E_PERM)
"ignore a non-player or a rogue property";
endtry
.
#201:12
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#202:0
"Nothing to do.";
return 1;
.
#202:1
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#203:0
{score, events, csdata} = args;
h = m = {};
original_score = score;
"Calculate multiple-word bonuses.";
b = this.multiword_bonuses[min(length(events), $)];
if (b)
score = score + b;
m = {@m, player:grammar_sub(tostr("%S %<receives> a multiple word bonus of ", b, " points!"))};
h = {@h, {0, "BONUS: Multiple words!", b, score}};
endif
"Calculate welfare bonus.";
usedtiles = csdata[3][7];
usedupper = $string_utils:UPPERCASE(tostr(@usedtiles));
usedscore = this:do_value(usedupper);
usedcount = csdata[3][6];
if (usedscore <= usedcount)
b = usedcount;
score = score + b;
m = {@m, player:grammar_sub(tostr("%S %<receives> a charity bonus of ", b, " points for ", usedupper, "!"))};
h = {@h, {0, ("BONUS: Low-point tiles for " + usedupper) + "!", b, score}};
endif
return (score != original_score) && {m, h, score};
.
#203:1
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#204:0
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#205:0
"drink liquid";
"Go ahead.  Try some.";
if (!player:is_local_to(this))
player:tell("You aren't near ", this:dnamec(), ".");
return;
endif
player:maybe_queue_action(tostr("drink ", dobjstr), {this, "drink"}, {dobjstr}, 80 - player:quickness());
.
#205:1
":drank_by(user, amount)";
"Deal with amount crystals in the mouth of user.";
{user, amount} = args;
this:set_amount(this.amount - amount);
.
#205:2
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#205:3
":do_drink(dobjstr)";
if (!player:is_local_to(this))
player:tell("You aren't near ", this:dnamec(), ".");
return;
endif
{dobjstr} = args;
amt = this:amount_from_string(dobjstr);
if (typeof(amt) == ERR)
amt = 1;
endif
amt = min(amt, this.amount);
if (amt < 1)
player:tell("You drink nothing.");
return;
endif
if (amt == this.amount)
this:announce_messages("drinkall");
else
this:announce_messages("drink");
endif
this:drank_by(player, amt);
.
#206:0
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass();
.
#206:1
"Blood adds .ins insanity per unit, with with an .endchance of incrementing Endurance, and a .wilchance of decrementing Willpower..";
{user, amount} = args;
if (!amount)
user:tell("Nothing happens.");
return;
endif
r = random(100);
if (r < this.will_chance)
user:mod_stat("Willpower", -1);
user:tell(this.lose_will_msg);
elseif (r > (max(user:get_stat("Endurance"), 100) - this.end_chance))
user:mod_stat("Endurance", 1);
user:tell(this.gain_end_msg);
else
user:tell(this.gain_ins_msg);
user:receive_insanity(amount * this.ins);
endif
return pass(@args);
.
#207:0
"Say something out loud, directed at someone or something.";
"Usage:";
"  `target message";
"Example:";
"  Munchkin is talking to Kenneth (in the same room).  He types:";
"    `kenneth What is the frequency?";
"  The room sees:";
"     Munchkin [to Kenneth]: What is the frequency?";
"You can also specify multiple directees:";
"  `tom,dick,harry Mmm.";
"   Munchkin [to Tom, Dick, and Harry]: Mmm.";
name = verb[2..$];
loc = player.location;
who = {};
env = loc:matching_contents();
for string in ($string_utils:explode(name, ","))
user = player:my_match_object(string, env);
if (!$match_utils:object_match_failed(user, string, env))
who = {@who, user};
endif
endfor
if (!who)
return;
endif
names = $string_utils:iname_list(who);
speech = player:filter_speech(argstr);
argstr = tostr("[to ", names, "]: ", speech);
loc:emote();
"The following -really- should be done somewhere else.";
volume = (`speech[$] ! E_RANGE' == "!") ? 4 | 1;
loc:broadcast_event_speech(player, speech, volume + random(3), @who);
.
#207:1
"Perform some physical, non-verbal, action.";
"Example:";
"  Munchkin has annoyed some would-be tough guy.  He types:";
"   Munchkin [hides behind the reactor.]";
"  The room sees:";
"   Munchkin [hides behind the reactor.]";
"If the '(' character is used, enclose in parentheses rather than brackets.";
vlen = length(verb);
if (vlen == 1)
text = argstr;
else
text = verb[2..vlen] + (argstr ? " " + argstr | "");
endif
if (!text)
player:tell("You need to give some parenthetical text.");
return;
endif
char = (verb[1] == "[") ? "]" | ")";
if (text[len = length(text)] == char)
text[len..len] = "";
endif
text = $string_utils:trim(text);
argstr = tostr(verb[1], " ", text, " ", char);
player.location:emote();
.
#207:2
"Say something out loud, directed at someone or something, here or not.";
"Usage:";
"  -target message";
"Example:";
"  Stu is annoyed with Ghandi.  The RL Ghandi.  He types:";
"   -ghandi damn you, skinny indian.";
"  The room sees:";
"    Munchkin [to Ghandi]: damn you, skinny indian.";
name = verb[2..$];
loc = player.location;
who = player.location:match_object(name);
speech = $code_utils:argstr(verb, args, argstr);
if (valid(who))
name = who:name();
who = {who};
else
name = name;
who = {};
endif
argstr = (("[to " + name) + "]: ") + speech;
player:emote();
volume = (`speech[$] ! E_RANGE' == "!") ? 4 | 1;
loc:broadcast_event_speech(player, speech, volume + random(3), @who);
.
#207:3
"Point yourself out as being something.";
"Usage:";
"  <message";
"Example:";
"  Corky just did something really stupid.  He types:";
"   <mentally disabled";
"  And the room sees:";
"    Corky <- mentally disabled";
argstr = (("<- " + verb[2..length(verb)]) + " ") + argstr;
player:emote();
.
#207:4
"Echo a line with your name appended in parentheses.";
"Usage:";
"  !message";
"Example:";
"  Ivan wants a mysterious grenade to roll into the room.  He types:";
"   !A small pineapple-shaped metal object clanks along the floor.";
"  And the room sees:";
"    A small pineapple-shaped metal object clanks along the floor.  --Ivan";
"Players who are `in-character' do not see the attribution tag.";
msg = (length(verb) > 1) ? (verb[2..length(verb)] + " ") + argstr | argstr;
tag = "  --" + player.name;
for who in (player.location:audience_for_announce())
who:tell(msg + (who.ic ? "" | tag));
endfor
.
#207:5
"  Quinn types: think what a hot smoochie";
"";
"Everyone sees: Quinn . o O ( what a hot smoochie )";
argstr = (". o O ( " + argstr) + " )";
player:emote();
.
#207:6
"sing lyric";
"Sing a line of song to the room.  It appears as follows:";
"       sing Joy to the World!";
"       Quinn o/~ Joy to the World! o/~";
note = "o/~";
argstr = tostr("sings, \"", note, " ", player:filter_speech(argstr), " ", note, "\"");
player.location:emote();
.
#208:0
"@move obj to dest";
if (player != caller_perms())
return E_PERM;
elseif (!this:allow_privileged_command(player, "@move"))
return E_PERM;
endif
set_task_perms(player);
su = $string_utils;
dobj = player:my_match_object(dobjstr, e = player:env());
if ($match_utils:object_match_failed(dobj, dobjstr, e))
return;
endif
if (iobjstr == "$nothing")
iobj = $nothing;
else
iobj = player:my_match_object(iobjstr, e);
endif
if ((iobj != $nothing) && $match_utils:object_match_failed(iobj, iobjstr, e))
return;
endif
if ($wiz_utils:is_builder(player))
"everything's okay";
elseif (!dobj:is_controllable_by(player))
player:notify("You may only @move your own things.");
return;
elseif (!iobj:is_controllable_by(player))
player:notify("You may only @move things to locations you own.  Try @moving it to yourself first.");
return;
elseif (is_player(dobj))
player:notify("You aren't allowed to @move users.");
return;
endif
old_loc = dobj.location;
if (old_loc == iobj)
player:notify(su:pronoun_sub("%[ddnamec] %<d:is> already located in %[idname]."));
return;
endif
dobj:moveto(iobj);
if (dobj.location == iobj)
player:notify(su:pronoun_sub("Moved %(dname) to %[idname].", (player == dobj) ? $you | dobj));
if (is_player(dobj))
if (valid(old_loc))
old_loc:announce_all(su:pronoun_sub("%[ddnamec] %<d:disappears> suddenly for parts unknown."));
if (dobj != player)
dobj:tell("You have been moved by ", player:iname(), ".");
endif
endif
if (valid(dobj.location))
dobj.location:announce(su:pronoun_sub("%[dinamec] %<d:materializes> out of thin air."));
endif
endif
elseif (dobj.location == old_loc)
if ($object_utils:contains(dobj, iobj))
player:notify(su:pronoun_sub("OOH it's all twisty; %[idname] %<i:is> inside of %[ddname]!"));
else
player:notify(su:pronoun_sub("Either %[ddname] %<d:doesn't> want to go, or %[idname] %<i:doesn't> want to accept %[dpo]."));
endif
elseif (dobj == player)
player:notify("You have been deflected from your original destination.");
else
player:notify(su:pronoun_sub("%[ddnamec] %<d:has> been deflected from %[dpp] original destination."));
endif
.
#208:1
"@go <place>";
"@join <user>";
"  Move yourself to PLACE, or wherever USER is located.  The room must be @unlocked, open for teleporting, and approved for teleport if you are not a programmer.";
"@summon <object>";
"  Move OBJECT to yourself.  Non-programmers must own OBJECT.";
"Non-programmers cannot use any of these commands if they are unconscious or involved in combat.";
if (player != caller_perms())
return E_PERM;
elseif (!this:allow_privileged_command(player, verb))
return E_PERM;
endif
set_task_perms(player);
is_gm = $rpg:trusted(player);
if (player:unconscious())
player:notify("You aren't going anywhere while unconscious.");
return E_NACC;
elseif ((!is_gm) && player:in_combat())
player:notify("You are involved in combat here.  You cannot use teleportation commands while in combat.");
return E_PERM;
elseif (verb == "@summon")
iobjstr = "me";
return this:("@move")();
endif
if (verb == "@go")
iobjstr = dobjstr;
else
dobj = $match_utils:match_player(dobjstr);
if ($match_utils:player_match_failed(dobj, dobjstr))
return;
endif
iobjstr = tostr(dobj.location);
endif
iobj = $match_utils:match_room(iobjstr);
globals = $string_utils:quoted_english_list($list_utils:map_prop($rpg:global_locations(), "name"), "", " or ");
if (!valid(iobj))
player:notify(tostr("You'll have to be more specific than ", iobjstr ? ("\"" + iobjstr) + "\"" | "that", " or try going to one of the global locations: ", globals, "."));
return E_INVIND;
elseif ((!is_gm) && (!$rpg:is_global_loc(iobj)))
player:notify(tostr("You can't go to ", "there" || iobj:dname(), " that way. Try using `@go` with an argument of one of the global jump-points: ", globals, "."));
return E_NACC;
endif
$rpg:faux_pedest(player, iobj);
.
#208:2
"@home   -- Return to your home.  Duh.";
"@start  -- Return to wherever new players start.";
if (player != caller_perms())
return E_PERM;
elseif (!this:allow_privileged_command(player, verb))
return E_PERM;
endif
set_task_perms(player);
here = player.location;
if ($object_utils:has_callable_verb(here, verb))
return here:(verb)(@args);
endif
if (player:in_combat())
player:notify("You are involved in combat here.  You cannot use the `@home` and `@start` commands while in combat.");
return E_PERM;
endif
verb[1..1] = "";
dest = (verb == "home") ? player.home | $player_start;
if (here == dest)
player:notify("Don't waste your energy; you're already there.");
return;
elseif (verb != "home")
"skip the next branches";
elseif ((typeof(dest) != OBJ) || (!$object_utils:isa(dest, $room)))
player:notify("You can't go around living in things that aren't rooms.  Your home has been reset to the default.");
player:set_home($player_start);
elseif (!$recycler:valid(dest))
player:tell("Hrm--Your home no longer exists!  Time to look around for a new one.  Resetting to the default.");
player:set_home($player_start);
endif
player:moveto(dest);
if (player.location == here)
player:notify("Something went wrong.  You didn't go anywhere.  Maybe your destination doesn't want you anymore?");
return;
endif
if (valid(here))
here:announce(tostr("<OOC> ", player:namec(), " teleports out via @", verb, "."));
endif
if (player.location == dest)
dest:announce_all(tostr("<OOC> ", player:namec(), " teleports in via @", verb, "."));
elseif (valid(dest))
player:notify("Something went wrong.  You moved somewhere, but not where you'd intended.");
dest:announce(tostr("<OOC> ", player:namec(), " teleports in via @", verb, ", looking rather bewildered."));
else
player:notify("Yoiks!  You're in $nothing!  Don't PANIC--try typing `@home` or `@start`.");
endif
.
#209:0
"All spoof verbs print unattributed string to a character or location.";
"@spoof string";
"  Spoof to your current location.";
"@spoof string to char";
"@spoof char with string";
"  Spoof to the given character.";
"@spoof string at char";
"  Spoof to the location of the given character.";
"@spoof string around char";
"  Spoof to the location of the given character, but NOT to the character eirself.";
"";
"If the parser is mistaking a preposition in 'string' as the command-line preposition, try enclosing it in quotes.";
"Eg. @spoof \"The ground begins to swell with tremors.\" at Quake_Boy";
"";
"Only spoofs performed by programmers/GMs are un-attributed.  All others are accompanied by an extra line in the following format:";
"--@spoof-->#NNNNN (NAME)";
"Where #NNNNN is the spoofing player's object number, and NAME is eir name.  Experienced client users (tf, vt) can /gag the attribution line for the object numbers of players whom they trust to spoof.";
all_but = {};
direct = 0;
if (prepstr == "to")
text = dobjstr;
target_str = iobjstr;
direct = 1;
elseif (prepstr == "with")
prepstr = "to";
text = iobjstr;
target_str = dobjstr;
direct = 1;
elseif (prepstr == "at")
text = dobjstr;
target_str = iobjstr;
elseif (("around" in args) && (m = match(argstr, "^ *\"?%([^\"]*%)\"? +around +%(.+%)$")))
prepstr = "around";
text = substitute("%1", m);
target_str = substitute("%2", m);
all_but = {"target"};
else
text = argstr;
target_str = "me";
endif
target = $rpg:match_character(target_str);
if ($rpg:character_match_failed(target, target_str))
return;
endif
if (i = "target" in all_but)
all_but[i] = target;
endif
attrib = $rpg:trusted(player) ? 0 | tostr("--@spoof-->", player, " (", player.name, ")");
if (direct)
target:tell(text);
attrib && target:tell(attrib);
else
target:room_announce_all_but(all_but, text);
attrib && target:room_announce_all(attrib);
endif
if ((player.location != target.location) || (direct && (player != target)))
player:tell("You @spoof \"", text, "\" ", prepstr, " ", target:dname(), " in ", target:public_loc(player):dname(), ".");
endif
.
#209:1
"@ic-shout message";
"This verb is used to announce actions that effect all IC players.";
"@gm-shout message";
"This one to announce a message to all online Gamemasters.";
if (!$rpg:trusted(player))
player:tell("You scream within yourself.");
return E_PERM;
endif
if ((length(args) == 1) && (argstr[1] == "\""))
argstr = args[1];
endif
gms = verb == "@gm-shout";
EMU = $emu;
msg = gms ? this.gm_shout_text_msg | this.ic_shout_text_msg;
txt = gms ? EMU:tokenize("bold", "magenta") | EMU:tokenize("magenta");
txt = (txt + argstr) + EMU:tokenize("normal");
msg = strsub(msg, "$text", txt);
msg = EMU.ANSI_Line_Prefix + $string_utils:pronoun_sub(msg);
for user in (connected_players())
if (gms ? $rpg:trusted(user) | user.ic)
user:tell(msg);
endif
endfor
if ((!gms) && (!player.ic))
player:tell("You announce your message to all IC users.  You're not one of them.");
endif
.
#210:0
"Usage: @paste";
"Announce a series of entered lines to the room the player is in.";
"Before the lines are quoted, player.paste_header is run through";
"Rremember 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_.";
len = player:linelen();
lines = $command_utils:read_lines();
text = {$string_utils:center("@paste from " + player.name, len, "-")};
text = {@text, @lines};
text = listappend(text, $string_utils:center("end @paste", len, "-"));
people = $set_utils:intersection(player.location:contents(), connected_players());
for target in (people)
target:tell_lines(text);
suspend(0);
endfor
.
#210: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:";
"      |Haakon has disconnected.";
"  And the room sees:";
"      Hacker | Haakon has disconnected.";
player.location:announce_all((((player.name + " | ") + verb[2..length(verb)]) + " ") + argstr);
.
#210:2
"@paste-to <player> [<player> ... ]";
"As @paste, but to one or more players.";
targets = $string_utils:match_player(args);
targets = $command_utils:player_match_result(targets, args);
if (!(targets = listdelete(targets, 1)))
return;
endif
len = player:linelen();
lines = $command_utils:read_lines();
if (!lines)
player:tell("No text entered.");
return;
endif
text = {$string_utils:center("Private message from " + player.name, len, "-")};
for line in (lines)
text = {@text, @player:linesplit(line, len)};
endfor
text = listappend(text, $string_utils:center("end message", len, "-"));
for target in (targets)
target:tell_lines(text);
endfor
player:tell("Done @pasting.");
.
#211:0
":parse_owned_args(@args) => {who, include/exclude-list, namecheck-list, with-columns, with-exits, with-kids, with-locations, with-prospectus, low-bytes, high-bytes, mailme}";
la = length(args);
if (la < 2)
return {la ? $string_utils:match_player(args[1]) | player, {}, "", 0, 0, 0, 0, 0, 0, $maxint, 0};
endif
wc = we = wl = wa = wk = mm = 0;
lb = 0;
hb = $maxint;
np = ("named" in args) || ("matching" in args);
wp = "with" in args;
if ((!wp) && (wp = ("over" in args) || ("under" in args)))
la = la + 1;
args = listinsert(args, "with", wp);
endif
ie = (wp && (wp != la)) ? (np > wp) ? args[wp + 1..np - 1] | args[wp + 1..la] | {};
lie = length(ie);
ns = (np && (np != la)) ? $string_utils:from_list((wp > np) ? args[np + 1..wp - 1] | args[np + 1..la], " ") | "";
if (i = "over" in ie)
"Limit output to objects over a certain size.";
if (i != lie)
lb = this:parse_byteref(ie[i + 1]);
ie = listdelete(ie, i + 1);
endif
ie = listdelete(ie, i);
endif
if (i = "under" in ie)
"Limit output to objects under a certain size.";
if (i != lie)
hb = this:parse_byteref(ie[i + 1]);
ie = listdelete(ie, i + 1);
endif
ie = listdelete(ie, i);
endif
if (i = ("col" in ie) || ("columns" in ie))
"Format output into columns.";
wc = 1;
ie = listdelete(ie, i);
endif
if (i = "exits" in ie)
"Include exits in output.";
we = 1;
ie = listdelete(ie, i);
endif
if (i = ("kids" in ie) || ("children" in ie))
"Limit output to objects with children.";
wk = 1;
ie = listdelete(ie, i);
endif
if (i = (("loc" in ie) || ("locations" in ie)) || ("locs" in ie))
"Include locations and/or exit source->dest in output.";
wl = 1;
ie = listdelete(ie, i);
endif
if (i = ("pros" in ie) || ("prospectus" in ie))
"Include bits, kids, verbcount, object size, and locations in output.";
wa = 1;
ie = listdelete(ie, i);
endif
if (i = "mailme" in ie)
"Mail the results instead of :telling them.";
mm = 1;
ie = listdelete(ie, i);
endif
who = (!(1 in {wp, np})) ? $string_utils:match_player(args[1]) | player;
return {who, ie, ns, wc, we, wk, wl, wa, lb, hb, mm};
.
#211:1
":output_owned(LIST output, NUM mail-me)";
if (caller != this)
return E_PERM;
elseif (args[2])
"... my little tribute to stan laurel ...";
subject = tostr("Your '@owned ", argstr, "' is ready, ma'am.");
$mail_agent:send_message(this, {player}, {subject, {this.owner}}, args[1]);
else
for x in (args[1])
player:tell(x);
if ((ticks_left() < 500) || (seconds_left() < 2))
suspend(random(3) - 1);
endif
endfor
endif
.
#211:2
":owned_by_class(player, {include}[, {exclude}])";
if (!$object_utils:has_property(who = args[1], "owned_objects"))
return E_PROPNF;
elseif (typeof(oo = who.owned_objects) != LIST)
return E_TYPE;
else
objects = {};
include = args[2];
exclude = {@args, {}}[3];
if ((!include) && (!exclude))
return oo;
endif
for o in (oo)
$command_utils:suspend_if_needed(0);
subject = child = bastard = o;
while ((valid(subject) && (!child)) && (!bastard))
"...give priority to include...";
if (subject in include)
objects = {@objects, o};
child = 1;
elseif (subject in exclude)
bastard = 1;
endif
subject = parent(subject);
endwhile
if ((!include) && (!bastard))
objects = {@objects, o};
endif
endfor
return objects;
endif
.
#211:3
return $string_utils:to_bytes(@args);
":size_string(NUM bytes[, NUM places_past_decimal_point])";
"Does not round numbers as well as it should, but blame that on 9.";
"=> Size in bytes, kilobytes, megabytes, or gigabytes.";
size = args[1];
space = "00000000000";
sigdig = min(listdelete(args, 1) ? args[2] | 1, length(space) - 1);
Kb = 1000;
Mb = 1000000;
Gb = 1000000000;
if (size < Kb)
string = {size, ".", 0, "  b"};
elseif (size < Mb)
string = {size / Kb, ".", size % Kb, " Kb"};
elseif (size < Gb)
string = {size / Mb, ".", size % Mb, " Mb"};
else
string = {size / Gb, ".", size % Gb, " Gb"};
endif
fractional = tostr(string[3], space);
if (!sigdig)
if (fractional[1] > "4")
string[1] = string[1] + 1;
endif
string = {string[1], string[4]};
else
fractional = fractional[1..sigdig + 1];
place = (fractional[sigdig + 1] == "0") ? sigdig | (sigdig + 1);
if (fractional[place] > "4")
if (place == 1)
string[1] = string[1] + 1;
elseif ((val = fractional[place - 1]) == "9")
while ((place > sigdig) && (fractional[place - 1] == "9"))
fractional[place] = "0";
place = place - 1;
endwhile
else
fractional[place - 1] = tostr(tonum(val) + 1);
endif
endif
string = listset(string, fractional[1..sigdig], 3);
endif
return tostr(@string);
.
#211:4
":parse_byteref(string) => NUM bytes referred to.";
"Eg: 10 Mb  => 10000000";
"     8k    => 8000";
"    100Gb  => $maxint";
if (mr = match(args[1], "%([0-9]+%) *%([bKMG]b?%)?"))
index = (br = substitute("%2", mr)) ? br[1] in {"b", "K", "M", "G"} | 1;
multp = {1, 1000, 1000000, 1000000000}[index];
bytes = (base = tonum(substitute("%1", mr))) * multp;
return (base > ($maxint / multp)) ? $maxint | bytes;
else
return E_TYPE;
endif
.
#211:5
":db_share(player) => Total Bytes used as record by .object_size.";
if (typeof(oo = args[1].owned_objects) != LIST)
return oo;
endif
bytes = 0;
for o in (oo)
if (size = o.object_size)
bytes = bytes + size[1];
endif
endfor
return bytes;
.
#211:6
":verbs(object) -> Return a list of verbs on the given object.";
return verbs(args[1]);
.
#211:7
"@owned [<player>][ with [option ... ][<class_limiters>]][named <searchfor>]";
"";
" Options include:";
"col*umns    --  arrange output in columns.";
"exits       --  include exits in listing (by default exits are excluded).";
"kids        --  only show objects with children, and display #kids.";
"loc*ations  --  show locations of each object, or source->dest if an exit.";
"pros*pectus -- 'loc' + #kids, #verbs and object size for each object.";
"mailme      --  mail you the results instead of :telling them.";
"over  x     --  show objects over  'x' in size.";
"under x     --  show objects under 'x' in size.";
"";
"'class_limiters' are objects whose kids you want included (or if preceded by !, not included) in the listing.";
"";
"'searchfor' is a string to match against the names and aliases of all owned objects.";
"";
"If @owned-regex is used, the string should be a regular-expression matched against each name and alias.";
"";
"The 'over' and 'under' options should be followed by either a raw number, or a size in the form of x(K|M|G)[b].  Whole numbers only, please.";
"";
"Prospectus format:";
"<verbs> <kids> #object: [!]Name     : <size> Xb    #location > locname";
"";
"A ! before the name indicates that the object has a clear description.";
"";
"Number of kids is a raw count of children(), NOT total descendants.";
"";
"Object size is determined by the .object_size property and is only an approximation based on the most recent polling.  'unknownB' in this field means that object_size has not been pre-calculated for the given object.";
this:update_usage(verb, player, args);
if (player != caller_perms())
return E_PERM;
endif
pa = this:parse_owned_args(@args);
if ($command_utils:player_match_failed(who = pa[1], args[1]))
return E_INVARG;
elseif ((!$wiz_utils:is_builder(player)) && (!who:is_readable_by(player)))
player:tell("You aren't allowed to look at ", who:pp(), " objects.");
return E_PERM;
elseif ((set = who.owned_objects) == E_PROPNF)
player:tell("Weird.  ", $string_utils:nn(who), " doesn't even HAVE an owned_objects property.");
return E_PROPNF;
elseif (typeof(set) != LIST)
player:tell("That character owns way too much to keep in a list.  You'll have to try @auditdB or something.");
return E_TYPE;
endif
su = $string_utils;
searchfor = pa[3];
regex = (verb == "@owned-regex") && searchfor;
if (match("", searchfor) == E_INVARG)
player:tell("\"", searchfor, "\" is a malformed regular expression.");
return E_INVARG;
endif
wc = pa[4];
wk = pa[6];
wp = (player:get_option($display_options, "full_owned") || pa[8]) || (verb[2] == "f");
wl = pa[7] || wp;
lb = pa[9];
hb = pa[10];
mm = pa[11];
exclude = {$exit};
include = output = {};
for x in (pa[2])
if (valid(o = player:my_match_object(strsub(x, "!", ""))))
if (x[1] == "!")
exclude = setadd(exclude, o);
else
include = setadd(include, o);
endif
endif
endfor
lmo = length(tostr(max_object()));
if (we = (($exit in include) || pa[5]) || (verb[1..2] == "@f"))
exclude = setremove(exclude, $exit);
endif
mnl = (we || wp) ? 16 | 32;
space = $string_utils:space(pll = player:linelen() || 79);
suspend_msg = $string_utils:centre((("... suspending " + verb) + (argstr ? " " + argstr | "")) + " ...", pll);
totalbytes = this:db_share(who);
shownbytes = 0;
set = this:owned_by_class(who, include, exclude);
for o in (set)
$command_utils:suspend_if_needed(5 + random(5), suspend_msg);
size = o.object_size || {0, 0};
bytes = size[1];
kids = children(o);
show = (!wk) || kids;
show = show && ((!bytes) || ((bytes > lb) && (bytes < hb)));
if (show && searchfor)
show = 0;
aliases = setadd(o.aliases, o.name);
while ((!show) && aliases)
show = regex ? match(aliases[1], searchfor) | index(aliases[1], searchfor);
aliases = listdelete(aliases, 1);
endwhile
endif
if (show)
shownbytes = shownbytes + size[1];
objn = tostr(o, space)[1..lmo] + (is_clear_property(o, "description") ? " !" | "  ");
name = tostr(o.name, space)[1..pll] + (wl ? ": " | "");
if (wl)
location = o.location;
ancestors = $object_utils:ancestors(o);
if (($room in ancestors) && (location == #-1))
lstr = "Room";
elseif (($exit in ancestors) && (location == #-1))
lstr = tostr($string_utils:nn(o.source, ""), "->", $string_utils:nn(o.dest, ""));
else
lstr = valid(location) ? (tostr(location, space)[1..lmo] + " > ") + location.name | "*** nowhere ***";
endif
text = {objn, name[1..mnl], " : "};
if (wk || wp)
text = {kids ? $string_utils:right(tostr(min(length(kids), 999)), -4) + " " | "     ", @text};
endif
if (wp)
text = {(vc = this:verbs(o)) ? $string_utils:right(tostr(length(vc)), -4) + " " | "     ", @text};
sstr = size[2] ? $string_utils:right($string_utils:to_bytes(bytes), 8) | "unknownB";
text = {@text, sstr, " ", tostr(o.r ? "r" | " ", o.f ? "f" | " ", o.w ? "w" | " "), " "};
endif
text = tostr(@text, lstr);
text = (length(text) > pll) ? (text + space)[1..pll] | text;
else
text = "";
if (wk && kids)
text = $string_utils:right(tostr(min(length(kids), 999)), -4) + " ";
endif
text = (text + objn) + name[1..min(length(o.name), length(name))];
endif
output = {@output, {o.name, text}};
endif
endfor
output = (length(output) > 50) ? $list_utils:slice(output, 2) | $list_utils:slice($list_utils:sort_alist_suspended(5 + random(5), output), 2);
complete = (wl || (!wc)) ? output | $string_utils:columnize(output, 2, pll);
showing = include ? tostr("kids of ", $string_utils:name_and_number_list(include)) | "all objects";
showing = showing + (wk ? " (with children)" | "");
lbs = (lb != 0) ? " over " + $string_utils:to_bytes(lb) | "";
hbs = (hb != $maxint) ? " under " + $string_utils:to_bytes(hb) | "";
bstr = (lbs && hbs) ? (lbs + " and") + hbs | (lbs || hbs);
showing = showing + (bstr ? bstr + " in size" | "");
title = tostr("Showing ", showing, searchfor ? (" matching \"" + searchfor) + "\"" | "", " owned by ", who.name, exclude ? tostr(", excluding kids of ", $string_utils:name_and_number_list(exclude)) | "", ":");
headers = (wp ? "vrbs " | "") + ((wk || wp) ? "kids " | "");
complete = {title, "", headers, @complete};
third = (pll / 3) + 1;
third = third - 3;
q = $quota_utils:get_quota(who);
t = tostr("%", third, " %", third, " %", third);
footer = {su:format(t, tostr("    Shown Objects: ", length(output)), "Shown Bytes: " + $string_utils:to_bytes(shownbytes), "     Quota: " + $string_utils:to_bytes(q))};
footer = {@footer, su:format(t, tostr("    Total Objects: ", length(who.owned_objects)), "Total Bytes: " + $string_utils:to_bytes(totalbytes), "Free Quota: " + $string_utils:to_bytes(q - totalbytes))};
this:output_owned({@complete, "", @footer}, mm);
.
#212:0
":match_puppet_for(OBJ user, STR spec)";
"E_PERM: Couldn't read puppets list.";
"E_PROPNF: No puppets list.";
"#match";
"{@matches}";
"{}: No match, puppets empty.";
"$failed_match: No match, puppets non-empty.";
"$nothing: No spec given.";
{user, spec} = args;
if (!spec)
return $nothing;
endif
try
puppets = user.puppets || {};
except (E_PERM)
return E_PERM;
except (E_PROPNF)
return E_PROPNF;
endtry
if (puppets && ((po = toobj(spec)) in puppets))
return po;
endif
match = puppets && $match_utils:match(spec, puppets, LIST);
if (!match)
match = $match_utils:match(spec, player:env(), LIST);
endif
if (!match)
return puppets && $failed_match;
elseif (length(match) == 1)
return match[1];
else
return match;
endif
.
#212:1
":do_match_puppet_for(OBJ user, STR spec)";
{user, spec} = args;
result = this:match_puppet_for(user, spec);
if (result == E_PERM)
user:tell("Your puppets list is unreadable!  Please contact a wizard.");
elseif (result == E_PROPNF)
user:tell("You don't have a puppets list!  Please contact a wizard.");
elseif (typeof(result) == OBJ)
if (valid(result))
return {result};
elseif (result == $nothing)
user:tell("You didn't specify a puppet name or alias.");
else
user:tell(tostr("No puppet found in your puppets list or your environment matching \"", spec, "\".  Use `@puppets` to show puppets you may remotely control."));
endif
elseif (result)
user:tell(tostr("Please be more specific; \"", spec, "\" could be ", $string_utils:iname_list(result, "none", " or "), "."));
else
user:tell(tostr("No puppet found in your environment matching \"", spec, "\".  Use `@add-puppets` to add a puppets to the list of those which you may remotely control."));
endif
return 0;
.
#212:2
"Relay to the command on a player's puppet.";
if (player != caller_perms())
return E_PERM;
endif
if ((verb == "@add-shaper") || (verb == "@remove-shaper"))
result = this:do_match_puppet_for(player, iobjstr);
else
result = this:do_match_puppet_for(player, dobjstr);
endif
if (!result)
return E_INVARG;
endif
{pup} = result;
if (!$object_utils:has_callable_verb(pup, verb))
player:tell(tostr("You can't issue that command on ", $string_utils:nn(pup), "."));
return;
endif
set_task_perms(caller_perms());
pup:(verb)(@args);
"-- WIZARDLY --";
.
#212:3
"@add-puppet <puppet-name>";
"Add a puppet to your personal list, allowing it to be controlled remotely.";
if (player != caller_perms())
return E_PERM;
endif
result = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(result, dobjstr))
return E_INVARG;
endif
pup = result;
if (pup in player.puppets)
player:tell(pup:grammar_sub("%N (%#) %<is> already in your list of puppets."));
return E_NONE;
elseif (!$object_utils:isa(pup, $puppet))
player:tell(pup:grammar_sub("%(dnamec) %<is> not a child of the generic puppet."));
return E_NACC;
endif
player.puppets = setadd(player.puppets, pup);
player:tell(pup:grammar_sub("%N (%#) added to your list of puppets."));
.
#212:4
"@remove-puppet <puppet-name>";
"Remove a puppet from your personal list of remote-controlled puppets.";
if (player != caller_perms())
return E_PERM;
endif
result = this:do_match_puppet_for(player, dobjstr);
if (!result)
return E_INVARG;
endif
{pup} = result;
if (!(pup in player.puppets))
player:tell(pup:grammar_sub("%(dnamec) %<is> not in your list of puppets."));
return E_NONE;
endif
player.puppets = setremove(player.puppets, pup);
player:tell(pup:grammar_sub("%N (%#) removed from your list of puppets."));
.
#212:5
"@list-puppets";
"List all your remote-controlled puppets.";
if (player != caller_perms())
return E_PERM;
endif
puppets = `player.puppets ! E_PROPNF => {}';
for pup in (puppets)
if (!$object_utils:isa(pup, $puppet))
player:tell(tostr("... removing non-puppet ", $string_utils:nn(pup)));
puppets = setremove(puppets, pup);
endif
endfor
if (!puppets)
player:tell(tostr("You don't maintain a list of puppets.  Use `@add-puppet' to begin one."));
return;
endif
player.puppets = puppets;
player:tell(tostr("You can remotely control ", $string_utils:name_and_number_list(puppets), "."));
.
#212:6
"add-shaper <player> to <puppet>";
"Allow `player' to control/edit this puppet.  Effectively add em as an owner.";
"Be sure you trust the player before adding them.";
if (player != caller_perms())
return raise(E_PERM);
endif
pmatch_result = this:do_match_puppet_for(player, iobjstr);
if (!pmatch_result)
return;
elseif ($match_utils:player_match_failed(dobj = $match_utils:match_player(dobjstr), dobjstr))
return;
endif
puppet = pmatch_result[1];
set_task_perms(player);
result = puppet:add_shaper(dobj);
if (typeof(result) == LIST)
player:tell("Added.  You now share control of ", $string_utils:nn(puppet), " with ", $string_utils:title_list(setremove(result, player), "nobody"), ".");
else
player:tell(puppet:grammar_sub("You can't add shapers to %(dname)"));
endif
.
#212:7
"remove-shaper <player> from <puppet>";
"Remove player from the puppet's trusted list.";
if (player != caller_perms())
return raise(E_PERM);
endif
pmatch_result = this:do_match_puppet_for(player, iobjstr);
if (!pmatch_result)
return;
elseif ($match_utils:player_match_failed(dobj = $match_utils:match_player(dobjstr), dobjstr))
return;
endif
puppet = pmatch_result[1];
set_task_perms(player);
result = puppet:remove_shaper(dobj);
if (typeof(result) == LIST)
player:tell("Removed.  You now share control of ", $string_utils:nn(puppet), " with ", $string_utils:title_list(setremove(result, player), "nobody"), ".");
else
player:tell(puppet:grammar_sub("You can't remove shapers from %(dname)"));
endif
.
#212:8
"list-shapers <puppet>";
"List joint controllers of the puppet.";
if (player != caller_perms())
return raise(E_PERM);
endif
pmatch_result = this:do_match_puppet_for(player, dobjstr);
if (!pmatch_result)
return;
endif
puppet = pmatch_result[1];
if (length(shapers = puppet:shapers()) > 1)
player:tell($string_utils:title_list(shapers, "nobody"), " share control of ", $string_utils:nn(puppet), ".");
else
player:tell(shapers[1].name, " has total control over ", $string_utils:nn(puppet), ".");
endif
.
#213:0
":add_hangout(OBJ room[, STR desc])";
"Add or update the given room as a 'hangout'.";
if (caller != this)
return E_PERM;
endif
room = args[1];
i = $list_utils:iassoc(room, this.hangouts);
if (!i)
this.hangouts = {@this.hangouts, {@args, ""}[1..2]};
elseif (length(args) > 1)
this.hangouts[i] = args;
else
return 1;
endif
return 1;
.
#213:1
":match_hangout(str)";
return $match_utils:match(args[1], $list_utils:slice(this.hangouts));
.
#213:2
":match_hangout_failed(str)";
"Try to match the given string to a hangout.  Return the room if it succeeds; otherwise print error messages to player and return true.";
s = args[1];
o = this:match_hangout(s);
if (o == $ambiguous_match)
player:tell("More than one hangout matches your request.  Use `@hangouts` to list them all.");
elseif (o == $nothing)
player:tell("You need to specify a string to match a hangout.");
elseif (!valid(o))
player:tell("There isn't a hangout matching your request.  Use `@hangouts` to list them all.");
else
return o;
endif
return 1;
.
#213:3
":get_directory_for(obj)";
return (d = $list_utils:assoc(args[1], this.hangouts)) && d[2];
.
#213:4
return $rpg:trusted(@args);
.
#213:5
"@hangout <room name>";
"Add a room to the list of hangouts.";
if (player != caller_perms())
return E_PERM;
elseif (!this:trusted(player))
player:tell("You aren't authorized to add hangouts.");
return E_PERM;
endif
o = player:my_match_room(dobjstr);
if ($match_utils:room_match_failed(o, dobjstr))
return;
endif
if (!o:is_controllable_by(player))
player:tell("You don't control ", $string_utils:nn(o), "; ask the owner (", o.owner.name, ") to add it for you.");
return E_PERM;
endif
r = this:update_hangout(o);
if (r)
player:tell($string_utils:nn(o), " added as a hangout.  Use `@directory ", o, " is \"Description, directions, etc.\"` to embellish its entry.");
else
player:tell("Failed (", $string_utils:print(r), ") to add ", $string_utils:nn(o), " as a hangout.  Sorry.");
endif
.
#213:6
"@hangouts";
"List rooms designated by their GM owners as \"hangouts\": social nuclei of the MOO.";
hangouts = this.hangouts;
if (!hangouts)
player:tell("No hangouts have been defined.");
return;
endif
ds = {};
stu = $string_utils;
mu = $math_utils;
con = $login:connected();
spc = stu:space(41);
OBJECT_UTILS = $object_utils;
for e in (hangouts)
r = e[1];
if (!OBJECT_UTILS:isa(r, $room))
hangouts = setremove(hangouts, e);
continue;
endif
pop = {};
ooc = 0;
all = r:matching_contents();
for c in (con)
if (c in all)
pop = {@pop, c};
if (!c.ic)
ooc = ooc + 1;
endif
con = setremove(con, c);
endif
endfor
lp = length(pop);
op = mu:to_percent(ooc, lp);
ip = mu:to_percent(lp - ooc, lp);
if (op == ip)
rs = "";
elseif (op > ip)
rs = tostr(", ", op, "% OOC");
else
rs = tostr(", ", ip, "% IC");
endif
if (lp < 1)
ps = "Deserted";
elseif (lp < 6)
ps = "Sparse";
else
ps = "Bustling";
endif
ps = tostr("[", ps, rs, "]");
a = stu:format("%21 %18 ", r:titlec(), ps);
if (d = e[2])
d = player:linesplit(d, 35);
c = {a + d[1]};
for l in (listdelete(d, 1))
c = {@c, spc + l};
endfor
else
c = {a};
endif
ds = {@ds, @c};
endfor
this.hangouts = hangouts;
player:tell_lines(ds);
.
#213:7
"@directory <hangout> is <description>";
"Describe a particular hangout.  Common inclusions would be the general IC/OOC status of the location, what kinds of characters usually hangout there, and directions to the room.";
if (player != caller_perms())
return E_PERM;
elseif (!this:trusted(player))
player:tell("You aren't authorized to add hangouts.");
return E_PERM;
endif
if (o = this:match_hangout_failed(dobjstr))
return;
endif
if (!o:is_controllable_by(player))
player:tell("You aren't allowed to edit the directory information for ", $string_utils:nn(o), "; ask ", o.owner, " to do it for you.");
return E_PERM;
endif
r = this:update_hangout(o, iobjstr);
if (r)
player:tell("Successfully updated the directory information for ", $string_utils:nn(o), ".");
else
player:tell("Failed (", $string_utils:print(r), ") to update the directory information for ", $string_utils:nn(o), ".  Sorry.");
endif
.
#214:0
"Catch all mail and editing commands, check if they've been revoked for the player.  Tell them so, or re-route to the commands on $player.";
if (player != caller_perms())
return E_PERM;
elseif (!this:allow_privileged_command(player, verb))
return E_PERM;
endif
set_task_perms(player);
player:(verb)(@args);
.
#214:1
"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";
if (player != caller_perms())
"...HACKER SWINE!";
return E_PERM;
endif
set_task_perms(player);
if (!args)
player:notify(tostr("Usage: ", verb, " <recipients(s)> [subj=<text>] [<message>]"));
return E_INVARG;
elseif (!(recipients = $mail_editor:parse_recipients({}, $string_utils:explode(args[1]))))
return;
endif
if ((length(args) > 1) && ((eq = index(args[2], "=")) && (index("subject", args[2][1..eq - 1]) == 1)))
subject = $string_utils:trim(args[2][eq + 1..length(args[2])]);
ws = $string_utils:word_start(argstr);
argstr = argstr[1..ws[1][2]] + argstr[ws[2][2] + 1..length(argstr)];
args = listdelete(args, 2);
else
subject = "";
endif
if (length(args) > 1)
unbroken = argstr[(argstr[1] == "\"") ? length(args[1]) + 4 | (length(args[1]) + 2)..length(argstr)] + "^";
msg = {};
while (unbroken)
if (i = index(unbroken, "^"))
msg = {@msg, unbroken[1..i - 1]};
endif
unbroken = unbroken[i + 1..length(unbroken)];
endwhile
else
if (!(subject || player:get_option($mail_options, "nosubject")))
player:notify("Subject:");
subject = $command_utils:read();
endif
player:notify("Enter lines of message:");
if (player.ic || (active = player in $mail_editor.active))
escapes = {};
else
escapes = {"@edit"};
endif
help_msg = {tostr("You are composing mail to ", $mail_agent:name_list(@recipients), ".")};
if (escapes)
help_msg = {@help_msg, "Type `@edit` to take this into the mail editor."};
elseif (player.ic)
help_msg = {@help_msg, "Because you're in character, you aren't allowed to take this message into the editor.  Type `@ooc` to remedy the situation."};
endif
msg = $command_utils:read_lines_escape(escapes, help_msg);
if (typeof(msg) == ERR)
player:notify(tostr(msg));
return;
elseif (msg[1] == "@edit")
if (this:allow_privileged_command(player, verb))
$mail_editor:invoke(1, verb, recipients, subject, {}, msg[2]);
endif
return;
elseif (!(msg[2] || subject))
player:notify("Blank message not sent.");
return;
endif
msg = msg[2];
endif
"The following line added to enforce a NO-LINEBREAK policy.  Might remove it if it proves troublesome.";
"msg = $string_utils:unbreak(msg);";
result = $mail_agent:send_message(player, recipients, subject, msg);
if (result && result[1])
player:notify(tostr("Message sent to ", $mail_agent:name_list(@listdelete(result, 1)), "."));
else
player:notify("Message not sent.");
endif
.
#215:0
return args[1].programmer;
.
#215:1
"@addskill <parentskill> named <newskillname>[, <aliases>, ...]";
if ((player != caller_perms()) || (player != #2))
player:tell("You aren't allowed to do that.");
return E_PERM;
endif
if ((!argstr) || (!(m = match(argstr, "%(.+%) +named +%(.+%)"))))
player:tell("usage:  @skill <parentskill> named <newskillname>[, <aliases>,...]");
return E_ARGS;
endif
pstr = substitute("%1", m);
parent = (pstr == "$skill") ? $skill | $rpg:match_skill(pstr);
if (parent == $ambiguous_match)
player:tell("I don't know which skill \"", pstr, "\" you mean.");
return parent;
elseif (!valid(parent))
player:tell("I don't know of any skill named \"", pstr, "\".");
return parent;
endif
aliases = $list_utils:map_arg($string_utils, "trim", $string_utils:explode(substitute("%2", m), ","));
if (!aliases)
player:tell("You must give the new skill's aliases in the form `name[, name]*'.");
return E_ARGS;
endif
name = aliases[1];
player:tell("Enter a description for ", name, ", detailing what kinds of tasks would warrant a resolve against the skill.");
desc = $command_utils:read_lines();
if (!desc)
player:tell("You must give a description for the skill.  Skill creation aborted.");
return E_NACC;
endif
skill = $recycler:_create(parent, player);
if (typeof(skill) != OBJ)
player:tell("Couldn't create skill object: ", skill);
return skill;
endif
props = {};
for alias in (aliases)
if (match(alias, "^[a-z_1-9]+$"))
props = {@props, alias};
endif
endfor
prop = "";
while (props && (!prop))
if (typeof(add_property($rpg.skills, props[1], skill, {player, "R"})) != ERR)
prop = props[1];
endif
props = listdelete(props, 1);
endwhile
if (!prop)
player:tell("At least one of the skill's aliases should be a valid property name (containing only letters, numbers, and/or underscores).  Skill creation aborted.");
$recycler:_recycle(skill);
return E_INVARG;
endif
if (typeof(add_property($skilled, prop, 0, {$skilled.owner, "R"})) == ERR)
player:tell("Failed to add \"", prop, "\" as a property on ", $string_utils:nn($skilled), ".  Skill creation aborted.");
$recycler:_recycle(skill);
return E_INVARG;
endif
skill:set_name(name);
skill:set_aliases(aliases);
skill:set_description(desc);
skill.property = prop;
skill:moveto($rpg.skills);
player:tell($string_utils:nn(skill), " created.");
.
#216:0
return $rpg:trusted(args[1]);
.
#216:1
player:tell("Use of @gate has been disabled.  It was quasi-IC in a cute way long ago, but nowadays it leads to broken continuity and buggy exits.  Please use @move or @join instead.");
return;
"  gate to <room>";
"Open a 'gate' to the given room; a temporary exit from your location to that room.  Use 'dispel <gate>' to kill it.";
"";
"  gate <gatenames> to <room>";
"Same as above, but specify names for the portal, delimited by commas.";
if ((caller_perms() != player) || (!$rpg:trusted(player)))
player:tell("Your magick is not sophisticated enough for such a task.");
return E_PERM;
elseif ($quota_utils:get_quota(player) < 2)
player:tell("Your personal resources are not sufficient to open a gate.  (Yer outta quota)");
return E_QUOTA;
endif
source = player.location;
dest = player:my_match_room(iobjstr);
if (valid(dest))
"ok";
elseif (valid(char = $rpg:match_character(iobjstr)))
dest = char:room();
else
player:tell("The destination string specified (\"", iobjstr, "\") is neither a room nor a character.");
return;
endif
generic_gate = this.generic_gate;
exit = $recycler:_create(generic_gate, player);
entrance = $recycler:_create(generic_gate, player);
exit.source = entrance.dest = source;
exit.dest = entrance.source = dest;
exit.otherside = entrance;
entrance.otherside = exit;
if (dobjstr)
$building_utils:set_names(exit, dobjstr);
$building_utils:set_names(entrance, dobjstr);
endif
if (!dest:is_unlocked_for(player))
player:tell("That room is warded against such magicks.  (Probably @locked)");
exit:_dispel();
return E_NACC;
elseif (!(source:add_exit(exit) && source:add_entrance(entrance)))
player:tell("Couldn't create a portal leading from ", source:id(), ".  (Aborting)");
exit:_dispel();
return E_NACC;
elseif (!(dest:add_entrance(exit) && dest:add_exit(entrance)))
player:tell("Couldn't create a portal leading to ", dest:id(), ".  (Aborting)");
exit:_dispel();
return E_NACC;
endif
exit:setup_expire();
player:tell(exit:summon_msg());
source:announce(exit:ssummon_msg());
dest:announce_all(entrance:dsummon_msg());
.
#216:2
"@heal <character>";
"Heal all of the character's wounds, insanity, fatigue, and revive em if e is unconscious.";
if ((player != caller_perms()) || (!$rpg:trusted(player)))
player:tell("You aren't capable of such magick.");
return E_PERM;
endif
dobj = player:my_match_object(dobjstr);
if ($match_utils:object_match_failed(dobj, dobjstr))
return;
endif
unc = dobj:unconscious();
inj = dobj:stat_injury();
ins = dobj:stat_insanity();
fat = dobj:stat_fatigue();
better = base = "%N %<is> enveloped in a glow of well-being.  ";
if (unc)
dobj.unconscious = 0;
better = better + "%S %<wakes> from %p involuntary slumber.  ";
endif
if (inj)
dobj:set_stat_injury(0);
better = better + "%P wounds close up, scars disappearing and bruised bones strong once more.  ";
endif
if (ins)
dobj:set_stat_insanity(0);
better = better + "%S %<stops> gibbering like a lunatic.  ";
endif
if (fat)
dobj:set_stat_fatigue(0);
better = better + "%S %<slows> %p tired gasps and %<dries> %p sweaty brow.  ";
endif
app = {};
if (dobj:stat_upper_mobility() < 100)
dobj:set_stat_upper_mobility(100);
app = {@app, "arms"};
endif
if (dobj:stat_lower_mobility() < 100)
dobj:set_stat_lower_mobility(100);
app = {@app, "legs"};
endif
if (app)
better = better + tostr("A youthful litheness seems to return to %p ", $string_utils:english_list(app), ".  ");
endif
if (better == base)
player:tell(dobj:psc(), " is neither injured, insane, fatigued, nor unconscious.  But it's the thought that counts, eh?");
return E_NONE;
endif
msg = "%N %<flails> %p hands in a gesture of virtual omnipotence, ";
player:tell($string_utils:pronoun_sub(msg + "chanting words loosely translated from the arcane tongue of GMs to mean: \"Heal %d!\""));
if (player:is_local_to(dobj))
dobj:room_announce($string_utils:pronoun_sub(msg + "embellishing the motions with a barrage of arcane syllables, none of which (except \"%d\") you understand."));
endif
$you:say_action($string_utils:trimr(better), dobj);
.
#216:3
"@stats <character> -- Show a character's RPG statistics.";
dobj = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(dobj, dobjstr))
"...failure...";
elseif (dobj:tell_stats() == E_VERBNF)
player:tell("No game statistics on ", dobj:name(), ".");
endif
.
#216:4
"@seek <user>";
"Attempt to fake a pedestrian entrance into the given user's location.";
if (!$rpg:trusted(player))
player:notify("Only magical GM types may @seek out others.");
return;
elseif ($match_utils:player_match_failed(u = $match_utils:match_player(dobjstr), dobjstr))
return;
endif
$rpg:faux_pedest(player, u.location);
.
#216:5
"@shout message";
if (!player.wizard)
player:tell("You scream within yourself.");
return E_PERM;
endif
if ((length(args) == 1) && (argstr[1] == "\""))
argstr = args[1];
endif
EMU = $emu;
txt = (EMU:tokenize("green") + argstr) + EMU:tokenize("normal");
msg = strsub(this.shout_text_msg, "$text", txt);
msg = EMU.ANSI_Line_Prefix + $string_utils:pronoun_sub(msg);
for user in (connected_players())
user:tell(msg);
endfor
.
#216:6
"@table";
"Show a table of the stats of any class of items.";
"Eventually.";
if (player != caller_perms())
player:tell("You can't do that with the perms of ", $string_utils:nn(caller_perms()), ".");
return E_PERM;
elseif (dobjstr == "weapons")
kids = $object_utils:branches($weapon);
h1 = "                      Att   Ddg   PrW   PrA   DAM   Lth   Pen   Slw   Exr";
h2 = "                     ----- ----- ----- ----- ----- ----- ----- ----- -----";
props = {"attack_mod", "dodge_mod", "parry_with", "parry_against", "damage", "lethality", "penetration", "slowness", "exertion"};
ex = {"  Att: Attack-Mod  PrW: Parry-With      DAM: Damage        Slw: Slowness"};
ex = {@ex, "  Ddg: Dodge Mod   PrA: Parry-Against   Pen: Penetration   Exr: Exertion"};
elseif (dobjstr in {"body areas", "body parts"})
kids = $object_utils:descendants($body_area);
h1 = "                      Dam% DaMax CSMod  Bod%  Dth%  KO%  UpMob LwMob";
h2 = "                     ----- ----- ----- ----- ----- ----- ----- -----";
props = {"damage_mod", "max_damage", "called_shot_mod", "percent_of_body_area", "fatality_chance", "knockout_chance", "affects_upper_mobility", "affects_lower_mobility"};
ex = {" Dam%: Damage Multiplier  CSMod: Called Shot Mod       Dth%: Fatality Chance"};
ex = {@ex, "DaMax: Maximum Damage      Bod%: Percent of Body Area   KO%: Knockout Chance"};
ex = {@ex, "UpMb%: Upper Mobility - %DAM                    LwMb%: Lower Mobility - %DAM"};
else
player:tell("You must choose one of \"weapons\" or \"body areas\".");
return E_INVARG;
endif
body = {};
s_msg = tostr(verb, ": Suspending for five seconds.");
for kid in (kids)
line = ($string_utils:left(kid:spawned_name(), -13) + " #") + $string_utils:right(tonum(kid), -5);
for p in (props)
v = kid.(p);
line = tostr(line, " ", (v > 999) ? "  +++" | ((v < -999) ? "  ---" | $string_utils:right(v, -5)));
endfor
body = listappend(body, line);
$command_utils:suspend_if_needed(5, s_msg);
endfor
for line in ({h1, h2, @body, h2, @ex})
notify(player, line);
endfor
.
#216:7
"@snitch <pc> with <note>";
"Add a note to the gm_notes property on the given PC.  Your note will be attributed with your name and the date.  Notes may be edited by any GM with `@edit pc.gm_notes`.";
if (caller_perms() != player)
return E_PERM;
endif
dobj = $match_utils:match_player(dobjstr);
if ($match_utils:player_match_failed(dobj, dobjstr))
return;
endif
if (!iobjstr)
player:tell("You must supply a note.  `@snitch ", dobjstr, " with BLAH BLAH BLAH`");
return;
endif
note = {tostr("----- ", $string_utils:nn(player), " @ ", ctime()), iobjstr};
dobj.gm_notes = {@dobj.gm_notes, @note};
player:tell("Added @snitch note \"", iobjstr, "\" to ", $string_utils:nn(dobj), ".");
.
#216:8
"@compare [CHARACTER.]STAT with CHARACTER[.STAT]";
"@compare allows characters to compare stats with each other, or a GM to compare the stats of those for whom e is mastering.";
if (i = index(dobjstr, "."))
char_1 = dobjstr[1..i - 1];
stat_1 = dobjstr[i + 1..length(dobjstr)];
else
char_1 = "me";
stat_1 = dobjstr;
endif
e = player:env();
dobj = player:my_match_object(char_1, e);
if ($match_utils:object_match_failed(dobj, char_1, e))
return;
endif
if (i = index(iobjstr, "."))
char_2 = iobjstr[1..i - 1];
stat_2 = iobjstr[i + 1..length(iobjstr)];
else
char_2 = iobjstr;
stat_2 = stat_1;
endif
iobj = player:my_match_object(char_2, e);
if ($match_utils:object_match_failed(iobj, char_2, e, "with whom to compare stats"))
return;
endif
dobj_stat = dobj:match_stat(stat_1);
if (!valid(dobj_stat))
player:notify(dobj:grammar_sub(("Either %n %<has> no \"" + stat_1) + "\" stat, or you'll have to be more specific."));
return;
endif
iobj_stat = iobj:match_stat(stat_2);
if (!valid(iobj_stat))
player:notify(iobj:grammar_sub(("Either %n %<has> no \"" + stat_1) + "\" stat, or you'll have to be more specific."));
return;
endif
dobj_total = dobj:total(dobj_stat);
iobj_total = iobj:total(iobj_stat);
diff = abs(dobj_total - iobj_total);
rand = random(diff + 1) + 1;
if ((iobj_total < dobj_total) ? dobj_total >= (iobj_total + rand) | ((dobj_total + rand) >= iobj_total))
less = iobj;
less_stat = iobj_stat;
more = dobj;
more_stat = dobj_stat;
else
less = dobj;
less_stat = dobj_stat;
more = iobj;
more_stat = iobj_stat;
endif
dobj = less;
player:notify($string_utils:pronoun_sub(tostr("[OOC] %(dname) %<wins> the \"", more_stat:name(), "\" test against", (stat_1 == stat_2) ? "" | ((" the \"" + less_stat:name()) + "\" stat of"), " %[diname]."), more));
.
#220:0
":trusts(object, actor)";
object = args[1];
return pass(@args) || object:get_option($combat_options, "trust_healers");
.
#245:0
":att_bonus(who)";
this.attributes = $rpg.senses;
return pass(@args);
.
#273:0
if (this.name_task[1] == task_id())
return this.name_task[2];
endif
rand = {"jaw", "nose", "ear", "mouth", "crown", "eye"};
name = $list_utils:random_element(rand);
this.name_task = {task_id(), name};
return name;
.
#296:0
if (!caller_perms().wizard)
return E_PERM;
endif
for p in ($object_utils:all_properties($room))
clear_property($player_start, p);
endfor
$player_start.name = "Genesis";
$player_start.aliases = {};
$player_start.description = tostr("In the beginning, ", #2.name, " (#2) was thrust into Heaven and Earth, otherwise known as ", $player_start.name, " (", $player_start, ").");
$player_start.exits = $player_start.entrances = {};
.
#297:0
":disfunc(who)";
"If this is the user's home, move them to $limbo.";
who = args[1];
if (who.home != this)
return pass(who);
endif
move(who, this.limbo);
this:announce_all_but({who}, $string_utils:pronoun_sub(this.osuck_msg, who));
.
#297:1
":enterfunc(who)";
"Push the little fella into $limbo if disconnected.";
who = args[1];
lmb = this.limbo;
return lmb:accept(who) ? move(who, lmb) | pass(who);
.
#297:2
if (!caller_perms().wizard)
raise(E_PERM);
endif
pass();
this.limbo = $limbo;
.
#298:0
{who, action, @rest} = args;
if ($object_utils:isany(who, this.peace_exceptions))
return pass(@args);
endif
if (rest)
{dobj, @mod} = rest;
endif
if ($rpg:is_violent_action(action))
this:announce_messages("peace", who);
return E_NACC;
endif
return 0;
"Hacked to use $rpg:is_violent_action.  <Quinn; 970106-1114>";
"Hacked to support exceptions to the non-violent actions restriction";
.
#298:1
object = args[1];
if ($object_utils:isany(object, this.banned_from_here))
object:moveto(player);
return E_NACC;
else
return pass(@args);
endif
.
#298:2
if (!this:is_controllable_by(caller_perms(), caller))
raise(E_PERM);
endif
return this.(verb[5..$]) = args[1];
.
#298:3
if (!caller_perms().wizard)
return raise(E_PERM);
endif
pass(@args);
this.banned_from_here = {};
this.peace_exceptions = {};
"19981015: Added.  <Quinn>";
.
#299:0
"Put anything you want to happen to players that enter the room in here.";
return;
.
#299:1
if (this.log_number != 0)
if ($list_utils:count(this.entered_list, args[1]) == 0)
if (length(this.entered_list) >= this.log_number)
this.entered_list = $list_utils:append(this.entered_list[2..this.log_number - 1], {args[1]});
else
this.entered_list = $list_utils:append(this.entered_list, {args[1]});
endif
else
if (this.entered_list != {args[1]})
this.entered_list = $list_utils:append($list_utils:setremove_all(this.entered_list, args[1]), {args[1]});
endif
if (length(this.entered_list) > this.log_number)
this.entered_list = this.entered_list[2..length(this.entered_list)];
elseif (length(this.entered_list) >= 1)
this.entered_list = $list_utils:append($list_utils:setremove_all(this.entered_list, args[1]), {args[1]});
endif
endif
endif
fork (0)
this:enter_junk(args[1]);
endfork
return pass(@args);
.
#299:2
what = args[1];
if ((length(what) < this.min_name_length) && (pass(@args) != #-3))
if (!pass(@args).obvious)
return $failed_match;
else
return pass(@args);
endif
else
return pass(@args);
endif
.
#299:3
if (this.hide_hidden_exits)
exits = pass(@args);
returned = {};
for exit in (exits)
if (exit.obvious)
returned = {@returned, exit};
endif
endfor
return returned;
else
return pass(@args);
endif
.
#300:0
{spellname, ?caster = player} = args;
if ($object_utils:isany(caster, this.ward_exceptions))
return 1;
else
spell = (typeof(spellname) == OBJ) ? spellname | caller;
ward_list = this.warded_against_spells;
if (typeof(ward_list) != LIST)
return ((!ward_list) || (spellname in this.ward_exceptions)) || $object_utils:isany(spell, this.ward_exceptions);
else
return ((!(((ward_list == $nothing) || (spellname in ward_list)) || $object_utils:isany(spell, ward_list))) || (spellname in this.ward_exceptions)) || $object_utils:isany(spell, this.ward_exceptions);
endif
endif
.
#300:1
if (!this:is_controllable_by(caller_perms(), caller))
raise(E_PERM);
endif
return this.(verb[5..$]) = args[1];
.
#301:0
if (caller != this)
return raise(E_PERM);
endif
{what, ?default = this.denied_default, @rest} = args;
accepted = this:accepted(@rest);
denied = this:denied(@rest);
if (what in denied)
return 1;
elseif (what in accepted)
return 0;
else
while (valid(what = parent(what)))
if (what in denied)
return 1;
elseif (what in accepted)
return 0;
endif
endwhile
endif
return default;
.
#301:1
if (caller != this)
return raise(E_PERM);
else
return this.(verb);
endif
.
#301:2
what = args[1];
if (this:is_denied(what))
return 0;
else
return pass(@args);
endif
.
#301:3
result = pass(@args);
if ((!result) && caller_perms().wizard)
return raise(E_PERM);
"... if we don't do that, the result of the accept will be ignored because the move task is running with wiz perms ...";
else
return result;
endif
.
#301:4
"Usage:   @accept <object> in <room>";
if (((player != caller) || (!$perm_utils:controls(player, this))) || (!$rpg:trusted(player)))
player:tell("Sorry, you can't do that.");
return;
endif
su = $string_utils;
if (!dobjstr)
player:tell("Accepted list: ", this.accepted ? su:nn(this.accepted) | "empty");
return;
endif
object = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(object, dobjstr))
return;
endif
if (!this:is_denied(object, -1))
if (object in this.accepted)
player:tell(su:nn(object), " is already accepted.");
else
player:tell(su:nn(object), " is not in the accepted list but one of its ancestor is, so no need to add it to the list.");
endif
elseif (index = object in this.denied)
this.denied = setremove(this.denied, object);
player:tell("Removed ", su:nn(object), " from the deny list. If you want to add it to the accepted list, do: '", verb, " ", argstr, "' one more time.");
else
this.accepted = setadd(this.accepted, object);
player:tell(su:nn(object), " is now accepted.");
endif
.
#301:5
"Usage:   @deny <object> in <room>";
if (((player != caller) || (!$perm_utils:controls(player, this))) || (!$rpg:trusted(player)))
player:tell("Sorry, you can't do that.");
return;
endif
su = $string_utils;
if (!dobjstr)
player:tell("Deny list: ", this.denied ? su:nn(this.denied) | "empty");
return;
endif
object = player:my_match_object(dobjstr);
if ($command_utils:object_match_failed(object, dobjstr))
return;
endif
if (this:is_denied(object, 0))
if (object in this.denied)
player:tell(su:nn(object), " is already denied.");
else
player:tell(su:nn(object), " is not in the denied list but one of its ancestor is, so no need to add it to the list.");
endif
elseif (object in this.accepted)
this.accepted = setremove(this.accepted, object);
player:tell("Removed ", su:nn(object), " from the accepted list. If you want to add it to the denied list, do: '", verb, " ", argstr, "' one more time.");
else
this.denied = setadd(this.denied, object);
player:tell(su:nn(object), " added to the denied list.");
endif
.
#302:0
":is_writable_by(OBJ <who> [, OBJ <caller>]) => Is 'who' allowed to edit this object?";
"NOTE: A character who may WRITE an object may also READ and CONTROL it.";
"Enhanced to allow a kind of sharing/multiple ownership...";
who = args[1];
return pass(@args) || ((this.GMs_are_controllers || (who in this.controllers)) && $rpg:trusted(who));
"We don't want a PC to be able to be a controller by mistake, by accident or by cheat :)";
.
#302:1
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
value = args[1];
if (typeof(value) != LIST)
raise(E_INVARG);
endif
return this.(verb[5..$]) = value;
else
raise(E_PERM);
endif
.
#302:2
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
return this.(verb[5..$]) = args[1];
else
raise(E_PERM);
endif
.
#302:3
":help_msg  => do the dirty work for help here";
help = {@this.user_help_msg, "----"};
if ($rpg:trusted(player) && (add_help = `pass(@args) ! E_VERBNF => this.help_msg'))
if (typeof(add_help) == LIST)
help = {@help, @add_help, "----"};
else
help = {@help, tostr(add_help), "----"};
endif
endif
return help;
.
#307:0
{caller, perms, ?raise = 1} = args;
if ((caller == this) || ((caller == player) && this:feature_ok(player)))
return 1;
else
return raise ? raise(E_PERM) | E_PERM;
endif
.
#307:1
{?who = player} = args;
if ((!(caller in {this, this.admin_fo})) && ((who != caller) || (!this:feature_ok(who))))
raise(E_PERM);
endif
this.feature_users = setadd(this.feature_users, who);
.
#307:2
{who} = args;
if (!(caller in {this, who, this.admin_fo}))
raise(E_PERM);
endif
this.feature_users = setremove(this.feature_users, who);
.
#307:3
{who} = args;
return $perm_utils:controls(who, this) || (who in this.fo_admins);
.
#307:4
if ((caller != this) && (caller != this.admin_fo))
raise(E_PERM);
endif
{user} = args;
this.accept_users = setadd(this.accept_users, user);
.
#307:5
if ((caller != this) && (caller != this.admin_fo))
raise(E_PERM);
endif
{user} = args;
this.accept_users = setremove(this.accept_users, user);
.
#307:6
{?user = player} = args;
if (location = $object_utils:has_verb(caller, vname = callers()[1][2]))
user:tell_lines($code_utils:verb_documentation(@location, vname));
endif
.
#307:7
who = args[1];
if (who in this.fo_admins)
return 1;
elseif (this.accept_GMs && $rpg:trusted(who))
return !$object_utils:isany(who, this.deny_users);
else
return (!(!`$object_utils:isany(who, this.accept_users, 0) ! E_TYPE => this.accept_users')) && (!$object_utils:isany(who, this.deny_users));
endif
.
#307:8
if ((caller != $public.admin_secure_feature) && (!this:is_controllable_by(caller_perms(), caller)))
raise(E_PERM);
endif
return this.(verb[5..$]) = args[1];
.
#307:9
if ((caller != this) && (!$perm_utils:controls(caller_perms(), this)))
return raise(E_PERM);
endif
try
"remove the FO from its users .features list...";
for user in (this.feature_users)
`user:remove_feature(this) ! ANY';
endfor
finally
return pass(@args);
endtry
.
#307:10
if ((caller != $public.admin_secure_feature) && (!this:is_controllable_by(caller_perms(), caller)))
raise(E_PERM);
endif
value = args[1];
if (typeof(value) != LIST)
raise(E_TYPE, "Should be a list.");
else
return this.(verb[5..$]) = args[1];
endif
.
#308:0
":is_abuser(pc)";
"Return true if the given PC has been tagged as an 'abuser', disallowing their use of privileged commands.";
{user} = args;
if (user in this.abusers)
return 1;
endif
if ($rpg:trusted(user))
return 0;
endif
return $justice:is_criminal(user);
.
#308:1
":forbidden_command_msg(STR cmd)";
if (!args)
cmd = "that command";
else
cmd = tostr("`", args[1], "`");
endif
return strsub(this.forbidden_command_msg, "$cmd", cmd);
.
#308:2
":notify_user(user, msg)";
if (caller != this)
return E_PERM;
endif
args[1]:notify(args[2]);
.
#308:3
":allow_privileged_command(OBJ, STR command)";
"Return true if the given user is allowed to issue a privileged command. Otherwise, inform the player why the privilege has been revoked and return false.";
pc = args[1];
gm = $rpg:trusted(pc);
if (gm)
"...a GM is a privileged people..";
elseif (unc = pc:unconscious())
this:notify_user(pc, tostr("You're unconscious.  Certain privileged commands are unavailable while unconscious", (unc > 0) ? "." | ".  Type `wake up` to return to consciousness."));
return E_PERM;
elseif (pc.ic > 0)
"allow NPC (ic < 0) to @edit, @move, etc";
this:notify_user(pc, "You are in-character.  You cannot use certain privileged commands while IC.  Type `@ooc` to remedy the situation.");
return E_PERM;
elseif (pc:in_combat())
this:notify_user(pc, "You are involved in combat here.  You cannot use certain privileged commands while in combat.  Try fleeing first.");
return E_PERM;
elseif (this:is_abuser(pc))
this:notify_user(pc, this:forbidden_command_msg(@listdelete(args, 1)));
return E_PERM;
endif
return 1;
.
0 clocks
0 queued tasks
0 suspended tasks
1 active connections with listeners
2 0
