/************************************************************************ Realms of Aurealis James Rhone aka Vall of RoA house.c Player house code and immortal interfaces with it. Based on Circle3.0's beta 8 house code and heavily modified since. ******** Heavily modified and expanded ******** *** BE AWARE OF ALL RIGHTS AND RESERVATIONS *** ******** Heavily modified and expanded ******** All rights reserved henceforth. Please note that no guarantees are associated with any code from Realms of Aurealis. All code which has been released to the general public has been done so with an 'as is' pretense. RoA is based on both Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well as the RoA license. *** Read, Learn, Understand, Improve *** *************************************************************************/ // // Modifications: // 1/6/97 - streamline, change, rewrite (jtrhone) // House code is unacceptably unstable and its behavior // has prompted this rewrite. #include "conf.h" #include "sysdep.h" #include "structures.h" #include "comm.h" #include "handler.h" #include "db.h" #include "interpreter.h" #include "acmd.h" #include "utils.h" #include "house.h" #include "objsave.h" #include "lists.h" #include "global.h" // external functions extern long get_id_by_name(char *name); extern char *get_name_by_id(long id); extern int inv_count(obdata *ob); extern BOOL load_house_objects(int hvnum); extern BOOL save_object(obdata *obj, FILE *fp); // internal globals int num_houses = 0; houserec house_control[MAX_HOUSES]; // No longer save object files in BINARY format // save them in text format (re: objsave.c) -roa void crashsave_house(int vnum) { int rnum; char fname[128]; FILE *fp; if ((rnum = real_room(vnum)) < 0) return; sprintf(fname, "house/%d.house",vnum); if (!(fp = fopen(fname, "w"))) { sprintf(buf, "SYSERR: Error opening house file %d.house",vnum); mudlog(buf, BRF, LEV_IMM, TRUE); return; } if (inv_count(world[rnum].contents) > MAX_HOUSE_OBJS) { sprintf(buf, "%%1WARNING%%0: Max house contents (%d) exceeded.\n\r" "House contents not saved.\n\r", MAX_HOUSE_OBJS); send_to_room(buf, rnum); fclose(fp); return; } // if nothing in room, remove file and return if (!world[rnum].contents) { fclose(fp); remove(fname); return; } // get rid of the non rentables in the house (keys/mails etc) -roa extract_norents(world[rnum].contents); // check one more time just so we're not calling a function for no reason // if the room only had norents, now it has nothing, remove file and return if (!world[rnum].contents) { fclose(fp); remove(fname); return; } // save_object is recursive, just send the first object in the list // re: objsave.c if (!save_object(world[rnum].contents, fp)) { fclose(fp); send_to_room("Error saving house contents, notify an immortal...\n\r",rnum); return; } fclose(fp); send_to_room("Saving house...\n\r", rnum); REMOVE_BIT(ROOM_FLAGS2(rnum), HOUSE_CRASH); sprintf(buf, "SYSUPD: House #%d saved.", vnum); mudlog(buf, BUG, LEV_IMM, FALSE); } // wax a house file from disk, mudlog any errors -roa void delete_house_file(int vnum) { char fname[128]; FILE *fp; sprintf(fname, "house/%d.house",vnum); if (!(fp = fopen(fname, "r"))) { if (errno != ENOENT) { sprintf(buf, "SYSERR: Error deleting house file #%d. (1)", vnum); mudlog(buf, BRF, LEV_IMM, TRUE); } return; } fclose(fp); if (unlink(fname) < 0) { sprintf(buf, "SYSERR: Error deleting house file #%d. (2)", vnum); mudlog(buf, BRF, LEV_IMM, TRUE); } } /****************************************************************** * Functions for house administration (creation, deletion, etc. * *****************************************************************/ int find_house(sh_int vnum) { int i; for (i = 0; i < num_houses; i++) if (house_control[i].vnum == vnum) return i; return -1; } /* Save the house control information */ void save_house_control(void) { FILE *fp; if (!(fp = fopen(HCONTROL_FILE, "wb"))) { perror("SYSERR: Unable to open house control file"); return; } // al la JE, write the block in on fell swoop fwrite(house_control, sizeof(houserec), num_houses, fp); fclose(fp); } /* call from boot_db - will load control recs, load objs */ /* should do sanity checks on vnums & remove invalid records */ void boot_houses(void) { houserec temp_house; int rroom; FILE *fp; // clear everything out before we fill it memset((char *)house_control, 0, sizeof(houserec) * MAX_HOUSES); if (!(fp = fopen(HCONTROL_FILE, "rb"))) { mudlog("SYSERR: Unable to locate house control file.", BRF, LEV_IMM, TRUE); return; } while (!feof(fp) && num_houses < MAX_HOUSES) { fread(&temp_house, sizeof(houserec), 1, fp); if (feof(fp)) break; // if owner no longer in player_index, skip it if (!get_name_by_id(temp_house.owner)) continue; // if the vnum doesnt correspond to an actual room, skip it if ((rroom = real_room(temp_house.vnum)) < 0) continue; // if we have already loaded this house, skip it if ((find_house(temp_house.vnum)) >= 0) continue; // copy data over, bump up house_counter house_control[num_houses++] = temp_house; // set appropriate flags on the entry room SET_BIT(ROOM_FLAGS(rroom), PRIVATE); SET_BIT(ROOM_FLAGS2(rroom), HOUSE); // load the objects into the house load_house_objects(temp_house.vnum); } fclose(fp); // finished reading in, now save it again save_house_control(); } /* "House Control" functions */ char *HCONTROL_FORMAT = "Usage: hcontrol build <house vnum> <player name>\r\n" " hcontrol destroy <house vnum>\r\n" " hcontrol pay <house vnum>\r\n" " hcontrol guestlist <house vnum>\n\r" " hcontrol squish <house vnum>\n\r" " hcontrol show\r\n"; #define NAME(x) ((temp = get_name_by_id(x)) == NULL ? "<UNDEF>" : temp) void list_houses(chdata * ch) { int i; char *timestr, *temp; char built_on[50], last_pay[50], own_name[50]; char buf[20000]; if (!num_houses) { send_to_char("No houses have been defined.\r\n", ch); return; } strcpy(buf, "%B%6 Vnum Owner #Guests Build Date Last Paymt%0\r\n"); strcat(buf, "%B%4------ --------- ------- ---------- ----------%0\r\n"); // go thru each house in house_control array and output info for (i = 0; i < num_houses; i++) { // determine build date if (house_control[i].built_on) { timestr = asctime(localtime(&(house_control[i].built_on))); *(timestr + 10) = 0; str_cpy(built_on, timestr, 50, "list_houses"); } else strcpy(built_on, "%4Unknown%0"); // determine last payment date if (house_control[i].last_payment) { timestr = asctime(localtime(&(house_control[i].last_payment))); *(timestr + 10) = 0; strcpy(last_pay, timestr); } else strcpy(last_pay, "%4None%0"); // owner's name in here strcpy(own_name, NAME(house_control[i].owner)); // now throw that info into the buf sprintf(buf+strlen(buf), "%-6d %-9.9s %-7d%-10.10s %-10.10s\n\r", house_control[i].vnum, CAP(own_name), house_control[i].num_guests, built_on, last_pay); } page_string(ch->desc, buf, 1); } void list_guests_to_char(chdata *ch, char *arg) { char owner[128]; int i, j; int vnum; char *temp; if (!is_number(arg)) { send_to_char("Argument must be a vnum.\n\r",ch); return; } vnum = atoi(arg); if ((i = find_house(vnum)) < 0) { sprintf(buf, "Vnum %d not defined in house control record.\n\r",vnum); S2C(); return; } strcpy(owner, NAME(house_control[i].owner)); sprintf(buf, "%%B%%6%s House Guest List%%0: House #%%6%d%%0, Owner: %%6%s%%0\n\r", shortmudname, house_control[i].vnum, CAP(owner)); S2C(); for (j = 0; j < house_control[i].num_guests; j++) { strcpy(buf, NAME(house_control[i].guests[j])); send_to_char(strcat(CAP(buf), "\r\n"), ch); } } // do the actual flagging of the house here // record build date and other data, no need for atriums anymore -roa void hcontrol_build_house(chdata *ch, char *arg) { char arg1[MAX_INPUT_LENGTH]; houserec temp_house; sh_int virt_house, real_house; long owner; if (num_houses >= MAX_HOUSES) { send_to_char("Max number of houses already defined. See Vall.\n\r",ch); return; } /* first arg: house's vnum */ arg = one_argument(arg, arg1); if (!*arg1) { send_to_char(HCONTROL_FORMAT, ch); return; } virt_house = atoi(arg1); if ((real_house = real_room(virt_house)) < 0) { send_to_char("No such room exists.\r\n", ch); return; } if ((find_house(virt_house)) >= 0) { send_to_char("House already exists.\r\n", ch); return; } /* second arg: player's name */ arg = one_argument(arg, arg1); if (!*arg1) { send_to_char(HCONTROL_FORMAT, ch); return; } if ((owner = get_id_by_name(arg1)) < 0) { sprintf(buf, "Unknown player '%s'.\r\n", arg1); S2C(); return; } temp_house.vnum = virt_house; temp_house.built_on = time(0); temp_house.owner = owner; temp_house.num_guests = 0; temp_house.last_payment = 0; temp_house.bitvector = 0; world[real_house].owner = owner; house_control[num_houses++] = temp_house; SET_BIT(ROOM_FLAGS(real_house), PRIVATE); SET_BIT(ROOM_FLAGS2(real_house), HOUSE); crashsave_house(virt_house); save_house_control(); send_to_char("House successfully created.\r\n", ch); send_to_char("Use %6rconnect%0 and %6wldsave%0 to connect and save.\n\r",ch); send_to_char("Remember: after rconnecting, wldsave BOTH affected zones.\n\r",ch); } void hcontrol_destroy_house(chdata *ch, char *arg) { int i, j; int real_house; if (!*arg) { send_to_char(HCONTROL_FORMAT, ch); return; } if ((i = find_house(atoi(arg))) < 0) { send_to_char("Unknown house.\r\n", ch); return; } if ((real_house = real_room(house_control[i].vnum)) < 0) { sprintf(buf, "SYSERR: House #%d, invalid vnum.",house_control[i].vnum); mudlog(buf, BRF, LEV_IMM, TRUE); } delete_house_file(house_control[i].vnum); for (j = i; j < num_houses - 1; j++) house_control[j] = house_control[j + 1]; num_houses--; send_to_char("House deleted.\r\n", ch); save_house_control(); // make sure the world jives, update it if (real_house >= 0) { world[real_house].owner = -1; FREENULL(world[real_house].name); FREENULL(world[real_house].description); world[real_house].name = str_dup("(default)"); world[real_house].description = str_dup("(default)\n\r"); REMOVE_BIT(ROOM_FLAGS(real_house), PRIVATE); REMOVE_BIT(ROOM_FLAGS2(real_house), HOUSE | HOUSE_CRASH); } } void hcontrol_pay_house(chdata *ch, char *arg) { int i; char owner[128]; char *temp; if (!*arg) { send_to_char(HCONTROL_FORMAT, ch); return; } if ((i = find_house(atoi(arg))) < 0) { send_to_char("Unknown house.\r\n", ch); return; } strcpy(owner, NAME(house_control[i].owner)); sprintf(buf, "PLRUPD: %s's house payment collected by %s.", CAP(owner), GET_NAME(ch)); mudlog(buf, NRM, LEV_IMM, TRUE); house_control[i].last_payment = time(0); save_house_control(); send_to_char("Payment recorded.\r\n", ch); } // eliminate guests from a house who are no longer in the pfile void squish_house(chdata *ch, char *arg) { char owner[128]; int i, j, k, count; int vnum; char *temp; if (!is_number(arg)) { send_to_char("Argument must be a vnum.\n\r",ch); return; } vnum = atoi(arg); if ((i = find_house(vnum)) < 0) { sprintf(buf, "Vnum %d not defined in house control record.\n\r",vnum); S2C(); return; } strcpy(owner, NAME(house_control[i].owner)); sprintf(buf, "Squishing %s's house.\n\r",CAP(owner)); S2C(); for (count = 0, j = 0; j < house_control[i].num_guests; j++) if (!get_name_by_id(house_control[i].guests[j])) { for (k = j; k < house_control[i].num_guests - 1; k++) house_control[i].guests[k] = house_control[i].guests[k + 1]; house_control[i].num_guests--; count++; } sprintf(buf, "%d invalid entries removed.\n\r",count); S2C(); } // The hcontrol command itself, used by imms to create/destroy houses // revamped, 1/6/97 jtrhone aka vall (see header above) ACMD(do_hcontrol) { char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH]; half_chop(argument, arg1, arg2); if (is_abbrev(arg1, "build")) hcontrol_build_house(ch, arg2); else if (is_abbrev(arg1, "destroy")) hcontrol_destroy_house(ch, arg2); else if (is_abbrev(arg1, "guestlist")) list_guests_to_char(ch, arg2); else if (is_abbrev(arg1, "pay")) hcontrol_pay_house(ch, arg2); else if (is_abbrev(arg1, "squish")) squish_house(ch, arg2); else if (is_abbrev(arg1, "show")) list_houses(ch); else send_to_char(HCONTROL_FORMAT, ch); } /* The house command, used by mortal house owners to assign guests */ ACMD(do_house) { int i, j, id; char tmparg[MAX_INPUT_LENGTH]; one_argument(argument, arg); if (!ROOM_FLAGGED2(ch->in_room, HOUSE)) send_to_char("You must be in your house to set guests.\r\n", ch); else if ((i = find_house(world[ch->in_room].number)) < 0) send_to_char("Your house was not found...seek immortal help.\r\n", ch); else if (GET_IDNUM(ch) != house_control[i].owner) send_to_char("Only the primary owner can set guests.\r\n", ch); else if (!*arg) { sprintf(tmparg, "%d", world[ch->in_room].number); list_guests_to_char(ch, tmparg); } else if (!str_cmp(arg, "public")) { SET_BIT(house_control[i].bitvector, HOUSE_PUBLIC); send_to_char("House now open to any player.\n\r",ch); } else if (!str_cmp(arg, "private")) { REMOVE_BIT(house_control[i].bitvector, HOUSE_PUBLIC); send_to_char("House now restricted to guest list.\n\r",ch); } else if ((id = get_id_by_name(arg)) < 0) send_to_char("No such player.\r\n", ch); else { for (j = 0; j < house_control[i].num_guests; j++) if (house_control[i].guests[j] == id) { for (; j < house_control[i].num_guests - 1; j++) house_control[i].guests[j] = house_control[i].guests[j + 1]; house_control[i].num_guests--; save_house_control(); send_to_char("Guest deleted.\r\n", ch); return; } // put in max guest check if (house_control[i].num_guests >= MAX_GUESTS) { send_to_char("You have the maximum number of guests already.\n\r",ch); return; } j = house_control[i].num_guests++; house_control[i].guests[j] = id; save_house_control(); send_to_char("Guest added.\r\n", ch); } } /* Misc. administrative functions */ /* crash-save all the houses */ void House_save_all(void) { int i; sh_int real_house; for (i = 0; i < num_houses; i++) if ((real_house = real_room(house_control[i].vnum)) >= 0) if (ROOM_FLAGGED2(real_house, HOUSE_CRASH)) crashsave_house(house_control[i].vnum); } // can a certain character enter a house int House_can_enter(chdata *ch, int house) { int i, j; if (GET_LEVEL(ch) >= LEV_GOD || (i = find_house(house)) < 0) return TRUE; if (GET_IDNUM(ch) == house_control[i].owner) return TRUE; for (j = 0; j < house_control[i].num_guests; j++) if (GET_IDNUM(ch) == house_control[i].guests[j]) return TRUE; if (HOUSE_FLAGGED(i, HOUSE_PUBLIC)) return TRUE; return FALSE; }