ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_wiz.C
Revision: 1.49
Committed: Sun Jul 1 05:00:19 2007 UTC (16 years, 11 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.48: +10 -11 lines
Log Message:
- upgrade crossfire trt to the GPL version 3 (hopefully correctly).
- add a single file covered by the GNU Affero General Public License
  (which is not yet released, so I used the current draft, which is
  legally a bit wavy, but its likely better than nothing as it expresses
  direct intent by the authors, and we can upgrade as soon as it has been
  released).
  * this should ensure availability of source code for the server at least
    and hopefully also archetypes and maps even when modified versions
    are not being distributed, in accordance of section 13 of the agplv3.

File Contents

# Content
1 /*
2 * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
3 *
4 * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team
5 * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
6 * Copyright (©) 1992,2007 Frank Tore Johansen
7 *
8 * Crossfire TRT is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 * The authors can be reached via e-mail to <crossfire@schmorp.de>
22 */
23
24 #include <global.h>
25 #include <sproto.h>
26 #include <spells.h>
27 #include <treasure.h>
28 #include <skills.h>
29
30 /** Defines for DM item stack **/
31 #define STACK_SIZE 50 /* Stack size, static */
32
33 /* Values for 'from' field of get_dm_object */
34 #define STACK_FROM_NONE 0 /* Item was not found */
35 #define STACK_FROM_TOP 1 /* Item is stack top */
36 #define STACK_FROM_STACK 2 /* Item is somewhere in stack */
37 #define STACK_FROM_NUMBER 3 /* Item is a number (may be top) */
38
39
40 /**
41 * Enough of the DM functions seem to need this that I broke
42 * it out to a seperate function. name is the person
43 * being saught, rq is who is looking for them. This
44 * prints diagnostics messages, and returns the
45 * other player, or NULL otherwise.
46 */
47 static player *
48 get_other_player_from_name (object *op, char *name)
49 {
50 if (!name)
51 return NULL;
52
53 for_all_players (pl)
54 if (!strncmp (pl->ob->name, name, MAX_NAME))
55 {
56 if (pl->ob == op)
57 {
58 new_draw_info (NDI_UNIQUE, 0, op, "You can't do that to yourself.");
59 return NULL;
60 }
61
62 if (pl->ns->state != ST_PLAYING)
63 {
64 new_draw_info (NDI_UNIQUE, 0, op, "That player is in no state for that right now.");
65 return NULL;
66 }
67
68 return pl;
69 }
70
71 new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
72 return 0;
73 }
74
75 /**
76 * Actually hides specified player (obviously a DM).
77 * If 'silent_dm' is non zero, other players are informed of DM entering/leaving,
78 * else they just think someone left/entered.
79 */
80 void
81 do_wizard_hide (object *op, int silent_dm)
82 {
83 if (op->contr->hidden)
84 {
85 op->contr->hidden = 0;
86 op->invisible = 1;
87 new_draw_info (NDI_UNIQUE, 0, op, "You are no longer hidden from other players");
88 op->map->players++;
89 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, "%s has entered the game.", &op->name);
90
91 if (!silent_dm)
92 new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_LT_GREEN, 1, NULL, "The Dungeon Master has arrived!");
93 }
94 else
95 {
96 op->contr->hidden = 1;
97 new_draw_info (NDI_UNIQUE, 0, op, "Other players will no longer see you.");
98 op->map->players--;
99
100 if (!silent_dm)
101 new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_LT_GREEN, 1, NULL, "The Dungeon Master is gone..");
102
103 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, "%s leaves the game.", &op->name);
104 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, "%s left the game.", &op->name);
105 }
106 }
107
108 int
109 command_hide (object *op, char *params)
110 {
111 do_wizard_hide (op, 0);
112 return 1;
113 }
114
115 /**
116 * This finds and returns the object which matches the name or
117 * object nubmer (specified via num #whatever).
118 */
119 static object *
120 find_object_both (char *params)
121 {
122 if (!params)
123 return NULL;
124
125 if (params[0] == '#')
126 return find_object (atol (params + 1));
127 else
128 return find_object_name (params);
129 }
130
131 /**
132 * Sets the god for some objects. params should contain two values -
133 * first the object to change, followed by the god to change it to.
134 */
135 int
136 command_setgod (object *op, char *params)
137 {
138 object *ob, *god;
139 char *str;
140
141 if (!params || !(str = strchr (params, ' ')))
142 {
143 new_draw_info (NDI_UNIQUE, 0, op, "Usage: setgod object god");
144 return 0;
145 }
146
147 /* kill the space, and set string to the next param */
148 *str++ = '\0';
149 if (!(ob = find_object_both (params)))
150 {
151 new_draw_info_format (NDI_UNIQUE, 0, op, "Set whose god - can not find object %s?", params);
152 return 1;
153 }
154
155 /*
156 * Perhaps this is overly restrictive? Should we perhaps be able
157 * to rebless altars and the like?
158 */
159 if (ob->type != PLAYER)
160 {
161 new_draw_info_format (NDI_UNIQUE, 0, op, "%s is not a player - can not change its god", &ob->name);
162 return 1;
163 }
164
165 god = find_god (str);
166 if (god == NULL)
167 {
168 new_draw_info_format (NDI_UNIQUE, 0, op, "No such god %s.", str);
169 return 1;
170 }
171
172 become_follower (ob, god);
173 return 1;
174 }
175
176 // TODO: Rewrite banish in perl and get rid of the following two functions
177 int
178 command_kick (object *op, char *params)
179 {
180 for_all_players (pl)
181 if ((params == NULL || !strcmp (&pl->ob->name, params)) && !INVOKE_PLAYER (KICK, pl, ARG_STRING (params)))
182 {
183 object *op = pl->ob;
184
185 if (!QUERY_FLAG (op, FLAG_REMOVED) && !QUERY_FLAG (op, FLAG_FREED))
186 {
187 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_RED, 5, op, "%s is kicked out of the game.", &op->name);
188 strcpy (op->contr->killer, "kicked");
189 }
190
191 pl->ns->destroy ();
192 }
193
194 return 1;
195 }
196
197 //TODO
198 #if 0
199 int
200 command_save_overlay (object *op, char *params)
201 {
202 if (!op)
203 return 0;
204
205 if (op != NULL && !QUERY_FLAG (op, FLAG_WIZ))
206 {
207 new_draw_info (NDI_UNIQUE, 0, op, "Sorry, you can't force an overlay save.");
208 return 1;
209 }
210
211 new_save_map (op->map, 2);
212 new_save_map (op->map, 0);
213 new_draw_info (NDI_UNIQUE, 0, op, "Current map has been saved as an" " overlay.");
214
215 ready_map_name (op->map->path, 0);
216
217 return 1;
218 }
219 #endif
220
221 int
222 command_freeze (object *op, char *params)
223 {
224 int ticks;
225 player *pl;
226
227 if (!params)
228 {
229 new_draw_info (NDI_UNIQUE, 0, op, "Usage: freeze [ticks] <player>.");
230 return 1;
231 }
232
233 ticks = atoi (params);
234 if (ticks)
235 {
236 while ((isdigit (*params) || isspace (*params)) && *params != 0)
237 params++;
238 if (*params == 0)
239 {
240 new_draw_info (NDI_UNIQUE, 0, op, "Usage: freeze [ticks] <player>.");
241 return 1;
242 }
243 }
244 else
245 ticks = 100;
246
247 pl = get_other_player_from_name (op, params);
248 if (!pl)
249 return 1;
250
251 new_draw_info (NDI_UNIQUE | NDI_RED, 0, pl->ob, "You have been frozen by the DM!");
252 new_draw_info_format (NDI_UNIQUE, 0, op, "You freeze %s for %d ticks", &pl->ob->name, ticks);
253 pl->ob->speed_left = -(pl->ob->speed * ticks);
254 return 0;
255 }
256
257 int
258 command_arrest (object *op, char *params)
259 {
260 object *dummy;
261 player *pl;
262
263 if (!op)
264 return 0;
265
266 if (params == NULL)
267 {
268 new_draw_info (NDI_UNIQUE, 0, op, "Usage: arrest <player>.");
269 return 1;
270 }
271
272 pl = get_other_player_from_name (op, params);
273 if (!pl)
274 return 1;
275
276 dummy = get_jail_exit (pl->ob);
277 if (!dummy)
278 {
279 /* we have nowhere to send the prisoner.... */
280 new_draw_info (NDI_UNIQUE, 0, op, "can't jail player, there is no map to hold them");
281 return 0;
282 }
283
284 pl->ob->enter_exit (dummy);
285 dummy->destroy ();
286 new_draw_info (NDI_UNIQUE, 0, pl->ob, "You have been arrested.");
287 new_draw_info (NDI_UNIQUE, 0, op, "OK.");
288 LOG (llevInfo, "Player %s arrested by %s\n", &pl->ob->name, &op->name);
289 return 1;
290 }
291
292 int
293 command_summon (object *op, char *params)
294 {
295 int i;
296 object *dummy;
297 player *pl;
298
299 if (!op)
300 return 0;
301
302 if (params == NULL)
303 {
304 new_draw_info (NDI_UNIQUE, 0, op, "Usage: summon <player>.");
305 return 1;
306 }
307
308 pl = get_other_player_from_name (op, params);
309 if (!pl)
310 return 1;
311
312 i = find_free_spot (op, op->map, op->x, op->y, 1, 9);
313 if (i == -1)
314 {
315 new_draw_info (NDI_UNIQUE, 0, op, "Can not find a free spot to place summoned player.");
316 return 1;
317 }
318
319 dummy = object::create ();
320 EXIT_PATH (dummy) = op->map->path;
321 EXIT_X (dummy) = op->x + freearr_x[i];
322 EXIT_Y (dummy) = op->y + freearr_y[i];
323 pl->ob->enter_exit (dummy);
324 dummy->destroy ();
325 new_draw_info (NDI_UNIQUE, 0, pl->ob, "You are summoned.");
326 new_draw_info (NDI_UNIQUE, 0, op, "OK.");
327
328 return 1;
329 }
330
331 /**
332 * This function is a real mess, because we're stucking getting
333 * the entire item description in one block of text, so we just
334 * can't simply parse it - we need to look for double quotes
335 * for example. This could actually get much simpler with just a
336 * little help from the client - if we could get line breaks, it
337 * makes parsing much easier, eg, something like:
338 * arch dragon
339 * name big nasty creature
340 * hp 5
341 * sp 30
342 * Is much easier to parse than
343 * dragon name "big nasty creature" hp 5 sp 30
344 * for example.
345 */
346 int
347 command_create (object *op, char *params)
348 {
349 object *tmp = NULL;
350 int nrof, i, magic, set_magic = 0, set_nrof = 0, gotquote, gotspace;
351 char buf[MAX_BUF], *cp, *bp = buf, *bp2, *bp3, *bp4, *endline;
352 archetype *at, *at_spell = NULL;
353 artifact *art = NULL;
354
355 if (!op)
356 return 0;
357
358 if (params == NULL)
359 {
360 new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] <archetype> [ of <artifact>]" " [variable_to_patch setting]");
361 return 1;
362 }
363
364 bp = params;
365
366 /* We need to know where the line ends */
367 endline = bp + strlen (bp);
368
369 if (sscanf (bp, "%d ", &nrof))
370 {
371 if ((bp = strchr (params, ' ')) == NULL)
372 {
373 new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] <archetype> [ of <artifact>]" " [variable_to_patch setting]");
374 return 1;
375 }
376 bp++;
377 set_nrof = 1;
378 LOG (llevDebug, "%s creates: (%d) %s\n", &op->name, nrof, bp);
379 }
380
381 if (sscanf (bp, "%d ", &magic))
382 {
383 if ((bp = strchr (bp, ' ')) == NULL)
384 {
385 new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] <archetype> [ of <artifact>]" " [variable_to_patch setting]");
386 return 1;
387 }
388
389 bp++;
390 set_magic = 1;
391 LOG (llevDebug, "%s creates: (%d) (%d) %s\n", &op->name, nrof, magic, bp);
392 }
393
394 if ((cp = strstr (bp, " of ")) != NULL)
395 {
396 *cp = '\0';
397 cp += 4;
398 }
399
400 for (bp2 = bp; *bp2; bp2++)
401 {
402 if (*bp2 == ' ')
403 {
404 *bp2 = '\0';
405 bp2++;
406 break;
407 }
408 }
409
410 if ((at = archetype::find (bp)) == NULL)
411 {
412 new_draw_info (NDI_UNIQUE, 0, op, "No such archetype.");
413 return 1;
414 }
415
416 if (cp)
417 {
418 char spell_name[MAX_BUF], *fsp = NULL;
419
420 /*
421 * Try to find a spell object for this. Note that
422 * we also set up spell_name which is only
423 * the first word.
424 */
425
426 at_spell = archetype::find (cp);
427 if (!at_spell || at_spell->type != SPELL)
428 at_spell = find_archetype_by_object_name (cp);
429 if (!at_spell || at_spell->type != SPELL)
430 {
431 assign (spell_name, cp);
432 fsp = strchr (spell_name, ' ');
433 if (fsp)
434 {
435 *fsp = 0;
436 fsp++;
437 at_spell = archetype::find (spell_name);
438
439 /* Got a spell, update the first string pointer */
440 if (at_spell && at_spell->type == SPELL)
441 bp2 = cp + strlen (spell_name) + 1;
442 else
443 at_spell = NULL;
444 }
445 }
446
447 /* OK - we didn't find a spell - presume the 'of'
448 * in this case means its an artifact.
449 */
450 if (!at_spell)
451 {
452 if (find_artifactlist (at->type) == NULL)
453 new_draw_info_format (NDI_UNIQUE, 0, op, "No artifact list for type %d\n", at->type);
454 else
455 {
456 art = find_artifactlist (at->type)->items;
457
458 while (art)
459 {
460 if (!strcmp (art->item->name, cp))
461 break;
462
463 art = art->next;
464 }
465
466 if (!art)
467 new_draw_info_format (NDI_UNIQUE, 0, op, "No such artifact ([%d] of %s)", at->type, cp);
468 }
469
470 LOG (llevDebug, "%s creates: (%d) (%d) (%s) of (%s)\n", &op->name, set_nrof ? nrof : 0, set_magic ? magic : 0, bp, cp);
471 }
472 } /* if cp */
473
474 if ((at->type == ROD || at->type == WAND || at->type == SCROLL ||
475 at->type == HORN || at->type == SPELLBOOK) && !at_spell)
476 {
477 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);
478 return 1;
479 }
480
481 /*
482 * Rather than have two different blocks with a lot of similar code,
483 * just create one object, do all the processing, and then determine
484 * if that one object should be inserted or if we need to make copies.
485 */
486 tmp = arch_to_object (at);
487
488 if (set_magic)
489 set_abs_magic (tmp, magic);
490
491 if (art)
492 give_artifact_abilities (tmp, art->item);
493
494 if (need_identify (tmp))
495 {
496 SET_FLAG (tmp, FLAG_IDENTIFIED);
497 CLEAR_FLAG (tmp, FLAG_KNOWN_MAGICAL);
498 }
499
500 /*
501 * This entire block here tries to find variable pairings,
502 * eg, 'hp 4' or the like. The mess here is that values
503 * can be quoted (eg "my cool sword"); So the basic logic
504 * is we want to find two spaces, but if we got a quote,
505 * any spaces there don't count.
506 */
507 while (*bp2 && bp2 <= endline)
508 {
509 bp4 = NULL;
510 gotspace = 0;
511 gotquote = 0;
512
513 /* find the first quote */
514 for (bp3 = bp2; *bp3 && gotspace < 2 && gotquote < 2; bp3++)
515 {
516
517 /* Found a quote - now lets find the second one */
518 if (*bp3 == '"')
519 {
520 *bp3 = ' ';
521 bp2 = bp3 + 1; /* Update start of string */
522 bp3++;
523 gotquote++;
524 while (*bp3)
525 {
526 if (*bp3 == '"')
527 {
528 *bp3 = '\0';
529 gotquote++;
530 }
531 else
532 bp3++;
533 }
534 }
535 else if (*bp3 == ' ')
536 gotspace++;
537 }
538
539 /*
540 * If we got two spaces, send the second one to null.
541 * if we've reached the end of the line, increase gotspace -
542 * this is perfectly valid for the list entry listed.
543 */
544 if (gotspace == 2 || gotquote == 2)
545 {
546 bp3--; /* Undo the extra increment */
547 *bp3 = '\0';
548 }
549 else if (*bp3 == '\0')
550 gotspace++;
551
552 if ((gotquote && gotquote != 2) || (gotspace != 2 && gotquote != 2))
553 {
554 /*
555 * Unfortunately, we've clobbered lots of values, so printing
556 * out what we have probably isn't useful. Break out, because
557 * trying to recover is probably won't get anything useful
558 * anyways, and we'd be confused about end of line pointers
559 * anyways.
560 */
561 new_draw_info_format (NDI_UNIQUE, 0, op, "Malformed create line: %s", bp2);
562 break;
563 }
564
565 /* bp2 should still point to the start of this line,
566 * with bp3 pointing to the end
567 */
568 if (set_variable (tmp, bp2) == -1)
569 new_draw_info_format (NDI_UNIQUE, 0, op, "Unknown variable %s", bp2);
570 else
571 new_draw_info_format (NDI_UNIQUE, 0, op, "(%s#%d)->%s", &tmp->name, tmp->count, bp2);
572
573 bp2 = bp3 + 1;
574 }
575
576 if (at->nrof)
577 {
578 if (at_spell)
579 insert_ob_in_ob (arch_to_object (at_spell), tmp);
580
581 tmp->x = op->x;
582 tmp->y = op->y;
583
584 if (set_nrof)
585 tmp->nrof = nrof;
586
587 tmp->map = op->map;
588
589 tmp = insert_ob_in_ob (tmp, op);
590 esrv_send_item (op, tmp);
591
592 /* Let's put this created item on stack so dm can access it easily. */
593 dm_stack_push (op->contr, tmp->count);
594
595 return 1;
596 }
597 else
598 {
599 for (i = 0; i < (set_nrof ? nrof : 1); i++)
600 {
601 object *prev = 0, *head = 0;
602
603 for (archetype *atmp = at; atmp; atmp = (archetype *)atmp->more)
604 {
605 object *dup = arch_to_object (atmp);
606
607 if (at_spell)
608 insert_ob_in_ob (arch_to_object (at_spell), dup);
609
610 /*
611 * The head is what contains all the important bits,
612 * so just copying it over should be fine.
613 */
614 if (!head)
615 {
616 head = dup;
617 tmp->copy_to (dup);
618 }
619
620 dup->x = op->x + dup->arch->x;
621 dup->y = op->y + dup->arch->y;
622 dup->map = op->map;
623
624 if (head != dup)
625 {
626 dup->head = head;
627 prev->more = dup;
628 }
629
630 prev = dup;
631 }
632
633 if (QUERY_FLAG (head, FLAG_ALIVE))
634 {
635 object *check = head;
636 int size_x = 0;
637 int size_y = 0;
638
639 while (check)
640 {
641 size_x = MAX (size_x, check->arch->x);
642 size_y = MAX (size_y, check->arch->y);
643 check = check->more;
644 }
645
646 if (out_of_map (op->map, head->x + size_x, head->y + size_y))
647 {
648 if (head->x < size_x || head->y < size_y)
649 {
650 dm_stack_pop (op->contr);
651 head->destroy ();
652 new_draw_info (NDI_UNIQUE, 0, op, "Object too big to insert in map, or wrong position.");
653 tmp->destroy ();
654 return 1;
655 }
656
657 check = head;
658
659 while (check)
660 {
661 check->x -= size_x;
662 check->y -= size_y;
663 check = check->more;
664 }
665 }
666
667 insert_ob_in_map (head, op->map, op, 0);
668 }
669 else
670 head = insert_ob_in_ob (head, op);
671
672 /* Let's put this created item on stack so dm can access it easily. */
673 /* Wonder if we really want to push all of these, but since
674 * things like rods have nrof 0, we want to cover those.
675 */
676 dm_stack_push (op->contr, head->count);
677
678 if (at->randomitems != NULL && !at_spell)
679 create_treasure (at->randomitems, head, GT_APPLY, op->map->difficulty, 0);
680
681 esrv_send_item (op, head);
682 }
683
684 /* free the one we used to copy */
685 tmp->destroy ();
686 }
687
688 return 1;
689 }
690
691 /*
692 * Now follows dm-commands which are also acceptable from sockets
693 */
694
695 int
696 command_inventory (object *op, char *params)
697 {
698 object *tmp;
699 int i;
700
701 if (!params)
702 {
703 inventory (op, NULL);
704 return 0;
705 }
706
707 if (!sscanf (params, "%d", &i) || (tmp = find_object (i)) == NULL)
708 {
709 new_draw_info (NDI_UNIQUE, 0, op, "Inventory of what object (nr)?");
710 return 1;
711 }
712
713 inventory (op, tmp);
714 return 1;
715 }
716
717 /* just show player's their skills for now. Dm's can
718 * already see skills w/ inventory command - b.t.
719 */
720
721 int
722 command_skills (object *op, char *params)
723 {
724 show_skills (op, params);
725 return 0;
726 }
727
728 int
729 command_dump (object *op, char *params)
730 {
731 object *tmp;
732
733 tmp = get_dm_object (op->contr, &params, NULL);
734 if (!tmp)
735 return 1;
736
737 char *dump = dump_object (tmp);
738 new_draw_info (NDI_UNIQUE, 0, op, dump);
739 free (dump);
740
741 if (QUERY_FLAG (tmp, FLAG_OBJ_ORIGINAL))
742 new_draw_info (NDI_UNIQUE, 0, op, "Object is marked original");
743
744 return 1;
745 }
746
747 int
748 command_patch (object *op, char *params)
749 {
750 char *arg, *arg2;
751 object *tmp;
752
753 tmp = get_dm_object (op->contr, &params, NULL);
754 if (!tmp)
755 /* Player already informed of failure */
756 return 1;
757
758 /* params set to first value by get_dm_default */
759 arg = params;
760 if (arg == NULL)
761 {
762 new_draw_info (NDI_UNIQUE, 0, op, "Patch what values?");
763 return 1;
764 }
765
766 if ((arg2 = strchr (arg, ' ')))
767 arg2++;
768
769 if (set_variable (tmp, arg) == -1)
770 new_draw_info_format (NDI_UNIQUE, 0, op, "Unknown variable %s", arg);
771 else
772 new_draw_info_format (NDI_UNIQUE, 0, op, "(%s#%d)->%s=%s", &tmp->name, tmp->count, arg, arg2);
773
774 return 1;
775 }
776
777 int
778 command_remove (object *op, char *params)
779 {
780 object *tmp;
781 int from;
782
783 tmp = get_dm_object (op->contr, &params, &from);
784 if (!tmp)
785 {
786 new_draw_info (NDI_UNIQUE, 0, op, "Remove what object (nr)?");
787 return 1;
788 }
789
790 if (tmp->type == PLAYER)
791 {
792 new_draw_info (NDI_UNIQUE, 0, op, "Unable to remove a player!");
793 return 1;
794 }
795
796 if (QUERY_FLAG (tmp, FLAG_REMOVED))
797 {
798 new_draw_info_format (NDI_UNIQUE, 0, op, "%s is already removed!", query_name (tmp));
799 return 1;
800 }
801
802 if (from != STACK_FROM_STACK)
803 /* Item is either stack top, or is a number thus is now stack top, let's remove it */
804 dm_stack_pop (op->contr);
805
806 /* Always work on the head - otherwise object will get in odd state */
807 if (tmp->head)
808 tmp = tmp->head;
809 tmp->remove ();
810 return 1;
811 }
812
813 int
814 command_free (object *op, char *params)
815 {
816 object *tmp;
817 int from;
818
819 tmp = get_dm_object (op->contr, &params, &from);
820
821 if (!tmp)
822 {
823 new_draw_info (NDI_UNIQUE, 0, op, "Free what object (nr)?");
824 return 1;
825 }
826
827 if (from != STACK_FROM_STACK)
828 /* Item is either stack top, or is a number thus is now stack top, let's remove it */
829 dm_stack_pop (op->contr);
830
831 if (tmp->head)
832 tmp = tmp->head;
833
834 tmp->destroy ();
835 return 1;
836 }
837
838 /**
839 * This adds exp to a player. We now allow adding to a specific skill.
840 */
841 int
842 command_addexp (object *op, char *params)
843 {
844 char buf[MAX_BUF], skill[MAX_BUF];
845 int i, q;
846 object *skillob = NULL;
847
848 skill[0] = '\0';
849 if ((params == NULL) || (strlen (params) > MAX_BUF) || ((q = sscanf (params, "%s %d %s", buf, &i, skill)) < 2))
850 {
851 new_draw_info (NDI_UNIQUE, 0, op, "Usage: addexp <who> <how much> [<skill>].");
852 return 1;
853 }
854
855 for_all_players (pl)
856 if (!strncmp (pl->ob->name, buf, MAX_NAME))
857 {
858 if (q >= 3)
859 {
860 skillob = find_skill_by_name (pl->ob, skill);
861 if (!skillob)
862 {
863 new_draw_info_format (NDI_UNIQUE, 0, op, "Unable to find skill %s in %s", skill, buf);
864 return 1;
865 }
866
867 i = check_exp_adjust (skillob, i);
868 skillob->stats.exp += i;
869 calc_perm_exp (skillob);
870 player_lvl_adj (pl->ob, skillob);
871 }
872
873 pl->ob->stats.exp += i;
874 calc_perm_exp (pl->ob);
875 player_lvl_adj (pl->ob, NULL);
876
877 return 1;
878 }
879
880 new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
881 return 1;
882 }
883
884 /**************************************************************************/
885
886 /* Mods made by Tyler Van Gorder, May 10-13, 1992. */
887
888 /* CSUChico : tvangod@cscihp.ecst.csuchico.edu */
889
890 /**************************************************************************/
891
892 int
893 command_stats (object *op, char *params)
894 {
895 char thing[20];
896 char buf[MAX_BUF];
897
898 thing[0] = '\0';
899 if (params == NULL || !sscanf (params, "%s", thing) || thing == NULL)
900 {
901 new_draw_info (NDI_UNIQUE, 0, op, "Who?");
902 return 1;
903 }
904
905 for_all_players (pl)
906 if (!strcmp (pl->ob->name, thing))
907 {
908 sprintf (buf, "Str : %-2d H.P. : %-4d MAX : %d", pl->ob->stats.Str, pl->ob->stats.hp, pl->ob->stats.maxhp);
909 new_draw_info (NDI_UNIQUE, 0, op, buf);
910 sprintf (buf, "Dex : %-2d S.P. : %-4d MAX : %d", pl->ob->stats.Dex, pl->ob->stats.sp, pl->ob->stats.maxsp);
911 new_draw_info (NDI_UNIQUE, 0, op, buf);
912 sprintf (buf, "Con : %-2d AC : %-4d WC : %d", pl->ob->stats.Con, pl->ob->stats.ac, pl->ob->stats.wc);
913 new_draw_info (NDI_UNIQUE, 0, op, buf);
914 sprintf (buf, "Int : %-2d Damage : %d", pl->ob->stats.Int, pl->ob->stats.dam);
915 new_draw_info (NDI_UNIQUE, 0, op, buf);
916 sprintf (buf, "Wis : %-2d EXP : %" PRId64, pl->ob->stats.Wis, pl->ob->stats.exp);
917 new_draw_info (NDI_UNIQUE, 0, op, buf);
918 sprintf (buf, "Pow : %-2d Grace : %d", pl->ob->stats.Pow, pl->ob->stats.grace);
919 new_draw_info (NDI_UNIQUE, 0, op, buf);
920 sprintf (buf, "Cha : %-2d Food : %d", pl->ob->stats.Cha, pl->ob->stats.food);
921 new_draw_info (NDI_UNIQUE, 0, op, buf);
922 return 1;
923 }
924
925 new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
926 return 1;
927 }
928
929 int
930 command_abil (object *op, char *params)
931 {
932 char thing[20], thing2[20];
933 int iii;
934 char buf[MAX_BUF];
935
936 iii = 0;
937 thing[0] = '\0';
938 thing2[0] = '\0';
939 if (params == NULL || !sscanf (params, "%s %s %d", thing, thing2, &iii) || thing == NULL)
940 {
941 new_draw_info (NDI_UNIQUE, 0, op, "Who?");
942 return 1;
943 }
944
945 if (thing2 == NULL)
946 {
947 new_draw_info (NDI_UNIQUE, 0, op, "You can't change that.");
948 return 1;
949 }
950
951 if (iii < MIN_STAT || iii > MAX_STAT)
952 {
953 new_draw_info (NDI_UNIQUE, 0, op, "Illegal range of stat.\n");
954 return 1;
955 }
956
957 for_all_players (pl)
958 {
959 if (!strcmp (pl->ob->name, thing))
960 {
961 if (!strcmp ("str", thing2)) pl->ob->stats.Str = iii, pl->orig_stats.Str = iii;
962 if (!strcmp ("dex", thing2)) pl->ob->stats.Dex = iii, pl->orig_stats.Dex = iii;
963 if (!strcmp ("con", thing2)) pl->ob->stats.Con = iii, pl->orig_stats.Con = iii;
964 if (!strcmp ("wis", thing2)) pl->ob->stats.Wis = iii, pl->orig_stats.Wis = iii;
965 if (!strcmp ("cha", thing2)) pl->ob->stats.Cha = iii, pl->orig_stats.Cha = iii;
966 if (!strcmp ("int", thing2)) pl->ob->stats.Int = iii, pl->orig_stats.Int = iii;
967 if (!strcmp ("pow", thing2)) pl->ob->stats.Pow = iii, pl->orig_stats.Pow = iii;
968
969 sprintf (buf, "%s has been altered.", &pl->ob->name);
970 new_draw_info (NDI_UNIQUE, 0, op, buf);
971 pl->ob->update_stats ();
972 return 1;
973 }
974 }
975
976 new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
977 return 1;
978 }
979
980 int
981 command_nowiz (object *op, char *params)
982 { /* 'nodm' is alias */
983 CLEAR_FLAG (op, FLAG_WIZ);
984 CLEAR_FLAG (op, FLAG_WIZPASS);
985 CLEAR_FLAG (op, FLAG_WIZCAST);
986
987 if (op->contr->hidden)
988 {
989 new_draw_info (NDI_UNIQUE, 0, op, "You are no longer hidden from other players");
990 op->map->players++;
991 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, "%s has entered the game.", &op->name);
992 op->contr->hidden = 0;
993 op->invisible = 1;
994 }
995 else
996 new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_LT_GREEN, 1, NULL, "The Dungeon Master is gone..");
997 return 1;
998 }
999
1000 /**
1001 * object *op is trying to become dm.
1002 * pl_name is name supplied by player. Restrictive DM will make it harder
1003 * for socket users to become DM - in that case, it will check for the players
1004 * character name.
1005 */
1006 static int
1007 checkdm (object *op, const char *pl_name, const char *pl_passwd, const char *pl_host)
1008 {
1009 FILE *dmfile;
1010 char buf[MAX_BUF];
1011 char line_buf[160], name[160], passwd[160], host[160];
1012
1013 #ifdef RESTRICTIVE_DM
1014 *pl_name = op->name ? op->name : "*";
1015 #endif
1016
1017 sprintf (buf, "%s/%s", settings.confdir, DMFILE);
1018 if ((dmfile = fopen (buf, "r")) == NULL)
1019 {
1020 LOG (llevDebug, "Could not find DM file.\n");
1021 return 0;
1022 }
1023
1024 while (fgets (line_buf, 160, dmfile) != NULL)
1025 {
1026 if (line_buf[0] == '#')
1027 continue;
1028 if (sscanf (line_buf, "%[^:]:%[^:]:%s\n", name, passwd, host) != 3)
1029 {
1030 LOG (llevError, "Warning - malformed dm file entry: %s\n", line_buf);
1031 }
1032 else if ((!strcmp (name, "*") || (pl_name && !strcmp (pl_name, name)))
1033 && (!strcmp (passwd, "*") || !strcmp (passwd, pl_passwd)) && (!strcmp (host, "*") || !strcmp (host, pl_host)))
1034 {
1035 fclose (dmfile);
1036 return (1);
1037 }
1038 }
1039 fclose (dmfile);
1040 return (0);
1041 }
1042
1043 int
1044 do_wizard_dm (object *op, char *params, int silent)
1045 {
1046 if (!op->contr)
1047 return 0;
1048
1049 if (QUERY_FLAG (op, FLAG_WIZ))
1050 {
1051 new_draw_info (NDI_UNIQUE, 0, op, "You are already the Dungeon Master!");
1052 return 0;
1053 }
1054
1055 if (checkdm (op, op->name, (params ? params : "*"), op->contr->ns->host))
1056 {
1057 SET_FLAG (op, FLAG_WIZ);
1058 SET_FLAG (op, FLAG_WIZPASS);
1059 SET_FLAG (op, FLAG_WIZCAST);
1060
1061 new_draw_info (NDI_UNIQUE, 0, op, "Ok, you are the Dungeon Master!");
1062 /*
1063 * Remove setting flying here - that won't work, because next
1064 * fix_player() is called that will get cleared - proper solution
1065 * is probably something like a wiz_force which gives that and any
1066 * other desired abilities.
1067 */
1068 clear_los (op->contr);
1069 op->contr->write_buf[0] = '\0';
1070
1071 if (!silent)
1072 new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_LT_GREEN, 1, NULL, "The Dungeon Master has arrived!");
1073
1074 return 1;
1075 }
1076 else
1077 {
1078 new_draw_info (NDI_UNIQUE, 0, op, "Sorry Pal, I don't think so.");
1079 op->contr->write_buf[0] = '\0';
1080 return 0;
1081 }
1082 }
1083
1084 /*
1085 * Actual command to perhaps become dm. Changed aroun a bit in version 0.92.2
1086 * - allow people on sockets to become dm, and allow better dm file
1087 */
1088 int
1089 command_dm (object *op, char *params)
1090 {
1091 if (!op->contr)
1092 return 0;
1093
1094 do_wizard_dm (op, params, 0);
1095
1096 return 1;
1097 }
1098
1099 int
1100 command_invisible (object *op, char *params)
1101 {
1102 if (op)
1103 {
1104 op->invisible += 100;
1105 update_object (op, UP_OBJ_CHANGE);
1106 new_draw_info (NDI_UNIQUE, 0, op, "You turn invisible.");
1107 }
1108
1109 return 0;
1110 }
1111
1112 /**
1113 * Returns spell object (from archetypes) by name.
1114 * Returns NULL if 0 or more than one spell matches.
1115 * Used for wizard's learn spell/prayer.
1116 *
1117 * op is the player issuing the command.
1118 *
1119 * Ignores archetypes "spelldirect_xxx" since these archetypes are not used
1120 * anymore (but may still be present in some player's inventories and thus
1121 * cannot be removed). We have to ignore them here since they have the same
1122 * name than other "spell_xxx" archetypes and would always conflict.
1123 */
1124 static object *
1125 get_spell_by_name (object *op, const char *spell_name)
1126 {
1127 archetype *at;
1128 archetype *found;
1129 int conflict_found;
1130 size_t spell_name_length;
1131
1132 /* First check for full name matches. */
1133 conflict_found = 0;
1134 found = NULL;
1135 for_all_archetypes (at)
1136 {
1137 if (at->type != SPELL)
1138 continue;
1139
1140 if (strncmp (at->archname, "spelldirect_", 12) == 0)
1141 continue;
1142
1143 if (strcmp (at->object::name, spell_name) != 0)
1144 continue;
1145
1146 if (found != NULL)
1147 {
1148 if (!conflict_found)
1149 {
1150 conflict_found = 1;
1151 new_draw_info_format (NDI_UNIQUE, 0, op, "More than one archetype matches the spell name %s:", spell_name);
1152 new_draw_info_format (NDI_UNIQUE, 0, op, "- %s", &found->archname);
1153 }
1154
1155 new_draw_info_format (NDI_UNIQUE, 0, op, "- %s", &at->archname);
1156 continue;
1157 }
1158
1159 found = at;
1160 }
1161
1162 /* No match if more more than one archetype matches. */
1163 if (conflict_found)
1164 return NULL;
1165
1166 /* Return if exactly one archetype matches. */
1167 if (found != NULL)
1168 return arch_to_object (found);
1169
1170 /* No full match found: now check for partial matches. */
1171 spell_name_length = strlen (spell_name);
1172 conflict_found = 0;
1173 found = NULL;
1174 for_all_archetypes (at)
1175 {
1176 if (at->type != SPELL)
1177 continue;
1178
1179 if (strncmp (at->archname, "spelldirect_", 12) == 0)
1180 continue;
1181
1182 if (strncmp (at->object::name, spell_name, spell_name_length) != 0)
1183 continue;
1184
1185 if (found != NULL)
1186 {
1187 if (!conflict_found)
1188 {
1189 conflict_found = 1;
1190 new_draw_info_format (NDI_UNIQUE, 0, op, "More than one spell matches %s:", spell_name);
1191 new_draw_info_format (NDI_UNIQUE, 0, op, "- %s", &found->object::name);
1192 }
1193 new_draw_info_format (NDI_UNIQUE, 0, op, "- %s", &at->object::name);
1194 continue;
1195 }
1196
1197 found = at;
1198 }
1199
1200 /* No match if more more than one archetype matches. */
1201 if (conflict_found)
1202 return NULL;
1203
1204 /* Return if exactly one archetype matches. */
1205 if (found != NULL)
1206 return arch_to_object (found);
1207
1208 /* No spell found: just print an error message. */
1209 new_draw_info_format (NDI_UNIQUE, 0, op, "The spell %s does not exist.", spell_name);
1210 return NULL;
1211 }
1212
1213 static int
1214 command_learn_spell_or_prayer (object *op, char *params, int special_prayer)
1215 {
1216 object *tmp;
1217
1218 if (op->contr == NULL || params == NULL)
1219 {
1220 new_draw_info (NDI_UNIQUE, 0, op, "Which spell do you want to learn?");
1221 return 0;
1222 }
1223
1224 tmp = get_spell_by_name (op, params);
1225 if (tmp == NULL)
1226 {
1227 return 0;
1228 }
1229
1230 if (check_spell_known (op, tmp->name))
1231 {
1232 new_draw_info_format (NDI_UNIQUE, 0, op, "You already know the spell %s.", &tmp->name);
1233 return 0;
1234 }
1235
1236 do_learn_spell (op, tmp, special_prayer);
1237 tmp->destroy ();
1238 return 1;
1239 }
1240
1241 int
1242 command_learn_spell (object *op, char *params)
1243 {
1244 return command_learn_spell_or_prayer (op, params, 0);
1245 }
1246
1247 int
1248 command_learn_special_prayer (object *op, char *params)
1249 {
1250 return command_learn_spell_or_prayer (op, params, 1);
1251 }
1252
1253 int
1254 command_forget_spell (object *op, char *params)
1255 {
1256 object *spell;
1257
1258 if (op->contr == NULL || params == NULL)
1259 {
1260 new_draw_info (NDI_UNIQUE, 0, op, "Which spell do you want to forget?");
1261 return 0;
1262 }
1263
1264 spell = lookup_spell_by_name (op, params);
1265 if (spell == NULL)
1266 {
1267 new_draw_info_format (NDI_UNIQUE, 0, op, "You do not know the spell %s.", params);
1268 return 0;
1269 }
1270
1271 do_forget_spell (op, spell->name);
1272 return 1;
1273 }
1274
1275 /**
1276 * Lists all plugins currently loaded with their IDs and full names.
1277 */
1278 int
1279 command_listplugins (object *op, char *params)
1280 {
1281 plugins_display_list (op);
1282 return 1;
1283 }
1284
1285 /**
1286 * Loads the given plugin. The DM specifies the name of the library to load (no
1287 * pathname is needed). Do not ever attempt to load the same plugin more than
1288 * once at a time, or bad things could happen.
1289 */
1290 int
1291 command_loadplugin (object *op, char *params)
1292 {
1293 char buf[MAX_BUF];
1294
1295 if (params == NULL)
1296 {
1297 new_draw_info (NDI_UNIQUE, 0, op, "Load which plugin?");
1298 return 1;
1299 }
1300
1301 strcpy (buf, LIBDIR);
1302 strcat (buf, "/plugins/");
1303 strcat (buf, params);
1304 LOG (llevDebug, "Requested plugin file is %s\n", buf);
1305 if (plugins_init_plugin (buf) == 0)
1306 new_draw_info (NDI_UNIQUE, 0, op, "Plugin successfully loaded.");
1307 else
1308 new_draw_info (NDI_UNIQUE, 0, op, "Could not load plugin.");
1309 return 1;
1310 }
1311
1312 /**
1313 * Unloads the given plugin. The DM specified the ID of the library to unload.
1314 * Note that some things may behave strangely if the correct plugins are not
1315 * loaded.
1316 */
1317 int
1318 command_unloadplugin (object *op, char *params)
1319 {
1320 if (params == NULL)
1321 {
1322 new_draw_info (NDI_UNIQUE, 0, op, "Remove which plugin?");
1323 return 1;
1324 }
1325
1326 if (plugins_remove_plugin (params) == 0)
1327 new_draw_info (NDI_UNIQUE, 0, op, "Plugin successfully removed.");
1328 else
1329 new_draw_info (NDI_UNIQUE, 0, op, "Could not remove plugin.");
1330 return 1;
1331 }
1332
1333 /**
1334 * A players wants to become DM and hide.
1335 * Let's see if that's authorized.
1336 * Make sure to not tell anything to anyone.
1337 */
1338 int
1339 command_dmhide (object *op, char *params)
1340 {
1341 if (!do_wizard_dm (op, params, 1))
1342 return 0;
1343
1344 do_wizard_hide (op, 1);
1345
1346 return 1;
1347 }
1348
1349 void
1350 dm_stack_pop (player *pl)
1351 {
1352 if (!pl->stack_items || !pl->stack_position)
1353 {
1354 new_draw_info (NDI_UNIQUE, 0, pl->ob, "Empty stack!");
1355 return;
1356 }
1357
1358 pl->stack_position--;
1359 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Popped item from stack, %d left.", pl->stack_position);
1360 }
1361
1362 /**
1363 * Get current stack top item for player.
1364 * Returns NULL if no stacked item.
1365 * If stacked item disappeared (freed), remove it.
1366 *
1367 * Ryo, august 2004
1368 */
1369 object *
1370 dm_stack_peek (player *pl)
1371 {
1372 object *ob;
1373
1374 if (!pl->stack_position)
1375 {
1376 new_draw_info (NDI_UNIQUE, 0, pl->ob, "Empty stack!");
1377 return NULL;
1378 }
1379
1380 ob = find_object (pl->stack_items[pl->stack_position - 1]);
1381 if (!ob)
1382 {
1383 new_draw_info (NDI_UNIQUE, 0, pl->ob, "Stacked item was removed!");
1384 dm_stack_pop (pl);
1385 return NULL;
1386 }
1387
1388 return ob;
1389 }
1390
1391 /**
1392 * Push specified item on player stack.
1393 * Inform player of position.
1394 * Initializes variables if needed.
1395 */
1396 void
1397 dm_stack_push (player *pl, tag_t item)
1398 {
1399 if (!pl->stack_items)
1400 {
1401 pl->stack_items = (tag_t *) malloc (sizeof (tag_t) * STACK_SIZE);
1402 memset (pl->stack_items, 0, sizeof (tag_t) * STACK_SIZE);
1403 }
1404
1405 if (pl->stack_position == STACK_SIZE)
1406 {
1407 new_draw_info (NDI_UNIQUE, 0, pl->ob, "Item stack full!");
1408 return;
1409 }
1410
1411 pl->stack_items[pl->stack_position] = item;
1412 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Item stacked as %d.", pl->stack_position);
1413 pl->stack_position++;
1414 }
1415
1416 /**
1417 * Checks 'params' for object code.
1418 *
1419 * Can be:
1420 * * empty => get current object stack top for player
1421 * * number => get item with that tag, stack it for future use
1422 * * $number => get specified stack item
1423 * * "me" => player himself
1424 *
1425 * At function exit, params points to first non-object char
1426 *
1427 * 'from', if not NULL, contains at exit:
1428 * * STACK_FROM_NONE => object not found
1429 * * STACK_FROM_TOP => top item stack, may be NULL if stack was empty
1430 * * STACK_FROM_STACK => item from somewhere in the stack
1431 * * STACK_FROM_NUMBER => item by number, pushed on stack
1432 *
1433 * Ryo, august 2004
1434 */
1435 object *
1436 get_dm_object (player *pl, char **params, int *from)
1437 {
1438 int item_tag, item_position;
1439 object *ob;
1440
1441 if (!pl)
1442 return NULL;
1443
1444 if (!params || !*params || **params == '\0')
1445 {
1446 if (from)
1447 *from = STACK_FROM_TOP;
1448 /* No parameter => get stack item */
1449 return dm_stack_peek (pl);
1450 }
1451
1452 /* Let's clean white spaces */
1453 while (**params == ' ')
1454 (*params)++;
1455
1456 /* Next case: number => item tag */
1457 if (sscanf (*params, "%d", &item_tag))
1458 {
1459 /* Move parameter to next item */
1460 while (isdigit (**params))
1461 (*params)++;
1462
1463 /* And skip blanks, too */
1464 while (**params == ' ')
1465 (*params)++;
1466
1467 /* Get item */
1468 ob = find_object (item_tag);
1469 if (!ob)
1470 {
1471 if (from)
1472 *from = STACK_FROM_NONE;
1473 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "No such item %d!", item_tag);
1474 return NULL;
1475 }
1476
1477 /* Got one, let's push it on stack */
1478 dm_stack_push (pl, item_tag);
1479 if (from)
1480 *from = STACK_FROM_NUMBER;
1481 return ob;
1482 }
1483
1484 /* Next case: $number => stack item */
1485 if (sscanf (*params, "$%d", &item_position))
1486 {
1487 /* Move parameter to next item */
1488 (*params)++;
1489
1490 while (isdigit (**params))
1491 (*params)++;
1492 while (**params == ' ')
1493 (*params)++;
1494
1495 if (item_position >= pl->stack_position)
1496 {
1497 if (from)
1498 *from = STACK_FROM_NONE;
1499 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "No such stack item %d!", item_position);
1500 return NULL;
1501 }
1502
1503 ob = find_object (pl->stack_items[item_position]);
1504 if (!ob)
1505 {
1506 if (from)
1507 *from = STACK_FROM_NONE;
1508 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Stack item %d was removed.", item_position);
1509 return NULL;
1510 }
1511
1512 if (from)
1513 *from = item_position < pl->stack_position - 1 ? STACK_FROM_STACK : STACK_FROM_TOP;
1514 return ob;
1515 }
1516
1517 /* Next case: 'me' => return pl->ob */
1518 if (!strncmp (*params, "me", 2))
1519 {
1520 if (from)
1521 *from = STACK_FROM_NUMBER;
1522 dm_stack_push (pl, pl->ob->count);
1523
1524 /* Skip to next token */
1525 (*params) += 2;
1526 while (**params == ' ')
1527 (*params)++;
1528
1529 return pl->ob;
1530 }
1531
1532 /* Last case: get stack top */
1533 if (from)
1534 *from = STACK_FROM_TOP;
1535 return dm_stack_peek (pl);
1536 }
1537
1538 /**
1539 * Pop the stack top.
1540 */
1541 int
1542 command_stack_pop (object *op, char *params)
1543 {
1544 dm_stack_pop (op->contr);
1545 return 0;
1546 }
1547
1548 /**
1549 * Push specified item on stack.
1550 */
1551 int
1552 command_stack_push (object *op, char *params)
1553 {
1554 object *ob;
1555 int from;
1556
1557 ob = get_dm_object (op->contr, &params, &from);
1558
1559 if (ob && from != STACK_FROM_NUMBER)
1560 /* Object was from stack, need to push it again */
1561 dm_stack_push (op->contr, ob->count);
1562
1563 return 0;
1564 }
1565
1566 /**
1567 * Displays stack contents.
1568 */
1569 int
1570 command_stack_list (object *op, char *params)
1571 {
1572 int item;
1573 object *display;
1574 player *pl = op->contr;
1575
1576 new_draw_info (NDI_UNIQUE, 0, op, "Item stack contents:");
1577
1578 for (item = 0; item < pl->stack_position; item++)
1579 {
1580 display = find_object (pl->stack_items[item]);
1581 if (display)
1582 new_draw_info_format (NDI_UNIQUE, 0, op, " %d : %s [%d]", item, &display->name, display->count);
1583 else
1584 /* Item was freed */
1585 new_draw_info_format (NDI_UNIQUE, 0, op, " %d : (lost item: %d)", item, pl->stack_items[item]);
1586 }
1587
1588 return 0;
1589 }
1590
1591 /**
1592 * Empty DM item stack.
1593 */
1594 int
1595 command_stack_clear (object *op, char *params)
1596 {
1597 op->contr->stack_position = 0;
1598 new_draw_info (NDI_UNIQUE, 0, op, "Item stack cleared.");
1599 return 0;
1600 }
1601
1602 int
1603 command_insert_into (object *op, char *params)
1604 {
1605 object *left, *right, *inserted;
1606 int left_from, right_from;
1607
1608 left = get_dm_object (op->contr, &params, &left_from);
1609 if (!left)
1610 {
1611 new_draw_info (NDI_UNIQUE, 0, op, "Insert into what object?");
1612 return 0;
1613 }
1614
1615 if (left_from == STACK_FROM_NUMBER)
1616 /* Item was stacked, remove it else right will be the same... */
1617 dm_stack_pop (op->contr);
1618
1619 right = get_dm_object (op->contr, &params, &right_from);
1620
1621 if (!right)
1622 {
1623 new_draw_info (NDI_UNIQUE, 0, op, "Insert what item?");
1624 return 0;
1625 }
1626
1627 if (left_from == STACK_FROM_TOP && right_from == STACK_FROM_TOP)
1628 {
1629 /*
1630 * Special case: both items were taken from stack top.
1631 * Override the behaviour, taking left as item just below top, if exists.
1632 * See function description for why.
1633 * Besides, can't insert an item into itself.
1634 */
1635 if (op->contr->stack_position > 1)
1636 {
1637 left = find_object (op->contr->stack_items[op->contr->stack_position - 2]);
1638 if (left)
1639 new_draw_info (NDI_UNIQUE, 0, op, "(Note: item to insert into taken from undertop)");
1640 else
1641 /* Stupid case: item under top was freed, fallback to stack top */
1642 left = right;
1643 }
1644 }
1645
1646 if (left == right)
1647 {
1648 new_draw_info (NDI_UNIQUE, 0, op, "Can't insert an object into itself!");
1649 return 0;
1650 }
1651
1652 if (right->type == PLAYER)
1653 {
1654 new_draw_info (NDI_UNIQUE, 0, op, "Can't insert a player into something!");
1655 return 0;
1656 }
1657
1658 if (!QUERY_FLAG (right, FLAG_REMOVED))
1659 right->remove ();
1660 inserted = insert_ob_in_ob (right, left);
1661 if (left->type == PLAYER)
1662 if (inserted == right)
1663 esrv_send_item (left, right);
1664 else
1665 esrv_update_item (UPD_WEIGHT | UPD_NAME | UPD_NROF, left, inserted);
1666
1667 new_draw_info_format (NDI_UNIQUE, 0, op, "Inserted %s in %s", query_name (inserted), query_name (left));
1668
1669 return 0;
1670
1671 }