I was going to do something else with the Virtual system, but the contest tried to keep the scope a little smaller to widen the field which is cool with me. I ended up writing a spell in 5 hours (at work no less O:)
I would like to see what other people wrote too! Here was my entry:
#include <lib.h>
#include <magic.h>
// Define this and the player can save x number of items in this
// magic portal
#undef SAVE_ROOM_INVENTORY
// These are only used if SAVE_ROOM_INVENTORY is turned on
#define SAVEDIR sprintf("/tmp/%c", PortalOwner[0])
#define SAVEFILE sprintf("/tmp/%c/%s", PortalOwner[0], PortalOwner)
#define MAXITEMSSAVED 20
inherit LIB_ROOM;
inherit LIB_SPELL;
private mapping PortalObjects;
private string PortalOwner;
private void create()
{
room::create();
spell::create();
PortalObjects = ([]);
SetSpell("pocketportal");
// Note: As I found out, this will not load outside of the
// approved "parse directories" (per valid_object in master).
// I did hack a copy of this to get around that restriction
// (see /realms/kriton/pocketportal.c)
SetRules("");
SetSpellType(SPELL_OTHER);
SetRequiredMagic(50);
SetSkills(([ "conjuring" : 40 ]));
// Base + Random Cost
SetMagicCost(250, 100);
SetDifficulty(50);
SetMorality(0);
SetHelp("Syntax: <cast pocketportal>\n\n"
"Creates a magical pocket dimension that you can store stuff in.\n"
"You may type <abduct [person]> once you have entered your portal "
"so long as the person is standing at the same location from which "
"you entered.");
}
/*
You can only abduct players located just "outside".
*/
private int eventAbductPlayer(string who)
{
object player;
if(this_player()->GetName() != PortalOwner)
return notify_fail("Only " + PortalOwner + " may abduct people.\n");
if(!who || strlen(who) == 0)
return notify_fail("Abduct who?\n");
if(!(player = find_player(who)))
return notify_fail("No player like that seems to be within "
"our reality.\n");
if(environment(player) != load_object(room::GetExit("out")))
return notify_fail("That person is not within reach.\n");
send_messages("", "Transparent hands appear in the air behind "
"$target_name and rip $target_objective from this reality.",
player, player, environment(player));
player->eventMoveLiving(this_object());
write("You abduct " + player->GetCapName() + "!");
return 1;
}
/*
Called prior to someone leaving. If the caster leaves, then
everyone must leave.
*/
private int eventPreExit(string str)
{
return 1;
}
#ifdef SAVE_ROOM_INVENTORY
void eventRestoreInventory()
{
string contents;
contents = unguarded((: read_file, SAVEFILE
;
if(!contents)
return;
foreach(string line in explode(contents, "\n"))
{
string filename, data;
if(sscanf(line, "%s:%s", filename, data) == 2)
{
object ob;
mixed m = restore_variable(data);
if(!catch(ob = new(filename, m)))
{
ob->eventLoadObject(m, 1);
ob->eventMove(this_object());
}
}
}
}
/*
Optionally writes the portal's inventory to disk
*/
void eventSaveInventory()
{
int count = 0;
if(file_size(SAVEDIR) != -2)
unguarded((: mkdir, SAVEDIR
;
unguarded((: rm, SAVEFILE
;
foreach(object ob in filter(all_inventory(), (: !living($1)
)
{
if(count++ < MAXITEMSSAVED)
{
unguarded((: write_file, SAVEFILE,
base_name(ob) + ":" + ob->GetSaveString() + "\n"
;
}
else if(count == MAXITEMSSAVED)
{
write("Warning, only " + MAXITEMSSAVED + " items will actually "
"be saved in your pocket portal.");
}
}
}
#endif
/*
If the portal creator leaves, all the guests go with. Otherwise
the portal owner could act as a "bus driver" and taxi people
around and re-open the portal at the new location and let people
exit.
*/
private void eventPostExit(string str)
{
if(this_player()->GetName() == PortalOwner)
{
array guests = filter(all_inventory(), (: interactive
;
send_messages("leave",
"$agent_name $agent_verb and the world blinks out of existance!",
this_player(), 0, this_object());
guests->eventMoveLiving(room::GetExit("out"));
#ifdef SAVE_ROOM_INVENTORY
eventSaveInventory();
#endif
}
}
/*
Initialize actions if this object happens to be a room
*/
private void init()
{
if(environment(this_player()) == this_object())
{
add_action((: eventAbductPlayer
, "abduct");
}
}
/*
Called upon successful casting.
*/
int eventCast(object caster, int level)
{
string name = caster->GetName();
object portal;
if(undefinedp(PortalObjects[name]))
{
portal = new(base_name(this_object()));
portal->SetClimate("indoors");
portal->SetAmbientLight(50);
// prevent a portal within a portal
portal->SetProperty("no teleport", 1);
portal->SetShort(sprintf("%s Pocket Dimension",
possessive_noun(caster->GetName())));
portal->SetLong("The walls of this seemingly grand mansion are "
"slightly transparent as if not quite real. Beyond the "
"walls you see nothing at all as if the entire world "
"was contained within this single structure. A small "
"hatch in the floor opens to reveal a bright light "
"that is presumably the only way out.");
portal->SetPortalOwner(caster);
#ifdef SAVE_ROOM_INVENTORY
portal->eventRestoreInventory();
#endif
}
else
{
portal = PortalObjects[name];
if( !objectp(portal) )
{
map_delete(PortalObjects, name);
return eventCast(caster, level);
}
}
// Add our exit with hooks
portal->SetExits(([
"out" : ({ base_name(environment(caster)),
portal->GetPreExitEvent(), portal->GetPostExitEvent() })
]));
PortalObjects[name] = portal;
portal->SetAmbientLight(50);
caster->eventMoveLiving( portal );
caster->AddSkillPoints("conjuring", 1 + random(10));
return 1;
}
// Returns a pointer to this specific instance of eventPreExit
function GetPreExitEvent() { return (: eventPreExit
; }
// Returns a pointer to this specific instance of eventPostExit
function GetPostExitEvent() { return (: eventPostExit
; }
/*
This is a hack to check the environment for the "no teleport"
property since it wasn't clear how this was enforced in DS.
*/
mixed eventParse(object who, array args...)
{
if(environment(who)->GetProperty("no teleport"))
{
return "You cannot teleport within a pocket dimension. "
"You must go out, first.";
}
return spell::eventParse(who, args...);
}
/*
Ensure this function is exposed.
*/
int SetAmbientLight(int light)
{
return room::SetAmbientLight(light);
}
/*
Sets the owner of this portal
*/
void SetPortalOwner(object owner)
{
PortalOwner = owner->GetName();
}
/*
Need to short circuit the CanReceive() method
so that non-Arches can use this spell.
*/
int CanReceive()
{
return 1;
}