Crossfire Server, Branch 1.12  R12190
c_wiz.c
Go to the documentation of this file.
00001 /*
00002  * static char *rcsid_c_wiz_c =
00003  *   "$Id: c_wiz.c 13939 2010-09-29 19:21:57Z ryo_saeba $";
00004  */
00005 
00006 /*
00007     CrossFire, A Multiplayer game for X-windows
00008 
00009     Copyright (C) 2006 Mark Wedel & Crossfire Development Team
00010     Copyright (C) 1992 Frank Tore Johansen
00011 
00012     This program is free software; you can redistribute it and/or modify
00013     it under the terms of the GNU General Public License as published by
00014     the Free Software Foundation; either version 2 of the License, or
00015     (at your option) any later version.
00016 
00017     This program is distributed in the hope that it will be useful,
00018     but WITHOUT ANY WARRANTY; without even the implied warranty of
00019     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020     GNU General Public License for more details.
00021 
00022     You should have received a copy of the GNU General Public License
00023     along with this program; if not, write to the Free Software
00024     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00025 
00026     The authors can be reached via e-mail at crossfire-devel@real-time.com
00027 */
00028 
00035 #include <global.h>
00036 #ifndef __CEXTRACT__
00037 #include <sproto.h>
00038 #endif
00039 #include <spells.h>
00040 #include <treasure.h>
00041 #include <skills.h>
00042 
00043 /* Defines for DM item stack **/
00044 #define STACK_SIZE         50   
00046 enum {
00047     STACK_FROM_NONE    = 0,   
00048     STACK_FROM_TOP     = 1,   
00049     STACK_FROM_STACK   = 2,   
00050     STACK_FROM_NUMBER  = 3    
00051 };
00052 
00067 static player *get_other_player_from_name(object *op, const char *name) {
00068     player *pl;
00069 
00070     if (!name)
00071         return NULL;
00072 
00073     for (pl = first_player; pl != NULL; pl = pl->next)
00074         if (!strncmp(pl->ob->name, name, MAX_NAME))
00075             break;
00076 
00077     if (pl == NULL) {
00078         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00079                       "No such player.", NULL);
00080         return NULL;
00081     }
00082 
00083     if (pl->ob == op) {
00084         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00085                       "You can't do that to yourself.", NULL);
00086         return NULL;
00087     }
00088     if (pl->state != ST_PLAYING) {
00089         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00090                       "That player is in no state for that right now.", NULL);
00091         return NULL;
00092     }
00093     return pl;
00094 }
00095 
00108 int command_loadtest(object *op, char *params) {
00109     uint32 x, y;
00110     char buf[1024];
00111 
00112     draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DEBUG,
00113                   "loadtest will stress server through teleporting at different map places. "
00114                   "Use at your own risk.  Very long loop used so server may have to be reset. "
00115                   "type loadtest TRUE to run",
00116                   NULL);
00117     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DEBUG,
00118                          "{%s}",
00119                          "{%s}",
00120                          params);
00121     if (!params)
00122         return 0;
00123     if (strncmp(params, "TRUE", 4))
00124         return 0;
00125 
00126     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DEBUG,
00127                          "gogogo", NULL);
00128 
00129     for (x = 0; x < settings.worldmaptilesx; x++) {
00130         for (y = 0; y < settings.worldmaptilesy; y++) {
00131             snprintf(buf, sizeof(buf), "/world/world_%u_%u", x+settings.worldmapstartx, y+settings.worldmapstarty);
00132             command_goto(op, buf);
00133         }
00134     }
00135 
00136     return 0;
00137 }
00138 
00147 void do_wizard_hide(object *op, int silent_dm) {
00148     if (op->contr->hidden) {
00149         op->contr->hidden = 0;
00150         op->invisible = 1;
00151         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
00152                       "You are no longer hidden from other players", NULL);
00153         op->map->players++;
00154         draw_ext_info_format(NDI_UNIQUE|NDI_ALL|NDI_DK_ORANGE, 5, NULL,
00155                              MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_PLAYER,
00156                              "%s has entered the game.",
00157                              "%s has entered the game.",
00158                              op->name);
00159         if (!silent_dm) {
00160             draw_ext_info(NDI_UNIQUE|NDI_ALL|NDI_LT_GREEN, 1, NULL,
00161                           MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_DM,
00162                           "The Dungeon Master has arrived!", NULL);
00163         }
00164     } else {
00165         op->contr->hidden = 1;
00166         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
00167                       "Other players will no longer see you.", NULL);
00168         op->map->players--;
00169         if (!silent_dm) {
00170             draw_ext_info(NDI_UNIQUE|NDI_ALL|NDI_LT_GREEN, 1, NULL,
00171                           MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_DM,
00172                           "The Dungeon Master is gone..", NULL);
00173         }
00174         draw_ext_info_format(NDI_UNIQUE|NDI_ALL|NDI_DK_ORANGE, 5, NULL,
00175                              MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_PLAYER,
00176                              "%s leaves the game.",
00177                              "%s leaves the game.",
00178                              op->name);
00179         draw_ext_info_format(NDI_UNIQUE|NDI_ALL|NDI_DK_ORANGE, 5, NULL,
00180                              MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_PLAYER,
00181                              "%s left the game.",
00182                              "%s left the game.",
00183                              op->name);
00184     }
00185 }
00186 
00197 int command_hide(object *op, char *params) {
00198     do_wizard_hide(op, 0);
00199     return 1;
00200 }
00201 
00211 static object *find_object_both(char *params) {
00212     if (!params)
00213         return NULL;
00214     if (params[0] == '#')
00215         return find_object(atol(params+1));
00216     else
00217         return find_object_name(params);
00218 }
00219 
00232 int command_setgod(object *op, char *params) {
00233     object *ob;
00234     const object *god;
00235     char *str;
00236 
00237     if (!params || !(str = strchr(params, ' '))) {
00238         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00239                       "Usage: setgod object god", NULL);
00240         return 0;
00241     }
00242 
00243     /* kill the space, and set string to the next param */
00244     *str++ = '\0';
00245     if (!(ob = find_object_both(params))) {
00246         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00247                              "Set whose god - can not find object %s?",
00248                              "Set whose god - can not find object %s?",
00249                              params);
00250         return 1;
00251     }
00252 
00253     /*
00254      * Perhaps this is overly restrictive?  Should we perhaps be able
00255      * to rebless altars and the like?
00256      */
00257     if (ob->type != PLAYER) {
00258         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00259                              "%s is not a player - can not change its god",
00260                              "%s is not a player - can not change its god",
00261                              ob->name);
00262         return 1;
00263     }
00264 
00265     god = find_god(str);
00266     if (god == NULL) {
00267         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00268                              "No such god %s.",
00269                              "No such god %s.",
00270                              str);
00271         return 1;
00272     }
00273 
00274     become_follower(ob, god);
00275     return 1;
00276 }
00277 
00293 int command_banish(object *op, char *params) {
00294     player *pl;
00295     FILE *banishfile;
00296     char buf[MAX_BUF];
00297     time_t now;
00298 
00299     if (!params) {
00300         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00301                       "Usage: banish <player>.", NULL);
00302         return 1;
00303     }
00304 
00305     pl = get_other_player_from_name(op, params);
00306     if (!pl)
00307         return 1;
00308 
00309     snprintf(buf, sizeof(buf), "%s/%s", settings.localdir, BANISHFILE);
00310 
00311     if ((banishfile = fopen(buf, "a")) == NULL) {
00312         LOG(llevDebug, "Could not find file banish_file.\n");
00313         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00314                       "Could not find banish_file.", NULL);
00315         return 0;
00316     }
00317 
00318     now = time(NULL);
00319     /*
00320      * Record this as a comment - then we don't have to worry about changing
00321      * the parsing code.
00322      */
00323     fprintf(banishfile, "# %s (%s) banned by %s at %s\n", pl->ob->name, pl->socket.host, op->name, ctime(&now));
00324     fprintf(banishfile, "*@%s\n", pl->socket.host);
00325     fclose(banishfile);
00326 
00327     LOG(llevDebug, "! %s banned %s from IP: %s.\n", op->name, pl->ob->name, pl->socket.host);
00328 
00329     draw_ext_info_format(NDI_UNIQUE|NDI_RED, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
00330                          "You banish %s",
00331                          "You banish %s",
00332                          pl->ob->name);
00333 
00334     draw_ext_info_format(NDI_UNIQUE|NDI_ALL|NDI_RED, 5, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_DM,
00335                          "%s banishes %s from the land!",
00336                          "%s banishes %s from the land!",
00337                          op->name, pl->ob->name);
00338     command_kick(op, pl->ob->name);
00339     return 1;
00340 }
00341 
00352 int command_kick(object *op, const char *params) {
00353     struct pl *pl;
00354 
00355     for (pl = first_player; pl != NULL; pl = pl->next) {
00356         if ((params == NULL || !strcmp(pl->ob->name, params)) && pl->ob != op) {
00357             object *op;
00358             int removed = 0;
00359 
00360             op = pl->ob;
00361             if (!QUERY_FLAG(op, FLAG_REMOVED)) {
00362                 /* Avion : Here we handle the KICK global event */
00363                 execute_global_event(EVENT_KICK, op, params);
00364                 remove_ob(op);
00365                 removed = 1;
00366             }
00367             op->direction = 0;
00368             draw_ext_info_format(NDI_UNIQUE|NDI_ALL|NDI_RED, 5, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_DM,
00369                                  "%s is kicked out of the game.",
00370                                  "%s is kicked out of the game.",
00371                                  op->name);
00372             strcpy(op->contr->killer, "left");
00373             check_score(op, 0); /* Always check score */
00374 
00375             /*
00376              * not sure how the player would be freed, but did see
00377              * a crash here - if that is the case, don't save the
00378              * the player.
00379              */
00380             if (!removed && !QUERY_FLAG(op, FLAG_FREED)) {
00381                 (void)save_player(op, 0);
00382                 if (op->map)
00383                     op->map->players--;
00384             }
00385 #if MAP_MAXTIMEOUT
00386             if (op->map)
00387                 op->map->timeout = MAP_TIMEOUT(op->map);
00388 #endif
00389             pl->socket.status = Ns_Dead;
00390         }
00391     }
00392 
00393     return 1;
00394 }
00395 
00406 int command_overlay_save(object *op, char *params) {
00407     if (!op)
00408         return 0;
00409 
00410     if (save_map(op->map, SAVE_MODE_OVERLAY) < 0)
00411         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
00412                       "Overlay save error!", NULL);
00413     else
00414         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
00415                       "Current map has been saved as an overlay.", NULL);
00416 
00417     return 1;
00418 }
00419 
00430 int command_overlay_reset(object *op, char *params) {
00431     char filename[MAX_BUF];
00432     struct stat stats;
00433 
00434     create_overlay_pathname(op->map->path, filename, MAX_BUF);
00435     if (!stat(filename, &stats))
00436         if (!unlink(filename))
00437             draw_ext_info(NDI_UNIQUE, 0, op,  MSG_TYPE_COMMAND,  MSG_TYPE_COMMAND_DM,
00438                           "Overlay successfully removed.", NULL);
00439         else
00440             draw_ext_info(NDI_UNIQUE, 0, op,  MSG_TYPE_COMMAND,  MSG_TYPE_COMMAND_DM,
00441                           "Overlay couldn't be removed.", NULL);
00442     else
00443         draw_ext_info(NDI_UNIQUE, 0, op,  MSG_TYPE_COMMAND,  MSG_TYPE_COMMAND_DM,
00444                       "No overlay for current map.", NULL);
00445 
00446     return 1;
00447 }
00448 
00459 int command_toggle_shout(object *op, char *params) {
00460     player *pl;
00461 
00462     if (!params) {
00463         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00464                       "Usage: toggle_shout <player>.", NULL);
00465         return 1;
00466     }
00467 
00468     pl = get_other_player_from_name(op, params);
00469     if (!pl)
00470         return 1;
00471 
00472     if (pl->ob->contr->no_shout == 0) {
00473         pl->ob->contr->no_shout = 1;
00474 
00475         draw_ext_info(NDI_UNIQUE|NDI_RED, 0, pl->ob, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_DM,
00476                       "You have been muzzled by the DM!", NULL);
00477         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
00478                              "You muzzle %s.",
00479                              "You muzzle %s.",
00480                              pl->ob->name);
00481 
00482         /* Avion : Here we handle the MUZZLE global event */
00483         execute_global_event(EVENT_MUZZLE, pl->ob, params);
00484 
00485         return 1;
00486     } else {
00487         pl->ob->contr->no_shout = 0;
00488         draw_ext_info(NDI_UNIQUE|NDI_ORANGE, 0, pl->ob, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_DM,
00489                       "You are allowed to shout and chat again.", NULL);
00490         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
00491                              "You remove %s's muzzle.",
00492                              "You remove %s's muzzle.",
00493                              pl->ob->name);
00494         return 1;
00495     }
00496 }
00497 
00508 int command_shutdown(object *op, char *params) {
00509     /*
00510      * We need to give op - command_kick expects it.  however, this means
00511      * the op won't get kicked off, so we do it ourselves
00512      */
00513     command_kick(op, NULL);
00514     check_score(op, 0); /* Always check score */
00515     (void)save_player(op, 0);
00516     play_again(op);
00517     cleanup();
00518     /* not reached */
00519     return 1;
00520 }
00521 
00532 int command_goto(object *op, char *params) {
00533     char *name;
00534     object *dummy;
00535 
00536     if (!op)
00537         return 0;
00538 
00539     if (params == NULL) {
00540         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00541                       "Go to what level?", NULL);
00542         return 1;
00543     }
00544 
00545     name = params;
00546     dummy = get_object();
00547     dummy->map = op->map;
00548     EXIT_PATH(dummy) = add_string(name);
00549     dummy->name = add_string(name);
00550 
00551     enter_exit(op, dummy);
00552     free_object(dummy);
00553     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
00554                          "Difficulty: %d.",
00555                          "Difficulty: %d.",
00556                          op->map->difficulty);
00557 
00558     return 1;
00559 }
00560 
00571 int command_freeze(object *op, char *params) {
00572     int ticks;
00573     player *pl;
00574 
00575     if (!params) {
00576         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00577                       "Usage: freeze [ticks] <player>.", NULL);
00578         return 1;
00579     }
00580 
00581     ticks = atoi(params);
00582     if (ticks) {
00583         while ((isdigit(*params) || isspace(*params)) && *params != 0)
00584             params++;
00585         if (*params == 0) {
00586             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00587                           "Usage: freeze [ticks] <player>.", NULL);
00588             return 1;
00589         }
00590     } else
00591         ticks = 100;
00592 
00593     pl = get_other_player_from_name(op, params);
00594     if (!pl)
00595         return 1;
00596 
00597     draw_ext_info(NDI_UNIQUE|NDI_RED, 0, pl->ob, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_DM,
00598                   "You have been frozen by the DM!", NULL);
00599 
00600     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
00601                          "You freeze %s for %d ticks",
00602                          "You freeze %s for %d ticks",
00603                          pl->ob->name, ticks);
00604 
00605     pl->ob->speed_left = -(pl->ob->speed*ticks);
00606     return 0;
00607 }
00608 
00619 int command_arrest(object *op, char *params) {
00620     object *dummy;
00621     player *pl;
00622 
00623     if (!op)
00624         return 0;
00625     if (params == NULL) {
00626         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00627                       "Usage: arrest <player>.", NULL);
00628         return 1;
00629     }
00630     pl = get_other_player_from_name(op, params);
00631     if (!pl)
00632         return 1;
00633     dummy = get_jail_exit(pl->ob);
00634     if (!dummy) {
00635         /* we have nowhere to send the prisoner....*/
00636         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00637                       "Can't jail player, there is no map to hold them", NULL);
00638         return 0;
00639     }
00640     enter_exit(pl->ob, dummy);
00641     free_object(dummy);
00642     draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_DM,
00643                   "You have been arrested.", NULL);
00644     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
00645                          "Jailed %s",
00646                          "Jailed %s",
00647                          pl->ob->name);
00648     LOG(llevInfo, "Player %s arrested by %s\n", pl->ob->name, op->name);
00649     return 1;
00650 }
00651 
00661 int command_summon(object *op, char *params) {
00662     int i;
00663     object *dummy;
00664     player *pl;
00665 
00666     if (!op)
00667         return 0;
00668 
00669     if (params == NULL) {
00670         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00671                       "Usage: summon <player>.", NULL);
00672         return 1;
00673     }
00674 
00675     pl = get_other_player_from_name(op, params);
00676     if (!pl)
00677         return 1;
00678 
00679     i = find_free_spot(op, op->map, op->x, op->y, 1, 9);
00680     if (i == -1) {
00681         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00682                       "Can not find a free spot to place summoned player.", NULL);
00683         return 1;
00684     }
00685 
00686     dummy = get_object();
00687     EXIT_PATH(dummy) = add_string(op->map->path);
00688     EXIT_X(dummy) = op->x+freearr_x[i];
00689     EXIT_Y(dummy) = op->y+freearr_y[i];
00690     enter_exit(pl->ob, dummy);
00691     free_object(dummy);
00692     draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_DM,
00693                   "You are summoned.", NULL);
00694     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
00695                          "You summon %s",
00696                          "You summon %s",
00697                          pl->ob->name);
00698     return 1;
00699 }
00700 
00711 /* mids 01/16/2002 */
00712 int command_teleport(object *op, char *params) {
00713     int i;
00714     object *dummy;
00715     player *pl;
00716 
00717     if (!op)
00718         return 0;
00719 
00720     if (params == NULL) {
00721         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00722                       "Usage: teleport <player>.", NULL);
00723         return 0;
00724     }
00725 
00726     pl = find_player_partial_name(params);
00727     if (!pl) {
00728         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00729                       "No such player or ambiguous name.", NULL);
00730         return 0;
00731     }
00732 
00733     i = find_free_spot(pl->ob, pl->ob->map, pl->ob->x, pl->ob->y, 1, 9);
00734     if (i == -1) {
00735         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00736                       "Can not find a free spot to teleport to.", NULL);
00737         return 0;
00738     }
00739 
00740     dummy = get_object();
00741     EXIT_PATH(dummy) = add_string(pl->ob->map->path);
00742     EXIT_X(dummy) = pl->ob->x+freearr_x[i];
00743     EXIT_Y(dummy) = pl->ob->y+freearr_y[i];
00744     enter_exit(op, dummy);
00745     free_object(dummy);
00746     if (!op->contr->hidden)
00747         draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_DM,
00748                       "You see a portal open.", NULL);
00749     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
00750                          "You teleport to %s",
00751                          "You teleport to %s",
00752                          pl->ob->name);
00753 
00754     return 1;
00755 }
00756 
00783 int command_create(object *op, char *params) {
00784     object *tmp = NULL;
00785     int i, magic, set_magic = 0, set_nrof = 0, gotquote, gotspace;
00786     uint32 nrof;
00787     char buf[MAX_BUF], *cp, *bp = buf, *bp2, *bp3, *bp4, *endline;
00788     archetype *at, *at_spell = NULL;
00789     artifact *art = NULL;
00790 
00791     if (!op)
00792         return 0;
00793 
00794     if (params == NULL) {
00795         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00796                       "Usage: create [nr] [magic] <archetype> [ of <artifact>] [variable_to_patch setting]",
00797                       NULL);
00798         return 1;
00799     }
00800     bp = params;
00801 
00802     /* We need to know where the line ends */
00803     endline = bp+strlen(bp);
00804 
00805     if (sscanf(bp, "%u ", &nrof)) {
00806         if ((bp = strchr(params, ' ')) == NULL) {
00807             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00808                           "Usage: create [nr] [magic] <archetype> [ of <artifact>] [variable_to_patch setting]",
00809                           NULL);
00810             return 1;
00811         }
00812         bp++;
00813         set_nrof = 1;
00814         LOG(llevDebug, "%s creates: (%u) %s\n", op->name, nrof, bp);
00815     }
00816     if (sscanf(bp, "%d ", &magic)) {
00817         if ((bp = strchr(bp, ' ')) == NULL) {
00818             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00819                           "Usage: create [nr] [magic] <archetype> [ of <artifact>] [variable_to_patch setting]",
00820                           NULL);
00821             return 1;
00822         }
00823         bp++;
00824         set_magic = 1;
00825         LOG(llevDebug, "%s creates: (%d) (%d) %s\n", op->name, nrof, magic, bp);
00826     }
00827     if ((cp = strstr(bp, " of ")) != NULL) {
00828         *cp = '\0';
00829         cp += 4;
00830     }
00831     for (bp2 = bp; *bp2; bp2++) {
00832         if (*bp2 == ' ') {
00833             *bp2 = '\0';
00834             bp2++;
00835             break;
00836         }
00837     }
00838 
00839     if ((at = find_archetype(bp)) == NULL) {
00840         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00841                       "No such archetype.", NULL);
00842         return 1;
00843     }
00844 
00845     if (cp) {
00846         char spell_name[MAX_BUF], *fsp = NULL;
00847 
00848         /*
00849          * Try to find a spell object for this. Note that
00850          * we also set up spell_name which is only
00851          * the first word.
00852          */
00853 
00854         at_spell = find_archetype(cp);
00855         if (!at_spell || at_spell->clone.type != SPELL)
00856             at_spell = find_archetype_by_object_name(cp);
00857         if (!at_spell || at_spell->clone.type != SPELL) {
00858             strcpy(spell_name, cp);
00859             fsp = strchr(spell_name, ' ');
00860             if (fsp) {
00861                 *fsp = 0;
00862                 fsp++;
00863                 at_spell = find_archetype(spell_name);
00864 
00865                 /* Got a spell, update the first string pointer */
00866                 if (at_spell && at_spell->clone.type == SPELL)
00867                     bp2 = cp+strlen(spell_name)+1;
00868                 else
00869                     at_spell = NULL;
00870             } else
00871                 at_spell = NULL;
00872         }
00873 
00874         /* OK - we didn't find a spell - presume the 'of'
00875          * in this case means its an artifact.
00876          */
00877         if (!at_spell) {
00878             if (find_artifactlist(at->clone.type) == NULL) {
00879                 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00880                                      "No artifact list for type %d\n",
00881                                      "No artifact list for type %d\n",
00882                                      at->clone.type);
00883             } else {
00884                 art = find_artifactlist(at->clone.type)->items;
00885 
00886                 do {
00887                     if (!strcmp(art->item->name, cp))
00888                         break;
00889                     art = art->next;
00890                 } while (art != NULL);
00891                 if (!art) {
00892                     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00893                                          "No such artifact ([%d] of %s)",
00894                                          "No such artifact ([%d] of %s)",
00895                                          at->clone.type, cp);
00896                 }
00897             }
00898             LOG(llevDebug, "%s creates: (%d) (%d) (%s) of (%s)\n", op->name, set_nrof ? nrof : 0, set_magic ? magic : 0, bp, cp);
00899         }
00900     } /* if cp */
00901 
00902     if ((at->clone.type == ROD || at->clone.type == WAND || at->clone.type == SCROLL || at->clone.type == HORN || at->clone.type == SPELLBOOK)
00903     && !at_spell) {
00904         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00905                              "Unable to find spell %s for object that needs it, or it is of wrong type",
00906                              "Unable to find spell %s for object that needs it, or it is of wrong type",
00907                              cp);
00908         return 1;
00909     }
00910 
00911     /*
00912      * Rather than have two different blocks with a lot of similar code,
00913      * just create one object, do all the processing, and then determine
00914      * if that one object should be inserted or if we need to make copies.
00915      */
00916     tmp = object_create_arch(at);
00917     if (settings.real_wiz == FALSE)
00918         SET_FLAG(tmp, FLAG_WAS_WIZ);
00919     if (set_magic)
00920         set_abs_magic(tmp, magic);
00921     if (art)
00922         give_artifact_abilities(tmp, art->item);
00923     if (need_identify(tmp)) {
00924         SET_FLAG(tmp, FLAG_IDENTIFIED);
00925         CLEAR_FLAG(tmp, FLAG_KNOWN_MAGICAL);
00926     }
00927 
00928     /*
00929      * This entire block here tries to find variable pairings,
00930      * eg, 'hp 4' or the like.  The mess here is that values
00931      * can be quoted (eg "my cool sword");  So the basic logic
00932      * is we want to find two spaces, but if we got a quote,
00933      * any spaces there don't count.
00934      */
00935     while (*bp2 && bp2 <= endline) {
00936         bp4 = NULL;
00937         gotspace = 0;
00938         gotquote = 0;
00939         /* find the first quote */
00940         for (bp3 = bp2; *bp3 && gotspace < 2 && gotquote < 2; bp3++) {
00941 
00942             /* Found a quote - now lets find the second one */
00943             if (*bp3 == '"') {
00944                 *bp3 = ' ';
00945                 bp2 = bp3+1;    /* Update start of string */
00946                 bp3++;
00947                 gotquote++;
00948                 while (*bp3) {
00949                     if (*bp3 == '"') {
00950                         *bp3 = '\0';
00951                         gotquote++;
00952                     } else
00953                         bp3++;
00954                 }
00955             } else if (*bp3 == ' ') {
00956                 gotspace++;
00957             }
00958         }
00959 
00960         /*
00961          * If we got two spaces, send the second one to null.
00962          * if we've reached the end of the line, increase gotspace -
00963          * this is perfectly valid for the list entry listed.
00964          */
00965         if (gotspace == 2 || gotquote == 2) {
00966             bp3--;      /* Undo the extra increment */
00967             *bp3 = '\0';
00968         } else if (*bp3 == '\0')
00969             gotspace++;
00970 
00971         if ((gotquote && gotquote != 2)
00972         || (gotspace != 2 && gotquote != 2)) {
00973             /*
00974              * Unfortunately, we've clobbered lots of values, so printing
00975              * out what we have probably isn't useful.  Break out, because
00976              * trying to recover is probably won't get anything useful
00977              * anyways, and we'd be confused about end of line pointers
00978              * anyways.
00979              */
00980             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00981                                  "Malformed create line: %s",
00982                                  "Malformed create line: %s",
00983                                  bp2);
00984             break;
00985         }
00986         /* bp2 should still point to the start of this line,
00987          * with bp3 pointing to the end
00988          */
00989         if (set_variable(tmp, bp2) == -1)
00990             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00991                                  "Unknown variable %s",
00992                                  "Unknown variable %s",
00993                                  bp2);
00994         else
00995             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
00996                                  "(%s#%d)->%s",
00997                                  "(%s#%d)->%s",
00998                                  tmp->name, tmp->count, bp2);
00999         bp2 = bp3+1;
01000     }
01001 
01002     if (at->clone.nrof) {
01003         if (at_spell)
01004             insert_ob_in_ob(arch_to_object(at_spell), tmp);
01005 
01006         tmp->x = op->x;
01007         tmp->y = op->y;
01008         if (set_nrof)
01009             tmp->nrof = nrof;
01010         tmp->map = op->map;
01011 
01012         if (at->clone.randomitems != NULL && !at_spell)
01013             create_treasure(at->clone.randomitems, tmp, GT_APPLY,
01014                             op->map->difficulty, 0);
01015 
01016         /* Multipart objects can't be in inventory, put'em on floor. */
01017         if (!tmp->more) {
01018             tmp = insert_ob_in_ob(tmp, op);
01019         } else {
01020             insert_ob_in_map_at(tmp, op->map, op, 0, op->x, op->y);
01021         }
01022 
01023         /* Let's put this created item on stack so dm can access it easily. */
01024         dm_stack_push(op->contr, tmp->count);
01025 
01026         return 1;
01027     } else {
01028         for (i = 0; i < (set_nrof ? nrof : 1); i++) {
01029             archetype *atmp;
01030             object *prev = NULL, *head = NULL, *dup;
01031 
01032             for (atmp = at; atmp != NULL; atmp = atmp->more) {
01033                 dup = arch_to_object(atmp);
01034 
01035                 if (at_spell)
01036                     insert_ob_in_ob(arch_to_object(at_spell), dup);
01037 
01038                 /*
01039                  * The head is what contains all the important bits,
01040                  * so just copying it over should be fine.
01041                  */
01042                 if (head == NULL) {
01043                     head = dup;
01044                     copy_object(tmp, dup);
01045                 }
01046                 if (settings.real_wiz == FALSE)
01047                     SET_FLAG(dup, FLAG_WAS_WIZ);
01048                 dup->x = op->x+dup->arch->clone.x;
01049                 dup->y = op->y+dup->arch->clone.y;
01050                 dup->map = op->map;
01051 
01052                 if (head != dup) {
01053                     dup->head = head;
01054                     prev->more = dup;
01055                 }
01056                 prev = dup;
01057             }
01058 
01059             if (QUERY_FLAG(head, FLAG_ALIVE)) {
01060                 object *check = head;
01061                 int size_x = 0;
01062                 int size_y = 0;
01063 
01064                 while (check) {
01065                     size_x = MAX(size_x, check->arch->clone.x);
01066                     size_y = MAX(size_y, check->arch->clone.y);
01067                     check = check->more;
01068                 }
01069 
01070                 if (out_of_map(op->map, head->x+size_x, head->y+size_y)) {
01071                     if (head->x < size_x || head->y < size_y) {
01072                         dm_stack_pop(op->contr);
01073                         free_object(head);
01074                         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01075                                       "Object too big to insert in map, or wrong position.", NULL);
01076                         free_object(tmp);
01077                         return 1;
01078                     }
01079 
01080                     check = head;
01081                     while (check) {
01082                         check->x -= size_x;
01083                         check->y -= size_y;
01084                         check = check->more;
01085                     }
01086                 }
01087 
01088                 insert_ob_in_map(head, op->map, op, 0);
01089             } else
01090                 head = insert_ob_in_ob(head, op);
01091 
01092             /* Let's put this created item on stack so dm can access it easily. */
01093             /* Wonder if we really want to push all of these, but since
01094              * things like rods have nrof 0, we want to cover those.
01095              */
01096             dm_stack_push(op->contr, head->count);
01097 
01098             if (at->clone.randomitems != NULL && !at_spell)
01099                 create_treasure(at->clone.randomitems, head, GT_APPLY, op->map->difficulty, 0);
01100         }
01101 
01102         /* free the one we used to copy */
01103         free_object(tmp);
01104     }
01105 
01106     return 1;
01107 }
01108 
01109 /*
01110  * Now follows dm-commands which are also acceptable from sockets
01111  */
01112 
01123 int command_inventory(object *op, char *params) {
01124     object *tmp;
01125     int i;
01126 
01127     if (!params) {
01128         inventory(op, NULL);
01129         return 0;
01130     }
01131 
01132     if (!sscanf(params, "%d", &i) || (tmp = find_object(i)) == NULL) {
01133         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01134                       "Inventory of what object (nr)?", NULL);
01135         return 1;
01136     }
01137 
01138     inventory(op, tmp);
01139     return 1;
01140 }
01141 
01156 int command_skills(object *op, char *params) {
01157     show_skills(op, params);
01158     return 0;
01159 }
01160 
01171 int command_dump(object *op, char *params) {
01172     object *tmp;
01173     StringBuffer *sb;
01174     char *diff;
01175 
01176     tmp = get_dm_object(op->contr, &params, NULL);
01177     if (!tmp)
01178         return 1;
01179 
01180     sb = stringbuffer_new();
01181     dump_object(tmp, sb);
01182     diff = stringbuffer_finish(sb);
01183     draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM, diff, diff);
01184     free(diff);
01185     if (QUERY_FLAG(tmp, FLAG_OBJ_ORIGINAL))
01186         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01187                       "Object is marked original", NULL);
01188     return 1;
01189 }
01190 
01202 int command_mon_aggr(object *op, char *params) {
01203     if (op->enemy || !QUERY_FLAG(op, FLAG_UNAGGRESSIVE)) {
01204         op->enemy = NULL;
01205         SET_FLAG(op, FLAG_UNAGGRESSIVE);
01206         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01207                       "Aggression turned OFF", NULL);
01208     } else {
01209         CLEAR_FLAG(op, FLAG_FRIENDLY);
01210         CLEAR_FLAG(op, FLAG_UNAGGRESSIVE);
01211         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01212                       "Aggression turned ON", NULL);
01213     }
01214 
01215     return 1;
01216 }
01217 
01232 int command_possess(object *op, char *params) {
01233     object *victim;
01234     player *pl;
01235     int i;
01236     char buf[MAX_BUF];
01237 
01238     victim = NULL;
01239     if (params != NULL) {
01240         if (sscanf(params, "%d", &i))
01241             victim = find_object(i);
01242         else if (sscanf(params, "%s", buf))
01243             victim = find_object_name(buf);
01244     }
01245     if (victim == NULL) {
01246         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01247                       "Patch what object (nr)?", NULL);
01248         return 1;
01249     }
01250 
01251     if (victim == op) {
01252         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01253                       "As insane as you are, I cannot allow you to possess yourself.", NULL);
01254         return 1;
01255     }
01256 
01257     /* make the switch */
01258     pl = op->contr;
01259     victim->contr = pl;
01260     pl->ob = victim;
01261     victim->type = PLAYER;
01262     SET_FLAG(victim, FLAG_WIZ);
01263 
01264     /* basic patchup */
01265     /* The use of hard coded values is terrible.  Note
01266      * that really, to be fair, this shouldn't get changed at
01267      * all - if you are possessing a kobold, you should have the
01268      * same limitations.  As it is, as more body locations are added,
01269      * this will give this player more locations than perhaps
01270      * they should be allowed.
01271      */
01272     for (i = 0; i < NUM_BODY_LOCATIONS; i++)
01273         if (i == 1 || i == 6 || i == 8 || i == 9)
01274             victim->body_info[i] = 2;
01275         else
01276             victim->body_info[i] = 1;
01277 
01278     esrv_new_player(pl, 80); /* just pick a weight, we don't care */
01279     esrv_send_inventory(victim, victim);
01280 
01281     fix_object(victim);
01282 
01283     do_some_living(victim);
01284     return 1;
01285 }
01286 
01296 int command_patch(object *op, char *params) {
01297     char *arg, *arg2;
01298     object *tmp;
01299 
01300     tmp = get_dm_object(op->contr, &params, NULL);
01301     if (!tmp)
01302         /* Player already informed of failure */
01303         return 1;
01304 
01305     /* params set to first value by get_dm_default */
01306     arg = params;
01307     if (arg == NULL) {
01308         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01309                       "Patch what values?", NULL);
01310         return 1;
01311     }
01312 
01313     if ((arg2 = strchr(arg, ' ')))
01314         arg2++;
01315     if (settings.real_wiz == FALSE)
01316         SET_FLAG(tmp, FLAG_WAS_WIZ); /* To avoid cheating */
01317     if (set_variable(tmp, arg) == -1)
01318         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01319                              "Unknown variable %s",
01320                              "Unknown variable %s",
01321                              arg);
01322     else {
01323         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01324                              "(%s#%d)->%s=%s",
01325                              "(%s#%d)->%s=%s",
01326                              tmp->name, tmp->count, arg, arg2);
01327     }
01328 
01329     return 1;
01330 }
01331 
01342 int command_remove(object *op, char *params) {
01343     object *tmp;
01344     int from;
01345 
01346     tmp = get_dm_object(op->contr, &params, &from);
01347     if (!tmp) {
01348         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01349                       "Remove what object (nr)?", NULL);
01350         return 1;
01351     }
01352 
01353     if (tmp->type == PLAYER) {
01354         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01355                       "Unable to remove a player!", NULL);
01356         return 1;
01357     }
01358 
01359     if (QUERY_FLAG(tmp, FLAG_REMOVED)) {
01360         char name[MAX_BUF];
01361 
01362         query_name(tmp, name, MAX_BUF);
01363         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01364                              "%s is already removed!",
01365                              "%s is already removed!",
01366                              name);
01367         return 1;
01368     }
01369 
01370     if (from != STACK_FROM_STACK)
01371         /* Item is either stack top, or is a number thus is now stack top, let's remove it  */
01372         dm_stack_pop(op->contr);
01373 
01374     /* Always work on the head - otherwise object will get in odd state */
01375     if (tmp->head)
01376         tmp = tmp->head;
01377     if (tmp->speed != 0) {
01378         tmp->speed = 0;
01379         update_ob_speed(tmp);
01380     }
01381     remove_ob(tmp);
01382     return 1;
01383 }
01384 
01394 int command_free(object *op, char *params) {
01395     object *tmp;
01396     int from;
01397 
01398     tmp = get_dm_object(op->contr, &params, &from);
01399 
01400     if (!tmp) {
01401         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01402                       "Free what object (nr)?", NULL);
01403         return 1;
01404     }
01405 
01406     if (from != STACK_FROM_STACK)
01407         /* Item is either stack top, or is a number thus is now stack top, let's remove it  */
01408         dm_stack_pop(op->contr);
01409 
01410     if (tmp->head)
01411         tmp = tmp->head;
01412 
01413     if (!QUERY_FLAG(tmp, FLAG_REMOVED)) {
01414         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01415                       "Warning: item was not removed, will do so now.", NULL);
01416         remove_ob(tmp);
01417     }
01418 
01419     free_object(tmp);
01420     return 1;
01421 }
01422 
01433 int command_addexp(object *op, char *params) {
01434     char buf[MAX_BUF], skill[MAX_BUF];
01435     int i, q;
01436     object *skillob = NULL;
01437     player *pl;
01438 
01439     skill[0] = '\0';
01440     if ((params == NULL)
01441     || (strlen(params) > MAX_BUF)
01442     || ((q = sscanf(params, "%s %d %s", buf, &i, skill)) < 2)) {
01443         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01444                       "Usage: addexp player quantity [skill].", NULL);
01445         return 1;
01446     }
01447 
01448     for (pl = first_player; pl != NULL; pl = pl->next)
01449         if (!strncmp(pl->ob->name, buf, MAX_NAME))
01450             break;
01451 
01452     if (pl == NULL) {
01453         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01454                       "No such player.", NULL);
01455         return 1;
01456     }
01457 
01458     if (q >= 3) {
01459         skillob = find_skill_by_name(pl->ob, skill);
01460         if (!skillob) {
01461             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01462                                  "Unable to find skill %s in %s",
01463                                  "Unable to find skill %s in %s",
01464                                  skill, buf);
01465             return 1;
01466         }
01467 
01468         i = check_exp_adjust(skillob, i);
01469         skillob->stats.exp += i;
01470         calc_perm_exp(skillob);
01471         player_lvl_adj(pl->ob, skillob);
01472     }
01473 
01474     pl->ob->stats.exp += i;
01475     calc_perm_exp(pl->ob);
01476     player_lvl_adj(pl->ob, NULL);
01477 
01478     if (settings.real_wiz == FALSE)
01479         SET_FLAG(pl->ob, FLAG_WAS_WIZ);
01480     return 1;
01481 }
01482 
01493 int command_speed(object *op, char *params) {
01494     int i;
01495 
01496     if (params == NULL || !sscanf(params, "%d", &i)) {
01497         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01498                              "Current speed is %d",
01499                              "Current speed is %d",
01500                              max_time);
01501         return 1;
01502     }
01503 
01504     set_max_time(i);
01505     reset_sleep();
01506     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01507                          "The speed is changed to %d.",
01508                          "The speed is changed to %d.",
01509                          i);
01510     return 1;
01511 }
01512 
01513 /**************************************************************************/
01514 /* Mods made by Tyler Van Gorder, May 10-13, 1992.                        */
01515 /* CSUChico : tvangod@cscihp.ecst.csuchico.edu                            */
01516 /**************************************************************************/
01517 
01528 int command_stats(object *op, char *params) {
01529     player *pl;
01530 
01531     if (params == NULL) {
01532         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01533                       "Who?", NULL);
01534         return 1;
01535     }
01536 
01537     pl = find_player_partial_name(params);
01538     if (pl == NULL) {
01539         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01540                       "No such player.", NULL);
01541         return 1;
01542     }
01543 
01544     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01545                          "[Fixed]Statistics for %s:", "Statistics for %s:", pl->ob->name);
01546 
01547     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01548                          "[fixed]Str : %-2d      H.P. : %-4d  MAX : %d",
01549                          "Str : %-2d      H.P. : %-4d  MAX : %d",
01550                          pl->ob->stats.Str, pl->ob->stats.hp, pl->ob->stats.maxhp);
01551 
01552     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01553                          "[fixed]Dex : %-2d      S.P. : %-4d  MAX : %d",
01554                          "Dex : %-2d      S.P. : %-4d  MAX : %d",
01555                          pl->ob->stats.Dex, pl->ob->stats.sp, pl->ob->stats.maxsp);
01556 
01557     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01558                          "[fixed]Con : %-2d        AC : %-4d  WC  : %d",
01559                          "Con : %-2d        AC : %-4d  WC  : %d",
01560                          pl->ob->stats.Con, pl->ob->stats.ac, pl->ob->stats.wc);
01561 
01562     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01563                          "[fixed]Int : %-2d    Damage : %d",
01564                          "Int : %-2d    Damage : %d",
01565                          pl->ob->stats.Int, pl->ob->stats.dam);
01566 
01567     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01568                          "[fixed]Wis : %-2d       EXP : %"FMT64,
01569                          "Wis : %-2d       EXP : %"FMT64,
01570                          pl->ob->stats.Wis, pl->ob->stats.exp);
01571 
01572     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01573                          "[fixed]Pow : %-2d    Grace : %d",
01574                          "Pow : %-2d    Grace : %d",
01575                          pl->ob->stats.Pow, pl->ob->stats.grace);
01576 
01577     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01578                          "[fixed]Cha : %-2d      Food : %d",
01579                          "Cha : %-2d      Food : %d",
01580                          pl->ob->stats.Cha, pl->ob->stats.food);
01581     return 1;
01582 }
01583 
01595 int command_abil(object *op, char *params) {
01596     char thing[20], thing2[20];
01597     int iii;
01598     player *pl;
01599 
01600     iii = 0;
01601     thing[0] = '\0';
01602     thing2[0] = '\0';
01603     if (params == NULL
01604     || !sscanf(params, "%s %s %d", thing, thing2, &iii)
01605     || thing == NULL) {
01606         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01607                       "Who?", NULL);
01608         return 1;
01609     }
01610 
01611     if (thing2 == NULL) {
01612         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01613                       "You can't change that.", NULL);
01614         return 1;
01615     }
01616 
01617     if (iii < MIN_STAT || iii > MAX_STAT) {
01618         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01619                       "Illegal range of stat.\n", NULL);
01620         return 1;
01621     }
01622 
01623     for (pl = first_player; pl != NULL; pl = pl->next) {
01624         if (!strcmp(pl->ob->name, thing)) {
01625             if (settings.real_wiz == FALSE)
01626                 SET_FLAG(pl->ob, FLAG_WAS_WIZ);
01627             if (!strcmp("str", thing2))
01628                 pl->ob->stats.Str = iii, pl->orig_stats.Str = iii;
01629             if (!strcmp("dex", thing2))
01630                 pl->ob->stats.Dex = iii, pl->orig_stats.Dex = iii;
01631             if (!strcmp("con", thing2))
01632                 pl->ob->stats.Con = iii, pl->orig_stats.Con = iii;
01633             if (!strcmp("wis", thing2))
01634                 pl->ob->stats.Wis = iii, pl->orig_stats.Wis = iii;
01635             if (!strcmp("cha", thing2))
01636                 pl->ob->stats.Cha = iii, pl->orig_stats.Cha = iii;
01637             if (!strcmp("int", thing2))
01638                 pl->ob->stats.Int = iii, pl->orig_stats.Int = iii;
01639             if (!strcmp("pow", thing2))
01640                 pl->ob->stats.Pow = iii, pl->orig_stats.Pow = iii;
01641             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01642                                  "%s has been altered.",
01643                                  "%s has been altered.",
01644                                  pl->ob->name);
01645             fix_object(pl->ob);
01646             return 1;
01647         }
01648     }
01649 
01650     draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01651                   "No such player.", NULL);
01652     return 1;
01653 }
01654 
01665 int command_reset(object *op, char *params) {
01666     mapstruct *m;
01667     object *dummy = NULL, *tmp = NULL;
01668     char path[HUGE_BUF];
01669     char *space, *confirmation = NULL;
01670     int res = 0;
01671 
01672     if (params == NULL) {
01673         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01674                       "Reset what map [name]?", NULL);
01675         return 1;
01676     }
01677 
01678     space = strchr(params, ' ');
01679     if (space != NULL) {
01680         confirmation = params;
01681         params = space + 1;
01682     }
01683 
01684     if (strcmp(params, ".") == 0)
01685         snprintf(path, sizeof(path), "%s", op->map->path);
01686     else
01687         path_combine_and_normalize(op->map->path, params, path, sizeof(path));
01688     m = has_been_loaded(path);
01689     if (m == NULL) {
01690         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01691                       "No such map.", NULL);
01692         return 1;
01693     }
01694 
01695     if (confirmation) {
01696         if (strcmp(params, ".") == 0 && m->unique) {
01697             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01698                                  "Can't reset a player unique map while on it, use 'reset full-reset %s' while not on it.",
01699                                  "Can't reset a player unique map while on it, use 'reset full-reset %s' while not on it.",
01700                                  m->path);
01701             return;
01702         }
01703 
01704         if (strncmp("full-reset", confirmation, strlen("full-reset"))) {
01705             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01706                           "Invalid confirmation, must be 'full-reset'.", "Invalid confirmation, must be 'full-reset'.");
01707             return;
01708         }
01709     }
01710 
01711     /* Forbid using reset on our own map when we're in a transport, as
01712      * it has the displeasant effect of crashing the server.
01713      * - gros, July 25th 2006 */
01714     if ((op->contr && op->contr->transport) && (op->map == m)) {
01715         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01716                       "You need to disembark first.", NULL);
01717         return 1;
01718     }
01719 
01720     snprintf(path, sizeof(path), "%s", m->path);
01721 
01722     if (m->in_memory != MAP_SWAPPED) {
01723         if (m->in_memory != MAP_IN_MEMORY) {
01724             LOG(llevError, "Tried to swap out map which was not in memory.\n");
01725             return 0;
01726         }
01727 
01728         /*
01729          * Only attempt to remove the player that is doing the reset, and not other
01730          * players or wiz's.
01731          */
01732         if (op->map == m) {
01733             if (strncmp(m->path, "/random/", 8) == 0) {
01734                 /* This is not a very satisfying solution - it would be much better
01735                  * to recreate a random map with the same seed value as the old one.
01736                  * Unfortunately, I think recreating the map would require some
01737                  * knowledge about its 'parent', which appears very non-trivial to
01738                  * me.
01739                  * On the other hand, this should prevent the freeze that this
01740                  * situation caused. - gros, 26th July 2006.
01741                  */
01742                 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01743                               "You cannot reset a random map when inside it.", NULL);
01744                 return 1;
01745             }
01746 
01747             dummy = get_object();
01748             dummy->map = NULL;
01749             EXIT_X(dummy) = op->x;
01750             EXIT_Y(dummy) = op->y;
01751             EXIT_PATH(dummy) = add_string(op->map->path);
01752             remove_ob(op);
01753             op->map = NULL;
01754             tmp = op;
01755         }
01756         res = swap_map(m);
01757     }
01758 
01759     if (res < 0 || m->in_memory != MAP_SWAPPED) {
01760         player *pl;
01761         int playercount = 0;
01762 
01763         /* Need to re-insert player if swap failed for some reason */
01764         if (tmp) {
01765             insert_ob_in_map(op, m, NULL, 0);
01766             free_object(dummy);
01767         }
01768 
01769         if (res < 0 && res != SAVE_ERROR_PLAYER)
01770             /* no need to warn if player on map, code below checks that. */
01771             draw_ext_info_format(NDI_UNIQUE|NDI_RED, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01772                                  "Reset failed, error code: %d.", NULL, res);
01773         else {
01774             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01775                           "Reset failed, couldn't swap map, the following players are on it:",
01776                           NULL);
01777             for (pl = first_player; pl != NULL; pl = pl->next) {
01778                 if (pl->ob->map == m && pl->ob != op) {
01779                     draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01780                                   pl->ob->name, NULL);
01781                     playercount++;
01782                 }
01783             }
01784             if (!playercount)
01785                 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01786                               "hmm, I don't see any other players on this map, something else is the problem.",
01787                               NULL);
01788             return 1;
01789         }
01790     }
01791 
01792     /* Here, map reset succeeded. */
01793 
01794     if (m && m->in_memory == MAP_SWAPPED) {
01795 
01796         if (confirmation) {
01797             map_remove_unique_files(m);
01798             LOG(llevDebug, "DM %s fully resetting map %s.\n", op->name, m->path);
01799         } else
01800             LOG(llevDebug, "DM %s resetting map %s.\n", op->name, m->path);
01801 
01802         /* setting this effectively causes an immediate reload */
01803         m->reset_time = 1;
01804         flush_old_maps();
01805     }
01806 
01807     if (confirmation)
01808         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01809                              "Fully resetting map %s.", "Fully resetting map %s.",
01810                              path);
01811     else
01812         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01813                              "Resetting map %s.", "Resetting map %s.",
01814                              path);
01815 
01816     if (tmp) {
01817         enter_exit(tmp, dummy);
01818         free_object(dummy);
01819     }
01820 
01821     if (confirmation == NULL) {
01822         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01823                          "Use 'reset full-reset %s' to fully reset the map.", params);
01824     }
01825 
01826     return 1;
01827 }
01828 
01839 int command_nowiz(object *op, char *params) { /* 'noadm' is alias */
01840     CLEAR_FLAG(op, FLAG_WIZ);
01841     CLEAR_FLAG(op, FLAG_WIZPASS);
01842     CLEAR_FLAG(op, FLAG_WIZCAST);
01843     if (op->contr->followed_player)
01844         FREE_AND_CLEAR_STR(op->contr->followed_player);
01845 
01846     if (settings.real_wiz == TRUE)
01847         CLEAR_FLAG(op, FLAG_WAS_WIZ);
01848     if (op->contr->hidden) {
01849         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01850                       "You are no longer hidden from other players", NULL);
01851         op->map->players++;
01852         draw_ext_info_format(NDI_UNIQUE|NDI_ALL|NDI_DK_ORANGE, 5, NULL, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_PLAYER,
01853                              "%s has entered the game.",
01854                              "%s has entered the game.",
01855                              op->name);
01856         op->contr->hidden = 0;
01857         op->invisible = 1;
01858     } else
01859         draw_ext_info(NDI_UNIQUE|NDI_ALL|NDI_LT_GREEN, 1, NULL, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_DM,
01860                       "The Dungeon Master is gone..", NULL);
01861 
01862     update_los(op);
01863 
01864     return 1;
01865 }
01866 
01887 static int checkdm(object *op, const char *pl_name, const char *pl_passwd, const char *pl_host) {
01888     FILE *dmfile;
01889     char buf[MAX_BUF];
01890     char line_buf[160], name[160], passwd[160], host[160];
01891 
01892 #ifdef RESTRICTIVE_DM
01893     *pl_name = op->name ? op->name : "*";
01894 #endif
01895 
01896     snprintf(buf, sizeof(buf), "%s/%s", settings.confdir, DMFILE);
01897     if ((dmfile = fopen(buf, "r")) == NULL) {
01898         LOG(llevDebug, "Could not find DM file.\n");
01899         return 0;
01900     }
01901 
01902     while (fgets(line_buf, 160, dmfile) != NULL) {
01903         if (line_buf[0] == '#')
01904             continue;
01905         if (sscanf(line_buf, "%[^:]:%[^:]:%s\n", name, passwd, host) != 3) {
01906             LOG(llevError, "Warning - malformed dm file entry: %s\n", line_buf);
01907         } else if ((!strcmp(name, "*") || (pl_name && !strcmp(pl_name, name)))
01908         && (!strcmp(passwd, "*") || !strcmp(passwd, pl_passwd))
01909         && (!strcmp(host, "*") || !strcmp(host, pl_host))) {
01910             fclose(dmfile);
01911             return (1);
01912         }
01913     }
01914     fclose(dmfile);
01915     return (0);
01916 }
01917 
01932 int do_wizard_dm(object *op, char *params, int silent) {
01933     if (!op->contr)
01934         return 0;
01935 
01936     if (QUERY_FLAG(op, FLAG_WIZ)) {
01937         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01938                       "You are already the Dungeon Master!", NULL);
01939         return 0;
01940     }
01941 
01942     if (checkdm(op, op->name, (params ? params : "*"), op->contr->socket.host)) {
01943         SET_FLAG(op, FLAG_WIZ);
01944         SET_FLAG(op, FLAG_WAS_WIZ);
01945         SET_FLAG(op, FLAG_WIZPASS);
01946         SET_FLAG(op, FLAG_WIZCAST);
01947         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
01948                       "Ok, you are the Dungeon Master!", NULL);
01949         /*
01950          * Remove setting flying here - that won't work, because next
01951         * fix_object() is called that will get cleared - proper solution
01952          * is probably something like a wiz_force which gives that and any
01953          * other desired abilities.
01954          */
01955         clear_los(op);
01956         op->contr->write_buf[0] = '\0';
01957 
01958         if (!silent)
01959             draw_ext_info(NDI_UNIQUE|NDI_ALL|NDI_LT_GREEN, 1, NULL,
01960                           MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_DM,
01961                           "The Dungeon Master has arrived!", NULL);
01962 
01963         return 1;
01964     } else {
01965         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01966                       "Sorry Pal, I don't think so.", NULL);
01967         op->contr->write_buf[0] = '\0';
01968         return 0;
01969     }
01970 }
01971 
01983 int command_dm(object *op, char *params) {
01984     do_wizard_dm(op, params, 0);
01985     return 1;
01986 }
01987 
01998 int command_invisible(object *op, char *params) {
01999     if (op) {
02000         op->invisible += 100;
02001         update_object(op, UP_OBJ_FACE);
02002         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
02003                       "You turn invisible.", NULL);
02004     }
02005 
02006     return 0;
02007 }
02008 
02026 static object *get_spell_by_name(object *op, const char *spell_name) {
02027     archetype *ar;
02028     archetype *found;
02029     int conflict_found;
02030     size_t spell_name_length;
02031 
02032     /* First check for full name matches. */
02033     conflict_found = 0;
02034     found = NULL;
02035     for (ar = first_archetype; ar != NULL; ar = ar->next) {
02036         if (ar->clone.type != SPELL)
02037             continue;
02038 
02039         if (strncmp(ar->name, "spelldirect_", 12) == 0)
02040             continue;
02041 
02042         if (strcmp(ar->clone.name, spell_name) != 0)
02043             continue;
02044 
02045         if (found != NULL) {
02046             if (!conflict_found) {
02047                 conflict_found = 1;
02048                 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02049                                      "More than one archetype matches the spell name %s:",
02050                                      "More than one archetype matches the spell name %s:",
02051                                      spell_name);
02052                 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02053                                      "- %s",
02054                                      "- %s",
02055                                      found->name);
02056             }
02057             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02058                                  "- %s",
02059                                  "- %s",
02060                                  ar->name);
02061             continue;
02062         }
02063 
02064         found = ar;
02065     }
02066 
02067     /* No match if more more than one archetype matches. */
02068     if (conflict_found)
02069         return NULL;
02070 
02071     /* Return if exactly one archetype matches. */
02072     if (found != NULL)
02073         return arch_to_object(found);
02074 
02075     /* No full match found: now check for partial matches. */
02076     spell_name_length = strlen(spell_name);
02077     conflict_found = 0;
02078     found = NULL;
02079     for (ar = first_archetype; ar != NULL; ar = ar->next) {
02080         if (ar->clone.type != SPELL)
02081             continue;
02082 
02083         if (strncmp(ar->name, "spelldirect_", 12) == 0)
02084             continue;
02085 
02086         if (strncmp(ar->clone.name, spell_name, spell_name_length) != 0)
02087             continue;
02088 
02089         if (found != NULL) {
02090             if (!conflict_found) {
02091                 conflict_found = 1;
02092                 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02093                                      "More than one spell matches %s:",
02094                                      "More than one spell matches %s:",
02095                                      spell_name);
02096                 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02097                                      "- %s",
02098                                      "- %s",
02099                                      found->clone.name);
02100             }
02101             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02102                                  "- %s",
02103                                  "- %s",
02104                                  ar->clone.name);
02105             continue;
02106         }
02107 
02108         found = ar;
02109     }
02110 
02111     /* No match if more more than one archetype matches. */
02112     if (conflict_found)
02113         return NULL;
02114 
02115     /* Return if exactly one archetype matches. */
02116     if (found != NULL)
02117         return arch_to_object(found);
02118 
02119     /* No spell found: just print an error message. */
02120     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02121                          "The spell %s does not exist.",
02122                          "The spell %s does not exist.",
02123                          spell_name);
02124     return NULL;
02125 }
02126 
02141 static int command_learn_spell_or_prayer(object *op, char *params, int special_prayer) {
02142     object *tmp;
02143 
02144     if (op->contr == NULL || params == NULL) {
02145         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02146                       "Which spell do you want to learn?", NULL);
02147         return 0;
02148     }
02149 
02150     tmp = get_spell_by_name(op, params);
02151     if (tmp == NULL) {
02152         return 0;
02153     }
02154 
02155     if (check_spell_known(op, tmp->name)) {
02156         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02157                              "You already know the spell %s.",
02158                              "You already know the spell %s.",
02159                              tmp->name);
02160         return 0;
02161     }
02162 
02163     do_learn_spell(op, tmp, special_prayer);
02164     free_object(tmp);
02165     return 1;
02166 }
02167 
02180 int command_learn_spell(object *op, char *params) {
02181     return command_learn_spell_or_prayer(op, params, 0);
02182 }
02183 
02196 int command_learn_special_prayer(object *op, char *params) {
02197     return command_learn_spell_or_prayer(op, params, 1);
02198 }
02199 
02210 int command_forget_spell(object *op, char *params) {
02211     object *spell;
02212 
02213     if (op->contr == NULL || params == NULL) {
02214         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02215                       "Which spell do you want to forget?", NULL);
02216         return 0;
02217     }
02218 
02219     spell = lookup_spell_by_name(op, params);
02220     if (spell == NULL) {
02221         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02222                              "You do not know the spell %s.",
02223                              "You do not know the spell %s.",
02224                              params);
02225         return 0;
02226     }
02227 
02228     do_forget_spell(op, spell->name);
02229     return 1;
02230 }
02231 
02242 int command_listplugins(object *op, char *params) {
02243     plugins_display_list(op);
02244     return 1;
02245 }
02246 
02259 int command_loadplugin(object *op, char *params) {
02260     char buf[MAX_BUF];
02261 
02262     if (params == NULL) {
02263         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02264                       "Load which plugin?", NULL);
02265         return 1;
02266     }
02267 
02268     strcpy(buf, LIBDIR);
02269     strcat(buf, "/plugins/");
02270     strcat(buf, params);
02271     LOG(llevDebug, "Requested plugin file is %s\n", buf);
02272     if (plugins_init_plugin(buf) == 0) {
02273         LOG(llevInfo, "DM %s loaded plugin %s\n", op->name, params);
02274         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
02275                              "Plugin %s successfully loaded.",
02276                              "Plugin %s successfully loaded.",
02277                              params);
02278     } else
02279         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02280                              "Could not load plugin %s.",
02281                              "Could not load plugin %s.",
02282                              params);
02283     return 1;
02284 }
02285 
02298 int command_unloadplugin(object *op, char *params) {
02299     if (params == NULL) {
02300         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02301                       "Remove which plugin?", NULL);
02302         return 1;
02303     }
02304 
02305     if (plugins_remove_plugin(params) == 0) {
02306         LOG(llevInfo, "DM %s unloaded plugin %s\n", op->name, params);
02307         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
02308                              "Plugin %s successfully removed.",
02309                              "Plugin %s successfully removed.",
02310                              params);
02311     } else
02312         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02313                              "Could not remove plugin %s.",
02314                              "Could not remove plugin %s.",
02315                              params);
02316     return 1;
02317 }
02318 
02333 int command_dmhide(object *op, char *params) {
02334     if (!do_wizard_dm(op, params, 1))
02335         return 0;
02336 
02337     do_wizard_hide(op, 1);
02338     return 1;
02339 }
02340 
02347 void dm_stack_pop(player *pl) {
02348     if (!pl->stack_items || !pl->stack_position) {
02349         draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02350                       "Empty stack!", NULL);
02351         return;
02352     }
02353 
02354     pl->stack_position--;
02355     draw_ext_info_format(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
02356                          "Popped item from stack, %d left.",
02357                          "Popped item from stack, %d left.",
02358                          pl->stack_position);
02359 }
02360 
02373 object *dm_stack_peek(player *pl) {
02374     object *ob;
02375 
02376     if (!pl->stack_position) {
02377         draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02378                       "Empty stack!", NULL);
02379         return NULL;
02380     }
02381 
02382     ob = find_object(pl->stack_items[pl->stack_position-1]);
02383     if (!ob) {
02384         draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
02385                       "Stacked item was removed!", NULL);
02386         dm_stack_pop(pl);
02387         return NULL;
02388     }
02389 
02390     return ob;
02391 }
02392 
02403 void dm_stack_push(player *pl, tag_t item) {
02404     if (!pl->stack_items) {
02405         pl->stack_items = (tag_t *)malloc(sizeof(tag_t)*STACK_SIZE);
02406         memset(pl->stack_items, 0, sizeof(tag_t)*STACK_SIZE);
02407     }
02408 
02409     if (pl->stack_position == STACK_SIZE) {
02410         draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02411                       "Item stack full!", NULL);
02412         return;
02413     }
02414 
02415     pl->stack_items[pl->stack_position] = item;
02416     draw_ext_info_format(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
02417                          "Item stacked as %d.",
02418                          "Item stacked as %d.",
02419                          pl->stack_position);
02420     pl->stack_position++;
02421 }
02422 
02450 object *get_dm_object(player *pl, char **params, int *from) {
02451     int item_tag, item_position;
02452     object *ob;
02453 
02454     if (!pl)
02455         return NULL;
02456 
02457     if (!params || !*params || **params == '\0') {
02458         if (from)
02459             *from = STACK_FROM_TOP;
02460         /* No parameter => get stack item */
02461         return dm_stack_peek(pl);
02462     }
02463 
02464     /* Let's clean white spaces */
02465     while (**params == ' ')
02466         (*params)++;
02467 
02468     /* Next case: number => item tag */
02469     if (sscanf(*params, "%d", &item_tag)) {
02470         /* Move parameter to next item */
02471         while (isdigit(**params))
02472             (*params)++;
02473 
02474         /* And skip blanks, too */
02475         while (**params == ' ')
02476             (*params)++;
02477 
02478         /* Get item */
02479         ob = find_object(item_tag);
02480         if (!ob) {
02481             if (from)
02482                 *from = STACK_FROM_NONE;
02483             draw_ext_info_format(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02484                                  "No such item %d!",
02485                                  "No such item %d!",
02486                                  item_tag);
02487             return NULL;
02488         }
02489 
02490         /* Got one, let's push it on stack */
02491         dm_stack_push(pl, item_tag);
02492         if (from)
02493             *from = STACK_FROM_NUMBER;
02494         return ob;
02495     }
02496 
02497     /* Next case: $number => stack item */
02498     if (sscanf(*params, "$%d", &item_position)) {
02499         /* Move parameter to next item */
02500         (*params)++;
02501 
02502         while (isdigit(**params))
02503             (*params)++;
02504         while (**params == ' ')
02505             (*params)++;
02506 
02507         if (item_position >= pl->stack_position) {
02508             if (from)
02509                 *from = STACK_FROM_NONE;
02510             draw_ext_info_format(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02511                                  "No such stack item %d!",
02512                                  "No such stack item %d!",
02513                                  item_position);
02514             return NULL;
02515         }
02516 
02517         ob = find_object(pl->stack_items[item_position]);
02518         if (!ob) {
02519             if (from)
02520                 *from = STACK_FROM_NONE;
02521             draw_ext_info_format(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
02522                                  "Stack item %d was removed.",
02523                                  "Stack item %d was removed.",
02524                                  item_position);
02525             return NULL;
02526         }
02527 
02528         if (from)
02529             *from = item_position < pl->stack_position-1 ? STACK_FROM_STACK : STACK_FROM_TOP;
02530         return ob;
02531     }
02532 
02533     /* Next case: 'me' => return pl->ob */
02534     if (!strncmp(*params, "me", 2)) {
02535         if (from)
02536             *from = STACK_FROM_NUMBER;
02537         dm_stack_push(pl, pl->ob->count);
02538 
02539         /* Skip to next token */
02540         (*params) += 2;
02541         while (**params == ' ')
02542             (*params)++;
02543 
02544         return pl->ob;
02545     }
02546 
02547     /* Last case: get stack top */
02548     if (from)
02549         *from = STACK_FROM_TOP;
02550     return dm_stack_peek(pl);
02551 }
02552 
02563 int command_stack_pop(object *op, char *params) {
02564     dm_stack_pop(op->contr);
02565     return 0;
02566 }
02567 
02578 int command_stack_push(object *op, char *params) {
02579     object *ob;
02580     int from;
02581     ob = get_dm_object(op->contr, &params, &from);
02582 
02583     if (ob && from != STACK_FROM_NUMBER)
02584         /* Object was from stack, need to push it again */
02585         dm_stack_push(op->contr, ob->count);
02586 
02587     return 0;
02588 }
02589 
02600 int command_stack_list(object *op, char *params) {
02601     int item;
02602     object *display;
02603     player *pl = op->contr;
02604 
02605     draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
02606                   "Item stack contents:", NULL);
02607 
02608     for (item = 0; item < pl->stack_position; item++) {
02609         display = find_object(pl->stack_items[item]);
02610         if (display)
02611             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
02612                                  " %d : %s [%d]",
02613                                  " %d : %s [%d]",
02614                                  item, display->name, display->count);
02615         else
02616             /* Item was freed */
02617             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
02618                                  " %d : (lost item: %d)",
02619                                  " %d : (lost item: %d)",
02620                                  item, pl->stack_items[item]);
02621     }
02622 
02623     return 0;
02624 }
02625 
02636 int command_stack_clear(object *op, char *params) {
02637     op->contr->stack_position = 0;
02638     draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
02639                   "Item stack cleared.", NULL);
02640     return 0;
02641 }
02642 
02664 int command_diff(object *op, char *params) {
02665     object *left, *right, *top;
02666     char *diff;
02667     StringBuffer *sb;
02668     int left_from, right_from;
02669 
02670     top = NULL;
02671 
02672     left = get_dm_object(op->contr, &params, &left_from);
02673     if (!left) {
02674         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02675                       "Compare to what item?", NULL);
02676         return 0;
02677     }
02678 
02679     if (left_from == STACK_FROM_NUMBER)
02680         /* Item was stacked, remove it else right will be the same... */
02681         dm_stack_pop(op->contr);
02682 
02683     right = get_dm_object(op->contr, &params, &right_from);
02684 
02685     if (!right) {
02686         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02687                       "Compare what item?", NULL);
02688         return 0;
02689     }
02690 
02691     draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
02692                   "Item difference:", NULL);
02693 
02694     if (left_from == STACK_FROM_TOP && right_from == STACK_FROM_TOP) {
02695         /*
02696          * Special case: both items were taken from stack top.
02697          * Override the behaviour, taking left as item just below top, if exists.
02698          * See function description for why.
02699          * Besides, if we don't do anything, compare an item to itself, not really useful.
02700          */
02701         if (op->contr->stack_position > 1) {
02702             left = find_object(op->contr->stack_items[op->contr->stack_position-2]);
02703             if (left)
02704                 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
02705                               "(Note: first item taken from undertop)", NULL);
02706             else
02707                 /* Stupid case: item under top was freed, fallback to stack top */
02708                 left = right;
02709         }
02710     }
02711 
02712     sb = stringbuffer_new();
02713     get_ob_diff(sb, left, right);
02714     diff = stringbuffer_finish(sb);
02715     if (*diff == '\0') {
02716         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM, "Objects are the same.", NULL);
02717     } else {
02718         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM, diff, NULL);
02719     }
02720     free(diff);
02721     return 0;
02722 }
02723 
02733 int command_insert_into(object *op, char *params) {
02734     object *left, *right, *inserted;
02735     int left_from, right_from;
02736     char what[MAX_BUF], where[MAX_BUF];
02737 
02738     left = get_dm_object(op->contr, &params, &left_from);
02739     if (!left) {
02740         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02741                       "Insert into what object?", NULL);
02742         return 0;
02743     }
02744 
02745     if (left_from == STACK_FROM_NUMBER)
02746         /* Item was stacked, remove it else right will be the same... */
02747         dm_stack_pop(op->contr);
02748 
02749     right = get_dm_object(op->contr, &params, &right_from);
02750 
02751     if (!right) {
02752         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02753                       "Insert what item?", NULL);
02754         return 0;
02755     }
02756 
02757     if (left_from == STACK_FROM_TOP && right_from == STACK_FROM_TOP) {
02758         /*
02759         * Special case: both items were taken from stack top.
02760         * Override the behaviour, taking left as item just below top, if exists.
02761         * See function description for why.
02762         * Besides, can't insert an item into itself.
02763         */
02764         if (op->contr->stack_position > 1) {
02765             left = find_object(op->contr->stack_items[op->contr->stack_position-2]);
02766             if (left)
02767                 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
02768                               "(Note: item to insert into taken from undertop)", NULL);
02769             else
02770                 /* Stupid case: item under top was freed, fallback to stack top */
02771                 left = right;
02772         }
02773     }
02774 
02775     if (left == right) {
02776         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02777                       "Can't insert an object into itself!", NULL);
02778         return 0;
02779     }
02780 
02781     if (right->type == PLAYER) {
02782         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02783                       "Can't insert a player into something!", NULL);
02784         return 0;
02785     }
02786 
02787     if (!QUERY_FLAG(right, FLAG_REMOVED))
02788         remove_ob(right);
02789     inserted = insert_ob_in_ob(right, left);
02790     if (left->type == PLAYER) {
02791         if (inserted != right)
02792             /* item was merged, so updating name and such. */
02793             esrv_update_item(UPD_WEIGHT|UPD_NAME|UPD_NROF, left, inserted);
02794     }
02795     query_name(inserted, what, MAX_BUF);
02796     query_name(left, where, MAX_BUF);
02797     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM,
02798                          "Inserted %s in %s",
02799                          "Inserted %s in %s",
02800                          what, where);
02801     return 0;
02802 
02803 }
02804 
02815 int command_style_map_info(object *op, char *params) {
02816     extern mapstruct *styles;
02817     mapstruct *mp;
02818     int maps_used = 0, mapmem = 0, objects_used = 0, x, y;
02819     object *tmp;
02820 
02821     for (mp = styles; mp != NULL; mp = mp->next) {
02822         maps_used++;
02823         mapmem += MAP_WIDTH(mp)*MAP_HEIGHT(mp)*(sizeof(object *)+sizeof(MapSpace))+sizeof(mapstruct);
02824         for (x = 0; x < MAP_WIDTH(mp); x++) {
02825             for (y = 0; y < MAP_HEIGHT(mp); y++) {
02826                 for (tmp = GET_MAP_OB(mp, x, y); tmp != NULL; tmp = tmp->above)
02827                     objects_used++;
02828             }
02829         }
02830     }
02831     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_MAPS,
02832                          "[fixed]Style maps loaded:    %d",
02833                          "Style maps loaded:    %d",
02834                          maps_used);
02835     draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_MAPS,
02836                   "[fixed]Memory used, not",
02837                   "Memory used, not");
02838     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_MAPS,
02839                          "[fixed]including objects:    %d",
02840                          "including objects:    %d",
02841                          mapmem);
02842     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_MAPS,
02843                          "Style objects:        %d",
02844                          "Style objects:        %d",
02845                          objects_used);
02846     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_MAPS,
02847                          "Mem for objects:      %d",
02848                          "Mem for objects:      %d",
02849                          objects_used*sizeof(object));
02850     return 0;
02851 }
02852 
02863 int command_follow(object *op, char *params) {
02864     player *other;
02865 
02866     if (!params) {
02867         if (op->contr->followed_player != NULL) {
02868             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_DM, "You stop following %s.", NULL, op->contr->followed_player);
02869             FREE_AND_CLEAR_STR(op->contr->followed_player);
02870         }
02871         return 0;
02872     }
02873 
02874     other = find_player_partial_name(params);
02875     if (!other) {
02876         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_DM, "No such player or ambiguous name.", NULL);
02877         return 0;
02878     }
02879     if (other == op->contr) {
02880         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_DM, "You can't follow yourself.", NULL);
02881         return 0;
02882     }
02883 
02884     if (op->contr->followed_player)
02885         FREE_AND_CLEAR_STR(op->contr->followed_player);
02886 
02887     op->contr->followed_player = add_string(other->ob->name);
02888     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_DM, "Following %s.", NULL, op->contr->followed_player);
02889     return 0;
02890 }