tmi2_fluffos_v2/
tmi2_fluffos_v2/bin/
tmi2_fluffos_v2/etc/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/ChangeLog.old/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/Win32/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/compat/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/compat/simuls/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/include/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/clone/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/command/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/data/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/etc/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/include/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/inherit/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/inherit/master/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/log/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/compiler/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/efuns/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/operators/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/u/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/tmp/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/windows/
tmi2_fluffos_v2/lib/
tmi2_fluffos_v2/lib/adm/
tmi2_fluffos_v2/lib/adm/daemons/languages/
tmi2_fluffos_v2/lib/adm/daemons/network/I3/
tmi2_fluffos_v2/lib/adm/daemons/virtual/
tmi2_fluffos_v2/lib/adm/daemons/virtual/template/
tmi2_fluffos_v2/lib/adm/news/
tmi2_fluffos_v2/lib/adm/obj/
tmi2_fluffos_v2/lib/adm/obj/master/
tmi2_fluffos_v2/lib/adm/priv/
tmi2_fluffos_v2/lib/adm/shell/
tmi2_fluffos_v2/lib/adm/tmp/
tmi2_fluffos_v2/lib/cmds/
tmi2_fluffos_v2/lib/d/
tmi2_fluffos_v2/lib/d/Conf/
tmi2_fluffos_v2/lib/d/Conf/adm/
tmi2_fluffos_v2/lib/d/Conf/boards/
tmi2_fluffos_v2/lib/d/Conf/cmds/
tmi2_fluffos_v2/lib/d/Conf/data/
tmi2_fluffos_v2/lib/d/Conf/logs/
tmi2_fluffos_v2/lib/d/Conf/obj/
tmi2_fluffos_v2/lib/d/Conf/text/help/
tmi2_fluffos_v2/lib/d/Fooland/adm/
tmi2_fluffos_v2/lib/d/Fooland/data/
tmi2_fluffos_v2/lib/d/Fooland/data/attic/
tmi2_fluffos_v2/lib/d/Fooland/items/
tmi2_fluffos_v2/lib/d/TMI/
tmi2_fluffos_v2/lib/d/TMI/adm/
tmi2_fluffos_v2/lib/d/TMI/boards/
tmi2_fluffos_v2/lib/d/TMI/data/
tmi2_fluffos_v2/lib/d/TMI/rooms/
tmi2_fluffos_v2/lib/d/grid/
tmi2_fluffos_v2/lib/d/grid/adm/
tmi2_fluffos_v2/lib/d/grid/data/
tmi2_fluffos_v2/lib/d/std/
tmi2_fluffos_v2/lib/d/std/adm/
tmi2_fluffos_v2/lib/data/adm/
tmi2_fluffos_v2/lib/data/adm/daemons/
tmi2_fluffos_v2/lib/data/adm/daemons/doc_d/
tmi2_fluffos_v2/lib/data/adm/daemons/emoted/
tmi2_fluffos_v2/lib/data/adm/daemons/network/http/
tmi2_fluffos_v2/lib/data/adm/daemons/network/services/mail_q/
tmi2_fluffos_v2/lib/data/adm/daemons/network/smtp/
tmi2_fluffos_v2/lib/data/adm/daemons/news/archives/
tmi2_fluffos_v2/lib/data/attic/connection/
tmi2_fluffos_v2/lib/data/attic/user/
tmi2_fluffos_v2/lib/data/std/connection/b/
tmi2_fluffos_v2/lib/data/std/connection/l/
tmi2_fluffos_v2/lib/data/std/user/a/
tmi2_fluffos_v2/lib/data/std/user/b/
tmi2_fluffos_v2/lib/data/std/user/d/
tmi2_fluffos_v2/lib/data/std/user/f/
tmi2_fluffos_v2/lib/data/std/user/l/
tmi2_fluffos_v2/lib/data/std/user/x/
tmi2_fluffos_v2/lib/data/u/d/dm/working/doc_d/
tmi2_fluffos_v2/lib/data/u/l/leto/doc_d/
tmi2_fluffos_v2/lib/data/u/l/leto/smtp/
tmi2_fluffos_v2/lib/doc/
tmi2_fluffos_v2/lib/doc/driverdoc/applies/
tmi2_fluffos_v2/lib/doc/driverdoc/applies/interactive/
tmi2_fluffos_v2/lib/doc/driverdoc/concepts/
tmi2_fluffos_v2/lib/doc/driverdoc/driver/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/arrays/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/buffers/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/compile/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/ed/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/filesystem/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/floats/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/functions/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/general/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/mappings/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/numbers/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/parsing/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/constructs/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/preprocessor/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/types/
tmi2_fluffos_v2/lib/doc/driverdoc/platforms/
tmi2_fluffos_v2/lib/doc/mudlib/
tmi2_fluffos_v2/lib/ftp/
tmi2_fluffos_v2/lib/include/driver/
tmi2_fluffos_v2/lib/log/
tmi2_fluffos_v2/lib/log/driver/
tmi2_fluffos_v2/lib/obj/net/
tmi2_fluffos_v2/lib/obj/shells/
tmi2_fluffos_v2/lib/obj/tools/
tmi2_fluffos_v2/lib/std/adt/
tmi2_fluffos_v2/lib/std/board/
tmi2_fluffos_v2/lib/std/body/
tmi2_fluffos_v2/lib/std/fun/
tmi2_fluffos_v2/lib/std/living/
tmi2_fluffos_v2/lib/std/object/
tmi2_fluffos_v2/lib/std/shop/
tmi2_fluffos_v2/lib/std/socket/
tmi2_fluffos_v2/lib/std/user/
tmi2_fluffos_v2/lib/std/virtual/
tmi2_fluffos_v2/lib/student/
tmi2_fluffos_v2/lib/student/kalypso/
tmi2_fluffos_v2/lib/student/kalypso/armor/
tmi2_fluffos_v2/lib/student/kalypso/rooms/
tmi2_fluffos_v2/lib/student/kalypso/weapons/
tmi2_fluffos_v2/lib/u/l/leto/
tmi2_fluffos_v2/lib/u/l/leto/cmds/
tmi2_fluffos_v2/lib/www/errors/
tmi2_fluffos_v2/lib/www/gateways/
tmi2_fluffos_v2/lib/www/images/
tmi2_fluffos_v2/old/
tmi2_fluffos_v2/win32/
/*
// File:          /d/Conf/voting_room.c
// Purpose:       A generic inheritable voting room
// Credits:
// 93-03-16       Douglas Reay (Pallando @TMI and other muds) wrote it.
//                This is not part of TMI's distribution, but may be used by
//                others, at the discretion of the author, on the same terms.
// 93-06-28       Pallando split off the stuff specific to TMI's conferences
*/

#include <config.h>
#include <mudlib.h>

inherit ROOM;

#define DEFAULT_SAVE_FILE TMP_DIR "votes"
#define TMP_FILE TMP_DIR + geteuid( this_player() ) + ".prop"
#define DEFAULT_OPTIONS ({ "endorse", "reject", "abstain" })

mapping vote_data;
mapping new_data;
string save_file;
string *default_options;

// room_create() has to be called after the restore_object in create() so that
// the room description can be changed without losing the vote data.
void room_create()
{
  set( "light", 1 );
  set( "short", "A generic voting room" );
  set( "long", @EndText
The long description of a generic voting room.
Type "help room" to get aid on the facilities available.
EndText
  );
}

// To alter the save_file in a voting room inheriting this one, add a function
//   create() { save_file = <...>; ::create(); }
// See at TMI-2 /d/Conf/room/conf_voting_room.c for an example.
void create()
{
  ::create();
  seteuid( getuid() );
  if( !save_file ) save_file = DEFAULT_SAVE_FILE;
  restore_object( save_file );
  if( !vote_data ) vote_data = ([]);
  if( !new_data ) new_data = ([]);
  if( !default_options ) default_options = DEFAULT_OPTIONS;
  room_create();
}

int is_convener()
{
  return wizardp( this_player() );
}

string is_voter()
{
  return capitalize( geteuid( this_player() ) );
}

void init()
{
  add_action( "cmd_help", "help" );
  add_action( "cmd_list", "list" );
  if( is_voter() )
  {
    add_action( "cmd_vote", "vote" );
    add_action( "cmd_propose", "propose" );
  }
  if( !is_convener() ) return;
  add_action( "cmd_nlist", "nlist" );
  add_action( "cmd_verify", "verify" );
  add_action( "cmd_proposer", "proposer" );
  add_action( "cmd_unverify", "unverify" );
  add_action( "cmd_edit", "edit" );
  add_action( "cmd_options", "options" );
  add_action( "cmd_proxy", "proxy" );
}

/************************ Functions anyone can use ************************/

int cmd_help( string a )
{
  if( a != "room" ) return 0;
  write( ""+
"help room     this message\n"+
"list          lists the proposals\n"+
"list <p>      lists proposal <p>\n"+
  "" );
  if( is_voter() ) write( ""+
"vote <p> <o>  Vote option <o> on proposal <p>\n"+
"vote <p>      Withdraw your vote on proposal <p>\n"+
"propose <p>   Make a new proposal <p> (subject to convener verification)\n"+
  "" );
  if( is_convener() ) write( ""+
"nlist <p>     List the unverified proposals.\n"+
"verify <p>    Move proposal <p> to the verified list.\n"+
"verify <p> as <new_p>     Rename proposal <p>\n"+
"unverify <p>  Move proposal <p> to the unverified list.\n"+
"              (Or, if <p> is already unverified, delete it.)\n"+
"proposer <p> <name>       Set the proposer of proposal <p> to <name>\n"+
"edit <p>      Edit text of proposal <p>\n"+
"options <p> <o>,<o>,...   Set the options for proposal <p> to <o>,<o>...\n"+
"proxy <voter> votes <p> <o>   Vote for someone else.\n"+
  "" );
  return 1;
}

int cmd_list( string a )
{
  string *names, option, *voters, ret;
  mapping proposal;
  int i, j;

  names = keys( vote_data );
  if( !sizeof( names ) )
  {
    write( "There are no current proposals.\n" );
    return 1;
  }
  if( !a )
  {
    ret = "";
    for( i = sizeof( names ) ; i ; i-- )
    {
      proposal = vote_data[names[(i-1)]];
      ret += names[(i-1)] + "\tproposed by " + proposal["proposer"] + "\n";
      ret += proposal["text"];
      ret += wrap( "Valid options: " + implode( proposal["options"], ", " ) );
    }
    this_player()-> more( explode( ret, "\n" ) );
    return 1;
  }
  proposal = vote_data[a];
  if( !mapp( proposal ) )
  {
    write( wrap( "Current proposals are: " + implode( names, ", " ) ) );
    return 1;
  }
  write( a + "\tproposed by " + proposal["proposer"] + "\n" );
  write( proposal["text"] );
  write( wrap( "Valid options: " + implode( proposal["options"], ", " ) ) );
  if( !sizeof( keys( proposal["votes"] ) ) )
  {
    write( "No votes yet cast on it.\n" );
  } else {
    i = sizeof( proposal["options"] );
    if( i > 1 ) write( "Total votes cast = " +
      sizeof( keys( proposal["votes"] ) ) + "\n" );
    for( ; i ; i-- )
    {
      option = proposal["options"][(i-1)];
      voters = proposal["cast"][option];
      j = sizeof( voters );
      write( "Option: "+option+" - "+j+" vote"+((j-1)?"s":"")+"\n" );
      if( j ) write( "  "+implode(voters,"\n  ")+"\n" );
    }
  }
  return 1;
}

/************************ Functions voters can use ************************/

// The optional second argument is used by cmd_proxy()
varargs int cmd_vote( string a, string voter )
{
  string name, option, cast;
  string *names;
  mapping proposal;

  if( !a )
    { notify_fail( "Syntax: vote <proposal> <option>\n" ); return 0; }
  if( sscanf( a, "%s %s", name, option ) < 2 )
    { name = a; option = 0; }
  names = keys( vote_data );
  if( !sizeof( names ) )
  {
    write( "There are no current proposals.\n" );
    return 1;
  }
  if( -1 == member_array( name, names ) )
  {
    write( wrap( "The current proposals are: " + implode( names, ", " ) ) );
    return 1;
  }
  proposal = vote_data[name];
  if( !voter ) voter = is_voter();
  if( !option )
  {
    if( cast = proposal["votes"][voter] )
    {
      proposal["cast"][cast] -= ({ voter });
      map_delete( proposal["votes"], voter );
      vote_data[name] = proposal;
      save_object( save_file );
      write( "You uncast your vote on proposal " + name + "\n" );
      return 1;
    }
    write( "You have no vote cast on proposal " + name + "\n" );
    return 1;
  }
  if( -1 == member_array( option, proposal["options"] ) )
  {
    write( wrap( "The valid options are: " +
      implode( proposal["options"], ", " ) ) );
    return 1;
  }
  if( cast = proposal["votes"][voter] )
    proposal["cast"][cast] -= ({ voter });
  proposal["votes"][voter] = option;
  proposal["cast"][option] += ({ voter });
  vote_data[name] = proposal;
  save_object( save_file );
  write( "You vote " + option + " on proposal " + name + "\n" );
  return 1;
}

int cmd_propose( string a )
{
  string tmp;

  if( !a ) { notify_fail( "Syntax: propose <name>\n" ); return 0; }
  if( -1 != member_array( a, keys( vote_data ) + keys( new_data ) ) )
    { notify_fail( "There is already a proposal named "+a+"\n" ); return 0; }
  notify_fail( "You may not have spaces in the proposal name.\n" );
  if( sscanf( a, "%*s %s", tmp ) ) return 0;
  write( "Enter the text of your proposal.\n" );
  this_player()-> edit( TMP_FILE, "callback_propose", this_object(), a );
  return 1;
}

// This function is used by cmd_propose()
int callback_propose( string a )
{
  mapping proposal;
  string text;

  proposal = ([]);
  text = read_file( TMP_FILE );
  rm( TMP_FILE );
  if( !text ) { notify_fail( "No proposal entered.\n" ); return 0; }
  proposal["text"] = text;
  proposal["proposer"] = is_voter();
  proposal["options"] = default_options;
  // This duplicates the get_options() incase someone's input_to is interupted.
  new_data[a] = proposal;
  write( "Your proposal will be added when a convener verifies it.\n" );
  save_object( save_file );
  write( "Press <return> for the options to be "+
    implode( default_options, ", " ) + "\n" +
    "Otherwise, enter your new options, seperating each one with a comma\n"+
    "New options = " );
  input_to( "get_options", ({ 0, a }) );
  return 1;
}

// This function initalises a proposal to accept votes on its options,
// If any votes have already been cast then these are cleared.
// Used by get_options() and cmd_verify()
mapping init_votes( mapping proposal )
{
  int i;

  proposal["votes"] = ([]);
  proposal["cast"] = ([]);
  if( !proposal["options"] ) proposal["options"] = default_options;
  for( i = sizeof( proposal["options"] ) ; i ; i-- )
    proposal["cast"][( proposal["options"][(i-1)] )] = ({ });
  return proposal;
}

// This function is used by callback_propose() and cmd_options()
int get_options( string opts, mixed args )
{
  string *options;
  mapping proposal;

  if( opts ) options = explode( opts, "," );
  else options = default_options;
  proposal = ( args[0] ? vote_data[(args[1])] : new_data[(args[1])] );
  proposal["options"] = options;
  proposal = init_votes( proposal );
  if( args[0] ) vote_data[(args[1])] = proposal;
  else new_data[(args[1])] = proposal;
  write( wrap( "The options for proposal " + args[1] + " are: " +
    implode( options, ", " ) ) );
  save_object( save_file );
  return 1;
}

/************************ Functions conveners can use ************************/

int cmd_nlist( string a )
{
  if( a && !undefinedp( new_data[a] ) )
    write( wrap( identify( new_data[a] ) ) );
  else if( sizeof( keys( new_data ) ) )
    write( wrap( implode( keys( new_data ), " " ) ) );
  else write( "There are no unverified proposals.\n" );
  return 1;
}

int cmd_verify( string a )
{
  mapping proposal;
  string name, new_name;
  int i;

  if( !a )
  {
    notify_fail( "Syntax: verify <name>[ as <new_name>]\n" );
    return 0;
  }
  if( sscanf( a, "%s as %s", name, new_name ) < 2 )
    { name = a; new_name = a; }
  if( !new_data[name] )
  {
    if( vote_data[name] && ( name != new_name ) )
    {
      vote_data[new_name] = vote_data[name];
      map_delete( vote_data, name );
      write( "Proposal " + name + " renamed as " + new_name + "\n" );
      save_object( save_file );
      return 1;
    }
    notify_fail( "I do not recognise " + name + " as the name of an "+
      "unverified proposal.\n" );
    return 0; 
  }
  proposal = init_votes( new_data[name] );
  vote_data[new_name] = proposal;
  map_delete( new_data, name );
  write( wrap( "ADDING PROPOSAL " + new_name + "\n" + identify( proposal ) ) );
  save_object( save_file );
  return 1;
}

int cmd_proposer( string a )
{
  string name, proposer;

  if( !a || sscanf( a, "%s %s", name, proposer ) <2 )
  {
    notify_fail( "Syntax: proposer <proposal> <proposer>\n" );
    return 0; 
  }
  if( !vote_data[name] ) 
  {
    notify_fail( name + " is not a verified proposal.\n" );
    return 0; 
  }
  vote_data[name]["proposer"] = proposer;
  save_object( save_file );
  write( "Ok.\n" );
  return 1;
}

int cmd_unverify( string a )
{
  if( !a ) { notify_fail( "Syntax: unverify <proposal_name>\n" ); return 0; }
  if( new_data[a] )
  {
    map_delete( new_data, a );
    save_object( save_file );
    write( "The unverified proposal " + a + " now deleted.\n" );
    return 1;
  }
  if( !vote_data[a] )
  {
    notify_fail( "No such proposal: " + a + "\n" );
    return 0;
  }
  new_data[a] = vote_data[a];
  map_delete( vote_data, a );
  save_object( save_file );
  write( "Proposal " + a + " moved to unverified list.\n" );
  return 1;
}

int cmd_edit( string a )
{
  mapping proposal;

  if( !a ) { notify_fail( "Syntax: edit <p>\n" ); return 0; }
  proposal = vote_data[a];
  if( !proposal ) { notify_fail( "No such proposal.\n" ); return 0; }
  rm( TMP_FILE );
  write_file( TMP_FILE, proposal["text"] );
  write( "Edit the text of the proposal:\n" );
  this_player()-> edit( TMP_FILE, "callback_edit", this_object(), a );
  return 1;
}

// This function is used by cmd_edit()
int callback_edit( string a )
{
  string text;

  text = read_file( TMP_FILE );
  rm( TMP_FILE );
  if( !text )
  {
    write( "Unverifying proposal.\n" );
    new_data[a] = vote_data[a];
    map_delete( vote_data, a );
    save_object( save_file );
    return 1;
  }
  vote_data[a]["text"] = text;
  save_object( save_file );
  write( "Ok.\n" );
  return 1;
}

int cmd_options( string a )
{
  string proposal_name, options;
  int veri;

  if( !a || ( sscanf( a, "%s %s", proposal_name, options ) < 2 ) )
  {
    notify_fail( "Syntax: options <p> <o1>,<o2>,<o3>,...\n" );
    return 0;
  }
  if( !undefinedp( vote_data[proposal_name] ) ) veri = 1;
  else if( !undefinedp( new_data[proposal_name] ) ) veri = 0;
  else { notify_fail( proposal_name + " is not a proposal.\n" ); return 0; }
  get_options( options, ({ veri, proposal_name }) );
  return 1;
}

int cmd_proxy( string a )
{
  string voter, vote;

  if( !a || ( sscanf( a, "%s votes %s", voter, vote ) < 2 ) )
  {
    notify_fail( "Syntax: proxy <voter> votes <proposal> <option>\n" );
    return 0;
  }
  return cmd_vote( vote, voter );
}

/************************ Functions debuggers can use ************************/

mixed query_vote_data( mixed a )
{
  if( !is_convener() ) return;
  if( a ) return vote_data;
  write( "vote_data = " + dump_variable( vote_data ) + "\n" );
  // If you don't have the dump_variable() simul_efun then don't worry
  // ... it does much the same as identify()
  return 1;
}

mixed query_new_data() { if( is_convener() ) return new_data; }
void set_new_data( mapping a ) { if( is_convener() ) new_data = a; }
void set_vote_data( mapping a ) { if( is_convener() ) vote_data = a; }

// NB If you had confidence about not losing data due to crashes, you could
// delete all the save_object() calls and put one in the remove() function.