mirror of
https://github.com/tbamud/tbamud.git
synced 2026-04-02 18:37:19 +02:00
2202 lines
70 KiB
C
2202 lines
70 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 "py_triggers.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 bool merge_money_pile(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_coins(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_coins(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);
|
||
int cap = 0;
|
||
|
||
if (!drop_otrigger(obj, ch))
|
||
return;
|
||
|
||
if (!has_obj_by_uid_in_lookup_table(object_id)) /* object might be extracted by drop_otrigger */
|
||
return;
|
||
|
||
/* --- Storage target validation (containers + storage WORN) --- */
|
||
if (!obj_is_storage(cont)) {
|
||
act("$P is not a container.", FALSE, ch, obj, cont, TO_CHAR);
|
||
return;
|
||
}
|
||
|
||
/* Prevent putting items into closed storage (matches container UX). */
|
||
if (obj_storage_is_closed(cont) &&
|
||
(GET_LEVEL(ch) < LVL_IMMORT || !PRF_FLAGGED(ch, PRF_NOHASSLE))) {
|
||
act("$P seems to be closed.", FALSE, ch, obj, cont, TO_CHAR);
|
||
return;
|
||
}
|
||
|
||
/* Capacity: containers use value[0]; worn storage uses WORN_CAPACITY. */
|
||
if (GET_OBJ_TYPE(cont) == ITEM_WORN)
|
||
cap = GET_OBJ_VAL(cont, WORN_CAPACITY);
|
||
else
|
||
cap = GET_OBJ_VAL(cont, 0);
|
||
|
||
if ((cap > 0) && (GET_OBJ_WEIGHT(cont) + GET_OBJ_WEIGHT(obj) > cap))
|
||
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 {
|
||
if (obj->carried_by)
|
||
obj_from_char(obj);
|
||
else if (obj->in_obj)
|
||
obj_from_obj(obj);
|
||
else if (IN_ROOM(obj) != NOWHERE)
|
||
obj_from_room(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 load. -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;
|
||
int amount_specified = 0;
|
||
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;
|
||
amount_specified = 1;
|
||
} 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 (obj_is_storage(cont)) {
|
||
/* Handle container-like logic (containers + storage WORN) */
|
||
if (obj_storage_is_closed(cont) &&
|
||
(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 {
|
||
if (amount_specified && GET_OBJ_TYPE(obj) == ITEM_MONEY && howmany > 0) {
|
||
int pile = GET_OBJ_VAL(obj, 0);
|
||
if (howmany < pile) {
|
||
struct obj_data *split = create_money(howmany);
|
||
if (!split) {
|
||
send_to_char(ch, "You fumble the coins.\r\n");
|
||
return;
|
||
}
|
||
GET_OBJ_VAL(obj, 0) = pile - howmany;
|
||
update_money_obj(obj);
|
||
GET_COINS(ch) = MAX(0, GET_COINS(ch) - howmany);
|
||
obj = split;
|
||
howmany = 1;
|
||
} else {
|
||
howmany = 1;
|
||
}
|
||
}
|
||
while (obj && howmany) {
|
||
next_obj = obj->next_content;
|
||
if (obj != cont) {
|
||
howmany--;
|
||
perform_put(ch, obj, cont); /* must be updated to accept storage WORN */
|
||
}
|
||
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); /* must be updated to accept storage WORN */
|
||
}
|
||
}
|
||
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 (GET_OBJ_TYPE(obj) == ITEM_MONEY)
|
||
update_money_obj(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)
|
||
{
|
||
if (GET_OBJ_TYPE(obj) != ITEM_MONEY)
|
||
return;
|
||
|
||
update_money_obj(obj);
|
||
}
|
||
|
||
static bool merge_money_pile(struct char_data *ch, struct obj_data *obj)
|
||
{
|
||
struct obj_data *target;
|
||
int coins;
|
||
|
||
if (!ch || !obj || GET_OBJ_TYPE(obj) != ITEM_MONEY)
|
||
return FALSE;
|
||
|
||
for (target = ch->carrying; target; target = target->next_content) {
|
||
if (target != obj && GET_OBJ_TYPE(target) == ITEM_MONEY)
|
||
break;
|
||
}
|
||
|
||
if (!target)
|
||
return FALSE;
|
||
|
||
coins = MAX(0, GET_OBJ_VAL(obj, 0));
|
||
if (coins <= 0)
|
||
return FALSE;
|
||
|
||
GET_OBJ_VAL(target, 0) += coins;
|
||
update_money_obj(target);
|
||
GET_COINS(ch) = MIN(MAX_COINS, GET_COINS(ch) + coins);
|
||
extract_obj(obj);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
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)) {
|
||
int coin_amt = (GET_OBJ_TYPE(obj) == ITEM_MONEY) ? GET_OBJ_VAL(obj, 0) : 0;
|
||
|
||
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);
|
||
if (!merge_money_pile(ch, obj))
|
||
get_check_money(ch, obj);
|
||
if (coin_amt == 1)
|
||
send_to_char(ch, "There was one coin.\r\n");
|
||
else if (coin_amt > 1)
|
||
send_to_char(ch, "There were %d coins.\r\n", coin_amt);
|
||
}
|
||
}
|
||
}
|
||
|
||
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);
|
||
|
||
/* Allow both ITEM_CONTAINER and storage-capable ITEM_WORN */
|
||
if (!obj_is_storage(cont)) {
|
||
act("$p is not a container.", FALSE, ch, cont, 0, TO_CHAR);
|
||
return;
|
||
}
|
||
|
||
if (obj_storage_is_closed(cont) &&
|
||
(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)) {
|
||
int coin_amt = (GET_OBJ_TYPE(obj) == ITEM_MONEY) ? GET_OBJ_VAL(obj, 0) : 0;
|
||
|
||
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);
|
||
if (!merge_money_pile(ch, obj))
|
||
get_check_money(ch, obj);
|
||
if (coin_amt == 1)
|
||
send_to_char(ch, "There was one coin.\r\n");
|
||
else if (coin_amt > 1)
|
||
send_to_char(ch, "There were %d coins.\r\n", coin_amt);
|
||
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 (!obj_is_storage(cont) && 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); /* must be updated */
|
||
} 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 (obj_is_storage(cont) || 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 (obj_is_storage(cont) || 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_coins(struct char_data *ch, int amount, byte mode, room_rnum RDR)
|
||
{
|
||
struct obj_data *obj;
|
||
int removed;
|
||
|
||
if (amount <= 0)
|
||
send_to_char(ch, "Heh heh heh.. we are jolly funny today, eh?\r\n");
|
||
else if (GET_COINS(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;
|
||
}
|
||
|
||
removed = remove_coins_from_char(ch, amount);
|
||
if (removed != amount) {
|
||
if (has_obj_by_uid_in_lookup_table(object_id))
|
||
extract_obj(obj);
|
||
send_to_char(ch, "You don't have that many coins!\r\n");
|
||
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 coins.\r\n");
|
||
obj_to_room(obj, IN_ROOM(ch));
|
||
} else {
|
||
char buf[MAX_STRING_LENGTH];
|
||
|
||
removed = remove_coins_from_char(ch, amount);
|
||
if (removed != amount) {
|
||
send_to_char(ch, "You don't have that many coins!\r\n");
|
||
return;
|
||
}
|
||
|
||
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 coins.\r\n");
|
||
}
|
||
}
|
||
}
|
||
|
||
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_coins(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_coins(struct char_data *ch, struct char_data *vict,
|
||
int amount)
|
||
{
|
||
char buf[MAX_STRING_LENGTH];
|
||
struct obj_data *money;
|
||
|
||
if (amount <= 0) {
|
||
send_to_char(ch, "Heh heh heh ... we are jolly funny today, eh?\r\n");
|
||
return;
|
||
}
|
||
if ((GET_COINS(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);
|
||
|
||
money = create_money(amount);
|
||
if (!money)
|
||
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);
|
||
extract_obj(money);
|
||
return;
|
||
}
|
||
if (GET_OBJ_WEIGHT(money) + 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);
|
||
extract_obj(money);
|
||
return;
|
||
}
|
||
|
||
snprintf(buf, sizeof(buf), "$n gives you %d 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)) {
|
||
if (remove_coins_from_char(ch, amount) != amount) {
|
||
send_to_char(ch, "You don't have that many coins!\r\n");
|
||
extract_obj(money);
|
||
return;
|
||
}
|
||
}
|
||
|
||
obj_to_char(money, vict);
|
||
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_coins(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 (!str_cmp(arg, "coins") || !str_cmp(arg, "coin")) {
|
||
send_to_char(ch, "How many coins do you want to give?\r\n");
|
||
return;
|
||
}
|
||
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_ch = obj->worn_by)) {
|
||
IS_CARRYING_W(tmp_ch) += weight;
|
||
GET_OBJ_WEIGHT(obj) += weight;
|
||
} 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);
|
||
}
|
||
}
|
||
|
||
ACMD(do_raise_lower_hood)
|
||
{
|
||
char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH];
|
||
struct obj_data *obj = NULL;
|
||
int j;
|
||
|
||
two_arguments(argument, arg1, arg2);
|
||
|
||
/* Must be exactly: "raise hood" or "lower hood" */
|
||
if (!*arg1 || str_cmp(arg1, "hood")) {
|
||
send_to_char(ch, "Usage: %s hood\r\n", (subcmd == SCMD_RAISE_HOOD) ? "raise" : "lower");
|
||
return;
|
||
}
|
||
|
||
/* Find a hooded worn item in equipment. Prefer:
|
||
- for lower: one that is currently up
|
||
- for raise: one that is currently down
|
||
*/
|
||
if (subcmd == SCMD_LOWER_HOOD) {
|
||
for (j = 0; j < NUM_WEARS; j++) {
|
||
obj = GET_EQ(ch, j);
|
||
if (!obj) continue;
|
||
if (GET_OBJ_TYPE(obj) != ITEM_WORN) continue;
|
||
if (GET_OBJ_VAL(obj, WORN_CAN_HOOD) != 1) continue;
|
||
if (IS_SET_AR(GET_OBJ_EXTRA(obj), ITEM_HOOD_UP))
|
||
break;
|
||
obj = NULL;
|
||
}
|
||
}
|
||
|
||
if (!obj && subcmd == SCMD_RAISE_HOOD) {
|
||
for (j = 0; j < NUM_WEARS; j++) {
|
||
obj = GET_EQ(ch, j);
|
||
if (!obj) continue;
|
||
if (GET_OBJ_TYPE(obj) != ITEM_WORN) continue;
|
||
if (GET_OBJ_VAL(obj, WORN_CAN_HOOD) != 1) continue;
|
||
if (!IS_SET_AR(GET_OBJ_EXTRA(obj), ITEM_HOOD_UP))
|
||
break;
|
||
obj = NULL;
|
||
}
|
||
}
|
||
|
||
/* If we didn’t find an ideal target, fall back to any hooded worn item. */
|
||
if (!obj) {
|
||
for (j = 0; j < NUM_WEARS; j++) {
|
||
obj = GET_EQ(ch, j);
|
||
if (!obj) continue;
|
||
if (GET_OBJ_TYPE(obj) != ITEM_WORN) continue;
|
||
if (GET_OBJ_VAL(obj, WORN_CAN_HOOD) != 1) continue;
|
||
break;
|
||
}
|
||
if (j >= NUM_WEARS) obj = NULL;
|
||
}
|
||
|
||
if (!obj) {
|
||
send_to_char(ch, "You aren't wearing anything with a hood.\r\n");
|
||
return;
|
||
}
|
||
|
||
if (subcmd == SCMD_RAISE_HOOD) {
|
||
if (GET_OBJ_VAL(obj, WORN_HOOD_UP_STATE) == 1) {
|
||
send_to_char(ch, "Your hood is already up.\r\n");
|
||
return;
|
||
}
|
||
|
||
GET_OBJ_VAL(obj, WORN_HOOD_UP_STATE) = 1;
|
||
SET_BIT_AR(GET_OBJ_EXTRA(obj), ITEM_HOOD_UP); /* optional mirror */
|
||
|
||
send_to_char(ch, "You raise your hood.\r\n");
|
||
act("$n raises $s hood.", FALSE, ch, 0, 0, TO_ROOM);
|
||
return;
|
||
}
|
||
|
||
/* SCMD_LOWER_HOOD */
|
||
if (GET_OBJ_VAL(obj, WORN_HOOD_UP_STATE) == 0) {
|
||
send_to_char(ch, "Your hood is already down.\r\n");
|
||
return;
|
||
}
|
||
|
||
GET_OBJ_VAL(obj, WORN_HOOD_UP_STATE) = 0;
|
||
REMOVE_BIT_AR(GET_OBJ_EXTRA(obj), ITEM_HOOD_UP); /* optional mirror */
|
||
|
||
send_to_char(ch, "You lower your hood.\r\n");
|
||
act("$n lowers $s hood.", FALSE, ch, 0, 0, TO_ROOM);
|
||
}
|
||
|
||
static void dump_obj_contents_to_room(struct obj_data *container, room_rnum room)
|
||
{
|
||
struct obj_data *obj, *next_obj;
|
||
|
||
if (!container || room == NOWHERE)
|
||
return;
|
||
|
||
for (obj = container->contains; obj; obj = next_obj) {
|
||
next_obj = obj->next_content;
|
||
obj_from_obj(obj);
|
||
obj_to_room(obj, room);
|
||
}
|
||
}
|
||
|
||
static int is_corpse_obj(struct obj_data *obj)
|
||
{
|
||
if (!obj)
|
||
return 0;
|
||
return (GET_OBJ_TYPE(obj) == ITEM_CONTAINER && GET_OBJ_VAL(obj, 3) == 1);
|
||
}
|
||
|
||
ACMD(do_skin)
|
||
{
|
||
char arg[MAX_INPUT_LENGTH];
|
||
struct obj_data *corpse = NULL;
|
||
struct skin_yield_entry *y;
|
||
room_rnum room;
|
||
mob_vnum mvnum;
|
||
mob_rnum mrnum;
|
||
int d20, total, successes = 0;
|
||
int number = 1;
|
||
|
||
one_argument(argument, arg);
|
||
|
||
if (!*arg) {
|
||
send_to_char(ch, "Skin what?\r\n");
|
||
return;
|
||
}
|
||
|
||
/* Prefer room first, then inventory. */
|
||
number = 1;
|
||
corpse = get_obj_in_list_vis(ch, arg, &number, world[IN_ROOM(ch)].contents);
|
||
|
||
if (!corpse) {
|
||
number = 1;
|
||
corpse = get_obj_in_list_vis(ch, arg, &number, ch->carrying);
|
||
}
|
||
|
||
if (!corpse) {
|
||
send_to_char(ch, "You don't see that here.\r\n");
|
||
return;
|
||
}
|
||
|
||
if (!is_corpse_obj(corpse)) {
|
||
send_to_char(ch, "You can't skin that.\r\n");
|
||
return;
|
||
}
|
||
|
||
room = IN_ROOM(ch);
|
||
if (room == NOWHERE) {
|
||
send_to_char(ch, "You can't do that here.\r\n");
|
||
return;
|
||
}
|
||
|
||
mvnum = corpse->corpse_mob_vnum;
|
||
if (mvnum <= 0) {
|
||
send_to_char(ch, "You aren't able to cut anything useful from the corpse.\r\n");
|
||
dump_obj_contents_to_room(corpse, room);
|
||
extract_obj(corpse);
|
||
return;
|
||
}
|
||
|
||
mrnum = real_mobile(mvnum);
|
||
if (mrnum < 0 || !mob_index[mrnum].skin_yields) {
|
||
send_to_char(ch, "You aren't able to cut anything useful from the corpse.\r\n");
|
||
dump_obj_contents_to_room(corpse, room);
|
||
extract_obj(corpse);
|
||
return;
|
||
}
|
||
|
||
total = roll_skill_check(ch, SKILL_SURVIVAL, 0, &d20);
|
||
|
||
if (d20 == 1) {
|
||
send_to_char(ch, "You aren't able to cut anything useful from the corpse.\r\n");
|
||
dump_obj_contents_to_room(corpse, room);
|
||
extract_obj(corpse);
|
||
return;
|
||
}
|
||
|
||
/* Evaluate configured yields (if any). */
|
||
for (y = mob_index[mrnum].skin_yields; y; y = y->next) {
|
||
if (total >= y->dc) {
|
||
struct obj_data *o = read_object(y->obj_vnum, VIRTUAL);
|
||
if (o) {
|
||
obj_to_room(o, room);
|
||
successes++;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (successes == 0) {
|
||
send_to_char(ch, "You aren't able to cut anything useful from the corpse.\r\n");
|
||
} else {
|
||
act("You skin $p, cutting away anything useful.", FALSE, ch, corpse, 0, TO_CHAR);
|
||
act("$n skins $p, cutting away anything useful.", FALSE, ch, corpse, 0, TO_ROOM);
|
||
}
|
||
|
||
dump_obj_contents_to_room(corpse, room);
|
||
|
||
extract_obj(corpse);
|
||
}
|
||
|
||
ACMD(do_forage)
|
||
{
|
||
room_rnum room;
|
||
struct forage_entry *entry, *best = NULL;
|
||
struct obj_data *obj;
|
||
int total;
|
||
int best_dc = -1;
|
||
int prof_bonus, cost;
|
||
int delay_seconds;
|
||
|
||
room = IN_ROOM(ch);
|
||
if (room == NOWHERE) {
|
||
send_to_char(ch, "You can't do that here.\r\n");
|
||
return;
|
||
}
|
||
|
||
prof_bonus = GET_PROFICIENCY(GET_SKILL(ch, SKILL_SURVIVAL));
|
||
cost = MAX(1, 10 - prof_bonus);
|
||
|
||
if (!IS_NPC(ch) && GET_STAMINA(ch) < cost) {
|
||
send_to_char(ch, "You are too exhausted to forage.\r\n");
|
||
return;
|
||
}
|
||
|
||
delay_seconds = rand_number(8, 12);
|
||
WAIT_STATE(ch, delay_seconds * PASSES_PER_SEC);
|
||
|
||
if (!IS_NPC(ch))
|
||
GET_STAMINA(ch) = MAX(0, GET_STAMINA(ch) - cost);
|
||
|
||
total = roll_skill_check(ch, SKILL_SURVIVAL, 0, NULL);
|
||
|
||
for (entry = world[room].forage; entry; entry = entry->next) {
|
||
if (total >= entry->dc && entry->dc > best_dc) {
|
||
best = entry;
|
||
best_dc = entry->dc;
|
||
}
|
||
}
|
||
|
||
if (best) {
|
||
obj = read_object(best->obj_vnum, VIRTUAL);
|
||
if (obj) {
|
||
obj_to_char(obj, ch);
|
||
act("You take some time to look around, and end up finding $p.", FALSE, ch, obj, 0, TO_CHAR);
|
||
act("$n takes some time to look around, and ends up finding $p.", FALSE, ch, obj, 0, TO_ROOM);
|
||
gain_skill(ch, "survival", TRUE);
|
||
return;
|
||
}
|
||
}
|
||
|
||
send_to_char(ch, "You take some time to look around, but don't find anything.\r\n");
|
||
act("$n takes some time to look around, but doesn't find anything.", FALSE, ch, 0, 0, TO_ROOM);
|
||
gain_skill(ch, "survival", FALSE);
|
||
}
|