/*
* This file is part of Deliantra, the Roguelike Realtime MMORPG.
*
* Copyright (©) 2005,2006,2007,2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
* Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
* Copyright (©) 1992,2007 Frank Tore Johansen
*
* Deliantra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* The authors can be reached via e-mail to
*/
/*
* Object (handling) commands
*/
#include
#include
#include
#include
#include
#include
/*
* Object id parsing functions
*/
#define ADD_ITEM(NEW,COUNT)\
if(!first) {\
first = new objectlink;\
last=first;\
} else {\
last->next = new objectlink;\
last=last->next;\
}\
last->next=0;\
last->ob=(NEW);\
last->id=(COUNT);
/**
* Search the inventory of 'pl' for what matches best with params.
* we use item_matched_string above - this gives us consistent behaviour
* between many commands. Return the best match, or NULL if no match.
* aflag is used with apply -u , and apply -a to
* only unapply applied, or apply unapplied objects
**/
static object *
find_best_apply_object_match (object *pl, const char *params, enum apply_flag aflag)
{
object *best = 0;
int match_val = 0;
for (object *tmp = pl->inv; tmp; tmp = tmp->below)
{
if (tmp->invisible)
continue;
int tmpmatch = item_matched_string (pl, tmp, params);
if (tmpmatch > match_val)
{
if ((aflag == AP_APPLY) && (QUERY_FLAG (tmp, FLAG_APPLIED)))
continue;
if ((aflag == AP_UNAPPLY) && (!QUERY_FLAG (tmp, FLAG_APPLIED)))
continue;
match_val = tmpmatch;
best = tmp;
}
}
return best;
}
/**
* Shortcut to find_best_apply_object_match(pl, params, AF_NULL);
**/
object *
find_best_object_match (object *pl, const char *params)
{
return find_best_apply_object_match (pl, params, AP_TOGGLE);
}
static bool
can_split (object *pl, object *&op, sint32 nrof)
{
if (object *tmp = op->split (nrof ? nrof : op->number_of ()))
{
op = tmp;
return true;
}
else
{
if (op->nrof > 1)
new_draw_info_format (NDI_UNIQUE, 0, pl, "There are only %d %s.", op->nrof, &op->name_pl);
else
new_draw_info_format (NDI_UNIQUE, 0, pl, "There is only one %s.", &op->name);
return false;
}
}
int
command_uskill (object *pl, char *params)
{
if (!params)
{
new_draw_info (NDI_UNIQUE, 0, pl, "Usage: use_skill ");
return 0;
}
return use_skill (pl, params);
}
int
command_rskill (object *pl, char *params)
{
object *skill;
if (!params)
{
new_draw_info (NDI_UNIQUE, 0, pl, "Usage: ready_skill ");
return 0;
}
skill = find_skill_by_name (pl, params);
if (!skill)
{
new_draw_info_format (NDI_UNIQUE, 0, pl, "You have no knowledge of the skill %s", params);
return 0;
}
pl->change_skill (0);
apply_special (pl, skill, AP_APPLY);
return 1;
}
/* These functions (command_search, command_disarm) are really just wrappers for
* things like 'use_skill ...'). In fact, they should really be obsoleted
* and replaced with those.
*/
int
command_search (object *op, char *params)
{
return use_skill (op, skill_names[SK_FIND_TRAPS]);
}
int
command_disarm (object *op, char *params)
{
return use_skill (op, skill_names[SK_DISARM_TRAPS]);
}
/* A little special because we do want to pass the full params along
* as it includes the object to throw.
*/
int
command_throw (object *op, char *params)
{
if (object *skop = find_skill_by_name (op, skill_names[SK_THROWING]))
return do_skill (op, op, skop, op->facing, params);
else
new_draw_info (NDI_UNIQUE, 0, op, "You have no knowledge of the skill throwing.");
return 0;
}
int
command_apply (object *op, char *params)
{
if (!params)
{
player_apply_below (op);
return 0;
}
else
{
apply_flag aflag = (apply_flag)0;
while (*params == ' ')
params++;
if (!strncmp (params, "-a ", 3))
{
aflag = AP_APPLY;
params += 3;
}
if (!strncmp (params, "-u ", 3))
{
aflag = AP_UNAPPLY;
params += 3;
}
while (*params == ' ')
params++;
if (object *inv = find_best_apply_object_match (op, params, aflag))
player_apply (op, inv, aflag, 0);
else
new_draw_info_format (NDI_UNIQUE, 0, op, "Could not find any match to the %s.", params);
}
return 0;
}
/*
* Check if an item op can be put into a sack. If pl exists then tell
* a player the reason of failure.
* returns 1 if it will fit, 0 if it will not. nrof is the number of
* objects (op) we want to put in. We specify it separately instead of
* using op->nrof because often times, a player may have specified a
* certain number of objects to drop, so we can pass that number, and
* not need to use split_ob and stuff.
*/
int
sack_can_hold (object *pl, object *sack, object *op, uint32 nrof)
{
if (!QUERY_FLAG (sack, FLAG_APPLIED))
{
new_draw_info_format (NDI_UNIQUE, 0, pl, "The %s is not active.", query_name (sack));
return 0;
}
if (sack == op)
{
new_draw_info_format (NDI_UNIQUE, 0, pl, "You can't put the %s into itself.", query_name (sack));
return 0;
}
if (sack->race && (sack->race != op->race || op->type == CONTAINER || (sack->stats.food && sack->stats.food != op->type)))
{
new_draw_info_format (NDI_UNIQUE, 0, pl, "You can put only %s into the %s.", &sack->race, query_name (sack));
return 0;
}
if (op->type == SPECIAL_KEY && sack->slaying && op->slaying)
{
new_draw_info_format (NDI_UNIQUE, 0, pl, "You can't put the key into %s.", query_name (sack));
return 0;
}
if (sack->weight_limit && (sint32) (sack->carrying + (nrof ? nrof : 1) *
(op->weight + (op->type == CONTAINER ? (op->carrying * op->stats.Str) : 0))
* (100 - sack->stats.Str) / 100) > sack->weight_limit)
{
new_draw_info_format (NDI_UNIQUE, 0, pl, "That won't fit in the %s!", query_name (sack));
return 0;
}
/* All other checks pass, must be OK */
return 1;
}
/* Pick up commands follow */
/* pl = player (not always - monsters can use this now)
* op is the object to put tmp into,
* tmp is the object to pick up, nrof is the number to
* pick up (0 means all of them)
*/
static void
pick_up_object (object *pl, object *op, object *tmp, int nrof)
{
object *env = tmp->env;
uint32 weight, effective_weight_limit;
int tmp_nrof = tmp->number_of ();
/* IF the player is flying & trying to take the item out of a container
* that is in his inventory, let him. tmp->env points to the container
* (sack, luggage, etc), tmp->env->env then points to the player (nested
* containers not allowed as of now)
*/
if ((pl->move_type & MOVE_FLYING) && !QUERY_FLAG (pl, FLAG_WIZ) && tmp->in_player () != pl)
{
pl->failmsg ("You are levitating, you can't reach the ground! "
"H");
return;
}
if (QUERY_FLAG (tmp, FLAG_NO_DROP))
return;
if (nrof > tmp_nrof || nrof <= 0)
nrof = tmp_nrof;
/* Figure out how much weight this object will add to the player */
weight = tmp->weight * nrof;
if (tmp->inv)
weight += tmp->carrying * (100 - tmp->stats.Str) / 100;
effective_weight_limit = weight_limit [min (MAX_STAT, pl->stats.Str)];
if ((pl->weight + pl->carrying + weight) > effective_weight_limit)
{
new_draw_info (0, 0, pl, "That item is too heavy for you to pick up.");
return;
}
if (!can_split (pl, tmp, nrof))
return;
if (QUERY_FLAG (tmp, FLAG_UNPAID))
{
tmp->flag.reset (FLAG_UNPAID);
new_draw_info_format (NDI_UNIQUE, 0, pl, "%s will cost you %s.", query_name (tmp), query_cost_string (tmp, pl, F_BUY | F_SHOP));
tmp->flag.set (FLAG_UNPAID);
}
else
new_draw_info_format (NDI_UNIQUE, 0, pl, "You pick up the %s.", query_name (tmp));
op->insert (tmp);
}
/* modified slightly to allow monsters use this -b.t. 5-31-95 */
void
pick_up (object *op, object *alt)
{
int need_fix_tmp = 0;
object *tmp = NULL;
maptile *tmp_map = NULL;
int count;
/* Decide which object to pick. */
if (alt)
{
if (!can_pick (op, alt))
{
new_draw_info_format (NDI_UNIQUE, 0, op, "You can't pick up the %s.", &alt->name);
goto leave;
}
tmp = alt;
}
else
{
if (op->below == NULL || !can_pick (op, op->below))
{
new_draw_info (NDI_UNIQUE, 0, op, "There is nothing to pick up here.");
goto leave;
}
tmp = op->below;
}
/* Try to catch it. */
tmp_map = tmp->map;
tmp = stop_item (tmp);
if (tmp == NULL)
goto leave;
need_fix_tmp = 1;
if (!can_pick (op, tmp))
goto leave;
if (op->type == PLAYER)
{
count = op->contr->count;
if (count == 0)
count = tmp->nrof;
}
else
count = tmp->nrof;
/* container is open, so use it */
if (tmp->flag [FLAG_STARTEQUIP])
alt = op;
else if (op->container)
{
alt = op->container;
if (alt != tmp->env && !sack_can_hold (op, alt, tmp, count))
goto leave;
}
else
{ /* non container pickup */
for (alt = op->inv; alt; alt = alt->below)
if (alt->type == CONTAINER && QUERY_FLAG (alt, FLAG_APPLIED) &&
alt->race && alt->race == tmp->race && sack_can_hold (NULL, alt, tmp, count))
break; /* perfect match */
if (!alt)
for (alt = op->inv; alt; alt = alt->below)
if (alt->type == CONTAINER && QUERY_FLAG (alt, FLAG_APPLIED) && sack_can_hold (NULL, alt, tmp, count))
break; /* General container comes next */
if (!alt)
alt = op; /* No free containers */
}
if (tmp->env == alt)
{
/* here it could be possible to check rent,
* if someone wants to implement it
*/
alt = op;
}
#ifdef PICKUP_DEBUG
LOG (llevDebug, "Pick_up(): %s picks %s (%d) and inserts it %s.\n", op->name, tmp->name, op->contr->count, alt->name);
#endif
/* startequip items are not allowed to be put into containers: */
if (op->type == PLAYER && alt->type == CONTAINER && QUERY_FLAG (tmp, FLAG_STARTEQUIP))
{
new_draw_info (NDI_UNIQUE, 0, op, "This object cannot be put into containers!");
goto leave;
}
pick_up_object (op, alt, tmp, count);
if (tmp->destroyed () || tmp->env)
need_fix_tmp = 0;
if (op->type == PLAYER)
op->contr->count = 0;
goto leave;
leave:
if (need_fix_tmp)
fix_stopped_item (tmp, tmp_map, op);
}
/* This takes (picks up) and item. op is the player
* who issued the command. params is a string to
* match against the item name. Basically, always
* returns zero, but that should be improved.
*/
int
command_take (object *op, char *params)
{
object *tmp, *next;
if (op->container)
tmp = op->container->inv;
else
{
tmp = op->above;
if (tmp)
while (tmp->above)
tmp = tmp->above;
if (!tmp)
tmp = op->below;
}
if (!tmp)
{
new_draw_info (NDI_UNIQUE, 0, op, "Nothing to take!");
return 0;
}
/* Makes processing easier */
if (params && *params == '\0')
params = 0;
int cnt = MAX_ITEM_PER_DROP;
while (tmp)
{
next = tmp->below;
if (tmp->invisible)
{
tmp = next;
continue;
}
/* This following two if and else if could be merged into line
* but that probably will make it more difficult to read, and
* not make it any more efficient
*/
if (params && item_matched_string (op, tmp, params))
{
if (--cnt < 0) break;
pick_up (op, tmp);
}
else if (can_pick (op, tmp) && !params)
{
if (--cnt < 0) break;
pick_up (op, tmp);
break;
}
tmp = next;
}
if (cnt < 0)
{
op->failmsg ("Couldn't pick up so many items at once.");
return 0;
}
if (!params && !tmp)
{
for (tmp = op->below; tmp; tmp = tmp->below)
if (!tmp->invisible)
{
new_draw_info_format (NDI_UNIQUE, 0, op, "You can't pick up a %s.", &tmp->name);
break;
}
if (!tmp)
new_draw_info (NDI_UNIQUE, 0, op, "There is nothing to pick up.");
}
return 0;
}
/*
* This function was part of drop, now is own function.
* Player 'op' tries to put object 'tmp' into sack 'sack',
* if nrof is non zero, then nrof objects is tried to put into sack.
* Note that the 'sack' in question can now be a transport,
* so this function isn't named very good anymore.
*/
void
put_object_in_sack (object *op, object *sack, object *tmp, uint32 nrof)
{
object *tmp2, *sack2;
char buf[MAX_BUF];
if (sack == tmp)
return; /* Can't put an object in itself */
if (QUERY_FLAG (tmp, FLAG_STARTEQUIP))
{
new_draw_info_format (NDI_UNIQUE, 0, op, "You cannot put the %s in the %s.", query_name (tmp), query_name (sack));
return;
}
if (tmp->type == CONTAINER && tmp->inv)
{
/* Eneq(@csd.uu.se): If the object to be dropped is a container
* we instead move the contents of that container into the active
* container, this is only done if the object has something in it.
*/
sack2 = tmp;
new_draw_info_format (NDI_UNIQUE, 0, op, "You move the items from %s into %s.", query_name (tmp), query_name (sack));
for (tmp2 = tmp->inv; tmp2; tmp2 = tmp)
{
tmp = tmp2->below;
if ((sack->type == CONTAINER && sack_can_hold (op, op->container, tmp2, tmp2->nrof)))
put_object_in_sack (op, sack, tmp2, 0);
else
{
sprintf (buf, "Your %s fills up.", query_name (sack));
new_draw_info (NDI_UNIQUE, 0, op, buf);
break;
}
}
return;
}
/* Don't worry about this for containers - our caller should have
* already checked this.
*/
if ((sack->type == CONTAINER) && !sack_can_hold (op, sack, tmp, (nrof ? nrof : tmp->nrof)))
return;
if (QUERY_FLAG (tmp, FLAG_APPLIED))
if (apply_special (op, tmp, AP_UNAPPLY | AP_NO_MERGE))
return;
/* we want to put some portion of the item into the container */
if (!can_split (op, tmp, nrof))
return;
new_draw_info_format (NDI_UNIQUE, 0, op, "You put the %s in %s.", query_name (tmp), query_name (sack));
sack->insert (tmp);
}
/*
* This function was part of drop, now is own function.
* Player 'op' tries to drop object 'tmp', if tmp is non zero, then
* nrof objects is tried to dropped.
* This is used when dropping objects onto the floor.
*/
void
drop_object (object *op, object *tmp, uint32 nrof)
{
if (QUERY_FLAG (tmp, FLAG_NO_DROP))
return;
if (QUERY_FLAG (tmp, FLAG_APPLIED))
if (apply_special (op, tmp, AP_UNAPPLY | AP_NO_MERGE))
return; /* can't unapply it */
//fprintf (stderr, "ui, on space is %ld\n", op->ms ().volume ());//D
/* We are only dropping some of the items. We split the current object
* off
*/
if (!can_split (op, tmp, nrof))
if (INVOKE_OBJECT (DROP, tmp, ARG_OBJECT (op)))
return;
if (QUERY_FLAG (tmp, FLAG_STARTEQUIP))
{
op->statusmsg (format ("You drop the %s.", query_name (tmp)));
op->statusmsg ("The god who lent it to you retrieves it.");
tmp->destroy ();
op->update_stats ();
return;
}
/* Call this before we update the various windows/players. At least
* that we, we know the weight is correct.
*/
// 2007-11-26: moved op->update_stats away and calling it later after
// all items of a drop command have been processed.
for (object *floor = GET_MAP_OB (op->map, op->x, op->y); floor; floor = floor->above)
if (INVOKE_OBJECT (DROP_ON, floor, ARG_OBJECT (tmp), ARG_OBJECT (op)))
return;
if (is_in_shop (op) && !QUERY_FLAG (tmp, FLAG_UNPAID) && tmp->type != MONEY)
{
if (!sell_item (tmp, op))
return; // if we can't sell it we don't drop it
}
tmp->x = op->x;
tmp->y = op->y;
insert_ob_in_map (tmp, op->map, op, INS_BELOW_ORIGINATOR);
}
void
drop (object *op, object *tmp)
{
/* Hopeful fix for disappearing objects when dropping from a container -
* somehow, players get an invisible object in the container, and the
* old logic would skip over invisible objects - works fine for the
* playes inventory, but drop inventory wants to use the next value.
*/
if (tmp->invisible)
{
/* if the following is the case, it must be in an container. */
if (tmp->env && tmp->env->type != PLAYER)
{
/* Just toss the object - probably shouldn't be hanging
* around anyways
*/
tmp->destroy ();
return;
}
else
{
while (tmp && tmp->invisible)
tmp = tmp->below;
}
}
if (tmp == NULL)
{
new_draw_info (NDI_UNIQUE, 0, op, "You don't have anything to drop.");
return;
}
if (QUERY_FLAG (tmp, FLAG_INV_LOCKED))
{
new_draw_info (NDI_UNIQUE, 0, op, "This item is locked");
return;
}
if (QUERY_FLAG (tmp, FLAG_NO_DROP))
{
#if 0
/* Eneq(@csd.uu.se): Objects with NO_DROP defined can't be dropped. */
new_draw_info (NDI_UNIQUE, 0, op, "This item can't be dropped.");
#endif
return;
}
if (op->type == PLAYER && op->contr->last_used == tmp)
op->contr->last_used = tmp->below ? tmp->below
: tmp->above ? tmp->above
: 0;
if (op->container)
{
if (op->type == PLAYER)
put_object_in_sack (op, op->container, tmp, op->contr->count);
else
put_object_in_sack (op, op->container, tmp, 0);
}
else
{
if (op->type == PLAYER)
drop_object (op, tmp, op->contr->count);
else
drop_object (op, tmp, 0);
}
if (op->type == PLAYER)
op->contr->count = 0;
}
/* Command will drop all items that have not been locked */
int
command_dropall (object *op, char *params)
{
if (op->inv == NULL)
{
new_draw_info (NDI_UNIQUE, 0, op, "Nothing to drop!");
return 0;
}
object *curinv = op->inv;
object *nextinv;
/*
This is the default. Drops everything not locked or considered
not something that should be dropped.
*/
/*
Care must be taken that the next item pointer is not to money as
the drop() routine will do unknown things to it when dropping
in a shop. --Tero.Pelander@utu.fi
*/
int cnt = MAX_ITEM_PER_DROP;
if (!params)
{
while (curinv)
{
nextinv = curinv->below;
while (nextinv && nextinv->type == MONEY)
nextinv = nextinv->below;
if (!QUERY_FLAG (curinv, FLAG_INV_LOCKED) && curinv->type != MONEY &&
curinv->type != FOOD && curinv->type != KEY &&
curinv->type != SPECIAL_KEY && curinv->type != GEM &&
!curinv->invisible && (curinv->type != CONTAINER || op->container != curinv))
{
drop (op, curinv);
if (--cnt <= 0) break;
}
curinv = nextinv;
}
}
else if (strcmp (params, "weapons") == 0)
{
while (curinv)
{
nextinv = curinv->below;
while (nextinv && nextinv->type == MONEY)
nextinv = nextinv->below;
if (!QUERY_FLAG (curinv, FLAG_INV_LOCKED) && ((curinv->type == WEAPON) || (curinv->type == BOW) || (curinv->type == ARROW)))
{
drop (op, curinv);
if (--cnt <= 0) break;
}
curinv = nextinv;
}
}
else if (strcmp (params, "armor") == 0 || strcmp (params, "armour") == 0)
{
while (curinv)
{
nextinv = curinv->below;
while (nextinv && nextinv->type == MONEY)
nextinv = nextinv->below;
if (!QUERY_FLAG (curinv, FLAG_INV_LOCKED) && ((curinv->type == ARMOUR) || curinv->type == SHIELD || curinv->type == HELMET))
{
drop (op, curinv);
if (--cnt <= 0) break;
}
curinv = nextinv;
}
}
else if (strcmp (params, "misc") == 0)
{
while (curinv)
{
nextinv = curinv->below;
while (nextinv && nextinv->type == MONEY)
nextinv = nextinv->below;
if (!QUERY_FLAG (curinv, FLAG_INV_LOCKED) && !QUERY_FLAG (curinv, FLAG_APPLIED))
{
switch (curinv->type)
{
case HORN:
case BOOK:
case SPELLBOOK:
case GIRDLE:
case AMULET:
case RING:
case CLOAK:
case BOOTS:
case GLOVES:
case BRACERS:
case SCROLL:
case ARMOUR_IMPROVER:
case WEAPON_IMPROVER:
case WAND:
case ROD:
case POTION:
drop (op, curinv);
curinv = nextinv;
break;
default:
curinv = nextinv;
break;
}
if (--cnt <= 0) break;
}
curinv = nextinv;
}
}
if (cnt <= 0)
op->failmsg ("Only dropped some items, can't drop that many items at once.");
/* draw_look(op);*/
return 0;
}
/* Object op wants to drop object(s) params. params can be a
* comma seperated list.
*/
int
command_drop (object *op, char *params)
{
object *tmp, *next;
int did_one = 0;
if (!params)
{
new_draw_info (NDI_UNIQUE, 0, op, "Drop what?");
return 0;
}
else
{
int cnt = MAX_ITEM_PER_DROP;
for (tmp = op->inv; tmp; tmp = next)
{
next = tmp->below;
if (QUERY_FLAG (tmp, FLAG_NO_DROP) || tmp->invisible)
continue;
if (item_matched_string (op, tmp, params))
{
drop (op, tmp);
if (--cnt <= 0) break;
did_one = 1;
}
}
if (!did_one)
new_draw_info (NDI_UNIQUE, 0, op, "Nothing to drop.");
if (cnt <= 0)
op->failmsg ("Only dropped some items, can't drop that many items at once.");
}
return 0;
}
int
command_examine (object *op, char *params)
{
if (!params)
{
object *tmp = op->below;
while (tmp && !tmp->client_visible ())
tmp = tmp->below;
if (tmp)
examine (op, tmp);
}
else
{
object *tmp = find_best_object_match (op, params);
if (tmp)
examine (op, tmp);
else
op->contr->infobox (MSG_CHANNEL ("examine"), format ("Could not find an object that matches %s", params));
}
return 0;
}
/* op should be a player.
* we return the object the player has marked with the 'mark' command
* below. If no match is found (or object has changed), we return
* NULL. We leave it up to the calling function to print messages if
* nothing is found.
*/
object *
find_marked_object (object *op)
{
object *tmp;
if (!op || !op->contr)
return NULL;
if (!op->contr->mark)
{
/* new_draw_info(NDI_UNIQUE,0,op,"You have no marked object");*/
return 0;
}
/* This may seem like overkill, but we need to make sure that they
* player hasn't dropped the item. We use count on the off chance that
* an item got reincarnated at some point.
*/
for (tmp = op->inv; tmp; tmp = tmp->below)
{
if (tmp->invisible)
continue;
if (tmp == op->contr->mark)
{
if (!tmp->destroyed ())
return tmp;
else
{
op->contr->mark = 0;
/* new_draw_info(NDI_UNIQUE,0,op,"You have no marked object");*/
return 0;
}
}
}
return 0;
}
std::string
object::describe_monster (object *who)
{
dynbuf_text buf (512, 512);
object *mon = head ? head : this;
if (QUERY_FLAG (mon, FLAG_UNDEAD))
buf << "It is an undead force.\r";
if (mon->level > who->level)
buf << "It is likely more powerful than you.\r";
else if (mon->level < who->level)
buf << "It is likely less powerful than you.\r";
else
buf << "It is probably as powerful as you.\r";
if (mon->attacktype & AT_ACID)
buf << "You seem to smell an acrid odor.\r";
/* Anyone know why this used to use the clone value instead of the
* maxhp field? This seems that it should give more accurate results.
*/
switch ((mon->stats.hp + 1) * 4 / (mon->stats.maxhp + 1))
{ /* From 1-4 */
case 1:
buf << "It is in a bad shape.\r";
break;
case 2:
buf << "It is hurt.\r";
break;
case 3:
buf << "It is somewhat hurt.\r";
break;
case 4:
buf << "It is in excellent shape.\r";
break;
}
if (present_in_ob (POISONING, mon))
buf << "It looks very ill.\r";
buf << '\n';
return buf;
}
/* tmp is the object being described, pl is who is examing it. */
const char *
long_desc (object *tmp, object *pl)
{
static std::string s;
return (s = tmp->long_desc (pl)).c_str ();
}
std::string
object::long_desc (object *who)
{
std::string buf (query_name (this));
switch (type)
{
case RING:
case SKILL:
case WEAPON:
case ARMOUR:
case BRACERS:
case HELMET:
case SHIELD:
case BOOTS:
case GLOVES:
case AMULET:
case GIRDLE:
case BOW:
case ARROW:
case CLOAK:
case FOOD:
case DRINK:
case FLESH:
case SKILL_TOOL:
case POWER_CRYSTAL:
{
const char *cp = ::describe_item (this, who);
if (*cp)
{
buf.append (" ");
buf.append (cp);
}
}
}
return buf;
}
/* op is the player
* tmp is the monster being examined.
*/
void
examine_monster (object *op, object *tmp)
{
new_draw_info (NDI_UNIQUE, 0, op, tmp->describe_monster (op).c_str ());
}
std::string
object::describe (object *who)
{
dynbuf_text buf (1024, 1024);
buf.printf ("That is: %s.\r", long_desc (who).c_str ());
if (custom_name)
buf.printf ("You call it %s.\r", &custom_name);
switch (type)
{
case SPELLBOOK:
if (flag [FLAG_IDENTIFIED] && inv)
buf.printf ("%s is a %s %s spell.\r", &inv->name, get_levelnumber (inv->level), &inv->skill);
break;
case BOOK:
if (msg)
buf << "Something is written in it.\r";
break;
case CONTAINER:
if (race)
{
if (weight_limit && stats.Str < 100)
buf.printf ("It can hold only %s and its weight limit is %.1f kg.\r",
&race, weight_limit / (10.0 * (100 - stats.Str)));
else
buf.printf ("It can hold only %s.\r", &race);
}
else if (weight_limit && stats.Str < 100)
buf.printf ("Its weight limit is %.1f kg.\r", weight_limit / (10.0 * (100 - stats.Str)));
break;
case WAND:
if (flag [FLAG_IDENTIFIED])
buf.printf ("It has %d charges left.\r", stats.food);
break;
}
if (materialname && !msg)
buf.printf ("It is made of: %s.\r", &materialname);
if (who)
/* Where to wear this item */
for (int i = 0; i < NUM_BODY_LOCATIONS; i++)
if (slot[i].info)
{
buf << (who->slot[i].info ? body_locations[i].use_name : body_locations[i].nonuse_name);
if (slot[i].info < -1 && who->slot[i].info)
buf.printf ("(%d)", -slot[i].info);
buf << ".\r";
}
if (weight)
buf.printf ("%s %3.3f kg.\r", nrof > 1 ? "They weigh" : "It weighs", weight * (nrof ? nrof : 1) / 1000.0);
if (value && !flag [FLAG_STARTEQUIP] && !flag [FLAG_NO_PICK] && who)
{
buf.printf ("You reckon %s worth %s.\r", nrof > 1 ? "they are" : "it is", query_cost_string (this, who, F_TRUE | F_APPROX));
if (is_in_shop (who))
{
if (flag [FLAG_UNPAID])
buf.printf ("%s would cost you %s.\r", nrof > 1 ? "They" : "It", query_cost_string (this, who, F_BUY | F_SHOP));
else
buf.printf ("You are offered %s for %s.\r", query_cost_string (this, who, F_SELL + F_SHOP), nrof > 1 ? "them" : "it");
}
}
if (flag [FLAG_MONSTER])
buf << describe_monster (who);
/* Is this item buildable? */
if (flag [FLAG_IS_BUILDABLE])
buf << "This is a buildable item.\r";
/* Does the object have a message? Don't show message for all object
* types - especially if the first entry is a match
*/
if (msg && type != EXIT && type != BOOK && type != CORPSE && !move_on && !has_dialogue ())
{
/* This is just a hack so when identifying the items, we print
* out the extra message
*/
if (need_identify (this) && flag [FLAG_IDENTIFIED])
buf << "The object has a story:\r";
buf << msg << '\n';
}
buf << '\n';
return std::string (buf.linearise (), buf.size ());
}
static void
display_new_pickup (object *op)
{
int i = op->contr->mode;
if (!(i & PU_NEWMODE))
return;
new_draw_info_format (NDI_UNIQUE, 0, op, "%d NEWMODE", i & PU_NEWMODE ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d DEBUG", i & PU_DEBUG ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d INHIBIT", i & PU_INHIBIT ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d STOP", i & PU_STOP ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d <= x pickup weight/value RATIO (0==off)", (i & PU_RATIO) * 5);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d FOOD", i & PU_FOOD ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d DRINK", i & PU_DRINK ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d VALUABLES", i & PU_VALUABLES ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d BOW", i & PU_BOW ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d ARROW", i & PU_ARROW ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d HELMET", i & PU_HELMET ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d SHIELD", i & PU_SHIELD ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d ARMOUR", i & PU_ARMOUR ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d BOOTS", i & PU_BOOTS ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d GLOVES", i & PU_GLOVES ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d CLOAK", i & PU_CLOAK ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d KEY", i & PU_KEY ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d MISSILEWEAPON", i & PU_MISSILEWEAPON ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d ALLWEAPON", i & PU_ALLWEAPON ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d MAGICAL", i & PU_MAGICAL ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d POTION", i & PU_POTION ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d SPELLBOOK", i & PU_SPELLBOOK ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d SKILLSCROLL", i & PU_SKILLSCROLL ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d READABLES", i & PU_READABLES ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d MAGICDEVICE", i & PU_MAGIC_DEVICE ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d NOT CURSED", i & PU_NOT_CURSED ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d JEWELS", i & PU_JEWELS ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "%d FLESH", i & PU_FLESH ? 1 : 0);
new_draw_info_format (NDI_UNIQUE, 0, op, "");
}
int
command_pickup (object *op, char *params)
{
uint32 i;
static const char *names[] = {
"debug", "inhibit", "stop", "food", "drink", "valuables", "bow", "arrow", "helmet",
"shield", "armour", "boots", "gloves", "cloak", "key", "missile", "allweapon",
"magical", "potion", "spellbook", "skillscroll", "readables", "magicdevice", "notcursed",
"jewels", "flesh", NULL
};
static uint32 modes[] = {
PU_DEBUG, PU_INHIBIT, PU_STOP, PU_FOOD, PU_DRINK, PU_VALUABLES, PU_BOW, PU_ARROW, PU_HELMET,
PU_SHIELD, PU_ARMOUR, PU_BOOTS, PU_GLOVES, PU_CLOAK, PU_KEY, PU_MISSILEWEAPON, PU_ALLWEAPON,
PU_MAGICAL, PU_POTION, PU_SPELLBOOK, PU_SKILLSCROLL, PU_READABLES, PU_MAGIC_DEVICE, PU_NOT_CURSED,
PU_JEWELS, PU_FLESH, 0
};
if (!params)
{
/* if the new mode is used, just print the settings */
if (op->contr->mode & PU_NEWMODE)
{
display_new_pickup (op);
return 1;
}
if (1)
LOG (llevDebug, "command_pickup: !params\n");
set_pickup_mode (op, (op->contr->mode > 6) ? 0 : op->contr->mode + 1);
return 0;
}
while (*params == ' ' && *params)
params++;
if (*params == '+' || *params == '-')
{
int mode;
for (mode = 0; names[mode]; mode++)
{
if (!strcmp (names[mode], params + 1))
{
i = op->contr->mode;
if (!(i & PU_NEWMODE))
i = PU_NEWMODE;
if (*params == '+')
i = i | modes[mode];
else
i = i & ~modes[mode];
op->contr->mode = i;
display_new_pickup (op);
return 1;
}
}
new_draw_info_format (NDI_UNIQUE, 0, op, "Pickup: invalid item %s\n", params);
return 1;
}
if (sscanf (params, "%u", &i) != 1)
{
if (1)
LOG (llevDebug, "command_pickup: params==NULL\n");
new_draw_info (NDI_UNIQUE, 0, op, "Usage: pickup <0-7> or .");
return 1;
}
set_pickup_mode (op, i);
return 1;
}
void
set_pickup_mode (object *op, int i)
{
switch (op->contr->mode = i)
{
case 0:
new_draw_info (NDI_UNIQUE, 0, op, "Mode: Don't pick up.");
break;
case 1:
new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up one item.");
break;
case 2:
new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up one item and stop.");
break;
case 3:
new_draw_info (NDI_UNIQUE, 0, op, "Mode: Stop before picking up.");
break;
case 4:
new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up all items.");
break;
case 5:
new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up all items and stop.");
break;
case 6:
new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up all magic items.");
break;
case 7:
new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up all coins and gems");
break;
}
}
int
command_search_items (object *op, char *params)
{
char buf[MAX_BUF];
if (settings.search_items == FALSE)
return 1;
if (params == NULL)
{
if (op->contr->search_str[0] == '\0')
{
new_draw_info (NDI_UNIQUE, 0, op, "Example: search magic+1");
new_draw_info (NDI_UNIQUE, 0, op, "Would automatically pick up all");
new_draw_info (NDI_UNIQUE, 0, op, "items containing the word 'magic+1'.");
return 1;
}
op->contr->search_str[0] = '\0';
new_draw_info (NDI_UNIQUE, 0, op, "Search mode turned off.");
op->update_stats ();
return 1;
}
if ((int) strlen (params) >= MAX_BUF)
{
new_draw_info (NDI_UNIQUE, 0, op, "Search string too long.");
return 1;
}
strcpy (op->contr->search_str, params);
sprintf (buf, "Searching for '%s'.", op->contr->search_str);
new_draw_info (NDI_UNIQUE, 0, op, buf);
op->update_stats ();
return 1;
}