ATM/Debit Card Snippet by Odis
==================================================================
http://ew.xidus.net

Disclaimer
==========
This should work on all Smaug derivitives (though it wouldn't exactly make sense for SMAUG :P).
This code was tested on a heavily modified version of SWR 1.0, but is should work on
everyones code anyway. I retain no responsibility if this code does anything to
your code, your game, or the system it's running on.

Description
===========
This is a very simple snippet I wipped up in about 15 minutes. This add's an option to the do_buy
command to buy with a atm/debit card and the funds come directly out of the person's bank account
instead of their cash on hand. They must have a debit card to do so. Simple, easy, and protects
some of the more conservative players happen as they wont have their money stolen as easily.

This is really basic. I show you how to modify do_buy. Based on this you can change do_buyship
and do_clanbuyship. You could also expand the system to allow for credit card theft and things
like that. You can also make player's quest to get their debit cards. Its up to you.

============
Instructions
============

(1) mud.h

Find:

/*
 * Item types.
 * Used in #OBJECTS.
 */
typedef enum
{
  ITEM_NONE, ITEM_LIGHT, ITEM_SCROLL, ITEM_WAND, ITEM_STAFF, ITEM_WEAPON,
  ITEM_FIREWEAPON, ITEM_MISSILE, ITEM_TREASURE, ITEM_ARMOR, ITEM_POTION,
  ITEM_WORN, ITEM_FURNITURE, ITEM_TRASH, ITEM_OLDTRAP, ITEM_CONTAINER,
  ITEM_NOTE, ITEM_DRINK_CON, ITEM_KEY, ITEM_FOOD, ITEM_MONEY, ITEM_PEN,
  ITEM_BOAT, ITEM_CORPSE_NPC, ITEM_CORPSE_PC, ITEM_FOUNTAIN, ITEM_PILL,
  ITEM_BLOOD, ITEM_BLOODSTAIN, ITEM_SCRAPS, ITEM_PIPE, ITEM_HERB_CON,
  ITEM_HERB, ITEM_INCENSE, ITEM_FIRE, ITEM_BOOK, ITEM_SWITCH, ITEM_LEVER,
  ITEM_PULLCHAIN, ITEM_BUTTON, ITEM_DIAL, ITEM_ORE, ITEM_DRILL,
  ITEM_MATCH, ITEM_TRAP, ITEM_MAP, ITEM_PORTAL, ITEM_PAPER,
  ITEM_TINDER, ITEM_LOCKPICK, ITEM_SPIKE, ITEM_METAL, ITEM_OIL, ITEM_FUEL,
  ITEM_SHORT_BOW, ITEM_LONG_BOW, ITEM_CROSSBOW, ITEM_AMMO, ITEM_QUIVER,
  ITEM_SHOVEL, ITEM_SALVE, ITEM_RAWSPICE, ITEM_LENS, ITEM_CRYSTAL, ITEM_DURAPLAST,
  ITEM_BATTERY, ITEM_TOOLKIT, ITEM_DURASTEEL, ITEM_OVEN, ITEM_MIRROR,
  ITEM_CIRCUIT, ITEM_SUPERCONDUCTOR, ITEM_COMLINK, ITEM_MEDPAC, ITEM_FABRIC,
  ITEM_RARE_METAL, ITEM_MAGNET, ITEM_THREAD, ITEM_SPICE, ITEM_SMUT, ITEM_DEVICE, ITEM_SPACECRAFT,
  ITEM_GRENADE, ITEM_LANDMINE, ITEM_PIECE, ITEM_DROID_CORPSE, ITEM_BOLT, ITEM_CHEMICAL
} item_types;

Change one to ITEM_DEBIT_CARD or add ITEM_DEBIT_CARD to the end.

If you added ITEM_DEBIT_CARD to the end:

#define MAX_ITEM_TYPE		     ITEM_CHEMICAL

Needs to be:

#define MAX_ITEM_TYPE		     ITEM_DEBIT_CARD

(2) build.c

Find:

char *	const	o_types	[] =
{
"none", "light", "_scroll", "_wand", "staff", "weapon", "_fireweapon", "missile",
"treasure", "armor", "potion", "_worn", "furniture", "trash", "_oldtrap",
"container", "_note", "drinkcon", "key", "food", "money", "pen", "_boat",
"corpse", "corpse_pc", "fountain", "pill", "_blood", "_bloodstain",
"scraps", "_pipe", "_herbcon", "_herb", "_incense", "fire", "book", "switch",
"lever", "_pullchain", "button", "dial", "_rune", "_runepouch", "_match", "trap",
"map", "_portal", "paper", "_tinder", "lockpick", "_spike", "_disease", "_oil",
"fuel", "_shortbow", "_longbow", "_crossbow", "ammo", "_quiver", "shovel",
"salve", "rawspice", "lens", "crystal", "duraplast", "battery", 
"toolkit", "durasteel", "oven", "mirror", "circuit", "superconductor", "comlink", "medpac",
"fabric", "rare_metal", "magnet",  "thread", "spice", "smut", "device", "spacecraft",
"grenade", "landmine", "government", "droid_corpse", "bolt", "chemical"
};

Change the one you changed, or add "debit_card" to the end if you added ITEM_DEBIT_CARD instead of changing
a previous value.

Find inside do_oset:
           send_to_char( "None        Light\n\r", ch );
           send_to_char( "Treasure    Armor      Comlink    Fabric      Grenade\n\r", ch );
           send_to_char( "Furniture   Trash      Container  Drink_con   Landmine\n\r", ch );
           send_to_char( "Key         Food       Money      Pen         Fuel\n\r", ch );
           send_to_char( "Fountain    Pill       Weapon     Medpac      Missile\n\r", ch );
           send_to_char( "Fire        Book       Superconductor         Rare_metal\n\r", ch );
           send_to_char( "Switch      Lever      Button     Dial        Government\n\r", ch );
           send_to_char( "Trap        Map        Portal     Paper       Magnet\n\r", ch );
           send_to_char( "Lockpick    Shovel     Thread     Smut        Ammo\n\r", ch );
           send_to_char( "Rawspice    Lens       Crystal    Duraplast   Battery\n\r", ch );	   
           send_to_char( "Toolkit     Durasteel  Oven       Mirror      Circuit\n\r", ch );	   
           send_to_char( "Potion      Salve      Pill       Device      Spacecraft\n\r", ch );	   
           send_to_char( "Bolt        Chemical\n\r", ch );	   

Change this to reflect which one you changed, or add Debit_card to the end.

(3) shops.c

Here is the modified do_buy function. Lines with a + are lines I added. Just change yours or copy and paste
(if you copy and paste, don't forget to take out the +'s!)

void do_buy( CHAR_DATA *ch, char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    int maxgold;
+    bool debit;

    argument = one_argument( argument, arg );

    if ( arg[0] == '\0' )
    {
	send_to_char( "Buy what?\n\r", ch );
	return;
    }

    if ( IS_SET(ch->in_room->room_flags, ROOM_PET_SHOP) )
    {
	char buf[MAX_STRING_LENGTH];
	CHAR_DATA *pet;
	ROOM_INDEX_DATA *pRoomIndexNext;
	ROOM_INDEX_DATA *in_room;
	
+    if ( argument[0] == '\0' )
+      debit = FALSE;
+   else if ( !str_cmp( "atm", argument ) || !str_cmp( "debit", argument ) )
+   {
+      bool has_card = FALSE;
+      
+      for ( obj = ch->last_carrying; obj; obj = obj->prev_content )        
+      {
+          if ( obj->item_type == ITEM_DEBIT_CARD )
+            has_card = TRUE;
+      }   
+      
+     if ( has_card == TRUE )
+      debit = TRUE;
+     else
+     {
+       send_to_char( "You don't even have your card with you!\n\r", ch );
+       return;    
+     }    
+    }    


	if ( IS_NPC(ch) )
	    return;

	pRoomIndexNext = get_room_index( ch->in_room->vnum + 1 );
	if ( !pRoomIndexNext )
	{
	    bug( "Do_buy: bad pet shop at vnum %d.", ch->in_room->vnum );
	    send_to_char( "Sorry, you can't buy that here.\n\r", ch );
	    return;
	}

	in_room     = ch->in_room;
	ch->in_room = pRoomIndexNext;
	pet         = get_char_room( ch, arg );
	ch->in_room = in_room;

	if ( pet == NULL || !IS_NPC( pet ) || !IS_SET(pet->act, ACT_PET) )
	{
	    send_to_char( "Sorry, you can't buy that here.\n\r", ch );
	    return;
	}

	if ( IS_SET(ch->act, PLR_BOUGHT_PET) )
	{
	    send_to_char( "You already bought one pet this level.\n\r", ch );
	    return;
	}

+	if ( (ch->gold < 10 * pet->top_level * pet->top_level) && debit == FALSE ) /* NOTE: THIS LINE CHANGED! */
	{
	    send_to_char( "You can't afford it.\n\r", ch );
	    return;
	}
+	else if ( (ch->pcdata->bank < 10 * pet->top_level * pet->top_level) && debit == TRUE )
+	{
+	  send_to_char( "You dont have enough money in your bank account for it.\n\r", ch );
+       return;    
+     }    

	if ( ch->top_level < pet->top_level )
	{
	    send_to_char( "You're not ready for this pet.\n\r", ch );
	    return;
	}

	maxgold = 10 * pet->top_level * pet->top_level;

+    if ( debit == FALSE )
+	  ch->gold	-= maxgold; /* this was already here, btw */
+	else
+	  ch->pcdata->bank  -= maxgold;

	boost_economy( ch->in_room->area, maxgold );
	pet		= create_mobile( pet->pIndexData );
	SET_BIT(ch->act, PLR_BOUGHT_PET);
	SET_BIT(pet->act, ACT_PET);
	SET_BIT(pet->affected_by, AFF_CHARM);

	argument = one_argument( argument, arg );
	if ( arg[0] != '\0' )
	{
	    sprintf( buf, "%s %s", pet->name, arg );
	    STRFREE( pet->name );
	    pet->name = STRALLOC( buf );
	}

	sprintf( buf, "%sA neck tag says 'I belong to %s'.\n\r",
	    pet->description, ch->name );
	STRFREE( pet->description );
	pet->description = STRALLOC( buf );

	char_to_room( pet, ch->in_room );
	add_follower( pet, ch );
	send_to_char( "Enjoy your pet.\n\r", ch );
    	act( AT_ACTION, "$n bought $N as a pet.", ch, NULL, pet, TO_ROOM );
	return;
    }
    else
    {
	CHAR_DATA *keeper;
	OBJ_DATA *obj;
	int cost;
	int noi = 1;		/* Number of items */
	sh_int mnoi = 20;	/* Max number of items to be bought at once */

	if ( ( keeper = find_keeper( ch ) ) == NULL )
	    return;

	maxgold = keeper->top_level * 10;

	if ( is_number( arg ) )
	{
	    noi = atoi( arg );
	    argument = one_argument( argument, arg );
	    if ( noi > mnoi )
	    {
		act( AT_TELL, "$n tells you 'I don't sell that many items at"
		  " once.'", keeper, NULL, ch, TO_VICT );
		ch->reply = keeper;
		return;
	    }
	}

+    if ( argument[0] == '\0' )
+      debit = FALSE;
+   else if ( !str_cmp( "atm", argument ) || !str_cmp( "debit", argument ) )
+   {
+      bool has_card = FALSE;
+      
+      for ( obj = ch->last_carrying; obj; obj = obj->prev_content )        
+      {
+          if ( obj->item_type == ITEM_DEBIT_CARD )
+            has_card = TRUE;
+      }   
+      
+     if ( has_card == TRUE )
+      debit = TRUE;
+     else
+     {
+       send_to_char( "You don't even have your card with you!\n\r", ch );
+       return;    
+     }    
+    }    

	obj  = get_obj_carry( keeper, arg );
	
	if ( !obj && arg[0] == '#' )
        {     
              int onum, oref;
              bool ofound = FALSE;
              
              onum =0;
              oref = atoi(arg+1);
              for ( obj = keeper->last_carrying; obj; obj = obj->prev_content )
	      { 
	        if ( obj->wear_loc == WEAR_NONE
	        &&   can_see_obj( ch, obj ) )
	            onum++;
                if ( onum == oref ) 
                {
                    ofound = TRUE;
                    break;
                }
                else if ( onum > oref )
                   break;
	      }
	      if (!ofound)
	         obj = NULL;
        }
	
	cost = ( get_cost( ch, keeper, obj, TRUE ) * noi );
	if ( cost <= 0 || !can_see_obj( ch, obj ) )
	{
	    act( AT_TELL, "$n tells you 'I don't sell that -- try 'list'.'",
		keeper, NULL, ch, TO_VICT );
	    ch->reply = keeper;
	    return;
	}

	if ( !IS_OBJ_STAT( obj, ITEM_INVENTORY ) && ( noi > 1 ) )
	{
	    interpret( keeper, "laugh" );
	    act( AT_TELL, "$n tells you 'I don't have enough of those in stock"
	     " to sell more than one at a time.'", keeper, NULL, ch, TO_VICT );
	    ch->reply = keeper;
	    return;
	}
	
+	if ( ch->gold < cost && debit == FALSE) /* NOTE: THIS LINE CHANGED */
	{
	    act( AT_TELL, "$n tells you 'You can't afford to buy $p.'",
		keeper, obj, ch, TO_VICT );
	    ch->reply = keeper;
	    return;
	}
	
+	if ( ch->pcdata->bank < cost && debit == TRUE)
+	{
+	    send_to_char( "You are almost slide your card through, but you remember you don't have enough money!\n\r", ch );
+	    return;	    
+     }    

	if ( IS_SET(obj->extra_flags, ITEM_PROTOTYPE) 
             && get_trust( ch ) < LEVEL_IMMORTAL )
	{
	    act( AT_TELL, "$n tells you 'This is a only a prototype!  I can't sell you that...'", 
		keeper, NULL, ch, TO_VICT );
      	    ch->reply = keeper;
	    return;
	}

	if ( ch->carry_number + get_obj_number( obj ) > can_carry_n( ch ) )
	{
	    send_to_char( "You can't carry that many items.\n\r", ch );
	    return;
	}

	if ( ch->carry_weight + ( get_obj_weight( obj ) * noi )
		+ (noi > 1 ? 2 : 0) > can_carry_w( ch ) )
	{
	    send_to_char( "You can't carry that much weight.\n\r", ch );
	    return;
	}

	if ( noi == 1 )
	{
	    if ( !IS_OBJ_STAT( obj, ITEM_INVENTORY ) )  
	       separate_obj( obj );
	    act( AT_ACTION, "$n buys $p.", ch, obj, NULL, TO_ROOM );
    	    act( AT_ACTION, "You buy $p.", ch, obj, NULL, TO_CHAR );
	}
        else
	{
	    sprintf( arg, "$n buys %d $p%s.", noi,
		( obj->short_descr[strlen(obj->short_descr)-1] == 's'
		? "" : "s" ) );
	    act( AT_ACTION, arg, ch, obj, NULL, TO_ROOM );
	    sprintf( arg, "You buy %d $p%s.", noi,
		( obj->short_descr[strlen(obj->short_descr)-1] == 's'
		? "" : "s" ) );
	    act( AT_ACTION, arg, ch, obj, NULL, TO_CHAR );
	    act( AT_ACTION, "$N puts them into a bag and hands it to you.",
		ch, NULL, keeper, TO_CHAR );
	}

+   if ( debit == FALSE )
+	ch->gold     -= cost; /* this line was already here, btw */
+   else if ( debit == TRUE )
+     ch->pcdata->bank     -= cost;


	keeper->gold += cost;

	if ( keeper->gold > maxgold )
	{
	    boost_economy( keeper->in_room->area, keeper->gold - maxgold/2 );
	    keeper->gold = maxgold/2;
	    act( AT_ACTION, "$n puts some money into a large safe.", keeper, NULL, NULL, TO_ROOM );
	}

	if ( IS_OBJ_STAT( obj, ITEM_INVENTORY ) )
	{
	    OBJ_DATA *buy_obj, *bag;

	    buy_obj = create_object( obj->pIndexData, obj->level );

	    /*
	     * Due to grouped objects and carry limitations in SMAUG
	     * The shopkeeper gives you a bag with multiple-buy,
	     * and also, only one object needs be created with a count
	     * set to the number bought.		-Thoric
	     */
	    if ( noi > 1 )
	    {
		bag = create_object( get_obj_index( OBJ_VNUM_SHOPPING_BAG ), 1 );
		/* perfect size bag ;) */
		bag->value[0] = bag->weight + (buy_obj->weight * noi);
		buy_obj->count = noi;
		obj->pIndexData->count += (noi - 1);
		numobjsloaded += (noi - 1);
		obj_to_obj( buy_obj, bag );
		obj_to_char( bag, ch );
	    }
	    else
		obj_to_char( buy_obj, ch );
	}
        else
	{
	    obj_from_char( obj );
	    obj_to_char( obj, ch );
	}

	return;
    }
}

Compile
=======
Make clean, then a Make (recompile).


Ending Notes
============
There you have it, an incredibly simple debit card system. Of course you may want to note the use of this
in some help files and what not, as it is not currently promoted in any code displayed here.

Also, don't forget to make a debit card item. Player's cant use them if there arent any of them :P


Contact Information:
===================
Please send comments, suggestions, bugs, etc. to my e-mail.

Odis, Owner, Coder, and Head Immortal of Gundam Wing: The Awakening
http://ew.xidus.net
admin@ew.xidus.net