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 }