This is a couple of changes that I have decided to make to CircleMUD for a couple of reasons. The cloning was done for two reasons, first, to allow shopkeepers to sell 'boxes' of stuff, like a box of shells for a gun. Secondly, it allows immortals to copy one object, or a group of object, even if they are changed, and keep any changes that are made. The recall was done for one main reason: saving coding. To constantly have to copy and paste the recall code anywhere you need it is a pain to begin with, but when you add many variable to where a player is recalled to, that makes a BIG mess. For example, on my MUD (currently) there are 7 hometowns, and when you recall, you end up in your own home town. However, if you use a 'homing crest' you can go to the city of the crest. And if you are a vampire, you go to the 'catacombs' of the city. Also, the recall code appears in about 5-6 locations in the code and that is an awful lot of wasted space. Also, something to note about the cloning. It currently will change any different string on an object. For example, if you somehow changed the alias of an object (by drinking all it's liquid for example) then it will copy that change. So far, only aliases and action_description can be altered in the code. (The action_description is changed when you write on a note) Until some measure is done to save these changes in the rent file, the next time the player logs on, the changes will be gone. As far as I know, the best way to fix this is to convert your house files & rent files to ASCII text files. This is not an easy conversion, but it is useful, especially if you happen to draw up a map on a note and want to save it. And no, I will not help you convert your MUD to ASCII rent files. The format for my changes is pretty simple, what is between +++ +++ is to be added. If it tells you where to add it, put it there, otherwise, just stick it at the end of the file, or where it is appropriate (in the case of the interpreter.c stuff) With the change stuff, the format is this: !!! Original code !!! New code !!! All in all, the format is very similar to the .diff files, except that I didn't put in the EXACT locations for the changes....I am relying on you to do SOME work. Add: ****utils.c Add a include for db.h +++ struct obj_data *clone_obj(struct obj_data *obj) { struct obj_data *cont, *new_obj; int j; if (!obj) { log("SYSERR: Attempt to clone non-existant object"); return NULL; } /* Load the puppy! */ new_obj = read_object(GET_OBJ_RNUM(obj), REAL); /* Copy all them string things */ if (obj->name != new_obj->name) new_obj->name = str_dup(obj->name); if (obj->description != new_obj->description) new_obj-> description = str_dup(obj-> description); if (obj->short_description != new_obj->short_description) new_obj-> short_description = str_dup(obj-> short_description); if (obj->action_description != new_obj->action_description) new_obj-> action_description = str_dup(obj-> action_description); /* Copy the values */ for (j = 0; j < 4; j++) GET_OBJ_VAL(new_obj, j) = GET_OBJ_VAL(obj, j); /* Copy any affects */ GET_OBJ_EXTRA(new_obj) = GET_OBJ_EXTRA(obj); /* Copy any modifiers */ for (j = 0; j < MAX_OBJ_AFFECT; j++) { new_obj->affected[j].location = obj->affected[j].location; new_obj->affected[j].modifier = obj->affected[j].location; } /* Copy any contents */ if (obj->contains) for (cont = obj->contains; cont; cont = cont->next_content) obj_to_obj(clone_obj(cont), new_obj); return new_obj; } +++ +++ void perform_recall(struct char_data *ch, char *in_msg, char *out_msg, bool do_look) { sh_int to_room; extern sh_int r_mortal_start_room; to_room = r_mortal_start_room; /* add recall room variables here */ /* Example: hometowns or clans */ if (ch == NULL || IS_NPC(ch)) return; if (out_msg) act(out_msg, TRUE, ch, 0, 0, TO_ROOM); else act("$n disappears.", TRUE, ch, 0, 0, TO_ROOM); char_from_room(ch); char_to_room(ch, to_room); if (in_msg) act(in_msg, TRUE, ch, 0, 0, TO_ROOM); else act("$n appears in the middle of the room.", TRUE, ch, 0, 0, TO_ROOM); if (do_look) look_at_room(ch, 0); } +++ ****shop.c +++ int get_total_value(struct obj_data *obj) { struct obj_data *cont; int value; if (!obj) { log("SYSERR: Getting value of non-existant object"); return 0; } value = GET_OBJ_COST(obj); if (obj->contains) for (cont = obj->contains; cont; cont = cont->next_content) value += get_total_value(cont); return value; } +++ in same_obj, after if (GET_OBJ_EXTRA(obj1) != GET_OBJ_EXTRA(obj2)) return (FALSE); +++ if (GET_OBJ_WEIGHT(obj1) != GET_OBJ_WEIGHT(obj2)) return (FALSE); for (index = 0; index < 4; index++) if (GET_OBJ_VAL(obj1, index) != GET_OBJ_VAL(obj2, index)) return (FALSE); if (obj1->contains || obj2->contains) return (FALSE); if (obj1->name != obj2->name) return (FALSE); if (obj1->description != obj2->description) return (FALSE); if (obj1->short_description != obj2->short_description) return (FALSE); if (obj1->action_description != obj2->action_description) return (FALSE); +++ in list_object, add definitions: +++ struct obj_data *cont; int cont_num = 0; +++ in list_object, after if ((GET_OBJ_TYPE(obj) == ITEM_WAND) || (GET_OBJ_TYPE(obj) == ITEM_STAFF)) if (GET_OBJ_VAL(obj, 2) < GET_OBJ_VAL(obj, 1)) strcat(buf3, " (partially used)"); +++ if (obj->contains) { for (cont = obj->contains; cont; cont = cont->next_content, cont_num++); sprintf(END_OF(buf3), " (%d content%s)", cont_num, ((cont_num > 1) ? "s" : "")); } +++ in get_selling_obj, after case OBJECT_DEAD: sprintf(buf, "%s %s", GET_NAME(ch), MSG_NO_USED_WANDSTAFF); break; +++ case OBJECT_CONT_NOTOK: sprintf(buf, "%s I won't buy the contents of it!", GET_NAME(ch)); break; +++ in trade_with, after if (IS_OBJ_STAT(item, ITEM_NOSELL)) return (OBJECT_NOTOK); +++ if (item->contains) for (cont = item->contains; cont; cont = cont->next_content) { cont_test = trade_with(cont, shop_nr); if (cont_test != OBJECT_OK) return OBJECT_CONT_NOTOK; } +++ ****utils.h +++ void perform_recall(struct char_data *ch, char *in_msg, char *out_msg, bool do_look); struct obj_data *clone_obj(struct obj_data *obj); +++ ****shop.h +++ int get_total_value(struct obj_data *obj) +++ after /* Possible states for objects trying to be sold */ #define OBJECT_DEAD 0 #define OBJECT_NOTOK 1 #define OBJECT_OK 2 +++ #define OBJECT_CONT_NOTOK 3 +++ ****interpreter.c +++ ACMD(do_clone); +++ +++ { "clone" , POS_DEAD , do_clone , LVL_IMMORT, 0 }, +++ ****act.wizard.c +++ ACMD(do_clone) { struct obj_data *obj; one_argument(argument, arg); if (!*arg) { send_to_char("Clone what?\r\n", ch); return; } if (!(obj = get_obj_in_list_vis(ch, arg, ch->carrying))) { send_to_char("You don't have that!", ch); return; } obj = clone_obj(obj); obj_to_char(obj, ch); act("You draw a sigil on $p and it duplicates!", FALSE, ch, obj, 0, TO_CHAR); act("$n draws a sigil on $p and it duplicates!", FALSE, ch, obj, 0, TO_ROOM); } +++ Changes: ****spells.c !!! ASPELL(spell_recall) { extern sh_int r_mortal_start_room; if (victim == NULL || IS_NPC(victim)) return; act("$n disappears.", TRUE, victim, 0, 0, TO_ROOM); char_from_room(victim); char_to_room(victim, r_mortal_start_room); act("$n appears in the middle of the room.", TRUE, victim, 0, 0, TO_ROOM); look_at_room(victim, 0); } !!! ASPELL(spell_recall) { perform_recall(victim, NULL, NULL, TRUE); } !!! ****in shop.c !!! int buy_price(struct obj_data * obj, int shop_nr) { return ((int) (GET_OBJ_COST(obj) * SHOP_BUYPROFIT(shop_nr))); } !!! int buy_price(struct obj_data * obj, int shop_nr) { return ((int) (get_total_value(obj) * SHOP_BUYPROFIT(shop_nr))); } !!! !!! int sell_price(struct char_data * ch, struct obj_data * obj, int shop_nr) { return ((int) (GET_OBJ_COST(obj) * SHOP_SELLPROFIT(shop_nr))); } !!! int sell_price(struct char_data * ch, struct obj_data * obj, int shop_nr) { return ((int) (get_total_value(obj) * SHOP_SELLPROFIT(shop_nr))); } !!! in shopping_buy !!! if (shop_producing(obj, shop_nr)) obj = read_object(GET_OBJ_RNUM(obj), REAL); else { obj_from_char(obj); SHOP_SORT(shop_nr)--; } obj_to_char(obj, ch); !!! if (shop_producing(obj, shop_nr)) obj = clone_obj(obj); else { obj_from_char(obj); SHOP_SORT(shop_nr)--; } !!!