diff --git a/lib/world/obj/1.obj b/lib/world/obj/1.obj index 046430c..73d32e2 100644 --- a/lib/world/obj/1.obj +++ b/lib/world/obj/1.obj @@ -261,6 +261,6 @@ A long, wooden bar takes up most of the south side of the room.~ It's a long wooden bar! ~ 6 0 0 0 0 0 0 0 0 0 0 0 0 -100 8 0 0 +8 0 0 0 200 0 0 0 0 $~ diff --git a/src/act.informative.c b/src/act.informative.c index c6bc624..b5389cb 100644 --- a/src/act.informative.c +++ b/src/act.informative.c @@ -84,6 +84,7 @@ static void show_obj_to_char(struct obj_data *obj, struct char_data *ch, int mod } } + switch (mode) { case SHOW_OBJ_LONG: /* Hide objects starting with . from non-holylighted people. - Elaseth */ @@ -121,6 +122,34 @@ static void show_obj_to_char(struct obj_data *obj, struct char_data *ch, int mod if (GET_OBJ_MAIN(obj) && *GET_OBJ_MAIN(obj)) { /* Use the pager so multi-line M-Descs display nicely. */ page_string(ch->desc, GET_OBJ_MAIN(obj), TRUE); + + /* For furniture, also show items on it after the main description */ + if (GET_OBJ_TYPE(obj) == ITEM_FURNITURE) { + /* Show seat availability */ + if (GET_OBJ_VAL(obj, 0) > 0) { + int current_occupants = GET_OBJ_VAL(obj, 1); + int max_occupants = GET_OBJ_VAL(obj, 0); + int available_seats = max_occupants - current_occupants; + + if (available_seats > 0) { + send_to_char(ch, "\r\n%s(%d seat%s available)%s", + CCYEL(ch, C_NRM), available_seats, + available_seats == 1 ? "" : "s", + CCNRM(ch, C_NRM)); + } else { + send_to_char(ch, "\r\n%s(no seats available)%s", + CCRED(ch, C_NRM), CCNRM(ch, C_NRM)); + } + } + + /* Show items on furniture */ + if (obj->contains) { + send_to_char(ch, "\r\nOn %s you see:\r\n", obj->short_description); + list_obj_to_char(obj->contains, ch, SHOW_OBJ_SHORT, TRUE); + } else { + send_to_char(ch, "\r\nYou see nothing on %s.", obj->short_description); + } + } return; } @@ -140,6 +169,33 @@ static void show_obj_to_char(struct obj_data *obj, struct char_data *ch, int mod send_to_char(ch, "It looks like a drink container."); break; + case ITEM_FURNITURE: + /* Show seat availability */ + if (GET_OBJ_VAL(obj, 0) > 0) { + int current_occupants = GET_OBJ_VAL(obj, 1); + int max_occupants = GET_OBJ_VAL(obj, 0); + int available_seats = max_occupants - current_occupants; + + if (available_seats > 0) { + send_to_char(ch, "%s(%d seat%s available)%s ", + CCYEL(ch, C_NRM), available_seats, + available_seats == 1 ? "" : "s", + CCNRM(ch, C_NRM)); + } else { + send_to_char(ch, "%s(no seats available)%s ", + CCRED(ch, C_NRM), CCNRM(ch, C_NRM)); + } + } + + /* Show items on furniture */ + if (obj->contains) { + send_to_char(ch, "On %s you see:\r\n", obj->short_description); + list_obj_to_char(obj->contains, ch, SHOW_OBJ_SHORT, TRUE); + } else { + send_to_char(ch, "You see nothing on %s.", obj->short_description); + } + break; + default: /* Optional: friendlier fallback that names the item. */ if (obj->short_description && *obj->short_description) diff --git a/src/act.item.c b/src/act.item.c index ec530b0..4491944 100644 --- a/src/act.item.c +++ b/src/act.item.c @@ -81,12 +81,37 @@ static void perform_put(struct char_data *ch, struct obj_data *obj, struct obj_d } } +/* Put an item on furniture (like a table, bar, etc.) */ +static void perform_put_on_furniture(struct char_data *ch, struct obj_data *obj, struct obj_data *furniture) +{ + long object_id = obj_script_id(obj); + + if (!drop_otrigger(obj, ch)) + return; + + if (!has_obj_by_uid_in_lookup_table(object_id)) /* object might be extracted by drop_otrigger */ + return; + + if (OBJ_FLAGGED(obj, ITEM_NODROP) && IN_ROOM(furniture) != NOWHERE) + act("You can't get $p out of your hand.", FALSE, ch, obj, NULL, TO_CHAR); + else { + obj_from_char(obj); + obj_to_obj(obj, furniture); + + act("$n puts $p on $P.", TRUE, ch, obj, furniture, TO_ROOM); + act("You put $p on $P.", FALSE, ch, obj, furniture, TO_CHAR); + } +} + /* The following put modes are supported: 1) put 2) put all. 3) put all - The must be in inventory, worn/equipped, or on ground. All objects to be put - into container must be in inventory. */ + 4) put on + 5) put all. on + 6) put all on + The or must be in inventory, worn/equipped, or on ground. + All objects to be put into container or on furniture must be in inventory. */ ACMD(do_put) { char arg1[MAX_INPUT_LENGTH]; @@ -120,42 +145,74 @@ ACMD(do_put) generic_find(thecont, FIND_OBJ_INV | FIND_OBJ_ROOM | FIND_OBJ_EQUIP, ch, &tmp_char, &cont); if (!cont) send_to_char(ch, "You don't see %s %s here.\r\n", AN(thecont), thecont); - else if (GET_OBJ_TYPE(cont) != ITEM_CONTAINER) - act("$p is not a container.", FALSE, ch, cont, 0, TO_CHAR); - else if (OBJVAL_FLAGGED(cont, CONT_CLOSED) && (GET_LEVEL(ch) < LVL_IMMORT || !PRF_FLAGGED(ch, PRF_NOHASSLE))) - send_to_char(ch, "You'd better open it first!\r\n"); - else { - if (obj_dotmode == FIND_INDIV) { /* put */ - if (!(obj = get_obj_in_list_vis(ch, theobj, NULL, ch->carrying))) - send_to_char(ch, "You aren't carrying %s %s.\r\n", AN(theobj), theobj); - else if (obj == cont && howmany == 1) - send_to_char(ch, "You attempt to fold it into itself, but fail.\r\n"); - else { - while (obj && howmany) { - next_obj = obj->next_content; + else if (GET_OBJ_TYPE(cont) == ITEM_CONTAINER) { + /* Handle container logic */ + if (OBJVAL_FLAGGED(cont, CONT_CLOSED) && (GET_LEVEL(ch) < LVL_IMMORT || !PRF_FLAGGED(ch, PRF_NOHASSLE))) + send_to_char(ch, "You'd better open it first!\r\n"); + else { + if (obj_dotmode == FIND_INDIV) { /* put */ + if (!(obj = get_obj_in_list_vis(ch, theobj, NULL, ch->carrying))) + send_to_char(ch, "You aren't carrying %s %s.\r\n", AN(theobj), theobj); + else if (obj == cont && howmany == 1) + send_to_char(ch, "You attempt to fold it into itself, but fail.\r\n"); + else { + while (obj && howmany) { + next_obj = obj->next_content; + if (obj != cont) { + howmany--; + perform_put(ch, obj, cont); + } + obj = get_obj_in_list_vis(ch, theobj, NULL, next_obj); + } + } + } else { + for (obj = ch->carrying; obj; obj = next_obj) { + next_obj = obj->next_content; + if (obj != cont && CAN_SEE_OBJ(ch, obj) && + (obj_dotmode == FIND_ALL || isname(theobj, obj->name))) { + found = 1; + perform_put(ch, obj, cont); + } + } + if (!found) + send_to_char(ch, "You don't seem to have %s %s.\r\n", + obj_dotmode == FIND_ALL ? "any" : "any", + obj_dotmode == FIND_ALL ? "items" : theobj); + } + } + } else if (GET_OBJ_TYPE(cont) == ITEM_FURNITURE) { + /* Handle furniture logic - put items ON furniture */ + if (obj_dotmode == FIND_INDIV) { /* put on */ + if (!(obj = get_obj_in_list_vis(ch, theobj, NULL, ch->carrying))) + send_to_char(ch, "You aren't carrying %s %s.\r\n", AN(theobj), theobj); + else if (obj == cont && howmany == 1) + send_to_char(ch, "You can't put something on itself.\r\n"); + else { + while (obj && howmany) { + next_obj = obj->next_content; if (obj != cont) { howmany--; - perform_put(ch, obj, cont); + perform_put_on_furniture(ch, obj, cont); } - obj = get_obj_in_list_vis(ch, theobj, NULL, next_obj); - } - } + obj = get_obj_in_list_vis(ch, theobj, NULL, next_obj); + } + } } else { - for (obj = ch->carrying; obj; obj = next_obj) { - next_obj = obj->next_content; - if (obj != cont && CAN_SEE_OBJ(ch, obj) && - (obj_dotmode == FIND_ALL || isname(theobj, obj->name))) { - found = 1; - perform_put(ch, obj, cont); - } - } - if (!found) { - if (obj_dotmode == FIND_ALL) - send_to_char(ch, "You don't seem to have anything to put in it.\r\n"); - else - send_to_char(ch, "You don't seem to have any %ss.\r\n", theobj); - } + for (obj = ch->carrying; obj; obj = next_obj) { + next_obj = obj->next_content; + if (obj != cont && CAN_SEE_OBJ(ch, obj) && + (obj_dotmode == FIND_ALL || isname(theobj, obj->name))) { + found = 1; + perform_put_on_furniture(ch, obj, cont); + } + } + if (!found) + send_to_char(ch, "You don't seem to have %s %s.\r\n", + obj_dotmode == FIND_ALL ? "any" : "any", + obj_dotmode == FIND_ALL ? "items" : theobj); } + } else { + act("$p is not a container or furniture.", FALSE, ch, cont, 0, TO_CHAR); } } } @@ -356,8 +413,8 @@ ACMD(do_get) mode = generic_find(arg2, FIND_OBJ_INV | FIND_OBJ_ROOM | FIND_OBJ_EQUIP, ch, &tmp_char, &cont); if (!cont) send_to_char(ch, "You don't have %s %s.\r\n", AN(arg2), arg2); - else if (GET_OBJ_TYPE(cont) != ITEM_CONTAINER) - act("$p is not a container.", FALSE, ch, cont, 0, TO_CHAR); + else if (GET_OBJ_TYPE(cont) != ITEM_CONTAINER && GET_OBJ_TYPE(cont) != ITEM_FURNITURE) + act("$p is not a container or furniture.", FALSE, ch, cont, 0, TO_CHAR); else get_from_container(ch, cont, arg1, mode, amount); } else { @@ -367,12 +424,12 @@ ACMD(do_get) } for (cont = ch->carrying; cont; cont = cont->next_content) if (CAN_SEE_OBJ(ch, cont) && (cont_dotmode == FIND_ALL || isname(arg2, cont->name))) { - if (GET_OBJ_TYPE(cont) == ITEM_CONTAINER) { + if (GET_OBJ_TYPE(cont) == ITEM_CONTAINER || GET_OBJ_TYPE(cont) == ITEM_FURNITURE) { found = 1; get_from_container(ch, cont, arg1, FIND_OBJ_INV, amount); } else if (cont_dotmode == FIND_ALLDOT) { found = 1; - act("$p is not a container.", FALSE, ch, cont, 0, TO_CHAR); + act("$p is not a container or furniture.", FALSE, ch, cont, 0, TO_CHAR); } } { @@ -385,12 +442,12 @@ ACMD(do_get) if (CAN_SEE_OBJ(ch, eq) && (cont_dotmode == FIND_ALL || isname(arg2, eq->name))) { - if (GET_OBJ_TYPE(eq) == ITEM_CONTAINER) { + if (GET_OBJ_TYPE(eq) == ITEM_CONTAINER || GET_OBJ_TYPE(eq) == ITEM_FURNITURE) { found = 1; get_from_container(ch, eq, arg1, FIND_OBJ_EQUIP, amount); } else if (cont_dotmode == FIND_ALLDOT) { found = 1; - act("$p is not a container.", FALSE, ch, eq, 0, TO_CHAR); + act("$p is not a container or furniture.", FALSE, ch, eq, 0, TO_CHAR); } } } @@ -398,17 +455,17 @@ ACMD(do_get) for (cont = world[IN_ROOM(ch)].contents; cont; cont = cont->next_content) if (CAN_SEE_OBJ(ch, cont) && (cont_dotmode == FIND_ALL || isname(arg2, cont->name))) { - if (GET_OBJ_TYPE(cont) == ITEM_CONTAINER) { + if (GET_OBJ_TYPE(cont) == ITEM_CONTAINER || GET_OBJ_TYPE(cont) == ITEM_FURNITURE) { get_from_container(ch, cont, arg1, FIND_OBJ_ROOM, amount); found = 1; } else if (cont_dotmode == FIND_ALLDOT) { - act("$p is not a container.", FALSE, ch, cont, 0, TO_CHAR); + act("$p is not a container or furniture.", FALSE, ch, cont, 0, TO_CHAR); found = 1; } } if (!found) { if (cont_dotmode == FIND_ALL) - send_to_char(ch, "You can't seem to find any containers.\r\n"); + send_to_char(ch, "You can't seem to find any containers or furniture.\r\n"); else send_to_char(ch, "You can't seem to find any %ss here.\r\n", arg2); } diff --git a/src/act.movement.c b/src/act.movement.c index 9afd977..7817002 100644 --- a/src/act.movement.c +++ b/src/act.movement.c @@ -803,6 +803,13 @@ ACMD(do_sit) act("There is no where left to sit upon $p.", TRUE, ch, furniture, 0, TO_CHAR); return; } else { + /* Check if furniture allows sitting position */ + int allowed_positions = GET_OBJ_VAL(furniture, 2); /* VAL_FURN_POSITIONS */ + if (allowed_positions > 0 && !(allowed_positions & (1 << 1))) { /* Check SIT bit (bit 1) */ + act("$p doesn't look comfortable for sitting.", TRUE, ch, furniture, 0, TO_CHAR); + return; + } + if (OBJ_SAT_IN_BY(furniture) == NULL) OBJ_SAT_IN_BY(furniture) = ch; for (tempch = OBJ_SAT_IN_BY(furniture); tempch != ch ; tempch = NEXT_SITTING(tempch)) { @@ -850,6 +857,14 @@ ACMD(do_rest) GET_POS(ch) = POS_RESTING; break; case POS_SITTING: + /* Check if sitting on furniture that allows resting */ + if (SITTING(ch) && GET_OBJ_TYPE(SITTING(ch)) == ITEM_FURNITURE) { + int allowed_positions = GET_OBJ_VAL(SITTING(ch), 2); /* VAL_FURN_POSITIONS */ + if (allowed_positions > 0 && !(allowed_positions & (1 << 2))) { /* Check REST bit (bit 2) */ + act("$p doesn't look comfortable for resting.", TRUE, ch, SITTING(ch), 0, TO_CHAR); + return; + } + } send_to_char(ch, "You rest your tired bones.\r\n"); act("$n rests.", TRUE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_RESTING; @@ -877,6 +892,14 @@ ACMD(do_sleep) case POS_STANDING: case POS_SITTING: case POS_RESTING: + /* Check if sitting/resting on furniture that allows sleeping */ + if (SITTING(ch) && GET_OBJ_TYPE(SITTING(ch)) == ITEM_FURNITURE) { + int allowed_positions = GET_OBJ_VAL(SITTING(ch), 2); /* VAL_FURN_POSITIONS */ + if (allowed_positions > 0 && !(allowed_positions & (1 << 3))) { /* Check SLEEP bit (bit 3) */ + act("$p doesn't look comfortable for sleeping.", TRUE, ch, SITTING(ch), 0, TO_CHAR); + return; + } + } send_to_char(ch, "You go to sleep.\r\n"); act("$n lies down and falls asleep.", TRUE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_SLEEPING; diff --git a/src/constants.c b/src/constants.c index 2f36e37..5439ac8 100644 --- a/src/constants.c +++ b/src/constants.c @@ -119,7 +119,6 @@ const char *furniture_position_bits[] = { "SIT", /* can sit on it */ "REST", /* can rest on it */ "SLEEP", /* can sleep on it */ - "KNEEL", /* can kneel at/on it */ "\n" }; diff --git a/src/oedit.c b/src/oedit.c index a48c4be..6f88f3d 100644 --- a/src/oedit.c +++ b/src/oedit.c @@ -102,8 +102,8 @@ static const char *money_val_labels[NUM_OBJ_VAL_POSITIONS] = { /* Furniture */ static const char *furniture_val_labels[NUM_OBJ_VAL_POSITIONS] = { - "capacity", "max_people", "allowed_pos", "furn_flags", - "comfy_bonus", "climb_dc", "script_hook", "reserved" + "max_seats", "current_occupants", "allowed_pos", "Value[3]", + "Value[4]", "Value[5]", "Value[6]", "Value[7]" }; /* Generic fallback */ @@ -525,8 +525,14 @@ static void oedit_disp_values_menu(struct descriptor_data *d) write_to_output(d, "\r\n-- Object Values Menu --\r\n"); for (i = 0; i < NUM_OBJ_VAL_POSITIONS; i++) { - write_to_output(d, "%2d) %-12s : %d\r\n", - i+1, labels[i], GET_OBJ_VAL(obj, i)); + /* Hide current_occupants field for furniture - it's managed by the game engine */ + if (GET_OBJ_TYPE(obj) == ITEM_FURNITURE && i == 1) { + write_to_output(d, "%2d) %-12s : %d (managed by game engine)\r\n", + i+1, labels[i], GET_OBJ_VAL(obj, i)); + } else { + write_to_output(d, "%2d) %-12s : %d\r\n", + i+1, labels[i], GET_OBJ_VAL(obj, i)); + } } write_to_output(d, "Q) Quit to main menu\r\nEnter choice : "); @@ -1008,6 +1014,13 @@ void oedit_parse(struct descriptor_data *d, char *arg) } else { int i = atoi(arg) - 1; if (i >= 0 && i < NUM_OBJ_VAL_POSITIONS) { + /* Prevent editing current_occupants for furniture - it's managed by the game engine */ + if (GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_FURNITURE && i == 1) { + write_to_output(d, "The current_occupants field is managed by the game engine and cannot be edited manually.\r\n"); + oedit_disp_values_menu(d); + return; + } + OLC_VAL_SLOT(d) = i; const char **labels = get_val_labels(OLC_OBJ(d)); diff --git a/src/structs.h b/src/structs.h index 0eb7862..64b83fc 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1336,14 +1336,9 @@ struct mob_loadout *loadout_deep_copy(const struct mob_loadout *src); /* Furniture defines for object values */ /* Furniture object values (obj_flags.value[x]) */ -#define VAL_FURN_CAPACITY 0 /* how much it can hold / support */ -#define VAL_FURN_MAX_OCC 1 /* max occupants */ -#define VAL_FURN_POSITIONS 2 /* allowed positions (bitvector) */ -#define VAL_FURN_FLAGS 3 /* behavior flags (bitvector) */ -#define VAL_FURN_COMFORT 4 /* comfort/healing bonus */ -#define VAL_FURN_ENTRY_DC 5 /* difficulty to climb onto */ -#define VAL_FURN_SCRIPT 6 /* script hook */ -#define VAL_FURN_RESERVED 7 /* reserved */ +#define VAL_FURN_CAPACITY 0 /* maximum number of people who can use furniture */ +#define VAL_FURN_MAX_OCC 1 /* current number of people using furniture (runtime only, managed by game engine) */ +#define VAL_FURN_POSITIONS 2 /* allowed positions bitvector: bit 0=STAND(1), bit 1=SIT(2), bit 2=REST(4), bit 3=SLEEP(8) */ /* Config structs */