ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_wiz.C
Revision: 1.72
Committed: Mon Oct 12 14:00:59 2009 UTC (14 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_82, rel-2_81
Changes since 1.71: +7 -6 lines
Log Message:
clarify license

File Contents

# Content
1 /*
2 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 *
4 * Copyright (©) 2005,2006,2007,2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
6 * Copyright (©) 1992,2007 Frank Tore Johansen
7 *
8 * Deliantra is free software: you can redistribute it and/or modify it under
9 * the terms of the Affero GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * 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 Affero GNU General Public License
19 * and the GNU General Public License along with this program. If not, see
20 * <http://www.gnu.org/licenses/>.
21 *
22 * The authors can be reached via e-mail to <support@deliantra.net>
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 *plop = pl->ob;
185
186 if (!QUERY_FLAG (plop, FLAG_REMOVED) && !QUERY_FLAG (plop, FLAG_FREED))
187 {
188 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_RED, 5, op, "%s is kicked out of the game.", &plop->name);
189 plop->contr->killer = op;
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->player_goto (dummy->slaying, dummy->stats.hp, dummy->stats.sp);//TODO
286 dummy->destroy ();
287
288 new_draw_info (NDI_UNIQUE, 0, pl->ob, "You have been arrested.");
289 new_draw_info (NDI_UNIQUE, 0, op, "OK.");
290 LOG (llevInfo, "Player %s arrested by %s\n", &pl->ob->name, &op->name);
291 return 1;
292 }
293
294 int
295 command_summon (object *op, char *params)
296 {
297 int i;
298 object *dummy;
299 player *pl;
300
301 if (!op)
302 return 0;
303
304 if (params == NULL)
305 {
306 new_draw_info (NDI_UNIQUE, 0, op, "Usage: summon <player>.");
307 return 1;
308 }
309
310 pl = get_other_player_from_name (op, params);
311 if (!pl)
312 return 1;
313
314 i = find_free_spot (op, op->map, op->x, op->y, 1, 9);
315 if (i == -1)
316 {
317 new_draw_info (NDI_UNIQUE, 0, op, "Can not find a free spot to place summoned player.");
318 return 1;
319 }
320
321 pl->ob->player_goto (op->map->path, op->x + freearr_x[i], op->y + freearr_y[i]);
322 new_draw_info (NDI_UNIQUE, 0, pl->ob, "You are summoned.");
323 new_draw_info (NDI_UNIQUE, 0, op, "OK.");
324
325 return 1;
326 }
327
328 /**
329 * This function is a real mess, because we're stucking getting
330 * the entire item description in one block of text, so we just
331 * can't simply parse it - we need to look for double quotes
332 * for example. This could actually get much simpler with just a
333 * little help from the client - if we could get line breaks, it
334 * makes parsing much easier, eg, something like:
335 * arch dragon
336 * name big nasty creature
337 * hp 5
338 * sp 30
339 * Is much easier to parse than
340 * dragon name "big nasty creature" hp 5 sp 30
341 * for example.
342 */
343 int
344 command_create (object *op, char *params)
345 {
346 object *tmp = NULL;
347 int nrof, i, magic, set_magic = 0, set_nrof = 0, gotquote, gotspace;
348 char buf[MAX_BUF], *cp, *bp = buf, *bp2, *bp3, *bp4, *endline;
349 archetype *at, *at_spell = NULL;
350 artifact *art = NULL;
351
352 if (!op)
353 return 0;
354
355 if (params == NULL)
356 {
357 new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] <archetype> [ of <artifact>]" " [variable_to_patch setting]");
358 return 1;
359 }
360
361 bp = params;
362
363 /* We need to know where the line ends */
364 endline = bp + strlen (bp);
365
366 if (sscanf (bp, "%d ", &nrof))
367 {
368 if ((bp = strchr (params, ' ')) == NULL)
369 {
370 new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] <archetype> [ of <artifact>]" " [variable_to_patch setting]");
371 return 1;
372 }
373 bp++;
374 set_nrof = 1;
375 LOG (llevDebug, "%s creates: (%d) %s\n", &op->name, nrof, bp);
376 }
377
378 if (sscanf (bp, "%d ", &magic))
379 {
380 if ((bp = strchr (bp, ' ')) == NULL)
381 {
382 new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] <archetype> [ of <artifact>]" " [variable_to_patch setting]");
383 return 1;
384 }
385
386 bp++;
387 set_magic = 1;
388 LOG (llevDebug, "%s creates: (%d) (%d) %s\n", &op->name, nrof, magic, bp);
389 }
390
391 if ((cp = strstr (bp, " of ")) != NULL)
392 {
393 *cp = '\0';
394 cp += 4;
395 }
396
397 for (bp2 = bp; *bp2; bp2++)
398 {
399 if (*bp2 == ' ')
400 {
401 *bp2 = '\0';
402 bp2++;
403 break;
404 }
405 }
406
407 if ((at = archetype::find (bp)) == NULL)
408 {
409 new_draw_info (NDI_UNIQUE, 0, op, "No such archetype.");
410 return 1;
411 }
412
413 if (cp)
414 {
415 char spell_name[MAX_BUF], *fsp = NULL;
416
417 /*
418 * Try to find a spell object for this. Note that
419 * we also set up spell_name which is only
420 * the first word.
421 */
422
423 at_spell = archetype::find (cp);
424 if (!at_spell || at_spell->type != SPELL)
425 at_spell = find_archetype_by_object_name (cp);
426 if (!at_spell || at_spell->type != SPELL)
427 {
428 assign (spell_name, cp);
429 fsp = strchr (spell_name, ' ');
430 if (fsp)
431 {
432 *fsp = 0;
433 fsp++;
434 at_spell = archetype::find (spell_name);
435
436 /* Got a spell, update the first string pointer */
437 if (at_spell && at_spell->type == SPELL)
438 bp2 = cp + strlen (spell_name) + 1;
439 else
440 at_spell = NULL;
441 }
442 }
443
444 /* OK - we didn't find a spell - presume the 'of'
445 * in this case means its an artifact.
446 */
447 if (!at_spell)
448 {
449 if (find_artifactlist (at->type) == NULL)
450 new_draw_info_format (NDI_UNIQUE, 0, op, "No artifact list for type %d\n", at->type);
451 else
452 {
453 art = find_artifactlist (at->type)->items;
454
455 while (art)
456 {
457 if (!strcmp (&art->item->name, cp))
458 break;
459
460 art = art->next;
461 }
462
463 if (!art)
464 new_draw_info_format (NDI_UNIQUE, 0, op, "No such artifact ([%d] of %s)", at->type, cp);
465 }
466
467 LOG (llevDebug, "%s creates: (%d) (%d) (%s) of (%s)\n", &op->name, set_nrof ? nrof : 0, set_magic ? magic : 0, bp, cp);
468 }
469 } /* if cp */
470
471 if ((at->type == ROD || at->type == WAND || at->type == SCROLL ||
472 at->type == HORN || at->type == SPELLBOOK) && !at_spell)
473 {
474 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);
475 return 1;
476 }
477
478 /*
479 * Rather than have two different blocks with a lot of similar code,
480 * just create one object, do all the processing, and then determine
481 * if that one object should be inserted or if we need to make copies.
482 */
483 tmp = arch_to_object (at);
484
485 if (set_magic)
486 set_abs_magic (tmp, magic);
487
488 if (art)
489 give_artifact_abilities (tmp, art->item);
490
491 if (need_identify (tmp))
492 {
493 SET_FLAG (tmp, FLAG_IDENTIFIED);
494 CLEAR_FLAG (tmp, FLAG_KNOWN_MAGICAL);
495 }
496
497 /*
498 * This entire block here tries to find variable pairings,
499 * eg, 'hp 4' or the like. The mess here is that values
500 * can be quoted (eg "my cool sword"); So the basic logic
501 * is we want to find two spaces, but if we got a quote,
502 * any spaces there don't count.
503 */
504 while (*bp2 && bp2 <= endline)
505 {
506 bp4 = NULL;
507 gotspace = 0;
508 gotquote = 0;
509
510 /* find the first quote */
511 for (bp3 = bp2; *bp3 && gotspace < 2 && gotquote < 2; bp3++)
512 {
513
514 /* Found a quote - now lets find the second one */
515 if (*bp3 == '"')
516 {
517 *bp3 = ' ';
518 bp2 = bp3 + 1; /* Update start of string */
519 bp3++;
520 gotquote++;
521 while (*bp3)
522 {
523 if (*bp3 == '"')
524 {
525 *bp3 = '\0';
526 gotquote++;
527 }
528 else
529 bp3++;
530 }
531 }
532 else if (*bp3 == ' ')
533 gotspace++;
534 }
535
536 /*
537 * If we got two spaces, send the second one to null.
538 * if we've reached the end of the line, increase gotspace -
539 * this is perfectly valid for the list entry listed.
540 */
541 if (gotspace == 2 || gotquote == 2)
542 {
543 bp3--; /* Undo the extra increment */
544 *bp3 = '\0';
545 }
546 else if (*bp3 == '\0')
547 gotspace++;
548
549 if ((gotquote && gotquote != 2) || (gotspace != 2 && gotquote != 2))
550 {
551 /*
552 * Unfortunately, we've clobbered lots of values, so printing
553 * out what we have probably isn't useful. Break out, because
554 * trying to recover is probably won't get anything useful
555 * anyways, and we'd be confused about end of line pointers
556 * anyways.
557 */
558 new_draw_info_format (NDI_UNIQUE, 0, op, "Malformed create line: %s", bp2);
559 break;
560 }
561
562 /* bp2 should still point to the start of this line,
563 * with bp3 pointing to the end
564 */
565 if (set_variable (tmp, bp2) == -1)
566 new_draw_info_format (NDI_UNIQUE, 0, op, "Unknown variable %s", bp2);
567 else
568 new_draw_info_format (NDI_UNIQUE, 0, op, "(%s#%d)->%s", &tmp->name, tmp->count, bp2);
569
570 bp2 = bp3 + 1;
571 }
572
573 if (at->nrof)
574 {
575 if (at_spell)
576 tmp->insert (arch_to_object (at_spell));
577
578 tmp->x = op->x;
579 tmp->y = op->y;
580 tmp->map = op->map;
581
582 if (set_nrof)
583 tmp->nrof = nrof;
584
585 op->insert (tmp);
586
587 /* Let's put this created item on stack so dm can access it easily. */
588 dm_stack_push (op->contr, tmp->count);
589
590 return 1;
591 }
592 else
593 {
594 for (i = 0; i < (set_nrof ? nrof : 1); i++)
595 {
596 object *prev = 0, *head = 0;
597
598 for (archetype *atmp = at; atmp; atmp = (archetype *)atmp->more)
599 {
600 object *dup = arch_to_object (atmp);
601
602 if (at_spell)
603 insert_ob_in_ob (arch_to_object (at_spell), dup);
604
605 /*
606 * The head is what contains all the important bits,
607 * so just copying it over should be fine.
608 */
609 if (!head)
610 {
611 head = dup;
612 tmp->copy_to (dup);
613 }
614
615 dup->x = op->x + dup->arch->x;
616 dup->y = op->y + dup->arch->y;
617 dup->map = op->map;
618
619 if (head != dup)
620 {
621 dup->head = head;
622 prev->more = dup;
623 }
624
625 prev = dup;
626 }
627
628 if (QUERY_FLAG (head, FLAG_ALIVE))
629 {
630 object *check = head;
631 int size_x = 0;
632 int size_y = 0;
633
634 while (check)
635 {
636 size_x = MAX (size_x, check->arch->x);
637 size_y = MAX (size_y, check->arch->y);
638 check = check->more;
639 }
640
641 if (out_of_map (op->map, head->x + size_x, head->y + size_y))
642 {
643 if (head->x < size_x || head->y < size_y)
644 {
645 dm_stack_pop (op->contr);
646 head->destroy ();
647 new_draw_info (NDI_UNIQUE, 0, op, "Object too big to insert in map, or wrong position.");
648 tmp->destroy ();
649 return 1;
650 }
651
652 check = head;
653
654 while (check)
655 {
656 check->x -= size_x;
657 check->y -= size_y;
658 check = check->more;
659 }
660 }
661
662 insert_ob_in_map (head, op->map, op, 0);
663 }
664 else
665 head = insert_ob_in_ob (head, op);
666
667 /* Let's put this created item on stack so dm can access it easily. */
668 /* Wonder if we really want to push all of these, but since
669 * things like rods have nrof 0, we want to cover those.
670 */
671 dm_stack_push (op->contr, head->count);
672
673 if (at->randomitems && !at_spell)
674 create_treasure (at->randomitems, head, GT_APPLY, op->map->difficulty, 0);
675 }
676
677 /* free the one we used to copy */
678 tmp->destroy ();
679 }
680
681 return 1;
682 }
683
684 /*
685 * Now follows dm-commands which are also acceptable from sockets
686 */
687
688 int
689 command_inventory (object *op, char *params)
690 {
691 int i;
692 object *tmp;
693
694 if (!params || !sscanf (params, "%d", &i) || !(tmp = find_object (i)))
695 {
696 op->contr->failmsg ("Inventory of what object (nr)?");
697 return 1;
698 }
699
700 op->contr->infobox (MSG_CHANNEL ("examine"), tmp->query_inventory (op));
701
702 return 1;
703 }
704
705 /* just show player's their skills for now. Dm's can
706 * already see skills w/ inventory command - b.t.
707 */
708
709 int
710 command_skills (object *op, char *params)
711 {
712 show_skills (op, params);
713 return 0;
714 }
715
716 int
717 command_dump (object *op, char *params)
718 {
719 object *tmp;
720
721 tmp = get_dm_object (op->contr, &params, NULL);
722 if (!tmp)
723 return 1;
724
725 char *dump = dump_object (tmp);
726 new_draw_info (NDI_UNIQUE, 0, op, dump);
727 free (dump);
728
729 if (QUERY_FLAG (tmp, FLAG_OBJ_ORIGINAL))
730 new_draw_info (NDI_UNIQUE, 0, op, "Object is marked original");
731
732 return 1;
733 }
734
735 int
736 command_patch (object *op, char *params)
737 {
738 char *arg, *arg2;
739 object *tmp;
740
741 tmp = get_dm_object (op->contr, &params, NULL);
742 if (!tmp)
743 /* Player already informed of failure */
744 return 1;
745
746 /* params set to first value by get_dm_default */
747 arg = params;
748 if (arg == NULL)
749 {
750 new_draw_info (NDI_UNIQUE, 0, op, "Patch what values?");
751 return 1;
752 }
753
754 if ((arg2 = strchr (arg, ' ')))
755 arg2++;
756
757 if (set_variable (tmp, arg) == -1)
758 new_draw_info_format (NDI_UNIQUE, 0, op, "Unknown variable %s", arg);
759 else
760 new_draw_info_format (NDI_UNIQUE, 0, op, "(%s#%d)->%s=%s", &tmp->name, tmp->count, arg, arg2);
761
762 return 1;
763 }
764
765 int
766 command_remove (object *op, char *params)
767 {
768 object *tmp;
769 int from;
770
771 tmp = get_dm_object (op->contr, &params, &from);
772 if (!tmp)
773 {
774 new_draw_info (NDI_UNIQUE, 0, op, "Remove what object (nr)?");
775 return 1;
776 }
777
778 if (tmp->type == PLAYER)
779 {
780 new_draw_info (NDI_UNIQUE, 0, op, "Unable to remove a player!");
781 return 1;
782 }
783
784 if (QUERY_FLAG (tmp, FLAG_REMOVED))
785 {
786 new_draw_info_format (NDI_UNIQUE, 0, op, "%s is already removed!", query_name (tmp));
787 return 1;
788 }
789
790 if (from != STACK_FROM_STACK)
791 /* Item is either stack top, or is a number thus is now stack top, let's remove it */
792 dm_stack_pop (op->contr);
793
794 /* Always work on the head - otherwise object will get in odd state */
795 if (tmp->head)
796 tmp = tmp->head;
797 tmp->remove ();
798 return 1;
799 }
800
801 int
802 command_free (object *op, char *params)
803 {
804 object *tmp;
805 int from;
806
807 tmp = get_dm_object (op->contr, &params, &from);
808
809 if (!tmp)
810 {
811 new_draw_info (NDI_UNIQUE, 0, op, "Free what object (nr)?");
812 return 1;
813 }
814
815 if (from != STACK_FROM_STACK)
816 /* Item is either stack top, or is a number thus is now stack top, let's remove it */
817 dm_stack_pop (op->contr);
818
819 if (tmp->head)
820 tmp = tmp->head;
821
822 tmp->destroy ();
823 return 1;
824 }
825
826 /**
827 * This adds exp to a player. We now allow adding to a specific skill.
828 */
829 int
830 command_addexp (object *op, char *params)
831 {
832 char buf[MAX_BUF], skill[MAX_BUF];
833 int q;
834 long long i; // use sint64 and finally provide format specifiers for sint64 etc. via configure
835 object *skillob = NULL;
836
837 skill[0] = '\0';
838 if ((params == NULL) || (strlen (params) > MAX_BUF) || ((q = sscanf (params, "%s %lld %s", buf, &i, skill)) < 2))
839 {
840 new_draw_info (NDI_UNIQUE, 0, op, "Usage: addexp <who> <how much> [<skill>].");
841 return 1;
842 }
843
844 for_all_players (pl)
845 if (!strncmp (pl->ob->name, buf, MAX_NAME))
846 {
847 if (q >= 3)
848 {
849 skillob = find_skill_by_name (pl->ob, skill);
850 if (!skillob)
851 {
852 new_draw_info_format (NDI_UNIQUE, 0, op, "Unable to find skill %s in %s", skill, buf);
853 return 1;
854 }
855
856 i = check_exp_adjust (skillob, i);
857 skillob->stats.exp += i;
858 calc_perm_exp (skillob);
859 player_lvl_adj (pl->ob, skillob);
860 }
861
862 pl->ob->stats.exp += i;
863 calc_perm_exp (pl->ob);
864 player_lvl_adj (pl->ob, NULL);
865
866 return 1;
867 }
868
869 new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
870 return 1;
871 }
872
873 /**************************************************************************/
874
875 /* Mods made by Tyler Van Gorder, May 10-13, 1992. */
876
877 /* CSUChico : tvangod@cscihp.ecst.csuchico.edu */
878
879 /**************************************************************************/
880
881 int
882 command_stats (object *op, char *params)
883 {
884 char thing[20];
885 char buf[MAX_BUF];
886
887 thing[0] = '\0';
888 if (params == NULL || !sscanf (params, "%s", thing) || thing == NULL)
889 {
890 new_draw_info (NDI_UNIQUE, 0, op, "Who?");
891 return 1;
892 }
893
894 for_all_players (pl)
895 if (!strcmp (&pl->ob->name, thing))
896 {
897 sprintf (buf, "Str : %-2d H.P. : %-4d MAX : %d", pl->ob->stats.Str, pl->ob->stats.hp, pl->ob->stats.maxhp);
898 new_draw_info (NDI_UNIQUE, 0, op, buf);
899 sprintf (buf, "Dex : %-2d S.P. : %-4d MAX : %d", pl->ob->stats.Dex, pl->ob->stats.sp, pl->ob->stats.maxsp);
900 new_draw_info (NDI_UNIQUE, 0, op, buf);
901 sprintf (buf, "Con : %-2d AC : %-4d WC : %d", pl->ob->stats.Con, pl->ob->stats.ac, pl->ob->stats.wc);
902 new_draw_info (NDI_UNIQUE, 0, op, buf);
903 sprintf (buf, "Int : %-2d Damage : %d", pl->ob->stats.Int, pl->ob->stats.dam);
904 new_draw_info (NDI_UNIQUE, 0, op, buf);
905 sprintf (buf, "Wis : %-2d EXP : %" PRId64, pl->ob->stats.Wis, pl->ob->stats.exp);
906 new_draw_info (NDI_UNIQUE, 0, op, buf);
907 sprintf (buf, "Pow : %-2d Grace : %d", pl->ob->stats.Pow, pl->ob->stats.grace);
908 new_draw_info (NDI_UNIQUE, 0, op, buf);
909 sprintf (buf, "Cha : %-2d Food : %d", pl->ob->stats.Cha, pl->ob->stats.food);
910 new_draw_info (NDI_UNIQUE, 0, op, buf);
911 return 1;
912 }
913
914 new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
915 return 1;
916 }
917
918 int
919 command_abil (object *op, char *params)
920 {
921 char thing[20], thing2[20];
922 int iii;
923 char buf[MAX_BUF];
924
925 iii = 0;
926 thing[0] = '\0';
927 thing2[0] = '\0';
928 if (params == NULL || !sscanf (params, "%s %s %d", thing, thing2, &iii) || thing == NULL)
929 {
930 new_draw_info (NDI_UNIQUE, 0, op, "Who?");
931 return 1;
932 }
933
934 if (thing2 == NULL)
935 {
936 new_draw_info (NDI_UNIQUE, 0, op, "You can't change that.");
937 return 1;
938 }
939
940 if (iii < MIN_STAT || iii > MAX_STAT)
941 {
942 new_draw_info (NDI_UNIQUE, 0, op, "Illegal range of stat.\n");
943 return 1;
944 }
945
946 for_all_players (pl)
947 {
948 if (!strcmp (&pl->ob->name, thing))
949 {
950 if (!strcmp ("str", thing2)) pl->ob->stats.Str = iii, pl->orig_stats.Str = iii;
951 if (!strcmp ("dex", thing2)) pl->ob->stats.Dex = iii, pl->orig_stats.Dex = iii;
952 if (!strcmp ("con", thing2)) pl->ob->stats.Con = iii, pl->orig_stats.Con = iii;
953 if (!strcmp ("wis", thing2)) pl->ob->stats.Wis = iii, pl->orig_stats.Wis = iii;
954 if (!strcmp ("cha", thing2)) pl->ob->stats.Cha = iii, pl->orig_stats.Cha = iii;
955 if (!strcmp ("int", thing2)) pl->ob->stats.Int = iii, pl->orig_stats.Int = iii;
956 if (!strcmp ("pow", thing2)) pl->ob->stats.Pow = iii, pl->orig_stats.Pow = iii;
957
958 sprintf (buf, "%s has been altered.", &pl->ob->name);
959 new_draw_info (NDI_UNIQUE, 0, op, buf);
960 pl->ob->update_stats ();
961 return 1;
962 }
963 }
964
965 new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
966 return 1;
967 }
968
969 int
970 command_nowiz (object *op, char *params)
971 { /* 'nodm' is alias */
972 CLEAR_FLAG (op, FLAG_WIZ);
973 CLEAR_FLAG (op, FLAG_WIZPASS);
974 CLEAR_FLAG (op, FLAG_WIZCAST);
975 CLEAR_FLAG (op, FLAG_WIZLOOK);
976 op->contr->do_los = 1;
977
978 if (op->contr->hidden)
979 {
980 new_draw_info (NDI_UNIQUE, 0, op, "You are no longer hidden from other players");
981 op->map->players++;
982 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, "%s has entered the game.", &op->name);
983 op->contr->hidden = 0;
984 op->invisible = 1;
985 }
986 else
987 new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_LT_GREEN, 1, NULL, "The Dungeon Master is gone..");
988
989 return 1;
990 }
991
992 /**
993 * object *op is trying to become dm.
994 * pl_name is name supplied by player. Restrictive DM will make it harder
995 * for socket users to become DM - in that case, it will check for the players
996 * character name.
997 */
998 static int
999 checkdm (object *op, const char *pl_name, const char *pl_passwd, const char *pl_host)
1000 {
1001 FILE *dmfile;
1002 char buf[MAX_BUF];
1003 char line_buf[160], name[160], passwd[160], host[160];
1004
1005 #ifdef RESTRICTIVE_DM
1006 *pl_name = op->name ? op->name : "*";
1007 #endif
1008
1009 sprintf (buf, "%s/%s", settings.confdir, DMFILE);
1010 if ((dmfile = fopen (buf, "r")) == NULL)
1011 {
1012 LOG (llevDebug, "Could not find DM file.\n");
1013 return 0;
1014 }
1015
1016 while (fgets (line_buf, 160, dmfile) != NULL)
1017 {
1018 if (line_buf[0] == '#')
1019 continue;
1020 if (sscanf (line_buf, "%[^:]:%[^:]:%s\n", name, passwd, host) != 3)
1021 {
1022 LOG (llevError, "Warning - malformed dm file entry: %s\n", line_buf);
1023 }
1024 else if ((!strcmp (name, "*") || (pl_name && !strcmp (pl_name, name)))
1025 && (!strcmp (passwd, "*") || !strcmp (passwd, pl_passwd)) && (!strcmp (host, "*") || !strcmp (host, pl_host)))
1026 {
1027 fclose (dmfile);
1028 return (1);
1029 }
1030 }
1031 fclose (dmfile);
1032 return (0);
1033 }
1034
1035 int
1036 do_wizard_dm (object *op, char *params, int silent)
1037 {
1038 if (!op->contr)
1039 return 0;
1040
1041 if (QUERY_FLAG (op, FLAG_WIZ))
1042 {
1043 new_draw_info (NDI_UNIQUE, 0, op, "You are already the Dungeon Master!");
1044 return 0;
1045 }
1046
1047 if (checkdm (op, op->name, (params ? params : "*"), op->contr->ns->host))
1048 {
1049 SET_FLAG (op, FLAG_WIZ);
1050 SET_FLAG (op, FLAG_WIZPASS);
1051 SET_FLAG (op, FLAG_WIZCAST);
1052 SET_FLAG (op, FLAG_WIZLOOK);
1053 op->contr->do_los = 1;
1054
1055 new_draw_info (NDI_UNIQUE, 0, op, "Ok, you are the Dungeon Master!");
1056 op->contr->write_buf[0] = '\0';
1057
1058 if (!silent)
1059 new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_LT_GREEN, 1, NULL, "The Dungeon Master has arrived!");
1060
1061 return 1;
1062 }
1063 else
1064 {
1065 new_draw_info (NDI_UNIQUE, 0, op, "Sorry Pal, I don't think so.");
1066 op->contr->write_buf[0] = '\0';
1067 return 0;
1068 }
1069 }
1070
1071 /*
1072 * Actual command to perhaps become dm. Changed aroun a bit in version 0.92.2
1073 * - allow people on sockets to become dm, and allow better dm file
1074 */
1075 int
1076 command_dm (object *op, char *params)
1077 {
1078 if (!op->contr)
1079 return 0;
1080
1081 do_wizard_dm (op, params, 0);
1082
1083 return 1;
1084 }
1085
1086 int
1087 command_invisible (object *op, char *params)
1088 {
1089 if (op)
1090 {
1091 op->invisible += 100;
1092 update_object (op, UP_OBJ_CHANGE);
1093 new_draw_info (NDI_UNIQUE, 0, op, "You turn invisible.");
1094 }
1095
1096 return 0;
1097 }
1098
1099 /**
1100 * Returns spell object (from archetypes) by name.
1101 * Returns NULL if 0 or more than one spell matches.
1102 * Used for wizard's learn spell/prayer.
1103 *
1104 * op is the player issuing the command.
1105 */
1106 static object *
1107 get_spell_by_name (object *op, shstr_cmp spell_name)
1108 {
1109 /* First check for full name matches. */
1110 int conflict_found = 0;
1111 archetype *found;
1112 for_all_archetypes (at)
1113 {
1114 if (at->type != SPELL)
1115 continue;
1116
1117 if (at->object::name != spell_name)
1118 continue;
1119
1120 if (found)
1121 {
1122 if (!conflict_found)
1123 {
1124 conflict_found = 1;
1125 new_draw_info_format (NDI_UNIQUE, 0, op, "More than one archetype matches the spell name %s:", &spell_name);
1126 new_draw_info_format (NDI_UNIQUE, 0, op, "- %s", &found->archname);
1127 }
1128
1129 new_draw_info_format (NDI_UNIQUE, 0, op, "- %s", &at->archname);
1130 return 0;
1131 }
1132
1133 found = at;
1134 }
1135
1136 /* Return if exactly one archetype matches. */
1137 if (found)
1138 return arch_to_object (found);
1139
1140 /* No spell found: just print an error message. */
1141 new_draw_info_format (NDI_UNIQUE, 0, op, "The spell does not exist.");
1142
1143 return 0;
1144 }
1145
1146 static int
1147 command_learn_spell_or_prayer (object *op, char *params, int special_prayer)
1148 {
1149 object *tmp;
1150
1151 if (op->contr == NULL || params == NULL)
1152 {
1153 new_draw_info (NDI_UNIQUE, 0, op, "Which spell do you want to learn?");
1154 return 0;
1155 }
1156
1157 tmp = get_spell_by_name (op, params);
1158 if (tmp == NULL)
1159 {
1160 return 0;
1161 }
1162
1163 if (check_spell_known (op, tmp->name))
1164 {
1165 new_draw_info_format (NDI_UNIQUE, 0, op, "You already know the spell %s.", &tmp->name);
1166 return 0;
1167 }
1168
1169 do_learn_spell (op, tmp, special_prayer);
1170 tmp->destroy ();
1171 return 1;
1172 }
1173
1174 int
1175 command_learn_spell (object *op, char *params)
1176 {
1177 return command_learn_spell_or_prayer (op, params, 0);
1178 }
1179
1180 int
1181 command_learn_special_prayer (object *op, char *params)
1182 {
1183 return command_learn_spell_or_prayer (op, params, 1);
1184 }
1185
1186 int
1187 command_forget_spell (object *op, char *params)
1188 {
1189 object *spell;
1190
1191 if (op->contr == NULL || params == NULL)
1192 {
1193 new_draw_info (NDI_UNIQUE, 0, op, "Which spell do you want to forget?");
1194 return 0;
1195 }
1196
1197 spell = lookup_spell_by_name (op, params);
1198 if (spell == NULL)
1199 {
1200 new_draw_info_format (NDI_UNIQUE, 0, op, "You do not know the spell %s.", params);
1201 return 0;
1202 }
1203
1204 do_forget_spell (op, spell->name);
1205 return 1;
1206 }
1207
1208 /**
1209 * A players wants to become DM and hide.
1210 * Let's see if that's authorized.
1211 * Make sure to not tell anything to anyone.
1212 */
1213 int
1214 command_dmhide (object *op, char *params)
1215 {
1216 if (!do_wizard_dm (op, params, 1))
1217 return 0;
1218
1219 do_wizard_hide (op, 1);
1220
1221 return 1;
1222 }
1223
1224 void
1225 dm_stack_pop (player *pl)
1226 {
1227 if (!pl->stack_items || !pl->stack_position)
1228 {
1229 new_draw_info (NDI_UNIQUE, 0, pl->ob, "Empty stack!");
1230 return;
1231 }
1232
1233 pl->stack_position--;
1234 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Popped item from stack, %d left.", pl->stack_position);
1235 }
1236
1237 /**
1238 * Get current stack top item for player.
1239 * Returns NULL if no stacked item.
1240 * If stacked item disappeared (freed), remove it.
1241 *
1242 * Ryo, august 2004
1243 */
1244 object *
1245 dm_stack_peek (player *pl)
1246 {
1247 object *ob;
1248
1249 if (!pl->stack_position)
1250 {
1251 new_draw_info (NDI_UNIQUE, 0, pl->ob, "Empty stack!");
1252 return NULL;
1253 }
1254
1255 ob = find_object (pl->stack_items[pl->stack_position - 1]);
1256 if (!ob)
1257 {
1258 new_draw_info (NDI_UNIQUE, 0, pl->ob, "Stacked item was removed!");
1259 dm_stack_pop (pl);
1260 return NULL;
1261 }
1262
1263 return ob;
1264 }
1265
1266 /**
1267 * Push specified item on player stack.
1268 * Inform player of position.
1269 * Initializes variables if needed.
1270 */
1271 void
1272 dm_stack_push (player *pl, tag_t item)
1273 {
1274 if (!pl->stack_items)
1275 {
1276 pl->stack_items = (tag_t *) malloc (sizeof (tag_t) * STACK_SIZE);
1277 memset (pl->stack_items, 0, sizeof (tag_t) * STACK_SIZE);
1278 }
1279
1280 if (pl->stack_position == STACK_SIZE)
1281 {
1282 new_draw_info (NDI_UNIQUE, 0, pl->ob, "Item stack full!");
1283 return;
1284 }
1285
1286 pl->stack_items[pl->stack_position] = item;
1287 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Item stacked as %d.", pl->stack_position);
1288 pl->stack_position++;
1289 }
1290
1291 /**
1292 * Checks 'params' for object code.
1293 *
1294 * Can be:
1295 * * empty => get current object stack top for player
1296 * * number => get item with that tag, stack it for future use
1297 * * $number => get specified stack item
1298 * * "me" => player himself
1299 *
1300 * At function exit, params points to first non-object char
1301 *
1302 * 'from', if not NULL, contains at exit:
1303 * * STACK_FROM_NONE => object not found
1304 * * STACK_FROM_TOP => top item stack, may be NULL if stack was empty
1305 * * STACK_FROM_STACK => item from somewhere in the stack
1306 * * STACK_FROM_NUMBER => item by number, pushed on stack
1307 *
1308 * Ryo, august 2004
1309 */
1310 object *
1311 get_dm_object (player *pl, char **params, int *from)
1312 {
1313 int item_tag, item_position;
1314 object *ob;
1315
1316 if (!pl)
1317 return NULL;
1318
1319 if (!params || !*params || **params == '\0')
1320 {
1321 if (from)
1322 *from = STACK_FROM_TOP;
1323 /* No parameter => get stack item */
1324 return dm_stack_peek (pl);
1325 }
1326
1327 /* Let's clean white spaces */
1328 while (**params == ' ')
1329 (*params)++;
1330
1331 /* Next case: number => item tag */
1332 if (sscanf (*params, "%d", &item_tag))
1333 {
1334 /* Move parameter to next item */
1335 while (isdigit (**params))
1336 (*params)++;
1337
1338 /* And skip blanks, too */
1339 while (**params == ' ')
1340 (*params)++;
1341
1342 /* Get item */
1343 ob = find_object (item_tag);
1344 if (!ob)
1345 {
1346 if (from)
1347 *from = STACK_FROM_NONE;
1348 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "No such item %d!", item_tag);
1349 return NULL;
1350 }
1351
1352 /* Got one, let's push it on stack */
1353 dm_stack_push (pl, item_tag);
1354 if (from)
1355 *from = STACK_FROM_NUMBER;
1356 return ob;
1357 }
1358
1359 /* Next case: $number => stack item */
1360 if (sscanf (*params, "$%d", &item_position))
1361 {
1362 /* Move parameter to next item */
1363 (*params)++;
1364
1365 while (isdigit (**params))
1366 (*params)++;
1367 while (**params == ' ')
1368 (*params)++;
1369
1370 if (item_position >= pl->stack_position)
1371 {
1372 if (from)
1373 *from = STACK_FROM_NONE;
1374 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "No such stack item %d!", item_position);
1375 return NULL;
1376 }
1377
1378 ob = find_object (pl->stack_items[item_position]);
1379 if (!ob)
1380 {
1381 if (from)
1382 *from = STACK_FROM_NONE;
1383 new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Stack item %d was removed.", item_position);
1384 return NULL;
1385 }
1386
1387 if (from)
1388 *from = item_position < pl->stack_position - 1 ? STACK_FROM_STACK : STACK_FROM_TOP;
1389 return ob;
1390 }
1391
1392 /* Next case: 'me' => return pl->ob */
1393 if (!strncmp (*params, "me", 2))
1394 {
1395 if (from)
1396 *from = STACK_FROM_NUMBER;
1397 dm_stack_push (pl, pl->ob->count);
1398
1399 /* Skip to next token */
1400 (*params) += 2;
1401 while (**params == ' ')
1402 (*params)++;
1403
1404 return pl->ob;
1405 }
1406
1407 /* Last case: get stack top */
1408 if (from)
1409 *from = STACK_FROM_TOP;
1410 return dm_stack_peek (pl);
1411 }
1412
1413 /**
1414 * Pop the stack top.
1415 */
1416 int
1417 command_stack_pop (object *op, char *params)
1418 {
1419 dm_stack_pop (op->contr);
1420 return 0;
1421 }
1422
1423 /**
1424 * Push specified item on stack.
1425 */
1426 int
1427 command_stack_push (object *op, char *params)
1428 {
1429 object *ob;
1430 int from;
1431
1432 ob = get_dm_object (op->contr, &params, &from);
1433
1434 if (ob && from != STACK_FROM_NUMBER)
1435 /* Object was from stack, need to push it again */
1436 dm_stack_push (op->contr, ob->count);
1437
1438 return 0;
1439 }
1440
1441 /**
1442 * Displays stack contents.
1443 */
1444 int
1445 command_stack_list (object *op, char *params)
1446 {
1447 int item;
1448 object *display;
1449 player *pl = op->contr;
1450
1451 new_draw_info (NDI_UNIQUE, 0, op, "Item stack contents:");
1452
1453 for (item = 0; item < pl->stack_position; item++)
1454 {
1455 display = find_object (pl->stack_items[item]);
1456 if (display)
1457 new_draw_info_format (NDI_UNIQUE, 0, op, " %d : %s [%d]", item, &display->name, display->count);
1458 else
1459 /* Item was freed */
1460 new_draw_info_format (NDI_UNIQUE, 0, op, " %d : (lost item: %d)", item, pl->stack_items[item]);
1461 }
1462
1463 return 0;
1464 }
1465
1466 /**
1467 * Empty DM item stack.
1468 */
1469 int
1470 command_stack_clear (object *op, char *params)
1471 {
1472 op->contr->stack_position = 0;
1473 new_draw_info (NDI_UNIQUE, 0, op, "Item stack cleared.");
1474 return 0;
1475 }
1476
1477 int
1478 command_insert_into (object *op, char *params)
1479 {
1480 object *left, *right, *inserted;
1481 int left_from, right_from;
1482
1483 left = get_dm_object (op->contr, &params, &left_from);
1484 if (!left)
1485 {
1486 new_draw_info (NDI_UNIQUE, 0, op, "Insert into what object?");
1487 return 0;
1488 }
1489
1490 if (left_from == STACK_FROM_NUMBER)
1491 /* Item was stacked, remove it else right will be the same... */
1492 dm_stack_pop (op->contr);
1493
1494 right = get_dm_object (op->contr, &params, &right_from);
1495
1496 if (!right)
1497 {
1498 new_draw_info (NDI_UNIQUE, 0, op, "Insert what item?");
1499 return 0;
1500 }
1501
1502 if (left_from == STACK_FROM_TOP && right_from == STACK_FROM_TOP)
1503 {
1504 /*
1505 * Special case: both items were taken from stack top.
1506 * Override the behaviour, taking left as item just below top, if exists.
1507 * See function description for why.
1508 * Besides, can't insert an item into itself.
1509 */
1510 if (op->contr->stack_position > 1)
1511 {
1512 left = find_object (op->contr->stack_items[op->contr->stack_position - 2]);
1513 if (left)
1514 new_draw_info (NDI_UNIQUE, 0, op, "(Note: item to insert into taken from undertop)");
1515 else
1516 /* Stupid case: item under top was freed, fallback to stack top */
1517 left = right;
1518 }
1519 }
1520
1521 if (left == right)
1522 {
1523 new_draw_info (NDI_UNIQUE, 0, op, "Can't insert an object into itself!");
1524 return 0;
1525 }
1526
1527 if (right->type == PLAYER)
1528 {
1529 new_draw_info (NDI_UNIQUE, 0, op, "Can't insert a player into something!");
1530 return 0;
1531 }
1532
1533 if (!QUERY_FLAG (right, FLAG_REMOVED))
1534 right->remove ();
1535
1536 insert_ob_in_ob (right, left);
1537
1538 new_draw_info_format (NDI_UNIQUE, 0, op, "Inserted %s in %s", query_name (inserted), query_name (left));
1539
1540 return 0;
1541
1542 }