ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_wiz.C
Revision: 1.97
Committed: Wed Dec 5 19:31:26 2018 UTC (5 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.96: +3 -3 lines
Log Message:
improve, not fix, some sscanf madness, probably introducing more bugs

File Contents

# Content
1 /*
2 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 *
4 * Copyright (©) 2017,2018 Marc Alexander Lehmann / the Deliantra team
5 * Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
6 * Copyright (©) 2002 Mark Wedel & Crossfire Development Team
7 * Copyright (©) 1992 Frank Tore Johansen
8 *
9 * Deliantra is free software: you can redistribute it and/or modify it under
10 * the terms of the Affero GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your
12 * option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the Affero GNU General Public License
20 * and the GNU General Public License along with this program. If not, see
21 * <http://www.gnu.org/licenses/>.
22 *
23 * The authors can be reached via e-mail to <support@deliantra.net>
24 */
25
26 #include <global.h>
27 #include <sproto.h>
28 #include <spells.h>
29 #include <treasure.h>
30 #include <skills.h>
31
32 /** Defines for DM item stack **/
33 #define STACK_SIZE 50 /* Stack size, static */
34
35 /* Values for 'from' field of get_dm_object */
36 #define STACK_FROM_NONE 0 /* Item was not found */
37 #define STACK_FROM_TOP 1 /* Item is stack top */
38 #define STACK_FROM_STACK 2 /* Item is somewhere in stack */
39 #define STACK_FROM_NUMBER 3 /* Item is a number (may be top) */
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 static void
77 dm_stack_pop (player *pl)
78 {
79 if (!pl->stack_items || !pl->stack_position)
80 {
81 new_draw_info (NDI_UNIQUE, 0, pl->ob, "Empty stack!");
82 return;
83 }
84
85 pl->stack_position--;
86 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Popped item from stack, %d left.", pl->stack_position);
87 }
88
89 /**
90 * Get current stack top item for player.
91 * Returns NULL if no stacked item.
92 * If stacked item disappeared (freed), remove it.
93 *
94 * Ryo, august 2004
95 */
96 static object *
97 dm_stack_peek (player *pl)
98 {
99 object *ob;
100
101 if (!pl->stack_position)
102 {
103 new_draw_info (NDI_UNIQUE, 0, pl->ob, "Empty stack!");
104 return NULL;
105 }
106
107 ob = find_object (pl->stack_items[pl->stack_position - 1]);
108 if (!ob)
109 {
110 new_draw_info (NDI_UNIQUE, 0, pl->ob, "Stacked item was removed!");
111 dm_stack_pop (pl);
112 return NULL;
113 }
114
115 return ob;
116 }
117
118 /**
119 * Push specified item on player stack.
120 * Inform player of position.
121 * Initializes variables if needed.
122 */
123 void
124 dm_stack_push (player *pl, tag_t item)
125 {
126 if (!pl->stack_items)
127 {
128 pl->stack_items = (tag_t *) malloc (sizeof (tag_t) * STACK_SIZE);
129 memset (pl->stack_items, 0, sizeof (tag_t) * STACK_SIZE);
130 }
131
132 if (pl->stack_position == STACK_SIZE)
133 {
134 new_draw_info (NDI_UNIQUE, 0, pl->ob, "Item stack full!");
135 return;
136 }
137
138 pl->stack_items[pl->stack_position] = item;
139 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Item stacked as %d.", pl->stack_position);
140 pl->stack_position++;
141 }
142
143 /**
144 * Checks 'params' for object code.
145 *
146 * Can be:
147 * * empty => get current object stack top for player
148 * * number => get item with that tag, stack it for future use
149 * * $number => get specified stack item
150 * * "me" => player himself
151 *
152 * At function exit, params points to first non-object char
153 *
154 * 'from', if not NULL, contains at exit:
155 * * STACK_FROM_NONE => object not found
156 * * STACK_FROM_TOP => top item stack, may be NULL if stack was empty
157 * * STACK_FROM_STACK => item from somewhere in the stack
158 * * STACK_FROM_NUMBER => item by number, pushed on stack
159 *
160 * Ryo, august 2004
161 */
162 static object *
163 get_dm_object (player *pl, char **params, int *from)
164 {
165 int item_tag, item_position;
166 object *ob;
167
168 if (!pl)
169 return NULL;
170
171 if (!params || !*params || **params == '\0')
172 {
173 if (from)
174 *from = STACK_FROM_TOP;
175 /* No parameter => get stack item */
176 return dm_stack_peek (pl);
177 }
178
179 /* Let's clean white spaces */
180 while (**params == ' ')
181 (*params)++;
182
183 /* Next case: number => item tag */
184 if (sscanf (*params, "%d", &item_tag))
185 {
186 /* Move parameter to next item */
187 while (isdigit (**params))
188 (*params)++;
189
190 /* And skip blanks, too */
191 while (**params == ' ')
192 (*params)++;
193
194 /* Get item */
195 ob = find_object (item_tag);
196 if (!ob)
197 {
198 if (from)
199 *from = STACK_FROM_NONE;
200 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "No such item %d!", item_tag);
201 return NULL;
202 }
203
204 /* Got one, let's push it on stack */
205 dm_stack_push (pl, item_tag);
206 if (from)
207 *from = STACK_FROM_NUMBER;
208 return ob;
209 }
210
211 /* Next case: $number => stack item */
212 if (sscanf (*params, "$%d", &item_position))
213 {
214 /* Move parameter to next item */
215 (*params)++;
216
217 while (isdigit (**params))
218 (*params)++;
219 while (**params == ' ')
220 (*params)++;
221
222 if (item_position >= pl->stack_position)
223 {
224 if (from)
225 *from = STACK_FROM_NONE;
226 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "No such stack item %d!", item_position);
227 return NULL;
228 }
229
230 ob = find_object (pl->stack_items[item_position]);
231 if (!ob)
232 {
233 if (from)
234 *from = STACK_FROM_NONE;
235 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Stack item %d was removed.", item_position);
236 return NULL;
237 }
238
239 if (from)
240 *from = item_position < pl->stack_position - 1 ? STACK_FROM_STACK : STACK_FROM_TOP;
241 return ob;
242 }
243
244 /* Next case: 'me' => return pl->ob */
245 if (!strncmp (*params, "me", 2))
246 {
247 if (from)
248 *from = STACK_FROM_NUMBER;
249 dm_stack_push (pl, pl->ob->count);
250
251 /* Skip to next token */
252 (*params) += 2;
253 while (**params == ' ')
254 (*params)++;
255
256 return pl->ob;
257 }
258
259 /* Last case: get stack top */
260 if (from)
261 *from = STACK_FROM_TOP;
262 return dm_stack_peek (pl);
263 }
264
265 /**
266 * This finds and returns the object which matches the name or
267 * object nubmer (specified via num #whatever).
268 */
269 static object *
270 find_object_both (char *params)
271 {
272 if (!params)
273 return NULL;
274
275 if (params[0] == '#')
276 return find_object (atol (params + 1));
277 else
278 return find_object_name (params);
279 }
280
281 /**
282 * Sets the god for some objects. params should contain two values -
283 * first the object to change, followed by the god to change it to.
284 */
285 int
286 command_setgod (object *op, char *params)
287 {
288 object *ob, *god;
289 char *str;
290
291 if (!params || !(str = strchr (params, ' ')))
292 {
293 new_draw_info (NDI_UNIQUE, 0, op, "Usage: setgod object god");
294 return 0;
295 }
296
297 /* kill the space, and set string to the next param */
298 *str++ = '\0';
299 if (!(ob = find_object_both (params)))
300 {
301 new_draw_info_format (NDI_UNIQUE, 0, op, "Set whose god - can not find object %s?", params);
302 return 1;
303 }
304
305 /*
306 * Perhaps this is overly restrictive? Should we perhaps be able
307 * to rebless altars and the like?
308 */
309 if (ob->type != PLAYER)
310 {
311 new_draw_info_format (NDI_UNIQUE, 0, op, "%s is not a player - can not change its god", &ob->name);
312 return 1;
313 }
314
315 god = find_god (str);
316 if (god == NULL)
317 {
318 new_draw_info_format (NDI_UNIQUE, 0, op, "No such god %s.", str);
319 return 1;
320 }
321
322 ob->become_follower (god);
323 return 1;
324 }
325
326 int
327 command_freeze (object *op, char *params)
328 {
329 int ticks;
330 player *pl;
331
332 if (!params)
333 {
334 new_draw_info (NDI_UNIQUE, 0, op, "Usage: freeze [ticks] <player>.");
335 return 1;
336 }
337
338 ticks = atoi (params);
339 if (ticks)
340 {
341 while ((isdigit (*params) || isspace (*params)) && *params != 0)
342 params++;
343 if (*params == 0)
344 {
345 new_draw_info (NDI_UNIQUE, 0, op, "Usage: freeze [ticks] <player>.");
346 return 1;
347 }
348 }
349 else
350 ticks = 100;
351
352 pl = get_other_player_from_name (op, params);
353 if (!pl)
354 return 1;
355
356 new_draw_info (NDI_UNIQUE | NDI_RED, 0, pl->ob, "You have been frozen by the DM!");
357 new_draw_info_format (NDI_UNIQUE, 0, op, "You freeze %s for %d ticks", &pl->ob->name, ticks);
358 pl->ob->speed_left = -(pl->ob->speed * ticks);
359 return 0;
360 }
361
362 int
363 command_arrest (object *op, char *params)
364 {
365 object *dummy;
366 player *pl;
367
368 if (!op)
369 return 0;
370
371 if (params == NULL)
372 {
373 new_draw_info (NDI_UNIQUE, 0, op, "Usage: arrest <player>.");
374 return 1;
375 }
376
377 pl = get_other_player_from_name (op, params);
378 if (!pl)
379 return 1;
380
381 dummy = get_jail_exit (pl->ob);
382 if (!dummy)
383 {
384 /* we have nowhere to send the prisoner.... */
385 new_draw_info (NDI_UNIQUE, 0, op, "can't jail player, there is no map to hold them");
386 return 0;
387 }
388
389 pl->ob->player_goto (dummy->slaying, dummy->stats.hp, dummy->stats.sp);//TODO
390 dummy->destroy ();
391
392 new_draw_info (NDI_UNIQUE, 0, pl->ob, "You have been arrested.");
393 new_draw_info (NDI_UNIQUE, 0, op, "OK.");
394 LOG (llevInfo, "Player %s arrested by %s\n", &pl->ob->name, &op->name);
395 return 1;
396 }
397
398 int
399 command_summon (object *op, char *params)
400 {
401 int i;
402 player *pl;
403
404 if (!op)
405 return 0;
406
407 if (params == NULL)
408 {
409 new_draw_info (NDI_UNIQUE, 0, op, "Usage: summon <player>.");
410 return 1;
411 }
412
413 pl = get_other_player_from_name (op, params);
414 if (!pl)
415 return 1;
416
417 maptile *m = op->map;
418 sint16 nx = op->x;
419 sint16 ny = op->y;
420
421 i = find_free_spot (op, m, nx, ny, 1, SIZEOFFREE1+1);
422 if (i == -1)
423 {
424 new_draw_info (NDI_UNIQUE, 0, op, "Can not find a free spot to place summoned player.");
425 return 1;
426 }
427
428 nx += DIRX (i);
429 ny += DIRY (i);
430
431 if (!xy_normalise (m, nx, ny))
432 {
433 new_draw_info (NDI_UNIQUE, 0, op, "Can not find a free spot to place summoned player.");
434 return 1;
435 }
436
437 pl->ob->player_goto (m->path, nx, ny);
438 new_draw_info (NDI_UNIQUE, 0, pl->ob, "You are summoned.");
439 new_draw_info (NDI_UNIQUE, 0, op, "OK.");
440
441 return 1;
442 }
443
444 /**
445 * This function is a real mess, because we're stucking getting
446 * the entire item description in one block of text, so we just
447 * can't simply parse it - we need to look for double quotes
448 * for example. This could actually get much simpler with just a
449 * little help from the client - if we could get line breaks, it
450 * makes parsing much easier, eg, something like:
451 * arch dragon
452 * name big nasty creature
453 * hp 5
454 * sp 30
455 * Is much easier to parse than
456 * dragon name "big nasty creature" hp 5 sp 30
457 * for example.
458 */
459 int
460 command_create (object *op, char *params)
461 {
462 object *tmp = NULL;
463 int nrof, i, magic, set_magic = 0, set_nrof = 0, gotquote, gotspace;
464 char buf[MAX_BUF], *cp, *bp = buf, *bp2, *bp3, *endline;
465 archetype *at, *at_spell = NULL;
466 artifact *art = NULL;
467
468 if (!op)
469 return 0;
470
471 if (params == NULL)
472 {
473 new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] <archetype> [ of <artifact>]" " [variable_to_patch setting]");
474 return 1;
475 }
476
477 bp = params;
478
479 /* We need to know where the line ends */
480 endline = bp + strlen (bp);
481
482 if (sscanf (bp, "%d ", &nrof))
483 {
484 if ((bp = strchr (params, ' ')) == NULL)
485 {
486 new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] <archetype> [ of <artifact>]" " [variable_to_patch setting]");
487 return 1;
488 }
489 bp++;
490 set_nrof = 1;
491 LOG (llevDebug, "%s creates: (%d) %s\n", &op->name, nrof, bp);
492 }
493
494 if (sscanf (bp, "%d ", &magic))
495 {
496 if ((bp = strchr (bp, ' ')) == NULL)
497 {
498 new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] <archetype> [ of <artifact>]" " [variable_to_patch setting]");
499 return 1;
500 }
501
502 bp++;
503 set_magic = 1;
504 LOG (llevDebug, "%s creates: (%d) (%d) %s\n", &op->name, nrof, magic, bp);
505 }
506
507 if ((cp = strstr (bp, " of ")) != NULL)
508 {
509 *cp = '\0';
510 cp += 4;
511 }
512
513 for (bp2 = bp; *bp2; bp2++)
514 {
515 if (*bp2 == ' ')
516 {
517 *bp2 = '\0';
518 bp2++;
519 break;
520 }
521 }
522
523 if ((at = archetype::find (bp)) == NULL)
524 {
525 new_draw_info (NDI_UNIQUE, 0, op, "No such archetype.");
526 return 1;
527 }
528
529 if (cp)
530 {
531 char spell_name[MAX_BUF], *fsp = NULL;
532
533 /*
534 * Try to find a spell object for this. Note that
535 * we also set up spell_name which is only
536 * the first word.
537 */
538
539 at_spell = archetype::find (cp);
540 if (!at_spell || at_spell->type != SPELL)
541 at_spell = find_archetype_by_object_name (cp);
542 if (!at_spell || at_spell->type != SPELL)
543 {
544 assign (spell_name, cp);
545 fsp = strchr (spell_name, ' ');
546 if (fsp)
547 {
548 *fsp = 0;
549 fsp++;
550 at_spell = archetype::find (spell_name);
551
552 /* Got a spell, update the first string pointer */
553 if (at_spell && at_spell->type == SPELL)
554 bp2 = cp + strlen (spell_name) + 1;
555 else
556 at_spell = NULL;
557 }
558 }
559
560 /* OK - we didn't find a spell - presume the 'of'
561 * in this case means its an artifact.
562 */
563 if (!at_spell)
564 {
565 if (find_artifactlist (at->type) == NULL)
566 new_draw_info_format (NDI_UNIQUE, 0, op, "No artifact list for type %d\n", at->type);
567 else
568 {
569 art = find_artifactlist (at->type)->items;
570
571 while (art)
572 {
573 if (!strcmp (&art->item->name, cp))
574 break;
575
576 art = art->next;
577 }
578
579 if (!art)
580 new_draw_info_format (NDI_UNIQUE, 0, op, "No such artifact ([%d] of %s)", at->type, cp);
581 }
582
583 LOG (llevDebug, "%s creates: (%d) (%d) (%s) of (%s)\n", &op->name, set_nrof ? nrof : 0, set_magic ? magic : 0, bp, cp);
584 }
585 } /* if cp */
586
587 if ((at->type == ROD || at->type == WAND || at->type == SCROLL ||
588 at->type == HORN || at->type == SPELLBOOK) && !at_spell)
589 {
590 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);
591 return 1;
592 }
593
594 /*
595 * Rather than have two different blocks with a lot of similar code,
596 * just create one object, do all the processing, and then determine
597 * if that one object should be inserted or if we need to make copies.
598 */
599 tmp = at->instance ();
600
601 if (set_magic)
602 set_abs_magic (tmp, magic);
603
604 if (art)
605 give_artifact_abilities (tmp, art->item);
606
607 if (tmp->need_identify ())
608 {
609 tmp->set_flag (FLAG_IDENTIFIED);
610 tmp->clr_flag (FLAG_KNOWN_MAGICAL);
611 }
612
613 /*
614 * This entire block here tries to find variable pairings,
615 * eg, 'hp 4' or the like. The mess here is that values
616 * can be quoted (eg "my cool sword"); So the basic logic
617 * is we want to find two spaces, but if we got a quote,
618 * any spaces there don't count.
619 */
620 while (*bp2 && bp2 <= endline)
621 {
622 gotspace = 0;
623 gotquote = 0;
624
625 /* find the first quote */
626 for (bp3 = bp2; *bp3 && gotspace < 2 && gotquote < 2; bp3++)
627 {
628
629 /* Found a quote - now lets find the second one */
630 if (*bp3 == '"')
631 {
632 *bp3 = ' ';
633 bp2 = bp3 + 1; /* Update start of string */
634 bp3++;
635 gotquote++;
636 while (*bp3)
637 {
638 if (*bp3 == '"')
639 {
640 *bp3 = '\0';
641 gotquote++;
642 }
643 else
644 bp3++;
645 }
646 }
647 else if (*bp3 == ' ')
648 gotspace++;
649 }
650
651 /*
652 * If we got two spaces, send the second one to null.
653 * if we've reached the end of the line, increase gotspace -
654 * this is perfectly valid for the list entry listed.
655 */
656 if (gotspace == 2 || gotquote == 2)
657 {
658 bp3--; /* Undo the extra increment */
659 *bp3 = '\0';
660 }
661 else if (*bp3 == '\0')
662 gotspace++;
663
664 if ((gotquote && gotquote != 2) || (gotspace != 2 && gotquote != 2))
665 {
666 /*
667 * Unfortunately, we've clobbered lots of values, so printing
668 * out what we have probably isn't useful. Break out, because
669 * trying to recover is probably won't get anything useful
670 * anyways, and we'd be confused about end of line pointers
671 * anyways.
672 */
673 new_draw_info_format (NDI_UNIQUE, 0, op, "Malformed create line: %s", bp2);
674 break;
675 }
676
677 /* bp2 should still point to the start of this line,
678 * with bp3 pointing to the end
679 */
680 if (set_variable (tmp, bp2) == -1)
681 new_draw_info_format (NDI_UNIQUE, 0, op, "Unknown variable %s", bp2);
682 else
683 new_draw_info_format (NDI_UNIQUE, 0, op, "(%s#%d)->%s", &tmp->name, tmp->count, bp2);
684
685 bp2 = bp3 + 1;
686 }
687
688 if (at->nrof)
689 {
690 if (at_spell)
691 tmp->insert (at_spell->instance ());
692
693 tmp->x = op->x;
694 tmp->y = op->y;
695 tmp->map = op->map;
696
697 if (set_nrof)
698 tmp->nrof = nrof;
699
700 op->insert (tmp);
701
702 /* Let's put this created item on stack so dm can access it easily. */
703 dm_stack_push (op->contr, tmp->count);
704
705 return 1;
706 }
707 else
708 {
709 for (i = 0; i < (set_nrof ? nrof : 1); i++)
710 {
711 object *prev = 0, *head = 0;
712
713 for (archetype *atmp = at; atmp; atmp = (archetype *)atmp->more)
714 {
715 object *dup = atmp->instance ();
716
717 if (at_spell)
718 insert_ob_in_ob (at_spell->instance (), dup);
719
720 /*
721 * The head is what contains all the important bits,
722 * so just copying it over should be fine.
723 */
724 if (!head)
725 {
726 head = dup;
727 tmp->copy_to (dup);
728 }
729
730 dup->x = op->x + dup->arch->x;
731 dup->y = op->y + dup->arch->y;
732 dup->map = op->map;
733
734 if (head != dup)
735 {
736 dup->head = head;
737 prev->more = dup;
738 }
739
740 prev = dup;
741 }
742
743 if (head->flag [FLAG_ALIVE])
744 {
745 object *check = head;
746 int size_x = 0;
747 int size_y = 0;
748
749 while (check)
750 {
751 size_x = max (size_x, check->arch->x);
752 size_y = max (size_y, check->arch->y);
753 check = check->more;
754 }
755
756 if (out_of_map (op->map, head->x + size_x, head->y + size_y))
757 {
758 if (head->x < size_x || head->y < size_y)
759 {
760 dm_stack_pop (op->contr);
761 head->destroy ();
762 new_draw_info (NDI_UNIQUE, 0, op, "Object too big to insert in map, or wrong position.");
763 tmp->destroy ();
764 return 1;
765 }
766
767 check = head;
768
769 while (check)
770 {
771 check->x -= size_x;
772 check->y -= size_y;
773 check = check->more;
774 }
775 }
776
777 insert_ob_in_map (head, op->map, op, 0);
778 }
779 else
780 head = insert_ob_in_ob (head, op);
781
782 /* Let's put this created item on stack so dm can access it easily. */
783 /* Wonder if we really want to push all of these, but since
784 * things like rods have nrof 0, we want to cover those.
785 */
786 dm_stack_push (op->contr, head->count);
787
788 if (at->randomitems && !at_spell)
789 create_treasure (at->randomitems, head, GT_APPLY, op->map->difficulty, 0);
790 }
791
792 /* free the one we used to copy */
793 tmp->destroy ();
794 }
795
796 return 1;
797 }
798
799 /*
800 * Now follows dm-commands which are also acceptable from sockets
801 */
802
803 int
804 command_inventory (object *op, char *params)
805 {
806 int i;
807 object *tmp;
808
809 if (!params || !sscanf (params, "%d", &i) || !(tmp = find_object (i)))
810 {
811 op->contr->failmsg ("Inventory of what object (nr)?");
812 return 1;
813 }
814
815 op->contr->infobox (MSG_CHANNEL ("examine"), tmp->query_inventory (op));
816
817 return 1;
818 }
819
820 /* just show player's their skills for now. Dm's can
821 * already see skills w/ inventory command - b.t.
822 */
823
824 int
825 command_skills (object *op, char *params)
826 {
827 show_skills (op, params);
828 return 0;
829 }
830
831 int
832 command_dump (object *op, char *params)
833 {
834 object *tmp;
835
836 tmp = get_dm_object (op->contr, &params, NULL);
837 if (!tmp)
838 return 1;
839
840 char *dump = dump_object (tmp);
841 new_draw_info (NDI_UNIQUE, 0, op, dump);
842 free (dump);
843
844 if (tmp->flag [FLAG_OBJ_ORIGINAL])
845 new_draw_info (NDI_UNIQUE, 0, op, "Object is marked original");
846
847 return 1;
848 }
849
850 int
851 command_patch (object *op, char *params)
852 {
853 char *arg, *arg2;
854 object *tmp;
855
856 tmp = get_dm_object (op->contr, &params, NULL);
857 if (!tmp)
858 /* Player already informed of failure */
859 return 1;
860
861 /* params set to first value by get_dm_default */
862 arg = params;
863 if (arg == NULL)
864 {
865 new_draw_info (NDI_UNIQUE, 0, op, "Patch what values?");
866 return 1;
867 }
868
869 if ((arg2 = strchr (arg, ' ')))
870 arg2++;
871
872 if (set_variable (tmp, arg) == -1)
873 new_draw_info_format (NDI_UNIQUE, 0, op, "Unknown variable %s", arg);
874 else
875 new_draw_info_format (NDI_UNIQUE, 0, op, "(%s#%d)->%s=%s", &tmp->name, tmp->count, arg, arg2);
876
877 return 1;
878 }
879
880 int
881 command_remove (object *op, char *params)
882 {
883 object *tmp;
884 int from;
885
886 tmp = get_dm_object (op->contr, &params, &from);
887 if (!tmp)
888 {
889 new_draw_info (NDI_UNIQUE, 0, op, "Remove what object (nr)?");
890 return 1;
891 }
892
893 if (tmp->type == PLAYER)
894 {
895 new_draw_info (NDI_UNIQUE, 0, op, "Unable to remove a player!");
896 return 1;
897 }
898
899 if (tmp->flag [FLAG_REMOVED])
900 {
901 new_draw_info_format (NDI_UNIQUE, 0, op, "%s is already removed!", query_name (tmp));
902 return 1;
903 }
904
905 if (from != STACK_FROM_STACK)
906 /* Item is either stack top, or is a number thus is now stack top, let's remove it */
907 dm_stack_pop (op->contr);
908
909 /* Always work on the head - otherwise object will get in odd state */
910 if (tmp->head)
911 tmp = tmp->head;
912 tmp->remove ();
913 return 1;
914 }
915
916 int
917 command_free (object *op, char *params)
918 {
919 object *tmp;
920 int from;
921
922 tmp = get_dm_object (op->contr, &params, &from);
923
924 if (!tmp)
925 {
926 new_draw_info (NDI_UNIQUE, 0, op, "Free what object (nr)?");
927 return 1;
928 }
929
930 if (from != STACK_FROM_STACK)
931 /* Item is either stack top, or is a number thus is now stack top, let's remove it */
932 dm_stack_pop (op->contr);
933
934 if (tmp->head)
935 tmp = tmp->head;
936
937 tmp->destroy ();
938 return 1;
939 }
940
941 /**
942 * This adds exp to a player. We now allow adding to a specific skill.
943 */
944 int
945 command_addexp (object *op, char *params)
946 {
947 char buf[1024], skill[1024];
948 int q;
949 sint64 i;
950 object *skillob = NULL;
951
952 skill[0] = '\0';
953 if ((params == NULL) || (strlen (params) > MAX_BUF) || ((q = sscanf (params, "%1023s %" SCNd64 " %1023[^\n]", buf, &i, skill)) < 2))
954 {
955 new_draw_info (NDI_UNIQUE, 0, op, "Usage: addexp <who> <how much> [<skill>].");
956 return 1;
957 }
958
959 for_all_players (pl)
960 if (!strncmp (pl->ob->name, buf, MAX_NAME))
961 {
962 if (q >= 3)
963 {
964 skillob = find_skill_by_name (pl->ob, skill);
965 if (!skillob)
966 {
967 new_draw_info_format (NDI_UNIQUE, 0, op, "Unable to find skill %s in %s", skill, buf);
968 return 1;
969 }
970
971 i = check_exp_adjust (skillob, i);
972 skillob->stats.exp += i;
973 calc_perm_exp (skillob);
974 player_lvl_adj (pl->ob, skillob);
975 }
976
977 pl->ob->stats.exp += i;
978 calc_perm_exp (pl->ob);
979 player_lvl_adj (pl->ob, NULL);
980
981 return 1;
982 }
983
984 new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
985 return 1;
986 }
987
988 /**************************************************************************/
989
990 /* Mods made by Tyler Van Gorder, May 10-13, 1992. */
991
992 /* CSUChico : tvangod@cscihp.ecst.csuchico.edu */
993
994 /**************************************************************************/
995
996 int
997 command_stats (object *op, char *params)
998 {
999 char buf[MAX_BUF];
1000
1001 if (params == NULL || params[0] == '\0')
1002 {
1003 new_draw_info (NDI_UNIQUE, 0, op, "Who?");
1004 return 1;
1005 }
1006
1007 for_all_players (pl)
1008 if (!strcmp (&pl->ob->name, params))
1009 {
1010 sprintf (buf, "Str : %-2d H.P. : %-4d MAX : %d", pl->ob->stats.Str, pl->ob->stats.hp, pl->ob->stats.maxhp);
1011 new_draw_info (NDI_UNIQUE, 0, op, buf);
1012 sprintf (buf, "Dex : %-2d S.P. : %-4d MAX : %d", pl->ob->stats.Dex, pl->ob->stats.sp, pl->ob->stats.maxsp);
1013 new_draw_info (NDI_UNIQUE, 0, op, buf);
1014 sprintf (buf, "Con : %-2d AC : %-4d WC : %d", pl->ob->stats.Con, pl->ob->stats.ac, pl->ob->stats.wc);
1015 new_draw_info (NDI_UNIQUE, 0, op, buf);
1016 sprintf (buf, "Int : %-2d Damage : %d", pl->ob->stats.Int, pl->ob->stats.dam);
1017 new_draw_info (NDI_UNIQUE, 0, op, buf);
1018 sprintf (buf, "Wis : %-2d EXP : %" PRId64, pl->ob->stats.Wis, pl->ob->stats.exp);
1019 new_draw_info (NDI_UNIQUE, 0, op, buf);
1020 sprintf (buf, "Pow : %-2d Grace : %d", pl->ob->stats.Pow, pl->ob->stats.grace);
1021 new_draw_info (NDI_UNIQUE, 0, op, buf);
1022 sprintf (buf, "Cha : %-2d Food : %d", pl->ob->stats.Cha, pl->ob->stats.food);
1023 new_draw_info (NDI_UNIQUE, 0, op, buf);
1024 return 1;
1025 }
1026
1027 new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
1028 return 1;
1029 }
1030
1031 int
1032 command_abil (object *op, char *params)
1033 {
1034 char thing[80], thing2[80];
1035 int iii;
1036 char buf[MAX_BUF];
1037
1038 iii = 0;
1039 thing[0] = '\0';
1040 thing2[0] = '\0';
1041 if (!params || 3 != sscanf (params, "%79s %79s %d", thing, thing2, &iii))
1042 {
1043 new_draw_info (NDI_UNIQUE, 0, op, "Who?");
1044 return 1;
1045 }
1046
1047 if (!*thing2)
1048 {
1049 new_draw_info (NDI_UNIQUE, 0, op, "You can't change that.");
1050 return 1;
1051 }
1052
1053 if (iii < MIN_STAT || iii > MAX_STAT)
1054 {
1055 new_draw_info (NDI_UNIQUE, 0, op, "Illegal range of stat.\n");
1056 return 1;
1057 }
1058
1059 for_all_players (pl)
1060 {
1061 if (!strcmp (&pl->ob->name, thing))
1062 {
1063 if (!strcmp ("str", thing2)) pl->ob->stats.Str = iii, pl->orig_stats.Str = iii;
1064 if (!strcmp ("dex", thing2)) pl->ob->stats.Dex = iii, pl->orig_stats.Dex = iii;
1065 if (!strcmp ("con", thing2)) pl->ob->stats.Con = iii, pl->orig_stats.Con = iii;
1066 if (!strcmp ("wis", thing2)) pl->ob->stats.Wis = iii, pl->orig_stats.Wis = iii;
1067 if (!strcmp ("cha", thing2)) pl->ob->stats.Cha = iii, pl->orig_stats.Cha = iii;
1068 if (!strcmp ("int", thing2)) pl->ob->stats.Int = iii, pl->orig_stats.Int = iii;
1069 if (!strcmp ("pow", thing2)) pl->ob->stats.Pow = iii, pl->orig_stats.Pow = iii;
1070
1071 sprintf (buf, "%s has been altered.", &pl->ob->name);
1072 new_draw_info (NDI_UNIQUE, 0, op, buf);
1073 pl->ob->update_stats ();
1074 return 1;
1075 }
1076 }
1077
1078 new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
1079 return 1;
1080 }
1081
1082 int
1083 command_invisible (object *op, char *params)
1084 {
1085 if (op)
1086 {
1087 op->invisible += TIME2TICK (60);
1088 update_object (op, UP_OBJ_CHANGE);
1089 new_draw_info (NDI_UNIQUE, 0, op, "You turn invisible.");
1090 }
1091
1092 return 0;
1093 }
1094
1095 /**
1096 * Returns spell object (from archetypes) by name.
1097 * Returns NULL if 0 or more than one spell matches.
1098 * Used for wizard's learn spell/prayer.
1099 *
1100 * op is the player issuing the command.
1101 */
1102 static object *
1103 get_spell_by_name (object *op, shstr_cmp spell_name)
1104 {
1105 /* First check for full name matches. */
1106 int conflict_found = 0;
1107 archetype *found = 0;
1108
1109 for_all_archetypes (at)
1110 {
1111 if (at->type != SPELL)
1112 continue;
1113
1114 if (at->object::name != spell_name)
1115 continue;
1116
1117 if (found)
1118 {
1119 if (!conflict_found)
1120 {
1121 conflict_found = 1;
1122 new_draw_info_format (NDI_UNIQUE, 0, op, "More than one archetype matches the spell name %s:", &spell_name);
1123 new_draw_info_format (NDI_UNIQUE, 0, op, "- %s", &found->archname);
1124 }
1125
1126 new_draw_info_format (NDI_UNIQUE, 0, op, "- %s", &at->archname);
1127 return 0;
1128 }
1129
1130 found = at;
1131 }
1132
1133 /* Return if exactly one archetype matches. */
1134 if (found)
1135 return found->instance ();
1136
1137 /* No spell found: just print an error message. */
1138 new_draw_info_format (NDI_UNIQUE, 0, op, "The spell does not exist.");
1139
1140 return 0;
1141 }
1142
1143 static int
1144 command_learn_spell_or_prayer (object *op, char *params, int special_prayer)
1145 {
1146 object *tmp;
1147
1148 if (op->contr == NULL || params == NULL)
1149 {
1150 new_draw_info (NDI_UNIQUE, 0, op, "Which spell do you want to learn?");
1151 return 0;
1152 }
1153
1154 tmp = get_spell_by_name (op, params);
1155 if (tmp == NULL)
1156 {
1157 return 0;
1158 }
1159
1160 if (check_spell_known (op, tmp->name))
1161 {
1162 new_draw_info_format (NDI_UNIQUE, 0, op, "You already know the spell %s.", &tmp->name);
1163 return 0;
1164 }
1165
1166 do_learn_spell (op, tmp, special_prayer);
1167 tmp->destroy ();
1168 return 1;
1169 }
1170
1171 int
1172 command_learn_spell (object *op, char *params)
1173 {
1174 return command_learn_spell_or_prayer (op, params, 0);
1175 }
1176
1177 int
1178 command_learn_special_prayer (object *op, char *params)
1179 {
1180 return command_learn_spell_or_prayer (op, params, 1);
1181 }
1182
1183 int
1184 command_forget_spell (object *op, char *params)
1185 {
1186 object *spell;
1187
1188 if (op->contr == NULL || params == NULL)
1189 {
1190 new_draw_info (NDI_UNIQUE, 0, op, "Which spell do you want to forget?");
1191 return 0;
1192 }
1193
1194 spell = op->find_spell (params);
1195 if (spell == NULL)
1196 {
1197 new_draw_info_format (NDI_UNIQUE, 0, op, "You do not know the spell %s.", params);
1198 return 0;
1199 }
1200
1201 do_forget_spell (op, spell->name);
1202 return 1;
1203 }
1204
1205 /**
1206 * Pop the stack top.
1207 */
1208 int
1209 command_stack_pop (object *op, char *params)
1210 {
1211 dm_stack_pop (op->contr);
1212 return 0;
1213 }
1214
1215 /**
1216 * Push specified item on stack.
1217 */
1218 int
1219 command_stack_push (object *op, char *params)
1220 {
1221 object *ob;
1222 int from;
1223
1224 ob = get_dm_object (op->contr, &params, &from);
1225
1226 if (ob && from != STACK_FROM_NUMBER)
1227 /* Object was from stack, need to push it again */
1228 dm_stack_push (op->contr, ob->count);
1229
1230 return 0;
1231 }
1232
1233 /**
1234 * Displays stack contents.
1235 */
1236 int
1237 command_stack_list (object *op, char *params)
1238 {
1239 int item;
1240 object *display;
1241 player *pl = op->contr;
1242
1243 new_draw_info (NDI_UNIQUE, 0, op, "Item stack contents:");
1244
1245 for (item = 0; item < pl->stack_position; item++)
1246 {
1247 display = find_object (pl->stack_items[item]);
1248 if (display)
1249 new_draw_info_format (NDI_UNIQUE, 0, op, " %d : %s [%d]", item, &display->name, display->count);
1250 else
1251 /* Item was freed */
1252 new_draw_info_format (NDI_UNIQUE, 0, op, " %d : (lost item: %d)", item, pl->stack_items[item]);
1253 }
1254
1255 return 0;
1256 }
1257
1258 /**
1259 * Empty DM item stack.
1260 */
1261 int
1262 command_stack_clear (object *op, char *params)
1263 {
1264 op->contr->stack_position = 0;
1265 new_draw_info (NDI_UNIQUE, 0, op, "Item stack cleared.");
1266 return 0;
1267 }
1268
1269 int
1270 command_insert_into (object *op, char *params)
1271 {
1272 object *left, *right;
1273 int left_from, right_from;
1274
1275 left = get_dm_object (op->contr, &params, &left_from);
1276 if (!left)
1277 {
1278 new_draw_info (NDI_UNIQUE, 0, op, "Insert into what object?");
1279 return 0;
1280 }
1281
1282 if (left_from == STACK_FROM_NUMBER)
1283 /* Item was stacked, remove it else right will be the same... */
1284 dm_stack_pop (op->contr);
1285
1286 right = get_dm_object (op->contr, &params, &right_from);
1287
1288 if (!right)
1289 {
1290 new_draw_info (NDI_UNIQUE, 0, op, "Insert what item?");
1291 return 0;
1292 }
1293
1294 if (left_from == STACK_FROM_TOP && right_from == STACK_FROM_TOP)
1295 {
1296 /*
1297 * Special case: both items were taken from stack top.
1298 * Override the behaviour, taking left as item just below top, if exists.
1299 * See function description for why.
1300 * Besides, can't insert an item into itself.
1301 */
1302 if (op->contr->stack_position > 1)
1303 {
1304 left = find_object (op->contr->stack_items[op->contr->stack_position - 2]);
1305 if (left)
1306 new_draw_info (NDI_UNIQUE, 0, op, "(Note: item to insert into taken from undertop)");
1307 else
1308 /* Stupid case: item under top was freed, fallback to stack top */
1309 left = right;
1310 }
1311 }
1312
1313 if (left == right)
1314 {
1315 new_draw_info (NDI_UNIQUE, 0, op, "Can't insert an object into itself!");
1316 return 0;
1317 }
1318
1319 if (right->type == PLAYER)
1320 {
1321 new_draw_info (NDI_UNIQUE, 0, op, "Can't insert a player into something!");
1322 return 0;
1323 }
1324
1325 if (!right->flag [FLAG_REMOVED])
1326 right->remove ();
1327
1328 object *inserted = insert_ob_in_ob (right, left);
1329
1330 new_draw_info_format (NDI_UNIQUE, 0, op, "Inserted %s in %s", query_name (inserted), query_name (left));
1331
1332 return 0;
1333
1334 }