#include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include <time.h> #include <errno.h> #if defined(WIN32) && !defined(__CYGWIN__) #else #include <unistd.h> #endif #include "structs.h" #include "awake.h" #include "comm.h" #include "handler.h" #include "db.h" #include "newdb.h" #include "interpreter.h" #include "utils.h" #include "house.h" #include "memory.h" #include "constants.h" #include "file.h" #include "vtable.h" extern char *cleanup(char *dest, const char *src); extern void ASSIGNMOB(long mob, SPECIAL(fname)); struct landlord *landlords = NULL; ACMD(do_say); struct life_data { char *name; sh_int cost; }; struct life_data lifestyle[] = { { "Low", 1 }, { "Middle", 3 }, { "High", 10 }, { "Luxury", 25 } }; /* First, the basics: finding the filename; loading/saving objects */ /* Return a filename given a house vnum */ bool House_get_filename(vnum_t vnum, char *filename) { if (vnum == NOWHERE) return FALSE; sprintf(filename, "house/%ld.house", vnum); return TRUE; } /* Load all objects for a house */ bool House_load(struct house_control_rec *house) { vnum_t room; File fl; char fname[MAX_STRING_LENGTH]; rnum_t rnum; struct obj_data *obj = NULL, *last_obj = NULL; struct phone_data *k, *j; long vnum; int inside = 0, last_in = 0; room = house->vnum; if ((rnum = real_room(room)) == NOWHERE) return FALSE; if (!House_get_filename(room, fname)) return FALSE; if (!(fl.Open(fname, "r+b"))) /* no file found */ return FALSE; VTable data; data.Parse(&fl); fl.Close(); for (int i = 0; i < MAX_GUESTS; i++) { sprintf(buf, "GUESTS/Guest%d", i); long p = data.GetLong(buf, 0); if (!playerDB.DoesExist(p)) p = 0; house->guests[i] = p; } int num_objs = data.NumSubsections("HOUSE"); int x; for (int i = 0; i < num_objs; i++) { const char *sect_name = data.GetIndexSection("HOUSE", i); sprintf(buf, "%s/Vnum", sect_name); vnum = data.GetLong(buf, 0); if ((obj = read_object(vnum, VIRTUAL))) { sprintf(buf, "%s/Name", sect_name); obj->restring = str_dup(data.GetString(buf, NULL)); sprintf(buf, "%s/Photo", sect_name); obj->photo = str_dup(data.GetString(buf, NULL)); for (x = 0; x < 10; x++) { sprintf(buf, "%s/Value %d", sect_name, x); GET_OBJ_VAL(obj, x) = data.GetInt(buf, GET_OBJ_VAL(obj, x)); } if (GET_OBJ_TYPE(obj) == ITEM_PHONE && GET_OBJ_VAL(obj, 2)) { bool found = FALSE; sprintf(buf, "%d%d", GET_OBJ_VAL(obj, 0), GET_OBJ_VAL(obj, 1)); for (j = phone_list; j; j = j->next) if (j->number == atoi(buf)) { found = TRUE; break; } if (!found) { k = new phone_data; k->number = atoi(buf); k->phone = obj; if (phone_list) k->next = phone_list; phone_list = k; } } sprintf(buf, "%s/Condition", sect_name); GET_OBJ_CONDITION(obj) = data.GetInt(buf, GET_OBJ_CONDITION(obj)); sprintf(buf, "%s/Timer", sect_name); GET_OBJ_TIMER(obj) = data.GetInt(buf, GET_OBJ_TIMER(obj)); sprintf(buf, "%s/Attempt", sect_name); GET_OBJ_ATTEMPT(obj) = data.GetInt(buf, 0); sprintf(buf, "%s/Cost", sect_name); GET_OBJ_COST(obj) = data.GetInt(buf, GET_OBJ_COST(obj)); sprintf(buf, "%s/Inside", sect_name); inside = data.GetInt(buf, 0); if (inside > 0) { if (inside == last_in) last_obj = last_obj->in_obj; else if (inside < last_in) while (inside <= last_in) { last_obj = last_obj->in_obj; last_in--; } if (last_obj) obj_to_obj(obj, last_obj); } else obj_to_room(obj, rnum); last_in = inside; last_obj = obj; } } return TRUE; } void House_save(struct house_control_rec *house, FILE *fl, long rnum) { int level = 0, o = 0; struct obj_data *obj = NULL; if (house) { obj = world[real_room(house->vnum)].contents; fprintf(fl, "[GUESTS]\n"); for (;o < MAX_GUESTS; o++) fprintf(fl, "\tGuest%d:\t%ld\n", o, house->guests[o]); } else { obj = world[rnum].contents; } fprintf(fl, "[HOUSE]\n"); o = 0; for (;obj;) { if (!IS_OBJ_STAT(obj, ITEM_NORENT) && GET_OBJ_TYPE(obj) != ITEM_KEY) { fprintf(fl, "\t[Object %d]\n", o); o++; fprintf(fl, "\t\tVnum:\t%ld\n", GET_OBJ_VNUM(obj)); fprintf(fl, "\t\tInside:\t%d\n", level); if (GET_OBJ_TYPE(obj) == ITEM_PHONE) for (int x = 0; x < 4; x++) fprintf(fl, "\t\tValue %d:\t%d\n", x, GET_OBJ_VAL(obj, x)); else if (GET_OBJ_TYPE(obj) != ITEM_WORN) for (int x = 0; x < 10; x++) fprintf(fl, "\t\tValue %d:\t%d\n", x, GET_OBJ_VAL(obj, x)); fprintf(fl, "\t\tCondition:\t%d\n", GET_OBJ_CONDITION(obj)); fprintf(fl, "\t\tTimer:\t%d\n", GET_OBJ_TIMER(obj)); fprintf(fl, "\t\tAttempt:\t%d\n", GET_OBJ_ATTEMPT(obj)); fprintf(fl, "\t\tCost:\t%d\n", GET_OBJ_COST(obj)); fprintf(fl, "\t\tExtraFlags:\t%s\n", GET_OBJ_EXTRA(obj).ToString()); if (obj->restring) fprintf(fl, "\t\tName:\t%s\n", obj->restring); if (obj->photo) fprintf(fl, "\t\tPhoto:$\n%s~\n", cleanup(buf2, obj->photo)); } if (obj->contains && !IS_OBJ_STAT(obj, ITEM_NORENT) && GET_OBJ_TYPE(obj) != ITEM_PART) { obj = obj->contains; level++; continue; } else if (!obj->next_content && obj->in_obj) while (obj && !obj->next_content && level >= 0) { obj = obj->in_obj; level--; } if (obj) obj = obj->next_content; } } struct house_control_rec *find_house(vnum_t vnum) { struct landlord *llord; struct house_control_rec *room; for (llord = landlords; llord; llord = llord->next) for (room = llord->rooms; room; room = room->next) if (room->vnum == vnum) return room; return NULL; } /* Save all objects in a house */ void House_crashsave(vnum_t vnum) { int rnum; char buf[MAX_STRING_LENGTH]; FILE *fp; if ((rnum = real_room(vnum)) == NOWHERE) return; if (!House_get_filename(vnum, buf)) return; if (!(fp = fopen(buf, "wb"))) { perror("SYSERR: Error saving house file"); return; } House_save(find_house(vnum), fp, 0); fclose(fp); } /* Delete a house save file */ void House_delete_file(vnum_t vnum) { char buf[MAX_INPUT_LENGTH], fname[MAX_INPUT_LENGTH]; FILE *fl; if (!House_get_filename(vnum, fname)) return; if (!(fl = fopen(fname, "rb"))) { if (errno != ENOENT) { sprintf(buf, "SYSERR: Error deleting house file #%ld. (1)", vnum); perror(buf); } return; } fclose(fl); if (unlink(fname) < 0) { sprintf(buf, "SYSERR: Error deleting house file #%ld. (2)", vnum); perror(buf); } } /****************************************************************** * Functions for house administration (creation, deletion, etc. * *****************************************************************/ /* Save the house control information */ void House_save_control(void) { struct house_control_rec *room; struct landlord *llord = landlords; int i = 0; FILE *fl; if (!(fl = fopen(HCONTROL_FILE, "w+b"))) { perror("SYSERR: Unable to open house control file"); return; } for (;llord; llord = llord->next) i++; fprintf(fl, "%d\n", i); for (llord = landlords; llord; llord = llord->next) { fprintf(fl, "%ld %s %d %d\n", llord->vnum, llord->race.ToString(), llord->basecost, llord->num_room); for (room = llord->rooms; room; room = room->next) fprintf(fl, "%ld %ld %d %d %s %ld 0 %ld\n", room->vnum, room->key, room->exit_num, room->mode, room->name, room->owner, room->owner ? room->date : 0); } fclose(fl); } struct house_control_rec *find_room(char *arg, struct house_control_rec *room, struct char_data *recep) { for (; room; room = room->next) if (isname(arg, room->name)) return room; do_say(recep, "Which room is that?", 0, 0); return NULL; } SPECIAL(landlord_spec) { struct char_data *recep = (struct char_data *) me; struct landlord *lord; struct house_control_rec *room; char buf3[MAX_STRING_LENGTH]; int i = 0; if (!(CMD_IS("list") || CMD_IS("retrieve") || CMD_IS("lease") || CMD_IS("leave") || CMD_IS("pay") || CMD_IS("status"))) return FALSE; if (!CAN_SEE(recep, ch)) { do_say(recep, "I don't deal with people I can't see!", 0, 0); return TRUE; } for (lord = landlords; lord; lord = lord->next) if (GET_MOB_VNUM(recep) == lord->vnum) break; if (!lord) return FALSE; one_argument(argument, arg); if (CMD_IS("list")) { send_to_char(ch, "The following rooms are free: \r\n"); send_to_char(ch, "Name Class Price Name Class Price\r\n"); send_to_char(ch, "----- ------ ------ ----- ------ -----\r\n"); for (room = lord->rooms; room; room = room->next) if (!room->owner) { if (!i) { sprintf(buf, "%-5s %-6s %-8d", room->name, lifestyle[room->mode].name, lord->basecost * lifestyle[room->mode].cost); i++; } else { sprintf(buf2, " %-5s %-6s %-8d\r\n", room->name, lifestyle[room->mode].name, lord->basecost * lifestyle[room->mode].cost); strcat(buf, buf2); send_to_char(ch, buf); i = 0; } } if (i) strcat(buf, "\r\n\n"); else sprintf(buf, "\r\n"); send_to_char(ch, buf); return TRUE; } else if (CMD_IS("retrieve")) { if (!*arg) { do_say(recep, "Which room would you like the key to?", 0, 0); return TRUE; } room = find_room(arg, lord->rooms, recep); if (!room) do_say(recep, "Which room is that?", 0, 0); else if (room->owner != GET_IDNUM(ch)) do_say(recep, "I would get fired if I did that.", 0, 0); else { do_say(recep, "Here you go...", 0, 0); struct obj_data *key = read_object(room->key, VIRTUAL); obj_to_char(key, ch); } return TRUE; } else if (CMD_IS("lease")) { if (!*arg) { do_say(recep, "Which room would you like to lease?", 0, 0); return TRUE; } room = find_room(arg, lord->rooms, recep); if (!room) { do_say(recep, "Which room is that?", 0, 0); return TRUE; } else if (room->owner) { do_say(recep, "Sorry, I'm afraid that room is already taken.", 0, 0); return TRUE; } int cost = lord->basecost * lifestyle[room->mode].cost; sprintf(buf3, "That will be %d nuyen.", cost); do_say(recep, buf3, 0, 0); if (GET_NUYEN(ch) < cost) { if (GET_BANK(ch) >= cost) { do_say(recep, "You don't have the money on you so I'll transfer it from your bank account.", 0, 0); GET_BANK(ch) -= cost; } else { do_say(recep, "Sorry, you don't have the required funds.", 0, 0); return TRUE; } } else { GET_NUYEN(ch) -= cost; send_to_char(ch, "You hand over the cash.\r\n"); } do_say(recep, "Thank you, here is your key.", 0, 0); struct obj_data *key = read_object(room->key, VIRTUAL); obj_to_char(key, ch); room->owner = GET_IDNUM(ch); room->date = time(0) + (SECS_PER_REAL_DAY*30); ROOM_FLAGS(real_room(room->vnum)).SetBit(ROOM_HOUSE); ROOM_FLAGS(room->atrium).SetBit(ROOM_ATRIUM); House_crashsave(room->vnum); House_save_control(); return TRUE; } else if (CMD_IS("leave")) { if (!*arg) { do_say(recep, "Which room would you like to leave?", 0, 0); return TRUE; } room = find_room(arg, lord->rooms, recep); if (!room) do_say(recep, "Which room is that?", 0, 0); else if (room->owner != GET_IDNUM(ch)) do_say(recep, "I would get fired if I did that.", 0, 0); else { room->owner = 0; ROOM_FLAGS(real_room(room->vnum)).RemoveBit(ROOM_HOUSE); ROOM_FLAGS(room->atrium).RemoveBit(ROOM_ATRIUM); House_save_control(); House_delete_file(room->vnum); do_say(recep, "I hope you enjoyed your time here.", 0, 0); } return TRUE; } else if (CMD_IS("pay")) { if (!*arg) { do_say(recep, "Which room would you like to pay the rent to?", 0, 0); return TRUE; } room = find_room(arg, lord->rooms, recep); if (!room) do_say(recep, "Which room is that?", 0, 0); else if (room->owner != GET_IDNUM(ch)) do_say(recep, "But that isn't your room.", 0, 0); else { int cost = lord->basecost * lifestyle[room->mode].cost; sprintf(buf3, "That will be %d nuyen.", cost); do_say(recep, buf3, 0, 0); if (GET_NUYEN(ch) < cost) { if (GET_BANK(ch) >= cost) { do_say(recep, "You don't have the money on you so I'll transfer it from your bank account.", 0, 0); GET_BANK(ch) -= cost; } else { do_say(recep, "Sorry, you don't have the required funds.", 0, 0); return TRUE; } } else { GET_NUYEN(ch) -= cost; send_to_char(ch, "You hand over the cash.\r\n"); } do_say(recep, "You are payed up for the next period.", 0, 0); room->date += SECS_PER_REAL_DAY*30; House_save_control(); } return TRUE; } else if (CMD_IS("status")) { if (!*arg) { do_say(recep, "Which room would you like to check the rent on?", 0, 0); return TRUE; } room = find_room(arg, lord->rooms, recep); if (!room) do_say(recep, "Which room is that?", 0, 0); else if (room->owner != GET_IDNUM(ch)) do_say(recep, "I'm afraid I can't release that information.", 0, 0); else { sprintf(buf2, "You are payed for another %d days.", (int)((room->date - time(0)) / 86400)); do_say(recep, buf2, 0, 0); } return TRUE; } return FALSE; } /* call from boot_db - will load control recs, load objs, set atrium bits */ /* should do sanity checks on vnums & remove invalid records */ void House_boot(void) { vnum_t real_house, owner, land, payed; FILE *fl; int num_land; char line[256], name[20]; int t[4]; struct house_control_rec *temp; struct landlord *templ = NULL, *lastl = NULL; if (!(fl = fopen(HCONTROL_FILE, "r+b"))) { log("House control file does not exist."); return; } if (!get_line(fl, line) || sscanf(line, "%d", &num_land) != 1) { log("Error at beginning of house control file."); return; } for (int i = 0; i < num_land; i++) { get_line(fl, line); if (sscanf(line, "%ld %s %d %d", &land, name, t, t + 1) != 4) { fprintf(stderr, "Format error in landlord #%d.\r\n", i); return; } ASSIGNMOB(land, landlord_spec); templ = new struct landlord; templ->vnum = land; templ->race.FromString(name); templ->basecost = t[0]; templ->num_room = t[1]; struct house_control_rec *last = NULL, *first = NULL; for (int x = 0; x < templ->num_room; x++) { get_line(fl, line); if (sscanf(line, "%ld %ld %d %d %s %ld %d %ld", &real_house, &land, t, t + 1, name, &owner, t + 2, &payed) != 8) { fprintf(stderr, "Format error in landlord #%d room #%d.\r\n", i, x); return; } temp = new struct house_control_rec; temp->vnum = real_house; temp->key = land; temp->atrium = TOROOM(real_room(real_house), t[0]); temp->exit_num = t[0]; temp->owner = owner; temp->date = payed; temp->mode = t[1]; temp->name = str_dup(name); for (int q = 0; q < MAX_GUESTS; q++) temp->guests[q] = 0; if (temp->owner) { if (playerDB.DoesExist(temp->owner) && temp->date > time(0)) House_load(temp); else { temp->owner = 0; House_delete_file(temp->vnum); } } if (last) last->next = temp; if (!first) first = temp; if (temp->owner) { ROOM_FLAGS(real_room(temp->vnum)).SetBit(ROOM_HOUSE); ROOM_FLAGS(temp->atrium).SetBit(ROOM_ATRIUM); } last = temp; } templ->rooms = first; templ->next = lastl; lastl = templ; } landlords = templ; fclose(fl); House_save_control(); } /* "House Control" functions */ char *HCONTROL_FORMAT = "Usage: hcontrol destroy <house vnum>\r\n" " hcontrol show\r\n"; static inline const char *NAME(DBIndex::vnum_t id) { const char *name = playerDB.GetNameV(id); return (name? name : "<UNDEF>"); } void hcontrol_list_houses(struct char_data *ch) { char *own_name; strcpy(buf, "Address Atrium Guests Owner\r\n"); strcat(buf, "------- ------ ------ ------------\r\n"); send_to_char(buf, ch); for (struct landlord *llord = landlords; llord; llord = llord->next) for (struct house_control_rec *house = llord->rooms; house; house = house->next) if (house->owner) { own_name = str_dup(playerDB.GetNameV(house->owner)); if (!own_name) own_name = str_dup("<UNDEF>"); sprintf(buf, "%7ld %7ld 0 %-12s\r\n", house->vnum, world[house->atrium].number, CAP(own_name)); send_to_char(buf, ch); } } void hcontrol_destroy_house(struct char_data * ch, char *arg) { struct house_control_rec *i = NULL; vnum_t real_atrium, real_house; if (!*arg) { send_to_char(HCONTROL_FORMAT, ch); return; } if ((i = find_house(atoi(arg))) == NULL) { send_to_char("Unknown house.\r\n", ch); return; } if ((real_atrium = real_room(i->atrium)) < 0) log("SYSERR: House had invalid atrium!"); else ROOM_FLAGS(real_atrium).RemoveBit(ROOM_ATRIUM); if ((real_house = real_room(i->vnum)) < 0) log("SYSERR: House had invalid vnum!"); else { ROOM_FLAGS(real_house).RemoveBit(ROOM_HOUSE); House_delete_file(i->vnum); i->owner = 0; send_to_char("House deleted.\r\n", ch); House_save_control(); for (struct landlord *llord = landlords; llord; llord = llord->next) for (i = llord->rooms; i; i = i->next) if ((real_atrium = real_room(i->atrium)) >= 0) { ROOM_FLAGS(real_atrium).SetBit(ROOM_ATRIUM); } } } /* The hcontrol command itself, used by imms to create/destroy houses */ ACMD(do_hcontrol) { char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH]; half_chop(argument, arg1, arg2); if (is_abbrev(arg1, "destroy")) hcontrol_destroy_house(ch, arg2); else if (is_abbrev(arg1, "show")) hcontrol_list_houses(ch); else send_to_char(HCONTROL_FORMAT, ch); } ACMD(do_decorate) { extern void write_world_to_disk(int vnum); struct house_control_rec *i; if (!ROOM_FLAGGED(ch->in_room, ROOM_HOUSE)) send_to_char("You must be in your house to set the description.\r\n", ch); else if ((i = find_house(GET_ROOM_VNUM(IN_ROOM(ch)))) == NULL) send_to_char("Um.. this house seems to be screwed up.\r\n", ch); else if (GET_IDNUM(ch) != i->owner) send_to_char("Only the primary owner can set the room description.\r\n", ch); else { PLR_FLAGS(ch).SetBit(PLR_WRITING); send_to_char("Enter your new room description. Terminate with a @ on a new line.\r\n", ch); act("$n starts to move things around the room.", TRUE, ch, 0, 0, TO_ROOM); STATE(ch->desc) = CON_DECORATE; ch->desc->str = new (char *); *ch->desc->str = NULL; ch->desc->max_str = MAX_MESSAGE_LENGTH; ch->desc->mail_to = 0; } } /* The house command, used by mortal house owners to assign guests */ ACMD(do_house) { struct house_control_rec *i = NULL; int j, id; one_argument(argument, arg); if (!ROOM_FLAGGED(IN_ROOM(ch), ROOM_HOUSE)) send_to_char("You must be in your house to set guests.\r\n", ch); else if ((i = find_house(GET_ROOM_VNUM(IN_ROOM(ch)))) == NULL) send_to_char("Um.. this house seems to be screwed up.\r\n", ch); else if (GET_IDNUM(ch) != i->owner) send_to_char("Only the primary owner can set guests.\r\n", ch); else if (!*arg) House_list_guests(ch, i, FALSE); else if ((id = playerDB.GetID(arg)) < 0) send_to_char("No such player.\r\n", ch); else if (id == GET_IDNUM(ch)) send_to_char("It's your house!\r\n", ch); else { for (j = 0; j < MAX_GUESTS; j++) if (i->guests[j] == id) { i->guests[j] = 0; House_crashsave(i->vnum); send_to_char("Guest deleted.\r\n", ch); return; } for (j = 0; j < MAX_GUESTS; j++) if (i->guests[j] == 0) break; if (j == MAX_GUESTS) { send_to_char("You have too many guests already.\r\n", ch); return; } i->guests[j] = id; House_crashsave(i->vnum); send_to_char("Guest added.\r\n", ch); } } /* Misc. administrative functions */ /* crash-save all the houses */ void House_save_all(void) { struct landlord *llord; struct house_control_rec *room; rnum_t real_house; for (llord = landlords; llord; llord = llord->next) for (room = llord->rooms; room; room = room->next) if ((real_house = real_room(room->vnum)) != NOWHERE) if (room->owner) House_crashsave(room->vnum); FILE *fl; for (int x = 0; x <= top_of_world; x++) if (ROOM_FLAGGED(x, ROOM_STORAGE)) { sprintf(buf, "storage/%ld", world[x].number); if (!(fl = fopen(buf, "wb"))) { sprintf(buf2, "Cannot open %s. Aborting Storage Saving.", buf); mudlog(buf2, NULL, LOG_WIZLOG, TRUE); return; } House_save(NULL, fl, x); fclose(fl); } } /* note: arg passed must be house vnum, so there. */ bool House_can_enter(struct char_data *ch, vnum_t house) { int j; struct house_control_rec *room = find_house(house); if (IS_NPC(ch) || !room) return FALSE; if (GET_LEVEL(ch) >= LVL_ADMIN) return TRUE; if (GET_IDNUM(ch) == room->owner) return TRUE; for (j = 0; j < MAX_GUESTS; j++) if (GET_IDNUM(ch) == room->guests[j]) return TRUE; return FALSE; } void House_list_guests(struct char_data *ch, struct house_control_rec *i, int quiet) { int j; char buf[MAX_STRING_LENGTH], buf2[MAX_NAME_LENGTH + 2]; strcpy(buf, " Guests: "); int x = 0; for (j = 0; j < MAX_GUESTS; j++) { const char *temp = playerDB.GetNameV(i->guests[j]); if (!temp || i->guests[j] == 0) continue; sprintf(buf2, "%s, ", temp); strcat(buf, CAP(buf2)); x++; } if (!x) strcat(buf, "None"); strcat(buf, "\r\n"); send_to_char(buf, ch); }