/*
* This file is part of Deliantra, the Roguelike Realtime MMORPG.
*
* Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
* Copyright (©) 2002 Mark Wedel & Crossfire Development Team
* Copyright (©) 1992 Frank Tore Johansen
*
* Deliantra is free software: you can redistribute it and/or modify it under
* the terms of the Affero 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 Affero GNU General Public License
* and the GNU General Public License along with this program. If not, see
* .
*
* The authors can be reached via e-mail to
*/
#include
#include
#include
#include
#include
/** Defines for DM item stack **/
#define STACK_SIZE 50 /* Stack size, static */
/* Values for 'from' field of get_dm_object */
#define STACK_FROM_NONE 0 /* Item was not found */
#define STACK_FROM_TOP 1 /* Item is stack top */
#define STACK_FROM_STACK 2 /* Item is somewhere in stack */
#define STACK_FROM_NUMBER 3 /* Item is a number (may be top) */
/**
* Enough of the DM functions seem to need this that I broke
* it out to a seperate function. name is the person
* being saught, rq is who is looking for them. This
* prints diagnostics messages, and returns the
* other player, or NULL otherwise.
*/
static player *
get_other_player_from_name (object *op, char *name)
{
if (!name)
return NULL;
for_all_players (pl)
if (!strncmp (pl->ob->name, name, MAX_NAME))
{
if (pl->ob == op)
{
new_draw_info (NDI_UNIQUE, 0, op, "You can't do that to yourself.");
return NULL;
}
if (pl->ns->state != ST_PLAYING)
{
new_draw_info (NDI_UNIQUE, 0, op, "That player is in no state for that right now.");
return NULL;
}
return pl;
}
new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
return 0;
}
static void
dm_stack_pop (player *pl)
{
if (!pl->stack_items || !pl->stack_position)
{
new_draw_info (NDI_UNIQUE, 0, pl->ob, "Empty stack!");
return;
}
pl->stack_position--;
new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Popped item from stack, %d left.", pl->stack_position);
}
/**
* Get current stack top item for player.
* Returns NULL if no stacked item.
* If stacked item disappeared (freed), remove it.
*
* Ryo, august 2004
*/
static object *
dm_stack_peek (player *pl)
{
object *ob;
if (!pl->stack_position)
{
new_draw_info (NDI_UNIQUE, 0, pl->ob, "Empty stack!");
return NULL;
}
ob = find_object (pl->stack_items[pl->stack_position - 1]);
if (!ob)
{
new_draw_info (NDI_UNIQUE, 0, pl->ob, "Stacked item was removed!");
dm_stack_pop (pl);
return NULL;
}
return ob;
}
/**
* Push specified item on player stack.
* Inform player of position.
* Initializes variables if needed.
*/
void
dm_stack_push (player *pl, tag_t item)
{
if (!pl->stack_items)
{
pl->stack_items = (tag_t *) malloc (sizeof (tag_t) * STACK_SIZE);
memset (pl->stack_items, 0, sizeof (tag_t) * STACK_SIZE);
}
if (pl->stack_position == STACK_SIZE)
{
new_draw_info (NDI_UNIQUE, 0, pl->ob, "Item stack full!");
return;
}
pl->stack_items[pl->stack_position] = item;
new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Item stacked as %d.", pl->stack_position);
pl->stack_position++;
}
/**
* Checks 'params' for object code.
*
* Can be:
* * empty => get current object stack top for player
* * number => get item with that tag, stack it for future use
* * $number => get specified stack item
* * "me" => player himself
*
* At function exit, params points to first non-object char
*
* 'from', if not NULL, contains at exit:
* * STACK_FROM_NONE => object not found
* * STACK_FROM_TOP => top item stack, may be NULL if stack was empty
* * STACK_FROM_STACK => item from somewhere in the stack
* * STACK_FROM_NUMBER => item by number, pushed on stack
*
* Ryo, august 2004
*/
static object *
get_dm_object (player *pl, char **params, int *from)
{
int item_tag, item_position;
object *ob;
if (!pl)
return NULL;
if (!params || !*params || **params == '\0')
{
if (from)
*from = STACK_FROM_TOP;
/* No parameter => get stack item */
return dm_stack_peek (pl);
}
/* Let's clean white spaces */
while (**params == ' ')
(*params)++;
/* Next case: number => item tag */
if (sscanf (*params, "%d", &item_tag))
{
/* Move parameter to next item */
while (isdigit (**params))
(*params)++;
/* And skip blanks, too */
while (**params == ' ')
(*params)++;
/* Get item */
ob = find_object (item_tag);
if (!ob)
{
if (from)
*from = STACK_FROM_NONE;
new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "No such item %d!", item_tag);
return NULL;
}
/* Got one, let's push it on stack */
dm_stack_push (pl, item_tag);
if (from)
*from = STACK_FROM_NUMBER;
return ob;
}
/* Next case: $number => stack item */
if (sscanf (*params, "$%d", &item_position))
{
/* Move parameter to next item */
(*params)++;
while (isdigit (**params))
(*params)++;
while (**params == ' ')
(*params)++;
if (item_position >= pl->stack_position)
{
if (from)
*from = STACK_FROM_NONE;
new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "No such stack item %d!", item_position);
return NULL;
}
ob = find_object (pl->stack_items[item_position]);
if (!ob)
{
if (from)
*from = STACK_FROM_NONE;
new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Stack item %d was removed.", item_position);
return NULL;
}
if (from)
*from = item_position < pl->stack_position - 1 ? STACK_FROM_STACK : STACK_FROM_TOP;
return ob;
}
/* Next case: 'me' => return pl->ob */
if (!strncmp (*params, "me", 2))
{
if (from)
*from = STACK_FROM_NUMBER;
dm_stack_push (pl, pl->ob->count);
/* Skip to next token */
(*params) += 2;
while (**params == ' ')
(*params)++;
return pl->ob;
}
/* Last case: get stack top */
if (from)
*from = STACK_FROM_TOP;
return dm_stack_peek (pl);
}
/**
* Actually hides specified player (obviously a DM).
* If 'silent_dm' is non zero, other players are informed of DM entering/leaving,
* else they just think someone left/entered.
*/
static void
do_wizard_hide (object *op, int silent_dm)
{
if (op->contr->hidden)
{
op->contr->hidden = 0;
op->invisible = 1;
new_draw_info (NDI_UNIQUE, 0, op, "You are no longer hidden from other players");
op->map->players++;
new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, "%s has entered the game.", &op->name);
if (!silent_dm)
new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_LT_GREEN, 1, NULL, "The Dungeon Master has arrived!");
}
else
{
op->contr->hidden = 1;
new_draw_info (NDI_UNIQUE, 0, op, "Other players will no longer see you.");
op->map->players--;
if (!silent_dm)
new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_LT_GREEN, 1, NULL, "The Dungeon Master is gone..");
new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, "%s leaves the game.", &op->name);
new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, "%s left the game.", &op->name);
}
}
int
command_hide (object *op, char *params)
{
do_wizard_hide (op, 0);
return 1;
}
/**
* This finds and returns the object which matches the name or
* object nubmer (specified via num #whatever).
*/
static object *
find_object_both (char *params)
{
if (!params)
return NULL;
if (params[0] == '#')
return find_object (atol (params + 1));
else
return find_object_name (params);
}
/**
* Sets the god for some objects. params should contain two values -
* first the object to change, followed by the god to change it to.
*/
int
command_setgod (object *op, char *params)
{
object *ob, *god;
char *str;
if (!params || !(str = strchr (params, ' ')))
{
new_draw_info (NDI_UNIQUE, 0, op, "Usage: setgod object god");
return 0;
}
/* kill the space, and set string to the next param */
*str++ = '\0';
if (!(ob = find_object_both (params)))
{
new_draw_info_format (NDI_UNIQUE, 0, op, "Set whose god - can not find object %s?", params);
return 1;
}
/*
* Perhaps this is overly restrictive? Should we perhaps be able
* to rebless altars and the like?
*/
if (ob->type != PLAYER)
{
new_draw_info_format (NDI_UNIQUE, 0, op, "%s is not a player - can not change its god", &ob->name);
return 1;
}
god = find_god (str);
if (god == NULL)
{
new_draw_info_format (NDI_UNIQUE, 0, op, "No such god %s.", str);
return 1;
}
ob->become_follower (god);
return 1;
}
int
command_freeze (object *op, char *params)
{
int ticks;
player *pl;
if (!params)
{
new_draw_info (NDI_UNIQUE, 0, op, "Usage: freeze [ticks] .");
return 1;
}
ticks = atoi (params);
if (ticks)
{
while ((isdigit (*params) || isspace (*params)) && *params != 0)
params++;
if (*params == 0)
{
new_draw_info (NDI_UNIQUE, 0, op, "Usage: freeze [ticks] .");
return 1;
}
}
else
ticks = 100;
pl = get_other_player_from_name (op, params);
if (!pl)
return 1;
new_draw_info (NDI_UNIQUE | NDI_RED, 0, pl->ob, "You have been frozen by the DM!");
new_draw_info_format (NDI_UNIQUE, 0, op, "You freeze %s for %d ticks", &pl->ob->name, ticks);
pl->ob->speed_left = -(pl->ob->speed * ticks);
return 0;
}
int
command_arrest (object *op, char *params)
{
object *dummy;
player *pl;
if (!op)
return 0;
if (params == NULL)
{
new_draw_info (NDI_UNIQUE, 0, op, "Usage: arrest .");
return 1;
}
pl = get_other_player_from_name (op, params);
if (!pl)
return 1;
dummy = get_jail_exit (pl->ob);
if (!dummy)
{
/* we have nowhere to send the prisoner.... */
new_draw_info (NDI_UNIQUE, 0, op, "can't jail player, there is no map to hold them");
return 0;
}
pl->ob->player_goto (dummy->slaying, dummy->stats.hp, dummy->stats.sp);//TODO
dummy->destroy ();
new_draw_info (NDI_UNIQUE, 0, pl->ob, "You have been arrested.");
new_draw_info (NDI_UNIQUE, 0, op, "OK.");
LOG (llevInfo, "Player %s arrested by %s\n", &pl->ob->name, &op->name);
return 1;
}
int
command_summon (object *op, char *params)
{
int i;
player *pl;
if (!op)
return 0;
if (params == NULL)
{
new_draw_info (NDI_UNIQUE, 0, op, "Usage: summon .");
return 1;
}
pl = get_other_player_from_name (op, params);
if (!pl)
return 1;
maptile *m = op->map;
sint16 nx = op->x;
sint16 ny = op->y;
i = find_free_spot (op, m, nx, ny, 1, SIZEOFFREE1+1);
if (i == -1)
{
new_draw_info (NDI_UNIQUE, 0, op, "Can not find a free spot to place summoned player.");
return 1;
}
nx += freearr_x [i];
ny += freearr_y [i];
if (!xy_normalise (m, nx, ny))
{
new_draw_info (NDI_UNIQUE, 0, op, "Can not find a free spot to place summoned player.");
return 1;
}
pl->ob->player_goto (m->path, nx, ny);
new_draw_info (NDI_UNIQUE, 0, pl->ob, "You are summoned.");
new_draw_info (NDI_UNIQUE, 0, op, "OK.");
return 1;
}
/**
* This function is a real mess, because we're stucking getting
* the entire item description in one block of text, so we just
* can't simply parse it - we need to look for double quotes
* for example. This could actually get much simpler with just a
* little help from the client - if we could get line breaks, it
* makes parsing much easier, eg, something like:
* arch dragon
* name big nasty creature
* hp 5
* sp 30
* Is much easier to parse than
* dragon name "big nasty creature" hp 5 sp 30
* for example.
*/
int
command_create (object *op, char *params)
{
object *tmp = NULL;
int nrof, i, magic, set_magic = 0, set_nrof = 0, gotquote, gotspace;
char buf[MAX_BUF], *cp, *bp = buf, *bp2, *bp3, *endline;
archetype *at, *at_spell = NULL;
artifact *art = NULL;
if (!op)
return 0;
if (params == NULL)
{
new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] [ of ]" " [variable_to_patch setting]");
return 1;
}
bp = params;
/* We need to know where the line ends */
endline = bp + strlen (bp);
if (sscanf (bp, "%d ", &nrof))
{
if ((bp = strchr (params, ' ')) == NULL)
{
new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] [ of ]" " [variable_to_patch setting]");
return 1;
}
bp++;
set_nrof = 1;
LOG (llevDebug, "%s creates: (%d) %s\n", &op->name, nrof, bp);
}
if (sscanf (bp, "%d ", &magic))
{
if ((bp = strchr (bp, ' ')) == NULL)
{
new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] [ of ]" " [variable_to_patch setting]");
return 1;
}
bp++;
set_magic = 1;
LOG (llevDebug, "%s creates: (%d) (%d) %s\n", &op->name, nrof, magic, bp);
}
if ((cp = strstr (bp, " of ")) != NULL)
{
*cp = '\0';
cp += 4;
}
for (bp2 = bp; *bp2; bp2++)
{
if (*bp2 == ' ')
{
*bp2 = '\0';
bp2++;
break;
}
}
if ((at = archetype::find (bp)) == NULL)
{
new_draw_info (NDI_UNIQUE, 0, op, "No such archetype.");
return 1;
}
if (cp)
{
char spell_name[MAX_BUF], *fsp = NULL;
/*
* Try to find a spell object for this. Note that
* we also set up spell_name which is only
* the first word.
*/
at_spell = archetype::find (cp);
if (!at_spell || at_spell->type != SPELL)
at_spell = find_archetype_by_object_name (cp);
if (!at_spell || at_spell->type != SPELL)
{
assign (spell_name, cp);
fsp = strchr (spell_name, ' ');
if (fsp)
{
*fsp = 0;
fsp++;
at_spell = archetype::find (spell_name);
/* Got a spell, update the first string pointer */
if (at_spell && at_spell->type == SPELL)
bp2 = cp + strlen (spell_name) + 1;
else
at_spell = NULL;
}
}
/* OK - we didn't find a spell - presume the 'of'
* in this case means its an artifact.
*/
if (!at_spell)
{
if (find_artifactlist (at->type) == NULL)
new_draw_info_format (NDI_UNIQUE, 0, op, "No artifact list for type %d\n", at->type);
else
{
art = find_artifactlist (at->type)->items;
while (art)
{
if (!strcmp (&art->item->name, cp))
break;
art = art->next;
}
if (!art)
new_draw_info_format (NDI_UNIQUE, 0, op, "No such artifact ([%d] of %s)", at->type, cp);
}
LOG (llevDebug, "%s creates: (%d) (%d) (%s) of (%s)\n", &op->name, set_nrof ? nrof : 0, set_magic ? magic : 0, bp, cp);
}
} /* if cp */
if ((at->type == ROD || at->type == WAND || at->type == SCROLL ||
at->type == HORN || at->type == SPELLBOOK) && !at_spell)
{
new_draw_info_format (NDI_UNIQUE, 0, op, "Unable to find spell %s for object that needs it, or it is of wrong type", cp);
return 1;
}
/*
* Rather than have two different blocks with a lot of similar code,
* just create one object, do all the processing, and then determine
* if that one object should be inserted or if we need to make copies.
*/
tmp = at->instance ();
if (set_magic)
set_abs_magic (tmp, magic);
if (art)
give_artifact_abilities (tmp, art->item);
if (tmp->need_identify ())
{
tmp->set_flag (FLAG_IDENTIFIED);
tmp->clr_flag (FLAG_KNOWN_MAGICAL);
}
/*
* This entire block here tries to find variable pairings,
* eg, 'hp 4' or the like. The mess here is that values
* can be quoted (eg "my cool sword"); So the basic logic
* is we want to find two spaces, but if we got a quote,
* any spaces there don't count.
*/
while (*bp2 && bp2 <= endline)
{
gotspace = 0;
gotquote = 0;
/* find the first quote */
for (bp3 = bp2; *bp3 && gotspace < 2 && gotquote < 2; bp3++)
{
/* Found a quote - now lets find the second one */
if (*bp3 == '"')
{
*bp3 = ' ';
bp2 = bp3 + 1; /* Update start of string */
bp3++;
gotquote++;
while (*bp3)
{
if (*bp3 == '"')
{
*bp3 = '\0';
gotquote++;
}
else
bp3++;
}
}
else if (*bp3 == ' ')
gotspace++;
}
/*
* If we got two spaces, send the second one to null.
* if we've reached the end of the line, increase gotspace -
* this is perfectly valid for the list entry listed.
*/
if (gotspace == 2 || gotquote == 2)
{
bp3--; /* Undo the extra increment */
*bp3 = '\0';
}
else if (*bp3 == '\0')
gotspace++;
if ((gotquote && gotquote != 2) || (gotspace != 2 && gotquote != 2))
{
/*
* Unfortunately, we've clobbered lots of values, so printing
* out what we have probably isn't useful. Break out, because
* trying to recover is probably won't get anything useful
* anyways, and we'd be confused about end of line pointers
* anyways.
*/
new_draw_info_format (NDI_UNIQUE, 0, op, "Malformed create line: %s", bp2);
break;
}
/* bp2 should still point to the start of this line,
* with bp3 pointing to the end
*/
if (set_variable (tmp, bp2) == -1)
new_draw_info_format (NDI_UNIQUE, 0, op, "Unknown variable %s", bp2);
else
new_draw_info_format (NDI_UNIQUE, 0, op, "(%s#%d)->%s", &tmp->name, tmp->count, bp2);
bp2 = bp3 + 1;
}
if (at->nrof)
{
if (at_spell)
tmp->insert (at_spell->instance ());
tmp->x = op->x;
tmp->y = op->y;
tmp->map = op->map;
if (set_nrof)
tmp->nrof = nrof;
op->insert (tmp);
/* Let's put this created item on stack so dm can access it easily. */
dm_stack_push (op->contr, tmp->count);
return 1;
}
else
{
for (i = 0; i < (set_nrof ? nrof : 1); i++)
{
object *prev = 0, *head = 0;
for (archetype *atmp = at; atmp; atmp = (archetype *)atmp->more)
{
object *dup = atmp->instance ();
if (at_spell)
insert_ob_in_ob (at_spell->instance (), dup);
/*
* The head is what contains all the important bits,
* so just copying it over should be fine.
*/
if (!head)
{
head = dup;
tmp->copy_to (dup);
}
dup->x = op->x + dup->arch->x;
dup->y = op->y + dup->arch->y;
dup->map = op->map;
if (head != dup)
{
dup->head = head;
prev->more = dup;
}
prev = dup;
}
if (head->flag [FLAG_ALIVE])
{
object *check = head;
int size_x = 0;
int size_y = 0;
while (check)
{
size_x = max (size_x, check->arch->x);
size_y = max (size_y, check->arch->y);
check = check->more;
}
if (out_of_map (op->map, head->x + size_x, head->y + size_y))
{
if (head->x < size_x || head->y < size_y)
{
dm_stack_pop (op->contr);
head->destroy ();
new_draw_info (NDI_UNIQUE, 0, op, "Object too big to insert in map, or wrong position.");
tmp->destroy ();
return 1;
}
check = head;
while (check)
{
check->x -= size_x;
check->y -= size_y;
check = check->more;
}
}
insert_ob_in_map (head, op->map, op, 0);
}
else
head = insert_ob_in_ob (head, op);
/* Let's put this created item on stack so dm can access it easily. */
/* Wonder if we really want to push all of these, but since
* things like rods have nrof 0, we want to cover those.
*/
dm_stack_push (op->contr, head->count);
if (at->randomitems && !at_spell)
create_treasure (at->randomitems, head, GT_APPLY, op->map->difficulty, 0);
}
/* free the one we used to copy */
tmp->destroy ();
}
return 1;
}
/*
* Now follows dm-commands which are also acceptable from sockets
*/
int
command_inventory (object *op, char *params)
{
int i;
object *tmp;
if (!params || !sscanf (params, "%d", &i) || !(tmp = find_object (i)))
{
op->contr->failmsg ("Inventory of what object (nr)?");
return 1;
}
op->contr->infobox (MSG_CHANNEL ("examine"), tmp->query_inventory (op));
return 1;
}
/* just show player's their skills for now. Dm's can
* already see skills w/ inventory command - b.t.
*/
int
command_skills (object *op, char *params)
{
show_skills (op, params);
return 0;
}
int
command_dump (object *op, char *params)
{
object *tmp;
tmp = get_dm_object (op->contr, ¶ms, NULL);
if (!tmp)
return 1;
char *dump = dump_object (tmp);
new_draw_info (NDI_UNIQUE, 0, op, dump);
free (dump);
if (tmp->flag [FLAG_OBJ_ORIGINAL])
new_draw_info (NDI_UNIQUE, 0, op, "Object is marked original");
return 1;
}
int
command_patch (object *op, char *params)
{
char *arg, *arg2;
object *tmp;
tmp = get_dm_object (op->contr, ¶ms, NULL);
if (!tmp)
/* Player already informed of failure */
return 1;
/* params set to first value by get_dm_default */
arg = params;
if (arg == NULL)
{
new_draw_info (NDI_UNIQUE, 0, op, "Patch what values?");
return 1;
}
if ((arg2 = strchr (arg, ' ')))
arg2++;
if (set_variable (tmp, arg) == -1)
new_draw_info_format (NDI_UNIQUE, 0, op, "Unknown variable %s", arg);
else
new_draw_info_format (NDI_UNIQUE, 0, op, "(%s#%d)->%s=%s", &tmp->name, tmp->count, arg, arg2);
return 1;
}
int
command_remove (object *op, char *params)
{
object *tmp;
int from;
tmp = get_dm_object (op->contr, ¶ms, &from);
if (!tmp)
{
new_draw_info (NDI_UNIQUE, 0, op, "Remove what object (nr)?");
return 1;
}
if (tmp->type == PLAYER)
{
new_draw_info (NDI_UNIQUE, 0, op, "Unable to remove a player!");
return 1;
}
if (tmp->flag [FLAG_REMOVED])
{
new_draw_info_format (NDI_UNIQUE, 0, op, "%s is already removed!", query_name (tmp));
return 1;
}
if (from != STACK_FROM_STACK)
/* Item is either stack top, or is a number thus is now stack top, let's remove it */
dm_stack_pop (op->contr);
/* Always work on the head - otherwise object will get in odd state */
if (tmp->head)
tmp = tmp->head;
tmp->remove ();
return 1;
}
int
command_free (object *op, char *params)
{
object *tmp;
int from;
tmp = get_dm_object (op->contr, ¶ms, &from);
if (!tmp)
{
new_draw_info (NDI_UNIQUE, 0, op, "Free what object (nr)?");
return 1;
}
if (from != STACK_FROM_STACK)
/* Item is either stack top, or is a number thus is now stack top, let's remove it */
dm_stack_pop (op->contr);
if (tmp->head)
tmp = tmp->head;
tmp->destroy ();
return 1;
}
/**
* This adds exp to a player. We now allow adding to a specific skill.
*/
int
command_addexp (object *op, char *params)
{
char buf[MAX_BUF], skill[MAX_BUF];
int q;
long long i; // use sint64 and finally provide format specifiers for sint64 etc. via configure
object *skillob = NULL;
skill[0] = '\0';
if ((params == NULL) || (strlen (params) > MAX_BUF) || ((q = sscanf (params, "%s %lld %[^\n]", buf, &i, skill)) < 2))
{
new_draw_info (NDI_UNIQUE, 0, op, "Usage: addexp [].");
return 1;
}
for_all_players (pl)
if (!strncmp (pl->ob->name, buf, MAX_NAME))
{
if (q >= 3)
{
skillob = find_skill_by_name (pl->ob, skill);
if (!skillob)
{
new_draw_info_format (NDI_UNIQUE, 0, op, "Unable to find skill %s in %s", skill, buf);
return 1;
}
i = check_exp_adjust (skillob, i);
skillob->stats.exp += i;
calc_perm_exp (skillob);
player_lvl_adj (pl->ob, skillob);
}
pl->ob->stats.exp += i;
calc_perm_exp (pl->ob);
player_lvl_adj (pl->ob, NULL);
return 1;
}
new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
return 1;
}
/**************************************************************************/
/* Mods made by Tyler Van Gorder, May 10-13, 1992. */
/* CSUChico : tvangod@cscihp.ecst.csuchico.edu */
/**************************************************************************/
int
command_stats (object *op, char *params)
{
char buf[MAX_BUF];
if (params == NULL || params[0] == '\0')
{
new_draw_info (NDI_UNIQUE, 0, op, "Who?");
return 1;
}
for_all_players (pl)
if (!strcmp (&pl->ob->name, params))
{
sprintf (buf, "Str : %-2d H.P. : %-4d MAX : %d", pl->ob->stats.Str, pl->ob->stats.hp, pl->ob->stats.maxhp);
new_draw_info (NDI_UNIQUE, 0, op, buf);
sprintf (buf, "Dex : %-2d S.P. : %-4d MAX : %d", pl->ob->stats.Dex, pl->ob->stats.sp, pl->ob->stats.maxsp);
new_draw_info (NDI_UNIQUE, 0, op, buf);
sprintf (buf, "Con : %-2d AC : %-4d WC : %d", pl->ob->stats.Con, pl->ob->stats.ac, pl->ob->stats.wc);
new_draw_info (NDI_UNIQUE, 0, op, buf);
sprintf (buf, "Int : %-2d Damage : %d", pl->ob->stats.Int, pl->ob->stats.dam);
new_draw_info (NDI_UNIQUE, 0, op, buf);
sprintf (buf, "Wis : %-2d EXP : %" PRId64, pl->ob->stats.Wis, pl->ob->stats.exp);
new_draw_info (NDI_UNIQUE, 0, op, buf);
sprintf (buf, "Pow : %-2d Grace : %d", pl->ob->stats.Pow, pl->ob->stats.grace);
new_draw_info (NDI_UNIQUE, 0, op, buf);
sprintf (buf, "Cha : %-2d Food : %d", pl->ob->stats.Cha, pl->ob->stats.food);
new_draw_info (NDI_UNIQUE, 0, op, buf);
return 1;
}
new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
return 1;
}
int
command_abil (object *op, char *params)
{
char thing[20], thing2[20];
int iii;
char buf[MAX_BUF];
iii = 0;
thing[0] = '\0';
thing2[0] = '\0';
if (params == NULL || !sscanf (params, "%s %s %d", thing, thing2, &iii) || thing == NULL)
{
new_draw_info (NDI_UNIQUE, 0, op, "Who?");
return 1;
}
if (thing2 == NULL)
{
new_draw_info (NDI_UNIQUE, 0, op, "You can't change that.");
return 1;
}
if (iii < MIN_STAT || iii > MAX_STAT)
{
new_draw_info (NDI_UNIQUE, 0, op, "Illegal range of stat.\n");
return 1;
}
for_all_players (pl)
{
if (!strcmp (&pl->ob->name, thing))
{
if (!strcmp ("str", thing2)) pl->ob->stats.Str = iii, pl->orig_stats.Str = iii;
if (!strcmp ("dex", thing2)) pl->ob->stats.Dex = iii, pl->orig_stats.Dex = iii;
if (!strcmp ("con", thing2)) pl->ob->stats.Con = iii, pl->orig_stats.Con = iii;
if (!strcmp ("wis", thing2)) pl->ob->stats.Wis = iii, pl->orig_stats.Wis = iii;
if (!strcmp ("cha", thing2)) pl->ob->stats.Cha = iii, pl->orig_stats.Cha = iii;
if (!strcmp ("int", thing2)) pl->ob->stats.Int = iii, pl->orig_stats.Int = iii;
if (!strcmp ("pow", thing2)) pl->ob->stats.Pow = iii, pl->orig_stats.Pow = iii;
sprintf (buf, "%s has been altered.", &pl->ob->name);
new_draw_info (NDI_UNIQUE, 0, op, buf);
pl->ob->update_stats ();
return 1;
}
}
new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
return 1;
}
int
command_nowiz (object *op, char *params)
{ /* 'nodm' is alias */
op->clr_flag (FLAG_WIZ);
op->clr_flag (FLAG_WIZPASS);
op->clr_flag (FLAG_WIZCAST);
op->clr_flag (FLAG_WIZLOOK);
op->contr->do_los = 1;
if (op->contr->hidden)
{
new_draw_info (NDI_UNIQUE, 0, op, "You are no longer hidden from other players");
op->map->players++;
new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, "%s has entered the game.", &op->name);
op->contr->hidden = 0;
op->invisible = 1;
}
else
new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_LT_GREEN, 1, NULL, "The Dungeon Master is gone..");
return 1;
}
/**
* object *op is trying to become dm.
* pl_name is name supplied by player. Restrictive DM will make it harder
* for socket users to become DM - in that case, it will check for the players
* character name.
*/
static int
checkdm (object *op, const char *pl_name, const char *pl_passwd, const char *pl_host)
{
FILE *dmfile;
char buf[MAX_BUF];
char line_buf[160], name[160], passwd[160], host[160];
#ifdef RESTRICTIVE_DM
*pl_name = op->name ? op->name : "*";
#endif
sprintf (buf, "%s/%s", settings.confdir, DMFILE);
if ((dmfile = fopen (buf, "r")) == NULL)
{
LOG (llevDebug, "Could not find DM file.\n");
return 0;
}
while (fgets (line_buf, 160, dmfile) != NULL)
{
if (line_buf[0] == '#')
continue;
if (sscanf (line_buf, "%[^:]:%[^:]:%s\n", name, passwd, host) != 3)
{
LOG (llevError, "Warning - malformed dm file entry: %s\n", line_buf);
}
else if ((!strcmp (name, "*") || (pl_name && !strcmp (pl_name, name)))
&& (!strcmp (passwd, "*") || !strcmp (passwd, pl_passwd)) && (!strcmp (host, "*") || !strcmp (host, pl_host)))
{
fclose (dmfile);
return (1);
}
}
fclose (dmfile);
return (0);
}
static int
do_wizard_dm (object *op, char *params, int silent)
{
if (!op->contr)
return 0;
if (op->flag [FLAG_WIZ])
{
new_draw_info (NDI_UNIQUE, 0, op, "You are already the Dungeon Master!");
return 0;
}
if (checkdm (op, op->name, (params ? params : "*"), op->contr->ns->host))
{
op->set_flag (FLAG_WIZ);
op->set_flag (FLAG_WIZPASS);
op->set_flag (FLAG_WIZCAST);
op->set_flag (FLAG_WIZLOOK);
op->contr->do_los = 1;
new_draw_info (NDI_UNIQUE, 0, op, "Ok, you are the Dungeon Master!");
op->contr->write_buf[0] = '\0';
if (!silent)
new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_LT_GREEN, 1, NULL, "The Dungeon Master has arrived!");
return 1;
}
else
{
new_draw_info (NDI_UNIQUE, 0, op, "Sorry Pal, I don't think so.");
op->contr->write_buf[0] = '\0';
return 0;
}
}
/*
* Actual command to perhaps become dm. Changed aroun a bit in version 0.92.2
* - allow people on sockets to become dm, and allow better dm file
*/
int
command_dm (object *op, char *params)
{
if (!op->contr)
return 0;
do_wizard_dm (op, params, 0);
return 1;
}
int
command_invisible (object *op, char *params)
{
if (op)
{
op->invisible += 100;
update_object (op, UP_OBJ_CHANGE);
new_draw_info (NDI_UNIQUE, 0, op, "You turn invisible.");
}
return 0;
}
/**
* Returns spell object (from archetypes) by name.
* Returns NULL if 0 or more than one spell matches.
* Used for wizard's learn spell/prayer.
*
* op is the player issuing the command.
*/
static object *
get_spell_by_name (object *op, shstr_cmp spell_name)
{
/* First check for full name matches. */
int conflict_found = 0;
archetype *found = 0;
for_all_archetypes (at)
{
if (at->type != SPELL)
continue;
if (at->object::name != spell_name)
continue;
if (found)
{
if (!conflict_found)
{
conflict_found = 1;
new_draw_info_format (NDI_UNIQUE, 0, op, "More than one archetype matches the spell name %s:", &spell_name);
new_draw_info_format (NDI_UNIQUE, 0, op, "- %s", &found->archname);
}
new_draw_info_format (NDI_UNIQUE, 0, op, "- %s", &at->archname);
return 0;
}
found = at;
}
/* Return if exactly one archetype matches. */
if (found)
return found->instance ();
/* No spell found: just print an error message. */
new_draw_info_format (NDI_UNIQUE, 0, op, "The spell does not exist.");
return 0;
}
static int
command_learn_spell_or_prayer (object *op, char *params, int special_prayer)
{
object *tmp;
if (op->contr == NULL || params == NULL)
{
new_draw_info (NDI_UNIQUE, 0, op, "Which spell do you want to learn?");
return 0;
}
tmp = get_spell_by_name (op, params);
if (tmp == NULL)
{
return 0;
}
if (check_spell_known (op, tmp->name))
{
new_draw_info_format (NDI_UNIQUE, 0, op, "You already know the spell %s.", &tmp->name);
return 0;
}
do_learn_spell (op, tmp, special_prayer);
tmp->destroy ();
return 1;
}
int
command_learn_spell (object *op, char *params)
{
return command_learn_spell_or_prayer (op, params, 0);
}
int
command_learn_special_prayer (object *op, char *params)
{
return command_learn_spell_or_prayer (op, params, 1);
}
int
command_forget_spell (object *op, char *params)
{
object *spell;
if (op->contr == NULL || params == NULL)
{
new_draw_info (NDI_UNIQUE, 0, op, "Which spell do you want to forget?");
return 0;
}
spell = op->find_spell (params);
if (spell == NULL)
{
new_draw_info_format (NDI_UNIQUE, 0, op, "You do not know the spell %s.", params);
return 0;
}
do_forget_spell (op, spell->name);
return 1;
}
/**
* A players wants to become DM and hide.
* Let's see if that's authorized.
* Make sure to not tell anything to anyone.
*/
int
command_dmhide (object *op, char *params)
{
if (!do_wizard_dm (op, params, 1))
return 0;
do_wizard_hide (op, 1);
return 1;
}
/**
* Pop the stack top.
*/
int
command_stack_pop (object *op, char *params)
{
dm_stack_pop (op->contr);
return 0;
}
/**
* Push specified item on stack.
*/
int
command_stack_push (object *op, char *params)
{
object *ob;
int from;
ob = get_dm_object (op->contr, ¶ms, &from);
if (ob && from != STACK_FROM_NUMBER)
/* Object was from stack, need to push it again */
dm_stack_push (op->contr, ob->count);
return 0;
}
/**
* Displays stack contents.
*/
int
command_stack_list (object *op, char *params)
{
int item;
object *display;
player *pl = op->contr;
new_draw_info (NDI_UNIQUE, 0, op, "Item stack contents:");
for (item = 0; item < pl->stack_position; item++)
{
display = find_object (pl->stack_items[item]);
if (display)
new_draw_info_format (NDI_UNIQUE, 0, op, " %d : %s [%d]", item, &display->name, display->count);
else
/* Item was freed */
new_draw_info_format (NDI_UNIQUE, 0, op, " %d : (lost item: %d)", item, pl->stack_items[item]);
}
return 0;
}
/**
* Empty DM item stack.
*/
int
command_stack_clear (object *op, char *params)
{
op->contr->stack_position = 0;
new_draw_info (NDI_UNIQUE, 0, op, "Item stack cleared.");
return 0;
}
int
command_insert_into (object *op, char *params)
{
object *left, *right;
int left_from, right_from;
left = get_dm_object (op->contr, ¶ms, &left_from);
if (!left)
{
new_draw_info (NDI_UNIQUE, 0, op, "Insert into what object?");
return 0;
}
if (left_from == STACK_FROM_NUMBER)
/* Item was stacked, remove it else right will be the same... */
dm_stack_pop (op->contr);
right = get_dm_object (op->contr, ¶ms, &right_from);
if (!right)
{
new_draw_info (NDI_UNIQUE, 0, op, "Insert what item?");
return 0;
}
if (left_from == STACK_FROM_TOP && right_from == STACK_FROM_TOP)
{
/*
* Special case: both items were taken from stack top.
* Override the behaviour, taking left as item just below top, if exists.
* See function description for why.
* Besides, can't insert an item into itself.
*/
if (op->contr->stack_position > 1)
{
left = find_object (op->contr->stack_items[op->contr->stack_position - 2]);
if (left)
new_draw_info (NDI_UNIQUE, 0, op, "(Note: item to insert into taken from undertop)");
else
/* Stupid case: item under top was freed, fallback to stack top */
left = right;
}
}
if (left == right)
{
new_draw_info (NDI_UNIQUE, 0, op, "Can't insert an object into itself!");
return 0;
}
if (right->type == PLAYER)
{
new_draw_info (NDI_UNIQUE, 0, op, "Can't insert a player into something!");
return 0;
}
if (!right->flag [FLAG_REMOVED])
right->remove ();
object *inserted = insert_ob_in_ob (right, left);
new_draw_info_format (NDI_UNIQUE, 0, op, "Inserted %s in %s", query_name (inserted), query_name (left));
return 0;
}