Imported from archive.
authorJeremy Stanley <fungi@yuggoth.org>
Sun, 11 Sep 2005 20:45:34 +0000 (20:45 +0000)
committerJeremy Stanley <fungi@yuggoth.org>
Sun, 11 Sep 2005 20:45:34 +0000 (20:45 +0000)
* command (command:create, command:delete, command:destroy)
(command:set), mudpy.py (command_create, command_delete)
(command_destroy, command_set): Added the admin commands create,
delete, destroy and set, as a foundation for future on-line
creation/World-building systems.

* katarsis: Included sample data from the Katarsis project.

* mudpy.py (Element.delete): Renamed to destroy and added a new
delete method used for removing individual facets, making them
consistent with the new admin commands.
(User.delete): Renamed to destroy for consistency with the Element
class' method names.
(User.can_run): New method acting as a rudimentary access control
system for commands flagged with an administrative bool facet in
command elements.
(command_help): Modified to filter out commands the user is
disallowed from running, and now highlights administrative command
names when they're available.
(handler_active): Standardized on command_name instead of command.

command
katarsis [new file with mode: 0644]
menu
mudpy.conf
mudpy.py

diff --git a/command b/command
index ded4f7e..ded5b45 100644 (file)
--- a/command
+++ b/command
@@ -1,33 +1,60 @@
 [control]
 read_only = yes
 
-[command:quit]
-action = command_quit(user, command, parameters)
-description = Leave Katarsis.
-help = This will save your account and disconnect your client connection.
+[command:create]
+action = command_create(user, parameters)
+administrative = yes
+description = Create a new element in the universe.
+help = Ways to create an element:$(eol)$(eol)   create actor:fred$(eol)   create other:garply foo/bar/baz
+
+[command:delete]
+action = command_delete(user, parameters)
+administrative = yes
+description = Delete an existing facet from an element.
+help = You can delete any facet of an element as follows:$(eol)$(eol)   delete location:boardroom terrain
+
+[command:destroy]
+action = command_destroy(user, parameters)
+administrative = yes
+description = Destroy an existing element in the universe.
+help = You can destroy any element in the universe as follows:$(eol)$(eol)   destroy prop:dagger
 
 [command:halt]
-action = command_halt(user, command, parameters)
+action = command_halt(user, parameters)
+administrative = yes
 description = Shut down the world.
 help = This will save all active accounts, disconnect all clients and stop the entire program.
 
 [command:help]
-action = command_help(user, command, parameters)
+action = command_help(user, parameters)
 description = List commands or get help on one.
 help = This will list all comand words available to you along with a brief description or, alternatively, give you detailed information on one command.
 
-[command:show]
-action = command_show(user, command, parameters)
-description = Show program data.
-help = For now, this is used to show things like "avatars", "time" and "universe".
+[command:quit]
+action = user.state = "main_utility"
+description = Leave Katarsis.
+help = This will save your account and disconnect your client connection.
 
 [command:reload]
-action = command_reload(user, command, parameters)
+action = command_reload(user)
+administrative = yes
 description = Reload code modules and data.
 help = This will reload all python code modules, reload configuration files and re-read data files.
 
 [command:say]
-action = command_say(user, command, parameters)
+action = command_say(user, parameters)
 description = State something out loud.
 help = This allows you to speak to other characters within the same room. If you end your sentence with specific punctuation, the aparent speech action (ask, exclaim, et cetera) will be adapted accordingly. It will also add punctuation and capitalize your message where needed.
 
+[command:set]
+action = command_set(user, parameters)
+administrative = yes
+description = Set a facet of an element.
+help = Invoke it like this:$(eol)$(eol)   set actor:dominique description You see nothing special.
+
+[command:show]
+action = command_show(user, parameters)
+administrative = yes
+description = Show element data.
+help = Here are the possible incantations:$(eol)$(eol)   show categories$(eol)   show category actor$(eol)   show element location:1:2:3:4$(eol)   show files$(eol)   show time
+
diff --git a/katarsis b/katarsis
new file mode 100644 (file)
index 0000000..5c06393
--- /dev/null
+++ b/katarsis
@@ -0,0 +1,559 @@
+[location:101]
+pickproof_n = yes
+name = East Envy Avenue
+closeable_n = yes
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Envy Avenue runs East and West here while a merchant's booth lies to the North.
+terrain = city
+link_e = location:102
+keywords_n = ['booth']
+description_n = This is a simple merchant's booth.
+link_w = location:161
+link_n = location:159
+
+[location:106]
+keywords_e = ['shop']
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Gluttony Street runs North and South here while a storefront beckons to the East.
+closeable_e = yes
+pickproof_e = yes
+terrain = city
+link_e = location:152
+name = North Gluttony Street
+description_e = This is the entrance to a shop.
+link_s = location:105
+link_n = location:107
+
+[location:118]
+pickproof_n = yes
+name = East Avarice Avenue
+closeable_n = yes
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Avarice Avenue runs East and West here while a merchant's booth lies to the North.
+terrain = city
+link_e = location:119
+keywords_n = ['booth']
+description_n = This is a simple merchant's booth.
+link_w = location:116
+link_n = location:147
+
+[location:159]
+link_s = location:101
+terrain = inside
+name = Tanner's Booth
+description = The smell of leather goods fills your nostrils. Tanned, finished and embroidered leather is hung and piled everywhere.
+
+[location:158]
+name = Cobbler's Booth
+terrain = inside
+closeable_s = yes
+keywords_s = ['booth']
+link_s = location:129
+pickproof_s = yes
+description = Shoes, shoes and more shoes... You need footwear, we have footwear. Look around, see what you like. Best prices in town! So you gonna buy something, stranger?
+
+[location:104]
+keywords_e = ['gate']
+name = East Market Gate
+closeable_e = yes
+pickproof_e = yes
+terrain = city
+link_e = location:0
+description_e = This gate leads from the market to the city proper.
+link_w = location:103
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the West, Envy Avenue heads into the market while the rest of the city lies trough the gate to the East.
+
+[location:151]
+terrain = inside
+name = New BuildWalk Room
+link_e = location:105
+description = This unfinished room was created by Imp.
+
+[location:150]
+terrain = inside
+link_w = location:122
+name = New BuildWalk Room
+description = This unfinished room was created by Imp.
+
+[location:153]
+link_n = location:108
+terrain = inside
+description = This unfinished room was created by Imp.
+name = New BuildWalk Room
+
+[location:152]
+terrain = inside
+link_w = location:106
+name = New BuildWalk Room
+description = This unfinished room was created by Imp.
+
+[location:155]
+terrain = inside
+link_w = location:114
+name = New BuildWalk Room
+description = This unfinished room was created by Imp.
+
+[location:154]
+link_s = location:109
+terrain = inside
+name = New BuildWalk Room
+description = This unfinished room was created by Imp.
+
+[location:157]
+link_n = location:130
+terrain = inside
+description = This booth sells cloth garments of every description. Fine silks and linens line the walls, draped from every protrusion and stacked on every surface.
+name = Tailor's Booth
+
+[location:156]
+terrain = inside
+name = New BuildWalk Room
+link_e = location:115
+description = This unfinished room was created by Imp.
+
+[location:115]
+link_n = location:114
+terrain = city
+name = South Anger Street
+keywords_w = ['booth']
+closeable_w = yes
+description_w = This is a simple merchant's booth.
+link_s = location:116
+pickproof_w = yes
+link_w = location:156
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Anger Street runs North and South here while a merchant's booth lies to the West.
+
+[location:114]
+keywords_e = ['booth']
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Anger Street runs North and South here while a merchant's booth lies to the East.
+closeable_e = yes
+pickproof_e = yes
+terrain = city
+link_e = location:155
+name = South Anger Street
+description_e = This is a simple merchant's booth.
+link_s = location:115
+link_n = location:161
+
+[location:117]
+description_s = This gate leads from the market to the city proper.
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the North, Anger Street heads into the market while the rest of the city lies trough the gate to the South.
+terrain = city
+name = South Market Gate
+closeable_s = yes
+keywords_s = ['gate']
+link_s = location:0
+pickproof_s = yes
+link_n = location:116
+
+[location:116]
+link_n = location:115
+terrain = city
+link_e = location:118
+name = South Anger and Avarice
+link_s = location:117
+link_w = location:123
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Avarice Avenue while Anger Street runs North and South.
+
+[location:111]
+pickproof_n = yes
+name = North Market Gate
+closeable_n = yes
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the South, Anger Street heads into the market while the rest of the city lies trough the gate to the North.
+terrain = city
+keywords_n = ['gate']
+link_s = location:110
+description_n = This gate leads from the market to the city proper.
+link_n = location:0
+
+[location:110]
+link_n = location:111
+terrain = city
+link_e = location:109
+name = North Anger and Sloth
+link_s = location:112
+link_w = location:135
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Sloth Avenue while Anger Street runs North and South.
+
+[location:113]
+link_n = location:112
+terrain = city
+name = North Anger Street
+keywords_w = ['booth']
+closeable_w = yes
+description_w = This is a simple merchant's booth.
+link_s = location:161
+pickproof_w = yes
+link_w = location:137
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Anger Street runs North and South here while a merchant's booth lies to the West.
+
+[location:112]
+keywords_e = ['booth']
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Anger Street runs North and South here while a merchant's booth lies to the East.
+closeable_e = yes
+pickproof_e = yes
+terrain = city
+link_e = location:138
+name = North Anger Street
+description_e = This is a simple merchant's booth.
+link_s = location:113
+link_n = location:110
+
+[location:137]
+terrain = inside
+name = New BuildWalk Room
+link_e = location:113
+description = This unfinished room was created by Imp.
+
+[location:136]
+description_w = This gate leads from the market to the city proper.
+terrain = city
+link_e = location:128
+name = West Market Gate
+keywords_w = ['gate']
+closeable_w = yes
+pickproof_w = yes
+link_w = location:0
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East, Envy Avenue heads into the market while the rest of the city lies trough the gate to the West.
+
+[location:135]
+description_s = This is a simple merchant's booth.
+name = West Sloth Avenue
+terrain = city
+link_e = location:110
+closeable_s = yes
+keywords_s = ['booth']
+link_s = location:139
+link_w = location:134
+pickproof_s = yes
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Sloth Avenue runs East and West here while a merchant's booth lies to the South.
+
+[location:134]
+pickproof_n = yes
+name = West Sloth Avenue
+closeable_n = yes
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Sloth Avenue runs East and West here while a storefront beckons to the North.
+terrain = city
+link_e = location:135
+keywords_n = ['shop']
+description_n = This is the entrance to a shop.
+link_w = location:133
+link_n = location:140
+
+[location:119]
+description_s = This is the entrance to a shop.
+name = East Avarice Avenue
+terrain = city
+link_e = location:120
+closeable_s = yes
+keywords_s = ['shop']
+link_s = location:148
+link_w = location:118
+pickproof_s = yes
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Avarice Avenue runs East and West here while a storefront beckons to the South.
+
+[location:132]
+keywords_e = ['booth']
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Lust Street runs North and South here while a merchant's booth lies to the East.
+closeable_e = yes
+pickproof_e = yes
+terrain = city
+link_e = location:141
+name = North Lust Street
+description_e = This is a simple merchant's booth.
+link_s = location:131
+link_n = location:133
+
+[location:131]
+link_n = location:132
+terrain = city
+name = North Lust Street
+keywords_w = ['shop']
+closeable_w = yes
+description_w = This is the entrance to a shop.
+link_s = location:128
+pickproof_w = yes
+link_w = location:142
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Lust Street runs North and South here while a storefront beckons to the West.
+
+[location:130]
+description_s = This is a simple merchant's booth.
+name = West Envy Avenue
+terrain = city
+link_e = location:161
+closeable_s = yes
+keywords_s = ['booth']
+link_s = location:157
+link_w = location:129
+pickproof_s = yes
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Envy Avenue runs East and West here while a merchant's booth lies to the South.
+
+[location:105]
+link_n = location:106
+terrain = city
+name = North Gluttony Street
+keywords_w = ['booth']
+closeable_w = yes
+description_w = This is a simple merchant's booth.
+link_s = location:103
+pickproof_w = yes
+link_w = location:151
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Gluttony Street runs North and South here while a merchant's booth lies to the West.
+
+[location:138]
+terrain = inside
+link_w = location:112
+name = New BuildWalk Room
+description = This unfinished room was created by Imp.
+
+[location:148]
+link_n = location:119
+terrain = inside
+description = This unfinished room was created by Imp.
+name = New BuildWalk Room
+
+[location:149]
+terrain = inside
+name = New BuildWalk Room
+link_e = location:121
+description = This unfinished room was created by Imp.
+
+[location:146]
+link_n = location:123
+terrain = inside
+description = This unfinished room was created by Imp.
+name = New BuildWalk Room
+
+[location:147]
+link_s = location:118
+terrain = inside
+name = New BuildWalk Room
+description = This unfinished room was created by Imp.
+
+[location:144]
+terrain = inside
+name = New BuildWalk Room
+link_e = location:126
+description = This unfinished room was created by Imp.
+
+[location:145]
+link_s = location:124
+terrain = inside
+name = New BuildWalk Room
+description = This unfinished room was created by Imp.
+
+[location:142]
+terrain = inside
+name = New BuildWalk Room
+link_e = location:131
+description = This unfinished room was created by Imp.
+
+[location:143]
+terrain = inside
+link_w = location:127
+name = New BuildWalk Room
+description = This unfinished room was created by Imp.
+
+[location:140]
+link_s = location:134
+terrain = inside
+name = New BuildWalk Room
+description = This unfinished room was created by Imp.
+
+[location:141]
+terrain = inside
+link_w = location:132
+name = New BuildWalk Room
+description = This unfinished room was created by Imp.
+
+[location:160]
+link_n = location:102
+terrain = inside
+description = You find yourself amidst cold, hard steel and iron equipment of every possible description. A slight breeze brings tinkle and clank sounds from all around you.
+name = Blacksmith's Booth
+
+[location:161]
+link_n = location:113
+terrain = city
+link_e = location:101
+name = Pride Square
+link_s = location:114
+link_w = location:130
+link_u = location:100
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Envy Avenue while Anger Street runs North and South. A short flight of stairs leads up to the bandstand.
+
+[location:108]
+description_s = This is a simple merchant's booth.
+name = East Sloth Avenue
+terrain = city
+link_e = location:107
+closeable_s = yes
+keywords_s = ['booth']
+link_s = location:153
+link_w = location:109
+pickproof_s = yes
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Sloth Avenue runs East and West here while a merchant's booth lies to the South.
+
+[location:109]
+pickproof_n = yes
+name = East Sloth Avenue
+closeable_n = yes
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Sloth Avenue runs East and West here while a storefront beckons to the North.
+terrain = city
+link_e = location:108
+keywords_n = ['shop']
+description_n = This is the entrance to a shop.
+link_w = location:110
+link_n = location:154
+
+[location:128]
+link_n = location:131
+terrain = city
+link_e = location:129
+name = Lust and West Envy
+link_s = location:127
+link_w = location:136
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Envy Avenue while Lust Street runs North and South.
+
+[location:139]
+link_n = location:135
+terrain = inside
+description = This unfinished room was created by Imp.
+name = New BuildWalk Room
+
+[location:102]
+description_s = This is a simple merchant's booth.
+name = East Envy Avenue
+terrain = city
+link_e = location:103
+closeable_s = yes
+keywords_s = ['booth']
+link_s = location:160
+link_w = location:101
+pickproof_s = yes
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Envy Avenue runs East and West here while a merchant's booth lies to the South.
+
+[location:103]
+link_n = location:105
+terrain = city
+link_e = location:104
+name = Gluttony and East Envy
+link_s = location:122
+link_w = location:102
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Envy Avenue while Gluttony Street runs North and South.
+
+[location:100]
+terrain = inside
+description = The stand is currently not in use for a performance, but shoppers gather here to rest and chat with one another. A pleasantly cool breeze coupled with a nice view of the market make this a good place to relax and recover energy.
+name = The Bandstand
+link_d = location:161
+
+[location:127]
+keywords_e = ['booth']
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Lust Street runs North and South here while a merchant's booth lies to the East.
+closeable_e = yes
+pickproof_e = yes
+terrain = city
+link_e = location:143
+name = South Lust Street
+description_e = This is a simple merchant's booth.
+link_s = location:126
+link_n = location:128
+
+[location:120]
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the West lies Avarice Avenue while Gluttony Street leads North.
+terrain = city
+name = The Corner of Gluttony and Avarice
+link_w = location:119
+link_n = location:121
+
+[location:121]
+link_n = location:122
+terrain = city
+name = South Gluttony Street
+keywords_w = ['booth']
+closeable_w = yes
+description_w = This is a simple merchant's booth.
+link_s = location:120
+pickproof_w = yes
+link_w = location:149
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Gluttony Street runs North and South here while a merchant's booth lies to the West.
+
+[location:122]
+keywords_e = ['shop']
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Gluttony Street runs North and South here while a storefront beckons to the East.
+closeable_e = yes
+pickproof_e = yes
+terrain = city
+link_e = location:150
+name = South Gluttony Street
+description_e = This is the entrance to a shop.
+link_s = location:121
+link_n = location:103
+
+[location:123]
+description_s = This is the entrance to a shop.
+name = West Avarice Avenue
+terrain = city
+link_e = location:116
+closeable_s = yes
+keywords_s = ['shop']
+link_s = location:146
+link_w = location:124
+pickproof_s = yes
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Avarice Avenue runs East and West here while a storefront beckons to the South.
+
+[location:124]
+pickproof_n = yes
+name = West Avarice Avenue
+closeable_n = yes
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Avarice Avenue runs East and West here while a merchant's booth lies to the North.
+terrain = city
+link_e = location:123
+keywords_n = ['booth']
+description_n = This is a simple merchant's booth.
+link_w = location:125
+link_n = location:145
+
+[location:133]
+name = The Corner of Lust and Sloth
+terrain = city
+link_e = location:134
+link_s = location:132
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East lies Sloth Avenue while Lust Street leads South.
+
+[location:129]
+pickproof_n = yes
+name = West Envy Avenue
+closeable_n = yes
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Envy Avenue runs East and West here while a merchant's booth lies to the North.
+terrain = city
+link_e = location:130
+keywords_n = ['booth']
+description_n = This is a simple merchant's booth.
+link_w = location:128
+link_n = location:158
+
+[location:125]
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East lies Avarice Avenue while Lust Street leads North.
+terrain = city
+link_e = location:124
+name = The Corner of Lust and Avarice
+link_n = location:126
+
+[location:107]
+name = The Corner of Gluttony and Sloth
+terrain = city
+link_s = location:106
+link_w = location:108
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the West lies Sloth Avenue while Gluttony Street leads South.
+
+[location:126]
+link_n = location:127
+terrain = city
+name = South Lust Street
+keywords_w = ['shop']
+closeable_w = yes
+description_w = This is the entrance to a shop.
+link_s = location:125
+pickproof_w = yes
+link_w = location:144
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Lust Street runs North and South here while a storefront beckons to the West.
+
diff --git a/menu b/menu
index ed46e9c..bc443cd 100644 (file)
--- a/menu
+++ b/menu
@@ -16,8 +16,8 @@ prompt = Whom would you like to awaken?
 prompt = >
 
 [menu:checking_new_account_name]
-action_d = user.account.delete()
-action_g = user.account.delete()
+action_d = user.account.destroy()
+action_g = user.account.destroy()
 branch_d = disconnecting
 branch_g = entering_account_name
 branch_n = entering_new_password
@@ -58,7 +58,7 @@ description = Your new avatar needs a name. This will be the name with which $(t
 prompt = Choose a name for $(tpop):
 
 [menu:delete_account]
-action_y = user.delete()
+action_y = user.destroy()
 branch_n = main_utility
 branch_y = disconnecting
 choice_n = no, don't delete my account
index c349956..ebc81e6 100644 (file)
@@ -1,5 +1,6 @@
 [control]
 default_files = {"account": "account", "actor": "actor", "command": "command", "internal": "internal", "location": "location", "menu": "menu", "other": "other" }
+include_files = katarsis
 private_files = account
 read_only = yes
 
index c6a4bde..b25340c 100644 (file)
--- a/mudpy.py
+++ b/mudpy.py
@@ -34,12 +34,17 @@ class Element:
                        DataFile(self.origin, universe)
                if not universe.files[self.origin].data.has_section(self.key):
                        universe.files[self.origin].data.add_section(self.key)
-       def delete(self):
-               log("Deleting: " + self.key + ".")
+       def destroy(self):
+               """Remove an element from the universe and destroy it."""
+               log("Destroying: " + self.key + ".")
                universe.files[self.origin].data.remove_section(self.key)
                del universe.categories[self.category][self.subkey]
                del universe.contents[self.key]
                del self
+       def delete(self, facet):
+               """Delete a facet from the element."""
+               if universe.files[self.origin].data.has_option(self.key, facet):
+                       universe.files[self.origin].data.remove_option(self.key, facet)
        def facets(self):
                """Return a list of facets for this element."""
                return universe.files[self.origin].data.options(self.key)
@@ -118,7 +123,7 @@ class DataFile:
                        DataFile(include_file, universe)
        def save(self):
                if ( self.data.sections() or exists(self.filename) ) and not ( self.data.has_option("control", "read_only") and self.data.getboolean("control", "read_only") ):
-                       if not exists(dirname(self.filename)): makedirs(dirname)
+                       if not exists(dirname(self.filename)): makedirs(dirname(self.filename))
                        file_descriptor = file(self.filename, "w")
                        if self.filename in universe.private_files and oct(S_IMODE(stat(self.filename)[ST_MODE])) != 0600:
                                chmod(self.filename, 0600)
@@ -404,6 +409,24 @@ class User:
                                # put on the end of the queue
                                self.input_queue.append(line)
 
+       def can_run(self, command):
+               """Check if the user can run this command object."""
+
+               # has to be in the commands category
+               if command not in universe.categories["command"].values(): result = False
+
+               # administrators can run any command
+               elif self.account.getboolean("administrator"): result = True
+
+               # everyone can run non-administrative commands
+               elif not command.getboolean("administrative"): result = True
+
+               # otherwise the command cannot be run by this user
+               else: result = False
+
+               # pass back the result
+               return result
+
        def new_avatar(self):
                """Instantiate a new, unconfigured avatar for this user."""
                counter = 0
@@ -416,15 +439,15 @@ class User:
        def delete_avatar(self, avatar):
                """Remove an avatar from the world and from the user's list."""
                if self.avatar is universe.contents[avatar]: self.avatar = None
-               universe.contents[avatar].delete()
+               universe.contents[avatar].destroy()
                avatars = self.account.getlist("avatars")
                avatars.remove(avatar)
                self.account.set("avatars", avatars)
 
-       def delete(self):
-               """Delete the user and associated avatars."""
+       def destroy(self):
+               """Destroy the user and associated avatars."""
                for avatar in self.account.getlist("avatars"): self.delete_avatar(avatar)
-               self.account.delete()
+               self.account.destroy()
 
        def list_avatar_names(self):
                """List names of assigned avatars."""
@@ -616,6 +639,10 @@ def replace_macros(user, text, is_input=False):
 
        return text
 
+def escape_macros(text):
+       """Escapes replacement macros in text."""
+       return text.replace("$(", "$_(")
+
 def check_time(frequency):
        """Check for a factor of the current increment count."""
        if type(frequency) is str:
@@ -948,7 +975,7 @@ def handler_entering_new_password(user):
        # too many tries, so adios
        else:
                user.send("$(eol)$(red)Too many failed password attempts...$(nrm)$(eol)")
-               user.account.delete()
+               user.account.destroy()
                user.state = "disconnecting"
 
 def handler_verifying_new_password(user):
@@ -974,7 +1001,7 @@ def handler_verifying_new_password(user):
        # otherwise, sayonara
        else:
                user.send("$(eol)$(red)Too many failed password attempts...$(nrm)$(eol)")
-               user.account.delete()
+               user.account.destroy()
                user.state = "disconnecting"
 
 def handler_active(user):
@@ -985,22 +1012,26 @@ def handler_active(user):
 
        # split out the command (first word) and parameters (everything else)
        if input_data.find(" ") > 0:
-               command, parameters = input_data.split(" ", 1)
+               command_name, parameters = input_data.split(" ", 1)
        else:
-               command = input_data
+               command_name = input_data
                parameters = ""
 
        # lowercase the command
-       command = command.lower()
+       command_name = command_name.lower()
 
        # the command matches a command word for which we have data
-       if command in universe.categories["command"]:
-               exec(universe.categories["command"][command].get("action"))
+       if command_name in universe.categories["command"]:
+               command = universe.categories["command"][command_name]
+       else: command = None
 
-       # no data matching the entered command word
-       elif command: command_error(user, command, parameters)
+       # if it's allowed, do it
+       if user.can_run(command): exec(command.get("action"))
 
-def command_halt(user, command="", parameters=""):
+       # otherwise, give an error
+       elif command_name: command_error(user, input_data)
+
+def command_halt(user, parameters):
        """Halt the world."""
 
        # see if there's a message or use a generic one
@@ -1014,7 +1045,7 @@ def command_halt(user, command="", parameters=""):
        # set a flag to terminate the world
        universe.terminate_world = True
 
-def command_reload(user, command="", parameters=""):
+def command_reload(user):
        """Reload all code modules, configs and data."""
 
        # let the user know and log
@@ -1024,11 +1055,7 @@ def command_reload(user, command="", parameters=""):
        # set a flag to reload
        universe.reload_modules = True
 
-def command_quit(user, command="", parameters=""):
-       """Quit the world."""
-       user.state = "main_utility"
-
-def command_help(user, command="", parameters=""):
+def command_help(user, parameters):
        """List available commands and provide help for commands."""
 
        # did the user ask for help on a specific command word?
@@ -1036,15 +1063,22 @@ def command_help(user, command="", parameters=""):
 
                # is the command word one for which we have data?
                if parameters in universe.categories["command"]:
+                       command = universe.categories["command"][parameters]
+               else: command = None
+
+               # only for allowed commands
+               if user.can_run(command):
 
                        # add a description if provided
-                       description = universe.categories["command"][parameters].get("description")
+                       description = command.get("description")
                        if not description:
                                description = "(no short description provided)"
-                       output = "$(grn)" + parameters + "$(nrm) - " + description + "$(eol)$(eol)"
+                       if command.getboolean("administrative"): output = "$(red)"
+                       else: output = "$(grn)"
+                       output += parameters + "$(nrm) - " + description + "$(eol)$(eol)"
 
                        # add the help text if provided
-                       help_text = universe.categories["command"][parameters].get("help")
+                       help_text = command.get("help")
                        if not help_text:
                                help_text = "No help is provided for this command."
                        output += help_text
@@ -1061,16 +1095,20 @@ def command_help(user, command="", parameters=""):
                sorted_commands = universe.categories["command"].keys()
                sorted_commands.sort()
                for item in sorted_commands:
-                       description = universe.categories["command"][item].get("description")
-                       if not description:
-                               description = "(no short description provided)"
-                       output += "   $(grn)" + item + "$(nrm) - " + description + "$(eol)"
+                       command = universe.categories["command"][item]
+                       if user.can_run(command):
+                               description = command.get("description")
+                               if not description:
+                                       description = "(no short description provided)"
+                               if command.getboolean("administrative"): output += "   $(red)"
+                               else: output += "   $(grn)"
+                               output += item + "$(nrm) - " + description + "$(eol)"
                output += "$(eol)Enter \"help COMMAND\" for help on a command named \"COMMAND\"."
 
        # send the accumulated output to the user
        user.send(output)
 
-def command_say(user, command="", parameters=""):
+def command_say(user, parameters):
        """Speak to others in the same room."""
 
        # check for replacement macros
@@ -1111,44 +1149,120 @@ def command_say(user, command="", parameters=""):
 
                # tell the room
                # TODO: we won't be using broadcast once there are actual rooms
-               broadcast(user.account.get("name") + " " + action + "s, \"" + message + "\"")
+               broadcast(user.avatar.get("name") + " " + action + "s, \"" + message + "\"")
 
        # there was no message
        else:
                user.send("What do you want to say?")
 
-def command_show(user, command="", parameters=""):
+def command_show(user, parameters):
        """Show program data."""
-       if parameters == "avatars":
-               message = "These are the avatars managed by your account:$(eol)"
-               avatars = user.list_avatar_names()
-               avatars.sort()
-               for avatar in avatars: message += "$(eol)   $(grn)" + avatar + "$(nrm)"
-       elif parameters == "files":
-               message = "These are the current files containing the universe:$(eol)"
-               keys = universe.files.keys()
-               keys.sort()
-               for key in keys: message += "$(eol)   $(grn)" + key + "$(nrm)"
-       elif parameters == "universe":
-               message = "These are the current elements in the universe:$(eol)"
-               keys = universe.contents.keys()
-               keys.sort()
-               for key in keys: message += "$(eol)   $(grn)" + key + "$(nrm)"
-       elif parameters == "time":
-               message = universe.categories["internal"]["counters"].get("elapsed") + " increments elapsed since the world was created."
-       elif parameters: message = "I don't know what \"" + parameters + "\" is."
-       else: message = "What do you want to show?"
+       message = ""
+       if parameters.find(" ") < 1:
+               if parameters == "time":
+                       message = universe.categories["internal"]["counters"].get("elapsed") + " increments elapsed since the world was created."
+               elif parameters == "categories":
+                       message = "These are the element categories:$(eol)"
+                       categories = universe.categories.keys()
+                       categories.sort()
+                       for category in categories: message += "$(eol)   $(grn)" + category + "$(nrm)"
+               elif parameters == "files":
+                       message = "These are the current files containing the universe:$(eol)"
+                       filenames = universe.files.keys()
+                       filenames.sort()
+                       for filename in filenames: message += "$(eol)   $(grn)" + filename + "$(nrm)"
+               else: message = ""
+       else:
+               arguments = parameters.split()
+               if arguments[0] == "category":
+                       if arguments[1] in universe.categories:
+                               message = "These are the elements in the \"" + arguments[1] + "\" category:$(eol)"
+                               elements = universe.categories[arguments[1]].keys()
+                               elements.sort()
+                               for element in elements:
+                                       message += "$(eol)   $(grn)" + universe.categories[arguments[1]][element].key + "$(nrm)"
+               elif arguments[0] == "element":
+                       if arguments[1] in universe.contents:
+                               message = "These are the properties of the \"" + arguments[1] + "\" element:$(eol)"
+                               element = universe.contents[arguments[1]]
+                               facets = element.facets()
+                               facets.sort()
+                               for facet in facets:
+                                       message += "$(eol)   $(grn)" + facet + ": $(red)" + escape_macros(element.get(facet)) + "$(nrm)"
+       if not message:
+               if parameters: message = "I don't know what \"" + parameters + "\" is."
+               else: message = "What do you want to show?"
+       user.send(message)
+
+def command_create(user, parameters):
+       """Create an element if it does not exist."""
+       if not parameters: message = "You must at least specify an element to create."
+       else:
+               arguments = parameters.split()
+               if len(arguments) == 1: arguments.append("")
+               if len(arguments) == 2:
+                       element, filename = arguments
+                       if element in universe.contents: message = "The \"" + element + "\" element already exists."
+                       else:
+                               message = "You create \"" + element + "\" within the universe."
+                               logline = user.account.get("name") + " created an element: " + element
+                               if filename:
+                                       logline += " in file " + filename
+                                       if filename not in universe.files:
+                                               message += " Warning: \"" + filename + "\" is not yet included in any other file and will not be read on startup unless this is remedied."
+                               Element(element, universe, filename)
+                               log(logline)
+               elif len(arguments) > 2: message = "You can only specify an element and a filename."
+       user.send(message)
+
+def command_destroy(user, parameters):
+       """Destroy an element if it exists."""
+       if not parameters: message = "You must specify an element to destroy."
+       else:
+               if parameters not in universe.contents: message = "The \"" + parameters + "\" element does not exist."
+               else:
+                       universe.contents[parameters].destroy()
+                       message = "You destroy \"" + parameters + "\" within the universe."
+                       log(user.account.get("name") + " destroyed an element: " + parameters)
+       user.send(message)
+
+def command_set(user, parameters):
+       """Set a facet of an element."""
+       if not parameters: message = "You must specify an element, a facet and a value."
+       else:
+               arguments = parameters.split(" ", 2)
+               if len(arguments) == 1: message = "What facet of element \"" + arguments[0] + "\" would you like to set?"
+               elif len(arguments) == 2: message = "What value would you like to set for the \"" + arguments[1] + "\" facet of the \"" + arguments[0] + "\" element?"
+               else:
+                       element, facet, value = arguments
+                       if element not in universe.contents: message = "The \"" + element + "\" element does not exist."
+                       else:
+                               universe.contents[element].set(facet, value)
+                               message = "You have successfully (re)set the \"" + facet + "\" facet of element \"" + element + "\". Try \"show element " + element + "\" for verification."
+       user.send(message)
+
+def command_delete(user, parameters):
+       """Delete a facet from an element."""
+       if not parameters: message = "You must specify an element and a facet."
+       else:
+               arguments = parameters.split(" ")
+               if len(arguments) == 1: message = "What facet of element \"" + arguments[0] + "\" would you like to delete?"
+               elif len(arguments) != 2: message = "You may only specify an element and a facet."
+               else:
+                       element, facet = arguments
+                       if element not in universe.contents: message = "The \"" + element + "\" element does not exist."
+                       elif facet not in universe.contents[element].facets(): message = "The \"" + element + "\" element has no \"" + facet + "\" facet."
+                       else:
+                               universe.contents[element].delete(facet)
+                               message = "You have successfully deleted the \"" + facet + "\" facet of element \"" + element + "\". Try \"show element " + element + "\" for verification."
        user.send(message)
 
-def command_error(user, command="", parameters=""):
+def command_error(user, input_data):
        """Generic error for an unrecognized command word."""
 
        # 90% of the time use a generic error
        if randrange(10):
-               message = "I'm not sure what \"" + command
-               if parameters:
-                       message += " " + parameters
-               message += "\" means..."
+               message = "I'm not sure what \"" + input_data + "\" means..."
 
        # 10% of the time use the classic diku error
        else: