ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_wiz.C
Revision: 1.10
Committed: Sun Sep 10 14:54:02 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.9: +4 -4 lines
Log Message:
remaining bugfixes

File Contents

# Content
1
2 /*
3 * static char *rcsid_c_wiz_c =
4 * "$Id: c_wiz.C,v 1.8 2006-09-10 13:20:27 root Exp $";
5 */
6
7 /*
8 CrossFire, A Multiplayer game for X-windows
9
10 Copyright (C) 2002 Mark Wedel & Crossfire Development Team
11 Copyright (C) 1992 Frank Tore Johansen
12
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26
27 The authors can be reached via e-mail at crossfire-devel@real-time.com
28 */
29
30 #include <global.h>
31 #ifndef __CEXTRACT__
32 # include <sproto.h>
33 #endif
34 #include <spells.h>
35 #include <treasure.h>
36 #include <skills.h>
37
38 /** Defines for DM item stack **/
39 #define STACK_SIZE 50 /* Stack size, static */
40
41 /* Values for 'from' field of get_dm_object */
42 #define STACK_FROM_NONE 0 /* Item was not found */
43 #define STACK_FROM_TOP 1 /* Item is stack top */
44 #define STACK_FROM_STACK 2 /* Item is somewhere in stack */
45 #define STACK_FROM_NUMBER 3 /* Item is a number (may be top) */
46
47
48 /**
49 * Enough of the DM functions seem to need this that I broke
50 * it out to a seperate function. name is the person
51 * being saught, rq is who is looking for them. This
52 * prints diagnostics messages, and returns the
53 * other player, or NULL otherwise.
54 */
55 static player *
56 get_other_player_from_name (object *op, char *name)
57 {
58 player *pl;
59
60 if (!name)
61 return NULL;
62
63 for (pl = first_player; pl != NULL; pl = pl->next)
64 if (!strncmp (pl->ob->name, name, MAX_NAME))
65 break;
66
67 if (pl == NULL)
68 {
69 new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
70 return NULL;
71 }
72
73 if (pl->ob == op)
74 {
75 new_draw_info (NDI_UNIQUE, 0, op, "You can't do that to yourself.");
76 return NULL;
77 }
78 if (pl->state != ST_PLAYING)
79 {
80 new_draw_info (NDI_UNIQUE, 0, op, "That player is in no state for that right now.");
81 return NULL;
82 }
83 return pl;
84 }
85
86 /**
87 * This command will stress server.
88 */
89 int
90 command_loadtest (object *op, char *params)
91 {
92 uint32 x, y;
93 char buf[1024];
94
95 new_draw_info (NDI_UNIQUE, 0, op, "loadtest will stress server through teleporting");
96 new_draw_info (NDI_UNIQUE, 0, op, "at different map places.");
97 new_draw_info (NDI_UNIQUE, 0, op, "use at your own risks.");
98 new_draw_info (NDI_UNIQUE, 0, op, "Very long loop used so server may have to be reset.");
99 new_draw_info (NDI_UNIQUE, 0, op, "type loadtest TRUE to run");
100 new_draw_info_format (NDI_UNIQUE, 0, op, "{%s}", params);
101
102 if (!params)
103 return 0;
104
105 if (strncmp (params, "TRUE", 4))
106 return 0;
107
108 new_draw_info_format (NDI_UNIQUE, 0, op, "gogogo");
109
110 for (x = 0; x < settings.worldmaptilesx; x++)
111 {
112 for (y = 0; y < settings.worldmaptilesy; y++)
113 {
114 sprintf (buf, "/world/world_%d_%d", x + settings.worldmapstartx, y + settings.worldmapstarty);
115 command_goto (op, buf);
116 }
117 }
118
119 return 0;
120 }
121
122 /**
123 * Actually hides specified player (obviously a DM).
124 * If 'silent_dm' is non zero, other players are informed of DM entering/leaving,
125 * else they just think someone left/entered.
126 */
127 void
128 do_wizard_hide (object *op, int silent_dm)
129 {
130 if (op->contr->hidden)
131 {
132 op->contr->hidden = 0;
133 op->invisible = 1;
134 new_draw_info (NDI_UNIQUE, 0, op, "You are no longer hidden from other players");
135 op->map->players++;
136 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, "%s has entered the game.", &op->name);
137 if (!silent_dm)
138 {
139 new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_LT_GREEN, 1, NULL, "The Dungeon Master has arrived!");
140 }
141 }
142 else
143 {
144 op->contr->hidden = 1;
145 new_draw_info (NDI_UNIQUE, 0, op, "Other players will no longer see you.");
146 op->map->players--;
147 if (!silent_dm)
148 {
149 new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_LT_GREEN, 1, NULL, "The Dungeon Master is gone..");
150 }
151 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, "%s leaves the game.", &op->name);
152 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, "%s left the game.", &op->name);
153 }
154 }
155
156 int
157 command_hide (object *op, char *params)
158 {
159 do_wizard_hide (op, 0);
160 return 1;
161 }
162
163 /**
164 * This finds and returns the object which matches the name or
165 * object nubmer (specified via num #whatever).
166 */
167 static object *
168 find_object_both (char *params)
169 {
170 if (!params)
171 return NULL;
172
173 if (params[0] == '#')
174 return find_object (atol (params + 1));
175 else
176 return find_object_name (params);
177 }
178
179 /**
180 * Sets the god for some objects. params should contain two values -
181 * first the object to change, followed by the god to change it to.
182 */
183 int
184 command_setgod (object *op, char *params)
185 {
186 object *ob, *god;
187 char *str;
188
189 if (!params || !(str = strchr (params, ' ')))
190 {
191 new_draw_info (NDI_UNIQUE, 0, op, "Usage: setgod object god");
192 return 0;
193 }
194
195 /* kill the space, and set string to the next param */
196 *str++ = '\0';
197 if (!(ob = find_object_both (params)))
198 {
199 new_draw_info_format (NDI_UNIQUE, 0, op, "Set whose god - can not find object %s?", params);
200 return 1;
201 }
202
203 /*
204 * Perhaps this is overly restrictive? Should we perhaps be able
205 * to rebless altars and the like?
206 */
207 if (ob->type != PLAYER)
208 {
209 new_draw_info_format (NDI_UNIQUE, 0, op, "%s is not a player - can not change its god", &ob->name);
210 return 1;
211 }
212
213 god = find_god (str);
214 if (god == NULL)
215 {
216 new_draw_info_format (NDI_UNIQUE, 0, op, "No such god %s.", str);
217 return 1;
218 }
219
220 become_follower (ob, god);
221 return 1;
222 }
223
224 /**
225 * Add player's IP to ban_file and kick them off the server
226 * I know most people have dynamic IPs but this is more of a short term
227 * solution if they have to get a new IP to play maybe they'll calm down.
228 * This uses the banish_file in the local directory *not* the ban_file
229 * The action is logged with a ! for easy searching. -tm
230 */
231 int
232 command_banish (object *op, char *params)
233 {
234 player *pl;
235 FILE *banishfile;
236 char buf[MAX_BUF];
237 time_t now;
238
239 if (!params)
240 {
241 new_draw_info (NDI_UNIQUE, 0, op, "Usage: banish <player>.");
242 return 1;
243 }
244
245 pl = get_other_player_from_name (op, params);
246 if (!pl)
247 return 1;
248
249 sprintf (buf, "%s/%s", settings.localdir, BANISHFILE);
250
251 if ((banishfile = fopen (buf, "a")) == NULL)
252 {
253 LOG (llevDebug, "Could not find file banish_file.\n");
254 new_draw_info (NDI_UNIQUE, 0, op, "Could not find banish_file.");
255 return 0;
256 }
257
258 now = time (NULL);
259 /*
260 * Record this as a comment - then we don't have to worry about changing
261 * the parsing code.
262 */
263 fprintf (banishfile, "# %s (%s) banned by %s at %s\n", &pl->ob->name, pl->socket.host, &op->name, ctime (&now));
264 fprintf (banishfile, "*@%s\n", pl->socket.host);
265 fclose (banishfile);
266
267 LOG (llevDebug, "! %s banned %s from IP: %s.\n", &op->name, &pl->ob->name, pl->socket.host);
268 new_draw_info_format (NDI_UNIQUE | NDI_RED, 0, op, "You banish %s", &pl->ob->name);
269 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_RED, 5, op, "%s banishes %s from the land!", &op->name, &pl->ob->name);
270 command_kick (op, (char *) &pl->ob->name);
271 return 1;
272 }
273
274 int
275 command_kick (object *op, char *params)
276 {
277 struct pl *pl;
278
279 for (pl = first_player; pl != NULL; pl = pl->next)
280 if ((params == NULL || !strcmp (&pl->ob->name, params)) && !INVOKE_PLAYER (KICK, pl, ARG_STRING (params)))
281 {
282 object *op = pl->ob;
283
284 if (!QUERY_FLAG (op, FLAG_REMOVED) && !QUERY_FLAG (op, FLAG_FREED))
285 {
286 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_RED, 5, op, "%s is kicked out of the game.", &op->name);
287 strcpy (op->contr->killer, "kicked");
288 }
289
290 pl->socket.status = Ns_Dead;
291 }
292
293 return 1;
294 }
295
296 int
297 command_save_overlay (object *op, char *params)
298 {
299 if (!op)
300 return 0;
301
302 if (op != NULL && !QUERY_FLAG (op, FLAG_WIZ))
303 {
304 new_draw_info (NDI_UNIQUE, 0, op, "Sorry, you can't force an overlay save.");
305 return 1;
306 }
307
308 new_save_map (op->map, 2);
309 new_save_map (op->map, 0);
310 new_draw_info (NDI_UNIQUE, 0, op, "Current map has been saved as an" " overlay.");
311
312 ready_map_name (op->map->path, 0);
313
314 return 1;
315 }
316
317 int
318 command_shutdown (object *op, char *params)
319 {
320 struct pl *pl;
321
322 if (op != NULL && !QUERY_FLAG (op, FLAG_WIZ))
323 {
324 new_draw_info (NDI_UNIQUE, 0, op, "Sorry, you can't shutdown the server.");
325 return 1;
326 }
327
328 for (pl = first_player; pl != NULL; pl = pl->next)
329 save_player (pl->ob, 0);
330
331 for (pl = first_player; pl != NULL; pl = pl->next)
332 if (!QUERY_FLAG (pl->ob, FLAG_REMOVED))
333 leave_map (pl->ob);
334
335 cleanup ();
336 /* not reached */
337 return 1;
338 }
339
340 int
341 command_goto (object *op, char *params)
342 {
343 char *name;
344 object *dummy;
345
346 if (!op)
347 return 0;
348
349 if (params == NULL)
350 {
351 new_draw_info (NDI_UNIQUE, 0, op, "Go to what level?");
352 return 1;
353 }
354
355 name = params;
356 dummy = get_object ();
357 dummy->map = op->map;
358 EXIT_PATH (dummy) = name;
359 dummy->name = name;
360
361 enter_exit (op, dummy);
362 free_object (dummy);
363 if (op->contr->loading == NULL)
364 {
365 new_draw_info_format (NDI_UNIQUE, 0, op, "Difficulty: %d.", op->map->difficulty);
366 }
367
368 return 1;
369 }
370
371 /* is this function called from somewhere ? -Tero */
372 int
373 command_generate (object *op, char *params)
374 {
375 object *tmp;
376 int nr = 1, i, retry;
377
378 if (!op)
379 return 0;
380
381 if (params != NULL)
382 sscanf (params, "%d", &nr);
383 for (i = 0; i < nr; i++)
384 {
385 retry = 50;
386 while ((tmp = generate_treasure (0, op->map->difficulty)) == NULL && --retry)
387 ;
388 if (tmp != NULL)
389 {
390 tmp = insert_ob_in_ob (tmp, op);
391 if (op->type == PLAYER)
392 esrv_send_item (op, tmp);
393 }
394 }
395
396 return 1;
397 }
398
399 int
400 command_freeze (object *op, char *params)
401 {
402 int ticks;
403 player *pl;
404
405 if (!params)
406 {
407 new_draw_info (NDI_UNIQUE, 0, op, "Usage: freeze [ticks] <player>.");
408 return 1;
409 }
410
411 ticks = atoi (params);
412 if (ticks)
413 {
414 while ((isdigit (*params) || isspace (*params)) && *params != 0)
415 params++;
416 if (*params == 0)
417 {
418 new_draw_info (NDI_UNIQUE, 0, op, "Usage: freeze [ticks] <player>.");
419 return 1;
420 }
421 }
422 else
423 ticks = 100;
424
425 pl = get_other_player_from_name (op, params);
426 if (!pl)
427 return 1;
428
429 new_draw_info (NDI_UNIQUE | NDI_RED, 0, pl->ob, "You have been frozen by the DM!");
430 new_draw_info_format (NDI_UNIQUE, 0, op, "You freeze %s for %d ticks", &pl->ob->name, ticks);
431 pl->ob->speed_left = -(pl->ob->speed * ticks);
432 return 0;
433 }
434
435 int
436 command_arrest (object *op, char *params)
437 {
438 object *dummy;
439 player *pl;
440
441 if (!op)
442 return 0;
443 if (params == NULL)
444 {
445 new_draw_info (NDI_UNIQUE, 0, op, "Usage: arrest <player>.");
446 return 1;
447 }
448 pl = get_other_player_from_name (op, params);
449 if (!pl)
450 return 1;
451 dummy = get_jail_exit (pl->ob);
452 if (!dummy)
453 {
454 /* we have nowhere to send the prisoner.... */
455 new_draw_info (NDI_UNIQUE, 0, op, "can't jail player, there is no map to hold them");
456 return 0;
457 }
458 enter_exit (pl->ob, dummy);
459 free_object (dummy);
460 new_draw_info (NDI_UNIQUE, 0, pl->ob, "You have been arrested.");
461 new_draw_info (NDI_UNIQUE, 0, op, "OK.");
462 LOG (llevInfo, "Player %s arrested by %s\n", &pl->ob->name, &op->name);
463 return 1;
464 }
465
466 int
467 command_summon (object *op, char *params)
468 {
469 int i;
470 object *dummy;
471 player *pl;
472
473 if (!op)
474 return 0;
475
476 if (params == NULL)
477 {
478 new_draw_info (NDI_UNIQUE, 0, op, "Usage: summon <player>.");
479 return 1;
480 }
481
482 pl = get_other_player_from_name (op, params);
483 if (!pl)
484 return 1;
485
486 i = find_free_spot (op, op->map, op->x, op->y, 1, 9);
487 if (i == -1)
488 {
489 new_draw_info (NDI_UNIQUE, 0, op, "Can not find a free spot to place summoned player.");
490 return 1;
491 }
492
493 dummy = get_object ();
494 EXIT_PATH (dummy) = op->map->path;
495 EXIT_X (dummy) = op->x + freearr_x[i];
496 EXIT_Y (dummy) = op->y + freearr_y[i];
497 enter_exit (pl->ob, dummy);
498 free_object (dummy);
499 new_draw_info (NDI_UNIQUE, 0, pl->ob, "You are summoned.");
500 new_draw_info (NDI_UNIQUE, 0, op, "OK.");
501
502 return 1;
503 }
504
505 /**
506 * Teleport next to target player.
507 */
508
509 /* mids 01/16/2002 */
510 int
511 command_teleport (object *op, char *params)
512 {
513 int i;
514 object *dummy;
515 player *pl;
516
517 if (!op)
518 return 0;
519
520 if (params == NULL)
521 {
522 new_draw_info (NDI_UNIQUE, 0, op, "Usage: teleport <player>.");
523 return 1;
524 }
525
526 pl = get_other_player_from_name (op, params);
527 if (!pl)
528 return 1;
529
530 i = find_free_spot (pl->ob, pl->ob->map, pl->ob->x, pl->ob->y, 1, 9);
531 if (i == -1)
532 {
533 new_draw_info (NDI_UNIQUE, 0, op, "Can not find a free spot to teleport to.");
534 return 1;
535 }
536
537 dummy = get_object ();
538 EXIT_PATH (dummy) = pl->ob->map->path;
539 EXIT_X (dummy) = pl->ob->x + freearr_x[i];
540 EXIT_Y (dummy) = pl->ob->y + freearr_y[i];
541 enter_exit (op, dummy);
542 free_object (dummy);
543 if (!op->contr->hidden)
544 new_draw_info (NDI_UNIQUE, 0, pl->ob, "You see a portal open.");
545 new_draw_info (NDI_UNIQUE, 0, op, "OK.");
546 return 1;
547 }
548
549 /**
550 * This function is a real mess, because we're stucking getting
551 * the entire item description in one block of text, so we just
552 * can't simply parse it - we need to look for double quotes
553 * for example. This could actually get much simpler with just a
554 * little help from the client - if we could get line breaks, it
555 * makes parsing much easier, eg, something like:
556 * arch dragon
557 * name big nasty creature
558 * hp 5
559 * sp 30
560 * Is much easier to parse than
561 * dragon name "big nasty creature" hp 5 sp 30
562 * for example.
563 */
564 int
565 command_create (object *op, char *params)
566 {
567 object *tmp = NULL;
568 int nrof, i, magic, set_magic = 0, set_nrof = 0, gotquote, gotspace;
569 char buf[MAX_BUF], *cp, *bp = buf, *bp2, *bp3, *bp4, *endline;
570 archetype *at, *at_spell = NULL;
571 artifact *art = NULL;
572
573 if (!op)
574 return 0;
575
576 if (params == NULL)
577 {
578 new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] <archetype> [ of <artifact>]" " [variable_to_patch setting]");
579 return 1;
580 }
581
582 bp = params;
583
584 /* We need to know where the line ends */
585 endline = bp + strlen (bp);
586
587 if (sscanf (bp, "%d ", &nrof))
588 {
589 if ((bp = strchr (params, ' ')) == NULL)
590 {
591 new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] <archetype> [ of <artifact>]" " [variable_to_patch setting]");
592 return 1;
593 }
594 bp++;
595 set_nrof = 1;
596 LOG (llevDebug, "%s creates: (%d) %s\n", &op->name, nrof, bp);
597 }
598
599 if (sscanf (bp, "%d ", &magic))
600 {
601 if ((bp = strchr (bp, ' ')) == NULL)
602 {
603 new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] <archetype> [ of <artifact>]" " [variable_to_patch setting]");
604 return 1;
605 }
606
607 bp++;
608 set_magic = 1;
609 LOG (llevDebug, "%s creates: (%d) (%d) %s\n", &op->name, nrof, magic, bp);
610 }
611
612 if ((cp = strstr (bp, " of ")) != NULL)
613 {
614 *cp = '\0';
615 cp += 4;
616 }
617
618 for (bp2 = bp; *bp2; bp2++)
619 {
620 if (*bp2 == ' ')
621 {
622 *bp2 = '\0';
623 bp2++;
624 break;
625 }
626 }
627
628 if ((at = find_archetype (bp)) == NULL)
629 {
630 new_draw_info (NDI_UNIQUE, 0, op, "No such archetype.");
631 return 1;
632 }
633
634 if (cp)
635 {
636 char spell_name[MAX_BUF], *fsp = NULL;
637
638 /*
639 * Try to find a spell object for this. Note that
640 * we also set up spell_name which is only
641 * the first word.
642 */
643
644 at_spell = find_archetype (cp);
645 if (!at_spell || at_spell->clone.type != SPELL)
646 at_spell = find_archetype_by_object_name (cp);
647 if (!at_spell || at_spell->clone.type != SPELL)
648 {
649 strcpy (spell_name, cp);
650 fsp = strchr (spell_name, ' ');
651 if (fsp)
652 {
653 *fsp = 0;
654 fsp++;
655 at_spell = find_archetype (spell_name);
656
657 /* Got a spell, update the first string pointer */
658 if (at_spell && at_spell->clone.type == SPELL)
659 bp2 = cp + strlen (spell_name) + 1;
660 else
661 at_spell = NULL;
662 }
663 }
664
665 /* OK - we didn't find a spell - presume the 'of'
666 * in this case means its an artifact.
667 */
668 if (!at_spell)
669 {
670 if (find_artifactlist (at->clone.type) == NULL)
671 new_draw_info_format (NDI_UNIQUE, 0, op, "No artifact list for type %d\n", at->clone.type);
672 else
673 {
674 art = find_artifactlist (at->clone.type)->items;
675
676 do
677 {
678 if (!strcmp (art->item->name, cp))
679 break;
680 art = art->next;
681 }
682 while (art != NULL);
683
684 if (!art)
685 new_draw_info_format (NDI_UNIQUE, 0, op, "No such artifact ([%d] of %s)", at->clone.type, cp);
686 }
687
688 LOG (llevDebug, "%s creates: (%d) (%d) (%s) of (%s)\n", &op->name, set_nrof ? nrof : 0, set_magic ? magic : 0, bp, cp);
689 }
690 } /* if cp */
691
692 if ((at->clone.type == ROD || at->clone.type == WAND || at->clone.type == SCROLL ||
693 at->clone.type == HORN || at->clone.type == SPELLBOOK) && !at_spell)
694 {
695 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);
696 return 1;
697 }
698
699 /*
700 * Rather than have two different blocks with a lot of similar code,
701 * just create one object, do all the processing, and then determine
702 * if that one object should be inserted or if we need to make copies.
703 */
704 tmp = arch_to_object (at);
705
706 if (settings.real_wiz == FALSE)
707 SET_FLAG (tmp, FLAG_WAS_WIZ);
708
709 if (set_magic)
710 set_abs_magic (tmp, magic);
711
712 if (art)
713 give_artifact_abilities (tmp, art->item);
714
715 if (need_identify (tmp))
716 {
717 SET_FLAG (tmp, FLAG_IDENTIFIED);
718 CLEAR_FLAG (tmp, FLAG_KNOWN_MAGICAL);
719 }
720
721 /*
722 * This entire block here tries to find variable pairings,
723 * eg, 'hp 4' or the like. The mess here is that values
724 * can be quoted (eg "my cool sword"); So the basic logic
725 * is we want to find two spaces, but if we got a quote,
726 * any spaces there don't count.
727 */
728 while (*bp2 && bp2 <= endline)
729 {
730 bp4 = NULL;
731 gotspace = 0;
732 gotquote = 0;
733
734 /* find the first quote */
735 for (bp3 = bp2; *bp3 && gotspace < 2 && gotquote < 2; bp3++)
736 {
737
738 /* Found a quote - now lets find the second one */
739 if (*bp3 == '"')
740 {
741 *bp3 = ' ';
742 bp2 = bp3 + 1; /* Update start of string */
743 bp3++;
744 gotquote++;
745 while (*bp3)
746 {
747 if (*bp3 == '"')
748 {
749 *bp3 = '\0';
750 gotquote++;
751 }
752 else
753 bp3++;
754 }
755 }
756 else if (*bp3 == ' ')
757 gotspace++;
758 }
759
760 /*
761 * If we got two spaces, send the second one to null.
762 * if we've reached the end of the line, increase gotspace -
763 * this is perfectly valid for the list entry listed.
764 */
765 if (gotspace == 2 || gotquote == 2)
766 {
767 bp3--; /* Undo the extra increment */
768 *bp3 = '\0';
769 }
770 else if (*bp3 == '\0')
771 gotspace++;
772
773 if ((gotquote && gotquote != 2) || (gotspace != 2 && gotquote != 2))
774 {
775 /*
776 * Unfortunately, we've clobbered lots of values, so printing
777 * out what we have probably isn't useful. Break out, because
778 * trying to recover is probably won't get anything useful
779 * anyways, and we'd be confused about end of line pointers
780 * anyways.
781 */
782 new_draw_info_format (NDI_UNIQUE, 0, op, "Malformed create line: %s", bp2);
783 break;
784 }
785
786 /* bp2 should still point to the start of this line,
787 * with bp3 pointing to the end
788 */
789 if (set_variable (tmp, bp2) == -1)
790 new_draw_info_format (NDI_UNIQUE, 0, op, "Unknown variable %s", bp2);
791 else
792 new_draw_info_format (NDI_UNIQUE, 0, op, "(%s#%d)->%s", &tmp->name, tmp->count, bp2);
793
794 bp2 = bp3 + 1;
795 }
796
797 if (at->clone.nrof)
798 {
799 if (at_spell)
800 insert_ob_in_ob (arch_to_object (at_spell), tmp);
801
802 tmp->x = op->x;
803 tmp->y = op->y;
804
805 if (set_nrof)
806 tmp->nrof = nrof;
807
808 tmp->map = op->map;
809
810 tmp = insert_ob_in_ob (tmp, op);
811 esrv_send_item (op, tmp);
812
813 /* Let's put this created item on stack so dm can access it easily. */
814 dm_stack_push (op->contr, tmp->count);
815
816 return 1;
817 }
818 else
819 {
820 for (i = 0; i < (set_nrof ? nrof : 1); i++)
821 {
822 archetype *atmp;
823 object *prev = 0, *head = 0;
824
825 for (atmp = at; atmp; atmp = atmp->more)
826 {
827 object *dup = arch_to_object (atmp);
828
829 if (at_spell)
830 insert_ob_in_ob (arch_to_object (at_spell), dup);
831
832 /*
833 * The head is what contains all the important bits,
834 * so just copying it over should be fine.
835 */
836 if (!head)
837 {
838 head = dup;
839 copy_object (tmp, dup);
840 }
841
842 if (settings.real_wiz == FALSE)
843 SET_FLAG (dup, FLAG_WAS_WIZ);
844
845 dup->x = op->x + dup->arch->clone.x;
846 dup->y = op->y + dup->arch->clone.y;
847 dup->map = op->map;
848
849 if (head != dup)
850 {
851 dup->head = head;
852 prev->more = dup;
853 }
854
855 prev = dup;
856 }
857
858 if (QUERY_FLAG (head, FLAG_ALIVE))
859 {
860 object *check = head;
861 int size_x = 0;
862 int size_y = 0;
863
864 while (check)
865 {
866 size_x = MAX (size_x, check->arch->clone.x);
867 size_y = MAX (size_y, check->arch->clone.y);
868 check = check->more;
869 }
870
871 if (out_of_map (op->map, head->x + size_x, head->y + size_y))
872 {
873 if (head->x < size_x || head->y < size_y)
874 {
875 dm_stack_pop (op->contr);
876 free_object (head);
877 new_draw_info (NDI_UNIQUE, 0, op, "Object too big to insert in map, or wrong position.");
878 free_object (tmp);
879 return 1;
880 }
881
882 check = head;
883
884 while (check)
885 {
886 check->x -= size_x;
887 check->y -= size_y;
888 check = check->more;
889 }
890 }
891
892 insert_ob_in_map (head, op->map, op, 0);
893 }
894 else
895 head = insert_ob_in_ob (head, op);
896
897 /* Let's put this created item on stack so dm can access it easily. */
898 /* Wonder if we really want to push all of these, but since
899 * things like rods have nrof 0, we want to cover those.
900 */
901 dm_stack_push (op->contr, head->count);
902
903 if (at->clone.randomitems != NULL && !at_spell)
904 create_treasure (at->clone.randomitems, head, GT_APPLY, op->map->difficulty, 0);
905
906 esrv_send_item (op, head);
907 }
908
909 /* free the one we used to copy */
910 free_object (tmp);
911 }
912
913 return 1;
914 }
915
916 /*
917 * Now follows dm-commands which are also acceptable from sockets
918 */
919
920 int
921 command_inventory (object *op, char *params)
922 {
923 object *tmp;
924 int i;
925
926 if (!params)
927 {
928 inventory (op, NULL);
929 return 0;
930 }
931
932 if (!sscanf (params, "%d", &i) || (tmp = find_object (i)) == NULL)
933 {
934 new_draw_info (NDI_UNIQUE, 0, op, "Inventory of what object (nr)?");
935 return 1;
936 }
937
938 inventory (op, tmp);
939 return 1;
940 }
941
942 /* just show player's their skills for now. Dm's can
943 * already see skills w/ inventory command - b.t.
944 */
945
946 int
947 command_skills (object *op, char *params)
948 {
949 show_skills (op, params);
950 return 0;
951 }
952
953 int
954 command_dump (object *op, char *params)
955 {
956 object *tmp;
957
958 tmp = get_dm_object (op->contr, &params, NULL);
959 if (!tmp)
960 return 1;
961
962 dump_object (tmp);
963 new_draw_info (NDI_UNIQUE, 0, op, errmsg);
964 if (QUERY_FLAG (tmp, FLAG_OBJ_ORIGINAL))
965 new_draw_info (NDI_UNIQUE, 0, op, "Object is marked original");
966 return 1;
967 }
968
969 /**
970 * When DM is possessing a monster, flip aggression on and off, to allow
971 * better motion.
972 */
973 int
974 command_mon_aggr (object *op, char *params)
975 {
976 if (op->enemy || !QUERY_FLAG (op, FLAG_UNAGGRESSIVE))
977 {
978 op->enemy = NULL;
979 SET_FLAG (op, FLAG_UNAGGRESSIVE);
980 new_draw_info (NDI_UNIQUE, 0, op, "Aggression turned OFF");
981 }
982 else
983 {
984 CLEAR_FLAG (op, FLAG_FRIENDLY);
985 CLEAR_FLAG (op, FLAG_UNAGGRESSIVE);
986 new_draw_info (NDI_UNIQUE, 0, op, "Aggression turned ON");
987 }
988
989 return 1;
990 }
991
992 /** DM can possess a monster. Basically, this tricks the client into thinking
993 * a given monster, is actually the player it controls. This allows a DM
994 * to inhabit a monster's body, and run around the game with it.
995 * This function is severely broken - it has tons of hardcoded values,
996 */
997 int
998 command_possess (object *op, char *params)
999 {
1000 object *victim, *curinv, *nextinv;
1001 player *pl;
1002 int i;
1003 char buf[MAX_BUF];
1004
1005 victim = NULL;
1006 if (params != NULL)
1007 {
1008 if (sscanf (params, "%d", &i))
1009 victim = find_object (i);
1010 else if (sscanf (params, "%s", buf))
1011 victim = find_object_name (buf);
1012 }
1013 if (victim == NULL)
1014 {
1015 new_draw_info (NDI_UNIQUE, 0, op, "Patch what object (nr)?");
1016 return 1;
1017 }
1018
1019 if (victim == op)
1020 {
1021 new_draw_info (NDI_UNIQUE, 0, op, "As insane as you are, I cannot " "allow you to possess yourself.");
1022 return 1;
1023 }
1024
1025 /* clear out the old inventory */
1026 curinv = op->inv;
1027 while (curinv != NULL)
1028 {
1029 nextinv = curinv->below;
1030 esrv_del_item (op->contr, curinv->count);
1031 curinv = nextinv;
1032 }
1033
1034 /* make the switch */
1035 pl = op->contr;
1036 victim->contr = pl;
1037 pl->ob = victim;
1038 victim->type = PLAYER;
1039 SET_FLAG (victim, FLAG_WIZ);
1040
1041 /* send the inventory to the client */
1042 curinv = victim->inv;
1043 while (curinv != NULL)
1044 {
1045 nextinv = curinv->below;
1046 esrv_send_item (victim, curinv);
1047 curinv = nextinv;
1048 }
1049 /* basic patchup */
1050 /* The use of hard coded values is terrible. Note
1051 * that really, to be fair, this shouldn't get changed at
1052 * all - if you are possessing a kobold, you should have the
1053 * same limitations. As it is, as more body locations are added,
1054 * this will give this player more locations than perhaps
1055 * they should be allowed.
1056 */
1057 for (i = 0; i < NUM_BODY_LOCATIONS; i++)
1058 if (i == 1 || i == 6 || i == 8 || i == 9)
1059 victim->body_info[i] = 2;
1060 else
1061 victim->body_info[i] = 1;
1062
1063 esrv_new_player (pl, 80); /* just pick a wieght, we don't care */
1064 esrv_send_inventory (victim, victim);
1065
1066 fix_player (victim);
1067
1068 do_some_living (victim);
1069 return 1;
1070 }
1071
1072 int
1073 command_patch (object *op, char *params)
1074 {
1075 char *arg, *arg2;
1076 object *tmp;
1077
1078 tmp = get_dm_object (op->contr, &params, NULL);
1079 if (!tmp)
1080 /* Player already informed of failure */
1081 return 1;
1082
1083 /* params set to first value by get_dm_default */
1084 arg = params;
1085 if (arg == NULL)
1086 {
1087 new_draw_info (NDI_UNIQUE, 0, op, "Patch what values?");
1088 return 1;
1089 }
1090
1091 if ((arg2 = strchr (arg, ' ')))
1092 arg2++;
1093 if (settings.real_wiz == FALSE)
1094 SET_FLAG (tmp, FLAG_WAS_WIZ); /* To avoid cheating */
1095 if (set_variable (tmp, arg) == -1)
1096 new_draw_info_format (NDI_UNIQUE, 0, op, "Unknown variable %s", arg);
1097 else
1098 {
1099 new_draw_info_format (NDI_UNIQUE, 0, op, "(%s#%d)->%s=%s", &tmp->name, tmp->count, arg, arg2);
1100 }
1101
1102 return 1;
1103 }
1104
1105 int
1106 command_remove (object *op, char *params)
1107 {
1108 object *tmp;
1109 int from;
1110
1111 tmp = get_dm_object (op->contr, &params, &from);
1112 if (!tmp)
1113 {
1114 new_draw_info (NDI_UNIQUE, 0, op, "Remove what object (nr)?");
1115 return 1;
1116 }
1117
1118 if (tmp->type == PLAYER)
1119 {
1120 new_draw_info (NDI_UNIQUE, 0, op, "Unable to remove a player!");
1121 return 1;
1122 }
1123
1124 if (QUERY_FLAG (tmp, FLAG_REMOVED))
1125 {
1126 new_draw_info_format (NDI_UNIQUE, 0, op, "%s is already removed!", query_name (tmp));
1127 return 1;
1128 }
1129
1130 if (from != STACK_FROM_STACK)
1131 /* Item is either stack top, or is a number thus is now stack top, let's remove it */
1132 dm_stack_pop (op->contr);
1133
1134 /* Always work on the head - otherwise object will get in odd state */
1135 if (tmp->head)
1136 tmp = tmp->head;
1137 remove_ob (tmp);
1138 return 1;
1139 }
1140
1141 int
1142 command_free (object *op, char *params)
1143 {
1144 object *tmp;
1145 int from;
1146
1147 tmp = get_dm_object (op->contr, &params, &from);
1148
1149 if (!tmp)
1150 {
1151 new_draw_info (NDI_UNIQUE, 0, op, "Free what object (nr)?");
1152 return 1;
1153 }
1154
1155 if (from != STACK_FROM_STACK)
1156 /* Item is either stack top, or is a number thus is now stack top, let's remove it */
1157 dm_stack_pop (op->contr);
1158
1159 if (!QUERY_FLAG (tmp, FLAG_REMOVED))
1160 {
1161 new_draw_info (NDI_UNIQUE, 0, op, "Warning, item wasn't removed.");
1162 remove_ob (tmp);
1163 }
1164
1165 if (tmp->head)
1166 tmp = tmp->head;
1167 free_object (tmp);
1168 return 1;
1169 }
1170
1171 /**
1172 * This adds exp to a player. We now allow adding to a specific skill.
1173 */
1174 int
1175 command_addexp (object *op, char *params)
1176 {
1177 char buf[MAX_BUF], skill[MAX_BUF];
1178 int i, q;
1179 object *skillob = NULL;
1180 player *pl;
1181
1182 skill[0] = '\0';
1183 if ((params == NULL) || (strlen (params) > MAX_BUF) || ((q = sscanf (params, "%s %d %s", buf, &i, skill)) < 2))
1184 {
1185 new_draw_info (NDI_UNIQUE, 0, op, "Usage: addexp <who> <how much> [<skill>].");
1186 return 1;
1187 }
1188
1189 for (pl = first_player; pl != NULL; pl = pl->next)
1190 if (!strncmp (pl->ob->name, buf, MAX_NAME))
1191 break;
1192
1193 if (pl == NULL)
1194 {
1195 new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
1196 return 1;
1197 }
1198
1199 if (q >= 3)
1200 {
1201 skillob = find_skill_by_name (pl->ob, skill);
1202 if (!skillob)
1203 {
1204 new_draw_info_format (NDI_UNIQUE, 0, op, "Unable to find skill %s in %s", skill, buf);
1205 return 1;
1206 }
1207
1208 i = check_exp_adjust (skillob, i);
1209 skillob->stats.exp += i;
1210 calc_perm_exp (skillob);
1211 player_lvl_adj (pl->ob, skillob);
1212 }
1213
1214 pl->ob->stats.exp += i;
1215 calc_perm_exp (pl->ob);
1216 player_lvl_adj (pl->ob, NULL);
1217
1218 if (settings.real_wiz == FALSE)
1219 SET_FLAG (pl->ob, FLAG_WAS_WIZ);
1220 return 1;
1221 }
1222
1223 /**************************************************************************/
1224
1225 /* Mods made by Tyler Van Gorder, May 10-13, 1992. */
1226
1227 /* CSUChico : tvangod@cscihp.ecst.csuchico.edu */
1228
1229 /**************************************************************************/
1230
1231 int
1232 command_stats (object *op, char *params)
1233 {
1234 char thing[20];
1235 player *pl;
1236 char buf[MAX_BUF];
1237
1238 thing[0] = '\0';
1239 if (params == NULL || !sscanf (params, "%s", thing) || thing == NULL)
1240 {
1241 new_draw_info (NDI_UNIQUE, 0, op, "Who?");
1242 return 1;
1243 }
1244
1245 for (pl = first_player; pl != NULL; pl = pl->next)
1246 if (!strcmp (pl->ob->name, thing))
1247 {
1248 sprintf (buf, "Str : %-2d H.P. : %-4d MAX : %d", pl->ob->stats.Str, pl->ob->stats.hp, pl->ob->stats.maxhp);
1249 new_draw_info (NDI_UNIQUE, 0, op, buf);
1250 sprintf (buf, "Dex : %-2d S.P. : %-4d MAX : %d", pl->ob->stats.Dex, pl->ob->stats.sp, pl->ob->stats.maxsp);
1251 new_draw_info (NDI_UNIQUE, 0, op, buf);
1252 sprintf (buf, "Con : %-2d AC : %-4d WC : %d", pl->ob->stats.Con, pl->ob->stats.ac, pl->ob->stats.wc);
1253 new_draw_info (NDI_UNIQUE, 0, op, buf);
1254 sprintf (buf, "Int : %-2d Damage : %d", pl->ob->stats.Int, pl->ob->stats.dam);
1255 new_draw_info (NDI_UNIQUE, 0, op, buf);
1256 sprintf (buf, "Wis : %-2d EXP : %lld", pl->ob->stats.Wis, (long long) pl->ob->stats.exp);
1257 new_draw_info (NDI_UNIQUE, 0, op, buf);
1258 sprintf (buf, "Pow : %-2d Grace : %d", pl->ob->stats.Pow, pl->ob->stats.grace);
1259 new_draw_info (NDI_UNIQUE, 0, op, buf);
1260 sprintf (buf, "Cha : %-2d Food : %d", pl->ob->stats.Cha, pl->ob->stats.food);
1261 new_draw_info (NDI_UNIQUE, 0, op, buf);
1262 break;
1263 }
1264 if (pl == NULL)
1265 new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
1266 return 1;
1267 }
1268
1269 int
1270 command_abil (object *op, char *params)
1271 {
1272 char thing[20], thing2[20];
1273 int iii;
1274 player *pl;
1275 char buf[MAX_BUF];
1276
1277 iii = 0;
1278 thing[0] = '\0';
1279 thing2[0] = '\0';
1280 if (params == NULL || !sscanf (params, "%s %s %d", thing, thing2, &iii) || thing == NULL)
1281 {
1282 new_draw_info (NDI_UNIQUE, 0, op, "Who?");
1283 return 1;
1284 }
1285
1286 if (thing2 == NULL)
1287 {
1288 new_draw_info (NDI_UNIQUE, 0, op, "You can't change that.");
1289 return 1;
1290 }
1291
1292 if (iii < MIN_STAT || iii > MAX_STAT)
1293 {
1294 new_draw_info (NDI_UNIQUE, 0, op, "Illegal range of stat.\n");
1295 return 1;
1296 }
1297
1298 for (pl = first_player; pl != NULL; pl = pl->next)
1299 {
1300 if (!strcmp (pl->ob->name, thing))
1301 {
1302 if (settings.real_wiz == FALSE)
1303 SET_FLAG (pl->ob, FLAG_WAS_WIZ);
1304 if (!strcmp ("str", thing2))
1305 pl->ob->stats.Str = iii, pl->orig_stats.Str = iii;
1306 if (!strcmp ("dex", thing2))
1307 pl->ob->stats.Dex = iii, pl->orig_stats.Dex = iii;
1308 if (!strcmp ("con", thing2))
1309 pl->ob->stats.Con = iii, pl->orig_stats.Con = iii;
1310 if (!strcmp ("wis", thing2))
1311 pl->ob->stats.Wis = iii, pl->orig_stats.Wis = iii;
1312 if (!strcmp ("cha", thing2))
1313 pl->ob->stats.Cha = iii, pl->orig_stats.Cha = iii;
1314 if (!strcmp ("int", thing2))
1315 pl->ob->stats.Int = iii, pl->orig_stats.Int = iii;
1316 if (!strcmp ("pow", thing2))
1317 pl->ob->stats.Pow = iii, pl->orig_stats.Pow = iii;
1318 sprintf (buf, "%s has been altered.", &pl->ob->name);
1319 new_draw_info (NDI_UNIQUE, 0, op, buf);
1320 fix_player (pl->ob);
1321 return 1;
1322 }
1323 }
1324
1325 new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
1326 return 1;
1327 }
1328
1329 int
1330 command_reset (object *op, char *params)
1331 {
1332 mapstruct *m;
1333 object *dummy = NULL, *tmp = NULL;
1334
1335 if (params == NULL)
1336 {
1337 new_draw_info (NDI_UNIQUE, 0, op, "Reset what map [name]?");
1338 return 1;
1339 }
1340
1341 if (strcmp (params, ".") == 0)
1342 params = op->map->path;
1343 m = has_been_loaded (params);
1344 if (m == NULL)
1345 {
1346 new_draw_info (NDI_UNIQUE, 0, op, "No such map.");
1347 return 1;
1348 }
1349
1350 if (m->in_memory != MAP_SWAPPED)
1351 {
1352 if (m->in_memory != MAP_IN_MEMORY)
1353 {
1354 LOG (llevError, "Tried to swap out map which was not in memory.\n");
1355 return 0;
1356 }
1357
1358 /*
1359 * Only attempt to remove the player that is doing the reset, and not other
1360 * players or wiz's.
1361 */
1362 if (op->map == m)
1363 {
1364 dummy = get_object ();
1365 dummy->map = NULL;
1366 EXIT_X (dummy) = op->x;
1367 EXIT_Y (dummy) = op->y;
1368 EXIT_PATH (dummy) = op->map->path;
1369 remove_ob (op);
1370 op->map = NULL;
1371 tmp = op;
1372 }
1373 swap_map (m);
1374 }
1375
1376 if (m->in_memory == MAP_SWAPPED)
1377 {
1378 LOG (llevDebug, "Resetting map %s.\n", m->path);
1379
1380 /* setting this effectively causes an immediate reload */
1381 m->reset_time = 1;
1382 flush_old_maps ();
1383 new_draw_info (NDI_UNIQUE, 0, op, "OK.");
1384 if (tmp)
1385 {
1386 enter_exit (tmp, dummy);
1387 free_object (dummy);
1388 }
1389 return 1;
1390 }
1391 else
1392 {
1393 player *pl;
1394 int playercount = 0;
1395
1396 /* Need to re-insert player if swap failed for some reason */
1397 if (tmp)
1398 {
1399 insert_ob_in_map (op, m, NULL, 0);
1400 free_object (dummy);
1401 }
1402
1403 new_draw_info (NDI_UNIQUE, 0, op, "Reset failed, couldn't swap map, the following players are on it:");
1404 for (pl = first_player; pl != NULL; pl = pl->next)
1405 {
1406 if (pl->ob->map == m && pl->ob != op)
1407 {
1408 new_draw_info_format (NDI_UNIQUE, 0, op, "%s", &pl->ob->name);
1409 playercount++;
1410 }
1411 }
1412 if (!playercount)
1413 new_draw_info (NDI_UNIQUE, 0, op, "hmm, I don't see any other players on this map, something else is the problem.");
1414 return 1;
1415 }
1416 }
1417
1418 int
1419 command_nowiz (object *op, char *params)
1420 { /* 'noadm' is alias */
1421 CLEAR_FLAG (op, FLAG_WIZ);
1422 CLEAR_FLAG (op, FLAG_WIZPASS);
1423 CLEAR_FLAG (op, FLAG_WIZCAST);
1424
1425 if (settings.real_wiz == TRUE)
1426 CLEAR_FLAG (op, FLAG_WAS_WIZ);
1427 if (op->contr->hidden)
1428 {
1429 new_draw_info (NDI_UNIQUE, 0, op, "You are no longer hidden from other players");
1430 op->map->players++;
1431 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, "%s has entered the game.", &op->name);
1432 op->contr->hidden = 0;
1433 op->invisible = 1;
1434 }
1435 else
1436 new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_LT_GREEN, 1, NULL, "The Dungeon Master is gone..");
1437 return 1;
1438 }
1439
1440 /**
1441 * object *op is trying to become dm.
1442 * pl_name is name supplied by player. Restrictive DM will make it harder
1443 * for socket users to become DM - in that case, it will check for the players
1444 * character name.
1445 */
1446 static int
1447 checkdm (object *op, const char *pl_name, const char *pl_passwd, const char *pl_host)
1448 {
1449 FILE *dmfile;
1450 char buf[MAX_BUF];
1451 char line_buf[160], name[160], passwd[160], host[160];
1452
1453 #ifdef RESTRICTIVE_DM
1454 *pl_name = op->name ? op->name : "*";
1455 #endif
1456
1457 sprintf (buf, "%s/%s", settings.confdir, DMFILE);
1458 if ((dmfile = fopen (buf, "r")) == NULL)
1459 {
1460 LOG (llevDebug, "Could not find DM file.\n");
1461 return 0;
1462 }
1463
1464 while (fgets (line_buf, 160, dmfile) != NULL)
1465 {
1466 if (line_buf[0] == '#')
1467 continue;
1468 if (sscanf (line_buf, "%[^:]:%[^:]:%s\n", name, passwd, host) != 3)
1469 {
1470 LOG (llevError, "Warning - malformed dm file entry: %s\n", line_buf);
1471 }
1472 else if ((!strcmp (name, "*") || (pl_name && !strcmp (pl_name, name)))
1473 && (!strcmp (passwd, "*") || !strcmp (passwd, pl_passwd)) && (!strcmp (host, "*") || !strcmp (host, pl_host)))
1474 {
1475 fclose (dmfile);
1476 return (1);
1477 }
1478 }
1479 fclose (dmfile);
1480 return (0);
1481 }
1482
1483 int
1484 do_wizard_dm (object *op, char *params, int silent)
1485 {
1486 if (!op->contr)
1487 return 0;
1488
1489 if (QUERY_FLAG (op, FLAG_WIZ))
1490 {
1491 new_draw_info (NDI_UNIQUE, 0, op, "You are already the Dungeon Master!");
1492 return 0;
1493 }
1494
1495 if (checkdm (op, op->name, (params ? params : "*"), op->contr->socket.host))
1496 {
1497 SET_FLAG (op, FLAG_WIZ);
1498 SET_FLAG (op, FLAG_WAS_WIZ);
1499 SET_FLAG (op, FLAG_WIZPASS);
1500 SET_FLAG (op, FLAG_WIZCAST);
1501 new_draw_info (NDI_UNIQUE, 0, op, "Ok, you are the Dungeon Master!");
1502 /*
1503 * Remove setting flying here - that won't work, because next
1504 * fix_player() is called that will get cleared - proper solution
1505 * is probably something like a wiz_force which gives that and any
1506 * other desired abilities.
1507 */
1508 clear_los (op);
1509 op->contr->write_buf[0] = '\0';
1510
1511 if (!silent)
1512 new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_LT_GREEN, 1, NULL, "The Dungeon Master has arrived!");
1513
1514 return 1;
1515 }
1516 else
1517 {
1518 new_draw_info (NDI_UNIQUE, 0, op, "Sorry Pal, I don't think so.");
1519 op->contr->write_buf[0] = '\0';
1520 return 0;
1521 }
1522 }
1523
1524 /*
1525 * Actual command to perhaps become dm. Changed aroun a bit in version 0.92.2
1526 * - allow people on sockets to become dm, and allow better dm file
1527 */
1528 int
1529 command_dm (object *op, char *params)
1530 {
1531 if (!op->contr)
1532 return 0;
1533
1534 do_wizard_dm (op, params, 0);
1535
1536 return 1;
1537 }
1538
1539 int
1540 command_invisible (object *op, char *params)
1541 {
1542 if (op)
1543 {
1544 op->invisible += 100;
1545 update_object (op, UP_OBJ_FACE);
1546 new_draw_info (NDI_UNIQUE, 0, op, "You turn invisible.");
1547 }
1548
1549 return 0;
1550 }
1551
1552 /**
1553 * Returns spell object (from archetypes) by name.
1554 * Returns NULL if 0 or more than one spell matches.
1555 * Used for wizard's learn spell/prayer.
1556 *
1557 * op is the player issuing the command.
1558 *
1559 * Ignores archetypes "spelldirect_xxx" since these archetypes are not used
1560 * anymore (but may still be present in some player's inventories and thus
1561 * cannot be removed). We have to ignore them here since they have the same
1562 * name than other "spell_xxx" archetypes and would always conflict.
1563 */
1564 static object *
1565 get_spell_by_name (object *op, const char *spell_name)
1566 {
1567 archetype *ar;
1568 archetype *found;
1569 int conflict_found;
1570 size_t spell_name_length;
1571
1572 /* First check for full name matches. */
1573 conflict_found = 0;
1574 found = NULL;
1575 for (ar = first_archetype; ar != NULL; ar = ar->next)
1576 {
1577 if (ar->clone.type != SPELL)
1578 continue;
1579
1580 if (strncmp (ar->name, "spelldirect_", 12) == 0)
1581 continue;
1582
1583 if (strcmp (ar->clone.name, spell_name) != 0)
1584 continue;
1585
1586 if (found != NULL)
1587 {
1588 if (!conflict_found)
1589 {
1590 conflict_found = 1;
1591 new_draw_info_format (NDI_UNIQUE, 0, op, "More than one archetype matches the spell name %s:", spell_name);
1592 new_draw_info_format (NDI_UNIQUE, 0, op, "- %s", &found->name);
1593 }
1594 new_draw_info_format (NDI_UNIQUE, 0, op, "- %s", &ar->name);
1595 continue;
1596 }
1597
1598 found = ar;
1599 }
1600
1601 /* No match if more more than one archetype matches. */
1602 if (conflict_found)
1603 return NULL;
1604
1605 /* Return if exactly one archetype matches. */
1606 if (found != NULL)
1607 return arch_to_object (found);
1608
1609 /* No full match found: now check for partial matches. */
1610 spell_name_length = strlen (spell_name);
1611 conflict_found = 0;
1612 found = NULL;
1613 for (ar = first_archetype; ar != NULL; ar = ar->next)
1614 {
1615 if (ar->clone.type != SPELL)
1616 continue;
1617
1618 if (strncmp (ar->name, "spelldirect_", 12) == 0)
1619 continue;
1620
1621 if (strncmp (ar->clone.name, spell_name, spell_name_length) != 0)
1622 continue;
1623
1624 if (found != NULL)
1625 {
1626 if (!conflict_found)
1627 {
1628 conflict_found = 1;
1629 new_draw_info_format (NDI_UNIQUE, 0, op, "More than one spell matches %s:", spell_name);
1630 new_draw_info_format (NDI_UNIQUE, 0, op, "- %s", &found->clone.name);
1631 }
1632 new_draw_info_format (NDI_UNIQUE, 0, op, "- %s", &ar->clone.name);
1633 continue;
1634 }
1635
1636 found = ar;
1637 }
1638
1639 /* No match if more more than one archetype matches. */
1640 if (conflict_found)
1641 return NULL;
1642
1643 /* Return if exactly one archetype matches. */
1644 if (found != NULL)
1645 return arch_to_object (found);
1646
1647 /* No spell found: just print an error message. */
1648 new_draw_info_format (NDI_UNIQUE, 0, op, "The spell %s does not exist.", spell_name);
1649 return NULL;
1650 }
1651
1652 static int
1653 command_learn_spell_or_prayer (object *op, char *params, int special_prayer)
1654 {
1655 object *tmp;
1656
1657 if (op->contr == NULL || params == NULL)
1658 {
1659 new_draw_info (NDI_UNIQUE, 0, op, "Which spell do you want to learn?");
1660 return 0;
1661 }
1662
1663 tmp = get_spell_by_name (op, params);
1664 if (tmp == NULL)
1665 {
1666 return 0;
1667 }
1668
1669 if (check_spell_known (op, tmp->name))
1670 {
1671 new_draw_info_format (NDI_UNIQUE, 0, op, "You already know the spell %s.", &tmp->name);
1672 return 0;
1673 }
1674
1675 do_learn_spell (op, tmp, special_prayer);
1676 free_object (tmp);
1677 return 1;
1678 }
1679
1680 int
1681 command_learn_spell (object *op, char *params)
1682 {
1683 return command_learn_spell_or_prayer (op, params, 0);
1684 }
1685
1686 int
1687 command_learn_special_prayer (object *op, char *params)
1688 {
1689 return command_learn_spell_or_prayer (op, params, 1);
1690 }
1691
1692 int
1693 command_forget_spell (object *op, char *params)
1694 {
1695 object *spell;
1696
1697 if (op->contr == NULL || params == NULL)
1698 {
1699 new_draw_info (NDI_UNIQUE, 0, op, "Which spell do you want to forget?");
1700 return 0;
1701 }
1702
1703 spell = lookup_spell_by_name (op, params);
1704 if (spell == NULL)
1705 {
1706 new_draw_info_format (NDI_UNIQUE, 0, op, "You do not know the spell %s.", params);
1707 return 0;
1708 }
1709
1710 do_forget_spell (op, spell->name);
1711 return 1;
1712 }
1713
1714 /**
1715 * Lists all plugins currently loaded with their IDs and full names.
1716 */
1717 int
1718 command_listplugins (object *op, char *params)
1719 {
1720 plugins_display_list (op);
1721 return 1;
1722 }
1723
1724 /**
1725 * Loads the given plugin. The DM specifies the name of the library to load (no
1726 * pathname is needed). Do not ever attempt to load the same plugin more than
1727 * once at a time, or bad things could happen.
1728 */
1729 int
1730 command_loadplugin (object *op, char *params)
1731 {
1732 char buf[MAX_BUF];
1733
1734 if (params == NULL)
1735 {
1736 new_draw_info (NDI_UNIQUE, 0, op, "Load which plugin?");
1737 return 1;
1738 }
1739
1740 strcpy (buf, LIBDIR);
1741 strcat (buf, "/plugins/");
1742 strcat (buf, params);
1743 LOG (llevDebug, "Requested plugin file is %s\n", buf);
1744 if (plugins_init_plugin (buf) == 0)
1745 new_draw_info (NDI_UNIQUE, 0, op, "Plugin successfully loaded.");
1746 else
1747 new_draw_info (NDI_UNIQUE, 0, op, "Could not load plugin.");
1748 return 1;
1749 }
1750
1751 /**
1752 * Unloads the given plugin. The DM specified the ID of the library to unload.
1753 * Note that some things may behave strangely if the correct plugins are not
1754 * loaded.
1755 */
1756 int
1757 command_unloadplugin (object *op, char *params)
1758 {
1759 if (params == NULL)
1760 {
1761 new_draw_info (NDI_UNIQUE, 0, op, "Remove which plugin?");
1762 return 1;
1763 }
1764
1765 if (plugins_remove_plugin (params) == 0)
1766 new_draw_info (NDI_UNIQUE, 0, op, "Plugin successfully removed.");
1767 else
1768 new_draw_info (NDI_UNIQUE, 0, op, "Could not remove plugin.");
1769 return 1;
1770 }
1771
1772 /**
1773 * A players wants to become DM and hide.
1774 * Let's see if that's authorized.
1775 * Make sure to not tell anything to anyone.
1776 */
1777 int
1778 command_dmhide (object *op, char *params)
1779 {
1780 if (!do_wizard_dm (op, params, 1))
1781 return 0;
1782
1783 do_wizard_hide (op, 1);
1784
1785 return 1;
1786 }
1787
1788 void
1789 dm_stack_pop (player *pl)
1790 {
1791 if (!pl->stack_items || !pl->stack_position)
1792 {
1793 new_draw_info (NDI_UNIQUE, 0, pl->ob, "Empty stack!");
1794 return;
1795 }
1796
1797 pl->stack_position--;
1798 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Popped item from stack, %d left.", pl->stack_position);
1799 }
1800
1801 /**
1802 * Get current stack top item for player.
1803 * Returns NULL if no stacked item.
1804 * If stacked item disappeared (freed), remove it.
1805 *
1806 * Ryo, august 2004
1807 */
1808 object *
1809 dm_stack_peek (player *pl)
1810 {
1811 object *ob;
1812
1813 if (!pl->stack_position)
1814 {
1815 new_draw_info (NDI_UNIQUE, 0, pl->ob, "Empty stack!");
1816 return NULL;
1817 }
1818
1819 ob = find_object (pl->stack_items[pl->stack_position - 1]);
1820 if (!ob)
1821 {
1822 new_draw_info (NDI_UNIQUE, 0, pl->ob, "Stacked item was removed!");
1823 dm_stack_pop (pl);
1824 return NULL;
1825 }
1826
1827 return ob;
1828 }
1829
1830 /**
1831 * Push specified item on player stack.
1832 * Inform player of position.
1833 * Initializes variables if needed.
1834 */
1835 void
1836 dm_stack_push (player *pl, tag_t item)
1837 {
1838 if (!pl->stack_items)
1839 {
1840 pl->stack_items = (tag_t *) malloc (sizeof (tag_t) * STACK_SIZE);
1841 memset (pl->stack_items, 0, sizeof (tag_t) * STACK_SIZE);
1842 }
1843
1844 if (pl->stack_position == STACK_SIZE)
1845 {
1846 new_draw_info (NDI_UNIQUE, 0, pl->ob, "Item stack full!");
1847 return;
1848 }
1849
1850 pl->stack_items[pl->stack_position] = item;
1851 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Item stacked as %d.", pl->stack_position);
1852 pl->stack_position++;
1853 }
1854
1855 /**
1856 * Checks 'params' for object code.
1857 *
1858 * Can be:
1859 * * empty => get current object stack top for player
1860 * * number => get item with that tag, stack it for future use
1861 * * $number => get specified stack item
1862 * * "me" => player himself
1863 *
1864 * At function exit, params points to first non-object char
1865 *
1866 * 'from', if not NULL, contains at exit:
1867 * * STACK_FROM_NONE => object not found
1868 * * STACK_FROM_TOP => top item stack, may be NULL if stack was empty
1869 * * STACK_FROM_STACK => item from somewhere in the stack
1870 * * STACK_FROM_NUMBER => item by number, pushed on stack
1871 *
1872 * Ryo, august 2004
1873 */
1874 object *
1875 get_dm_object (player *pl, char **params, int *from)
1876 {
1877 int item_tag, item_position;
1878 object *ob;
1879
1880 if (!pl)
1881 return NULL;
1882
1883 if (!params || !*params || **params == '\0')
1884 {
1885 if (from)
1886 *from = STACK_FROM_TOP;
1887 /* No parameter => get stack item */
1888 return dm_stack_peek (pl);
1889 }
1890
1891 /* Let's clean white spaces */
1892 while (**params == ' ')
1893 (*params)++;
1894
1895 /* Next case: number => item tag */
1896 if (sscanf (*params, "%d", &item_tag))
1897 {
1898 /* Move parameter to next item */
1899 while (isdigit (**params))
1900 (*params)++;
1901
1902 /* And skip blanks, too */
1903 while (**params == ' ')
1904 (*params)++;
1905
1906 /* Get item */
1907 ob = find_object (item_tag);
1908 if (!ob)
1909 {
1910 if (from)
1911 *from = STACK_FROM_NONE;
1912 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "No such item %d!", item_tag);
1913 return NULL;
1914 }
1915
1916 /* Got one, let's push it on stack */
1917 dm_stack_push (pl, item_tag);
1918 if (from)
1919 *from = STACK_FROM_NUMBER;
1920 return ob;
1921 }
1922
1923 /* Next case: $number => stack item */
1924 if (sscanf (*params, "$%d", &item_position))
1925 {
1926 /* Move parameter to next item */
1927 (*params)++;
1928
1929 while (isdigit (**params))
1930 (*params)++;
1931 while (**params == ' ')
1932 (*params)++;
1933
1934 if (item_position >= pl->stack_position)
1935 {
1936 if (from)
1937 *from = STACK_FROM_NONE;
1938 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "No such stack item %d!", item_position);
1939 return NULL;
1940 }
1941
1942 ob = find_object (pl->stack_items[item_position]);
1943 if (!ob)
1944 {
1945 if (from)
1946 *from = STACK_FROM_NONE;
1947 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Stack item %d was removed.", item_position);
1948 return NULL;
1949 }
1950
1951 if (from)
1952 *from = item_position < pl->stack_position - 1 ? STACK_FROM_STACK : STACK_FROM_TOP;
1953 return ob;
1954 }
1955
1956 /* Next case: 'me' => return pl->ob */
1957 if (!strncmp (*params, "me", 2))
1958 {
1959 if (from)
1960 *from = STACK_FROM_NUMBER;
1961 dm_stack_push (pl, pl->ob->count);
1962
1963 /* Skip to next token */
1964 (*params) += 2;
1965 while (**params == ' ')
1966 (*params)++;
1967
1968 return pl->ob;
1969 }
1970
1971 /* Last case: get stack top */
1972 if (from)
1973 *from = STACK_FROM_TOP;
1974 return dm_stack_peek (pl);
1975 }
1976
1977 /**
1978 * Pop the stack top.
1979 */
1980 int
1981 command_stack_pop (object *op, char *params)
1982 {
1983 dm_stack_pop (op->contr);
1984 return 0;
1985 }
1986
1987 /**
1988 * Push specified item on stack.
1989 */
1990 int
1991 command_stack_push (object *op, char *params)
1992 {
1993 object *ob;
1994 int from;
1995
1996 ob = get_dm_object (op->contr, &params, &from);
1997
1998 if (ob && from != STACK_FROM_NUMBER)
1999 /* Object was from stack, need to push it again */
2000 dm_stack_push (op->contr, ob->count);
2001
2002 return 0;
2003 }
2004
2005 /**
2006 * Displays stack contents.
2007 */
2008 int
2009 command_stack_list (object *op, char *params)
2010 {
2011 int item;
2012 object *display;
2013 player *pl = op->contr;
2014
2015 new_draw_info (NDI_UNIQUE, 0, op, "Item stack contents:");
2016
2017 for (item = 0; item < pl->stack_position; item++)
2018 {
2019 display = find_object (pl->stack_items[item]);
2020 if (display)
2021 new_draw_info_format (NDI_UNIQUE, 0, op, " %d : %s [%d]", item, &display->name, display->count);
2022 else
2023 /* Item was freed */
2024 new_draw_info_format (NDI_UNIQUE, 0, op, " %d : (lost item: %d)", item, pl->stack_items[item]);
2025 }
2026
2027 return 0;
2028 }
2029
2030 /**
2031 * Empty DM item stack.
2032 */
2033 int
2034 command_stack_clear (object *op, char *params)
2035 {
2036 op->contr->stack_position = 0;
2037 new_draw_info (NDI_UNIQUE, 0, op, "Item stack cleared.");
2038 return 0;
2039 }
2040
2041 int
2042 command_insert_into (object *op, char *params)
2043 {
2044 object *left, *right, *inserted;
2045 int left_from, right_from;
2046
2047 left = get_dm_object (op->contr, &params, &left_from);
2048 if (!left)
2049 {
2050 new_draw_info (NDI_UNIQUE, 0, op, "Insert into what object?");
2051 return 0;
2052 }
2053
2054 if (left_from == STACK_FROM_NUMBER)
2055 /* Item was stacked, remove it else right will be the same... */
2056 dm_stack_pop (op->contr);
2057
2058 right = get_dm_object (op->contr, &params, &right_from);
2059
2060 if (!right)
2061 {
2062 new_draw_info (NDI_UNIQUE, 0, op, "Insert what item?");
2063 return 0;
2064 }
2065
2066 if (left_from == STACK_FROM_TOP && right_from == STACK_FROM_TOP)
2067 {
2068 /*
2069 * Special case: both items were taken from stack top.
2070 * Override the behaviour, taking left as item just below top, if exists.
2071 * See function description for why.
2072 * Besides, can't insert an item into itself.
2073 */
2074 if (op->contr->stack_position > 1)
2075 {
2076 left = find_object (op->contr->stack_items[op->contr->stack_position - 2]);
2077 if (left)
2078 new_draw_info (NDI_UNIQUE, 0, op, "(Note: item to insert into taken from undertop)");
2079 else
2080 /* Stupid case: item under top was freed, fallback to stack top */
2081 left = right;
2082 }
2083 }
2084
2085 if (left == right)
2086 {
2087 new_draw_info (NDI_UNIQUE, 0, op, "Can't insert an object into itself!");
2088 return 0;
2089 }
2090
2091 if (right->type == PLAYER)
2092 {
2093 new_draw_info (NDI_UNIQUE, 0, op, "Can't insert a player into something!");
2094 return 0;
2095 }
2096
2097 if (!QUERY_FLAG (right, FLAG_REMOVED))
2098 remove_ob (right);
2099 inserted = insert_ob_in_ob (right, left);
2100 if (left->type == PLAYER)
2101 if (inserted == right)
2102 esrv_send_item (left, right);
2103 else
2104 esrv_update_item (UPD_WEIGHT | UPD_NAME | UPD_NROF, left, inserted);
2105
2106 new_draw_info_format (NDI_UNIQUE, 0, op, "Inserted %s in %s", query_name (inserted), query_name (left));
2107
2108 return 0;
2109
2110 }