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