2386 lines
80 KiB
C
2386 lines
80 KiB
C
/*
|
|
* static char *rcsid_c_object_c =
|
|
* "$Id: c_object.c 12045 2009-07-09 23:07:04Z akirschbaum $";
|
|
*/
|
|
/*
|
|
CrossFire, A Multiplayer game for X-windows
|
|
|
|
Copyright (C) 2002-2007 Mark Wedel & Crossfire Development Team
|
|
Copyright (C) 1992 Frank Tore Johansen
|
|
|
|
This program 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 2 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, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
The author can be reached via e-mail to crossfire-devel@real-time.com
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* Object commands, including picking/dropping, locking, etc.
|
|
* @todo clean multiple variations of same stuff (pickup and such), or rename for less confusion.
|
|
*/
|
|
|
|
#include <global.h>
|
|
#include <loader.h>
|
|
#include <skills.h>
|
|
#ifndef __CEXTRACT__
|
|
#include <sproto.h>
|
|
#endif
|
|
#include <living.h>
|
|
#include <math.h>
|
|
|
|
static void set_pickup_mode(const object *op, int i);
|
|
|
|
/*
|
|
* Object id parsing functions
|
|
*/
|
|
|
|
/** Simple ::objectlink allocation, fail-safe. */
|
|
#define OBLINKMALLOC(p) if (!((p) = (objectlink *)malloc(sizeof(objectlink)))) \
|
|
fatal(OUT_OF_MEMORY);
|
|
|
|
/**
|
|
* Search from start and through below 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.
|
|
*
|
|
* @param start
|
|
* first object to start searching at.
|
|
* @param pl
|
|
* what object we're searching for.
|
|
* @param params
|
|
* what to search for.
|
|
* @param aflag
|
|
* Either 0 or AP_APPLY or AP_UNAPPLY. Used with apply -u, and apply -a to
|
|
* only unapply applied, or apply unapplied objects.
|
|
* @return
|
|
* matching object, or NULL if no suitable.
|
|
**/
|
|
static object *find_best_apply_object_match(object *start, object *pl, const char *params, int aflag) {
|
|
object *tmp, *best = NULL;
|
|
int match_val = 0, tmpmatch;
|
|
|
|
for (tmp = start; tmp; tmp = tmp->below) {
|
|
if (tmp->invisible)
|
|
continue;
|
|
if ((aflag == AP_APPLY) && (QUERY_FLAG(tmp, FLAG_APPLIED)))
|
|
continue;
|
|
if ((aflag == AP_UNAPPLY) && (!QUERY_FLAG(tmp, FLAG_APPLIED)))
|
|
continue;
|
|
if ((tmpmatch = item_matched_string(pl, tmp, params)) > match_val) {
|
|
match_val = tmpmatch;
|
|
best = tmp;
|
|
}
|
|
}
|
|
return best;
|
|
}
|
|
|
|
/**
|
|
* Shortcut to find_best_apply_object_match(pl->inv, pl, params, AF_NULL);
|
|
*
|
|
* @param pl
|
|
* who to search an item for.
|
|
* @param params
|
|
* what to search for.
|
|
* @return
|
|
* matching object, or NULL if no suitable.
|
|
**/
|
|
static object *find_best_object_match(object *pl, const char *params) {
|
|
return find_best_apply_object_match(pl->inv, pl, params, AP_NULL);
|
|
}
|
|
|
|
/**
|
|
* 'use_skill' command.
|
|
*
|
|
* @param pl
|
|
* player.
|
|
* @param params
|
|
* skill to use, and optional parameters.
|
|
* @return
|
|
* whether skill was used or not.
|
|
*/
|
|
int command_uskill(object *pl, char *params) {
|
|
if (!params) {
|
|
draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Usage: use_skill <skill name>", NULL);
|
|
return 0;
|
|
}
|
|
return use_skill(pl, params);
|
|
}
|
|
|
|
/**
|
|
* 'ready_skill' command.
|
|
*
|
|
* @param pl
|
|
* player.
|
|
* @param params
|
|
* skill name.
|
|
* @return
|
|
* whether skill was readied or not.
|
|
*/
|
|
int command_rskill(object *pl, char *params) {
|
|
object *skill;
|
|
|
|
if (!params) {
|
|
draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Usage: ready_skill <skill name>", NULL);
|
|
return 0;
|
|
}
|
|
skill = find_skill_by_name(pl, params);
|
|
|
|
if (!skill) {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_MISSING,
|
|
"You have no knowledge of the skill %s",
|
|
"You have no knowledge of the skill %s",
|
|
params);
|
|
return 0;
|
|
}
|
|
return change_skill(pl, skill, 0);
|
|
}
|
|
|
|
|
|
/* These functions (command_search, command_disarm) are really juse wrappers for
|
|
* things like 'use_skill ...'). In fact, they should really be obsoleted
|
|
* and replaced with those.
|
|
*/
|
|
/**
|
|
* 'search' command.
|
|
*
|
|
* @param op
|
|
* player.
|
|
* @param params
|
|
* unused.
|
|
* @return
|
|
* whether skill was used or not.
|
|
*/
|
|
int command_search(object *op, char *params) {
|
|
return use_skill(op, skill_names[SK_FIND_TRAPS]);
|
|
}
|
|
|
|
/**
|
|
* 'disarm' command.
|
|
*
|
|
* @param op
|
|
* player.
|
|
* @param params
|
|
* unused.
|
|
* @return
|
|
* whether skill was used or not.
|
|
*/
|
|
int command_disarm(object *op, char *params) {
|
|
return use_skill(op, skill_names[SK_DISARM_TRAPS]);
|
|
}
|
|
|
|
/**
|
|
* 'throw' command.
|
|
*
|
|
* A little special because we do want to pass the full params along
|
|
* as it includes the object to throw.
|
|
*
|
|
* @param op
|
|
* player.
|
|
* @param params
|
|
* what to throw.
|
|
* @return
|
|
* whether skill was used or not.
|
|
*/
|
|
int command_throw(object *op, char *params) {
|
|
object *skop;
|
|
|
|
skop = find_skill_by_name(op, skill_names[SK_THROWING]);
|
|
if (skop)
|
|
return do_skill(op, op, skop, op->facing, params);
|
|
else {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_MISSING,
|
|
"You have no knowledge of the skill throwing.", NULL);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* 'apply' command.
|
|
*
|
|
* @param op
|
|
* player.
|
|
* @param params
|
|
* what to apply.
|
|
* @return
|
|
* whether skill was used or not.
|
|
*/
|
|
int command_apply(object *op, char *params) {
|
|
if (!params) {
|
|
player_apply_below(op);
|
|
return 0;
|
|
} else {
|
|
int aflag = 0;
|
|
object *inv = op->inv;
|
|
|
|
while (*params == ' ')
|
|
params++;
|
|
if (!strncmp(params, "-a ", 3)) {
|
|
aflag = AP_APPLY;
|
|
params += 3;
|
|
}
|
|
if (!strncmp(params, "-u ", 3)) {
|
|
aflag = AP_UNAPPLY;
|
|
params += 3;
|
|
}
|
|
if (!strncmp(params, "-b ", 3)) {
|
|
params += 3;
|
|
if (op->container)
|
|
inv = op->container->inv;
|
|
else {
|
|
inv = op;
|
|
while (inv->above)
|
|
inv = inv->above;
|
|
}
|
|
}
|
|
while (*params == ' ')
|
|
params++;
|
|
|
|
inv = find_best_apply_object_match(inv, op, params, aflag);
|
|
if (inv) {
|
|
player_apply(op, inv, aflag, 0);
|
|
} else
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Could not find any match to the %s.",
|
|
"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.
|
|
*
|
|
* @param pl
|
|
* player.
|
|
* @param sack
|
|
* container to try to put into.
|
|
* @param op
|
|
* what to put in the sack.
|
|
* @param nrof
|
|
* 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.
|
|
* @return
|
|
* 1 if it will fit, 0 if it will not.
|
|
*/
|
|
int sack_can_hold(const object *pl, const object *sack, const object *op, uint32 nrof) {
|
|
char name[MAX_BUF];
|
|
query_name(sack, name, MAX_BUF);
|
|
|
|
if (!QUERY_FLAG(sack, FLAG_APPLIED)) {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"The %s is not active.",
|
|
"The %s is not active.",
|
|
name);
|
|
return 0;
|
|
}
|
|
if (sack == op) {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"You can't put the %s into itself.",
|
|
"You can't put the %s into itself.",
|
|
name);
|
|
return 0;
|
|
}
|
|
/* Special handling for "flesh bags" --kts */
|
|
if (sack->subtype == CONTAINER_FLESH
|
|
&& (!op->other_arch || (op->other_arch && sack->race && strstr(sack->race, op->other_arch->name) == NULL)) ) {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"You can only put %s parts into the %s.",
|
|
"You can only put %s parts into the %s.",
|
|
sack->race, sack->name);
|
|
return 0;
|
|
}
|
|
if (sack->race && sack->subtype != CONTAINER_FLESH
|
|
&& (sack->race != op->race || op->type == CONTAINER || (sack->stats.food && sack->stats.food != op->type))) {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"You can put only %s into the %s.",
|
|
"You can put only %s into the %s.",
|
|
sack->race, name);
|
|
return 0;
|
|
}
|
|
if (op->type == SPECIAL_KEY && sack->slaying && op->slaying) {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"You can't want put the key into %s.",
|
|
"You can't want put the key into %s.",
|
|
name);
|
|
return 0;
|
|
}
|
|
if (sack->weight_limit && sack->carrying+(nrof ? nrof : 1)
|
|
*(op->weight+(op->type == CONTAINER ? (op->carrying*op->stats.Str) : 0))
|
|
*(100-sack->stats.Str)/100 > sack->weight_limit) {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"That won't fit in the %s!",
|
|
"That won't fit in the %s!",
|
|
name);
|
|
return 0;
|
|
}
|
|
/* All other checks pass, must be OK */
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Try to pick up some item.
|
|
*
|
|
* @param pl
|
|
* object (player or monster) picking up.
|
|
* @param op
|
|
* object to put tmp into.
|
|
* @param tmp
|
|
* object to pick up.
|
|
* @param nrof
|
|
* number of tmp to pick up (0 means all of them).
|
|
*/
|
|
static void pick_up_object(object *pl, object *op, object *tmp, int nrof) {
|
|
/* buf needs to be big (more than 256 chars) because you can get
|
|
* very long item names.
|
|
*/
|
|
char buf[HUGE_BUF], name[MAX_BUF];
|
|
object *env = tmp->env;
|
|
uint32 weight, effective_weight_limit;
|
|
int tmp_nrof = tmp->nrof ? tmp->nrof : 1;
|
|
|
|
/* 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)
|
|
&& get_player_container(tmp) != pl) {
|
|
draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"You are levitating, you can't reach the ground!", NULL);
|
|
return;
|
|
}
|
|
if (QUERY_FLAG(tmp, FLAG_NO_DROP))
|
|
return;
|
|
|
|
if (QUERY_FLAG(tmp, FLAG_WAS_WIZ) && !QUERY_FLAG(pl, FLAG_WAS_WIZ)) {
|
|
draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
|
|
"The object disappears in a puff of smoke! It must have been an illusion.",
|
|
NULL);
|
|
if (!QUERY_FLAG(tmp, FLAG_REMOVED))
|
|
remove_ob(tmp);
|
|
free_object(tmp);
|
|
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;
|
|
|
|
if (pl->stats.Str <= MAX_STAT)
|
|
effective_weight_limit = weight_limit[pl->stats.Str];
|
|
else
|
|
effective_weight_limit = weight_limit[MAX_STAT];
|
|
|
|
if ((pl->weight+pl->carrying+weight) > effective_weight_limit) {
|
|
draw_ext_info(0, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
|
|
"That item is too heavy for you to pick up.", NULL);
|
|
return;
|
|
}
|
|
|
|
if (settings.real_wiz == FALSE && QUERY_FLAG(pl, FLAG_WAS_WIZ))
|
|
SET_FLAG(tmp, FLAG_WAS_WIZ);
|
|
|
|
if (nrof != tmp_nrof) {
|
|
char failure[MAX_BUF];
|
|
|
|
tmp = get_split_ob(tmp, nrof, failure, sizeof(failure));
|
|
if (!tmp) {
|
|
draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
failure, NULL);
|
|
return;
|
|
}
|
|
} else {
|
|
/* If the object is in a container, send a delete to the client.
|
|
* - we are moving all the items from the container to elsewhere,
|
|
* so it needs to be deleted.
|
|
*/
|
|
if (!QUERY_FLAG(tmp, FLAG_REMOVED)) {
|
|
remove_ob(tmp); /* Unlink it */
|
|
}
|
|
}
|
|
query_name(tmp, name, MAX_BUF);
|
|
|
|
if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
|
|
char *value = stringbuffer_finish(query_cost_string(tmp, pl, F_BUY|F_SHOP, NULL));
|
|
snprintf(buf, sizeof(buf), "%s will cost you %s.", name, value);
|
|
free(value);
|
|
} else
|
|
snprintf(buf, sizeof(buf), "You pick up the %s.", name);
|
|
|
|
/* Now item is about to be picked. */
|
|
if (execute_event(tmp, EVENT_PICKUP, pl, op, NULL, SCRIPT_FIX_ALL) != 0)
|
|
return;
|
|
|
|
draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
buf, NULL);
|
|
|
|
tmp = insert_ob_in_ob(tmp, op);
|
|
|
|
/* All the stuff below deals with client/server code, and is only
|
|
* usable by players
|
|
*/
|
|
if (pl->type != PLAYER)
|
|
return;
|
|
|
|
/* Additional weight changes speed, etc */
|
|
fix_object(pl);
|
|
|
|
/* These are needed to update the weight for the container we
|
|
* are putting the object in.
|
|
*/
|
|
if (op != pl) {
|
|
esrv_update_item(UPD_WEIGHT, pl, op);
|
|
esrv_update_item(UPD_WEIGHT, pl, pl);
|
|
}
|
|
|
|
/* Update the container the object was in */
|
|
if (env && env != pl && env != op)
|
|
esrv_update_item(UPD_WEIGHT, pl, env);
|
|
}
|
|
|
|
/**
|
|
* Try to pick up an item.
|
|
*
|
|
* @param op
|
|
* object trying to pick up.
|
|
* @param alt
|
|
* optional object op is trying to pick. If NULL, try to pick first item under op.
|
|
*/
|
|
void pick_up(object *op, object *alt) {
|
|
/* modified slightly to allow monsters use this -b.t. 5-31-95 */
|
|
object *tmp = NULL, *tmp1;
|
|
mapstruct *tmp_map = NULL;
|
|
int count;
|
|
tag_t tag;
|
|
|
|
/* Decide which object to pick. */
|
|
if (alt) {
|
|
if (!can_pick(op, alt)) {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"You can't pick up the %s.",
|
|
"You can't pick up the %s.",
|
|
alt->name);
|
|
return;
|
|
}
|
|
tmp = alt;
|
|
} else {
|
|
if (op->below == NULL || !can_pick(op, op->below)) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"There is nothing to pick up here.", NULL);
|
|
return;
|
|
}
|
|
tmp = op->below;
|
|
}
|
|
|
|
/* it is possible that the object is a thrown object and is flying about.
|
|
* in that case, what we want to pick up is the payload. Objects
|
|
* that are thrown are encapsulated into a thrown object.
|
|
* stop_item() returns the payload (unlinked from map) and gets rid of the
|
|
* container object. If this object isn't picked up, we need to insert
|
|
* it back on the map.
|
|
* A bug here is that even attempting to pick up one of these objects will
|
|
* result in this logic being called even if player is unable to pick it
|
|
* up.
|
|
*/
|
|
|
|
tmp_map = tmp->map;
|
|
tmp1 = stop_item(tmp);
|
|
if (tmp1 == NULL)
|
|
return;
|
|
|
|
/* If it is a thrown object, insert it back into the map here.
|
|
* makes life easier further along. Do no merge so pick up code
|
|
* behaves more sanely.
|
|
*/
|
|
if (tmp1 != tmp) {
|
|
tmp = insert_ob_in_map(tmp1, tmp_map, op, INS_NO_MERGE);
|
|
}
|
|
|
|
if (tmp == NULL) return;
|
|
|
|
if (!can_pick(op, tmp)) return;
|
|
|
|
/* Establish how many of the object we are picking up */
|
|
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 (op->container) {
|
|
alt = op->container;
|
|
if (alt != tmp->env && !sack_can_hold(op, alt, tmp, count)) return;
|
|
} else {
|
|
/* non container pickup. See if player has any
|
|
* active containers.
|
|
*/
|
|
object *container=NULL;
|
|
|
|
/* Look for any active containers that can hold this item.
|
|
* we cover two cases here - the perfect match case, where we
|
|
* break out of the loop, and the general case (have a container),
|
|
* Moved this into a single loop - reduces redundant code, is
|
|
* more efficient and easier to follow. MSW 2009-04-06
|
|
*/
|
|
for (alt = op->inv; alt; alt = alt->below) {
|
|
if (alt->type == CONTAINER
|
|
&& QUERY_FLAG(alt, FLAG_APPLIED)
|
|
&& sack_can_hold(NULL, alt, tmp, count)) {
|
|
if (alt->race && alt->race == tmp->race) {
|
|
break; /* perfect match */
|
|
}
|
|
else if (!container) {
|
|
container = alt;
|
|
}
|
|
}
|
|
}
|
|
/* Note container could be null, but no reason to check for it */
|
|
if (!alt) alt=container;
|
|
|
|
if (!alt)
|
|
alt = op; /* No free containers */
|
|
}
|
|
/* see if this object is already in this container. If so,
|
|
* move it to player inventory from this container.
|
|
*/
|
|
if (tmp->env == alt) {
|
|
alt = op;
|
|
}
|
|
|
|
/* Don't allow players to be put into containers. Instead,
|
|
* just put them in the players inventory.
|
|
*/
|
|
if (tmp->type == CONTAINER && alt->type==CONTAINER) {
|
|
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
|
|
* Not sure why we have this limitation
|
|
* @@ BE LIMITED NO LONGER !!!!
|
|
*/
|
|
/*if (op->type == PLAYER
|
|
&& alt->type == CONTAINER
|
|
&& QUERY_FLAG(tmp, FLAG_STARTEQUIP)) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"This object cannot be put into containers!", NULL);
|
|
return;
|
|
}*/
|
|
|
|
tag = tmp->count;
|
|
pick_up_object(op, alt, tmp, count);
|
|
if (op->type == PLAYER)
|
|
op->contr->count = 0;
|
|
}
|
|
|
|
/**
|
|
* This takes (picks up) and item.
|
|
*
|
|
* @param op
|
|
* player who issued the command.
|
|
* @param params
|
|
* string to match against the item name.
|
|
* @return
|
|
* 0.
|
|
*/
|
|
int command_take(object *op, char *params) {
|
|
object *tmp, *next;
|
|
int ival;
|
|
int missed = 0;
|
|
|
|
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 == NULL) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Nothing to take!", NULL);
|
|
return 0;
|
|
}
|
|
|
|
/* Makes processing easier */
|
|
if (params && *params == '\0')
|
|
params = NULL;
|
|
|
|
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 && (ival = item_matched_string(op, tmp, params)) > 0) {
|
|
if ((ival <= 2) && (!can_pick(op, tmp))) {
|
|
if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR))/* don't count floor tiles */
|
|
missed++;
|
|
} else
|
|
pick_up(op, tmp);
|
|
} else if (can_pick(op, tmp) && !params) {
|
|
pick_up(op, tmp);
|
|
break;
|
|
}
|
|
tmp = next;
|
|
/* Might as well just skip over the player immediately -
|
|
* we know it can't be picked up
|
|
*/
|
|
if (tmp == op)
|
|
tmp = tmp->below;
|
|
}
|
|
if (!params && !tmp) {
|
|
for (tmp = op->below; tmp != NULL; tmp = tmp->next)
|
|
if (!tmp->invisible) {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"You can't pick up a %s.",
|
|
"You can't pick up a %s.",
|
|
tmp->name ? tmp->name : "null");
|
|
|
|
break;
|
|
}
|
|
if (!tmp)
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"There is nothing to pick up.", NULL);
|
|
}
|
|
if (missed == 1)
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"You were unable to take one of the items.", NULL);
|
|
else if (missed > 1)
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
|
|
"You were unable to take %d of the items.",
|
|
"You were unable to take %d of the items.",
|
|
missed);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Something tries to put an object into another.
|
|
*
|
|
* This function was part of drop(), now is own function.
|
|
*
|
|
* @note
|
|
* the 'sack' in question can now be a transport,
|
|
* so this function isn't named very good anymore.
|
|
*
|
|
* @param op
|
|
* who is moving the item.
|
|
* @param sack
|
|
* where to put the object.
|
|
* @param tmp
|
|
* what to put into sack.
|
|
* @param nrof
|
|
* if non zero, then nrof objects is tried to put into sack, else everything is put.
|
|
*/
|
|
void put_object_in_sack(object *op, object *sack, object *tmp, uint32 nrof) {
|
|
tag_t tmp_tag, tmp2_tag;
|
|
object *tmp2, *sack2, *orig = sack;
|
|
char name_sack[MAX_BUF], name_tmp[MAX_BUF];
|
|
|
|
if (sack == tmp)
|
|
return; /* Can't put an object in itself */
|
|
query_name(sack, name_sack, MAX_BUF);
|
|
if (sack->type != CONTAINER && sack->type != TRANSPORT) {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"The %s is not a container.",
|
|
"The %s is not a container.",
|
|
name_sack);
|
|
return;
|
|
}
|
|
/* Disabled, as I see no real problem with allowing god-given items to be inserted into containers -kts */
|
|
/*if (QUERY_FLAG(tmp, FLAG_STARTEQUIP)) {
|
|
query_name(tmp, name_tmp, MAX_BUF);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"You cannot put the %s in the %s.",
|
|
"You cannot put the %s in the %s.",
|
|
name_tmp, name_sack);
|
|
return;
|
|
}*/
|
|
if (tmp->type == CONTAINER) {
|
|
if (tmp->inv) {
|
|
if (tmp->slaying)
|
|
return;
|
|
/* Eneq(@csd.uu.se): If the object to be dropped is a container
|
|
* and does not require a key to be opened,
|
|
* we instead move the contents of that container into the active
|
|
* container, this is only done if the object has something in it.
|
|
* If object is container but need a key, just don't do anything
|
|
*/
|
|
sack2 = tmp;
|
|
query_name(tmp, name_tmp, MAX_BUF);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"You move the items from %s into %s.",
|
|
"You move the items from %s into %s.",
|
|
name_tmp, 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))
|
|
|| (sack->type == TRANSPORT && transport_can_hold(sack, tmp2, tmp2->nrof))) {
|
|
put_object_in_sack(op, sack, tmp2, 0);
|
|
} else {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND,
|
|
MSG_TYPE_COMMAND_FAILURE,
|
|
"Your %s fills up.",
|
|
"Your %s fills up.",
|
|
name_sack);
|
|
break;
|
|
}
|
|
}
|
|
esrv_update_item(UPD_WEIGHT, op, sack2);
|
|
return;
|
|
} else {
|
|
query_name(tmp, name_tmp, MAX_BUF);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"You can not put a %s into a %s",
|
|
"You can not put a %s into a %s",
|
|
name_tmp,
|
|
name_sack);
|
|
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 (nrof && tmp->nrof != nrof) {
|
|
char failure[MAX_BUF];
|
|
object *tmp2 = tmp;
|
|
|
|
tmp2_tag = tmp2->count;
|
|
tmp = get_split_ob(tmp, nrof, failure, sizeof(failure));
|
|
|
|
if (!tmp) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
failure, NULL);
|
|
return;
|
|
}
|
|
} else
|
|
remove_ob(tmp);
|
|
|
|
if (sack->nrof > 1) {
|
|
orig = get_split_ob(sack, sack->nrof-1, NULL, 0);
|
|
set_object_face_main(orig);
|
|
CLEAR_FLAG(orig, FLAG_APPLIED);
|
|
if (sack->env) {
|
|
insert_ob_in_ob(orig, sack->env);
|
|
} else {
|
|
insert_ob_in_map_at(orig, sack->map, NULL, 0, sack->x, sack->y);
|
|
orig->move_off = 0;
|
|
}
|
|
}
|
|
|
|
query_name(tmp, name_tmp, MAX_BUF);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"You put the %s in %s.",
|
|
"You put the %s in %s.",
|
|
name_tmp, name_sack);
|
|
tmp_tag = tmp->count;
|
|
tmp2 = insert_ob_in_ob(tmp, sack);
|
|
if (!QUERY_FLAG(op, FLAG_NO_FIX_PLAYER))
|
|
fix_object(op); /* This is overkill, fix_player() is called somewhere */
|
|
/* in object.c */
|
|
|
|
/* If a transport, need to update all the players in the transport
|
|
* the view of what is in it.
|
|
*/
|
|
if (sack->type == TRANSPORT) {
|
|
for (tmp = sack->inv; tmp; tmp = tmp->below) {
|
|
if (tmp->type == PLAYER)
|
|
tmp->contr->socket.update_look = 1;
|
|
}
|
|
} else {
|
|
/* update the sacks weight */
|
|
esrv_update_item(UPD_WEIGHT, op, sack);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Try to drop an object on the floor.
|
|
*
|
|
* This function was part of drop, now is own function.
|
|
*
|
|
* @param op
|
|
* who is dropping the item.
|
|
* @param tmp
|
|
* item to drop.
|
|
* @param nrof
|
|
* if is non zero, then nrof objects is tried to be dropped.
|
|
* @return
|
|
* object dropped, NULL if it was destroyed.
|
|
* @todo shouldn't tmp be NULL if was_destroyed returns true?
|
|
*/
|
|
object *drop_object(object *op, object *tmp, uint32 nrof) {
|
|
tag_t tmp_tag;
|
|
|
|
if (QUERY_FLAG(tmp, FLAG_NO_DROP)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (QUERY_FLAG(tmp, FLAG_APPLIED)) {
|
|
if (apply_special(op, tmp, AP_UNAPPLY|AP_NO_MERGE))
|
|
return NULL; /* can't unapply it */
|
|
}
|
|
|
|
/* Lauwenmark: Handle for plugin drop event */
|
|
if (execute_event(tmp, EVENT_DROP, op, NULL, NULL, SCRIPT_FIX_ALL) != 0)
|
|
return NULL;
|
|
|
|
/* We are only dropping some of the items. We split the current objec
|
|
* off
|
|
*/
|
|
if (nrof && tmp->nrof != nrof) {
|
|
char failure[MAX_BUF];
|
|
|
|
tmp = get_split_ob(tmp, nrof, failure, sizeof(failure));
|
|
if (!tmp) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
failure, NULL);
|
|
return NULL;
|
|
}
|
|
} else
|
|
remove_ob(tmp);
|
|
|
|
if (QUERY_FLAG(tmp, FLAG_STARTEQUIP)) {
|
|
char name[MAX_BUF];
|
|
|
|
query_name(tmp, name, MAX_BUF);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"You drop the %s. The gods who lent it to you retrieves it.",
|
|
"You drop the %s. The gods who lent it to you retrieves it.",
|
|
name);
|
|
free_object(tmp);
|
|
|
|
if (!QUERY_FLAG(op, FLAG_NO_FIX_PLAYER))
|
|
fix_object(op);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* If SAVE_INTERVAL is commented out, we never want to save
|
|
* the player here.
|
|
*/
|
|
#ifdef SAVE_INTERVAL
|
|
/* I'm not sure why there is a value check - since the save
|
|
* is done every SAVE_INTERVAL seconds, why care the value
|
|
* of what he is dropping?
|
|
*/
|
|
if (op->type == PLAYER
|
|
&& !QUERY_FLAG(tmp, FLAG_UNPAID)
|
|
&& (tmp->nrof ? tmp->value*tmp->nrof : tmp->value > 2000)
|
|
&& (op->contr->last_save_time+SAVE_INTERVAL) <= time(NULL)) {
|
|
save_player(op, 1);
|
|
op->contr->last_save_time = time(NULL);
|
|
}
|
|
#endif /* SAVE_INTERVAL */
|
|
|
|
|
|
tmp->x = op->x;
|
|
tmp->y = op->y;
|
|
|
|
tmp_tag = tmp->count;
|
|
insert_ob_in_map(tmp, op->map, op, 0);
|
|
if (!was_destroyed(tmp, tmp_tag) && !QUERY_FLAG(tmp, FLAG_UNPAID) && tmp->type != MONEY && is_in_shop(op)) {
|
|
sell_item(tmp, op);
|
|
}
|
|
|
|
/* Call this before we update the various windows/players. At least
|
|
* that we, we know the weight is correct.
|
|
*/
|
|
if (!QUERY_FLAG(op, FLAG_NO_FIX_PLAYER)) {
|
|
fix_object(op); /* This is overkill, fix_player() is called somewhere */
|
|
/* in object.c */
|
|
|
|
/* Need to update weight of player */
|
|
if (op->type == PLAYER)
|
|
esrv_update_item(UPD_WEIGHT, op, op);
|
|
}
|
|
return tmp;
|
|
}
|
|
|
|
/**
|
|
* Drop an item, either on the floor or in a container.
|
|
*
|
|
* @param op
|
|
* who is dropping an item.
|
|
* @param tmp
|
|
* what object to drop.
|
|
*/
|
|
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
|
|
*/
|
|
remove_ob(tmp);
|
|
free_object(tmp);
|
|
return;
|
|
} else {
|
|
while (tmp != NULL && tmp->invisible)
|
|
tmp = tmp->below;
|
|
}
|
|
}
|
|
|
|
if (tmp == NULL) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"You don't have anything to drop.", NULL);
|
|
return;
|
|
}
|
|
if (QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"This item is locked", NULL);
|
|
return;
|
|
}
|
|
if (QUERY_FLAG(tmp, FLAG_NO_DROP)) {
|
|
return;
|
|
}
|
|
|
|
if (op->type == PLAYER) {
|
|
if (op->contr->last_used == tmp && op->contr->last_used_id == tmp->count) {
|
|
object *n = NULL;
|
|
|
|
if (tmp->below != NULL)
|
|
n = tmp->below;
|
|
else if (tmp->above != NULL)
|
|
n = tmp->above;
|
|
op->contr->last_used = n;
|
|
if (n != NULL)
|
|
op->contr->last_used_id = n->count;
|
|
else
|
|
op->contr->last_used_id = 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 to drop all items that have not been locked.
|
|
*
|
|
* @param op
|
|
* player.
|
|
* @param params
|
|
* optional specifier, like 'armour', 'weapon' and such.
|
|
* @return
|
|
* 0.
|
|
*/
|
|
int command_dropall(object *op, char *params) {
|
|
object *curinv, *nextinv;
|
|
int count = 0;
|
|
|
|
if (op->inv == NULL) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op,
|
|
MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Nothing to drop!", NULL);
|
|
return 0;
|
|
}
|
|
|
|
curinv = op->inv;
|
|
|
|
if (op->contr)
|
|
count = op->contr->count;
|
|
|
|
/* Set this so we don't call it for _every_ object that
|
|
* is dropped.
|
|
*/
|
|
SET_FLAG(op, FLAG_NO_FIX_PLAYER);
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
if (params == NULL) {
|
|
while (curinv != NULL) {
|
|
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 (op->contr)
|
|
op->contr->count = count;
|
|
}
|
|
curinv = nextinv;
|
|
}
|
|
} else if (strcmp(params, "weapons") == 0) {
|
|
while (curinv != NULL) {
|
|
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 (op->contr)
|
|
op->contr->count = count;
|
|
}
|
|
curinv = nextinv;
|
|
}
|
|
} else if (strcmp(params, "armor") == 0 || strcmp(params, "armour") == 0) {
|
|
while (curinv != NULL) {
|
|
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 (op->contr)
|
|
op->contr->count = count;
|
|
}
|
|
curinv = nextinv;
|
|
}
|
|
} else if (strcmp(params, "food") == 0) {
|
|
while (curinv != NULL) {
|
|
nextinv = curinv->below;
|
|
if (!QUERY_FLAG(curinv, FLAG_INV_LOCKED) && (curinv->type == FOOD || curinv->type == DRINK)) {
|
|
drop(op, curinv);
|
|
if (op->contr)
|
|
op->contr->count = count;
|
|
}
|
|
curinv = nextinv;
|
|
}
|
|
} else if (strcmp(params, "flesh") == 0) {
|
|
while (curinv != NULL) {
|
|
nextinv = curinv->below;
|
|
if (!QUERY_FLAG(curinv, FLAG_INV_LOCKED) && (curinv->type == FLESH)) {
|
|
drop(op, curinv);
|
|
if (op->contr)
|
|
op->contr->count = count;
|
|
}
|
|
curinv = nextinv;
|
|
}
|
|
} else if (strcmp(params, "misc") == 0) {
|
|
while (curinv != NULL) {
|
|
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;
|
|
if (op->contr)
|
|
op->contr->count = count;
|
|
break;
|
|
|
|
default:
|
|
curinv = nextinv;
|
|
break;
|
|
}
|
|
}
|
|
curinv = nextinv;
|
|
}
|
|
}
|
|
op->contr->socket.update_look = 1;
|
|
CLEAR_FLAG(op, FLAG_NO_FIX_PLAYER);
|
|
/* call it now, once */
|
|
fix_object(op);
|
|
/* Need to update weight of player. Likewise, only do it once */
|
|
if (op->type == PLAYER)
|
|
esrv_update_item(UPD_WEIGHT, op, op);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* 'drop' command.
|
|
*
|
|
* @param op
|
|
* player.
|
|
* @param params
|
|
* what to drop.
|
|
* @return
|
|
* 0.
|
|
*/
|
|
int command_drop(object *op, char *params) {
|
|
object *tmp, *next;
|
|
int did_one = 0;
|
|
int ival = 0;
|
|
int missed = 0;
|
|
|
|
if (!params) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Drop what?", NULL);
|
|
return 0;
|
|
} else {
|
|
for (tmp = op->inv; tmp; tmp = next) {
|
|
next = tmp->below;
|
|
if (QUERY_FLAG(tmp, FLAG_NO_DROP) || tmp->invisible)
|
|
continue;
|
|
if ((ival = item_matched_string(op, tmp, params)) > 0) {
|
|
if ((QUERY_FLAG(tmp, FLAG_INV_LOCKED)) && ((ival == 1) || (ival == 2)))
|
|
missed++;
|
|
else
|
|
drop(op, tmp);
|
|
did_one = 1;
|
|
}
|
|
}
|
|
if (!did_one)
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Nothing to drop.", NULL);
|
|
if (missed == 1)
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"One item couldn't be dropped because it was locked.", NULL);
|
|
else if (missed > 1)
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"%d items couldn't be dropped because they were locked.",
|
|
"%d items couldn't be dropped because they were locked.",
|
|
missed);
|
|
}
|
|
if (op->type == PLAYER) {
|
|
op->contr->count = 0;
|
|
op->contr->socket.update_look = 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Put all contents of the container on the ground below the player or in opened container, except locked items.
|
|
*
|
|
* @param container
|
|
* what to empty.
|
|
* @param pl
|
|
* player to drop for.
|
|
*/
|
|
static void empty_container(object *container, object *pl) {
|
|
object *inv;
|
|
object *next;
|
|
int left = 0;
|
|
char name[MAX_BUF];
|
|
|
|
if (!container->inv)
|
|
return;
|
|
|
|
for (inv = container->inv; inv; inv = next) {
|
|
next = inv->below;
|
|
if (QUERY_FLAG(inv, FLAG_INV_LOCKED)) {
|
|
/* you can have locked items in container. */
|
|
left++;
|
|
continue;
|
|
}
|
|
drop(pl, inv);
|
|
if (inv->below == next)
|
|
/* item couldn't be dropped for some reason. */
|
|
left++;
|
|
}
|
|
esrv_update_item(UPD_WEIGHT, pl, container);
|
|
|
|
query_name(container, name, sizeof(name));
|
|
if (left)
|
|
draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, "You empty the %s except %d items.", NULL, name, left);
|
|
else
|
|
draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, "You empty the %s.", NULL, name);
|
|
}
|
|
|
|
/**
|
|
* 'empty' command.
|
|
*
|
|
* @param op
|
|
* player.
|
|
* @param params
|
|
* item specifier.
|
|
* @return
|
|
* 0.
|
|
*/
|
|
int command_empty(object *op, char *params) {
|
|
object *inv;
|
|
object *container;
|
|
|
|
if (!params) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Empty what?", NULL);
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp(params, "all") == 0) {
|
|
for (inv = op->inv; inv; inv = inv->below)
|
|
if (inv->type == CONTAINER)
|
|
empty_container(inv, op);
|
|
return 0;
|
|
}
|
|
|
|
container = find_best_object_match(op, params);
|
|
if (!container) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"No such item.", NULL);
|
|
return 0;
|
|
}
|
|
if (container->type != CONTAINER) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"This is not a container!", NULL);
|
|
return 0;
|
|
}
|
|
empty_container(container, op);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* 'examine' command.
|
|
*
|
|
* @param op
|
|
* player.
|
|
* @param params
|
|
* optional item specifier.
|
|
* @return
|
|
* 0.
|
|
*/
|
|
int command_examine(object *op, char *params) {
|
|
if (!params) {
|
|
object *tmp = op->below;
|
|
|
|
while (tmp && !LOOK_OBJ(tmp))
|
|
tmp = tmp->below;
|
|
if (tmp)
|
|
examine(op, tmp);
|
|
} else {
|
|
object *tmp = find_best_object_match(op, params);
|
|
|
|
if (tmp)
|
|
examine(op, tmp);
|
|
else
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Could not find an object that matches %s",
|
|
"Could not find an object that matches %s",
|
|
params);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* @param op
|
|
* object. Should be a player.
|
|
* @return
|
|
* marked object if still valid, NULL else.
|
|
*/
|
|
object *find_marked_object(object *op) {
|
|
object *tmp;
|
|
|
|
if (!op || !op->contr || !op->contr->mark)
|
|
return NULL;
|
|
|
|
/* 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->count == op->contr->mark_count)
|
|
return tmp;
|
|
else {
|
|
op->contr->mark = NULL;
|
|
op->contr->mark_count = 0;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* 'mark' command, to mark an item for some effects (enchant armor, ...).
|
|
*
|
|
* @param op
|
|
* player.
|
|
* @param params
|
|
* If empty, we print out the currently marked object.
|
|
* Otherwise, try to find a matching object - try best match first.
|
|
* @return
|
|
* 1 or 0.
|
|
*/
|
|
int command_mark(object *op, char *params) {
|
|
char name[MAX_BUF];
|
|
|
|
if (!op->contr)
|
|
return 1;
|
|
if (!params) {
|
|
object *mark = find_marked_object(op);
|
|
if (!mark)
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"You have no marked object.", NULL);
|
|
else {
|
|
query_name(mark, name, MAX_BUF);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"%s is marked.",
|
|
"%s is marked.",
|
|
name);
|
|
}
|
|
} else {
|
|
object *mark1 = find_best_object_match(op, params);
|
|
|
|
if (!mark1) {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Could not find an object that matches %s",
|
|
"Could not find an object that matches %s",
|
|
params);
|
|
return 1;
|
|
} else {
|
|
op->contr->mark = mark1;
|
|
op->contr->mark_count = mark1->count;
|
|
query_name(mark1, name, MAX_BUF);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"Marked item %s",
|
|
"Marked item %s",
|
|
name);
|
|
return 0;
|
|
}
|
|
}
|
|
return 0; /*shouldnt get here */
|
|
}
|
|
|
|
/**
|
|
* Player examine a monster.
|
|
*
|
|
* @param op
|
|
* player.
|
|
* @param tmp
|
|
* monster being examined.
|
|
*/
|
|
void examine_monster(object *op, object *tmp) {
|
|
object *mon = tmp->head ? tmp->head : tmp;
|
|
|
|
if (QUERY_FLAG(mon, FLAG_UNDEAD))
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"It is an undead force.", NULL);
|
|
if (mon->level > op->level)
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"It is likely more powerful than you.", NULL);
|
|
else if (mon->level < op->level)
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"It is likely less powerful than you.", NULL);
|
|
else
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"It is probably as powerful as you.", NULL);
|
|
|
|
if (mon->attacktype&AT_ACID)
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"You smell an acrid odor.", NULL);
|
|
|
|
/* 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:
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"It is in a bad shape.", NULL);
|
|
break;
|
|
|
|
case 2:
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"It is hurt.", NULL);
|
|
break;
|
|
|
|
case 3:
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"It is somewhat hurt.", NULL);
|
|
break;
|
|
|
|
case 4:
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"It is in excellent shape.", NULL);
|
|
break;
|
|
}
|
|
if (present_in_ob(POISONING, mon) != NULL)
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"It looks very ill.", NULL);
|
|
}
|
|
|
|
/**
|
|
* Player examines some object.
|
|
*
|
|
* @param op
|
|
* player.
|
|
* @param tmp
|
|
* object to examine.
|
|
*/
|
|
void examine(object *op, object *tmp) {
|
|
char buf[VERY_BIG_BUF];
|
|
int in_shop;
|
|
int i;
|
|
|
|
buf[0] = '\0';
|
|
|
|
if (tmp == NULL || tmp->type == CLOSE_CON)
|
|
return;
|
|
|
|
/* Put the description in buf. */
|
|
ob_describe(tmp, op, buf, sizeof(buf));
|
|
|
|
/* Send the player the description, prepending "That is" if singular
|
|
* and "Those are" if plural.
|
|
*/
|
|
if (tmp->nrof <= 1)
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"That is %s",
|
|
"That is %s",
|
|
buf);
|
|
else
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"Those are %s",
|
|
"Those are %s",
|
|
buf);
|
|
buf[0] = '\0';
|
|
|
|
if (tmp->custom_name) {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"You name it %s",
|
|
"You name it %s",
|
|
tmp->custom_name);
|
|
}
|
|
|
|
switch (tmp->type) {
|
|
case SPELLBOOK:
|
|
if (QUERY_FLAG(tmp, FLAG_IDENTIFIED) && tmp->inv) {
|
|
char level[100];
|
|
|
|
get_levelnumber(tmp->inv->level, level, 100);
|
|
snprintf(buf, sizeof(buf), "%s is a %s level %s spell", tmp->inv->name, level, tmp->inv->skill);
|
|
}
|
|
break;
|
|
|
|
case BOOK:
|
|
if (tmp->msg != NULL)
|
|
snprintf(buf, sizeof(buf), "Something is written in it.");
|
|
break;
|
|
|
|
case CONTAINER:
|
|
if (tmp->race != NULL) {
|
|
if (tmp->weight_limit && tmp->stats.Str < 100)
|
|
snprintf(buf, sizeof(buf), "It can hold only %s and its weight limit is %.1f kg.", tmp->race, tmp->weight_limit/(10.0*(100-tmp->stats.Str)));
|
|
else
|
|
snprintf(buf, sizeof(buf), "It can hold only %s.", tmp->race);
|
|
} else
|
|
if (tmp->weight_limit && tmp->stats.Str < 100)
|
|
snprintf(buf, sizeof(buf), "Its weight limit is %.1f kg.", tmp->weight_limit/(10.0*(100-tmp->stats.Str)));
|
|
break;
|
|
|
|
case WAND:
|
|
if (QUERY_FLAG(tmp, FLAG_IDENTIFIED))
|
|
snprintf(buf, sizeof(buf), "It has %d charges left.", tmp->stats.food);
|
|
break;
|
|
}
|
|
|
|
if (buf[0] != '\0')
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
buf, NULL);
|
|
|
|
if (tmp->materialname != NULL && !tmp->msg) {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"It is made of: %s.",
|
|
"It is made of: %s.",
|
|
tmp->materialname);
|
|
}
|
|
/* Where to wear this item */
|
|
for (i = 0; i < NUM_BODY_LOCATIONS; i++) {
|
|
if (tmp->body_info[i] < -1) {
|
|
if (op->body_info[i])
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"It goes %s (%d)",
|
|
"It goes %s (%d)",
|
|
body_locations[i].use_name, -tmp->body_info[i]);
|
|
else
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"It goes %s",
|
|
"It goes %s",
|
|
body_locations[i].nonuse_name);
|
|
} else if (tmp->body_info[i]) {
|
|
if (op->body_info[i])
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"It goes %s",
|
|
"It goes %s",
|
|
body_locations[i].use_name);
|
|
else
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"It goes %s",
|
|
"It goes %s",
|
|
body_locations[i].nonuse_name);
|
|
}
|
|
}
|
|
|
|
if (tmp->weight) {
|
|
snprintf(buf, sizeof(buf), tmp->nrof > 1 ? "They weigh %3.3f kg." : "It weighs %3.3f kg.", tmp->weight*((float)(tmp->nrof ? tmp->nrof : 1)/1000.0));
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
buf, NULL);
|
|
}
|
|
|
|
in_shop = is_in_shop(op);
|
|
|
|
if (tmp->value && !QUERY_FLAG(tmp, FLAG_STARTEQUIP) && !QUERY_FLAG(tmp, FLAG_NO_PICK)) {
|
|
char *value = stringbuffer_finish(query_cost_string(tmp, op, F_SELL|F_APPROX, NULL));
|
|
snprintf(buf, sizeof(buf), "You reckon %s worth %s.", tmp->nrof > 1 ? "they are" : "it is", value);
|
|
free(value);
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
buf, NULL);
|
|
if (in_shop) {
|
|
if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
|
|
value = stringbuffer_finish(query_cost_string(tmp, op, F_BUY|F_SHOP, NULL));
|
|
snprintf(buf, sizeof(buf), "%s would cost you %s.", tmp->nrof > 1 ? "They" : "It", value);
|
|
free(value);
|
|
} else {
|
|
value = stringbuffer_finish(query_cost_string(tmp, op, F_SELL+F_SHOP, NULL));
|
|
snprintf(buf, sizeof(buf), "You are offered %s for %s.", value, tmp->nrof > 1 ? "them" : "it");
|
|
free(value);
|
|
}
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
buf, NULL);
|
|
}
|
|
}
|
|
|
|
if (QUERY_FLAG(tmp, FLAG_MONSTER))
|
|
examine_monster(op, tmp);
|
|
|
|
/* Is this item buildable? */
|
|
if (QUERY_FLAG(tmp, FLAG_IS_BUILDABLE))
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"This is a buildable item.", NULL);
|
|
|
|
/* Does the object have a message? Don't show message for all object
|
|
* types - especially if the first entry is a match
|
|
*/
|
|
if (tmp->msg
|
|
&& tmp->type != EXIT
|
|
&& tmp->type != BOOK
|
|
&& tmp->type != CORPSE
|
|
&& !tmp->move_on
|
|
&& strncasecmp(tmp->msg, "@match", 6)) {
|
|
/* This is just a hack so when identifying hte items, we print
|
|
* out the extra message
|
|
*/
|
|
if (need_identify(tmp) && QUERY_FLAG(tmp, FLAG_IDENTIFIED))
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
"The object has a story:", NULL);
|
|
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
tmp->msg, NULL);
|
|
}
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
|
|
" ", " "); /* Blank line */
|
|
}
|
|
|
|
/**
|
|
* Prints object's inventory.
|
|
*
|
|
* @param op
|
|
* who to print for.
|
|
* @param inv
|
|
* if NULL then print op's inventory, else print the inventory of inv.
|
|
*/
|
|
void inventory(object *op, object *inv) {
|
|
object *tmp;
|
|
const char *in;
|
|
int items = 0, length;
|
|
char weight[MAX_BUF], name[MAX_BUF];
|
|
|
|
if (inv == NULL && op == NULL) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Inventory of what object?", NULL);
|
|
return;
|
|
}
|
|
tmp = inv ? inv->inv : op->inv;
|
|
|
|
while (tmp) {
|
|
if ((!tmp->invisible && (inv == NULL || inv->type == CONTAINER || QUERY_FLAG(tmp, FLAG_APPLIED)))
|
|
|| (!op || QUERY_FLAG(op, FLAG_WIZ)))
|
|
items++;
|
|
tmp = tmp->below;
|
|
}
|
|
if (inv == NULL) { /* player's inventory */
|
|
if (items == 0) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"You carry nothing.", NULL);
|
|
return;
|
|
} else {
|
|
length = 28;
|
|
in = "";
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INVENTORY,
|
|
"Inventory:", NULL);
|
|
}
|
|
} else {
|
|
if (items == 0)
|
|
return;
|
|
else {
|
|
length = 28;
|
|
in = " ";
|
|
}
|
|
}
|
|
for (tmp = inv ? inv->inv : op->inv; tmp; tmp = tmp->below) {
|
|
if ((!op || !QUERY_FLAG(op, FLAG_WIZ))
|
|
&& (tmp->invisible || (inv && inv->type != CONTAINER && !QUERY_FLAG(tmp, FLAG_APPLIED))))
|
|
continue;
|
|
query_weight(tmp, weight, MAX_BUF);
|
|
query_name(tmp, name, MAX_BUF);
|
|
if ((!op || QUERY_FLAG(op, FLAG_WIZ)))
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INVENTORY,
|
|
"[fixed]%s- %-*.*s (%5d) %-8s",
|
|
"%s- %-*.*s (%5d) %-8s",
|
|
in, length, length, name, tmp->count, weight);
|
|
else
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INVENTORY,
|
|
"[fixed]%s- %-*.*s %-8s",
|
|
"%s- %-*.*s %-8s",
|
|
in, length+8, length+8, name, weight);
|
|
}
|
|
if (!inv && op) {
|
|
query_weight(op, weight, MAX_BUF);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INVENTORY,
|
|
"[fixed]%-*s %-8s",
|
|
"%-*s %-8s",
|
|
41, "Total weight :", weight);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Utility function to display the pickup mode for a player.
|
|
*
|
|
* @param op
|
|
* must be a player.
|
|
*/
|
|
static void display_new_pickup(const object *op) {
|
|
int i = op->contr->mode;
|
|
|
|
if (!(i&PU_NEWMODE))
|
|
return;
|
|
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d NEWMODE",
|
|
"%d NEWMODE",
|
|
i&PU_NEWMODE ? 1 : 0);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d DEBUG",
|
|
"%d DEBUG",
|
|
i&PU_DEBUG ? 1 : 0);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d INHIBIT",
|
|
"%d INHIBIT",
|
|
i&PU_INHIBIT ? 1 : 0);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d STOP",
|
|
"%d STOP",
|
|
i&PU_STOP ? 1 : 0);
|
|
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d <= x pickup weight/value RATIO (0==off)",
|
|
"%d <= x pickup weight/value RATIO (0==off)",
|
|
(i&PU_RATIO)*5);
|
|
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d FOOD",
|
|
"%d FOOD",
|
|
i&PU_FOOD ? 1 : 0);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d DRINK",
|
|
"%d DRINK",
|
|
i&PU_DRINK ? 1 : 0);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d VALUABLES",
|
|
"%d VALUABLES",
|
|
i&PU_VALUABLES ? 1 : 0);
|
|
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d BOW",
|
|
"%d BOW",
|
|
i&PU_BOW ? 1 : 0);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d ARROW",
|
|
"%d ARROW",
|
|
i&PU_ARROW ? 1 : 0);
|
|
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d HELMET",
|
|
"%d HELMET",
|
|
i&PU_HELMET ? 1 : 0);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d SHIELD",
|
|
"%d SHIELD",
|
|
i&PU_SHIELD ? 1 : 0);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d ARMOUR",
|
|
"%d ARMOUR",
|
|
i&PU_ARMOUR ? 1 : 0);
|
|
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d BOOTS",
|
|
"%d BOOTS",
|
|
i&PU_BOOTS ? 1 : 0);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d GLOVES",
|
|
"%d GLOVES",
|
|
i&PU_GLOVES ? 1 : 0);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d CLOAK",
|
|
"%d CLOAK",
|
|
i&PU_CLOAK ? 1 : 0);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d KEY",
|
|
"%d KEY",
|
|
i&PU_KEY ? 1 : 0);
|
|
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d MISSILEWEAPON",
|
|
"%d MISSILEWEAPON",
|
|
i&PU_MISSILEWEAPON ? 1 : 0);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d ALLWEAPON",
|
|
"%d ALLWEAPON",
|
|
i&PU_ALLWEAPON ? 1 : 0);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d MAGICAL",
|
|
"%d MAGICAL",
|
|
i&PU_MAGICAL ? 1 : 0);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d POTION",
|
|
"%d POTION",
|
|
i&PU_POTION ? 1 : 0);
|
|
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d SPELLBOOK",
|
|
"%d SPELLBOOK",
|
|
i&PU_SPELLBOOK ? 1 : 0);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d SKILLSCROLL",
|
|
"%d SKILLSCROLL",
|
|
i&PU_SKILLSCROLL ? 1 : 0);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d READABLES",
|
|
"%d READABLES",
|
|
i&PU_READABLES ? 1 : 0);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d MAGICDEVICE",
|
|
"%d MAGICDEVICE",
|
|
i&PU_MAGIC_DEVICE ? 1 : 0);
|
|
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d NOT CURSED",
|
|
"%d NOT CURSED",
|
|
i&PU_NOT_CURSED ? 1 : 0);
|
|
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d JEWELS",
|
|
"%d JEWELS",
|
|
i&PU_JEWELS ? 1 : 0);
|
|
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"%d FLESH",
|
|
"%d FLESH",
|
|
i&PU_FLESH ? 1 : 0);
|
|
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO,
|
|
"", "");
|
|
}
|
|
|
|
/**
|
|
* 'pickup' command.
|
|
*
|
|
* @param op
|
|
* player.
|
|
* @param params
|
|
* pickup mode. Can be empty to display the current mode.
|
|
* @return
|
|
* 1 if success, 0 else.
|
|
* @todo trash old pickup mode, merge with new pickup.
|
|
*/
|
|
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 const 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++;
|
|
|
|
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;
|
|
}
|
|
}
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Pickup: invalid item %s\n",
|
|
"Pickup: invalid item %s\n",
|
|
params);
|
|
return 1;
|
|
}
|
|
|
|
if (sscanf(params, "%u", &i) != 1) {
|
|
if (1)
|
|
LOG(llevDebug, "command_pickup: params==NULL\n");
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Usage: pickup <0-7> or <value_density> .", NULL);
|
|
return 1;
|
|
}
|
|
set_pickup_mode(op, i);
|
|
display_new_pickup(op);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Sets the 'old' pickup mode.
|
|
*
|
|
* @param op
|
|
* player.
|
|
* @param i
|
|
* new pickup mode.
|
|
*/
|
|
static void set_pickup_mode(const object *op, int i) {
|
|
switch (op->contr->mode = i) {
|
|
case 0:
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"Mode: Don't pick up.", NULL);
|
|
break;
|
|
|
|
case 1:
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"Mode: Pick up one item.", NULL);
|
|
break;
|
|
|
|
case 2:
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"Mode: Pick up one item and stop.", NULL);
|
|
break;
|
|
|
|
case 3:
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"Mode: Stop before picking up.", NULL);
|
|
break;
|
|
|
|
case 4:
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"Mode: Pick up all items.", NULL);
|
|
break;
|
|
|
|
case 5:
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"Mode: Pick up all items and stop.", NULL);
|
|
break;
|
|
|
|
case 6:
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"Mode: Pick up all magic items.", NULL);
|
|
break;
|
|
|
|
case 7:
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"Mode: Pick up all coins and gems", NULL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 'search-items' command.
|
|
*
|
|
* @param op
|
|
* player.
|
|
* @param params
|
|
* options.
|
|
* @return
|
|
* 1.
|
|
*/
|
|
int command_search_items(object *op, char *params) {
|
|
if (settings.search_items == FALSE)
|
|
return 1;
|
|
|
|
if (params == NULL) {
|
|
if (op->contr->search_str[0] == '\0') {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"Example: search magic+1 "
|
|
"Would automatically pick up all "
|
|
"items containing the word 'magic+1'.",
|
|
NULL);
|
|
return 1;
|
|
}
|
|
op->contr->search_str[0] = '\0';
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"Search mode turned off.", NULL);
|
|
fix_object(op);
|
|
return 1;
|
|
}
|
|
if ((int)strlen(params) >= MAX_BUF) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Search string too long.", NULL);
|
|
return 1;
|
|
}
|
|
strcpy(op->contr->search_str, params);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"Searching for '%s'.",
|
|
"Searching for '%s'.",
|
|
op->contr->search_str);
|
|
fix_object(op);
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Changing the custom name of an item
|
|
*
|
|
* Syntax is: rename \<what object\> to \<new name\>
|
|
* - if 'what object' is omitted, marked object is used
|
|
* - if 'to new name' is omitted, custom name is cleared
|
|
*
|
|
* Names are considered for all purpose having a length <=127 (max length sent to client
|
|
* by server).
|
|
*
|
|
* @param op
|
|
* player.
|
|
* @param params
|
|
* how to rename.
|
|
* @return 1
|
|
*/
|
|
int command_rename_item(object *op, char *params) {
|
|
char buf[VERY_BIG_BUF], name[MAX_BUF];
|
|
int itemnumber;
|
|
object *item = NULL;
|
|
object *tmp;
|
|
char *closebrace;
|
|
size_t counter;
|
|
tag_t tag;
|
|
|
|
if (params) {
|
|
/* Let's skip white spaces */
|
|
while (' ' == *params)
|
|
params++;
|
|
|
|
/* Checking the first part */
|
|
if ((itemnumber = atoi(params)) != 0) {
|
|
for (item = op->inv; item && ((item->count != itemnumber) || item->invisible); item = item->below)
|
|
;
|
|
if (!item) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Tried to rename an invalid item.", NULL);
|
|
return 1;
|
|
}
|
|
while (isdigit(*params) || ' ' == *params)
|
|
params++;
|
|
} else if ('<' == *params) {
|
|
/* Got old name, let's get it & find appropriate matching item */
|
|
closebrace = strchr(params, '>');
|
|
if (!closebrace) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Syntax error!", NULL);
|
|
return 1;
|
|
}
|
|
/* Sanity check for buffer overruns */
|
|
if ((closebrace-params) > 127) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Old name too long (up to 127 characters allowed)!", NULL);
|
|
return 1;
|
|
}
|
|
/* Copy the old name */
|
|
snprintf(buf, sizeof(buf), "%.*s", (int)(closebrace-(params+1)), params+1);
|
|
|
|
/* Find best matching item */
|
|
item = find_best_object_match(op, buf);
|
|
if (!item) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Could not find a matching item to rename.", NULL);
|
|
return 1;
|
|
}
|
|
|
|
/* Now need to move pointer to just after > */
|
|
params = closebrace+1;
|
|
while (' ' == *params)
|
|
params++;
|
|
} else {
|
|
/* Use marked item */
|
|
item = find_marked_object(op);
|
|
if (!item) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"No marked item to rename.", NULL);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* Now let's find the new name */
|
|
if (!strncmp(params, "to ", 3)) {
|
|
params += 3;
|
|
while (' ' == *params)
|
|
params++;
|
|
if ('<' != *params) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Syntax error, expecting < at start of new name!", NULL);
|
|
return 1;
|
|
}
|
|
closebrace = strchr(params+1, '>');
|
|
if (!closebrace) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Syntax error, expecting > at end of new name!", NULL);
|
|
return 1;
|
|
}
|
|
|
|
/* Sanity check for buffer overruns */
|
|
if ((closebrace-params) > 127) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"New name too long (up to 127 characters allowed)!", NULL);
|
|
return 1;
|
|
}
|
|
|
|
/* Copy the new name */
|
|
snprintf(buf, sizeof(buf), "%.*s", (int)(closebrace-(params+1)), params+1);
|
|
|
|
/* Let's check it for weird characters */
|
|
for (counter = 0; counter < strlen(buf); counter++) {
|
|
if (isalnum(buf[counter]))
|
|
continue;
|
|
if (' ' == buf[counter])
|
|
continue;
|
|
if ('\'' == buf[counter])
|
|
continue;
|
|
if ('+' == buf[counter])
|
|
continue;
|
|
if ('_' == buf[counter])
|
|
continue;
|
|
if ('-' == buf[counter])
|
|
continue;
|
|
|
|
/* If we come here, then the name contains an invalid character...
|
|
* tell the player & exit
|
|
*/
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Invalid new name!", NULL);
|
|
return 1;
|
|
}
|
|
} else {
|
|
/* If param contains something, then syntax error... */
|
|
if (strlen(params)) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"Syntax error, expected 'to <' after old name!", NULL);
|
|
return 1;
|
|
}
|
|
/* New name is empty */
|
|
buf[0] = '\0';
|
|
}
|
|
} else {
|
|
/* Last case: params==NULL */
|
|
item = find_marked_object(op);
|
|
if (!item) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"No marked item to rename.", NULL);
|
|
return 1;
|
|
}
|
|
buf[0] = '\0';
|
|
}
|
|
|
|
/* Coming here, everything is fine... */
|
|
if (!strlen(buf)) {
|
|
/* Clear custom name */
|
|
if (item->custom_name == NULL) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
|
|
"This item has no custom name.", NULL);
|
|
return 1;
|
|
}
|
|
|
|
FREE_AND_CLEAR_STR(item->custom_name);
|
|
query_base_name(item, item->nrof > 1 ? 1 : 0, name, MAX_BUF);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"You stop calling your %s with weird names.",
|
|
"You stop calling your %s with weird names.",
|
|
name);
|
|
} else {
|
|
if (item->custom_name != NULL && strcmp(item->custom_name, buf) == 0) {
|
|
query_base_name(item, item->nrof > 1 ? 1 : 0, name, MAX_BUF);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"You keep calling your %s %s.",
|
|
"You keep calling your %s %s.",
|
|
name, buf);
|
|
return 1;
|
|
}
|
|
|
|
/* Set custom name */
|
|
FREE_AND_COPY(item->custom_name, buf);
|
|
|
|
query_base_name(item, item->nrof > 1 ? 1 : 0, name, MAX_BUF);
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
|
|
"Your %s will now be called %s.",
|
|
"Your %s will now be called %s.",
|
|
name, buf);
|
|
}
|
|
|
|
tag = item->count;
|
|
tmp = merge_ob(item, NULL);
|
|
if (tmp == NULL) {
|
|
/* object was not merged - if it was, merge_ob handles updating for us. */
|
|
esrv_update_item(UPD_NAME, op, item);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Alternate way to lock/unlock items (command line).
|
|
*
|
|
* @param op
|
|
* player
|
|
* @param params
|
|
* sent command line.
|
|
*/
|
|
int command_lock_item(object *op, char *params) {
|
|
object *item;
|
|
object *tmp;
|
|
tag_t tag;
|
|
char name[HUGE_BUF];
|
|
|
|
if (!params || strlen(params) == 0) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
|
|
"Lock what item?", "Lock what item?");
|
|
return 1;
|
|
}
|
|
|
|
item = find_best_object_match(op, params);
|
|
if (!item) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
|
|
"Can't find any matching item.", "Can't find any matching item.");
|
|
return 1;
|
|
}
|
|
|
|
query_short_name(item, name, HUGE_BUF);
|
|
if (QUERY_FLAG(item, FLAG_INV_LOCKED)) {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
|
|
"Unlocked %s.", "Unlocked %s.", name);
|
|
CLEAR_FLAG(item, FLAG_INV_LOCKED);
|
|
} else {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
|
|
"Locked %s.", "Locked %s.", name);
|
|
SET_FLAG(item, FLAG_INV_LOCKED);
|
|
}
|
|
|
|
tag = item->count;
|
|
tmp = merge_ob(item, NULL);
|
|
if (tmp == NULL) {
|
|
/* object was not merged, if it was merge_ob handles updates for us */
|
|
esrv_update_item(UPD_FLAGS, op, item);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Try to use an item on another. Items are checked for key/values matching.
|
|
* @param op
|
|
* player.
|
|
* @param params
|
|
* sent string, with all parameters.
|
|
* @return
|
|
* 1.
|
|
*/
|
|
int command_use(object *op, char *params) {
|
|
char *with, copy[MAX_BUF];
|
|
object *first, *second, *add;
|
|
archetype *arch;
|
|
int count;
|
|
sstring data;
|
|
|
|
if (!op->type == PLAYER)
|
|
return 1;
|
|
|
|
snprintf(copy, sizeof(copy), "%s", params);
|
|
with = strstr(copy, " with ");
|
|
if (!with) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, "Syntax is use <item> with <item>.", NULL);
|
|
return 1;
|
|
}
|
|
|
|
with[0] = '\0';
|
|
with = with+strlen(" with ");
|
|
|
|
first = find_best_object_match(op, copy);
|
|
if (!first) {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, "No match for %s.", NULL, copy);
|
|
return 1;
|
|
}
|
|
second = find_best_object_match(op, with);
|
|
if (!second) {
|
|
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, "No match for %s.", NULL, with);
|
|
return 1;
|
|
}
|
|
|
|
snprintf(copy, sizeof(copy), "on_use_with_%s", first->arch->name);
|
|
data = get_ob_key_value(second, copy);
|
|
if (!data) {
|
|
snprintf(copy, sizeof(copy), "on_use_with_%d_%d", first->type, first->subtype);
|
|
data = get_ob_key_value(second, copy);
|
|
if (!data) {
|
|
snprintf(copy, sizeof(copy), "on_use_with_%d", first->type);
|
|
data = get_ob_key_value(second, copy);
|
|
if (!data) {
|
|
draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, "Nothing happens.", NULL);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
while (data != NULL) {
|
|
if (strncmp(data, "add ", 4) == 0) {
|
|
data += 4;
|
|
if (isdigit(*data)) {
|
|
count = atol(data);
|
|
data = strchr(data, ' ')+1;
|
|
} else
|
|
count = 1;
|
|
with = strchr(data, ' ');
|
|
if (!with) {
|
|
strncpy(copy, data, sizeof(copy));
|
|
data = NULL;
|
|
} else {
|
|
*with = '\0';
|
|
strncpy(copy, data, sizeof(copy));
|
|
data += strlen(copy)+1;
|
|
}
|
|
arch = find_archetype(copy);
|
|
if (!arch) {
|
|
LOG(llevError, "Use: invalid archetype %s in %s.\n", copy, second->name);
|
|
return 1;
|
|
}
|
|
add = object_create_arch(arch);
|
|
add->nrof = count;
|
|
insert_ob_in_ob(add, op);
|
|
} else if (strncmp(data, "remove $", 8) == 0) {
|
|
data += 8;
|
|
if (*data == '1') {
|
|
if (first)
|
|
first = decrease_ob(first);
|
|
data += 2;
|
|
} else if (*data == '2') {
|
|
if (second)
|
|
second = decrease_ob(second);
|
|
data += 2;
|
|
} else {
|
|
LOG(llevError, "Use: invalid use string %s in %s\n", data, second->name);
|
|
return 1;
|
|
}
|
|
} else {
|
|
LOG(llevError, "Use: invalid use string %s in %s\n", data, second->name);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|