/*
 * An effect to put someone to sleep.  
 *
 * This effect takes one argument:
 * (int) time: the time until the person will wake up.
 *
 * If the person is already asleep, <time> is added to the
 * length of time remaining until he wakes.
 *
 * NB: the value of the "passed out" indicates an object's 
 * current state thus:
 *   0 = not asleep, not unconscious
 *   1 = not asleep, unconscious      (OUT_COLD)
 *   2 = asleep, not unconscious      (ASLEEP)
 *   3 = asleep, unconscious          (OUT_COLD + ASLEEP)
 *
 * The purpose of this effect is to modify the above states
 * accordingly when a player falls asleep or wakes up.
 *
 *
 * This effect is classified as "asleep"
 * This effect has a shadow associated with it.
 *
 */
#include <effect.h>
#include <living.h>
#define CLASS "asleep"
#define SHADOW "/std/shadows/other/asleep"
/** @ignore yes */
string query_classification() { return CLASS; }
/** @ignore yes */
string query_shadow_ob() { return SHADOW; }
/** @ignore yes */
int beginning( object player, int time ) {
  /* Determine the player's current state */
  int state = player->query_property( PASSED_OUT );
  /* If his state == 0, ie he is alert, give him a message
   * warning him that he's fallen asleep. */
  if( !state )
    tell_object( player, "You fall asleep.\n" );
  /* If he currently alert or is unconscious but not asleep,
   * put him to sleep now.  NB: If he's both asleep and
   * unconscious, this will put him over the top but the next 
   * step will sort out his states */
  if( state != ASLEEP )
    state += ASLEEP;
  /* If he's beyond recognizable values, set him back to the
   * max value */
  if( state > ASLEEP + OUT_COLD ) 
    state = ASLEEP + OUT_COLD;
  /* Now we know what his new value for the "passed out" property
   * should be.  Add that and continue on with making him appear
   * to be asleep. */
  player->add_property( PASSED_OUT, state );
  player->set_position( "lying" );
  player->add_extra_look( this_object() );
  /* Don't put him to sleep for negative time */
  if( time < 0 ) time = 0;
  player->submit_ee( 0, time, EE_REMOVE );
  return time;
} /* beginning() */
/** @ignore yes */
int merge_effect( object player, int old_time, int new_time ) {
  /* We already know he's asleep.  Just add (or subtract)
   * the new time from the remaining duration of the old effect
   * and let it continue */
  new_time += player->expected_tt( 1, player->effects_matching( 
                  CLASS )[0] );
  /* Still don't put him to sleep for a negative time */
  if( new_time < 0 ) new_time = 0;
  player->submit_ee( 0, new_time, EE_REMOVE );
  return new_time;
} /* merge_effect() */
/** @ignore yes */
void end( object player ) {
  /* We must determine what state he's in and therefore what state
   * to leave him in when he wakes up.  Ie: is he also unconscious? */
  int state = player->query_property( PASSED_OUT );
  /* If he was both asleep and unconscious, this will make him
   * only unconscious.  If he was only asleep, this will remove
   * the property.  If he was only unconscious but not asleep,
   * something has gone awry. */
  state -= ASLEEP;  
  if( state < 0 )         // shouldn't happen
    state = 0;
  if( state == ASLEEP ) // this shouldn't happen either
    state = 0;
  /* If he's fully alert now, remove the property and tell him
   * he's woken up.  If he's still unconscious, don't give him a
   * message and reset his "passed out" value. */
  if( !state ) {
    player->remove_property( PASSED_OUT );
    tell_object( player, "You wake up.\n" );
    player->new_parser( "yawn" );
    player->new_parser( "stretch sleepily" );
  }
  else 
    player->add_property( PASSED_OUT, state );
  player->remove_extra_look( this_object() );
} /* end() */
/** @ignore yes */
string extra_look( object player ) {
  if( !sizeof( player->effects_matching( CLASS ) ) )
    return "";
  return sprintf( "%s is asleep.\n", 
                   capitalize( player->query_pronoun() ) );
} /* extra_look() */