musicmud-2.1.6/data/
musicmud-2.1.6/data/help/
musicmud-2.1.6/data/policy/
musicmud-2.1.6/data/wild/
musicmud-2.1.6/data/world/
musicmud-2.1.6/doc/
musicmud-2.1.6/src/ident/
musicmud-2.1.6/src/lua/
musicmud-2.1.6/src/lua/include/
musicmud-2.1.6/src/lua/src/lib/
musicmud-2.1.6/src/lua/src/lua/
musicmud-2.1.6/src/lua/src/luac/
/* 
 * MusicMUD - Look module
 * Copyright (C) 1998-2003 Abigail Brady
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 */

#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>

#include "musicmud.h"
#include "verbs.h"
#include "util.h"
#include "State.h"
#include "flagnames.h"
#include "misc.h"
#include "util.h"
#include "wordwrap.h"
#include "events.h"
#include "trap.h"
#include "pflags.h"
#include "vsprintf.h"
#include "prep.h"
#include "match.h"
#include "levels.h"
#include "href.h"
#include "shared.h"
#include "body.h"

#include <map>

#define INDENT "  "

#define WIERD get_wierd()

#define MODULE "look"

static string short_desc(MudObject *what) {
  string what2;
  char name[128];
  int stat = state(what);
  sprintf(name, "short_desc.%i", stat);
  const char *shortdesc = what->get(name);
  if (shortdesc) {
    what2 = shortdesc;
    return what2;
  }
  shortdesc = what->get("short_desc");
  if (shortdesc) {
    what2 = shortdesc;
  } else {
    const char *w3 = what->get("long");
    if (!w3)
      w3 = what->get("name");
    if (!w3)
      w3 = what->get("short");
    if (!w3)
      w3 = "???";
    what2 = w3;
  }
  if (!is_mobile(what) && !is_player(what)) {
    if (!what->get_flag(FL_STATED) && is_closed(what)) {
      
      if (is_locked(what) && what->get_flag(FL_CANLATCH)) {
	what2 += ", latched shut";
      } else {
		
	what2 += ", closed";
	if (is_locked(what))
	  what2 += " and locked";
	else
	  what2 += ".";
      }
    }
    if (what->get_flag(FL_ONFIRE)) {
      what2 += " ^y(^Yablaze^y)^n";
    } else if (is_lit(what)) {
      what2 += " ^y(^Ylit^y)^n";
    } 
    if (what->get_flag(FL_OUTDOORS)) {
      what2 += " ^c(^Coutdoors^c)^n";
    }
  }
  what2[0] = toupper(what2[0]);
  return what2;      
}

map<TeleObject, NewWorld > collapse_sitters(const NewWorld &inwhat) {
  
  map<TeleObject, NewWorld> m;
  
  MudObject *o;
  int i;

  foreach((&inwhat), o, i) {
    if (MudObject *ch=o->get_object(KEY_SITON))
      {
	TeleObject t = TeleObject(o->owner, ch, o->get_int(KEY_SITONN, 0));
	if (m.find(t)!=m.end()) {
	  m[t].add(o);
	} else {
	  NewWorld w;
	  w.add(o);
	  m[t] = w;
	}
      }
  }

  return m;
}

static void dolook_objects(MudObject *who, bool iswiz) {
  MudObject *o;
  int i;
  MudObject *where = who->owner;

  map<string, TeleWorld> m;
  map<string, string> plu;

  TeleWorld stuff;

  NewWorld sitters;

  foreach(where->children, o, i) 
    if (visible_to(who, o) &&
	!is_player(o) &&
	!is_mobile(o) &&
	!o->get_flag(FL_FLOOR) &&
	name(o) &&
	o != who) {
      if (o->get_object(KEY_SITON)) {
	if (low_enough(who, o))
	  sitters.add(o);
      } else {	
	stuff.add(o);
      }
    }

  MudObject *port = where->get_object("port");
  if (port && port != where)
    foreach(port->children, o, i)
      if (o->get_flag(FL_SHIP) && !o->get_flag(FL_BROKEN))
	who->printf("  %#M\n", o);

  for (i=0;i<where->array_size("tele");i++) {
    MudObject *teleob = where->array_get_object("tele", i);
    int vis = where->array_get_int("tele", i, "vis", 0);
    if (vis==1) {
      int m = where->array_get_int("tele", i, "count", 1);
      if (m > 10) 
	m = 16;
      for (int i=0;i<m;i++) {
	stuff.add(TeleObject(where, teleob, i));
      }
    }
  }
  
  for (int i=0;i<stuff.getsize();i++) {
    TeleObject o = stuff.get(i);
#if 0
    MudObject *rider = o->get_object("$rider");
    if (rider && rider->owner == o->owner)
      continue;
#endif
    
    const char *n = 0;
    string ns;
    
    n = name(o, 0, 1);
    const char *nons = name(o, 0, 1, 1);
    
    string addend = "";
    
    if (!o->get_flag(FL_STATED)
	&& (state(o)||
	    o->get_flag(FL_CONTAINER)||
	    o->get_flag(FL_CANOPEN))) {
      if (streq(nons, n)) {
	if (state(o)==0)
	  addend += "open, ";
	else if (state(o)==1)
	  addend += "closed, ";
	else if (state(o)==2)
	  if (o->get_flag(FL_CANLATCH))
	    addend += "latched, ";
	  else
	    addend += "locked, ";
      }
    }
    
    if (o->get_flag(FL_ONFIRE))
      addend += "ablaze, ";
    else if (o->get_flag(FL_LIT)) 
	addend += "lit, ";
    
    if (o->get_flag(FL_OUTDOORS)) 
      addend += "outdoors, ";
    
    if (n && addend.length()) {
      addend = addend.substr(0, addend.length()-2);
      ns = ssprintf("%s (%s)", n, addend.c_str());
      n = ns.c_str();
    }
    
    if (o->get_flag(FL_INVISIBLE)) {
      if (iswiz)
	n = (ns = ssprintf("[%s]", n)).c_str();
      else
	continue;
    }
    
    if (!n)
      n = o->id;
    
    if (!n)
      continue;
    
    if (m.find(n)!=m.end()) {
      m[n].add(o);
    } else {
      TeleWorld a;
      a.add(o);
      m[n] = a;
    }
    
    if (name(o, 1) && plu.find(n)==plu.end()) {
      plu[n] = name(o, 1, 1);
    }
  }

  map<string, TeleWorld>::iterator it = m.begin();
  while (it != m.end()) {
    
    string ph = it->first;
    int hidden = 0;

    if (ph[0]=='[') {
      ph = ph.substr(1, ph.length()-2);
      hidden = 1;
    }

    const char *num = "";
    if (it->second.getsize() != 1) {
      ph = plural_phrase(remove_articles(ph.c_str()));
      if (plu.find(it->first)!=plu.end()) {
	ph = plu.find(it->first)->second;
      }
      num = numbertostring(it->second.getsize());
    }
    
    if (num[0])
      if (!hidden) {
	who->printf("  %#s %s\n", num, ph.c_str());
      } else {
	who->printf("  [%#s %s]\n", num, ph.c_str());
      }
    else 
      {
	TeleObject obj = it->second.get(0);
	if (const char *pos=obj->get("short_desc"))
	  ph = pos;
	if (const char *pos=obj->array_get("short_desc", state(obj)))
	  ph = pos;

	string c, e;
	make_href(who, obj, c, e);

	if (iswiz) {
	  string extra = "";
	  if (obj->id[0]!='@' && obj->get_object("start")!=obj->owner) {
	    extra = ssprintf(" [start:%s]", obj->get("start"));
	  }
	  if (!hidden) {
	    who->printf("  %s%#s%s (%s)%s\n", c.c_str(), ph.c_str(), e.c_str(), obj->id, extra.c_str());
	  } else {
	    who->printf("  [%s%#s%s (%s)%s]\n", c.c_str(), ph.c_str(), e.c_str(), obj->id, extra.c_str());
	  }
	} else {
	  if (!hidden)
	    who->printf("  %s%#s%s\n", c.c_str(), ph.c_str(), e.c_str());
	  else
	    who->printf("  [%s%#s%s]\n", c.c_str(), ph.c_str(), e.c_str());
	}
      }

    it++;
  }  

  if (sitters) {
    map<TeleObject, NewWorld> s = collapse_sitters(sitters);
    for (typeof(s.begin()) i = s.begin();i!=s.end();i++) {
      who->printf("  Upon %M %s %W.\n", i->first.what, plural(i->second)?"are":"is", &i->second);
    }
  }

  if (iswiz) {
    int any = 0;
    MudObject *f = floor(where);
    if (f) {
      who->printf("  {%M %s} ", f, f->id);
      any = 1;
    }
    if (who->get_int("televirt", 0)) {
      for (i=0;i<where->array_size("tele");i++) {
	MudObject *teleob = where->array_get_object("tele", i);
	int vis = where->array_get_int("tele", i, "vis", 0);
	if (teleob && !vis && !teleob->get_flag(FL_FLOOR))
	  {
	    if (!any)
	      who->printf("  ");
	    int howmany = where->array_get_int("tele", i, "count", 1);
	    if (howmany == 1) {
	      who->printf("{%M %s} ", teleob, teleob->id);
	    } else {
	      who->printf("{%s %s} ", give_number(teleob, howmany).c_str(), teleob->id);
	    }
	    any = 1;
	  }
      }
    }
    if (any) {
      who->printf("\n");
    }
  }  
}

static bool isplayer(MudObject *what) { return is_player(what); }
static bool ismobile(MudObject *what) { return is_mobile(what); }

static string comma_list(MudObject *who, int iswiz, const NewWorld &list) {
  MudObject *obj;
  int i;
  string s = "";
  foreach_alpha((&list), obj, i) {
    if (i) {
      if (i == list.getsize()-1)
	s += " and ";
      else
	s += ", ";
    }

    string c, e;
    make_href(who, obj, c, e);
    
    s += c;

    if (obj->get("lookname"))
      s += obj->get("lookname");
    else if (obj->get("name"))
	s += obj->get("name");
    else
      s += obj->id;

    s += e;

    const char *bod = body(obj);

    if (const char *m=obj->get("$piggy")) {
      const char *n = get_mog_noun(m);
      if (n) {
	if (obj->get("mogname"))
	  s = obj->get("mogname");
	s += " the ";
	s += n;
	bod = get_mog_noun(m, &animal_t::body);
      }
    }

    string paren = "";

    if ((obj->get_flag(FL_STORE) || (obj->owner->get_flag(FL_STORE) 
				    && obj->owner->get_object("shopmob")==obj))
	&& obj->owner == who->owner) 
      paren += ", a trader";
  
    if (streq(bod, "human")) {
      MudObject *wf = wornfrom(obj);
      int notop = 0, nolegs = 0;
      if (!worn_on(obj, "body", 0) && (!wf || worn_on(obj, "body", 0)))
	notop = 1;
      if (!worn_on(obj, "legs", 0) && (!wf || worn_on(obj, "legs", 0)))
	nolegs = 1;
      if (notop && nolegs)
	paren += ", naked";
      else if (notop)
	paren += ", topless";
    }

    if (MudObject *o=weapon(obj)) {
      TeleObject wep(obj, o, 0);
      paren += ssprintf("%|, wielding %P", obj, wep);
    }

    if (paren.length()) {
      s += " (";
      s += paren.substr(2);
      s += ")";
    }

    if (iswiz && is_mobile(obj)) {
      s += " (";
      s += obj->id;
      s += ")";
    }
  }
  return s;
}

static string comma_list(MudObject *who, int iswiz, MudObject *contents) {
  NewWorld w;
  w.add(contents);
  return comma_list(who, iswiz, w);
}

static void use(bool iswiz, MudObject *who, MudObject *what, const string &txt)
{
  if (iswiz)
    who->printf(INDENT"%#s (%s)\n", txt.c_str(), what->id);
  else
    who->printf(INDENT"%#s\n", txt.c_str());
}

static void dolook_mobplayer(MudObject *who, bool iswiz, fn_t t, MudObject *where=0) {

  if (!where) where = who->owner;

  NewWorld standing;
  NewWorld sleeping;
  NewWorld resting;
  NewWorld swimming;
  NewWorld floored;
  NewWorld cardboard;
  NewWorld ridden;
  NewWorld otherside;
  NewWorld flying;
  NewWorld sitters;

  MudObject *obj;
  int i;

  foreach(where->children, obj, i) {
    MudObject *m = mount(obj);
    if (m) {
      ridden.add(*m);
    }
  }

  foreach(where->children, obj, i) {
    if (t && !t(obj)) continue;
    
    if (ridden.get(obj->id)) 
      continue;
    
    if (obj == who)
      continue;

    if (!obj->get("name"))
      continue;

    if (obj->get_flag(FL_INVISIBLE))
      continue;

    if (!visible_to(who, obj))
      continue;
    
    // people who are fighting
    if (MudObject *o2=obj->get_object("_fighting")) {
      if (strcmp(o2->id, obj->id)>=0 || (o2==who) || o2->get_object("_fighting")!=obj) {
	use(0, who, obj, ssprintf("%#s fighting %s.", comma_list(who, iswiz, obj).c_str(),
				      o2==who?"you":name(o2)));
	continue;
      }

      continue;
    }

    // people who are riding something
    if (MudObject *m=mount(obj)) {
      const char *mverb = m->get("riding_verb")?:"riding";

      if (Player *p=dynamic_cast<Player*>(obj)) {
	if (!p->p) {
	  use(0, who, obj, ssprintf("A cardboard cutout of %s%| %s %P.", 
				    comma_list(who, iswiz, obj).c_str(), 
				    obj,
				    mverb, m));
          continue;
	}
      }

      use(0, who, obj, ssprintf(m==who?"%#s%| %s you.":"%#s%| %s %P.", 
				    comma_list(who, iswiz, obj).c_str(), 
				obj,
				    mverb, m));
      continue;
    }

    if (is_player(obj)) {
      // Deal with linkdead people
      Player *p = (Player*)obj;
      if (!p->p) {
	cardboard.add(*p);
	continue;
      }
    }

    // mobiles with a short_desc
    if (is_mobile(obj)) {
      if (obj->get("short_desc") || obj->get("short_desc.0")) {
	use(iswiz, who, obj, short_desc(obj));
	continue;
      }
    }
    
    // people who are flying
    if (obj->get_flag(FL_FLYING)) {
      flying.add(*obj);
      continue;
    }

    // people who are swimming
    if (obj->get_flag(FL_SWIMMING)) {
      swimming.add(*obj);
      continue;
    }

    MudObject *on = obj->get_object(KEY_SITON);

    if (on) {
      // sitting, sleeping, or standing on anything
      sitters.add(*obj);
      continue;
    }
    
    // people who are sitting on the floor
    if (obj->get_flag(FL_SITTING)) {

      if (obj->get("setsit")) {
	use(iswiz, who, obj, build_setin(obj, setsit));
	continue;
      }
      
      floored.add(*obj);
      continue;
      
    }

    // people who are sleeping on the floor
    if (obj->get_flag(FL_SLEEPING)) {
      if (obj->get("setsleep")) {
	use(iswiz, who, obj, build_setin(obj, setsleep));
	continue;
      }

      sleeping.add(*obj);
      continue;
    }

    // people who are standing on the other side of the bridge
    const char *f1 = obj->get("$camefrom");
    const char *f2 = who->get("$camefrom");
    if (where->get_flag(FL_BROKEN) && f1 && f2 && !streq(f1, f2)) {
      otherside.add(*obj);
      continue;
    }
    
    // things with a setstand
    if (obj->get("setstand")) {
      use(iswiz, who, obj, build_setin(obj, setstand));
      continue;
    }

    // things that are standing, but have no legs (Resting)

    if (obj->get_flag(FL_NOLEGS)) {
      resting.add(*obj);
      continue;
    }

    // things that are standing, normally

    standing.add(*obj);
  }


  map<TeleObject, NewWorld> clist = collapse_sitters(sitters);
  for (map<TeleObject, NewWorld>::iterator it=clist.begin();it!=clist.end();it++) {
    NewWorld standers;
    NewWorld sleepers;
    NewWorld sitters;

    MudObject *o;
    int i;

    if (!it->second.getsize())
      continue;

    foreach((&it->second), o, i) {
      if (o->get_flag(FL_SLEEPING)) sleepers.add(*o);
      else if (o->get_flag(FL_SITTING)) sitters.add(*o);
      else standers.add(*o);
    }

    string s;
    if (standers.getsize()) {
      s += comma_list(who, iswiz, standers);
      s += " standing";
    }

    if (sitters.getsize()) {
      if (standers.getsize())
	if (sleepers.getsize())
	  s += ", ";
        else
	  s += ", and ";
      s += comma_list(who, iswiz, sitters);
      s += " sitting";
    }

    if (sleepers.getsize()) {
      if (standers.getsize() || sitters.getsize())
	s += ", and ";
      s += comma_list(who, iswiz, sleepers);
      s += " sleeping";
    }

    who->printf(INDENT"%#s on %P.\n", s.c_str(), *it->first);
  }
  
  /* Linkdead people */
  if (cardboard) {
    who->printf(INDENT "%s of %s %s here.\n", 
		cardboard.getsize()==1?"A cardboard cutout":"Cardboard cutouts",
		comma_list(who, iswiz, cardboard).c_str(),
		where->get_flag(FL_NOGRAVITY)?"floating":"standing");
  }
  
  /* People who are standing/floating */
  if (standing) {
    who->printf(INDENT "%#s %s here.\n", 
		comma_list(who, iswiz, standing).c_str(),
		where->get_flag(FL_NOGRAVITY)?"floating":"standing");
  }
  
  if (otherside) {
    who->printf(INDENT "%#s on the other side.\n", 
		comma_list(who, iswiz, otherside).c_str());
  }
  
  if (flying) {
    who->printf(INDENT "%#s flying here.\n", 
		comma_list(who, iswiz, flying).c_str());
  }

  /* People who are resting here, having no legs */
  if (resting) {
    who->printf(INDENT "%#s %s here.\n", 
		comma_list(who, iswiz, resting).c_str(),
		where->get_flag(FL_NOGRAVITY)?"floating":"resting");
  }
  
  /* People who are sleeping here */
  if (sleeping) {
    who->printf(INDENT "%#s sleeping here.\n", 
		comma_list(who, iswiz, sleeping).c_str());
  }
  
  /* People who are swimming here */
  if (swimming) {
    who->printf(INDENT "%#s swimming here.\n", 
		comma_list(who, iswiz, swimming).c_str());
  }
  
  /* People are are sitting on the floor here */
  if (floored) {
    who->printf(INDENT "%#s sitting on the floor.\n", 
		comma_list(who, iswiz, floored).c_str());
  }
  
  /* Bunnies (optional) */
  
  if (who->get_int(KEY_BUNNIED) > now) {
    if (t == ismobile) {
      who->printf(INDENT "^PBunnies^n frolicking here.\n");
    }
  }
}

static const char *exit_the(const char *what) {
  if (streq(what, "south")) return "to the south";
  if (streq(what, "north")) return "to the north";
  if (streq(what, "west")) return "to the west";
  if (streq(what, "east")) return "to the east";
  if (streq(what, "below")) return "below";
  if (streq(what, "up")) return "above";
  if (streq(what, "out")) return "the outside";
  return what;
}


static string enpara(const char *r) 
{
  string t = "";
  char lastch = 0;
  while (*r == ' ')
    r++;
  while (*r) {
    if (*r=='\n') {
      if (r[1]=='\n') {
	t += "\n  ";
	lastch = ' ';
	r+=2;
	continue;
      }
      if (lastch != ' ') {
	t += " ";
	r++;
      } else
	r++;
      continue;
    }
    t += lastch = *r;
    r++;
  }
  return t;
}
 
static void yousee(MudObject *who, MudObject *where, MudObject *ext=0) {
  int clas = 0;
  if (const char *cl=who->get("colours")) {
    if (cl && streq(cl, "classic")) {
      clas = 1;
    }
  }
  if (who->owner == where) 
    who->spec_printf("%sYou see...^n\n",  clas?"^r":"^W");
  else {
    string hell = format_roomname(where, who, "^{D", "^}");
    who->spec_printf("%s%#s ^W%s...\n",  clas?"^r":"^W", hell.c_str(), exit_the(ext->get("short")));
  }
}

static string lowercase(string what)
{
  string t;
  const char *o = what.c_str();
  while (*o) {
    t += tolower(*o);
    o++;
  }
  return t;
}

static void youare(MudObject *player, MudObject *room) {
  string y = "You are";

  MudObject *fl=0;

  const char *myprep = 0;

    if (room->get_int("namestyle")==100) {

      if (IS_COMMANDER(player)) {
	y += ssprintf(" ^D%M^n", room, room->id);
      } else {
	y += ssprintf(" ^D%M^n", room);
      }

    } else if (is_player(room) || is_mobile(room)) {
      if (room->get_object(KEY_WIELD)==player)
	y += ssprintf(" being wielded by %M", room);
      else if (iswornby(player, room))
	y += ssprintf(" being worn by %M", room);
      else
	y += ssprintf(" being carried by %M", room);
    } else {

      if (player->get_flag(FL_FLYING)) {
	y += ssprintf(" flying");
	if (room->get_flag(FL_OUTDOORS))
	  myprep = "over";
      } else if (player->get_flag(FL_SITTING)) {
	y += ssprintf(" sitting");
	fl = saton(player);
	if (!fl)
	  fl = floor(room);
	if (fl) {
	  string cmd, end;
	  make_href(player, fl, cmd, end);
	  y += ssprintf(" on %s%Y%s", cmd.c_str(), fl, end.c_str());
	}
      } else if (player->get_flag(FL_SLEEPING)) {
	player->printf(" sleeping");
	fl = saton(player);
	if (fl) {
	  string cmd, end;
	  make_href(player, fl, cmd, end);
	  y += ssprintf(" on %s%Y%s", cmd.c_str(), fl, end.c_str());
	} 	
      } else if (MudObject *m=mount(player)) {
	const char *rv = m->get("riding_verb");
	if (!rv) rv =m->get_flag(FL_FLYING)?"flying":"riding";
	string cmd, end;
	make_href(player, m, cmd, end);
	y += mud_sprintf(player, " %s %s%Y%s", rv, cmd.c_str(), m, end.c_str());
      } else if (player->get_flag(FL_SWIMMING)) {
	y += ssprintf(" swimming");
      } else if (room->get_flag(FL_NOGRAVITY)) {
	y += ssprintf(" floating");
      } else if (player->get_flag(FL_NOLEGS)) {
	y += ssprintf(" resting");
      } else {
	fl = player->get_object(KEY_SITON);
	if (!fl)
	  fl = floor(room);
	if (fl) {
	  string cmd, end;
	  make_href(player, fl, cmd, end);
	  y += ssprintf(" standing on %s%M%s", cmd.c_str(), fl, end.c_str());
	} else {
	  y += ssprintf(" mysteriously levitating");
	}
      }

      string prep = format_roomname(room, player, "^{D\3RName\4", "\3/RName\4^}", "in", myprep);

      int q = 0;

      if (fl) {
	string cmd, end;
	make_href(player, fl, cmd, end);
	string blah = ssprintf("on %s%M%s", cmd.c_str(), fl, end.c_str());
	string pr2 = lowercase(lose_colour(prep.c_str()));
	blah = lowercase(lose_colour(blah.c_str()));
	if (blah == pr2)
	  q = 1;
      }

      if (!q) {
	y += ssprintf(" %s", prep.c_str());
      }
    }

    if (IS_COMMANDER(player)) {
      y += ssprintf(" ^D(^d%s^D)^n.\n", room->id);
    } else {
      y += ssprintf(".\n");
    }

    player->printf("%s", y.c_str());
}

static void flags(MudObject *player, MudObject *room) {

      player->spec_printf("^dFlags ^n");

      bool thing = 0;
      for (Flag i=FL_FIRST;i<FL_MAX;i++) {
	int a = room->get_rflag(i);
	int b = room->get_flag(i);

	if (a || b) {

	  if (a && b) player->printf("^d[^D%s^d]^n ", flag_names[i]);
	  if (!a && b) player->printf("+^d[^D%s^d]^n ", flag_names[i]);
	  if (a && !b) player->printf("(-%s) ", flag_names[i]);
	  thing = 1;
	}
      }

      if (thing) {
	player->printf("\n");
      } else {
	player->cancel_printf();
      }
}

int isnighthour(int i) {
  if (i >= 20) return 1;
  if (i <= 4) return 1;
  return 0;
}

bool is_dark(MudObject *o) {
  while (o) {
    if (o->get_flag(FL_DARK))
      return 1;
    
    if (o->get_flag(FL_OUTDOORS) && isnighthour(mudtime(o).hour))
      return 1;

    o = o->owner;
  }
  return 0;
}

void light_sources(MudObject *obj, NewWorld &sources) {
  MudObject *o;
  int i;
  foreach(obj->children, o, i) {
    if (o->get_flag(FL_LIT) || o->get_flag(FL_ONFIRE)) {
      sources.add(*o);
    }
    light_sources(o, sources);
  }
}


MudObject *possessor(MudObject *o, MudObject *ashigh=mud) 
{
  while (o) {
    if (o==ashigh)
      return 0;

    if (is_person(o))
      return o;

    o = o->owner;
  }

  return 0;
}

string magiclist_for(const NewWorld &c, MudObject *who) {
  MudObject *o;
  int i;

  map<MudObject*, NewWorld> stuff;

  NewWorld rest;

  foreach(&c, o, i) {
    if (is_person(o)) {
      rest.add(o);
      continue;
    }

    if (MudObject *p=possessor(o)) {
      stuff[p].add(o);
      continue;
    }
    
    rest.add(o);
    continue;
  }

  list<string> elms;

  if (rest.get(who->id)) {
    elms.push_back("you");
  }
  foreach(&rest, o, i) {
    if (o != who && name(o))
      elms.push_back(name(o));
  }

  typeof(stuff.begin()) it = stuff.begin();
  while (it != stuff.end()) {
    
    string s;
    
    if (it->first == who)
      s += "your ";
    else {
      s += name(it->first);
      s += "'s ";
    }

    foreach(&it->second, o, i) {
      const char *n = name(o);
      if (!n) n = "???";
      if (i && i == it->second.getsize()-1)
	s += " and ";
      else if (i)
	s += ", ";	
      s += remove_articles(n);
    }

    elms.push_back(s);

    it++;
  }

  string s = "";
  list<string>::iterator eit = elms.begin();
  while (eit != elms.end()) {
    string ts = *eit;
    if (eit != elms.begin()) {
      eit++;
      if (eit == elms.end())
	s += " and ";
      else
	s += ", ";
    } else {
      eit++;
    }
    s += ts;
  }
  return s;

  //  return s;
}

static string describe_light(MudObject *forwho, MudObject *where) {

  string s;

  if (is_dark(where) && !where->get_flag(FL_ONFIRE) &&
      !where->get_flag(FL_LIT)) {
      NewWorld sources;
      light_sources(where, sources);
      if (sources.getsize()>=1) {
	s = mud_sprintf(forwho, "Light is provided by %s.", 
			magiclist_for(sources, forwho).c_str());
      }
  }
  
  if (!where->get_flag(FL_OUTDOORS))
    return s;

  switch (mudtime(where).hour) {
  case 0:
    if (where->get_flag(FL_LIT)) {
      return "Artificial lights illuminate the night.";
    } else {
      if (s.length())
	return "It is the middle of the night. " + s;
      else
	return "It is the middle of the night.";
    }
    break;
    
  case 1:  case 2:  case 3:   case 4:
  case 20: case 21: case 22:  case 23:
    if (where->get_flag(FL_LIT)) {
      return "Artificial lights illuminate the night.";
    } else {
      if (s.length())
	return "It is night-time. " + s;
      else
	return "It is night-time.";
    }
    break;
    
  case 5:  case 6:  case 7:
    return "It is early morning.";
    break;
    
  case 8:  case 9:  case 10:    case 11:
     return "It is morning.";
     break;

   case 12:
     return "It is about midday.";
     break;

  case 13:  case 14: case 15: case 16:
     return "It is afternoon.";
	   break;

   case 17:  case 18:
     return "It is evening.";
     break;

   case 19:
     return "It is late evening.";
     break;

  }

  return "You aren't sure what time of day it is.";
}


static bool verb_look(MudObject *player, int argc, const char*argv[]) {
    bool green = false;

    rectify_state(player);
    
    MudObject *room = player->owner;

    if (!room) {
	player->printf("There seems to be a serious bug.\n");
	return true;
    }

    room->ref();

    int clas = 0;
    if (const char *cl=player->get("colours")) {
      if (cl && streq(cl, "classic")) {
	clas = 1;
      }
    }

    if (player->get_flag(FL_SLEEPING)) {
        player->printf("You dream of a galaxy far, far away.\n");
        return true;
    }

    if (!cansee(player)) {
        player->printf("You can't see a thing.\n");
        return true;
    }

    if (argc>2 && streq(argv[1], "in")) {
      Verb *v = verbs->get("lookin");
      if (v) 
	v->invoke(player, argc-1, argv+1);
      return true;
    }

    if (argc>2 && streq(argv[1], "at")) {
      string w = the_rest(argc, argv, 2);
      player->interpretf("examine %s", w.c_str());
      return true;
    }

    if (argc>1 && !streq(argv[1], "-")) {
      string w = the_rest(argc, argv, 1);
      player->interpretf("examine %s", w.c_str());
      return true;
    }
    
    if (room->get_flag(FL_ONFIRE)) 
      if (!wf_wears_with_flag(player, FL_NIGHTVISION)) {
        player->printf("^RYou cannot see anything through the smoke and flames.\n");
	return true;
      } else {
	player->printf("The smoke forces you to use your night-vision equipment.\n");
	if (player->get_flag(FL_COLOUR)) {
	  green = true;
  	  player->printf("^G");
	  player->send_data();
	  player->set_flag(FL_COLOUR, 0);
	}
      }
    
    if (!green) {
      if (!has_light(room)) {
	if (!wf_wears_with_flag(player, FL_NIGHTVISION)) {
	  player->printf("^nIt's ^LDARK^n!!!\n");
	  return true;
	} else {
	  player->printf("The dark forces you to use your night-vision equipment.\n");
	  if (player->get_flag(FL_COLOUR)) {
	    green = true;
	    player->printf("^G");
	    player->send_data();
	    player->set_flag(FL_COLOUR, 0);
	  }
	}
      }
    }
	
    int state=0;
    
    if (!streq(argv[1], "-")) {
       youare(player, room);

    }

    if (player->get_priv(PFL_SEESTATS)) {
       flags(player, room);
    }

    if (!player->get_flag(FL_BRIEF)) {
      player->printf("\3RDesc\4");

      if (!dotrap(E_BEFOREROOMDESC, player, room)) {
      /* ::: before_room_desc o1==room; before room desc printed. return 1 to not print room desc (but can't suppress daylight) */

      if (const char *dc=room->get("desc_cmd")) {
         static int i=0;
         if (!i) {
           i++;
           player->interpret(dc);
           i--;
         }
      } else {

	MudObject *df = room;
	if (df->get_object("descfrom")) {
	  df = df->get_object("descfrom");
	}

	const char *d = df->get("desc");
	if (const char *rd=df->get("room_desc"))
	  d = rd;
	
	if (const char *rd=df->get("$desc"))
	  d = rd;
	
	if (room->get_int(KEY_STATE)==1) {
	  if (const char *rd=df->get("desc.1"))
	    d = rd;
	}
	
	if (d) {
	  string rd;
	  if (room->get_int("enpara",1 )) rd = enpara(d);
	  else rd = d;
	  player->printf("^n  %s\n", rd.c_str());
	}
      }
      }

      string light = describe_light(player, room);

      if (!room->get_flag(FL_DEATH) || player->get_priv(PFL_IMMORTAL)) {
      player->unset("!timeofday");
      if (room->get_flag(FL_OUTDOORS) && !room->get_flag(FL_NOGRAVITY)) {
	if (light.length())
	  player->printf("  %s\n", light.c_str());
	player->set("!timeofday", mudtime(room).hour);
      } else {
	if (light.length())
	  player->printf("  %s\n", light.c_str());
      }
      }
      
      dotrap(E_AFTERROOMDESC, player, room);
      player->printf("\3/RDesc\4");
      /* ::: after_room_desc o1==room; after room desc and daylight status printed */
    }

    if (room->get_flag(FL_SHIP) && (room->get_flag(FL_OUTDOORS)||room->get_flag(FL_TRANSPARENT))
	&& room->get_object("ship")) {
      player->printf("%sAround you : ^n\n  %s\n", clas?"^r":"^W", room->get_object("ship")->owner->get("desc"));
    }

    yousee(player, player->owner);

    dolook_objects(player, player->get_priv(PFL_SEESTATS));
    dolook_mobplayer(player, player->get_priv(PFL_SEESTATS), isplayer);
    dolook_mobplayer(player, player->get_priv(PFL_SEESTATS), ismobile);
    
    player->cancel_printf();

    if (room->get_flag(FL_LINKED)) {
      
      int i;
      MudObject *obj;
      
      foreach(room->children, obj, i) {
	if (MudObject *o = linkedlink(obj)) {
	  state = 0;
	  yousee(player, o, obj);
	  
	  dolook_mobplayer(player, player->get_priv(PFL_SEESTATS), isplayer, o);
	  dolook_mobplayer(player, player->get_priv(PFL_SEESTATS), ismobile, o);
	  
	  player->cancel_printf();
	}
      }
    }
    
    if (!room->get_flag(FL_DEATH) || player->get_priv(PFL_IMMORTAL)) {

    Verb *v = verbs->get("exits");
    if (v) 
      v->invoke(player, 0, 0);

    }

    if (green) {
      player->send_data();
      player->set_flag(FL_COLOUR, 1);
      player->printf("^G");
      player->send_data();
      player->printf("^n");
    }
    
    return true;
};

#include "verbmodule.h"

void startup() {
  AUTO_VERB(look,      1, 0,       PFL_NONE);  
}