mirror of
https://github.com/tbamud/tbamud.git
synced 2026-03-19 02:36:33 +01:00
1816 lines
60 KiB
C
1816 lines
60 KiB
C
/**************************************************************************
|
|
* File: act.item.c Part of tbaMUD *
|
|
* Usage: Object handling routines -- get/drop and container handling. *
|
|
* *
|
|
* All rights reserved. See license for complete information. *
|
|
* *
|
|
* Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
|
|
* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. *
|
|
**************************************************************************/
|
|
|
|
#include "conf.h"
|
|
#include "sysdep.h"
|
|
#include "structs.h"
|
|
#include "utils.h"
|
|
#include "comm.h"
|
|
#include "interpreter.h"
|
|
#include "handler.h"
|
|
#include "db.h"
|
|
#include "spells.h"
|
|
#include "constants.h"
|
|
#include "dg_scripts.h"
|
|
#include "oasis.h"
|
|
#include "act.h"
|
|
#include "quest.h"
|
|
|
|
|
|
/* local function prototypes */
|
|
/* do_get utility functions */
|
|
static int can_take_obj(struct char_data *ch, struct obj_data *obj);
|
|
static void get_check_money(struct char_data *ch, struct obj_data *obj);
|
|
static void get_from_container(struct char_data *ch, struct obj_data *cont, char *arg, int mode, int amount);
|
|
static void get_from_room(struct char_data *ch, char *arg, int amount);
|
|
static void perform_get_from_container(struct char_data *ch, struct obj_data *obj, struct obj_data *cont, int mode);
|
|
static int perform_get_from_room(struct char_data *ch, struct obj_data *obj);
|
|
/* do_give utility functions */
|
|
static struct char_data *give_find_vict(struct char_data *ch, char *arg);
|
|
static void perform_give(struct char_data *ch, struct char_data *vict, struct obj_data *obj);
|
|
static void perform_give_gold(struct char_data *ch, struct char_data *vict, int amount);
|
|
/* do_drop utility functions */
|
|
static int perform_drop(struct char_data *ch, struct obj_data *obj, byte mode, const char *sname, room_rnum RDR);
|
|
static void perform_drop_gold(struct char_data *ch, int amount, byte mode, room_rnum RDR);
|
|
/* do_put utility functions */
|
|
static void perform_put(struct char_data *ch, struct obj_data *obj, struct obj_data *cont);
|
|
/* do_remove utility functions */
|
|
static void perform_remove(struct char_data *ch, int pos);
|
|
/* do_wear utility functions */
|
|
static void perform_wear(struct char_data *ch, struct obj_data *obj, int where);
|
|
static void wear_message(struct char_data *ch, struct obj_data *obj, int where);
|
|
|
|
static void perform_put(struct char_data *ch, struct obj_data *obj, struct obj_data *cont)
|
|
{
|
|
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 ((GET_OBJ_VAL(cont, 0) > 0) &&
|
|
(GET_OBJ_WEIGHT(cont) + GET_OBJ_WEIGHT(obj) > GET_OBJ_VAL(cont, 0)))
|
|
act("$p won't fit in $P.", FALSE, ch, obj, cont, TO_CHAR);
|
|
else if (OBJ_FLAGGED(obj, ITEM_NODROP) && IN_ROOM(cont) != 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, cont);
|
|
|
|
act("$n puts $p in $P.", TRUE, ch, obj, cont, TO_ROOM);
|
|
|
|
/* Yes, I realize this is strange until we have auto-equip on rent. -gg */
|
|
if (OBJ_FLAGGED(obj, ITEM_NODROP) && !OBJ_FLAGGED(cont, ITEM_NODROP)) {
|
|
SET_BIT_AR(GET_OBJ_EXTRA(cont), ITEM_NODROP);
|
|
act("You get a strange feeling as you put $p in $P.", FALSE,
|
|
ch, obj, cont, TO_CHAR);
|
|
} else
|
|
act("You put $p in $P.", FALSE, ch, obj, cont, TO_CHAR);
|
|
}
|
|
}
|
|
|
|
/* 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 <object> <container>
|
|
2) put all.<object> <container>
|
|
3) put all <container>
|
|
4) put <object> on <furniture>
|
|
5) put all.<object> on <furniture>
|
|
6) put all on <furniture>
|
|
The <container> or <furniture> 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];
|
|
char arg2[MAX_INPUT_LENGTH];
|
|
char arg3[MAX_INPUT_LENGTH];
|
|
struct obj_data *obj, *next_obj, *cont;
|
|
struct char_data *tmp_char;
|
|
int obj_dotmode, cont_dotmode, found = 0, howmany = 1;
|
|
char *theobj, *thecont;
|
|
|
|
one_argument(two_arguments(argument, arg1, arg2), arg3); /* three_arguments */
|
|
|
|
if (*arg3 && is_number(arg1)) {
|
|
howmany = atoi(arg1);
|
|
theobj = arg2;
|
|
thecont = arg3;
|
|
} else {
|
|
theobj = arg1;
|
|
thecont = arg2;
|
|
}
|
|
obj_dotmode = find_all_dots(theobj);
|
|
cont_dotmode = find_all_dots(thecont);
|
|
|
|
if (!*theobj)
|
|
send_to_char(ch, "Put what in what?\r\n");
|
|
else if (cont_dotmode != FIND_INDIV)
|
|
send_to_char(ch, "You can only put things into one container at a time.\r\n");
|
|
else if (!*thecont) {
|
|
send_to_char(ch, "What do you want to put %s in?\r\n", obj_dotmode == FIND_INDIV ? "it" : "them");
|
|
} else {
|
|
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) {
|
|
/* 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 <obj> <container> */
|
|
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 <obj> on <furniture> */
|
|
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_on_furniture(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_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);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int can_take_obj(struct char_data *ch, struct obj_data *obj)
|
|
{
|
|
if (!(CAN_WEAR(obj, ITEM_WEAR_TAKE))) {
|
|
act("$p: you can't take that!", FALSE, ch, obj, 0, TO_CHAR);
|
|
return (0);
|
|
}
|
|
|
|
if (!IS_NPC(ch) && !PRF_FLAGGED(ch, PRF_NOHASSLE)) {
|
|
if (IS_CARRYING_N(ch) >= CAN_CARRY_N(ch)) {
|
|
act("$p: you can't carry that many items.", FALSE, ch, obj, 0, TO_CHAR);
|
|
return (0);
|
|
} else if ((IS_CARRYING_W(ch) + GET_OBJ_WEIGHT(obj)) > CAN_CARRY_W(ch)) {
|
|
act("$p: you can't carry that much weight.", FALSE, ch, obj, 0, TO_CHAR);
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
if (OBJ_SAT_IN_BY(obj)){
|
|
act("It appears someone is sitting on $p..", FALSE, ch, obj, 0, TO_CHAR);
|
|
return (0);
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
static void get_check_money(struct char_data *ch, struct obj_data *obj)
|
|
{
|
|
int value = GET_OBJ_VAL(obj, 0);
|
|
|
|
if (GET_OBJ_TYPE(obj) != ITEM_MONEY || value <= 0)
|
|
return;
|
|
|
|
extract_obj(obj);
|
|
|
|
increase_gold(ch, value);
|
|
|
|
if (value == 1)
|
|
send_to_char(ch, "There was 1 coin.\r\n");
|
|
else
|
|
send_to_char(ch, "There were %d coins.\r\n", value);
|
|
}
|
|
|
|
static void perform_get_from_container(struct char_data *ch, struct obj_data *obj,
|
|
struct obj_data *cont, int mode)
|
|
{
|
|
if (mode == FIND_OBJ_INV || can_take_obj(ch, obj)) {
|
|
if (IS_CARRYING_N(ch) >= CAN_CARRY_N(ch))
|
|
act("$p: you can't hold any more items.", FALSE, ch, obj, 0, TO_CHAR);
|
|
else if (get_otrigger(obj, ch)) {
|
|
obj_from_obj(obj);
|
|
obj_to_char(obj, ch);
|
|
act("You get $p from $P.", FALSE, ch, obj, cont, TO_CHAR);
|
|
act("$n gets $p from $P.", TRUE, ch, obj, cont, TO_ROOM);
|
|
get_check_money(ch, obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
void get_from_container(struct char_data *ch, struct obj_data *cont,
|
|
char *arg, int mode, int howmany)
|
|
{
|
|
struct obj_data *obj, *next_obj;
|
|
int obj_dotmode, found = 0;
|
|
|
|
obj_dotmode = find_all_dots(arg);
|
|
|
|
if (OBJVAL_FLAGGED(cont, CONT_CLOSED) && (GET_LEVEL(ch) < LVL_IMMORT || !PRF_FLAGGED(ch, PRF_NOHASSLE)))
|
|
act("$p is closed.", FALSE, ch, cont, 0, TO_CHAR);
|
|
else if (obj_dotmode == FIND_INDIV) {
|
|
if (!(obj = get_obj_in_list_vis(ch, arg, NULL, cont->contains))) {
|
|
char buf[MAX_STRING_LENGTH];
|
|
|
|
snprintf(buf, sizeof(buf), "There doesn't seem to be %s %s in $p.", AN(arg), arg);
|
|
act(buf, FALSE, ch, cont, 0, TO_CHAR);
|
|
} else {
|
|
struct obj_data *obj_next;
|
|
while (obj && howmany--) {
|
|
obj_next = obj->next_content;
|
|
perform_get_from_container(ch, obj, cont, mode);
|
|
obj = get_obj_in_list_vis(ch, arg, NULL, obj_next);
|
|
}
|
|
}
|
|
} else {
|
|
if (obj_dotmode == FIND_ALLDOT && !*arg) {
|
|
send_to_char(ch, "Get all of what?\r\n");
|
|
return;
|
|
}
|
|
for (obj = cont->contains; obj; obj = next_obj) {
|
|
next_obj = obj->next_content;
|
|
if (CAN_SEE_OBJ(ch, obj) &&
|
|
(obj_dotmode == FIND_ALL || isname(arg, obj->name))) {
|
|
found = 1;
|
|
perform_get_from_container(ch, obj, cont, mode);
|
|
}
|
|
}
|
|
if (!found) {
|
|
if (obj_dotmode == FIND_ALL)
|
|
act("$p seems to be empty.", FALSE, ch, cont, 0, TO_CHAR);
|
|
else {
|
|
char buf[MAX_STRING_LENGTH];
|
|
|
|
snprintf(buf, sizeof(buf), "You can't seem to find any %ss in $p.", arg);
|
|
act(buf, FALSE, ch, cont, 0, TO_CHAR);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int perform_get_from_room(struct char_data *ch, struct obj_data *obj)
|
|
{
|
|
if (can_take_obj(ch, obj) && get_otrigger(obj, ch)) {
|
|
obj_from_room(obj);
|
|
obj_to_char(obj, ch);
|
|
act("You get $p.", FALSE, ch, obj, 0, TO_CHAR);
|
|
act("$n gets $p.", TRUE, ch, obj, 0, TO_ROOM);
|
|
get_check_money(ch, obj);
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static void get_from_room(struct char_data *ch, char *arg, int howmany)
|
|
{
|
|
struct obj_data *obj, *next_obj;
|
|
int dotmode, found = 0;
|
|
|
|
dotmode = find_all_dots(arg);
|
|
|
|
if (dotmode == FIND_INDIV) {
|
|
if (!(obj = get_obj_in_list_vis(ch, arg, NULL, world[IN_ROOM(ch)].contents))) {
|
|
/* Are they trying to take something in a room extra description? */
|
|
if (find_exdesc(arg, world[IN_ROOM(ch)].ex_description) != NULL) {
|
|
send_to_char(ch, "You can't take %s %s.\r\n", AN(arg), arg);
|
|
return;
|
|
}
|
|
send_to_char(ch, "You don't see %s %s here.\r\n", AN(arg), arg);
|
|
} else {
|
|
struct obj_data *obj_next;
|
|
while(obj && howmany--) {
|
|
obj_next = obj->next_content;
|
|
perform_get_from_room(ch, obj);
|
|
obj = get_obj_in_list_vis(ch, arg, NULL, obj_next);
|
|
}
|
|
}
|
|
} else {
|
|
if (dotmode == FIND_ALLDOT && !*arg) {
|
|
send_to_char(ch, "Get all of what?\r\n");
|
|
return;
|
|
}
|
|
for (obj = world[IN_ROOM(ch)].contents; obj; obj = next_obj) {
|
|
next_obj = obj->next_content;
|
|
if (CAN_SEE_OBJ(ch, obj) &&
|
|
(dotmode == FIND_ALL || isname(arg, obj->name))) {
|
|
found = 1;
|
|
perform_get_from_room(ch, obj);
|
|
}
|
|
}
|
|
if (!found) {
|
|
if (dotmode == FIND_ALL)
|
|
send_to_char(ch, "There doesn't seem to be anything here.\r\n");
|
|
else
|
|
send_to_char(ch, "You don't see any %ss here.\r\n", arg);
|
|
}
|
|
}
|
|
}
|
|
|
|
ACMD(do_get)
|
|
{
|
|
char arg1[MAX_INPUT_LENGTH];
|
|
char arg2[MAX_INPUT_LENGTH];
|
|
char arg3[MAX_INPUT_LENGTH];
|
|
|
|
int cont_dotmode, found = 0, mode;
|
|
struct obj_data *cont;
|
|
struct char_data *tmp_char;
|
|
|
|
one_argument(two_arguments(argument, arg1, arg2), arg3); /* three_arguments */
|
|
|
|
if (!*arg1)
|
|
send_to_char(ch, "Get what?\r\n");
|
|
else if (!*arg2)
|
|
get_from_room(ch, arg1, 1);
|
|
else if (is_number(arg1) && !*arg3)
|
|
get_from_room(ch, arg2, atoi(arg1));
|
|
else {
|
|
int amount = 1;
|
|
if (is_number(arg1)) {
|
|
amount = atoi(arg1);
|
|
strcpy(arg1, arg2); /* strcpy: OK (sizeof: arg1 == arg2) */
|
|
strcpy(arg2, arg3); /* strcpy: OK (sizeof: arg2 == arg3) */
|
|
}
|
|
cont_dotmode = find_all_dots(arg2);
|
|
if (cont_dotmode == FIND_INDIV) {
|
|
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 && 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 {
|
|
if (cont_dotmode == FIND_ALLDOT && !*arg2) {
|
|
send_to_char(ch, "Get from all of what?\r\n");
|
|
return;
|
|
}
|
|
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 || 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 or furniture.", FALSE, ch, cont, 0, TO_CHAR);
|
|
}
|
|
}
|
|
{
|
|
int i;
|
|
struct obj_data *eq;
|
|
for (i = 0; i < NUM_WEARS; i++) {
|
|
eq = GET_EQ(ch, i);
|
|
if (!eq)
|
|
continue;
|
|
|
|
if (CAN_SEE_OBJ(ch, eq) &&
|
|
(cont_dotmode == FIND_ALL || isname(arg2, eq->name))) {
|
|
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 or furniture.", FALSE, ch, eq, 0, TO_CHAR);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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 || 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 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 or furniture.\r\n");
|
|
else
|
|
send_to_char(ch, "You can't seem to find any %ss here.\r\n", arg2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void perform_drop_gold(struct char_data *ch, int amount, byte mode, room_rnum RDR)
|
|
{
|
|
struct obj_data *obj;
|
|
|
|
if (amount <= 0)
|
|
send_to_char(ch, "Heh heh heh.. we are jolly funny today, eh?\r\n");
|
|
else if (GET_GOLD(ch) < amount)
|
|
send_to_char(ch, "You don't have that many coins!\r\n");
|
|
else {
|
|
if (mode != SCMD_JUNK) {
|
|
WAIT_STATE(ch, PULSE_VIOLENCE); /* to prevent coin-bombing */
|
|
obj = create_money(amount);
|
|
char buf[MAX_STRING_LENGTH];
|
|
long object_id = obj_script_id(obj);
|
|
|
|
if (!drop_wtrigger(obj, ch)) {
|
|
if (has_obj_by_uid_in_lookup_table(object_id))
|
|
extract_obj(obj);
|
|
|
|
return;
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf), "$n drops %s.", money_desc(amount));
|
|
act(buf, TRUE, ch, 0, 0, TO_ROOM);
|
|
|
|
send_to_char(ch, "You drop some gold.\r\n");
|
|
obj_to_room(obj, IN_ROOM(ch));
|
|
} else {
|
|
char buf[MAX_STRING_LENGTH];
|
|
|
|
snprintf(buf, sizeof(buf), "$n discards %s.", money_desc(amount));
|
|
act(buf, FALSE, ch, 0, 0, TO_ROOM);
|
|
|
|
send_to_char(ch, "You discard some gold.\r\n");
|
|
}
|
|
decrease_gold(ch, amount);
|
|
}
|
|
}
|
|
|
|
static int perform_drop(struct char_data *ch, struct obj_data *obj,
|
|
byte mode, const char *sname, room_rnum RDR)
|
|
{
|
|
char buf[MAX_STRING_LENGTH];
|
|
int value;
|
|
long object_id = obj_script_id(obj);
|
|
|
|
if (!drop_otrigger(obj, ch))
|
|
return 0;
|
|
|
|
if (!has_obj_by_uid_in_lookup_table(object_id))
|
|
return 0; // item was extracted by script
|
|
|
|
if ((mode == SCMD_DROP) && !drop_wtrigger(obj, ch))
|
|
return 0;
|
|
|
|
if (!has_obj_by_uid_in_lookup_table(object_id))
|
|
return 0; // item was extracted by script
|
|
|
|
if (OBJ_FLAGGED(obj, ITEM_NODROP) && !PRF_FLAGGED(ch, PRF_NOHASSLE)) {
|
|
snprintf(buf, sizeof(buf), "You can't %s $p, it must be CURSED!", sname);
|
|
act(buf, FALSE, ch, obj, 0, TO_CHAR);
|
|
return (0);
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf), "You %s $p.", sname);
|
|
act(buf, FALSE, ch, obj, 0, TO_CHAR);
|
|
|
|
snprintf(buf, sizeof(buf), "$n %ss $p.", sname);
|
|
act(buf, TRUE, ch, obj, 0, TO_ROOM);
|
|
|
|
obj_from_char(obj);
|
|
|
|
switch (mode) {
|
|
case SCMD_DROP:
|
|
obj_to_room(obj, IN_ROOM(ch));
|
|
return (0);
|
|
case SCMD_JUNK:
|
|
value = MAX(1, MIN(200, GET_OBJ_COST(obj) / 16));
|
|
extract_obj(obj);
|
|
return (value);
|
|
default:
|
|
log("SYSERR: Incorrect argument %d passed to perform_drop.", mode);
|
|
/* SYSERR_DESC: This error comes from perform_drop() and is output when
|
|
* perform_drop() is called with an illegal 'mode' argument. */
|
|
break;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
ACMD(do_drop)
|
|
{
|
|
char arg[MAX_INPUT_LENGTH];
|
|
struct obj_data *obj, *next_obj;
|
|
room_rnum RDR = 0;
|
|
byte mode = SCMD_DROP;
|
|
int dotmode, amount = 0, multi;
|
|
const char *sname;
|
|
|
|
switch (subcmd) {
|
|
case SCMD_JUNK:
|
|
sname = "discard";
|
|
mode = SCMD_JUNK;
|
|
break;
|
|
default:
|
|
sname = "drop";
|
|
break;
|
|
}
|
|
|
|
argument = one_argument(argument, arg);
|
|
|
|
if (!*arg) {
|
|
send_to_char(ch, "What do you want to %s?\r\n", sname);
|
|
return;
|
|
} else if (is_number(arg)) {
|
|
multi = atoi(arg);
|
|
one_argument(argument, arg);
|
|
if (!str_cmp("coins", arg) || !str_cmp("coin", arg))
|
|
perform_drop_gold(ch, multi, mode, RDR);
|
|
else if (multi <= 0)
|
|
send_to_char(ch, "Yeah, that makes sense.\r\n");
|
|
else if (!*arg)
|
|
send_to_char(ch, "What do you want to %s %d of?\r\n", sname, multi);
|
|
else if (!(obj = get_obj_in_list_vis(ch, arg, NULL, ch->carrying)))
|
|
send_to_char(ch, "You don't seem to have any %ss.\r\n", arg);
|
|
else {
|
|
do {
|
|
next_obj = get_obj_in_list_vis(ch, arg, NULL, obj->next_content);
|
|
amount += perform_drop(ch, obj, mode, sname, RDR);
|
|
obj = next_obj;
|
|
} while (obj && --multi);
|
|
}
|
|
} else {
|
|
dotmode = find_all_dots(arg);
|
|
|
|
/* Can't junk or donate all */
|
|
if ((dotmode == FIND_ALL) && (subcmd == SCMD_JUNK)) {
|
|
if (subcmd == SCMD_JUNK)
|
|
send_to_char(ch, "You need to specify what you want to discard.\r\n");
|
|
return;
|
|
}
|
|
if (dotmode == FIND_ALL) {
|
|
if (!ch->carrying)
|
|
send_to_char(ch, "You don't seem to be carrying anything.\r\n");
|
|
else
|
|
for (obj = ch->carrying; obj; obj = next_obj) {
|
|
next_obj = obj->next_content;
|
|
amount += perform_drop(ch, obj, mode, sname, RDR);
|
|
}
|
|
} else if (dotmode == FIND_ALLDOT) {
|
|
if (!*arg) {
|
|
send_to_char(ch, "What do you want to %s all of?\r\n", sname);
|
|
return;
|
|
}
|
|
if (!(obj = get_obj_in_list_vis(ch, arg, NULL, ch->carrying)))
|
|
send_to_char(ch, "You don't seem to have any %ss.\r\n", arg);
|
|
|
|
while (obj) {
|
|
next_obj = get_obj_in_list_vis(ch, arg, NULL, obj->next_content);
|
|
amount += perform_drop(ch, obj, mode, sname, RDR);
|
|
obj = next_obj;
|
|
}
|
|
} else {
|
|
if (!(obj = get_obj_in_list_vis(ch, arg, NULL, ch->carrying)))
|
|
send_to_char(ch, "You don't seem to have %s %s.\r\n", AN(arg), arg);
|
|
else
|
|
amount += perform_drop(ch, obj, mode, sname, RDR);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static void perform_give(struct char_data *ch, struct char_data *vict,
|
|
struct obj_data *obj)
|
|
{
|
|
if (!give_otrigger(obj, ch, vict))
|
|
return;
|
|
if (!receive_mtrigger(vict, ch, obj))
|
|
return;
|
|
|
|
if (OBJ_FLAGGED(obj, ITEM_NODROP) && !PRF_FLAGGED(ch, PRF_NOHASSLE)) {
|
|
act("You can't let go of $p!! Yeech!", FALSE, ch, obj, 0, TO_CHAR);
|
|
return;
|
|
}
|
|
if (IS_CARRYING_N(vict) >= CAN_CARRY_N(vict) && GET_LEVEL(ch) < LVL_IMMORT && GET_LEVEL(vict) < LVL_IMMORT) {
|
|
act("$N seems to have $S hands full.", FALSE, ch, 0, vict, TO_CHAR);
|
|
return;
|
|
}
|
|
if (GET_OBJ_WEIGHT(obj) + IS_CARRYING_W(vict) > CAN_CARRY_W(vict) && GET_LEVEL(ch) < LVL_IMMORT && GET_LEVEL(vict) < LVL_IMMORT) {
|
|
act("$E can't carry that much weight.", FALSE, ch, 0, vict, TO_CHAR);
|
|
return;
|
|
}
|
|
obj_from_char(obj);
|
|
obj_to_char(obj, vict);
|
|
act("You give $p to $N.", FALSE, ch, obj, vict, TO_CHAR);
|
|
act("$n gives you $p.", FALSE, ch, obj, vict, TO_VICT);
|
|
act("$n gives $p to $N.", TRUE, ch, obj, vict, TO_NOTVICT);
|
|
|
|
autoquest_trigger_check( ch, vict, obj, AQ_OBJ_RETURN);
|
|
}
|
|
|
|
/* utility function for give */
|
|
static struct char_data *give_find_vict(struct char_data *ch, char *arg)
|
|
{
|
|
struct char_data *vict;
|
|
|
|
skip_spaces(&arg);
|
|
if (!*arg)
|
|
send_to_char(ch, "To who?\r\n");
|
|
else if (!(vict = get_char_vis(ch, arg, NULL, FIND_CHAR_ROOM)))
|
|
send_to_char(ch, "%s", CONFIG_NOPERSON);
|
|
else if (vict == ch)
|
|
send_to_char(ch, "What's the point of that?\r\n");
|
|
else
|
|
return (vict);
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
static void perform_give_gold(struct char_data *ch, struct char_data *vict,
|
|
int amount)
|
|
{
|
|
char buf[MAX_STRING_LENGTH];
|
|
|
|
if (amount <= 0) {
|
|
send_to_char(ch, "Heh heh heh ... we are jolly funny today, eh?\r\n");
|
|
return;
|
|
}
|
|
if ((GET_GOLD(ch) < amount) && (IS_NPC(ch) || (GET_LEVEL(ch) < LVL_GOD))) {
|
|
send_to_char(ch, "You don't have that many coins!\r\n");
|
|
return;
|
|
}
|
|
send_to_char(ch, "%s", CONFIG_OK);
|
|
|
|
snprintf(buf, sizeof(buf), "$n gives you %d gold coin%s.", amount, amount == 1 ? "" : "s");
|
|
act(buf, FALSE, ch, 0, vict, TO_VICT);
|
|
|
|
snprintf(buf, sizeof(buf), "$n gives %s to $N.", money_desc(amount));
|
|
act(buf, TRUE, ch, 0, vict, TO_NOTVICT);
|
|
|
|
if (IS_NPC(ch) || (GET_LEVEL(ch) < LVL_GOD))
|
|
decrease_gold(ch, amount);
|
|
|
|
increase_gold(vict, amount);
|
|
bribe_mtrigger(vict, ch, amount);
|
|
}
|
|
|
|
ACMD(do_give)
|
|
{
|
|
char arg[MAX_STRING_LENGTH];
|
|
int amount, dotmode;
|
|
struct char_data *vict;
|
|
struct obj_data *obj, *next_obj;
|
|
|
|
argument = one_argument(argument, arg);
|
|
|
|
if (!*arg)
|
|
send_to_char(ch, "Give what to who?\r\n");
|
|
else if (is_number(arg)) {
|
|
amount = atoi(arg);
|
|
argument = one_argument(argument, arg);
|
|
if (!str_cmp("coins", arg) || !str_cmp("coin", arg)) {
|
|
one_argument(argument, arg);
|
|
if ((vict = give_find_vict(ch, arg)) != NULL)
|
|
perform_give_gold(ch, vict, amount);
|
|
return;
|
|
} else if (!*arg) /* Give multiple code. */
|
|
send_to_char(ch, "What do you want to give %d of?\r\n", amount);
|
|
else if (!(vict = give_find_vict(ch, argument)))
|
|
return;
|
|
else if (!(obj = get_obj_in_list_vis(ch, arg, NULL, ch->carrying)))
|
|
send_to_char(ch, "You don't seem to have any %ss.\r\n", arg);
|
|
else {
|
|
while (obj && amount--) {
|
|
next_obj = get_obj_in_list_vis(ch, arg, NULL, obj->next_content);
|
|
perform_give(ch, vict, obj);
|
|
obj = next_obj;
|
|
}
|
|
}
|
|
} else {
|
|
char buf1[MAX_INPUT_LENGTH];
|
|
|
|
one_argument(argument, buf1);
|
|
if (!(vict = give_find_vict(ch, buf1)))
|
|
return;
|
|
dotmode = find_all_dots(arg);
|
|
if (dotmode == FIND_INDIV) {
|
|
if (!(obj = get_obj_in_list_vis(ch, arg, NULL, ch->carrying)))
|
|
send_to_char(ch, "You don't seem to have %s %s.\r\n", AN(arg), arg);
|
|
else
|
|
perform_give(ch, vict, obj);
|
|
} else {
|
|
if (dotmode == FIND_ALLDOT && !*arg) {
|
|
send_to_char(ch, "All of what?\r\n");
|
|
return;
|
|
}
|
|
if (!ch->carrying)
|
|
send_to_char(ch, "You don't seem to be holding anything.\r\n");
|
|
else
|
|
for (obj = ch->carrying; obj; obj = next_obj) {
|
|
next_obj = obj->next_content;
|
|
if (CAN_SEE_OBJ(ch, obj) &&
|
|
((dotmode == FIND_ALL || isname(arg, obj->name))))
|
|
perform_give(ch, vict, obj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void weight_change_object(struct obj_data *obj, int weight)
|
|
{
|
|
struct obj_data *tmp_obj;
|
|
struct char_data *tmp_ch;
|
|
|
|
if (IN_ROOM(obj) != NOWHERE) {
|
|
GET_OBJ_WEIGHT(obj) += weight;
|
|
} else if ((tmp_ch = obj->carried_by)) {
|
|
obj_from_char(obj);
|
|
GET_OBJ_WEIGHT(obj) += weight;
|
|
obj_to_char(obj, tmp_ch);
|
|
} else if ((tmp_obj = obj->in_obj)) {
|
|
obj_from_obj(obj);
|
|
GET_OBJ_WEIGHT(obj) += weight;
|
|
obj_to_obj(obj, tmp_obj);
|
|
} else {
|
|
log("SYSERR: Unknown attempt to subtract weight from an object.");
|
|
/* SYSERR_DESC: weight_change_object() outputs this error when weight is
|
|
* attempted to be removed from an object that is not carried or in
|
|
* another object. */
|
|
}
|
|
}
|
|
|
|
void name_from_drinkcon(struct obj_data *obj)
|
|
{
|
|
const char *liqname;
|
|
char *new_name;
|
|
|
|
if (!obj || (GET_OBJ_TYPE(obj) != ITEM_DRINKCON && GET_OBJ_TYPE(obj) != ITEM_FOUNTAIN))
|
|
return;
|
|
|
|
if (obj->name == obj_proto[GET_OBJ_RNUM(obj)].name)
|
|
obj->name = strdup(obj_proto[GET_OBJ_RNUM(obj)].name);
|
|
|
|
liqname = drinknames[GET_OBJ_VAL(obj, 2)];
|
|
|
|
remove_from_string(obj->name, liqname);
|
|
new_name = right_trim_whitespace(obj->name);
|
|
free(obj->name);
|
|
obj->name = new_name;
|
|
|
|
}
|
|
|
|
void name_to_drinkcon(struct obj_data *obj, int type)
|
|
{
|
|
char *new_name;
|
|
|
|
if (!obj || (GET_OBJ_TYPE(obj) != ITEM_DRINKCON && GET_OBJ_TYPE(obj) != ITEM_FOUNTAIN))
|
|
return;
|
|
|
|
CREATE(new_name, char, strlen(obj->name) + strlen(drinknames[type]) + 2);
|
|
sprintf(new_name, "%s %s", obj->name, drinknames[type]); /* sprintf: OK */
|
|
|
|
if (GET_OBJ_RNUM(obj) == NOTHING || obj->name != obj_proto[GET_OBJ_RNUM(obj)].name)
|
|
free(obj->name);
|
|
|
|
obj->name = new_name;
|
|
}
|
|
|
|
ACMD(do_drink)
|
|
{
|
|
char arg[MAX_INPUT_LENGTH];
|
|
struct obj_data *temp;
|
|
struct affected_type af;
|
|
int amount, weight;
|
|
int on_ground = 0;
|
|
|
|
one_argument(argument, arg);
|
|
|
|
if (IS_NPC(ch)) /* Cannot use GET_COND() on mobs. */
|
|
return;
|
|
|
|
if (!*arg) {
|
|
char buf[MAX_STRING_LENGTH];
|
|
switch (SECT(IN_ROOM(ch))) {
|
|
case SECT_WATER_SWIM:
|
|
case SECT_WATER_NOSWIM:
|
|
case SECT_UNDERWATER:
|
|
if ((GET_COND(ch, HUNGER) > 20) && (GET_COND(ch, THIRST) > 0)) {
|
|
send_to_char(ch, "Your stomach can't contain anymore!\r\n");
|
|
}
|
|
snprintf(buf, sizeof(buf), "$n takes a refreshing drink.");
|
|
act(buf, TRUE, ch, 0, 0, TO_ROOM);
|
|
send_to_char(ch, "You take a refreshing drink.\r\n");
|
|
gain_condition(ch, THIRST, 1);
|
|
if (GET_COND(ch, THIRST) > 20)
|
|
send_to_char(ch, "You don't feel thirsty any more.\r\n");
|
|
return;
|
|
default:
|
|
send_to_char(ch, "Drink from what?\r\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!(temp = get_obj_in_list_vis(ch, arg, NULL, ch->carrying))) {
|
|
if (!(temp = get_obj_in_list_vis(ch, arg, NULL, world[IN_ROOM(ch)].contents))) {
|
|
send_to_char(ch, "You can't find it!\r\n");
|
|
return;
|
|
} else
|
|
on_ground = 1;
|
|
}
|
|
|
|
if ((GET_OBJ_TYPE(temp) != ITEM_DRINKCON) &&
|
|
(GET_OBJ_TYPE(temp) != ITEM_FOUNTAIN)) {
|
|
send_to_char(ch, "You can't drink from that!\r\n");
|
|
return;
|
|
}
|
|
|
|
if (on_ground && (GET_OBJ_TYPE(temp) == ITEM_DRINKCON)) {
|
|
send_to_char(ch, "You have to be holding that to drink from it.\r\n");
|
|
return;
|
|
}
|
|
|
|
if ((GET_COND(ch, DRUNK) > 10) && (GET_COND(ch, THIRST) > 0)) {
|
|
/* The pig is drunk */
|
|
send_to_char(ch, "You can't seem to get close enough to your mouth.\r\n");
|
|
act("$n tries to drink but misses $s mouth!", TRUE, ch, 0, 0, TO_ROOM);
|
|
return;
|
|
}
|
|
|
|
if ((GET_COND(ch, HUNGER) > 20) && (GET_COND(ch, THIRST) > 0)) {
|
|
send_to_char(ch, "Your stomach can't contain anymore!\r\n");
|
|
return;
|
|
}
|
|
|
|
/* Already empty? Update sdesc safely and report. */
|
|
if (GET_OBJ_VAL(temp, 1) < 1) {
|
|
send_to_char(ch, "It is empty.\r\n");
|
|
|
|
if (GET_OBJ_TYPE(temp) == ITEM_DRINKCON) {
|
|
obj_rnum rnum = GET_OBJ_RNUM(temp);
|
|
const char *proto_sd = (rnum != NOTHING) ? obj_proto[rnum].short_description : NULL;
|
|
const char *base = proto_sd ? proto_sd : temp->short_description;
|
|
const char *noun = base ? base : "container";
|
|
|
|
/* Strip leading article from base noun phrase. */
|
|
if (!strn_cmp(noun, "a ", 2)) noun += 2;
|
|
else if (!strn_cmp(noun, "an ", 3)) noun += 3;
|
|
else if (!strn_cmp(noun, "the ", 4)) noun += 4;
|
|
|
|
/* Find " of " if present to drop the tail when empty. */
|
|
const char *ofp = strstr(noun, " of ");
|
|
size_t noun_len = ofp ? (size_t)(ofp - noun) : strlen(noun);
|
|
|
|
char sbuf[MAX_STRING_LENGTH];
|
|
/* Max noun we can print after "an empty " and a space is bounded by precision */
|
|
/* "an empty " is 9 characters plus the space already included -> total prefix 9 */
|
|
/* Use precision to avoid overrun: */
|
|
snprintf(sbuf, sizeof(sbuf), "an empty %.*s", (int)MIN(noun_len, sizeof(sbuf) - 10), noun);
|
|
|
|
if (temp->short_description && temp->short_description != proto_sd)
|
|
free(temp->short_description);
|
|
temp->short_description = strdup(sbuf);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!consume_otrigger(temp, ch, OCMD_DRINK)) /* check trigger */
|
|
return;
|
|
|
|
if (subcmd == SCMD_DRINK) {
|
|
char buf[MAX_STRING_LENGTH];
|
|
|
|
snprintf(buf, sizeof(buf), "$n drinks %s from $p.", drinks[GET_OBJ_VAL(temp, 2)]);
|
|
act(buf, TRUE, ch, temp, 0, TO_ROOM);
|
|
|
|
send_to_char(ch, "You drink the %s.\r\n", drinks[GET_OBJ_VAL(temp, 2)]);
|
|
|
|
if (drink_aff[GET_OBJ_VAL(temp, 2)][DRUNK] > 0)
|
|
amount = (25 - GET_COND(ch, THIRST)) / drink_aff[GET_OBJ_VAL(temp, 2)][DRUNK];
|
|
else
|
|
amount = rand_number(3, 10);
|
|
|
|
} else {
|
|
act("$n sips from $p.", TRUE, ch, temp, 0, TO_ROOM);
|
|
send_to_char(ch, "It tastes like %s.\r\n", drinks[GET_OBJ_VAL(temp, 2)]);
|
|
amount = 1;
|
|
}
|
|
|
|
amount = MIN(amount, GET_OBJ_VAL(temp, 1));
|
|
|
|
/* You can't subtract more than the object weighs, unless its unlimited. */
|
|
if (GET_OBJ_VAL(temp, 0) > 0) {
|
|
weight = MIN(amount, GET_OBJ_WEIGHT(temp));
|
|
weight_change_object(temp, -weight); /* Subtract amount */
|
|
}
|
|
|
|
gain_condition(ch, DRUNK, drink_aff[GET_OBJ_VAL(temp, 2)][DRUNK] * amount / 4);
|
|
gain_condition(ch, HUNGER, drink_aff[GET_OBJ_VAL(temp, 2)][HUNGER] * amount / 4);
|
|
gain_condition(ch, THIRST, drink_aff[GET_OBJ_VAL(temp, 2)][THIRST] * amount / 4);
|
|
|
|
if (GET_COND(ch, DRUNK) > 10)
|
|
send_to_char(ch, "You feel drunk.\r\n");
|
|
|
|
if (GET_COND(ch, THIRST) > 20)
|
|
send_to_char(ch, "You don't feel thirsty any more.\r\n");
|
|
|
|
if (GET_COND(ch, HUNGER) > 20)
|
|
send_to_char(ch, "You are full.\r\n");
|
|
|
|
if (GET_OBJ_VAL(temp, 3) && GET_LEVEL(ch) < LVL_IMMORT) { /* The crap was poisoned ! */
|
|
send_to_char(ch, "Oops, it tasted rather strange!\r\n");
|
|
act("$n chokes and utters some strange sounds.", TRUE, ch, 0, 0, TO_ROOM);
|
|
|
|
new_affect(&af);
|
|
af.spell = SPELL_POISON;
|
|
af.duration = amount * 3;
|
|
SET_BIT_AR(af.bitvector, AFF_POISON);
|
|
affect_join(ch, &af, FALSE, FALSE, FALSE, FALSE);
|
|
}
|
|
|
|
/* Empty the container (unless unlimited), and no longer poison. */
|
|
if (GET_OBJ_VAL(temp, 0) > 0) {
|
|
GET_OBJ_VAL(temp, 1) -= amount;
|
|
|
|
/* Rebuild short description to match remaining percentage (safe). */
|
|
if (GET_OBJ_TYPE(temp) == ITEM_DRINKCON) {
|
|
int cap = GET_OBJ_VAL(temp, 0);
|
|
int rem = GET_OBJ_VAL(temp, 1);
|
|
|
|
obj_rnum rnum = GET_OBJ_RNUM(temp);
|
|
const char *proto_sd = (rnum != NOTHING) ? obj_proto[rnum].short_description : NULL;
|
|
const char *base = proto_sd ? proto_sd : temp->short_description;
|
|
const char *noun = base ? base : "container";
|
|
|
|
/* Strip leading article. */
|
|
if (!strn_cmp(noun, "a ", 2)) noun += 2;
|
|
else if (!strn_cmp(noun, "an ", 3)) noun += 3;
|
|
else if (!strn_cmp(noun, "the ", 4)) noun += 4;
|
|
|
|
char sbuf[MAX_STRING_LENGTH];
|
|
|
|
if (rem <= 0) {
|
|
/* Empty: hide previous liquid, use container base before " of ". */
|
|
const char *ofp = strstr(noun, " of ");
|
|
size_t noun_len = ofp ? (size_t)(ofp - noun) : strlen(noun);
|
|
/* "an empty " (9 chars) + noun_len (bounded by precision) */
|
|
snprintf(sbuf, sizeof(sbuf), "an empty %.*s", (int)MIN(noun_len, sizeof(sbuf) - 10), noun);
|
|
} else {
|
|
/* Non-empty: banded status + full noun (including " of <liquid>"). */
|
|
const char *status = "partially filled";
|
|
if (cap > 0) {
|
|
int pct = (rem * 100) / cap;
|
|
if (pct >= 75)
|
|
status = "partially filled";
|
|
else if (pct >= 50)
|
|
status = "half-filled";
|
|
else
|
|
status = "nearly empty";
|
|
}
|
|
|
|
/* Choose article from status' first letter. */
|
|
bool use_an = FALSE;
|
|
if (status && *status) {
|
|
char first = LOWER((unsigned char)status[0]);
|
|
use_an = (first == 'a' || first == 'e' || first == 'i' || first == 'o' || first == 'u');
|
|
}
|
|
|
|
/* Format safely: "<a/an> <status> <noun>" */
|
|
const char *article = use_an ? "an" : "a";
|
|
/* Compute remaining space for noun using precision to cap it */
|
|
size_t prefix_len = strlen(article) + 1 /* space */ + strlen(status) + 1 /* space */;
|
|
size_t max_noun = (sizeof(sbuf) > (prefix_len + 1)) ? (sizeof(sbuf) - prefix_len - 1) : 0;
|
|
snprintf(sbuf, sizeof(sbuf), "%s %s %.*s", article, status, (int)max_noun, noun);
|
|
}
|
|
|
|
if (temp->short_description && temp->short_description != proto_sd)
|
|
free(temp->short_description);
|
|
temp->short_description = strdup(sbuf);
|
|
}
|
|
|
|
if (!GET_OBJ_VAL(temp, 1)) { /* The last bit */
|
|
name_from_drinkcon(temp);
|
|
GET_OBJ_VAL(temp, 2) = 0;
|
|
GET_OBJ_VAL(temp, 3) = 0;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
ACMD(do_eat)
|
|
{
|
|
char arg[MAX_INPUT_LENGTH];
|
|
struct obj_data *obj = NULL;
|
|
struct affected_type af;
|
|
int amount = 0;
|
|
|
|
one_argument(argument, arg);
|
|
|
|
if (IS_NPC(ch)) /* Cannot use GET_COND() on mobs. */
|
|
return;
|
|
|
|
if (!*arg) {
|
|
send_to_char(ch, "Eat what?\r\n");
|
|
return;
|
|
}
|
|
|
|
/* Find in inventory first, then in room */
|
|
if (!(obj = get_obj_in_list_vis(ch, arg, NULL, ch->carrying))) {
|
|
if (!(obj = get_obj_in_list_vis(ch, arg, NULL, world[IN_ROOM(ch)].contents))) {
|
|
send_to_char(ch, "You can't find it!\r\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* If the player used 'taste', we handle both food and drink here (no delegation). */
|
|
if (subcmd == SCMD_TASTE) {
|
|
/* DRINKS: sip logic (inline; do not call do_drink) */
|
|
if (GET_OBJ_TYPE(obj) == ITEM_DRINKCON || GET_OBJ_TYPE(obj) == ITEM_FOUNTAIN) {
|
|
int cap = GET_OBJ_VAL(obj, 0);
|
|
int rem = GET_OBJ_VAL(obj, 1);
|
|
int liq = GET_OBJ_VAL(obj, 2); /* liquid type */
|
|
|
|
if (GET_COND(ch, DRUNK) > 10 && GET_COND(ch, THIRST) > 0) {
|
|
send_to_char(ch, "You can't seem to get close enough to your mouth.\r\n");
|
|
act("$n tries to drink but misses $s mouth!", TRUE, ch, 0, 0, TO_ROOM);
|
|
return;
|
|
}
|
|
|
|
if (rem < 1) {
|
|
send_to_char(ch, "It is empty.\r\n");
|
|
|
|
/* Update sdesc to "an empty <container>" like our drink update */
|
|
if (GET_OBJ_TYPE(obj) == ITEM_DRINKCON) {
|
|
obj_rnum rnum = GET_OBJ_RNUM(obj);
|
|
const char *proto_sd = (rnum != NOTHING) ? obj_proto[rnum].short_description : NULL;
|
|
const char *base = obj->short_description ? obj->short_description : proto_sd;
|
|
const char *noun = base ? base : "container";
|
|
|
|
/* strip leading article */
|
|
if (!strn_cmp(noun, "a ", 2)) noun += 2;
|
|
else if (!strn_cmp(noun, "an ", 3)) noun += 3;
|
|
else if (!strn_cmp(noun, "the ", 4)) noun += 4;
|
|
|
|
char sbuf[MAX_STRING_LENGTH];
|
|
const char *ofp = strstr(noun, " of ");
|
|
size_t noun_len = ofp ? (size_t)(ofp - noun) : strlen(noun);
|
|
snprintf(sbuf, sizeof(sbuf), "an empty %.*s", (int)MIN(noun_len, sizeof(sbuf) - 10), noun);
|
|
|
|
if (obj->short_description && obj->short_description != proto_sd)
|
|
free(obj->short_description);
|
|
obj->short_description = strdup(sbuf);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Take a single sip */
|
|
amount = 1;
|
|
rem = MAX(0, rem - amount);
|
|
GET_OBJ_VAL(obj, 1) = rem;
|
|
|
|
/* Condition effects: use HUNGER (not FULL) in this codebase */
|
|
gain_condition(ch, DRUNK, (drink_aff[liq][DRUNK] * amount) / 4);
|
|
gain_condition(ch, HUNGER,(drink_aff[liq][HUNGER] * amount) / 4); /* <-- FIX */
|
|
gain_condition(ch, THIRST,(drink_aff[liq][THIRST] * amount) / 4);
|
|
|
|
if (GET_COND(ch, DRUNK) > 10)
|
|
send_to_char(ch, "You feel drunk.\r\n");
|
|
|
|
if (GET_COND(ch, THIRST) > 20)
|
|
send_to_char(ch, "You don't feel thirsty.\r\n");
|
|
|
|
if (GET_OBJ_VAL(obj, 3) && GET_LEVEL(ch) < LVL_IMMORT) {
|
|
send_to_char(ch, "It tastes strange!\r\n");
|
|
act("$n tastes something strange and coughs.", FALSE, ch, 0, 0, TO_ROOM);
|
|
|
|
memset(&af, 0, sizeof(af));
|
|
af.spell = SPELL_POISON; /* <-- FIX: use spell, not type */
|
|
af.duration = amount * 3;
|
|
af.location = APPLY_NONE;
|
|
af.modifier = 0;
|
|
/* no af.bonus in this codebase */
|
|
af.bitvector[0] = af.bitvector[1] = af.bitvector[2] = af.bitvector[3] = 0;
|
|
SET_BIT_AR(af.bitvector, AFF_POISON);
|
|
affect_join(ch, &af, FALSE, FALSE, FALSE, FALSE);
|
|
}
|
|
|
|
act("You sip from $p.", FALSE, ch, obj, 0, TO_CHAR);
|
|
act("$n sips from $p.", TRUE, ch, obj, 0, TO_ROOM);
|
|
|
|
/* Update dynamic sdesc band like drink code ("partially filled", etc.) */
|
|
if (GET_OBJ_TYPE(obj) == ITEM_DRINKCON) {
|
|
obj_rnum rnum = GET_OBJ_RNUM(obj);
|
|
const char *proto_sd = (rnum != NOTHING) ? obj_proto[rnum].short_description : NULL;
|
|
const char *base = obj->short_description ? obj->short_description : proto_sd;
|
|
const char *noun = base ? base : "container";
|
|
|
|
/* Strip leading article */
|
|
if (!strn_cmp(noun, "a ", 2)) noun += 2;
|
|
else if (!strn_cmp(noun, "an ", 3)) noun += 3;
|
|
else if (!strn_cmp(noun, "the ", 4)) noun += 4;
|
|
|
|
char sbuf[MAX_STRING_LENGTH];
|
|
|
|
if (rem <= 0) {
|
|
const char *ofp = strstr(noun, " of ");
|
|
size_t noun_len = ofp ? (size_t)(ofp - noun) : strlen(noun);
|
|
snprintf(sbuf, sizeof(sbuf), "an empty %.*s", (int)MIN(noun_len, sizeof(sbuf) - 10), noun);
|
|
} else {
|
|
const char *status = "partially filled";
|
|
if (cap > 0) {
|
|
int pct = (rem * 100) / cap;
|
|
if (pct >= 75)
|
|
status = "partially filled";
|
|
else if (pct >= 50)
|
|
status = "half-filled";
|
|
else
|
|
status = "nearly empty";
|
|
}
|
|
bool use_an = FALSE;
|
|
if (status && *status) {
|
|
char first = LOWER((unsigned char)status[0]);
|
|
use_an = (first == 'a' || first == 'e' || first == 'i' || first == 'o' || first == 'u');
|
|
}
|
|
const char *article = use_an ? "an" : "a";
|
|
|
|
size_t prefix_len = strlen(article) + 1 + strlen(status) + 1;
|
|
size_t max_noun = (sizeof(sbuf) > (prefix_len + 1))
|
|
? (sizeof(sbuf) - prefix_len - 1) : 0;
|
|
snprintf(sbuf, sizeof(sbuf), "%s %s %.*s", article, status, (int)max_noun, noun);
|
|
}
|
|
|
|
if (obj->short_description && obj->short_description != proto_sd)
|
|
free(obj->short_description);
|
|
obj->short_description = strdup(sbuf);
|
|
}
|
|
|
|
return;
|
|
} /* end taste of drink container */
|
|
/* otherwise fall through to taste FOOD below */
|
|
}
|
|
|
|
/* From here: regular EAT/TASTE handling for FOOD (multi-bite) */
|
|
if (GET_OBJ_TYPE(obj) != ITEM_FOOD && GET_LEVEL(ch) < LVL_IMMORT) {
|
|
send_to_char(ch, "You can't eat THAT!\r\n");
|
|
return;
|
|
}
|
|
|
|
/* Prevent overstuffing */
|
|
if (GET_COND(ch, HUNGER) > 20) {
|
|
send_to_char(ch, "You are too full to eat more!\r\n");
|
|
return;
|
|
}
|
|
|
|
if (!consume_otrigger(obj, ch, OCMD_EAT)) /* check trigger */
|
|
return;
|
|
|
|
/* Determine bites & nutrition */
|
|
int cap = GET_OBJ_VAL(obj, VAL_FOOD_BITE_CAP);
|
|
int left = GET_OBJ_VAL(obj, VAL_FOOD_BITES_LEFT);
|
|
int per = GET_OBJ_VAL(obj, VAL_FOOD_HOURS_PER_BITE);
|
|
bool poisoned = (GET_OBJ_VAL(obj, VAL_FOOD_POISONED) != 0);
|
|
|
|
if (left < 1) {
|
|
send_to_char(ch, "There's nothing left of it.\r\n");
|
|
return;
|
|
}
|
|
|
|
/* Messaging differs for taste vs eat, nutrition identical per bite */
|
|
if (subcmd == SCMD_EAT) {
|
|
act("You eat a bite of $p.", FALSE, ch, obj, 0, TO_CHAR);
|
|
act("$n eats a bite of $p.", TRUE, ch, obj, 0, TO_ROOM);
|
|
} else {
|
|
act("You nibble a little bit of $p.", FALSE, ch, obj, 0, TO_CHAR);
|
|
act("$n tastes a little bit of $p.", TRUE, ch, obj, 0, TO_ROOM);
|
|
}
|
|
|
|
/* One bite per action (simple & consistent). */
|
|
left = MAX(0, left - 1);
|
|
GET_OBJ_VAL(obj, VAL_FOOD_BITES_LEFT) = left;
|
|
|
|
/* Apply hunger gain from one bite */
|
|
amount = MAX(0, per);
|
|
gain_condition(ch, HUNGER, amount);
|
|
|
|
if (GET_COND(ch, HUNGER) > 20)
|
|
send_to_char(ch, "You are full.\r\n");
|
|
|
|
if (poisoned && GET_LEVEL(ch) < LVL_IMMORT) {
|
|
send_to_char(ch, "Oops, that tasted rather strange!\r\n");
|
|
act("$n coughs and utters some strange sounds.", FALSE, ch, 0, 0, TO_ROOM);
|
|
|
|
memset(&af, 0, sizeof(af));
|
|
af.spell = SPELL_POISON; /* <-- FIX: use spell, not type */
|
|
af.duration = MAX(1, amount * 2);
|
|
af.location = APPLY_NONE;
|
|
af.modifier = 0;
|
|
/* no af.bonus in this codebase */
|
|
af.bitvector[0] = af.bitvector[1] = af.bitvector[2] = af.bitvector[3] = 0;
|
|
SET_BIT_AR(af.bitvector, AFF_POISON);
|
|
affect_join(ch, &af, FALSE, FALSE, FALSE, FALSE);
|
|
}
|
|
|
|
/* If no bites remain, consume the object.
|
|
* Otherwise, update the instance sdesc with an "eaten" status band. */
|
|
if (left <= 0) {
|
|
send_to_char(ch, "There's nothing left now.\r\n");
|
|
extract_obj(obj);
|
|
} else {
|
|
/* Build "<a/an> <status> <noun>" using the instance/proto sdesc as noun */
|
|
obj_rnum rnum = GET_OBJ_RNUM(obj);
|
|
const char *proto_sd = (rnum != NOTHING) ? obj_proto[rnum].short_description : NULL;
|
|
const char *base = obj->short_description ? obj->short_description : proto_sd;
|
|
const char *noun = base ? base : "food";
|
|
|
|
/* Strip leading article from noun */
|
|
if (!strn_cmp(noun, "a ", 2)) noun += 2;
|
|
else if (!strn_cmp(noun, "an ", 3)) noun += 3;
|
|
else if (!strn_cmp(noun, "the ", 4)) noun += 4;
|
|
|
|
const char *status = "partially eaten";
|
|
if (cap > 0) {
|
|
int pct = (left * 100) / cap;
|
|
if (pct >= 75)
|
|
status = "partially eaten";
|
|
else if (pct >= 50)
|
|
status = "half-eaten";
|
|
else
|
|
status = "nearly eaten";
|
|
}
|
|
|
|
bool use_an = FALSE;
|
|
if (status && *status) {
|
|
char first = LOWER((unsigned char)status[0]);
|
|
use_an = (first == 'a' || first == 'e' || first == 'i' || first == 'o' || first == 'u');
|
|
}
|
|
const char *article = use_an ? "an" : "a";
|
|
|
|
char sbuf[MAX_STRING_LENGTH];
|
|
size_t prefix_len = strlen(article) + 1 + strlen(status) + 1;
|
|
size_t max_noun = (sizeof(sbuf) > (prefix_len + 1))
|
|
? (sizeof(sbuf) - prefix_len - 1) : 0;
|
|
snprintf(sbuf, sizeof(sbuf), "%s %s %.*s", article, status, (int)max_noun, noun);
|
|
|
|
if (obj->short_description && obj->short_description != proto_sd)
|
|
free(obj->short_description);
|
|
obj->short_description = strdup(sbuf);
|
|
}
|
|
}
|
|
|
|
ACMD(do_pour)
|
|
{
|
|
char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH];
|
|
struct obj_data *from_obj = NULL, *to_obj = NULL;
|
|
int amount = 0;
|
|
|
|
two_arguments(argument, arg1, arg2);
|
|
|
|
if (subcmd == SCMD_POUR) {
|
|
if (!*arg1) { /* No arguments */
|
|
send_to_char(ch, "From what do you want to pour?\r\n");
|
|
return;
|
|
}
|
|
if (!(from_obj = get_obj_in_list_vis(ch, arg1, NULL, ch->carrying))) {
|
|
send_to_char(ch, "You can't find it!\r\n");
|
|
return;
|
|
}
|
|
if (GET_OBJ_TYPE(from_obj) != ITEM_DRINKCON) {
|
|
send_to_char(ch, "You can't pour from that!\r\n");
|
|
return;
|
|
}
|
|
}
|
|
if (subcmd == SCMD_FILL) {
|
|
if (!*arg1) { /* no arguments */
|
|
send_to_char(ch, "What do you want to fill? And what are you filling it from?\r\n");
|
|
return;
|
|
}
|
|
if (!(to_obj = get_obj_in_list_vis(ch, arg1, NULL, ch->carrying))) {
|
|
send_to_char(ch, "You can't find it!\r\n");
|
|
return;
|
|
}
|
|
if (GET_OBJ_TYPE(to_obj) != ITEM_DRINKCON) {
|
|
act("You can't fill $p!", FALSE, ch, to_obj, 0, TO_CHAR);
|
|
return;
|
|
}
|
|
if (!*arg2) { /* no 2nd argument */
|
|
act("What do you want to fill $p from?", FALSE, ch, to_obj, 0, TO_CHAR);
|
|
return;
|
|
}
|
|
if (!(from_obj = get_obj_in_list_vis(ch, arg2, NULL, world[IN_ROOM(ch)].contents))) {
|
|
send_to_char(ch, "There doesn't seem to be %s %s here.\r\n", AN(arg2), arg2);
|
|
return;
|
|
}
|
|
if (GET_OBJ_TYPE(from_obj) != ITEM_FOUNTAIN) {
|
|
act("You can't fill something from $p.", FALSE, ch, from_obj, 0, TO_CHAR);
|
|
return;
|
|
}
|
|
}
|
|
if (GET_OBJ_VAL(from_obj, 1) == 0) {
|
|
act("The $p is empty.", FALSE, ch, from_obj, 0, TO_CHAR);
|
|
return;
|
|
}
|
|
if (subcmd == SCMD_POUR) { /* pour */
|
|
if (!*arg2) {
|
|
send_to_char(ch, "Where do you want it? Out or in what?\r\n");
|
|
return;
|
|
}
|
|
if (!str_cmp(arg2, "out")) {
|
|
if (GET_OBJ_VAL(from_obj, 0) > 0) {
|
|
act("$n empties $p.", TRUE, ch, from_obj, 0, TO_ROOM);
|
|
act("You empty $p.", FALSE, ch, from_obj, 0, TO_CHAR);
|
|
|
|
weight_change_object(from_obj, -GET_OBJ_VAL(from_obj, 1)); /* Empty */
|
|
|
|
name_from_drinkcon(from_obj);
|
|
GET_OBJ_VAL(from_obj, 1) = 0;
|
|
GET_OBJ_VAL(from_obj, 2) = 0;
|
|
GET_OBJ_VAL(from_obj, 3) = 0;
|
|
}
|
|
else
|
|
send_to_char(ch, "You can't possibly pour that container out!\r\n");
|
|
|
|
return;
|
|
}
|
|
if (!(to_obj = get_obj_in_list_vis(ch, arg2, NULL, ch->carrying))) {
|
|
send_to_char(ch, "You can't find it!\r\n");
|
|
return;
|
|
}
|
|
if ((GET_OBJ_TYPE(to_obj) != ITEM_DRINKCON) &&
|
|
(GET_OBJ_TYPE(to_obj) != ITEM_FOUNTAIN)) {
|
|
send_to_char(ch, "You can't pour anything into that.\r\n");
|
|
return;
|
|
}
|
|
}
|
|
if (to_obj == from_obj) {
|
|
send_to_char(ch, "A most unproductive effort.\r\n");
|
|
return;
|
|
}
|
|
if ((GET_OBJ_VAL(to_obj, 0) < 0) ||
|
|
(!(GET_OBJ_VAL(to_obj, 1) < GET_OBJ_VAL(to_obj, 0)))) {
|
|
send_to_char(ch, "There is already another liquid in it!\r\n");
|
|
return;
|
|
}
|
|
if (!(GET_OBJ_VAL(to_obj, 1) < GET_OBJ_VAL(to_obj, 0))) {
|
|
send_to_char(ch, "There is no room for more.\r\n");
|
|
return;
|
|
}
|
|
if (subcmd == SCMD_POUR)
|
|
send_to_char(ch, "You pour the %s into the %s.", drinks[GET_OBJ_VAL(from_obj, 2)], arg2);
|
|
|
|
if (subcmd == SCMD_FILL) {
|
|
act("You gently fill $p from $P.", FALSE, ch, to_obj, from_obj, TO_CHAR);
|
|
act("$n gently fills $p from $P.", TRUE, ch, to_obj, from_obj, TO_ROOM);
|
|
}
|
|
/* New alias */
|
|
if (GET_OBJ_VAL(to_obj, 1) == 0)
|
|
name_to_drinkcon(to_obj, GET_OBJ_VAL(from_obj, 2));
|
|
|
|
/* First same type liq. */
|
|
GET_OBJ_VAL(to_obj, 2) = GET_OBJ_VAL(from_obj, 2);
|
|
|
|
/* Then how much to pour */
|
|
if (GET_OBJ_VAL(from_obj, 0) > 0) {
|
|
GET_OBJ_VAL(from_obj, 1) -= (amount =
|
|
(GET_OBJ_VAL(to_obj, 0) - GET_OBJ_VAL(to_obj, 1)));
|
|
|
|
GET_OBJ_VAL(to_obj, 1) = GET_OBJ_VAL(to_obj, 0);
|
|
|
|
if (GET_OBJ_VAL(from_obj, 1) < 0) { /* There was too little */
|
|
GET_OBJ_VAL(to_obj, 1) += GET_OBJ_VAL(from_obj, 1);
|
|
amount += GET_OBJ_VAL(from_obj, 1);
|
|
name_from_drinkcon(from_obj);
|
|
GET_OBJ_VAL(from_obj, 1) = 0;
|
|
GET_OBJ_VAL(from_obj, 2) = 0;
|
|
GET_OBJ_VAL(from_obj, 3) = 0;
|
|
}
|
|
}
|
|
else {
|
|
GET_OBJ_VAL(to_obj, 1) = GET_OBJ_VAL(to_obj, 0);
|
|
amount = GET_OBJ_VAL(to_obj, 0);
|
|
}
|
|
/* Poisoned? */
|
|
GET_OBJ_VAL(to_obj, 3) = (GET_OBJ_VAL(to_obj, 3) || GET_OBJ_VAL(from_obj, 3))
|
|
;
|
|
/* Weight change, except for unlimited. */
|
|
if (GET_OBJ_VAL(from_obj, 0) > 0) {
|
|
weight_change_object(from_obj, -amount);
|
|
}
|
|
weight_change_object(to_obj, amount); /* Add weight */
|
|
}
|
|
|
|
static void wear_message(struct char_data *ch, struct obj_data *obj, int where)
|
|
{
|
|
const char *wear_messages[][2] = {
|
|
{"$n lights $p and holds it.",
|
|
"You light $p and hold it."},
|
|
|
|
{"$n slides $p on to $s right ring finger.",
|
|
"You slide $p on to your right ring finger."},
|
|
|
|
{"$n slides $p on to $s left ring finger.",
|
|
"You slide $p on to your left ring finger."},
|
|
|
|
{"$n wears $p around $s neck.",
|
|
"You wear $p around your neck."},
|
|
|
|
{"$n wears $p around $s neck.",
|
|
"You wear $p around your neck."},
|
|
|
|
{"$n straps $p around $s back.",
|
|
"You wear $p on your back."},
|
|
|
|
{"$n wears $p on $s body.",
|
|
"You wear $p on your body."},
|
|
|
|
{"$n wears $p on $s head.",
|
|
"You wear $p on your head."},
|
|
|
|
{"$n puts $p on $s legs.",
|
|
"You put $p on your legs."},
|
|
|
|
{"$n wears $p on $s feet.",
|
|
"You wear $p on your feet."},
|
|
|
|
{"$n puts $p on $s hands.",
|
|
"You put $p on your hands."},
|
|
|
|
{"$n wears $p on $s arms.",
|
|
"You wear $p on your arms."},
|
|
|
|
{"$n straps $p around $s arm as a shield.",
|
|
"You start to use $p as a shield."},
|
|
|
|
{"$n wears $p about $s body.",
|
|
"You wear $p around your body."},
|
|
|
|
{"$n wears $p around $s waist.",
|
|
"You wear $p around your waist."},
|
|
|
|
{"$n puts $p on around $s right wrist.",
|
|
"You put $p on around your right wrist."},
|
|
|
|
{"$n puts $p on around $s left wrist.",
|
|
"You put $p on around your left wrist."},
|
|
|
|
{"$n wields $p.",
|
|
"You wield $p."},
|
|
|
|
{"$n grabs $p.",
|
|
"You grab $p."}
|
|
};
|
|
|
|
act(wear_messages[where][0], TRUE, ch, obj, 0, TO_ROOM);
|
|
act(wear_messages[where][1], FALSE, ch, obj, 0, TO_CHAR);
|
|
}
|
|
|
|
static void perform_wear(struct char_data *ch, struct obj_data *obj, int where)
|
|
{
|
|
/*
|
|
* ITEM_WEAR_TAKE is used for objects that do not require special bits
|
|
* to be put into that position (e.g. you can hold any object, not just
|
|
* an object with a HOLD bit.)
|
|
*/
|
|
|
|
int wear_bitvectors[] = {
|
|
ITEM_WEAR_TAKE, ITEM_WEAR_FINGER, ITEM_WEAR_FINGER, ITEM_WEAR_NECK,
|
|
ITEM_WEAR_NECK, ITEM_WEAR_BACK, ITEM_WEAR_BODY, ITEM_WEAR_HEAD,
|
|
ITEM_WEAR_LEGS, ITEM_WEAR_FEET, ITEM_WEAR_HANDS, ITEM_WEAR_ARMS,
|
|
ITEM_WEAR_SHIELD, ITEM_WEAR_ABOUT, ITEM_WEAR_WAIST, ITEM_WEAR_WRIST,
|
|
ITEM_WEAR_WRIST, ITEM_WEAR_WIELD, ITEM_WEAR_TAKE
|
|
};
|
|
|
|
const char *already_wearing[] = {
|
|
"You're already using a light.\r\n",
|
|
"YOU SHOULD NEVER SEE THIS MESSAGE. PLEASE REPORT.\r\n",
|
|
"You're already wearing something on both of your ring fingers.\r\n",
|
|
"YOU SHOULD NEVER SEE THIS MESSAGE. PLEASE REPORT.\r\n",
|
|
"You can't wear anything else around your neck.\r\n",
|
|
"You can't wear any more on your back.\r\n",
|
|
"You're already wearing something on your body.\r\n",
|
|
"You're already wearing something on your head.\r\n",
|
|
"You're already wearing something on your legs.\r\n",
|
|
"You're already wearing something on your feet.\r\n",
|
|
"You're already wearing something on your hands.\r\n",
|
|
"You're already wearing something on your arms.\r\n",
|
|
"You're already using a shield.\r\n",
|
|
"You're already wearing something about your body.\r\n",
|
|
"You already have something around your waist.\r\n",
|
|
"YOU SHOULD NEVER SEE THIS MESSAGE. PLEASE REPORT.\r\n",
|
|
"You're already wearing something around both of your wrists.\r\n",
|
|
"You're already wielding a weapon.\r\n",
|
|
"You're already holding something.\r\n"
|
|
};
|
|
|
|
/* first, make sure that the wear position is valid. */
|
|
if (!CAN_WEAR(obj, wear_bitvectors[where])) {
|
|
act("You can't wear $p there.", FALSE, ch, obj, 0, TO_CHAR);
|
|
return;
|
|
}
|
|
/* for neck, finger, and wrist, try pos 2 if pos 1 is already full */
|
|
if ((where == WEAR_FINGER_R) || (where == WEAR_NECK_1) || (where == WEAR_WRIST_R))
|
|
if (GET_EQ(ch, where))
|
|
where++;
|
|
|
|
if (GET_EQ(ch, where)) {
|
|
send_to_char(ch, "%s", already_wearing[where]);
|
|
return;
|
|
}
|
|
|
|
/* See if a trigger disallows it */
|
|
if (!wear_otrigger(obj, ch, where) || (obj->carried_by != ch))
|
|
return;
|
|
|
|
wear_message(ch, obj, where);
|
|
obj_from_char(obj);
|
|
equip_char(ch, obj, where);
|
|
}
|
|
|
|
int find_eq_pos(struct char_data *ch, struct obj_data *obj, char *arg)
|
|
{
|
|
int where = -1;
|
|
|
|
const char *keywords[] = {
|
|
"!RESERVED!",
|
|
"finger",
|
|
"!RESERVED!",
|
|
"neck",
|
|
"!RESERVED!",
|
|
"back",
|
|
"body",
|
|
"head",
|
|
"legs",
|
|
"feet",
|
|
"hands",
|
|
"arms",
|
|
"shield",
|
|
"about",
|
|
"waist",
|
|
"wrist",
|
|
"!RESERVED!",
|
|
"!RESERVED!",
|
|
"!RESERVED!",
|
|
"\n"
|
|
};
|
|
|
|
if (!arg || !*arg) {
|
|
if (CAN_WEAR(obj, ITEM_WEAR_FINGER)) where = WEAR_FINGER_R;
|
|
if (CAN_WEAR(obj, ITEM_WEAR_NECK)) where = WEAR_NECK_1;
|
|
if (CAN_WEAR(obj, ITEM_WEAR_BACK)) where = WEAR_BACK;
|
|
if (CAN_WEAR(obj, ITEM_WEAR_BODY)) where = WEAR_BODY;
|
|
if (CAN_WEAR(obj, ITEM_WEAR_HEAD)) where = WEAR_HEAD;
|
|
if (CAN_WEAR(obj, ITEM_WEAR_LEGS)) where = WEAR_LEGS;
|
|
if (CAN_WEAR(obj, ITEM_WEAR_FEET)) where = WEAR_FEET;
|
|
if (CAN_WEAR(obj, ITEM_WEAR_HANDS)) where = WEAR_HANDS;
|
|
if (CAN_WEAR(obj, ITEM_WEAR_ARMS)) where = WEAR_ARMS;
|
|
if (CAN_WEAR(obj, ITEM_WEAR_SHIELD)) where = WEAR_SHIELD;
|
|
if (CAN_WEAR(obj, ITEM_WEAR_ABOUT)) where = WEAR_ABOUT;
|
|
if (CAN_WEAR(obj, ITEM_WEAR_WAIST)) where = WEAR_WAIST;
|
|
if (CAN_WEAR(obj, ITEM_WEAR_WRIST)) where = WEAR_WRIST_R;
|
|
} else if ((where = search_block(arg, keywords, FALSE)) < 0)
|
|
send_to_char(ch, "'%s'? What part of your body is THAT?\r\n", arg);
|
|
|
|
return (where);
|
|
}
|
|
|
|
ACMD(do_wear)
|
|
{
|
|
char arg1[MAX_INPUT_LENGTH];
|
|
char arg2[MAX_INPUT_LENGTH];
|
|
struct obj_data *obj, *next_obj;
|
|
int where, dotmode, items_worn = 0;
|
|
|
|
two_arguments(argument, arg1, arg2);
|
|
|
|
if (!*arg1) {
|
|
send_to_char(ch, "Wear what?\r\n");
|
|
return;
|
|
}
|
|
dotmode = find_all_dots(arg1);
|
|
|
|
if (*arg2 && (dotmode != FIND_INDIV)) {
|
|
send_to_char(ch, "You can't specify the same body location for more than one item!\r\n");
|
|
return;
|
|
}
|
|
if (dotmode == FIND_ALL) {
|
|
for (obj = ch->carrying; obj; obj = next_obj) {
|
|
next_obj = obj->next_content;
|
|
if (CAN_SEE_OBJ(ch, obj) && (where = find_eq_pos(ch, obj, 0)) >= 0) {
|
|
if (GET_LEVEL(ch) < GET_OBJ_LEVEL(obj))
|
|
send_to_char(ch, "You are not experienced enough to use that.\r\n");
|
|
else {
|
|
items_worn++;
|
|
perform_wear(ch, obj, where);
|
|
}
|
|
}
|
|
}
|
|
if (!items_worn)
|
|
send_to_char(ch, "You don't seem to have anything wearable.\r\n");
|
|
} else if (dotmode == FIND_ALLDOT) {
|
|
if (!*arg1) {
|
|
send_to_char(ch, "Wear all of what?\r\n");
|
|
return;
|
|
}
|
|
if (!(obj = get_obj_in_list_vis(ch, arg1, NULL, ch->carrying)))
|
|
send_to_char(ch, "You don't seem to have any %ss.\r\n", arg1);
|
|
else if (GET_LEVEL(ch) < GET_OBJ_LEVEL(obj))
|
|
send_to_char(ch, "You are not experienced enough to use that.\r\n");
|
|
else
|
|
while (obj) {
|
|
next_obj = get_obj_in_list_vis(ch, arg1, NULL, obj->next_content);
|
|
if ((where = find_eq_pos(ch, obj, 0)) >= 0)
|
|
perform_wear(ch, obj, where);
|
|
else
|
|
act("You can't wear $p.", FALSE, ch, obj, 0, TO_CHAR);
|
|
obj = next_obj;
|
|
}
|
|
} else {
|
|
if (!(obj = get_obj_in_list_vis(ch, arg1, NULL, ch->carrying)))
|
|
send_to_char(ch, "You don't seem to have %s %s.\r\n", AN(arg1), arg1);
|
|
else if (GET_LEVEL(ch) < GET_OBJ_LEVEL(obj))
|
|
send_to_char(ch, "You are not experienced enough to use that.\r\n");
|
|
else {
|
|
if ((where = find_eq_pos(ch, obj, arg2)) >= 0)
|
|
perform_wear(ch, obj, where);
|
|
else if (!*arg2)
|
|
act("You can't wear $p.", FALSE, ch, obj, 0, TO_CHAR);
|
|
}
|
|
}
|
|
}
|
|
|
|
ACMD(do_wield)
|
|
{
|
|
char arg[MAX_INPUT_LENGTH];
|
|
struct obj_data *obj;
|
|
|
|
one_argument(argument, arg);
|
|
|
|
if (!*arg)
|
|
send_to_char(ch, "Wield what?\r\n");
|
|
else if (!(obj = get_obj_in_list_vis(ch, arg, NULL, ch->carrying)))
|
|
send_to_char(ch, "You don't seem to have %s %s.\r\n", AN(arg), arg);
|
|
else {
|
|
if (!CAN_WEAR(obj, ITEM_WEAR_WIELD))
|
|
send_to_char(ch, "You can't wield that.\r\n");
|
|
else if (GET_OBJ_WEIGHT(obj) > CAN_WIELD_W(ch))
|
|
send_to_char(ch, "It's too heavy for you to use.\r\n");
|
|
else if (GET_LEVEL(ch) < GET_OBJ_LEVEL(obj))
|
|
send_to_char(ch, "You are not experienced enough to use that.\r\n");
|
|
else
|
|
perform_wear(ch, obj, WEAR_WIELD);
|
|
}
|
|
}
|
|
|
|
ACMD(do_grab)
|
|
{
|
|
char arg[MAX_INPUT_LENGTH];
|
|
struct obj_data *obj;
|
|
|
|
one_argument(argument, arg);
|
|
|
|
if (!*arg)
|
|
send_to_char(ch, "Hold what?\r\n");
|
|
else if (!(obj = get_obj_in_list_vis(ch, arg, NULL, ch->carrying)))
|
|
send_to_char(ch, "You don't seem to have %s %s.\r\n", AN(arg), arg);
|
|
else if (GET_LEVEL(ch) < GET_OBJ_LEVEL(obj))
|
|
send_to_char(ch, "You are not experienced enough to use that.\r\n");
|
|
else {
|
|
if (GET_OBJ_TYPE(obj) == ITEM_LIGHT)
|
|
perform_wear(ch, obj, WEAR_LIGHT);
|
|
else {
|
|
if (!CAN_WEAR(obj, ITEM_WEAR_HOLD) && GET_OBJ_TYPE(obj) != ITEM_WAND &&
|
|
GET_OBJ_TYPE(obj) != ITEM_STAFF && GET_OBJ_TYPE(obj) != ITEM_SCROLL &&
|
|
GET_OBJ_TYPE(obj) != ITEM_POTION)
|
|
send_to_char(ch, "You can't hold that.\r\n");
|
|
else
|
|
perform_wear(ch, obj, WEAR_HOLD);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void perform_remove(struct char_data *ch, int pos)
|
|
{
|
|
struct obj_data *obj;
|
|
|
|
if (!(obj = GET_EQ(ch, pos)))
|
|
log("SYSERR: perform_remove: bad pos %d passed.", pos);
|
|
/* This error occurs when perform_remove() is passed a bad 'pos'
|
|
* (location) to remove an object from. */
|
|
else if (OBJ_FLAGGED(obj, ITEM_NODROP) && !PRF_FLAGGED(ch, PRF_NOHASSLE))
|
|
act("You can't remove $p, it must be CURSED!", FALSE, ch, obj, 0, TO_CHAR);
|
|
else if (IS_CARRYING_N(ch) >= CAN_CARRY_N(ch)&& !PRF_FLAGGED(ch, PRF_NOHASSLE))
|
|
act("$p: you can't carry that many items!", FALSE, ch, obj, 0, TO_CHAR);
|
|
else {
|
|
if (!remove_otrigger(obj, ch))
|
|
return;
|
|
|
|
obj_to_char(unequip_char(ch, pos), ch);
|
|
act("You stop using $p.", FALSE, ch, obj, 0, TO_CHAR);
|
|
act("$n stops using $p.", TRUE, ch, obj, 0, TO_ROOM);
|
|
}
|
|
}
|
|
|
|
ACMD(do_remove)
|
|
{
|
|
char arg[MAX_INPUT_LENGTH];
|
|
int i, dotmode, found;
|
|
|
|
one_argument(argument, arg);
|
|
|
|
if (!*arg) {
|
|
send_to_char(ch, "Remove what?\r\n");
|
|
return;
|
|
}
|
|
dotmode = find_all_dots(arg);
|
|
|
|
if (dotmode == FIND_ALL) {
|
|
found = 0;
|
|
for (i = 0; i < NUM_WEARS; i++)
|
|
if (GET_EQ(ch, i)) {
|
|
perform_remove(ch, i);
|
|
found = 1;
|
|
}
|
|
if (!found)
|
|
send_to_char(ch, "You're not using anything.\r\n");
|
|
} else if (dotmode == FIND_ALLDOT) {
|
|
if (!*arg)
|
|
send_to_char(ch, "Remove all of what?\r\n");
|
|
else {
|
|
found = 0;
|
|
for (i = 0; i < NUM_WEARS; i++)
|
|
if (GET_EQ(ch, i) && CAN_SEE_OBJ(ch, GET_EQ(ch, i)) &&
|
|
isname(arg, GET_EQ(ch, i)->name)) {
|
|
perform_remove(ch, i);
|
|
found = 1;
|
|
}
|
|
if (!found)
|
|
send_to_char(ch, "You don't seem to be using any %ss.\r\n", arg);
|
|
}
|
|
} else {
|
|
if ((i = get_obj_pos_in_equip_vis(ch, arg, NULL, ch->equipment)) < 0)
|
|
send_to_char(ch, "You don't seem to be using %s %s.\r\n", AN(arg), arg);
|
|
else
|
|
perform_remove(ch, i);
|
|
}
|
|
}
|