/ circle3.1 / src / spells.c
spells.c
  1  /* ************************************************************************
  2  *   File: spells.c                                      Part of CircleMUD *
  3  *  Usage: Implementation of "manual spells".  Circle 2.2 spell compat.    *
  4  *                                                                         *
  5  *  All rights reserved.  See license.doc for complete information.        *
  6  *                                                                         *
  7  *  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
  8  *  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
  9  ************************************************************************ */
 10  
 11  
 12  #include "conf.h"
 13  #include "sysdep.h"
 14  
 15  #include "structs.h"
 16  #include "utils.h"
 17  #include "comm.h"
 18  #include "spells.h"
 19  #include "handler.h"
 20  #include "db.h"
 21  #include "constants.h"
 22  #include "interpreter.h"
 23  
 24  
 25  /* external variables */
 26  extern room_rnum r_mortal_start_room;
 27  extern int mini_mud;
 28  extern int pk_allowed;
 29  
 30  /* external functions */
 31  void clearMemory(struct char_data *ch);
 32  void weight_change_object(struct obj_data *obj, int weight);
 33  int mag_savingthrow(struct char_data *ch, int type, int modifier);
 34  void name_to_drinkcon(struct obj_data *obj, int type);
 35  void name_from_drinkcon(struct obj_data *obj);
 36  int compute_armor_class(struct char_data *ch);
 37  
 38  /*
 39   * Special spells appear below.
 40   */
 41  
 42  ASPELL(spell_create_water)
 43  {
 44    int water;
 45  
 46    if (ch == NULL || obj == NULL)
 47      return;
 48    /* level = MAX(MIN(level, LVL_IMPL), 1);	 - not used */
 49  
 50    if (GET_OBJ_TYPE(obj) == ITEM_DRINKCON) {
 51      if ((GET_OBJ_VAL(obj, 2) != LIQ_WATER) && (GET_OBJ_VAL(obj, 1) != 0)) {
 52        name_from_drinkcon(obj);
 53        GET_OBJ_VAL(obj, 2) = LIQ_SLIME;
 54        name_to_drinkcon(obj, LIQ_SLIME);
 55      } else {
 56        water = MAX(GET_OBJ_VAL(obj, 0) - GET_OBJ_VAL(obj, 1), 0);
 57        if (water > 0) {
 58  	if (GET_OBJ_VAL(obj, 1) >= 0)
 59  	  name_from_drinkcon(obj);
 60  	GET_OBJ_VAL(obj, 2) = LIQ_WATER;
 61  	GET_OBJ_VAL(obj, 1) += water;
 62  	name_to_drinkcon(obj, LIQ_WATER);
 63  	weight_change_object(obj, water);
 64  	act("$p is filled.", FALSE, ch, obj, 0, TO_CHAR);
 65        }
 66      }
 67    }
 68  }
 69  
 70  
 71  ASPELL(spell_recall)
 72  {
 73    if (victim == NULL || IS_NPC(victim))
 74      return;
 75  
 76    act("$n disappears.", TRUE, victim, 0, 0, TO_ROOM);
 77    char_from_room(victim);
 78    char_to_room(victim, r_mortal_start_room);
 79    act("$n appears in the middle of the room.", TRUE, victim, 0, 0, TO_ROOM);
 80    look_at_room(victim, 0);
 81  }
 82  
 83  
 84  ASPELL(spell_teleport)
 85  {
 86    room_rnum to_room;
 87  
 88    if (victim == NULL || IS_NPC(victim))
 89      return;
 90  
 91    do {
 92      to_room = rand_number(0, top_of_world);
 93    } while (ROOM_FLAGGED(to_room, ROOM_PRIVATE | ROOM_DEATH | ROOM_GODROOM));
 94  
 95    act("$n slowly fades out of existence and is gone.",
 96        FALSE, victim, 0, 0, TO_ROOM);
 97    char_from_room(victim);
 98    char_to_room(victim, to_room);
 99    act("$n slowly fades into existence.", FALSE, victim, 0, 0, TO_ROOM);
100    look_at_room(victim, 0);
101  }
102  
103  #define SUMMON_FAIL "You failed.\r\n"
104  
105  ASPELL(spell_summon)
106  {
107    if (ch == NULL || victim == NULL)
108      return;
109  
110    if (GET_LEVEL(victim) > MIN(LVL_IMMORT - 1, level + 3)) {
111      send_to_char(ch, "%s", SUMMON_FAIL);
112      return;
113    }
114  
115    if (!pk_allowed) {
116      if (MOB_FLAGGED(victim, MOB_AGGRESSIVE)) {
117        act("As the words escape your lips and $N travels\r\n"
118  	  "through time and space towards you, you realize that $E is\r\n"
119  	  "aggressive and might harm you, so you wisely send $M back.",
120  	  FALSE, ch, 0, victim, TO_CHAR);
121        return;
122      }
123      if (!IS_NPC(victim) && !PRF_FLAGGED(victim, PRF_SUMMONABLE) &&
124  	!PLR_FLAGGED(victim, PLR_KILLER)) {
125        send_to_char(victim, "%s just tried to summon you to: %s.\r\n"
126  	      "%s failed because you have summon protection on.\r\n"
127  	      "Type NOSUMMON to allow other players to summon you.\r\n",
128  	      GET_NAME(ch), world[IN_ROOM(ch)].name,
129  	      (ch->player.sex == SEX_MALE) ? "He" : "She");
130  
131        send_to_char(ch, "You failed because %s has summon protection on.\r\n", GET_NAME(victim));
132        mudlog(BRF, LVL_IMMORT, TRUE, "%s failed summoning %s to %s.", GET_NAME(ch), GET_NAME(victim), world[IN_ROOM(ch)].name);
133        return;
134      }
135    }
136  
137    if (MOB_FLAGGED(victim, MOB_NOSUMMON) ||
138        (IS_NPC(victim) && mag_savingthrow(victim, SAVING_SPELL, 0))) {
139      send_to_char(ch, "%s", SUMMON_FAIL);
140      return;
141    }
142  
143    act("$n disappears suddenly.", TRUE, victim, 0, 0, TO_ROOM);
144  
145    char_from_room(victim);
146    char_to_room(victim, IN_ROOM(ch));
147  
148    act("$n arrives suddenly.", TRUE, victim, 0, 0, TO_ROOM);
149    act("$n has summoned you!", FALSE, ch, 0, victim, TO_VICT);
150    look_at_room(victim, 0);
151  }
152  
153  
154  
155  ASPELL(spell_locate_object)
156  {
157    struct obj_data *i;
158    char name[MAX_INPUT_LENGTH];
159    int j;
160  
161    /*
162     * FIXME: This is broken.  The spell parser routines took the argument
163     * the player gave to the spell and located an object with that keyword.
164     * Since we're passed the object and not the keyword we can only guess
165     * at what the player originally meant to search for. -gg
166     */
167    strlcpy(name, fname(obj->name), sizeof(name));
168    j = level / 2;
169  
170    for (i = object_list; i && (j > 0); i = i->next) {
171      if (!isname(name, i->name))
172        continue;
173  
174      send_to_char(ch, "%c%s", UPPER(*i->short_description), i->short_description);
175  
176      if (i->carried_by)
177        send_to_char(ch, " is being carried by %s.\r\n", PERS(i->carried_by, ch));
178      else if (IN_ROOM(i) != NOWHERE)
179        send_to_char(ch, " is in %s.\r\n", world[IN_ROOM(i)].name);
180      else if (i->in_obj)
181        send_to_char(ch, " is in %s.\r\n", i->in_obj->short_description);
182      else if (i->worn_by)
183        send_to_char(ch, " is being worn by %s.\r\n", PERS(i->worn_by, ch));
184      else
185        send_to_char(ch, "'s location is uncertain.\r\n");
186  
187      j--;
188    }
189  
190    if (j == level / 2)
191      send_to_char(ch, "You sense nothing.\r\n");
192  }
193  
194  
195  
196  ASPELL(spell_charm)
197  {
198    struct affected_type af;
199  
200    if (victim == NULL || ch == NULL)
201      return;
202  
203    if (victim == ch)
204      send_to_char(ch, "You like yourself even better!\r\n");
205    else if (!IS_NPC(victim) && !PRF_FLAGGED(victim, PRF_SUMMONABLE))
206      send_to_char(ch, "You fail because SUMMON protection is on!\r\n");
207    else if (AFF_FLAGGED(victim, AFF_SANCTUARY))
208      send_to_char(ch, "Your victim is protected by sanctuary!\r\n");
209    else if (MOB_FLAGGED(victim, MOB_NOCHARM))
210      send_to_char(ch, "Your victim resists!\r\n");
211    else if (AFF_FLAGGED(ch, AFF_CHARM))
212      send_to_char(ch, "You can't have any followers of your own!\r\n");
213    else if (AFF_FLAGGED(victim, AFF_CHARM) || level < GET_LEVEL(victim))
214      send_to_char(ch, "You fail.\r\n");
215    /* player charming another player - no legal reason for this */
216    else if (!pk_allowed && !IS_NPC(victim))
217      send_to_char(ch, "You fail - shouldn't be doing it anyway.\r\n");
218    else if (circle_follow(victim, ch))
219      send_to_char(ch, "Sorry, following in circles can not be allowed.\r\n");
220    else if (mag_savingthrow(victim, SAVING_PARA, 0))
221      send_to_char(ch, "Your victim resists!\r\n");
222    else {
223      if (victim->master)
224        stop_follower(victim);
225  
226      add_follower(victim, ch);
227  
228      af.type = SPELL_CHARM;
229      af.duration = 24 * 2;
230      if (GET_CHA(ch))
231        af.duration *= GET_CHA(ch);
232      if (GET_INT(victim))
233        af.duration /= GET_INT(victim);
234      af.modifier = 0;
235      af.location = 0;
236      af.bitvector = AFF_CHARM;
237      affect_to_char(victim, &af);
238  
239      act("Isn't $n just such a nice fellow?", FALSE, ch, 0, victim, TO_VICT);
240      if (IS_NPC(victim))
241        REMOVE_BIT(MOB_FLAGS(victim), MOB_SPEC);
242    }
243  }
244  
245  
246  
247  ASPELL(spell_identify)
248  {
249    int i, found;
250    size_t len;
251  
252    if (obj) {
253      char bitbuf[MAX_STRING_LENGTH];
254  
255      sprinttype(GET_OBJ_TYPE(obj), item_types, bitbuf, sizeof(bitbuf));
256      send_to_char(ch, "You feel informed:\r\nObject '%s', Item type: %s\r\n", obj->short_description, bitbuf);
257  
258      if (GET_OBJ_AFFECT(obj)) {
259        sprintbit(GET_OBJ_AFFECT(obj), affected_bits, bitbuf, sizeof(bitbuf));
260        send_to_char(ch, "Item will give you following abilities:  %s\r\n", bitbuf);
261      }
262  
263      sprintbit(GET_OBJ_EXTRA(obj), extra_bits, bitbuf, sizeof(bitbuf));
264      send_to_char(ch, "Item is: %s\r\n", bitbuf);
265  
266      send_to_char(ch, "Weight: %d, Value: %d, Rent: %d\r\n", GET_OBJ_WEIGHT(obj), GET_OBJ_COST(obj), GET_OBJ_RENT(obj));
267  
268      switch (GET_OBJ_TYPE(obj)) {
269      case ITEM_SCROLL:
270      case ITEM_POTION:
271        len = i = 0;
272  
273        if (GET_OBJ_VAL(obj, 1) >= 1) {
274  	i = snprintf(bitbuf + len, sizeof(bitbuf) - len, " %s", skill_name(GET_OBJ_VAL(obj, 1)));
275          if (i >= 0)
276            len += i;
277        }
278  
279        if (GET_OBJ_VAL(obj, 2) >= 1 && len < sizeof(bitbuf)) {
280  	i = snprintf(bitbuf + len, sizeof(bitbuf) - len, " %s", skill_name(GET_OBJ_VAL(obj, 2)));
281          if (i >= 0)
282            len += i;
283        }
284  
285        if (GET_OBJ_VAL(obj, 3) >= 1 && len < sizeof(bitbuf)) {
286  	i = snprintf(bitbuf + len, sizeof(bitbuf) - len, " %s", skill_name(GET_OBJ_VAL(obj, 3)));
287          if (i >= 0)
288            len += i;
289        }
290  
291        send_to_char(ch, "This %s casts: %s\r\n", item_types[(int) GET_OBJ_TYPE(obj)], bitbuf);
292        break;
293      case ITEM_WAND:
294      case ITEM_STAFF:
295        send_to_char(ch, "This %s casts: %s\r\nIt has %d maximum charge%s and %d remaining.\r\n",
296  		item_types[(int) GET_OBJ_TYPE(obj)], skill_name(GET_OBJ_VAL(obj, 3)),
297  		GET_OBJ_VAL(obj, 1), GET_OBJ_VAL(obj, 1) == 1 ? "" : "s", GET_OBJ_VAL(obj, 2));
298        break;
299      case ITEM_WEAPON:
300        send_to_char(ch, "Damage Dice is '%dD%d' for an average per-round damage of %.1f.\r\n",
301  		GET_OBJ_VAL(obj, 1), GET_OBJ_VAL(obj, 2), ((GET_OBJ_VAL(obj, 2) + 1) / 2.0) * GET_OBJ_VAL(obj, 1));
302        break;
303      case ITEM_ARMOR:
304        send_to_char(ch, "AC-apply is %d\r\n", GET_OBJ_VAL(obj, 0));
305        break;
306      }
307      found = FALSE;
308      for (i = 0; i < MAX_OBJ_AFFECT; i++) {
309        if ((obj->affected[i].location != APPLY_NONE) &&
310  	  (obj->affected[i].modifier != 0)) {
311  	if (!found) {
312  	  send_to_char(ch, "Can affect you as :\r\n");
313  	  found = TRUE;
314  	}
315  	sprinttype(obj->affected[i].location, apply_types, bitbuf, sizeof(bitbuf));
316  	send_to_char(ch, "   Affects: %s By %d\r\n", bitbuf, obj->affected[i].modifier);
317        }
318      }
319    } else if (victim) {		/* victim */
320      send_to_char(ch, "Name: %s\r\n", GET_NAME(victim));
321      if (!IS_NPC(victim))
322        send_to_char(ch, "%s is %d years, %d months, %d days and %d hours old.\r\n",
323  	      GET_NAME(victim), age(victim)->year, age(victim)->month,
324  	      age(victim)->day, age(victim)->hours);
325      send_to_char(ch, "Height %d cm, Weight %d pounds\r\n", GET_HEIGHT(victim), GET_WEIGHT(victim));
326      send_to_char(ch, "Level: %d, Hits: %d, Mana: %d\r\n", GET_LEVEL(victim), GET_HIT(victim), GET_MANA(victim));
327      send_to_char(ch, "AC: %d, Hitroll: %d, Damroll: %d\r\n", compute_armor_class(victim), GET_HITROLL(victim), GET_DAMROLL(victim));
328      send_to_char(ch, "Str: %d/%d, Int: %d, Wis: %d, Dex: %d, Con: %d, Cha: %d\r\n",
329  	GET_STR(victim), GET_ADD(victim), GET_INT(victim),
330  	GET_WIS(victim), GET_DEX(victim), GET_CON(victim), GET_CHA(victim));
331    }
332  }
333  
334  
335  
336  /*
337   * Cannot use this spell on an equipped object or it will mess up the
338   * wielding character's hit/dam totals.
339   */
340  ASPELL(spell_enchant_weapon)
341  {
342    int i;
343  
344    if (ch == NULL || obj == NULL)
345      return;
346  
347    /* Either already enchanted or not a weapon. */
348    if (GET_OBJ_TYPE(obj) != ITEM_WEAPON || OBJ_FLAGGED(obj, ITEM_MAGIC))
349      return;
350  
351    /* Make sure no other affections. */
352    for (i = 0; i < MAX_OBJ_AFFECT; i++)
353      if (obj->affected[i].location != APPLY_NONE)
354        return;
355  
356    SET_BIT(GET_OBJ_EXTRA(obj), ITEM_MAGIC);
357  
358    obj->affected[0].location = APPLY_HITROLL;
359    obj->affected[0].modifier = 1 + (level >= 18);
360  
361    obj->affected[1].location = APPLY_DAMROLL;
362    obj->affected[1].modifier = 1 + (level >= 20);
363  
364    if (IS_GOOD(ch)) {
365      SET_BIT(GET_OBJ_EXTRA(obj), ITEM_ANTI_EVIL);
366      act("$p glows blue.", FALSE, ch, obj, 0, TO_CHAR);
367    } else if (IS_EVIL(ch)) {
368      SET_BIT(GET_OBJ_EXTRA(obj), ITEM_ANTI_GOOD);
369      act("$p glows red.", FALSE, ch, obj, 0, TO_CHAR);
370    } else
371      act("$p glows yellow.", FALSE, ch, obj, 0, TO_CHAR);
372  }
373  
374  
375  ASPELL(spell_detect_poison)
376  {
377    if (victim) {
378      if (victim == ch) {
379        if (AFF_FLAGGED(victim, AFF_POISON))
380          send_to_char(ch, "You can sense poison in your blood.\r\n");
381        else
382          send_to_char(ch, "You feel healthy.\r\n");
383      } else {
384        if (AFF_FLAGGED(victim, AFF_POISON))
385          act("You sense that $E is poisoned.", FALSE, ch, 0, victim, TO_CHAR);
386        else
387          act("You sense that $E is healthy.", FALSE, ch, 0, victim, TO_CHAR);
388      }
389    }
390  
391    if (obj) {
392      switch (GET_OBJ_TYPE(obj)) {
393      case ITEM_DRINKCON:
394      case ITEM_FOUNTAIN:
395      case ITEM_FOOD:
396        if (GET_OBJ_VAL(obj, 3))
397  	act("You sense that $p has been contaminated.",FALSE,ch,obj,0,TO_CHAR);
398        else
399  	act("You sense that $p is safe for consumption.", FALSE, ch, obj, 0,
400  	    TO_CHAR);
401        break;
402      default:
403        send_to_char(ch, "You sense that it should not be consumed.\r\n");
404      }
405    }
406  }