dw_fluffos_v2/
dw_fluffos_v2/fluffos-2.9-ds2.05/
dw_fluffos_v2/fluffos-2.9-ds2.05/ChangeLog.old/
dw_fluffos_v2/fluffos-2.9-ds2.05/Win32/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/simuls/
dw_fluffos_v2/fluffos-2.9-ds2.05/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/clone/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/command/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/data/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/etc/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/master/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/log/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/compiler/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/efuns/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/operators/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/u/
dw_fluffos_v2/fluffos-2.9-ds2.05/tmp/
dw_fluffos_v2/fluffos-2.9-ds2.05/windows/
dw_fluffos_v2/lib/
dw_fluffos_v2/lib/binaries/cmds/
dw_fluffos_v2/lib/binaries/cmds/creator/
dw_fluffos_v2/lib/binaries/cmds/living/
dw_fluffos_v2/lib/binaries/cmds/player/
dw_fluffos_v2/lib/binaries/d/admin/obj/
dw_fluffos_v2/lib/binaries/d/liaison/
dw_fluffos_v2/lib/binaries/global/virtual/
dw_fluffos_v2/lib/binaries/global/virtual/setup_compiler/
dw_fluffos_v2/lib/binaries/obj/handlers/autodoc/
dw_fluffos_v2/lib/binaries/obj/handlers/terrain_things/
dw_fluffos_v2/lib/binaries/obj/misc/
dw_fluffos_v2/lib/binaries/obj/misc/buckets/
dw_fluffos_v2/lib/binaries/obj/monster/
dw_fluffos_v2/lib/binaries/obj/reactions/
dw_fluffos_v2/lib/binaries/obj/reagents/
dw_fluffos_v2/lib/binaries/secure/cmds/creator/
dw_fluffos_v2/lib/binaries/secure/master/
dw_fluffos_v2/lib/binaries/std/
dw_fluffos_v2/lib/binaries/std/dom/
dw_fluffos_v2/lib/binaries/std/effects/object/
dw_fluffos_v2/lib/binaries/std/guilds/
dw_fluffos_v2/lib/binaries/std/languages/
dw_fluffos_v2/lib/binaries/std/races/
dw_fluffos_v2/lib/binaries/std/room/
dw_fluffos_v2/lib/binaries/std/room/basic/
dw_fluffos_v2/lib/binaries/std/shops/
dw_fluffos_v2/lib/binaries/std/shops/inherit/
dw_fluffos_v2/lib/binaries/www/
dw_fluffos_v2/lib/cmds/guild-race/
dw_fluffos_v2/lib/cmds/guild-race/crafts/
dw_fluffos_v2/lib/cmds/guild-race/other/
dw_fluffos_v2/lib/cmds/playtester/
dw_fluffos_v2/lib/cmds/playtester/senior/
dw_fluffos_v2/lib/d/admin/
dw_fluffos_v2/lib/d/admin/log/
dw_fluffos_v2/lib/d/admin/mapper/31-10-01/mapmaker/event/
dw_fluffos_v2/lib/d/admin/meetings/
dw_fluffos_v2/lib/d/admin/obj/
dw_fluffos_v2/lib/d/admin/room/we_care/
dw_fluffos_v2/lib/d/admin/save/
dw_fluffos_v2/lib/d/dist/
dw_fluffos_v2/lib/d/dist/mtf/
dw_fluffos_v2/lib/d/dist/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/chars/
dw_fluffos_v2/lib/d/dist/pumpkin/desert/
dw_fluffos_v2/lib/d/dist/pumpkin/gumboot/
dw_fluffos_v2/lib/d/dist/pumpkin/hospital/
dw_fluffos_v2/lib/d/dist/pumpkin/inherit/
dw_fluffos_v2/lib/d/dist/pumpkin/map/
dw_fluffos_v2/lib/d/dist/pumpkin/plain/
dw_fluffos_v2/lib/d/dist/pumpkin/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/save/
dw_fluffos_v2/lib/d/dist/pumpkin/squash/
dw_fluffos_v2/lib/d/dist/pumpkin/terrain/
dw_fluffos_v2/lib/d/dist/pumpkin/woods/
dw_fluffos_v2/lib/d/dist/start/
dw_fluffos_v2/lib/d/learning/TinyTown/buildings/
dw_fluffos_v2/lib/d/learning/TinyTown/map/
dw_fluffos_v2/lib/d/learning/TinyTown/roads/
dw_fluffos_v2/lib/d/learning/add_command/
dw_fluffos_v2/lib/d/learning/arms_and_weps/
dw_fluffos_v2/lib/d/learning/chars/
dw_fluffos_v2/lib/d/learning/cutnpaste/
dw_fluffos_v2/lib/d/learning/examples/npcs/
dw_fluffos_v2/lib/d/learning/examples/player_houses/npcs/
dw_fluffos_v2/lib/d/learning/examples/terrain_map/basic/
dw_fluffos_v2/lib/d/learning/functions/
dw_fluffos_v2/lib/d/learning/handlers/
dw_fluffos_v2/lib/d/learning/help_topics/npcs/
dw_fluffos_v2/lib/d/learning/help_topics/objects/
dw_fluffos_v2/lib/d/learning/help_topics/rcs_demo/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/crowd/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/situations/
dw_fluffos_v2/lib/d/learning/items/
dw_fluffos_v2/lib/d/learning/save/
dw_fluffos_v2/lib/d/liaison/
dw_fluffos_v2/lib/d/liaison/NEWBIE/doc/
dw_fluffos_v2/lib/d/liaison/NEWBIE/save/oldlog/
dw_fluffos_v2/lib/db/
dw_fluffos_v2/lib/doc/
dw_fluffos_v2/lib/doc/creator/
dw_fluffos_v2/lib/doc/creator/autodoc/include/reaction/
dw_fluffos_v2/lib/doc/creator/autodoc/include/ritual_system/
dw_fluffos_v2/lib/doc/creator/autodoc/include/talker/
dw_fluffos_v2/lib/doc/creator/autodoc/include/terrain_map/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/baggage/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clock/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clothing/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/cont_save/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/corpse/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/money/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/monster/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/scabbard/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/service_provider/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/state_changer/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/wand/
dw_fluffos_v2/lib/doc/creator/autodoc/std/book_dir/
dw_fluffos_v2/lib/doc/creator/autodoc/std/key/
dw_fluffos_v2/lib/doc/creator/autodoc/std/learning/
dw_fluffos_v2/lib/doc/creator/autodoc/std/map/
dw_fluffos_v2/lib/doc/creator/autodoc/std/race/
dw_fluffos_v2/lib/doc/creator/autodoc/std/weapon_logic/
dw_fluffos_v2/lib/doc/creator/files/
dw_fluffos_v2/lib/doc/creator/policy/
dw_fluffos_v2/lib/doc/creator/room/
dw_fluffos_v2/lib/doc/effects/
dw_fluffos_v2/lib/doc/ideas/
dw_fluffos_v2/lib/doc/known_command/
dw_fluffos_v2/lib/doc/lpc/basic_manual/
dw_fluffos_v2/lib/doc/lpc/intermediate/
dw_fluffos_v2/lib/doc/new/add_command/
dw_fluffos_v2/lib/doc/new/handlers/
dw_fluffos_v2/lib/doc/new/living/
dw_fluffos_v2/lib/doc/new/living/race/
dw_fluffos_v2/lib/doc/new/living/spells/
dw_fluffos_v2/lib/doc/new/player/
dw_fluffos_v2/lib/doc/new/room/guild/
dw_fluffos_v2/lib/doc/new/room/outside/
dw_fluffos_v2/lib/doc/new/room/storeroom/
dw_fluffos_v2/lib/doc/object/
dw_fluffos_v2/lib/doc/playtesters/
dw_fluffos_v2/lib/doc/policy/
dw_fluffos_v2/lib/doc/weapons/
dw_fluffos_v2/lib/global/handlers/
dw_fluffos_v2/lib/global/virtual/setup_compiler/
dw_fluffos_v2/lib/include/
dw_fluffos_v2/lib/include/cmds/
dw_fluffos_v2/lib/include/effects/
dw_fluffos_v2/lib/include/npc/
dw_fluffos_v2/lib/include/shops/
dw_fluffos_v2/lib/net/daemon/chars/
dw_fluffos_v2/lib/net/inherit/
dw_fluffos_v2/lib/net/intermud3/
dw_fluffos_v2/lib/net/intermud3/services/
dw_fluffos_v2/lib/net/obj/
dw_fluffos_v2/lib/net/save/
dw_fluffos_v2/lib/net/smnmp/
dw_fluffos_v2/lib/net/snmp/
dw_fluffos_v2/lib/obj/amulets/
dw_fluffos_v2/lib/obj/b_day/
dw_fluffos_v2/lib/obj/examples/
dw_fluffos_v2/lib/obj/food/alcohol/
dw_fluffos_v2/lib/obj/food/chocolates/
dw_fluffos_v2/lib/obj/food/fruits/
dw_fluffos_v2/lib/obj/food/meat/
dw_fluffos_v2/lib/obj/food/nuts/
dw_fluffos_v2/lib/obj/food/seafood/
dw_fluffos_v2/lib/obj/food/vegetables/
dw_fluffos_v2/lib/obj/fungi/
dw_fluffos_v2/lib/obj/furnitures/artwork/
dw_fluffos_v2/lib/obj/furnitures/bathroom/
dw_fluffos_v2/lib/obj/furnitures/beds/
dw_fluffos_v2/lib/obj/furnitures/cabinets/
dw_fluffos_v2/lib/obj/furnitures/chairs/
dw_fluffos_v2/lib/obj/furnitures/chests/
dw_fluffos_v2/lib/obj/furnitures/clocks/
dw_fluffos_v2/lib/obj/furnitures/crockery/
dw_fluffos_v2/lib/obj/furnitures/cupboards/
dw_fluffos_v2/lib/obj/furnitures/cushions/
dw_fluffos_v2/lib/obj/furnitures/fake_plants/
dw_fluffos_v2/lib/obj/furnitures/lamps/
dw_fluffos_v2/lib/obj/furnitures/mirrors/
dw_fluffos_v2/lib/obj/furnitures/outdoor/
dw_fluffos_v2/lib/obj/furnitures/safes/
dw_fluffos_v2/lib/obj/furnitures/shelves/
dw_fluffos_v2/lib/obj/furnitures/sideboards/
dw_fluffos_v2/lib/obj/furnitures/sofas/
dw_fluffos_v2/lib/obj/furnitures/stoves/
dw_fluffos_v2/lib/obj/furnitures/tables/
dw_fluffos_v2/lib/obj/furnitures/wardrobes/
dw_fluffos_v2/lib/obj/handlers/
dw_fluffos_v2/lib/obj/handlers/autodoc/
dw_fluffos_v2/lib/obj/jewellery/anklets/
dw_fluffos_v2/lib/obj/jewellery/bracelets/
dw_fluffos_v2/lib/obj/jewellery/earrings/
dw_fluffos_v2/lib/obj/jewellery/misc/
dw_fluffos_v2/lib/obj/jewellery/necklaces/
dw_fluffos_v2/lib/obj/jewellery/rings/
dw_fluffos_v2/lib/obj/media/
dw_fluffos_v2/lib/obj/misc/buckets/
dw_fluffos_v2/lib/obj/misc/jars/
dw_fluffos_v2/lib/obj/misc/papers/
dw_fluffos_v2/lib/obj/misc/player_shop/
dw_fluffos_v2/lib/obj/misc/shops/
dw_fluffos_v2/lib/obj/misc/traps/
dw_fluffos_v2/lib/obj/monster/
dw_fluffos_v2/lib/obj/monster/godmother/
dw_fluffos_v2/lib/obj/monster/transport/
dw_fluffos_v2/lib/obj/plants/inherit/
dw_fluffos_v2/lib/obj/potions/
dw_fluffos_v2/lib/open/boards/
dw_fluffos_v2/lib/save/autodoc/
dw_fluffos_v2/lib/save/bank_accounts/
dw_fluffos_v2/lib/save/boards/frog/
dw_fluffos_v2/lib/save/books/bed_catalog/
dw_fluffos_v2/lib/save/creators/
dw_fluffos_v2/lib/save/mail/
dw_fluffos_v2/lib/save/mail/p/
dw_fluffos_v2/lib/save/soul/data/
dw_fluffos_v2/lib/save/tasks/
dw_fluffos_v2/lib/save/vaults/
dw_fluffos_v2/lib/secure/cmds/lord/
dw_fluffos_v2/lib/secure/config/
dw_fluffos_v2/lib/secure/items/
dw_fluffos_v2/lib/secure/player/
dw_fluffos_v2/lib/soul/
dw_fluffos_v2/lib/soul/i/
dw_fluffos_v2/lib/soul/j/
dw_fluffos_v2/lib/soul/k/
dw_fluffos_v2/lib/soul/o/
dw_fluffos_v2/lib/soul/q/
dw_fluffos_v2/lib/soul/to_approve/
dw_fluffos_v2/lib/soul/u/
dw_fluffos_v2/lib/soul/v/
dw_fluffos_v2/lib/soul/wish_list/
dw_fluffos_v2/lib/soul/y/
dw_fluffos_v2/lib/soul/z/
dw_fluffos_v2/lib/std/creator/
dw_fluffos_v2/lib/std/effects/
dw_fluffos_v2/lib/std/effects/attached/
dw_fluffos_v2/lib/std/effects/external/
dw_fluffos_v2/lib/std/effects/fighting/
dw_fluffos_v2/lib/std/effects/other/
dw_fluffos_v2/lib/std/environ/
dw_fluffos_v2/lib/std/guilds/
dw_fluffos_v2/lib/std/hospital/
dw_fluffos_v2/lib/std/house/
dw_fluffos_v2/lib/std/house/onebedhouse/
dw_fluffos_v2/lib/std/house/onebedhut/
dw_fluffos_v2/lib/std/house/tworoomflat/
dw_fluffos_v2/lib/std/languages/
dw_fluffos_v2/lib/std/liquids/
dw_fluffos_v2/lib/std/nationality/
dw_fluffos_v2/lib/std/nationality/accents/
dw_fluffos_v2/lib/std/nationality/accents/national/
dw_fluffos_v2/lib/std/nationality/accents/regional/
dw_fluffos_v2/lib/std/npc/goals/
dw_fluffos_v2/lib/std/npc/goals/basic/
dw_fluffos_v2/lib/std/npc/goals/misc/
dw_fluffos_v2/lib/std/npc/inherit/
dw_fluffos_v2/lib/std/npc/plans/
dw_fluffos_v2/lib/std/npc/plans/basic/
dw_fluffos_v2/lib/std/outsides/
dw_fluffos_v2/lib/std/races/shadows/
dw_fluffos_v2/lib/std/room/basic/topography/
dw_fluffos_v2/lib/std/room/controller/
dw_fluffos_v2/lib/std/room/controller/topography/
dw_fluffos_v2/lib/std/room/furniture/games/
dw_fluffos_v2/lib/std/room/furniture/inherit/
dw_fluffos_v2/lib/std/room/inherit/carriage/
dw_fluffos_v2/lib/std/room/inherit/topography/
dw_fluffos_v2/lib/std/room/punishments/
dw_fluffos_v2/lib/std/room/topography/area/
dw_fluffos_v2/lib/std/room/topography/iroom/
dw_fluffos_v2/lib/std/room/topography/milestone/
dw_fluffos_v2/lib/std/shadows/
dw_fluffos_v2/lib/std/shadows/attached/
dw_fluffos_v2/lib/std/shadows/curses/
dw_fluffos_v2/lib/std/shadows/disease/
dw_fluffos_v2/lib/std/shadows/fighting/
dw_fluffos_v2/lib/std/shadows/room/
dw_fluffos_v2/lib/std/shops/controllers/
dw_fluffos_v2/lib/std/shops/objs/
dw_fluffos_v2/lib/std/shops/player_shop/
dw_fluffos_v2/lib/std/shops/player_shop/office_code/
dw_fluffos_v2/lib/std/socket/
dw_fluffos_v2/lib/www/
dw_fluffos_v2/lib/www/external/autodoc/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/images/
dw_fluffos_v2/lib/www/external/java/telnet/examples/
dw_fluffos_v2/lib/www/external/java/telnet/tools/
dw_fluffos_v2/lib/www/pics/
dw_fluffos_v2/lib/www/secure/creator/
dw_fluffos_v2/lib/www/secure/editors/
dw_fluffos_v2/lib/www/secure/survey_results/
dw_fluffos_v2/win32/
/*  RCS stuff...  -*- LPC -*-
 * $Locker:  $
 *
 * $Id: book.c,v 1.39 2003/03/23 03:30:02 presto Exp $
 *
 */
/**
 * A nice book object.  A book will be a collection of pages.
 * You can tear pages out of a book, you can add pages (though adding
 * a page isnt overly useful, unless you bind it in of course).
 *<p>
 * Each page is an object.  Pages that are torn out are handled
 * as empty spots in the array.
 *<p>
 * @change 26-Feb-96 Jeremy
 * I took out the "my book" checks in test_add() and test_remove()
 * because they were keeping written pages from loading.  If
 * anyone knows why the checks were necessary, let me know.
 * @change 25-Aug-96 Jeremy
 * Changed test_add() so that it won't add things held by players.
 * This is because if they "put" something in the book, it's inaccessible
 * (maybe this is why pages had the "my book" property?  I like this
 * solution better, but not much).
 * @author Pinkfish
 */
inherit "/std/object";

#include <move_failures.h>
#include <player.h>

private mixed *_pages;
private string _default_page_object;
private object _def_p_obj;
/* Open_page set to 0 means closed. */
private int _open_page;
private int _book_num;
/*
 * If this is set, we ignore the flag and only print the real
 * short of the book.
 */
private int _ignore_open_page;
/*
 * This should be used in conjuction with created books
 * that yuou wish the contents to update when changed.
 */
private int _ignore_saved_pages;
private nosave object _player;
private nosave int _num_torn_out = -1;

protected int do_open(int page);
protected int do_tear(int number);
protected int do_turn(int number);
protected int do_close();
object create_default_page_object();

void create() {
  _pages = ({ });
  _default_page_object = "/obj/misc/paper";
  load_object(_default_page_object);
  _def_p_obj = find_object(_default_page_object);
  ::create();
  add_help_file("book");
} /* create() */

/**
 * Tells us if this is a book object.
 * @return always returns 1
 */
int query_book() { return 1; }

/**
 * @ignore yes
 * Mess with the weight to make the pages taken into account as well.
 */
int query_weight() {
   if (!_def_p_obj) {
      load_object(_default_page_object);
      _def_p_obj = find_object(_default_page_object);
   }
   return ::query_weight() +
          sizeof(filter(_pages, (: objectp($1) :))) * _def_p_obj->query_weight();
} /* query_weight() */

/**
 * Adds the commands onto the player.  The commands are "read", "open",
 * "tear", "turn" and "close".
 * @ignore yes
 */
void init() {
  add_command("open", "<direct:object>", (: do_open(1) :));
  add_command("open", "<direct:object> to [page] <number>",
              (: do_open($4[1]) :));
  add_command("tear", "page from <direct:object>", (: do_tear(1) :));
  add_command("tear", "[all] pages from <direct:object>", (: do_tear(0) :));
  add_command("tear", "<number> [of] pages from <direct:object>",
              (: do_tear($4[0]) :));
  add_command("rip", "page from <direct:object>", (: do_tear(1) :));
  add_command("rip", "<number> [of] pages from <direct:object>",
              (: do_tear($4[0]) :));
  add_command("rip", "[all] pages from <direct:object>", (: do_tear(0) :));
  add_command("turn", "[a|1] page of <direct:object>", (: do_turn(1) :));
  add_command("turn", "<number> pages of <direct:object>",
              (: do_turn($4[0]) :));
  add_command("turn", "<direct:object> to [page] <number>",
              (: do_open($4[1]) :) );
  add_command("turn", "to page <number> of <direct:object>",
              (: do_open($4[0]) :) );
  add_command("close", "<direct:object>", (: do_close() :));
} /* init() */

/**
 * @ignore yes
 */
int add_weight( int number ) {
   adjust_weight( number );
   return 1;
} /* add_weight() */

/**
 * To see if things can be added to us.  This has a few nasty
 * cludges in it to stop a few things. 
 * @see /std/basic/misc
 * @param ob the object being added
 * @param flag the flag sent to this function
 * @return 1 if it can be added, 0 if it cannot
 * @ignore
 */
int test_add(object ob, int flag) {
  return (object)ob->query_property("my book") == this_object();
  // This is a kludge to keep people from putting things in the book.
  // It's not foolproof, but it should do until I think of a better way.
/*
  if (!environment(ob) || !living(environment(ob)))
    return 1;
  return 0;
 */
} /* test_add() */

/**
 * To check to see if things can be removed from us.
 * @see /std/basic/misc
 * @param ob the object being removed
 * @param flag the flag sent to the function
 * @param dest where it is going to
 * @return 1 if it can be removed, 0 if it cannot
 * @ignore
 */
int test_remove( object ob, int flag, mixed dest ) {
  return ob->query_property("my book") != this_object();
  //return 1;
} /* test_remove() */

/**
 * Set the number of pages in the book.  If there are too many pages
 * in the book, then pages are removed from the top to create the
 * correct size and if there are too few pages then pages are
 * added onto the end of the pages array.
 * @param no the number of pages
 * @see query_pages()
 * @see query_num_pages()
 */
void set_no_pages(int no) {
  int i;
  int siz;

  siz = sizeof(_pages);
  if (no < siz) { /* removeing pages?  Oh well, if you insist. */
    _pages = _pages[0..no-1];
    if (_open_page >= no) {
      _open_page = no;
    }
  } else {
    /* Adding pages, more normal... */
    _pages = _pages + allocate(no-siz);
    for (i=siz;i<no;i++) {
      /* Set them empty and only make them real if someone wants them. */
      _pages[i] = ({ });
    }
  }
} /* set_no_pages() */

/**
 * Returns the pages array.
 * @return the array containing the pages information
 */
mixed *query_pages() { return _pages; }

/**
 * Sets the currently open page.  This does all the magic needed to make
 * the book appear as if it is the currently opened page.  If the open
 * page is 0 then the book is closed.  If it is out of the upper
 * bound then the book is opened to the last page.
 * @param i the page to open to
 * @see query_open_page()
 * @see query_current_page()
 */
void set_open_page(int i) {
  /* Valid page or already there. */
  if (i < 0 || i == _open_page) {
     return ;
  }

  if (!_open_page && i) {
    add_alias("page");
    add_plural("pages");
  }

  if (i > sizeof(_pages)) {
    _open_page = sizeof(_pages);
  } else {
    _open_page = i;
  }

  if (!_open_page) {
    remove_alias("page");
    remove_plural("pages");
  }
} /* set_open_page() */

/** 
 * What is the current open page.  Returns 0 if the book is not
 * open.
 * @return the current open page
 * @see set_open_page()
 * @see query_current_page()
 */
int query_open_page() {
   return _open_page;
} /* query_open_page() */

/**
 * This method checks to see if the current page is torn out.
 * @return 1 if torn out, 0 if not
 * @see query_current_page()
 * @see query_open_page()
 * @see is_page_torn_out()
 */
int is_current_page_torn_out() {
   if (!_open_page) {
      /* The cover cannot be torn out. */
      return 0;
   }
   if ( !_pages ) {
      return 0;
   }
     
   if (!_pages[_open_page-1]) {
      return 1;
   }
   return 0;
} /* is_current_page_torn_out() */

/**
 * This method checks to see if the specified page is torn out.
 * @param page the page number to check
 * @return 1 if it is torn out, - if not
 * @see is_current_page_torn_out()
 */
int is_page_torn_out(int page) {
   if (page < 1 || page > sizeof(_pages)) {
      return 0;
   }

   if (!_pages[page - 1]) {
      return 1;
   }
   return 0;
} /* is_page_torn_out() */

/**
 * Returns the object associated with the current open page.  If the
 * page does not actualy exist yet then the default base object
 * is returned.  This object should be handled carefully...  If the
 * page is torn out then the next readable page is returned or
 * 0 is returned.
 * @return the current page object
 * @see query_current_page_clone()
 * @see set_open_page()
 * @see query_open_page()
 * @see query_current_page_clone()
 */
object query_current_page() {
   int i;

   if (!_open_page) {
      return this_object(); /* The book itself... */
   }
   for (i = _open_page - 1; i < sizeof(_pages); i++) {
     if (_pages[i]) {
       if (!_def_p_obj) {
         load_object(_default_page_object);
         _def_p_obj = find_object(_default_page_object);
       }
       return _def_p_obj;
     }
   }
   return 0;
} /* query_current_page() */

/**
 * This function makes sure the page actually
 * exists.  If it does not exist, then it will clone one
 * up for us.  This one should be used as you will not end up with the
 * base object in this case.  If you are planning to modify the page,
 * then use this call.
 * @see query_current_page()
 * @return the object of the current page
 */
object query_current_page_clone() {
  return this_object();
} /* query_current_page_clone() */

/**
 * This method checks to see if the page is still the default page object
 * or if it is something else altogether.
 * @return 1 if it is the default page object, 0 if not
 */
int is_default_page(int num) {
   if (num > 0 && num <= sizeof(_pages)) {
      if (_pages[num - 1] &&
          !objectp(_pages[num - 1])) {
         return 1;
      }
   }
   return 0;
} /* is_default_page() */

/**
 * This method returns the contents of the selected page.  The number
 * must be greator than 0.  This will return the exact value of the
 * page, it will not search for non-torn out pages.  Becare with
 * the return value of this as the default page object might be
 * returned if the page does not actually exist.  If you need a
 * real object remember to use the clone version.
 * @param num the page to return
 * @return 0 on failure or if the page is torn out, the object on success
 */
object query_selected_page(int num) {
   if (!intp(num) || num <= 0 || num > sizeof(_pages)) {
      return 0;
   }

   if (_pages[num - 1]) {
      if (objectp(_pages[num - 1])) {
         return _pages[num - 1];
      } else {
         if (!_def_p_obj) {
            load_object(_default_page_object);
            _def_p_obj = find_object(_default_page_object);
         }
         return _def_p_obj;
      }
   }
   return 0;
} /* query_selected_page() */

/**
 * This method returns the contents of the selected page, if a default
 * object is returned a real clone for the page is created instead.
 * @param num the page to return
 * @return 0 on failure or if the page is torn out, the object on success
 */
object query_selected_page_clone(int num) {
  return this_object();
} /* query_selected_page_clone() */

/**
 * This method tears the current page out of the book and returns it
 * to us.  This object will be moved into the destination so that it is
 * no longer inside us.  If it cannot be moved of the page has already
 * been remove then the function will return 0.
 * @param dest the destination to move the page to
 * @return the torn out page, or 0 on failure
 */
object tear_current_page_out(object dest) {
  object page;

  if (is_current_page_torn_out()) {
    /* Too late... */
    return 0;
  }

  if (_pages[_open_page-1]) {
    page = clone_object(_default_page_object);
    page->add_alias("page");
    page->add_plural("pages");
    if(_pages[_open_page-1] != 1)
      page->set_read_mess(_pages[_open_page-1]);
   }

  // First remove the weight from the outside.
  if (page && page->move(dest) == MOVE_OK) {
    _pages[_open_page-1] = 0;
    return page;
  }
  return 0;
} /* tear_current_page_out() */

/**
 * This method adds a new page in after the selected page.  The pages
 * are numbered from 1, so adding a page after page 0 will place a
 * page on the start and after the last page onto the end.
 * @param page the page to add
 * @param after the page to add it after
 * @return 1 on success, 0 on failure
 * @see query_current_page()
 * @see query_num_pages()
 */
int add_page_after(object page, int after) {
   if (after < 0 || after > sizeof(_pages) + 1 ||
       !objectp(page) || !intp(after)) {
      return 0;
   }
   _pages = _pages[0..after - 1] + page->query_read_mess() + _pages[after..];
   page->move("/room/rubbish");
   return 1;
} /* add_page_after() */

/**
 * This method replaces the selected page with a new page.  The old page
 * is dested if it can be and replaced with a nice shiny new page object.
 * @param page the page to replace with
 * @param num the page number to replace
 * @return 1 on success, 0 on failure
 * @see add_page_after()
 */
int replace_page_with(object page, int num) {
   if (num < 1 || num > sizeof(_pages) ||
       !objectp(page) || !intp(num)) {
      return 0;
   }
   _pages[num-1] = page->query_read_mess();
   page->move("/room/rubbish");
} /* replace_page_with() */

/**
 * This method makes the selected page blank.  It defaults it back to a
 * default page object as if the book had just been created.
 * @param num the page to make blank
 * @see replace_page_with()
 * @see add_page_after()
 * @see add_blank_page_after()
 */
int make_page_blank(int num) {
   if (num < 1 || num > sizeof(_pages) || !intp(num)) {
      return 0;
   }
   _pages[num-1] = 1;
} /* make_page_blank() */

/**
 * @ignore yes
 */
string short(int flags) {
  if (!flags || _ignore_open_page) {
    return ::short(flags);
  }
  if (_open_page) {
    return "open " + ::short(flags);
  }
  return "closed " + ::short(flags);
} /* short() */

/**
 * This is over ridden here to allow the open/closed sttus of the book to be
 * matched.
 * @ignore yes 
 */
string *parse_command_adjectiv_id_list() {
   if (_open_page) {
      return ::parse_command_adjectiv_id_list() + ({ "open" });
   }
   return ::parse_command_adjectiv_id_list() + ({ "closed" });
} /* parse_command_adjectiv_id_list() */

/**
 * @ignore yes
 */
string long(string str, int dark) {
  string ret;
  int i;

  if (!_open_page) {
    return ::long(str, dark)+"It is closed.\n";
  }
  ret = ::long(str, dark)+"It is open at page " + _open_page + ".\n";
  for (i=_open_page-1;i<sizeof(_pages) && !_pages[i];i++) {
    if (!_pages[i]) {
      ret += "Page "+(i+1)+" has been torn out.\n";
    }
  }
  if (i >= sizeof(_pages)) {
    ret += "All the rest of the pages have been torn out!\n";
  } else {
    if (i != _open_page -1) { /* A page has been torn out... */
      ret += "You can see page "+(i+1)+" however.\n";
    }

    // Only show the page if they "look page of book" or somesuch.
    if(str && strsrch(str, "page") != -1) {
      ret += _default_page_object->long(str, dark);
    }
  }
  return ret;
} /* long() */

/**
 * Opens the book to the right place.  This is the add_command function.
 * @param indir the indirect objects (not used)
 * @param id_mactch the string matching the id (not used)
 * @param ind_match the string matching the indirect ob (not used)
 * @param args the arguments associated with the pattern
 * @param the pattern which was matched.
 * @see /global/new_parse->add_command()
 * @return 1 if it succeeded, 0 if it failed
 * @ignore yes
 */
protected int do_open(int page) {
  if (page <= 0)  {
     write("Oddly enough, the first page is page 1.\n");
     page = 1;
  }
  else if (page > sizeof(_pages))  {
     write("There " + (sizeof(_pages) == 1 ? "is only " : "are only ") +
           query_num(sizeof(_pages)) +
           (sizeof(_pages) == 1 ? " page in " : " pages in ") +
           the_short() + ".\n");
     page = sizeof(_pages);
  }
  if (query_open_page() == page) {
    add_failed_mess("The $D is already open at page " + page + ".\n");
  }
  if (_open_page > 0  &&  is_page_torn_out (page)) {
     add_failed_mess("The page " +  page + " in $D is torn out.\n");
     return 0;
  }
  _ignore_open_page = 1;
  call_out((: _ignore_open_page = 0 :), 4);
  set_open_page(page);
  add_succeeded_mess("$N $V $D to page " + page + ".\n", ({ }));
  return 1;
} /* do_open() */

/**
 * Turns a page of the book.
 * @return always returns 1
 * @ignore yes
 */
protected int do_turn(int number) {
  int tmp;

  tmp = query_open_page();
  if (tmp+number > sizeof(_pages)) {
    set_open_page(0);
    add_succeeded_mess("$N close$s $D.\n");
  } else {
    if (tmp == 0) {
       add_succeeded_mess("$N turn$s $D to page " + number + ".\n");
    }
    else add_succeeded_mess("$N turn$s " + number +
           (number > 1 ? " pages " : " page ") + "of $D.\n");
    set_open_page(tmp + number);
  }
  if (tmp == query_open_page()) {
     add_failed_mess("Unable to turn page of $D.\n", ({ }));
     return 0;
  }
  _ignore_open_page = 1;
  call_out((: _ignore_open_page = 0 :), 4);
  return 1;
} /* do_turn() */

/**
 * Closes the book.
 * @return 0 if it failed, 1 if it succeeded
 * @ignore yes
 */
protected int do_close() {
  if (!query_open_page()) {
    this_player()->add_failed_mess(this_object(), "$D is already closed.\n",
                                   ({}));
    return 0;
  }
  _ignore_open_page = 1;
  call_out((: _ignore_open_page = 0 :), 4);
  set_open_page(0);
  return 1;
} /* do_close() */

/**
 * Tears out the current page.
 * @return 0 if it failed, 1 if it succeded
 * @ignore yes
 */
int do_tear(int number) {
  int i;
 
  if (_ignore_saved_pages) {
    add_failed_mess("For some reason you cannot seem to tear any pages "
                    "from $D.\n");
    return 0;
  }
 
  if (!_open_page) {
    this_player()->add_failed_mess(this_object(), "$D is closed!\n",
                                   ({}));
    return 0;
  }

  // do all the pages.
  if(number == 0) {
    _open_page = 1;
    number = sizeof(_pages);
  }
  
  for(i=0; i<number; i++) {
    
    if(!tear_current_page_out(this_player()))
      break;
      
    if ( _open_page != sizeof( _pages ) ) {
        _open_page++;
    }
  }

  if(i) {
    if(i > 1)
      add_succeeded_mess("$N $V " + i + " pages from $D.\n");
    else
      add_succeeded_mess("$N $V a page from $D.\n");
    return 1;
  }

  return 0;
} /* do_tear() */

/* The stuff redefined for handleing books... */
/**
 * @ignore yes 
 */
varargs void set_read_mess(string str, string lang, int size) {

  if (_open_page) {
    if(pointerp(str)) {
      _pages[_open_page-1] = str;
      return;
    }
    if(str)
      _pages[_open_page-1] = ({ ({ str, 0, lang, size }) });
    else
      _pages[_open_page-1] = 1;
    return;
  }
  return ::set_read_mess(str, lang, size);
} /* set_read_mess() */

/**
 * @ignore yes 
 */
void add_read_mess(mixed str, string type, string lang, int size) {
  if(_open_page) {
    if(!arrayp(_pages[_open_page-1]))
      _pages[_open_page-1] = ({ });
    _pages[_open_page-1] += ({ ({ str, type, lang, size }) });
    return;
  }
  ::add_read_mess(str, type, lang, size);
} /* add_read_mess() */

/**
 * @ignore yes 
 */
mixed query_read_mess() {
  if (_open_page) {
    if(!_pages[_open_page-1])
      return ({ });
    return _pages[_open_page-1];
  }
  return ::query_read_mess();
} /* query_read_mess() */

/** @ignore yes */
mixed *stats() {
  return ({
    ({ "num pages" , sizeof(_pages) }),
    ({ "ignore saved pages" , _ignore_saved_pages }),
    ({ "default page ob" , _default_page_object }),
    ({ "open page", _open_page }),
    ({ "book number" , _book_num }),
  }) + ::stats();
} /* stats() */

/**
 * @ignore yes 
 */
void dest_me() {
  int i;

  for (i=0;i<sizeof(_pages);i++) {
    if (objectp(_pages[i])) {
      _pages[i]->dest_me();
    }
  }
  ::dest_me();
} /* dest_me() */

/**
 * @ignore yes 
 */
mixed query_static_auto_load() {
  if (file_name(this_object())[0..8] == "/std/book") {
    return int_query_static_auto_load();
  }
} /* query_static_auto_load() */

/**
 * @ignore yes 
 */
mapping query_dynamic_auto_load() {
  mapping bing;

  bing = ([
      "::" : ::query_dynamic_auto_load(),
      "default page object" : _default_page_object,
      "open page" : _open_page,
      "book num" : _book_num,
    ]);
  if (!_ignore_saved_pages) {
    bing["pages"] = _pages;
  }
  return bing;
} /* query_dynamic_auto_load() */

/**
 * @ignore yes 
 */
void init_dynamic_arg(mapping map, object player) {
  int i;
  object *tmp;

  if (!player) {
    player = _player;
    if (!player) {
       player = this_player();
    }
  }
  if (map["::"]) {
    ::init_dynamic_arg(map["::"]);
  }
  if (map["default page object"]) {
    _default_page_object = map["default page object"];
    load_object(_default_page_object);
    _def_p_obj = find_object(_default_page_object);
  }
  if (map["pages"] && !_ignore_saved_pages) {
    // Are we looking at an old-style book that needs converting?
    if(sizeof(map["pages"]) && sizeof(map["pages"][0]) == 3) {
      for(i=0; i<sizeof(_pages); i++) {
        tmp = (object *)player->load_auto_load_to_array(map["pages"][i],
                                                        this_object(), player);
        if(sizeof(tmp->query_read_mess()))
          _pages[i] = tmp->query_read_mess()[0];
        tmp->move("/room/rubbish");
      }
    } else
      _pages = map["pages"];
  }
  _book_num = map["book num"];
  set_open_page(map["open page"]);
} /* init_dynamic_arg() */

/**
 * Sets the object to use as the default page.  If this is not set the
 * the object /obj/misc/paper.c will be used.
 * @param ob the object to use as the default page
 */
void set_default_page_object(string obj) {
  load_object(obj);
  if (find_object(obj)) {
    _default_page_object = obj;
    _def_p_obj = find_object(obj);
  }
} /* set_default_page_object() */

/**
 * This method returns the default page object.
 * @return the default page object (as a string)
 */
string query_default_page_object() {
   return _default_page_object;
} /* query_default_page_object() */

/**
 * This method creates a new default page object for this book.  THis
 * can be used for whatever nefarious purposes you want.
 * @return a new default page object
 */
object create_default_page_object() {
   return clone_object(_default_page_object);
} /* create_default_page_object() */

/**
 * Queries the number of pages in the book.
 * @return the number of pages in the book
 * @see set_no_pages()
 */
int query_num_pages() {
  return sizeof(_pages);
} /* query_num_pages() */

/**
 * This method returns the current number of the book.  This is assigned
 * and used in conjunction with the book handler.
 * @see /obj/handlers/book_handler.c
 * @see /std/print_shop.c
 * @return the current book number
 */
int query_book_num() {
   return _book_num;
} /* query_book_num() */

/**
 * This method sets the current number of the book.  This is assigned
 * and used in conjunction with the book handler.
 * @see /obj/handlers/book_handler.c
 * @see /std/print_shop.c
 * @param num the new book number
 */
void set_book_num(int num) {
   _book_num = num;
} /* set_book_num() */

/**
 * This method returns the current setting of the ignore saved
 * pages variable.  If this is true then
 * the pages form the players auto_load stuff will be ignored totaly
 * and only the page creates in setup would be
 * used.
 * @see set_ignore_saved_pages()
 * @return the current ignore saved page attribute
 */
int query_ignore_saved_pages() {
   return _ignore_saved_pages;
} /* query_ignore_saved_pages() */

/**
 * This method sets the current setting of the ignore saved
 * pages variable.  If this is true then
 * the pages form the players auto_load stuff will be ignored totaly
 * and only the page creates in setup would be
 * used.  This can only be called from an object
 * which inherits this one (hence the protected method).
 * @param saved the new value of the saved attribute
 * @see query_ignore_saved_pages()
 */
protected void set_ignore_saved_pages(int saved) {
   _ignore_saved_pages = saved;
} /* set_ignore_saved_pages() */

/**
 * This method is called by the auto loading process to set the current
 * player on this object.
 * @param player the player to set
 * @see query_player()
 */
void set_player(object player) {
   _player = player;
} /* set_player() */

/**
 * This method will return  the player associated with the auto loading
 * process.
 * @return the player in use by the autoloading
 * @see set_player()
 */
object query_player() {
   return _player;
} /* query_player() */

/**
 * @ignore
 * This will determine the books read short, which is different to a
 * normal short because it says wild stuff about the cover and things.
 */
string query_read_short(object player, int ignore_labels) {
  if(!::query_read_short(player, 0))
    return 0;
  if (!_open_page) {
    return "the cover of " + ::query_read_short(player, 0);
  }

  return "page " + query_num(_open_page) + " of " +
    ::query_read_short(player, 1);
} /* query_read_short() */

/**
 * @ignore
 * This is overridden specifically to stop labels appearing on anything
 * but the cover.
 */
string query_readable_message(object player, int ignore_labels) {
   return ::query_readable_message(player, _open_page != 0);
} /* query_readable_message() */