Source for file DpObject.php
Documentation is available at DpObject.php
* The standard object which is built upon by all other objects
* DutchPIPE version 0.4; PHP version 5
* LICENSE: This source file is subject to version 1.0 of the DutchPIPE license.
* If you did not receive a copy of the DutchPIPE license, you can obtain one at
* http://dutchpipe.org/license/1_0.txt or by sending a note to
* license@dutchpipe.org, in which case you will be mailed a copy immediately.
* @subpackage dpuniverse_std
* @author Lennert Stock <ls@dutchpipe.org>
* @copyright 2006, 2007 Lennert Stock
* @license http://dutchpipe.org/license/1_0.txt DutchPIPE License
* @version Subversion: $Id: DpObject.php 311 2007-09-03 12:48:09Z ls $
* @link http://dutchpipe.org/manual/package/DutchPIPE
* The DutchPIPE property and coinherit system which all objects extend on
* Gets title type constants
* The standard object which is built upon by all other objects
* Creates the following DutchPIPE properties:<br />
* - string <b>uniqueId</b> - Unique Id for this instance, for example
* - integer <b>resetTime</b> - UNIX timestamp of next reset
* - string <b>location</b> - Location in dpuniverse, for example
* - string <b>sublocation</b> - Optional sublocation, for example
* - int|float <b>credits</b> - Credits contained, integer or float depending
* - string <b>template</b> - Optional replacement for dpdefault.tpl,
* absolute path on server
* - string <b>title</b> - Title for this object, "beer", used for object
* - string <b>titleDefinite</b> - Definite title for this object, "the beer"
* - string <b>titleIndefinite</b> - Indefinite title for this object, "a beer"
* - string <b>titleType</b> - Type of this object's title
* - string <b>titleImg</b> - URL for the avatar or other image representing
* - int <b>titleImgWidth</b> - Width of the title image in pixels, you can
* optionally set this by hand for extra speed but it is not mandatory
* - int <b>titleImgHeight</b> - Height of the title image in pixels, you can
* optionally set this by hand for extra speed but it is not mandatory
* - boolean <b>isDraggable</b> - Can we be dragged on the screen by a given
* - string <b>body</b> - HTML content of this object
* - integer <b>creationTime</b> - UNIX timestamp when this object instance was
* - integer <b>lastEventTime</b> - UNIX timestamp of last event, used by
* - string <b>navigationTrailHtml</b> - HTML with a navigation trail for this
* - int|float <b>value</b> - Monetary value, integer or float depending on
* - boolean <b>isRemoved</b> - TRUE if removed from universe but not destructed
* - integer <b>lastActionTime</b> - UNIX timestamp of last action performed by
* @subpackage dpuniverse_std
* @author Lennert Stock <ls@dutchpipe.org>
* @copyright 2006, 2007 Lennert Stock
* @license http://dutchpipe.org/license/1_0.txt DutchPIPE License
* @version Release: 0.2.1
* @link http://dutchpipe.org/manual/package/DutchPIPE
* Public ids for this object, 'beer', 'cool beer'
* Actions defined by this object, see addAction
private $mActions = array();
* Aliases used for actions, for example 'exa' for 'examine'
private $mActionAliases = array();
* Image map areas, enables actions to be defined on areas of images
private $mMapAreas = array();
* Clickable actions on definable areas of images
private $mMapAreaActions = array();
* 'Items' on this object which can be examined
private $mItems = array();
private $mItemAliases = array();
* Objects to be checked for presence in our inventory during reset
private $mCheckPresentObjects = array();
* Array of strings with method names which the DutchPIPE client may call
* @see addValidClientCall, removeValidClientCall, isValidClientCall
private $mValidClientCalls = array();
* Called by the universe object when the object is created.
* Calls {@link createDpObject()} in the inheriting class.
* :WARNING: Use get_current_dpuniverse()->newDpObject only to create
* objects, do not use 'new'.
* :WARNING: It is unlikely you need to call this function directly
* @param string $unique_id A unique id for this object
* @param int $reset_time The UNIX reet time for this object
* @param string $location Location, e.g. /page/cms.php
* @param string $sublocation Sublocation, e.g. 96
* @see __construct2, createDpObject
final function __construct($unique_id, $reset_time, $location,
/* This method may only be called once, at creation time */
if (isset ($this->creationTime)) {
* Sets a unique internal id for this object
* This method is called by __construct with a string such as
* 'object_628' to set a unique id. This id is used in HTML output as
* the id of the DIV element spanning this object's HTML appearance, by
* the AJAX engine to operate on the object, et cetera.
* WARNING: Don't call this method. Only __construct should call it.
if (FALSE !== $sublocation) {
/* Standard setup calls to set some default values */
* Path to non-default XHTML template file for this object, if any
* Title, 'Cool, fresh beer'
* Definite title, 'The cool, fresh beer'
* Indefinite title, 'A cool, fresh beer'
* Type of title, 'A beer', 'The beer', etc.
* Path to the avatar or object image
* The long description or page content
dp_text('You see nothing special.<br />'));
* Creates this object, continued
* Called by the universe object when the object is created.
* Calls {@link createDpObject()} in the inheriting class.
* :WARNING: It is unlikely you need to call this function directly
* @see __construct, createDpObject
final function __construct2()
/* Call CreateDpObjects for objects that extend on this object */
if (!isset ($this->title)) {
$this->title = dp_text('initialized object');
$this->titleDefinite = dp_text('the initialized object');
$this->titleIndefinite = dp_text('an initialized object');
* Sets this object up at the time it is created
* An empty function which can be redefined by the class extending on
* DpObject. When the object is created, it has no title, HTML body, etc.,
* so here methods such as $this->setTitle() are called.
* Building blocks extending on DpObject may define their own create
* function. For example, DpPage defines createDpPage.
* Called by the universe object at regular intervals as defined in
* {@link dpuniverse-ini.php}. Calls the method 'resetDpObject' in this
* object. You can redefine that function to periodically do stuff such as
* alter the state of this object.
* Called by this object at regular intervals as defined in
* dpuniverse-ini.php. An empty function which can be redefined by the
* class extending on DpObject. To be used to periodically do stuff such as
* alter the state of this object.
* Called by PHP when this object is destroyed, handles events
* Called by PHP when this object is destroyed. removeDpObject should be
* called in this object to remove it from the universe. Don't use
* unset to remove objects. On a PHP level, __destruct will then be called
* Calls the event method in this object and its environment, if any, when
* defined, using constants from events.php.
* Triggers the EVENT_LEFT_INV event in the environment of this object with
* "from" ($this) and "to" (0) parameters:
* $environment->event(EVENT_LEFT_INV, $this, 0);
* Triggers the EVENT_CHANGED_ENV event in this object with "from" (the
* environment) and "to" (0) parameters:
* $this->event(EVENT_CHANGED_ENV, $environment, 0);
* If the object had no environment, the above two events are not called,
* instead the EVENT_CHANGED_ENV event is triggered in this object with only
* a "from" (0) parameter:
* $this->event(EVENT_CHANGED_ENV, 0);
* Last, the EVENT_DESTROYING_OBJ event is triggered in this object:
* $this->event(EVENT_DESTROYING_OBJ);
* @see removeDpObject, events.php
if (!isset ($this->isRemoved) || TRUE !== $this->isRemoved) {
* Removes this object from the universe
* The object is destroyed and no longer part of the universe. Don't use
* unset to remove objects. Removes the object from browsers viewing the
* same environment. On a PHP level, __destruct will then be called in this
echo dp_text("removeDpObject() called in object " . $this->title
foreach ($this->mCheckPresentObjects as $key => &$ob) {
unset ($this->mCheckPresentObjects[$key]);
$this->mCheckPresentObjects = array();
$this->mActions = array();
$this->mActionAliases = array();
while (FALSE !== $env->getEnvironment()) {
$env = $env->getEnvironment();
$env->tell('<removeDpElement id="' . $this->uniqueId
. '"> </removeDpElement>');
/* :TODO: Move all users somewhere so they don't get destroyed */
* Moves this object into the inventory of another object
* @param mixed &$target_ob path or object to move into to
* @param boolean $simple skip some checks
* @return int TRUE for success, an error code for failure
/* Retrieve the target object, or fail */
if (isset ($this->isLiving) && TRUE === $this->isLiving) {
$this->lastActionTime = !isset ($this->lastActionTime)
/* Checks if the living carrying this object may not drop it: */
if (FALSE !== $curr_env && $curr_env->isLiving
&& (bool) $curr_env->noDrop) {
/* Checks if target is neither a page nor a living */
if (FALSE !== $target_ob->getEnvironment() &&
FALSE === $target_ob->isLiving) {
* Checks if the object may be inserted into containers, for
* instance, drinks could disallow this, to prevent 'spilling':
&& (bool) $this->preventInsert()) {
/* Checks if the target is a container, or fail */
if (FALSE === ((bool) $target_ob->isContainer)) {
/* Checks if the object may be taken by livings */
if ($target_ob->isLiving && (bool) $this->noTake) {
if (isset ($this->weight) && isset ($target_ob->maxWeightCarry)
&& $target_ob->weightCarry + $this->weight >
$target_ob->maxWeightCarry) {
return E_MOVEOBJECT_HEAVY;
if (isset ($this->volume) && isset ($target_ob->maxVolumeCarry)
&& $target_ob->volumeCarry + $this->volume >
$target_ob->maxVolumeCarry) {
* If we're moving to a new environment, check for user defined
* functions to prevent movement
if ($curr_env !== $target_ob) {
$this->preventMove($curr_env, $target_ob))) {
(int) $curr_env->preventLeave($this, $target_ob))) {
(int) $target_ob->preventEnter($this, $curr_env))) {
/* Post-movement stuff */
if ($curr_env !== $target_ob) {
return E_MOVEOBJECT_REMOVED;
if (FALSE !== ($curr_env)) {
while (FALSE !== $old_page->getEnvironment()) {
$old_page = $old_page->getEnvironment();
while (FALSE !== $new_page->getEnvironment()) {
$new_page = $new_page->getEnvironment();
$abstract_class = FALSE === $target_ob->getEnvironment()
? 'dpobject' : 'dpobject2';
if (isset ($old_page) && $old_page === $new_page) {
'abstract' => '<moveDpElement id="'
. '" where="' . $target_ob->uniqueId
. '" class="' . $abstract_class
. '"> </moveDpElement>'));
if ($target_ob === $new_page) {
'graphical' => '<addDpElement id="'
. $this->uniqueId . '" where="'
. '" class="title_img draggable" '
'graphical') . '</addDpElement>'));
'graphical' => '<removeDpElement id="'
. '"> </removeDpElement>'));
$old_page->tell('<removeDpElement id="'
. '"> </removeDpElement>');
if ($target_ob === $new_page) {
$new_page->tell(array('abstract' => '<addDpElement id="'
. $this->uniqueId . '" where="'
. $target_ob->uniqueId . '" class="'
FALSE, $new_page, 'abstract') . '</addDpElement>',
'graphical' => '<addDpElement id="'
. $this->uniqueId . '" where="'
. '" class="title_img draggable" '
. '</addDpElement>'), $this);
$dest_all = $target_ob->getInventory();
foreach ($dest_all as &$ob) {
if (!isset ($this->_GET) || !isset ($this->_GET['getdivs'])) {
$body = isset ($this->_GET) && isset ($this->_GET['ajax'])
/* Back/forward button is used */
? $target_ob->getAppearanceInventory(0, TRUE, NULL,
$this->displayMode) . '<div class="dpclr"> </div>'
: $target_ob->getAppearance(0, TRUE, NULL,
$template_path = is_null($target_ob->template) ? ''
: ' template="' . $target_ob->template . '"';
. (isset ($this->_GET) && isset ($this->_GET['ajax'])
? 'dpinventory' . '"' : 'dppage' . '"' . $template_path)
. '>' . $body . '</div>');
$this->tell('<inputpersistent persistent="'
. (!$this->inputPersistent ? 'off' : $this->inputPersistent)
. '"> </inputpersistent>');
if ($this->isUser && ($type = $target_ob->isMovingArea)) {
if (!isset ($this->_GET) || !isset ($this->_GET['ajax'])) {
$this->tell('<script type="text/javascript" src="'
. 'interface/iutil.js"></script>');
$this->tell('<script type="text/javascript" src="'
. 'interface/idrag.js"></script>');
$containment = $type === 1 ? "containment : 'parent',\n"
$cssfix = $type == 1 ? '.dpinventory, .dpinventory2'
if (\$.iDrag == undefined) return;
\$('div.draggable').DraggableDestroy();
\$('div.draggable').Draggable({
opacity: 0.7,{$containment}
onChange : function() { stopdrag(this) }
\$('{$cssfix}').css('position', 'relative');
\$('{$cssfix}').css('overflow', 'hidden');
\$(function(){init_drag();});</script>\n");
* Checks if an object is present in this object's inventory
* If $what is a string, searches for an object with that id. If $what is an
* object, searches for that object. Searches are done in the inventory of
* @param mixed $what string (id) or object to search for
* @return boolean TRUE if $what is in our inventory, FALSE otherwise
* Makes sure an object is present in this object's inventory
* Replenishes this object's inventory up to $number instances of $what.
* This method is usually called from the reset method of pages and npc's.
* If $what is a string, searches for an object with that id. If $what is an
* object, searches for that object. Searches are done in the inventory of
* A $number can be given to search for more objects instead of just one.
* For example, it could check for the presence of 5 roses, and if there
* are only 3, create another 2.
* If $mustBeHere is FALSE (it is TRUE by default), it will not replenish
* objects which are still in another part of the universe, instead of
* having been destroyed. For example, the 2 missing roses could have been
* taken by a user who is now on another page, and with this argument set
* to FALSE, no extra objects will be created.
* If $mustBeHere is TRUE, $moveHere can be set to TRUE (it is FALSE by
* default) to move objects which still exist but are located in another
* part of the universe, back to this object, its original location.
* @param mixed $what string (id) or object to search for
* @param int $number number of such objects to search for
* @param boolean $mustBeHere search only here for object?
* @param boolean $moveHere replenish from objects elsewhere?
function makePresent($what, $number = 1, $mustBeHere = TRUE,
if (0 < count($this->mCheckPresentObjects)) {
foreach ($keys as $key) {
if (!isset ($this->mCheckPresentObjects[$key])) {
if (empty($this->mCheckPresentObjects[$key])
|| $this->mCheckPresentObjects[$key]->isRemoved) {
unset ($this->mCheckPresentObjects[$key]);
$ob = $this->mCheckPresentObjects[$key];
|| (is_string($what) && $ob->location === $what)) {
$ob->moveDpObject($this);
if (++ $found === $number) {
} elseif (++ $found === $number) {
elseif (++ $found === $number) {
while ($found < $number) {
$ob->moveDpObject($this);
$this->mCheckPresentObjects[] = & $ob;
* Gets the object reference to the environment of this object
* @return mixed object reference or FALSE for no environment
* Gets an array with object references to all objects in our inventory
* If this object contains no other objects, an empty array is returned.
* @return array object references to objects in our inventory
* Calls the given method after the given number of seconds
* Use this to perform delayed method calls in this object. Note that
* functions such as get_current_dpuser() can be totally different when the
* method is called. Also note that the actual delay is not exact science.
* @param string $method name of method to call in this object
* @param int $secs delay in seconds
* Tells data (message, window, location, ...) to this object
* Tells a message to this object, for instance a chat line. Handled by
* @param string $data message string
* Sets an array of names used to refer to this object
* Overwrites previous set ids. Ids are case insensitive and all turned
* @param array $ids array of name strings
* @see addId(), removeId(), getIds(), isId()
* Adds one or more ids for this object
* A single id can be added at a time, or an array with ids can be given,
* or multiple arguments can be given (strings or array of strings).
* @param string|array $id name string or array of name strings
* @see setIds(), removeId(), getIds(), isId()
for ($i = 0; $i < $sz; $i++ ) {
* Removes one or more ids for this object
* A single id can be removed at a time, or an array with ids can be given,
* or multiple arguments can be given (strings or array of strings).
* @param string|array $id name string or array of name strings
* @see setIds(), addId(), getIds(), isId()
for ($i = 0; $i < $sz && sizeof($this->mIds); $i++ ) {
if (dp_strlen($ids) && isset ($this->mIds[$ids])) {
unset ($this->mIds[$ids]);
if (dp_strlen($id) && isset ($this->mIds[$id])) {
* Gets the array of ids for this object, or an empty array for no ids
* @return array array of name strings
* @see setIds(), addId(), removeId(), isId()
* Checks if the given id is a valid id for this object
* For example, a barkeeper object was set up like this:
* $this->addId('barkeeper');
* Now isId('barkeeper') called in this object will return TRUE, but
* 'the barkeeper' and 'a barkeeper' will also return TRUE.
* The disable the last behaviour, set $checkWithArticle to FALSE.
* @param string $id name string to check
* @param string $checkWithArticle also check ids with articles
* @return boolean TRUE if the id is valid, FALSE otherwise
* @see setIds(), addId(), removeId(), getIds()
function isId($id, $checkWithArticle = TRUE)
|| $id == $this->uniqueId
|| ($checkWithArticle && $this->_isIdWithArticle($id));
* Checks if the given id is a valid id when articles are stripped off
* Strips off 'a', 'an' and 'the' (for English), and checks is the remainder
* is a valid id for this object.
* @param string $id name string to check
* @return boolean TRUE if the id is valid, FALSE otherwise
private function _isIdWithArticle($id)
if (FALSE === $space_pos) {
return in_array($first_word, $articles) && $this->isId($rest, FALSE);
* Sets the title for this object, "beer", used for object labels, etc.
* This title is used for object labels, page titles, messages, et cetera.
* Objects are visualized using two mechanisms: the object's "title" and
* the object's "body". The title is used when the object is nearby, for
* instance in the environment of a user that sees it. The body is used
* when we're in the object (a page) or examining the object. In other
* words, the object's title is a very abstract way to describe the object
* when looking at it from a greater distance.
* In plain language, this function controls the avatar or object image,
* and the label under it.
* The $title should be a short description like "beer" without "a" or "the"
* The $title_img should be a URL to the image shown to represent this
* The second argument, $type, should be a constant as defined in
* include/title_types.php and is used by the framework to, for example,
* construct lines such as "Lennert takes a cool, fresh beer" instead of
* "A Lennert takes a cool fresh, beer" or "The Lennert takes a cool, fresh
* DPUNIVERSE_TITLE_TYPE_INDEFINITE - the title is indefinite, "a beer"
* DPUNIVERSE_TITLE_TYPE_DEFINITE - the title is definite, "the hulk"
* DPUNIVERSE_TITLE_TYPE_NAME - the title is a name, "Lennert"
* DPUNIVERSE_TITLE_TYPE_PLURAL - the title is plural, "sweets" (not yet
* @param string $title short description, "beer"
* @param string $titleType noun type, use the constants above
* @param string $titleImg URL to avatar or object image
public function setTitle($title, $titleType = FALSE, $titleImg = FALSE)
$this->titleDefinite = NULL;
$this->titleIndefinite = NULL;
if (FALSE !== $titleType) {
$this->titleType = $titleType;
if (FALSE !== $titleImg) {
$this->titleImg = $titleImg;
* Gets the object's title with optional prefixes such as 'a' and 'the'
* Gets the title as set with setTitle, for example "barkeeper" if no $type
* If $type is DPUNIVERSE_TITLE_TYPE_INDEFINITE, "a ..." or "an ..." is
* put in front if the object's title type has been set to indefinite. So
* for a barkeeper this returns "a barkeeper", for me it returns "Lennert".
* If $type is DPUNIVERSE_TITLE_TYPE_DEFINITE, "the" is put in front if the
* object's title type is not DPUNIVERSE_TITLE_TYPE_NAME. So for a barkeeper
* this returns "the barkeeper", for me it returns "Lennert".
* @param string $type noun type, use the constants above
* @return string the object's title
switch ($this->getTitleType()) {
return !is_null($this->titleDefinite) ? $this->titleDefinite
if (!is_null($this->titleIndefinite)) {
return $this->titleIndefinite;
switch ($this->getTitleType()) {
return !is_null($this->titleDefinite) ? $this->titleDefinite
* Sets the HTML content of this object
* If this is a page, this defines the page content. For other objects, it
* defines what you see when examining the object or moving into the object
* (which makes the object behave like a page).
* When a single argument is given, sets the HTML content to the given text.
* Pairs of arguments can be given to set the content in other ways, with
* the second argument the type, and the first the data (what the data is
* string (default, raw data)
* file (read content of given filename)
* $this->setBody('Hello world');
* $this->setBody('helloworld.html', 'file');
* $this->setBody('Prefix', 'string', 'helloworld.html', 'file', 'Postfix',
* When a file is read, the following strings are replaced by their
* corresponding constants:
* /dpclient.php, /page/ and /images/
* Instead of text or a path, a method to call whenever the body property is
* requested can be given using an array two elements: an object and a
* method name. Depending on the type, the method should return text or a
* @param string $body content data or method
* @param string $type content type
public function setBody($body, $type = 'text')
for ($i = 0; $i < $num_args; $i += 2) {
* Gets the HTML content of this object
* @return string HTML content of this object
elseif ($type === 'file') {
} elseif ($type === 'url') {
/* Experimental, ignore */
$rval .= $this->getBodyUrl($data);
* Gets the HTML content of this object
* @param string $url location of contents
* @return string HTML content of this object
private function _getBodyUrl($url)
echo dp_text("Getting mailman contents\n");
'flags_var_name' => 'hl',
'get_form_name' => '__script_get_form',
'proxy_url_form_name' => 'poxy_url_form',
'proxy_settings_form_name' => 'poxy_settings_form',
$PHProxy = new PHProxy($config, $flags);
$PHProxy->start_transfer($data);
$tmp = $PHProxy->return_response();
$fp = fopen($data, "rb");
$tmp .= fread($fp, 4096);
$meta_data = stream_get_meta_data($fp);
foreach($meta_data['wrapper_data'] as $response) {
$tmp .= htmlentities($response) . '<br />';
if (FALSE !== $pos1 && FALSE !== $pos2) {
array('/mailman/', '="90%"'), $tmp);
* Gets the HTML "appearance" of this object
* Gets HTML to represent the object to another objects. That is, other
* objects call this method in order to "see" it, and HTML is returned. How
* an object is seen depends on how the object is related to the object that
* is viewing it in terms of "physical" location.
* In other words, a level of 0 means this object is seen by something in
* its inventory (a user sees a page). Level 1 means this object is seen by
* an object in its environment (a user sees another user). Level 2 means
* this object is in the inventory of the object that is seeing it.
* @param int $level level of visibility
* @param boolean $include_div include div with id around HTML?
* @param object $from experimental
* @param string $displayMode 'abstract' or 'graphical'
* @param boolean $displayTitlebar display title bar for pages?
* @param boolean $elementId to be used as html element id
* @return string HTML "appearance" of this object
* @see getAppearanceTitle
$from = NULL, $displayMode = 'abstract',
$displayTitlebar = TRUE, $elementId = 'dppage')
if (TRUE === $displayTitlebar) {
$template_path = is_null($this->template) ? ''
: ' template="' . $this->template . '"';
$login_link = !isset ($user->isRegistered)
|| TRUE !== $user->isRegistered
. '"padding-left: 4px">' . dp_text('Login/register')
. 'style="padding-left: 4px">' . dp_text('Logout')
$titlebar = '<div id="titlebarleft">' .
$navtrail . '</div><div id='
. '"titlebarright"> <div id="dploginout">'
. $user->title . '</span>')
. ' <span id="loginlink">'
. $login_link . '</span>    '
. 'align="absbottom" width="11" height="11" border="0" '
. 'alt="' . $bottom . '" title="' . $bottom . '" '
. 'onclick="_gel(\'dpaction\').focus(); '
. 'scroll(0, 999999)" /></div></div>';
$titlebar = '<div id="titlebar">' . $titlebar . '</div>';
$body = '<div id="' . $elementId . '"' . $template_path
. '><div id="' . $elementId . '_inner1">' . $titlebar
. '<div class="' . $elementId . '_inner2" id="' . $elementId
. '_inner2">' . '<div id="dppage_body">'
. ($displayTitlebar === - 1 ? '' : $this->getBody()
$from, $displayMode, $displayTitlebar, $elementId)
. '<div class="dpclr"> </div>';
if (isset ($this->isLiving) && TRUE === $this->isLiving) {
$reguser_age = $inactive_time = $session_age = '';
if ($displayTitlebar !== - 1) {
if (isset ($this->isUser) && TRUE === $this->isUser) {
? (!isset ($this->isRegistered)
|| TRUE !== $this->isRegistered
? dp_text("You have been %s on this site.",
: dp_text("This session, you have been %s on this site.",
: (!isset ($this->isRegistered)
|| TRUE !== $this->isRegistered ? ucfirst(
dp_text("%s has been %s on this site.",
: dp_text("This session, %s has been %s on this site.",
$this->sessionAge))) . '<br /><br />';
$reguser_age = !isset ($this->isRegistered)
|| TRUE !== $this->isRegistered
dp_text("In total, you have been %s on this site.",
: dp_text("In total, %s has been %s on this site.",
$this->age)) . '<br /><br />';
$inactive_time = !isset ($this->isInactive)
|| TRUE !== $this->isInactive ? ''
: dp_text('%s has been inactive for %s.',
$this->inactive) . '<br /><br />';
return $body . $session_age . $reguser_age . $inactive_time .
. ($inventory == '' ? dp_text('Nothing') : $inventory)
$body .= $inventory . '</div></div></div>';
} elseif (1 === $level) {
$status = !isset ($this->status) || FALSE === $this->status
? '' : ' (' . $this->status . ')';
if ($displayMode === 'graphical' && isset ($this->titleImg)) {
return FALSE === $include_div ? $title_img
: '<div id="' . $this->uniqueId . '" '
. 'class="title_img' . ($from !== $this ? '' : '_me')
. ' draggable" style="width: '
. 'px">' . $title_img . '</div>';
$body = $from === $this ? '<span class="me">'
$inventory .= $ob->getAppearance($level + 1, $include_div,
return FALSE === $include_div ? $body . $inventory
: '<div id="' . $this->uniqueId
. '" class="dpobject" onclick="get_actions(\''
. $this->uniqueId . '\', event)">'
. $body . $inventory . '</div>';
} elseif (2 === $level) {
$status = !isset ($this->status) || FALSE === $this->status
? '' : ' (' . $this->status . ')';
return FALSE === $include_div
: '<div id="' . $this->uniqueId
. '" class="dpobject2" onclick="get_actions(\''
. $this->uniqueId . '\')">'
return $body . (0 === dp_strlen($inventory) ? "" : $inventory)
* Gets the graphical appearance in HTML for this object
* Returns the HTML with the image and the title of the object. The method
* filterAppearance($level, &$from, $appearance, &$user) is called in the
* environments of the object if defined. $level is 1, $from is an array
* starting with this object, followed by elements defining a path to the
* current object being called, $appearance the default appearance HTML and
* $user the user for which we're getting the appearance of the object. If
* you define it, it should return a string with the HTML for the
* appearance, just like getAppearanceTitle does itself.
* @param object &$user performer of actions
* @return string the graphical appearance in HTML for this object
* @see getAppearanceTitle
$status = !isset ($this->status) || FALSE === $this->status
? '' : ' (' . $this->status . ')';
$title_pre = $title_post = '';
$title_img_class = "dpimage";
$title_img_class .= ' draggable';
$title_img_class .= ' dpinactive';
$title_pre = '<span class="dpinactive_txt">';
: ' width="' . $width . '"';
: ' height="' . $height . '"';
$margin_top = FALSE === $height
$img_title = '<img src="' . $this->titleImg . '" '
. 'border="0"' . $width_html . $height_html . $margin_top
. ' class="' . $title_img_class . '" ' . 'onclick="get_actions(\''
. $this->uniqueId . '\', event)" ' . 'alt="' . $alt . '" title="'
. $alt . '" /> <br />' . $title_pre /* IE6 needs space before br */
$img_title = $env->filterAppearance(1, $from, $img_title,
$env = $env->getEnvironment();
* Gets the HTML "appearance" of all objects in this object's inventory
* Gets HTML to represents the all objects in this object's inventory using
* @param int $level level of visibility of this object
* @param boolean $include_div include div with id around HTML?
* @param object $from expiremental
* @param string $displayMode 'abstract' or 'graphical'
* @return string HTML "appearances" of this object's inventory
$from = NULL, $displayMode = 'abstract', $displayTitlebar = TRUE,
$inventory .= $ob->getAppearance($level + 1, $include_div, $from,
$displayMode, $displayTitlebar, $elementId);
$div_id = 'dppage' === $elementId ? 'dpinventory' : 'dpobinv';
return $inventory == '' ? ''
: "<div id=\"$div_id\"><div class=\"dpinventory2\" id=\""
. "{ $this->uniqueId}\"> $inventory</div><div class=\"dpclr\"> "
* Gets a HTML navigation trail for this object
* By default, a 'Home' link is always present.
* @return string HTML for navigation trail
function getNavigationTrailHtml()
return '<div id="navlink">' . DPUNIVERSE_NAVLOGO . '</div>';
* Adds an examinable 'item' to this object
* 'Items' in objects allow you to perform some basic actions, such as
* examine, without making new objects.
* For example, consider this page with the following description:<br />
* You are in front of a house. You see a sign.
* The following call will make the house examinable:<br />
* <code>add_item('house', 'It is a big, brick house.<br />');</code>
* The $item can be an identifier string or an array with strings with
* identifiers, for example array('house', 'brick house'). Such aliases can
* be used from the command line, for example 'examine brick house'.
* The $description should be a string with the textual description.
* The $method is optional and should be a string containing a method or an
* array with two elements: an object to call and a string with the method.
* This method is called at runtime to obtain the description. It should be
* defined like this:<br />
* <code>function <methodName>($item)</code>
* and return a string.<br />
* When the $description is NULL, only the result from this method will be
* shown. With the $description is a normal string, the result of the method
* is appended. When the description has the %s token in it, the result of
* the method is inserted there. Example:<br />
* <code>add_item('door', 'A solid door. It is %s.', 'getOpenOrClosed');</code>
* The optional $mapArea can be given to make this item accessible on an
* imagemap, so people can click on it to examine it. Otherwise the item
* can only be examined by using the command line. It should be a map area
* id if you set one earlier with setMapArea, or an array with the arguments
* to setMapArea to define a new one (see setMapArea documentation for more
* If a map area is used, the action on the imagemap is labelled 'examine'.
* This can be overruled by supplying $mapAreaActionMenu, which should be
* a string with an alternative label.
* @param mixed $item string or array of strings with
* @param string $description item description
* @param mixed $method method for dynamic description
* @param mixed $mapArea imagemap area id or definition
* @param mixed $mapAreaActionMenu action to appear on menu
* @see getItem, getItemDescription, getItems, addAction, setMapArea
function addItem($item, $description, $method = NULL, $mapArea = NULL,
$mapAreaActionMenu = NULL)
if (1 < ($sz = sizeof($item))) {
for ($i = 1; $i < $sz; $i++) {
$this->mItemAliases[$item[$i]] = $tmp;
$this->mItems[$item] = array($description, $method);
if (is_array($mapArea)) {
call_user_func_array(array(&$this, 'setMapArea'), $mapArea);
if (is_null($mapAreaActionMenu)) {
$mapAreaActionMenu = dp_text('examine');
sprintf(dp_text("examine %s"), $item));
* Gets an array with data about an item
* An array is returned with two elements: the item description and the item
* method. Either can be NULL. If the item was not found, FALSE is returned.
* @param string $item the item id to search for
* @return array array with data about the given item, or FALSE
* @see addItem, getItemDescription, getItems, addAction, setMapArea
if (isset($this->mItems[$item])) {
return $this->mItems[$item];
} elseif (isset($this->mItemAliases[$item])) {
return $this->mItems[$this->mItemAliases[$item]];
$articles = explode('#', dp_text('a#an#the'));
$space_pos = dp_strpos($item, ' ');
if (FALSE !== $space_pos) {
$first_word = dp_substr($item, 0, $space_pos);
$rest = dp_substr($item, $space_pos + 1);
if (in_array($first_word, $articles)) {
* Gets the description of a given item
* Returns the description of the given item, or FALSE if no such item was
* found. Items can be added with a method to call (see addItem), here this
* @param string $item the item id to search for
* @return mixed string with description or FALSE
* @see addItem, getItem, getItems, addAction, setMapArea
function getItemDescription($item)
if (isset($this->mItems[$item])) {
$item_data =& $this->mItems[$item];
} elseif (isset($this->mItemAliases[$item])) {
$item_data =& $this->mItems[$this->mItemAliases[$item]];
$articles = explode('#', dp_text('a#an#the'));
$space_pos = dp_strpos($item, ' ');
if (FALSE !== $space_pos) {
$first_word = dp_substr($item, 0, $space_pos);
$rest = dp_substr($item, $space_pos + 1);
if (in_array($first_word, $articles)) {
$description = $item_data[0];
$method_description = !is_array($method) ? $this->{$method}($item)
: $method[0]->{$method[1]}($item);
if (is_null($description) || '' === $description) {
return $method_description;
$combined_description = sprintf($description, $method_description);
return $combined_description != $description ? $combined_description :
$description . $method_description;
* Gets an array with all item data added to this object
* @return array all items added to this object
* @see addItem, getItem, getItemDescription, addAction, setMapArea
* Adds an action to the object
* Use this method to have actions added to the menus you get when clicking
* on items (the "action menu"), and at the same time to add actions you can
* type. See DpLiving.php for some good examples.
* Because of the complexity of this method, it has its own manual page.
* See {@tutorial DutchPIPE/actions.pkg} for further information.
* @param mixed $actionMenu title of clickable menu item
* @param mixed $actionVerb alternative verb to type
* @param mixed $actionMethod method called to perform action
* @param mixed $actionOperant on who/what does it have effect?
* @param mixed $actionTarget where should menu item appear?
* @param mixed $actionAuthorized who may perform this action?
* @param mixed $actionScope who sees action to begin with?
* @param mixed $mapArea imagemap area id or definition
* @param string $mapAreaAction specific action to use
* @see removeAction, getActionData, getActionsMenu, setMapArea
* @tutorial DutchPIPE/actions.pkg
final public function addAction($actionMenu, $actionVerb, $actionMethod,
$actionOperant = DP_ACTION_OPERANT_MENU,
$actionTarget = DP_ACTION_TARGET_SELF,
$actionAuthorized = DP_ACTION_AUTHORIZED_ALL,
$actionScope = DP_ACTION_SCOPE_ALL,
if (!isset($this->mActions[$tmp])) {
end($this->mActions[$tmp]);
$new_key = key($this->mActions[$tmp]) + 1;
reset($this->mActions[$tmp]);
if (1 < ($sz = sizeof($actionVerb))) {
for ($i = 1; $i < $sz; $i++) {
if (!isset($this->mActionAliases[$actionVerb[$i]])) {
$this->mActionAliases[$actionVerb[$i]] = array();
$this->mActionAliases[$actionVerb[$i]][] =
if (!isset($this->mActions[$actionVerb])) {
$this->mActions[$actionVerb] = array();
$this->mActions[$actionVerb][] = array($actionMenu, $actionMethod,
$actionOperant, $actionTarget, $actionAuthorized, $actionScope,
$mapArea, $mapAreaAction);
end($this->mActions[$actionVerb]);
$mapAreaAction = "{$actionVerb} {$this->uniqueId}";
$mapAreaAction, $actionVerb,
key($this->mActions[$actionVerb]));
* Removes the given action.
* Removes the action associated to the given $actionVerb. If the action was
* linked to an imagemap area, provide the area id with $mapAreaId to
* delete this information as well.
* @param boolean $actionVerb alternative command to type
* @param string $mapAreaId imagemap area id
* @see addAction, getActionData, getActionsMenu
final public function removeAction($actionVerb, $mapAreaId = NULL)
if (isset($this->mActionAliases[$actionVerb])) {
$actionVerb = $this->mActionAliases[$actionVerb][0];
$action_nr = $this->mActionAliases[$actionVerb][1];
if (isset($this->mActions[$actionVerb])) {
if (isset($this->mActions[$actionVerb][$action_nr])) {
$this->_removeAction($actionVerb, $action_nr);
foreach ($this->mActions[$actionVerb] as
$action_nr => &$action_data) {
$this->_removeAction($actionVerb, $action_nr, $mapAreaId);
* Helper method for removeAction
* @param boolean $actionVerb alternative command to type
* @param integer &$action_nr index of the verb in alternatives list
* @param string $mapAreaId imagemap area id
private function _removeAction($actionVerb, $actionNr, $mapAreaId = NULL)
$action_data =& $this->mActions[$actionVerb][$actionNr];
$map_area_id = $action_data[6];
if (is_null($mapAreaId) || (!is_null($map_area_id)
&& $mapAreaId === $map_area_id)) {
if (!is_null($map_area_id)) {
unset($this->mActions[$actionVerb][$actionNr]);
if (0 === count($this->mActions[$actionVerb])) {
unset($this->mActions[$actionVerb]);
* Gets data of one, multiple or all actions
* Without arguments, an array is returned with each element of pair
* verb => array(menulabel, method, operant, target, authorized, scope)
* with operant one of DP_ACTION_OPERANT_, target one of DP_ACTION_TARGET_,
* authorized one of DP_ACTION_AUTHORIZED_ and scope one of DP_ACTION_SCOPE_
* from dpuniverse/include/actions.php, for example:
* 'read' => array('read me!', 'actionRead', DP_ACTION_OPERANT_MENU,
* DP_ACTION_TARGET_SELF, DP_ACTION_AUTHORIZED_ALL, DP_ACTION_SCOPE_ALL)
* You can narrow the result set down by supplying a verb, in which case an
* array with one or more results is returned, or FALSE if the given verb is
* not defined as an action.
* Narrow it down further by giving a number, usually 0, to get the data at
* the index of the array you would get without supplying a number.
* @param boolean $actionVerb alternative command to type
* @param integer &$actionNr index of the verb in alternatives list
* @return array array with actions, can be empty
* @see addAction, removeAction, getActionsMenu
final function getActionData($actionVerb = NULL, $actionNr = NULL)
if (is_null($actionVerb)) {
if (!isset($this->mActions[$actionVerb])) {
if (is_null($actionNr)) {
return $this->mActions[$actionVerb];
if (!isset($this->mActions[$actionVerb][$actionNr])) {
return $this->mActions[$actionVerb][$actionNr];
* Tells the current user the HTML with the action menu for this object
* @see addAction, removeAction, getActionData, getTargettedActions
function getActionsMenu()
* A checksum is bounced back so the client can find the right
if (!($user = get_current_dpuser())
|| !isset($user->_GET['checksum'])) {
$user->lastActionTime = !isset($user->lastActionTime)
if (!($rval = $this->_getActionsMenuLevels($user))) {
list($lvl_titles, $lvl_cnt, $lvl_last_title) = $rval;
if (!isset($user->_GET['map_name'])
|| !isset($user->_GET['map_area_id'])) {
is_null($lvl_last_title) ? NULL : $lvl_titles);
(is_null($lvl_last_title) ? NULL : $lvl_titles),
$user->_GET['map_area_id']);
* For map area actions, when there's only one action, don't show a
* menu, but execute action immediately
if (1 === count($actions) && 0 === $lvl_cnt) {
$user->_GET['menuaction'] = '1';
$user->performAction($actions[key($actions)][0]);
$icon_used = $submenu_used = FALSE;
foreach ($actions as $action_menu_title => &$action_data) {
/* If set to FALSE, inserts action into the command line */
$is_submenu = is_array($action_data) && $action_data[6];
if (is_array($action_data) && $action_data[4]) {
$icon_over = $action_data[5];
list($actionstr, $send_action, $ghosted) =
$this->_getActionsMenuFullAction($action_data, $user);
if ((!isset($user->inputMode) || 'say' !== $user->inputMode)
$get_operant = $is_submenu = TRUE;
$gstyle = $gdstyle = $gdsstyle = '';
$gdstyle = ' am_deep_ghosted';
$gdsstyle = ' am_deep_selected_ghosted';
$mouseover = "if (action_over(this)) { ";
$mouseout = $mouseclick = '';
if (!$lvl_cnt && !$is_submenu) {
$mouseover .= "jQuery('div.am_deep_selected')."
. "removeClass('am_deep_selected am_selected $gdsstyle') ";
$add_to_mouseover = '; ';
if (!$is_submenu || $ghosted) {
$mouseout = "am_target_out = this";
$mouseclick = $ghosted ? 'am_no_close = true'
: ((FALSE === $send_action ? 'show_input'
: 'send_action2server') . "('" .
$mouseover .= $add_to_mouseover
. "jQuery(this).addClass('am_deep_selected" . $gdsstyle
. "'); " . (isset($user->_GET['map_area_id'])
? "get_map_area_actions('{ $user->_GET['map_name']}', "
. "'{ $user->_GET['map_area_id']}', "
. "'{ $this->uniqueId}', event, ";
for ($i = 0; $i < $lvl_cnt; $i++) {
$add_to_mouseover = '; ';
$mouseclick = 'am_no_close = true';
$mouseover .= $add_to_mouseover
. "jQuery('span.am_icon', this).attr('id', "
. "jQuery('span.am_icon > img', this).attr('src')); "
. "jQuery('span.am_icon > img', this).attr('src', "
$action_menu .= '<div id="action_menu' . $lvl_cnt . '" '
. 'class="am' . (!$is_submenu ? '' : ' am_deep' . $gdstyle)
. $gstyle . '" onmouseover="' . $mouseover
. '" onmouseout="' . $mouseout
. '" onclick="' . $mouseclick . '">'
. '<span class="AM_ICON" id=' . $icon_over . '>' . $icon
. '<span class="am_title">' . $action_menu_title . '</span>'
. '<span class="AM_SUBMENU"> </span> '
. '<br clear="all" /></div>' . "\n";
$action_menu = str_replace(array('AM_ICON', 'AM_SUBMENU'),
array((!$icon_used ? 'am_empty' : 'am_icon'),
(!$submenu_used ? 'am_empty2' : 'am_submenu')), $action_menu);
$user->tell('<actions id="' . $this->uniqueId
. '" level="' . $lvl_cnt . '" checksum="' . $user->_GET['checksum']
. '"><div class="actionwindow_inner">' . $action_menu
* Gets information about the current trail of menu and submenus
* Used by getActionsMenu.
* @param object &$user performer of actions
* @return array data structure with level info
private function &_getActionsMenuLevels(&$user)
* If the user is navigating to submenus, the client passes each menu
* title leading to the submenu, for example:
while (isset($user->_GET['l' . ($lvl_cnt + 1)])) {
$lvl_titles[] = $lvl_last_title = $user->_GET['l' . $lvl_cnt];
$rval = array($lvl_titles, $lvl_cnt, $lvl_last_title);
* Gets actions which can be performed on this object, for action menu
* Gets an array with actions which can be performed on this object, so
* we can make a menu when hovering over the object image with the mouse.
* This includes actions that are defined by other objects but appear in
* this object's action menu.
* An array is returned, empty if there are no menu actions found, with
* key-value pairs. Each key is the (first) menu title associated with the
* action pair, each value an array of three elements: the verb, the
* "operant" as defined with addAction and a boolean indicating whether this
* @param object &$user user getting the menu
* @param array $levels current path of user in (sub)menu
* @param string $mapAreaId id of area in imagemap
* @return array array with menu actions, can be empty
/* Gets menu actions on this object defined by this object */
$actions = $this->_getTargettedActions($this, $user, $levels,
* Gets menu actions on this object defined by objects in inventory:
$this->_getTargettedActions($ob, $user, $levels, $mapAreaId));
* Gets menu actions on this object defined by objects in this
$inv = $env->getInventory();
$this->_getTargettedActions($ob, $user, $levels,
* Gets menu actions on this object defined by the object's
$this->_getTargettedActions($env, $user, $levels, $mapAreaId));
* Gets actions that can be performed on a given object by a given user
* An array is returned with each element a pair consisting of:
* menulabel => array(verb, operant, ghosted)
* with operant one of DP_ACTION_OPERANT_ from
* dpuniverse/include/actions.php, for example:
* 'read me!' => array('read', DP_ACTION_OPERANT_MENU, FALSE)
* @param object &$ob target of actions
* @param object &$user performer of actions
* @param array $levels current path of user in (sub)menu
* @param string $mapAreaId id of area in imagemap
* @return array array with actions, can be empty
* @see getTargettedActions, _checkTargettedAction
private function &_getTargettedActions(&$ob, &$user, $levels = NULL,
$ob_actions = is_null($mapAreaId) ? $ob->getActionData()
: $ob->getMapAreaActions($mapAreaId);
if (FALSE === $ob_actions || !count($ob_actions)) {
$is_registered = isset($user->isRegistered)
&& $this->isRegistered === TRUE;
$is_admin = isset($user->isAdmin) && $user->isAdmin === TRUE;
foreach ($ob_actions as $verb => &$a_d) {
foreach ($a_d as $action_data) {
if (FALSE === $this->_addTargettedAction($t_actions,
$verb, $action_data, $ob, $user, $level, $levels,
$is_registered, $is_admin)) {
foreach ($ob_actions as $action_data) {
if (FALSE === $this->_addTargettedAction($t_actions, NULL,
$action_data, $ob, $user, $level, $levels,
$is_registered, $is_admin)) {
* Adds action that can be performed on a given object by a given user
* Used by _getTargettedActions.
* @param array &$t_actions targetted actions found so far
* @param string $verb associated verb for this action
* @param array &$actionData complete action data
* @param object &$ob target of actions
* @param object &$user performer of actions
* @param integer $level submenu level, starts at 0
* @param array $levels sublevel titles
* @param boolean $isRegistered is performer a registered user?
* @param boolean $isAdmin is performer an administrator?
* @return array array with actions, can be empty
* @see _getTargettedActions
private function _addTargettedAction(&$t_actions, $verb, &$actionData,
&$ob, &$user, $level, &$levels, $isRegistered, $isAdmin)
$verb_data = $operant = NULL;
$verb_data = $ob->getActionData($actionData[2],
$operant = $verb_data[2];
$operant = <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_OPERANT_MENU">DP_ACTION_OPERANT_MENU</a>;
$operant = $actionData[2];
$operant = $operant[0]->{$operant[1]}($verb, $this, $ob,
} elseif (DP_ACTION_OPERANT_METHOD === $operant) {
$operant = $this->getActionOperant($verb, $this, $ob,
DP_ACTION_OPERANT_METHOD_MENU === $operant) {
< $level - $add_operant) {
&& count($actionData[0]) < $level) {
for ($i = 0; $i < $level; $i++) {
$action_menu_title = !is_array($actionData[0])
? $actionData[0] : $actionData[0][$i];
if (FALSE !== ($pos = dp_strrpos($action_menu_title, '#'))) {
$action_menu_title = dp_substr($action_menu_title,
if (0 === $i && $levels[0] === $action_menu_title) {
if ($levels[$i] <> $action_menu_title) {
$this->_checkTargettedAction($verb, $actionData, $ob,
$user, $isRegistered, $isAdmin)) {
$ghosted = <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_AUTHORIZED_DISABLED">DP_ACTION_AUTHORIZED_DISABLED</a> & $actionData[4];
$verb_data = $ob->getActionData($actionData[2],
if (!$get_operant && FALSE === $this->_checkTargettedAction(
$actionData[2], $verb_data, $ob, $user, $isRegistered,
$ghosted = <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_AUTHORIZED_DISABLED">DP_ACTION_AUTHORIZED_DISABLED</a> & $verb_data[4];
if (!$get_operant || $ghosted || ($level < (!is_array($actionData[0])
? 1 : count($actionData[0])))) {
$titles =& $actionData[0];
$title = !is_array($titles) ? $titles : $titles[$level];
$icon = $icon_over = FALSE;
} elseif (3 == count($tmp)) {
$is_submenu = $cnt > $level + 1
- ($ghosted && $level + 1 === $cnt ? 0 : $add_operant);
? array($verb, $actionData[2], $ghosted, $ob, $icon,
: array($actionData[1], (is_null($verb_data)
? <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_OPERANT_MENU">DP_ACTION_OPERANT_MENU</a> : $verb_data[2]), $ghosted, $ob,
$icon, $icon_over, $is_submenu);
$t_actions[$title] = $data;
} elseif ($get_operant) {
$data = $operant[0]->{$operant[1]}($verb, $this, $ob,
} elseif (DP_ACTION_OPERANT_METHOD_MENU === $operant) {
$data = $ob->getActionOperantMenu($verb, $this, $ob,
* Checks if an action can be included in an action menu
* Processes DP_ACTION_TARGET_, DP_ACTION_SCOPE_ and DP_ACTION_AUTHORIZED_
* directives of the action.
* @param string $verb associated verb for this action
* @param array &$actionData complete action data
* @param object &$ob target of actions
* @param object &$user performer of actions
* @param boolean $isRegistered is performer a registered user?
* @param boolean $isAdmin is performer an administrator?
* @return boolean TRUE to include action, FALSE to exclude
* @see getTargettedActions, _getTargettedActions
private function _checkTargettedAction($verb, &$actionData, &$ob, &$user,
$isRegistered = FALSE, $isAdmin = FALSE)
$target =& $actionData[3];
if (is_array($target) && 2 === count($target) && isset($target[0])
&& is_object($target[0]) && isset($target[1])
if ($target === <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_TARGET_NONE">DP_ACTION_TARGET_NONE</a>) {
$compare = $compare | <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_TARGET_SELF">DP_ACTION_TARGET_SELF</a>;
if (isset($this->isLiving) && TRUE === $this->isLiving) {
$compare = $compare | <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_TARGET_LIVING">DP_ACTION_TARGET_LIVING</a>;
if (isset($this->isUser) && TRUE === $this->isUser) {
$compare = $compare | <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_TARGET_USER">DP_ACTION_TARGET_USER</a>;
&& FALSE !== ($ob_env = $ob->getEnvironment())
&& $this_env === $ob_env) {
$compare = $compare | <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_TARGET_OBJENV">DP_ACTION_TARGET_OBJENV</a>;
$compare = $compare | <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_TARGET_OBJINV">DP_ACTION_TARGET_OBJINV</a>;
if (!($target & $compare)) {
$scope =& $actionData[5];
if ($scope & <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_SCOPE_SELF">DP_ACTION_SCOPE_SELF</a>) {
if ($scope & <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_SCOPE_INVENTORY">DP_ACTION_SCOPE_INVENTORY</a>) {
if ($user->getEnvironment() !== $ob) {
if ($scope & <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_SCOPE_ENVIRONMENT">DP_ACTION_SCOPE_ENVIRONMENT</a>) {
if ($user->getEnvironment() !== $ob->getEnvironment()) {
if ($auth & <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_AUTHORIZED_GUEST">DP_ACTION_AUTHORIZED_GUEST</a>) {
if (TRUE === $isRegistered) {
} elseif ($auth & <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_AUTHORIZED_REGISTERED">DP_ACTION_AUTHORIZED_REGISTERED</a>) {
if (TRUE !== $isRegistered) {
} elseif ($auth & <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_AUTHORIZED_ADMIN">DP_ACTION_AUTHORIZED_ADMIN</a>) {
* Gets the full action string for an action and some other data
* Used by getActionsMenu.
* @param array &$actionData action data array
* @param object &$user performer of actions
* @return array data structure with action info
private function _getActionsMenuFullAction(&$actionData, $user)
$is_submenu = is_array($actionData) && $actionData[6];
$gstyle = $gdstyle = $gdsstyle = '';
if (!$is_submenu && is_array($actionData)) {
$action = $actionData[0];
$operant = $actionData[1];
$ghosted = $actionData[2];
$defined_by = $actionData[3];
$operant = $operant[0]->{$operant[1]}($action, $this,
} elseif (DP_ACTION_OPERANT_METHOD === $operant) {
$operant = $this->getActionOperant($action, $this,
DP_ACTION_OPERANT_METHOD_MENU === $operant) {
} elseif (<a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_OPERANT_MENU">DP_ACTION_OPERANT_MENU</a> === $operant) {
$actionstr = isset($user->_GET['map_area_id']) ? $action
: $action . ' ' . $this->uniqueId;
} elseif (<a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_OPERANT_NONE">DP_ACTION_OPERANT_NONE</a> === $operant) {
$actionstr = $action . ' ' . $operant;
if (isset($user->inputMode)
&& 'say' === $user->inputMode
$actionstr = '/' . $actionstr;
/* DP_ACTION_OPERANT_COMPLETE */
$actionstr = $action . ' ';
if (isset($user->inputMode) && 'say' === $user->inputMode) {
$actionstr = dp_text('say') === $action ? ''
$actionstr = is_string($actionData) ? $actionData : '';
return array($actionstr, $send_action, $ghosted, $gstyle, $gdstyle,
* Creates a new imagemap area which can be used by actions
* Creates an area for the imagemap $mapName. Make sure the right <map> and
* <area> HTML is included when this object is viewed. The image should have
* The id given with $mapAreaId can be used by others methods, such as
* addItem or addAction to refer to this area.
* The shape of the imagemap given with $mapAreaShape is one of:
* See your HTML reference for a full explanation of these types.
* The coordinates of the shape should be given with $mapAreaCoords, for
* example, the coordinates for a rectangle:
* See your HTML reference for a full explanation about coordinates.
* An optional tooltip can be shown when hovering over this area with the
* mouse with $mapAreaAlt.
* @param string $mapName name of imagemap
* @param string $mapAreaId id of area in this map
* @param string $mapAreaShape cirlce, poly, rect
* @param string $mapAreaCoords x,y,... (depends on shape)
* @param string $mapAreaAlt mouse tooltip for this area
* @see getMapArea, addMapAreaAction, removeMapAreaAction,
* getMapAreaActions, getMapAreaHtml, getActionsMenu
function setMapArea($mapName, $mapAreaId, $mapAreaShape, $mapAreaCoords,
if (!isset($this->mMapAreas[$mapName])) {
$this->mMapAreas[$mapName] = array();
$this->mMapAreas[$mapName][$mapAreaId] =
array($mapAreaShape, $mapAreaCoords, $mapAreaAlt);
* Gets data of one, multiple or all imagemap areas
* If no arguments are given data of all imagemap areas is returned. If an
* imagemap name is given, all data for that map is returned, or FALSE if
* the imagemap is not defined. If both the map name and an id of the area
* in the map are given, data for that area is returned, or FALSE if not
* @param string $mapName name of imagemap
* @param string $mapAreaId id of area in imagemap
* @return mixed array with imagemap areas, can be empty, or FALSE
* @see setMapArea, addMapAreaAction, removeMapAreaAction,
* getMapAreaActions, getMapAreaHtml, getActionsMenu
function getMapArea($mapName = NULL, $mapAreaId = NULL)
return !isset($this->mMapAreas[$mapName]) ? FALSE
: $this->mMapAreas[$mapName];
return !isset($this->mMapAreas[$mapName])
|| !isset($this->mMapAreas[$mapName][$mapAreaId]) ? FALSE
: $this->mMapAreas[$mapName][$mapAreaId];
* Adds an action to a map area so it becomes clickable there
* Creates a menu item labelled $actionMenuTitle on the imagemap area with
* id $mapAreaId. When clicked, executes $action for that user.
* Can optionally be associated with an existing action by giving an
* $actionVerb and $actionVerbKey. Normal map area menu actions always
* appear. By associating it to a regular action, all its settings such as
* scope, target and authorization are used. Use addAction to set up such
* a map area action, as addAction will call addMapAreaAction with the right
* verb and verb key (a verb key is needed because there can be multiple
* @param string $mapAreaId id of area in imagemap
* @param string $actionMenuTitle title of menu item
* @param string $action action executed when clicked
* @param string $actionVerb optional associated verb
* @param string $actionVerbKey optional index of this verb
* @see setMapArea, getMapArea, removeMapAreaAction,
8 getMapAreaActions, getMapAreaHtml, getActionsMenu
$actionVerb = NULL, $actionVerbKey = NULL)
if (!isset($this->mMapAreaActions[$mapAreaId])) {
$this->mMapAreaActions[$mapAreaId] = array();
$new_area_data = array($actionMenuTitle, $action, $actionVerb,
foreach ($this->mMapAreaActions[$mapAreaId] as $area_data) {
if ($area_data === $new_area_data) {
$this->mMapAreaActions[$mapAreaId][] = $new_area_data;
* Removes a map area action
* Either 1) the action menu title, 2) the executed action string when
* clicked (while the title is NULL) or 3) both can be given. Removes the
* @param string $mapAreaId id of area in imagemap
* @param string $actionMenuTitle title of menu item
* @param string $action action executed when clicked
* @see setMapArea, getMapArea, addMapAreaAction, getMapAreaActions,
* getMapAreaHtml, getActionsMenu
if (!isset($this->mMapAreaActions[$mapAreaId])
foreach ($this->mMapAreaActions[$mapAreaId] as $i => &$area_data) {
|| $area_data[0] === $actionMenuTitle)
&& (is_null($action) || $area_data[1] === $action)) {
unset($this->mMapAreaActions[$mapAreaId][$i]);
* Gets all map area actions for all areas or for a given area
* @param string $mapAreaId id of area in imagemap
* @return array array with map area data, can be empty
* @see setMapArea, getMapArea, addMapAreaAction,
* removeMapAreaAction, getMapAreaHtml, getActionsMenu
return $this->mMapAreaActions;
return !isset($this->mMapAreaActions[$mapAreaId]) ? FALSE
: $this->mMapAreaActions[$mapAreaId];
* Gets HTML for all imagemaps or the given imagemap for inclusion in page
* Constructs the right HTML with <map> and <area> tags and onclick events.
* Returns an empty string of no map data was found. Used by getBody.
* @param string $mapName name of imagemap
* @return string HTML for one or more imagemaps, or empty string.
* @see getBody, setMapArea, getMapArea, addMapAreaAction,
* removeMapAreaAction, getMapAreaActions, getActionsMenu
foreach ($map_names as $map_name) {
if (!isset($this->mMapAreas[$mapName])) {
$rval = "<map name=\"{ $mapName}\">\n ";
foreach ($this->mMapAreas[$mapName] as
$map_area_id => &$map_area_data) {
$actions = $this->getMapAreaActions($map_area_id);
$nr_of_actions = count($actions);
if (0 === $nr_of_actions) {
} elseif (1 === $nr_of_actions) {
$rval .= "<area shape=\"{ $map_area_data[0]}\" "
. "coords=\"{ $map_area_data[1]}}\" "
. "href=\"javascript:void(0)\" "
. "alt=\"{ $alt}\" title=\"{ $alt}\" "
. "onclick=\"get_map_area_actions('{ $mapName}', "
. "'{ $map_area_id}', '{ $this->uniqueId}', event)\" "
. "style=\"cursor: pointer\" />\n";
* Tries if a user action can be performed on this object
* When a user doesn't use the action menu, but gives a command such as
* 'take beer', or when an NPC performs an action, the system doesn't know
* which object defines the action. Therefore, it calls this method in all
* objects which are eligable to perform this action. Objects are searched
* for, in this order, in the object performing the action, its inventory,
* in its environment, and the environment itself.
* performActionSubject returns TRUE if the action was completed by this
* object, or FALSE if this action was not directed at this object, in which
* case the system will try the next object, if any.
* @param string $action the complete input string
* @param object &$living user or npc performing the action
* @return boolean TRUE for action completed, FALSE otherwise
$action = dp_strlen($action) == 1 ? $say : $say . ' '
$action = dp_strlen($action) == 1 ? $tell : $tell . ' '
if (FALSE !== ($x = dp_strpos($action, ' '))) {
if (isset($this->isLiving) && TRUE === $this->isLiving
/* Try inventory and environment */
if ($ob !== $this && TRUE ===
if (isset($this->mActions[$verb])) {
reset($this->mActions[$verb]);
$action_data = $this->mActions[$verb][key($this->mActions[$verb])];
} elseif (isset($this->mActions[$action])) {
reset($this->mActions[$verb]);
$action_data = $this->mActions[$action]
[key($this->mActions[$verb])];
} elseif (isset($this->mActionAliases[$verb])) {
reset($this->mActionAliases[$verb]);
$alias =& $this->mActionAliases[$verb]
[key($this->mActionAliases[$verb])];
$action_data = $this->mActions[$alias[0]][$alias[1]];
} elseif (isset($this->mActionAliases[$action])) {
reset($this->mActionAliases[$verb]);
$alias =& $this->mActionAliases[$action]
[key($this->mActionAliases[$verb])];
$action_data = $this->mActions[$alias[0]][$alias[1]];
$scope =& $action_data[5];
if ($scope & <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_SCOPE_SELF">DP_ACTION_SCOPE_SELF</a>) {
if ($scope & <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_SCOPE_INVENTORY">DP_ACTION_SCOPE_INVENTORY</a>) {
if ($scope & <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_SCOPE_ENVIRONMENT">DP_ACTION_SCOPE_ENVIRONMENT</a>) {
$auth =& $action_data[4];
if ($auth === <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_AUTHORIZED_GUEST">DP_ACTION_AUTHORIZED_GUEST</a>) {
if (TRUE === $living->isRegistered) {
} elseif ($auth === <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_AUTHORIZED_REGISTERED">DP_ACTION_AUTHORIZED_REGISTERED</a>) {
if (TRUE !== $living->isRegistered) {
} elseif ($auth === <a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---actions.php.html#defineDP_ACTION_AUTHORIZED_ADMIN">DP_ACTION_AUTHORIZED_ADMIN</a>) {
if (TRUE !== $living->isAdmin) {
$call_method = $action_data[1];
$call_obj =& $action_data[1][0];
$call_method = $action_data[1][1];
(bool)$call_obj->{$call_method}(!$is_phrase ? $verb : $action,
* Called when certain events occur, given with $name. The property
* lastEventTime is set to a unix time stamp. Calls eventDpObject.
* @param object $name Name of event
* @param mixed $args One or more arguments, depends on event
final function event($name)
$this->lastEventTime = time();
* Called when certain events occur, given with $name.
* @param object $name Name of event
* @param mixed $args One or more arguments, depends on event
* Called by the universe object, checks if this object can be removed.
* To save some memory, the universe object will call this method in objects
* with no environment, which haven't been referenced for a while.
* If no users or special object using the isNoCleanUp property are present,
* the object and all of its inventory is destroyed.
foreach ($this->mCheckPresentObjects as $i => &$ob) {
dp_text("handleCleanUp() called in %s: not removed",
if ($ob->isUser || $ob->isNoCleanUp) {
dp_text("handleCleanUp() called in %s: not removed",
* Can we be dragged on the screen by the given user?
* Experimental mouse dragging of objects.
* @param object &$by_who subject that wants to drag us
* @return boolean TRUE if $by_who may drag us, FALSE otherwise
* Reports graphical movement of this object to other objects
* Called by the client-side dpclient-js.php script after an object was
* dragged by a user. Coordinates are passed as 'x' and 'y' in a GET
* request and passed on to other users in the same environment.
if ($ob !== $user && $ob->isUser) {
$user->getTitle(<a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---title_types.php.html#defineDPUNIVERSE_TITLE_TYPE_DEFINITE">DPUNIVERSE_TITLE_TYPE_DEFINITE</a>),
$this->getTitle(<a href="../DutchPIPE/dpuniverse_include/_dpuniverse---include---title_types.php.html#defineDPUNIVERSE_TITLE_TYPE_DEFINITE">DPUNIVERSE_TITLE_TYPE_DEFINITE</a>)));
$ob->tell('<reportmove id="' . $this->uniqueId . '" left="'
. $user->_GET['x'] . '" top="' . $user->_GET['y']
. '"> </reportmove>');
* Sets a heap object of a given amount in this object
* @param string $pathname path to code from universe base path
* @param string $idProperty unique property to identify heap object
* @param int|float$amount amount, depends on heap type
* @return boolean TRUE for success, FALSE otherwise
if ($ob->{$idProperty}) {
$heap_ob->amount = $amount;
* Gets the amount of a given heap object
* @param string $idProperty unique property to identify heap object
* @return int|floatamount, depends on heap type
if ($ob->{$idProperty}) {
* Sets the amount of credits in the inventory of this object
* @param int|float$credits amount of credits
* @return boolean TRUE for success, FALSE otherwise
return $this->setHeapAmount(<a href="../DutchPIPE/config/_config---dpuniverse-ini.php.html#defineDPUNIVERSE_OBJ_PATH">DPUNIVERSE_OBJ_PATH</a> . 'credits.php',
* Gets the amount of credits in the inventory of this object
* @return int|floatamount of credits
* Makes the given method callable from the DutchPIPE client
* @param string $methodName method to add
* @see removeValidClientCall, isValidClientCall
$this->mValidClientCalls[] = $methodName;
* Removes the given method from callable methods from the DutchPIPE client
* @param string $methodName method to remove
* @see addValidClientCall, isValidClientCall
if (in_array($methodName, $this->mValidClientCalls, TRUE)) {
unset($this->mValidClientCalls[$methodName]);
* May the given method be called in this object from the DutchPIPE client?
* @param string $methodName method to check
* @return boolean TRUE if the method can be called, FALSE otherwise
* @see addValidClientCall, removeValidClientCall
return in_array($methodName, $this->mValidClientCalls, TRUE);
* Gets the width in pixels of the title image
* @return mixed Integer with width in pixels, FALSE for unknown
$this->_initTitleImgDimensions();
return is_null($title_img_width) ? FALSE : $title_img_width;
* Gets the height in pixels of the title image
* @return mixed Integer with height in pixels, FALSE for unknown
$this->_initTitleImgDimensions();
return is_null($title_img_height) ? FALSE : $title_img_height;
* Sets the titleImgWidth and titleImgHeight properties
* Sets these properties in this object if they aren't set and if PHP is
* installed with the GD library.
* @see getTitleImgWidth, getTitleImgHeight
private function _initTitleImgDimensions()
<a href="../DutchPIPE/config/_config---dpuniverse-ini.php.html#defineDPUNIVERSE_AVATAR_STD_URL">DPUNIVERSE_AVATAR_STD_URL</a>
=> <a href="../DutchPIPE/config/_config---dpuniverse-ini.php.html#defineDPUNIVERSE_AVATAR_STD_PATH">DPUNIVERSE_AVATAR_STD_PATH</a>,
<a href="../DutchPIPE/config/_config---dpuniverse-ini.php.html#defineDPUNIVERSE_AVATAR_CUSTOM_GUEST_URL">DPUNIVERSE_AVATAR_CUSTOM_GUEST_URL</a>
=> <a href="../DutchPIPE/config/_config---dpuniverse-ini.php.html#defineDPUNIVERSE_AVATAR_CUSTOM_GUEST_PATH">DPUNIVERSE_AVATAR_CUSTOM_GUEST_PATH</a>,
<a href="../DutchPIPE/config/_config---dpuniverse-ini.php.html#defineDPUNIVERSE_AVATAR_CUSTOM_REG_URL">DPUNIVERSE_AVATAR_CUSTOM_REG_URL</a>
=> <a href="../DutchPIPE/config/_config---dpuniverse-ini.php.html#defineDPUNIVERSE_AVATAR_CUSTOM_REG_PATH">DPUNIVERSE_AVATAR_CUSTOM_REG_PATH</a>,
<a href="../DutchPIPE/config/_config---dpuniverse-ini.php.html#defineDPUNIVERSE_IMAGE_URL">DPUNIVERSE_IMAGE_URL</a>
=> <a href="../DutchPIPE/config/_config---dpuniverse-ini.php.html#defineDPUNIVERSE_IMAGE_PATH">DPUNIVERSE_IMAGE_PATH</a>
foreach ($url2path as $url => $path) {
$title_img = str_replace($url, $path, $this->titleImg);
if ($this->titleImg !== $title_img) {
list($width, $height) = $image_info;
|