ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_wiz.C
Revision: 1.41
Committed: Wed Mar 14 04:12:29 2007 UTC (17 years, 2 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.40: +1 -1 lines
Log Message:
- rewrote more face handling code
- automatically send smooth faces, as the client will need them anyways
  and it makes little sense to wait for the client to axk for it. of course,
  gcfclient suffers from weird ordering problems again.
- UP_OBJ_FACE was often abused in situations where other things changed,
  updated lots of spaces, probably more to be done.
- update_smooth became so small that inlining it actually clarified
  the code. similar for update_space, which is not inlined for other reasons.
- faces were not initialised properly
- add versioncheck for face data
- rewrite invisibility handling a bit: god finger etc. now makes you blink,
  blinking routine has changed to be less annoying and more useful while
  still indicating invisibleness.

File Contents

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