1 /*
2 * spells.c
3 * Copyright (C) 2009-2020 Joachim de Groot <jdegroot@web.de>
4 *
5 * NLarn is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * NLarn is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <glib.h>
20 #include <string.h>
21
22 #include "display.h"
23 #include "map.h"
24 #include "extdefs.h"
25 #include "random.h"
26 #include "sobjects.h"
27 #include "spells.h"
28 #include "spheres.h"
29
30 static gboolean spell_type_player(spell *s, struct player *p);
31 static gboolean spell_type_point(spell *s, struct player *p);
32 static gboolean spell_type_ray(spell *s, struct player *p);
33 static gboolean spell_type_flood(spell *s, struct player *p);
34 static gboolean spell_type_blast(spell *s, struct player *p);
35
36 static gboolean spell_alter_reality(spell *s, struct player *p);
37 static gboolean spell_create_sphere(spell *s, struct player *p);
38 static gboolean spell_cure_poison(spell *s, struct player *p);
39 static gboolean spell_cure_blindness(spell *s, struct player *p);
40 static gboolean spell_phantasmal_forces(spell *s, struct player *p);
41 static gboolean spell_scare_monsters(spell *s, struct player *p);
42 static gboolean spell_summon_demon(spell *s, struct player *p);
43 static gboolean spell_make_wall(spell *s, struct player *p);
44
45 const spell_data spells[SP_MAX] =
46 {
47 {
48 SP_PRO, "pro","protection",
49 SC_PLAYER, DAM_NONE, ET_PROTECTION, spell_type_player,
50 "Generates a protection field",
51 NULL, NULL,
52 COLOURLESS, 1, 260, TRUE
53 },
54 {
55 SP_MLE, "mle", "magic missile",
56 SC_RAY, DAM_MAGICAL, ET_NONE, spell_type_ray,
57 "Creates and hurls a missile magic of magical energy at a target",
58 "The missile hits the %s.",
59 "The missile bounces off the %s.",
60 COLOURLESS, 1, 320, TRUE
61 },
62 {
63 SP_DEX, "dex", "dexterity",
64 SC_PLAYER, DAM_NONE, ET_INC_DEX, spell_type_player,
65 "Improves the caster's dexterity",
66 NULL, NULL,
67 COLOURLESS, 1, 260, FALSE
68 },
69 {
70 SP_SLE, "sle", "sleep",
71 SC_POINT, DAM_NONE, ET_SLEEP, spell_type_point,
72 "Causes some monsters to go to sleep",
73 NULL,
74 "The %s doesn't sleep.",
75 COLOURLESS, 1, 260, TRUE
76 },
77 {
78 SP_CHM, "chm", "charm monster",
79 SC_POINT, DAM_NONE, ET_CHARM_MONSTER, spell_type_point,
80 "Some monsters may be awed at your magnificence",
81 NULL, "The %s isn't impressed.",
82 COLOURLESS, 1, 260, FALSE
83 },
84 {
85 SP_SSP, "ssp", "sonic spear",
86 SC_RAY, DAM_PHYSICAL, ET_NONE, spell_type_ray,
87 "Causes your hands to emit a screeching sound toward what they point",
88 "The sound damages the %s.",
89 "The %s can't hear the noise.",
90 LIGHTCYAN, 2, 480, FALSE
91 },
92 {
93 SP_STR, "str", "strength",
94 SC_PLAYER, DAM_NONE, ET_INC_STR, spell_type_player,
95 "Increase the caster's strength for a short term",
96 NULL, NULL,
97 COLOURLESS, 2, 460, FALSE
98 },
99 {
100 SP_CPO, "cpo", "cure poison",
101 SC_PLAYER, DAM_NONE, ET_NONE, spell_cure_poison,
102 "The caster is cured from poison",
103 NULL, NULL,
104 COLOURLESS, 2, 460, TRUE
105 },
106 {
107 SP_HEL, "hel", "healing",
108 SC_PLAYER, DAM_NONE, ET_INC_HP, spell_type_player,
109 "Restores some HP to the caster",
110 NULL, NULL,
111 COLOURLESS, 2, 500, TRUE
112 },
113 {
114 SP_CBL, "cbl", "cure blindness",
115 SC_PLAYER, DAM_NONE, ET_NONE, spell_cure_blindness,
116 "Restores sight to one so unfortunate as to be blinded",
117 NULL, NULL,
118 COLOURLESS, 2, 400, TRUE
119 },
120 {
121 SP_CRE, "cre", "create monster",
122 SC_OTHER, DAM_NONE, ET_NONE, spell_create_monster,
123 "Creates a monster near the caster appropriate for the location",
124 NULL, NULL,
125 COLOURLESS, 2, 400, FALSE
126 },
127 {
128 SP_PHA, "pha", "phantasmal forces",
129 SC_OTHER, DAM_NONE, ET_NONE, spell_phantasmal_forces,
130 "Creates illusions, and if believed, the monster flees",
131 "The %s believed!",
132 "The %s didn't believe the illusions!",
133 COLOURLESS, 2, 600, FALSE
134 },
135 {
136 SP_INV, "inv", "invisibility",
137 SC_PLAYER, DAM_NONE, ET_INVISIBILITY, spell_type_player,
138 "The caster becomes invisible",
139 NULL, NULL,
140 COLOURLESS, 2, 600, FALSE
141 },
142 {
143 SP_BAL, "bal", "fireball",
144 SC_BLAST, DAM_FIRE, ET_NONE, spell_type_blast,
145 "Makes a ball of fire that burns on what it hits",
146 "The fireball hits the %s.",
147 NULL,
148 LIGHTRED, 3, 1200, FALSE
149 },
150 {
151 SP_CLD, "cld", "cone of cold",
152 SC_RAY, DAM_COLD, ET_NONE, spell_type_ray,
153 "Sends forth a cone of cold which freezes what it touches",
154 "The cone of cold strikes the %s.",
155 NULL,
156 WHITE, 3, 1200, FALSE
157 },
158 {
159 SP_PLY, "ply", "polymorph",
160 SC_POINT, DAM_NONE, ET_NONE, spell_type_point,
161 "You can find out what this does for yourself",
162 NULL,
163 "The %s resists.",
164 COLOURLESS, 3, 950, FALSE
165 },
166 {
167 SP_CAN, "can", "cancellation",
168 SC_PLAYER, DAM_NONE, ET_CANCELLATION, spell_type_player,
169 "Protects the caster against spheres of annihilation",
170 NULL, NULL,
171 COLOURLESS, 3, 950, FALSE
172 },
173 {
174 SP_HAS, "has", "haste self",
175 SC_PLAYER, DAM_NONE, ET_SPEED, spell_type_player,
176 "Speeds up the caster's movements",
177 NULL, NULL,
178 COLOURLESS, 3, 950, FALSE
179 },
180 {
181 SP_CKL, "ckl", "killing cloud",
182 SC_FLOOD, DAM_ACID, ET_NONE, spell_type_flood,
183 "Creates a fog of poisonous gas which kills all that is within it",
184 "The %s gasps for air.",
185 NULL,
186 COLOURLESS, 3, 1200, FALSE
187 },
188 {
189 SP_VPR, "vpr", "vaporize rock",
190 SC_OTHER, DAM_NONE, ET_NONE, spell_vaporize_rock,
191 "This changes rock to air",
192 NULL, NULL,
193 COLOURLESS, 3, 950, FALSE
194 },
195 {
196 SP_DRY, "dry", "dehydration",
197 SC_POINT, DAM_PHYSICAL, ET_NONE, spell_type_point,
198 "Dries up water in the immediate vicinity",
199 "The %s shrivels up.",
200 "The %s isn't affected.",
201 COLOURLESS, 4, 1600, FALSE
202 },
203 {
204 SP_LIT, "lit", "lightning",
205 SC_RAY, DAM_ELECTRICITY, ET_NONE, spell_type_ray,
206 "Your finger will emit a lightning bolt when this spell is cast",
207 "A lightning bolt hits the %s.",
208 "The %s loves fire and lightning!",
209 YELLOW, 4, 1600, FALSE
210 },
211 {
212 SP_DRL, "drl", "drain life",
213 SC_POINT, DAM_PHYSICAL, ET_NONE, spell_type_point,
214 "Subtracts hit points from both you and a monster",
215 NULL, NULL,
216 COLOURLESS, 4, 1400, FALSE
217 },
218 {
219 SP_GLO, "glo", "invulnerability",
220 SC_PLAYER, DAM_NONE, ET_INVULNERABILITY, spell_type_player,
221 "This globe helps to protect the player from physical attack",
222 NULL, NULL,
223 COLOURLESS, 4, 1400, FALSE
224 },
225 {
226 SP_FLO, "flo", "flood",
227 SC_FLOOD, DAM_WATER, ET_NONE, spell_type_flood,
228 "This creates an avalanche of H2O to flood the immediate chamber",
229 "The %s struggles for air in the flood!",
230 "The %s loves the water!",
231 COLOURLESS, 4, 1600, FALSE
232 },
233 {
234 SP_FGR, "fgr", "finger of death",
235 SC_POINT, DAM_PHYSICAL, ET_NONE, spell_type_point,
236 "This is a holy spell and calls upon your god to back you up",
237 "The %s's heart stopped.",
238 "The %s isn't affected.",
239 COLOURLESS, 4, 1600, FALSE
240 },
241 {
242 SP_SCA, "sca", "scare monsters",
243 SC_OTHER, DAM_NONE, ET_NONE, spell_scare_monsters,
244 "Terrifies nearby monsters so that hopefully they flee the magic user",
245 NULL, NULL,
246 COLOURLESS, 5, 2000, FALSE
247 },
248 {
249 SP_HLD, "hld", "hold monster",
250 SC_POINT, DAM_NONE, ET_HOLD_MONSTER, spell_type_point,
251 "The monster is frozen in his tracks if this is successful",
252 NULL, NULL,
253 COLOURLESS, 5, 2000, FALSE
254 },
255 {
256 SP_STP, "stp", "time stop",
257 SC_PLAYER, DAM_NONE, ET_TIMESTOP, spell_type_player,
258 "All movement in the caverns ceases for a limited duration",
259 NULL, NULL,
260 COLOURLESS, 5, 2500, FALSE
261 },
262 {
263 SP_TEL, "tel", "teleport away",
264 SC_POINT, DAM_NONE, ET_NONE, spell_type_point,
265 "Moves a particular monster around the caverns",
266 NULL, NULL,
267 COLOURLESS, 5, 2000, FALSE
268 },
269 {
270 SP_MFI, "mfi", "magic fire",
271 SC_FLOOD, DAM_FIRE, ET_NONE, spell_type_flood,
272 "This causes a curtain of fire to appear all around you",
273 "The %s cringes from the flame.",
274 NULL,
275 COLOURLESS, 5, 2500, FALSE
276 },
277 {
278 SP_MKW, "mkw", "make wall",
279 SC_OTHER, DAM_NONE, ET_NONE, spell_make_wall,
280 "Makes a wall in the specified place",
281 NULL, NULL,
282 COLOURLESS, 6, 3000, FALSE
283 },
284 {
285 SP_SPH, "sph", "sphere of annihilation",
286 SC_OTHER, DAM_NONE, ET_NONE, spell_create_sphere,
287 "Anything caught in this sphere is instantly killed",
288 NULL, NULL,
289 COLOURLESS, 6, 3500, FALSE
290 },
291 {
292 SP_SUM, "sum", "summon demon",
293 SC_OTHER, DAM_NONE, ET_NONE, spell_summon_demon,
294 "Summons a demon who hopefully helps you out",
295 NULL, NULL,
296 COLOURLESS, 6, 3500, FALSE
297 },
298 {
299 SP_WTW, "wtw", "walk through walls",
300 SC_PLAYER, DAM_NONE, ET_WALL_WALK, spell_type_player,
301 "Allows the caster to walk through walls for a short period of time",
302 NULL, NULL,
303 COLOURLESS, 6, 3800, FALSE
304 },
305 {
306 SP_ALT, "alt", "alter reality",
307 SC_OTHER, DAM_NONE, ET_NONE, spell_alter_reality,
308 "God only knows what this will do",
309 NULL,
310 "Polinneaus won't let you mess with his caverns!",
311 COLOURLESS, 6, 3800, FALSE
312 },
313 };
314
315 struct book_obfuscation_s
316 {
317 const char* desc;
318 const int weight;
319 const int colour;
320 }
321 book_obfuscation[SP_MAX] =
322 {
323 { "parchment-bound", 800, BROWN, },
324 { "thick", 1200, RED, },
325 { "dusty", 800, LIGHTGRAY, },
326 { "leather-bound", 800, BROWN, },
327 { "heavy", 1200, GREEN, },
328 { "ancient", 800, DARKGRAY, },
329 { "buckram", 800, LIGHTGRAY, },
330 { "gilded", 800, YELLOW, },
331 { "embossed", 800, BLUE, },
332 { "old", 800, LIGHTGRAY, },
333 { "thin", 400, GREEN, },
334 { "light", 400, WHITE, },
335 { "large", 1200, BLUE, },
336 { "vellum", 800, BROWN, },
337 { "tan", 800, BROWN, },
338 { "papyrus", 800, BROWN, },
339 { "linen", 800, WHITE, },
340 { "musty", 800, GREEN, },
341 { "faded", 800, DARKGRAY, },
342 { "antique", 800, DARKGRAY, },
343 { "worn out", 800, DARKGRAY, },
344 { "tattered", 800, LIGHTGRAY, },
345 { "aged", 800, DARKGRAY, },
346 { "ornate", 800, BLUE, },
347 { "inconspicuous", 800, LIGHTGRAY, },
348 { "awe-inspiring", 800, WHITE, },
349 { "stained", 800, BROWN, },
350 { "mottled", 800, RED, },
351 { "plaid", 800, RED, },
352 { "wax-lined", 800, BROWN, },
353 { "bamboo", 800, YELLOW, },
354 { "clasped", 800, YELLOW, },
355 { "well-thumbed", 800, BLUE, },
356 { "ragged", 800, LIGHTGRAY, },
357 { "dull", 800, DARKGRAY, },
358 { "canvas", 800, YELLOW, },
359 /*
360 reserve descriptions for unimplemented spells:
361 chambray
362 */
363 };
364
365 /* the last cast spell */
366 static spell *last_spell = NULL;
367
368 /* local functions */
369 static int spell_cast(player *p, spell *s);
370 static void spell_print_success_message(spell *s, monster *m);
371 static void spell_print_failure_message(spell *s, monster *m);
372 static int count_adjacent_water_squares(position pos);
373 static int try_drying_ground(position pos);
374
375 /* simple wrapper for spell_area_pos_hit() */
376 static gboolean spell_traj_pos_hit(const GList *traj,
377 const damage_originator *damo,
378 gpointer data1, gpointer data2);
379
380 static gboolean spell_area_pos_hit(position pos,
381 const damage_originator *damo,
382 gpointer data1, gpointer data2);
383
spell_new(spell_id id)384 spell *spell_new(spell_id id)
385 {
386 spell *nspell;
387
388 g_assert(id < SP_MAX);
389
390 nspell = g_malloc0(sizeof(spell));
391 nspell->id = id;
392 nspell->knowledge = 1;
393
394 return nspell;
395 }
396
spell_destroy(spell * s)397 void spell_destroy(spell *s)
398 {
399 g_assert(s != NULL);
400 g_free(s);
401 }
402
spell_serialize(spell * s)403 cJSON *spell_serialize(spell *s)
404 {
405 cJSON *sser = cJSON_CreateObject();
406
407 cJSON_AddNumberToObject(sser, "id", s->id);
408 cJSON_AddNumberToObject(sser, "knowledge", s->knowledge);
409 cJSON_AddNumberToObject(sser, "used", s->used);
410
411 return sser;
412 }
413
spell_deserialize(cJSON * sser)414 spell *spell_deserialize(cJSON *sser)
415 {
416 spell *s = g_malloc0(sizeof(spell));
417
418 s->id = cJSON_GetObjectItem(sser, "id")->valueint;
419 s->knowledge = cJSON_GetObjectItem(sser, "knowledge")->valueint;
420 s->used = cJSON_GetObjectItem(sser, "used")->valueint;
421
422 return s;
423 }
424
spells_serialize(GPtrArray * sparr)425 cJSON *spells_serialize(GPtrArray *sparr)
426 {
427 cJSON *sser = cJSON_CreateArray();
428
429 for (guint idx = 0; idx < sparr->len; idx++)
430 {
431 spell *s = g_ptr_array_index(sparr, idx);
432 cJSON_AddItemToArray(sser, spell_serialize(s));
433 }
434
435 return sser;
436 }
437
spells_deserialize(cJSON * sser)438 GPtrArray *spells_deserialize(cJSON *sser)
439 {
440 GPtrArray *n_spells = g_ptr_array_new_with_free_func(
441 (GDestroyNotify)spell_destroy);
442
443 for (int idx = 0; idx < cJSON_GetArraySize(sser); idx++)
444 {
445 spell *s = spell_deserialize(cJSON_GetArrayItem(sser, idx));
446 g_ptr_array_add(n_spells, s);
447 }
448
449 return n_spells;
450 }
451
spell_sort(gconstpointer a,gconstpointer b)452 int spell_sort(gconstpointer a, gconstpointer b)
453 {
454 gint order;
455 spell *spell_a = *((spell**)a);
456 spell *spell_b = *((spell**)b);
457
458 if (spell_a->id == spell_b->id)
459 order = 0;
460 else
461 order = strcmp(spell_code(spell_a), spell_code(spell_b));
462
463 return order;
464 }
465
466 /* Knowledge of a spell and intelligence make casting easier. */
spell_success_value(player * p,spell * sp)467 static int spell_success_value(player *p, spell *sp)
468 {
469 g_assert(p != NULL && sp != NULL);
470
471 if (player_get_int(p) < (3 * spell_level(sp)))
472 return 0;
473
474 return (player_get_int(p) - 2 * (spell_level(sp) - sp->knowledge));
475 }
476
477
spell_cast_new(struct player * p)478 int spell_cast_new(struct player *p)
479 {
480 /* check if the player knows any spell */
481 if (!p->known_spells || !p->known_spells->len)
482 {
483 log_add_entry(nlarn->log, "You don't know any spells.");
484 return 0;
485 }
486
487 /* spell casting is impossible when confused */
488 if (player_effect(p, ET_CONFUSION))
489 {
490 log_add_entry(nlarn->log, "You can't aim your magic!");
491 return 0;
492 }
493
494 /* show spell selection dialogue */
495 last_spell = display_spell_select("Select a spell to cast", p);
496
497 /* player aborted spell selection by pressing ESC */
498 if (!last_spell)
499 return 0;
500
501 return spell_cast(p, last_spell);
502 }
503
spell_cast_previous(struct player * p)504 int spell_cast_previous(struct player *p)
505 {
506 /* spell casting is impossible when confused */
507 if (player_effect(p, ET_CONFUSION))
508 {
509 log_add_entry(nlarn->log, "You can't aim your magic!");
510 return 0;
511 }
512
513 /* not casted any spell before */
514 if (!last_spell)
515 {
516 return spell_cast_new(p);
517 }
518
519 return spell_cast(p, last_spell);
520 }
521
spell_learn(player * p,spell_id spell_type)522 int spell_learn(player *p, spell_id spell_type)
523 {
524 g_assert(p != NULL && spell_type < SP_MAX);
525
526 if (!spell_known(p, spell_type))
527 {
528 /* Check if the player's intelligence is sufficient to learn the spell */
529 if ((spells[spell_type].level * 3) > (int)player_get_int(p))
530 /* spell is beyond the players scope */
531 return FALSE;
532
533 /* Check if the player's level is spell sufficient to learn the spell */
534 if (spells[spell_type].level > (int)p->level)
535 /* spell is beyond the players scope */
536 return FALSE;
537
538 spell *s = spell_new(spell_type);
539 g_ptr_array_add(p->known_spells, s);
540 return s->knowledge;
541 }
542 else
543 {
544 /* spell already known, improve knowledge */
545 for (guint idx = 0; idx < p->known_spells->len; idx++)
546 {
547 /* search spell */
548 spell *s = (spell *)g_ptr_array_index(p->known_spells, idx);
549
550 if (s->id == spell_type)
551 {
552 /* found it */
553 s->knowledge++;
554 return s->knowledge;
555 }
556 }
557 }
558
559 /* should not reach this point, but who knows.. */
560 return FALSE;
561 }
562
spell_known(player * p,spell_id spell_type)563 int spell_known(player *p, spell_id spell_type)
564 {
565 g_assert(p != NULL && spell_type < SP_MAX);
566
567 for (guint idx = 0; idx < p->known_spells->len; idx++)
568 {
569 spell *s = g_ptr_array_index(p->known_spells, idx);
570 if (s->id == spell_type)
571 {
572 return s->knowledge;
573 }
574 }
575
576 return FALSE;
577 }
578
spell_desc_by_id(spell_id sid)579 gchar* spell_desc_by_id(spell_id sid)
580 {
581 GString *desc = g_string_new(spells[sid].description);
582
583 const char *stdesc;
584 switch(spell_type_by_id(sid))
585 {
586 case SC_PLAYER:
587 stdesc = "affects the player";
588 break;
589 case SC_POINT:
590 stdesc = "affects a single monster";
591 break;
592 case SC_RAY:
593 stdesc = "emits a ray";
594 break;
595 case SC_FLOOD:
596 stdesc = "creates an effect which fills an area";
597 break;
598 case SC_BLAST:
599 stdesc = "creates an explosion";
600 break;
601 default:
602 stdesc = NULL;
603 }
604
605 if (stdesc)
606 {
607 g_string_append_printf(desc, " (%s)", stdesc);
608 }
609
610 g_string_append_c(desc, '.');
611
612 return g_string_free(desc, FALSE);
613 }
614
spell_type_player(spell * s,struct player * p)615 static gboolean spell_type_player(spell *s, struct player *p)
616 {
617 effect *e = NULL;
618
619 g_assert(s != NULL && p != NULL && (spell_type(s) == SC_PLAYER));
620
621 /* check if the player is already affected by the effect */
622 if ((e = player_effect_get(p, spell_effect(s))))
623 {
624 /* player has cast this spell before */
625 if (effect_type_inc_amount(e->type))
626 {
627 /* The effect amount can be incremented.
628 * Increase the amount of the effect up to the base
629 * effect value * spell knowledge value. */
630 if (e->amount < (effect_type_amount(e->type) * (int)s->knowledge))
631 {
632 e->amount += effect_type_amount(e->type);
633 log_add_entry(nlarn->log, "You have extended the power of %s.",
634 spell_name(s));
635
636 /* force recalculation of burdened
637 status if extending strength */
638 if (e->type == ET_INC_STR)
639 {
640 player_inv_weight_recalc(p->inventory, NULL);
641 }
642 }
643 else
644 {
645 /* maximum reached -> indicate failure */
646 log_add_entry(nlarn->log, "You have already extended the "
647 "power of %s to the extent of your knowledge.",
648 spell_name(s));
649
650 return FALSE;
651 }
652 }
653 else if (effect_type_inc_duration(e->type))
654 {
655 /* The duration of this effect can be incremented.
656 * Increase the duration of the effect up to the base
657 * effect duration * spell knowledge value. */
658 if (e->turns + effect_type_duration(e->type)
659 < (effect_type_duration(e->type) * s->knowledge))
660 {
661 e->turns += effect_type_duration(e->type);
662 log_add_entry(nlarn->log, "You have extended the duration "
663 "of %s.", spell_name(s));
664 }
665 else
666 {
667 /* maximum reached -> indicate failure */
668 log_add_entry(nlarn->log, "You have already extended the "
669 "duration of %s to the extent of your knowledge.",
670 spell_name(s));
671
672 return FALSE;
673 }
674 }
675
676 return TRUE;
677 }
678
679 e = effect_new(spell_effect(s));
680
681 /* make effects that are permanent by default non-permanent */
682 /* unless it is the spell of healing, which does work this way */
683 if ((e->turns == 1) && (e->type != ET_INC_HP))
684 {
685 e->turns = 100 * s->knowledge;
686 }
687
688 if (e->type == ET_INC_HP)
689 {
690 e->amount *= s->knowledge;
691 }
692
693 player_effect_add(p, e);
694
695 return TRUE;
696 }
697
spell_type_point(spell * s,struct player * p)698 static gboolean spell_type_point(spell *s, struct player *p)
699 {
700 monster *m = NULL;
701 position pos;
702 effect *e;
703 char buffer[61];
704 int amount = 0;
705
706 g_assert(s != NULL && p != NULL && (spell_type(s) == SC_POINT));
707
708 g_snprintf(buffer, 60, "Select a target for %s.", spell_name(s));
709
710 /* Allow non-visible positions if the player is blinded. */
711 pos = display_get_position(p, buffer, FALSE, FALSE, 0, FALSE,
712 !player_effect(p, ET_BLINDNESS));
713
714 /* player pressed ESC */
715 if (!pos_valid(pos))
716 {
717 log_add_entry(nlarn->log, "Aborted.");
718 return FALSE;
719 }
720
721 if (pos_identical(pos, p->pos))
722 {
723 log_add_entry(nlarn->log, "This spell only works on monsters.");
724 return FALSE;
725 }
726
727 /* When the player is blinded, check if the position can be reached.
728 As it is possible to target any position, it might be a position
729 the player could not target under normal circumstances. */
730 if (player_effect(p, ET_BLINDNESS) &&
731 !map_pos_is_visible(game_map(nlarn, Z(p->pos)), p->pos, pos))
732 {
733 /* be sure to waste the MPs and give no hints. */
734 return TRUE;
735 }
736
737 m = map_get_monster_at(game_map(nlarn, Z(p->pos)), pos);
738
739 if (!m)
740 {
741 if (s->id == SP_DRY)
742 return try_drying_ground(pos);
743
744 if (!player_effect(p, ET_BLINDNESS))
745 {
746 log_add_entry(nlarn->log, "The is no monster there.");
747 return FALSE;
748 }
749 else
750 {
751 /* The spell didn't do anything, but as the player is blinded
752 assume it was targeted at the position intentionally probing
753 for targets. */
754 return TRUE;
755 }
756 }
757
758 switch (s->id)
759 {
760 /* charm monster */
761 case SP_CHM:
762 if ((rand_m_n(5, 30) * monster_level(m) - player_get_wis(p)) < 30)
763 {
764 e = effect_new(spell_effect(s));
765 e->turns *= s->knowledge;
766 monster_effect_add(m, e);
767 }
768 else
769 {
770 spell_print_failure_message(s, m);
771 }
772
773 break; /* SP_CHM */
774
775 /* dehydration */
776 case SP_DRY:
777 amount = (100 * s->knowledge) + p->level;
778 spell_print_success_message(s, m);
779 monster_damage_take(m, damage_new(DAM_MAGICAL, ATT_MAGIC, amount,
780 DAMO_PLAYER, p));
781 break; /* SP_DRY */
782
783 /* drain life */
784 case SP_DRL:
785 amount = min(p->hp - 1, (int)p->hp_max / 2);
786
787 spell_print_success_message(s, m);
788 monster_damage_take(m, damage_new(DAM_MAGICAL, ATT_MAGIC, amount,
789 DAMO_PLAYER, p));
790
791 player_damage_take(p, damage_new(DAM_MAGICAL, ATT_MAGIC, amount,
792 DAMO_PLAYER, NULL), PD_SPELL, SP_DRL);
793
794 break; /* SP_DRL */
795
796 /* finger of death */
797 case SP_FGR:
798 {
799 // Lower chances of working against undead and demons.
800 const int roll = (monster_flags(m, UNDEAD) ? 40 :
801 monster_flags(m, DEMON) ? 30 : 20);
802
803 if ((player_get_wis(p) + s->knowledge) > (guint)rand_m_n(10, roll))
804 {
805 spell_print_success_message(s, m);
806 monster_damage_take(m, damage_new(DAM_MAGICAL, ATT_MAGIC, 2000,
807 DAMO_PLAYER, p));
808 }
809 else
810 spell_print_failure_message(s, m);
811 break; /* SP_FGR */
812 }
813
814 /* polymorph */
815 case SP_PLY:
816 if (chance(5 * (monster_level(m) - 2 * s->knowledge)))
817 {
818 /* It didn't work */
819 spell_print_failure_message(s, m);
820 }
821 else
822 {
823 monster_polymorph(m);
824 }
825 break;
826
827 /* teleport */
828 case SP_TEL:
829 if (monster_in_sight(m))
830 {
831 log_add_entry(nlarn->log, "The %s disappears.",
832 monster_name(m));
833 }
834
835 map *mmap = game_map(nlarn, Z(monster_pos(m)));
836 monster_pos_set(m, mmap, map_find_space(mmap, LE_MONSTER, FALSE));
837 break; /* SP_TEL */
838
839 default:
840 /* spell has an effect, add that to the monster */
841 g_assert(spell_effect(s) != ET_NONE);
842
843 e = effect_new(spell_effect(s));
844
845 if (!e->amount)
846 {
847 e->amount = p->intelligence;
848 }
849
850 e->amount *= s->knowledge;
851 e = monster_effect_add(m, e);
852
853 if (e)
854 spell_print_success_message(s, m);
855 else
856 spell_print_failure_message(s, m);
857
858 break;
859 }
860
861 return TRUE;
862 }
863
spell_type_ray(spell * s,struct player * p)864 static gboolean spell_type_ray(spell *s, struct player *p)
865 {
866 g_assert(s != NULL && p != NULL && (spell_type(s) == SC_RAY));
867
868 char buffer[61];
869
870 g_snprintf(buffer, 60, "Select a target for the %s.", spell_name(s));
871 /* Allow non-visible positions if the player is blinded. */
872 position target = display_get_position(p, buffer, TRUE, FALSE, 0, FALSE,
873 !player_effect(p, ET_BLINDNESS));
874
875 /* player pressed ESC */
876 if (!pos_valid(target))
877 {
878 log_add_entry(nlarn->log, "Aborted.");
879 return FALSE;
880 }
881
882 if (pos_identical(target, p->pos))
883 {
884 log_add_entry(nlarn->log, "Why would you want to do that?");
885 return FALSE;
886 }
887
888 damage_originator damo = { DAMO_PLAYER, p };
889 damage *dam = damage_new(spells[s->id].damage_type, ATT_MAGIC, 0,
890 damo.ot, damo.originator);
891
892 /* determine amount of damage */
893 switch (s->id)
894 {
895 case SP_MLE:
896 dam->amount = (1 + rand_1n(5)) * s->knowledge + p->level;
897 break;
898
899 case SP_SSP:
900 dam->amount = (2 + rand_1n(10)) * s->knowledge + p->level;
901 break;
902
903 case SP_CLD:
904 dam->amount = (3 + rand_1n(15)) * s->knowledge + p->level;
905 break;
906
907 case SP_LIT:
908 dam->amount = (4 + rand_1n(20)) * s->knowledge + p->level;
909 break;
910 default:
911 /* this shouldn't happen */
912 break;
913 }
914
915 /* throw a ray to the selected target */
916 map_trajectory(p->pos, target, &damo, spell_traj_pos_hit,
917 s, dam, TRUE, '*', spell_colour(s), TRUE);
918
919 /* The callback functions give a copy of the damage to the specific
920 functions, thus the original has to be destroyed here. */
921 damage_free(dam);
922
923 return TRUE;
924 }
925
spell_type_flood(spell * s,struct player * p)926 static gboolean spell_type_flood(spell *s, struct player *p)
927 {
928 position pos;
929 char buffer[81];
930 int radius = 0;
931 int amount = 0;
932 map_tile_t type = LT_NONE;
933
934 g_assert(s != NULL && p != NULL && (spell_type(s) == SC_FLOOD));
935
936 g_snprintf(buffer, 60, "Where do you want to place the %s?", spell_name(s));
937 pos = display_get_position(p, buffer, FALSE, FALSE, 0, FALSE, TRUE);
938
939 /* player pressed ESC */
940 if (!pos_valid(pos))
941 {
942 log_add_entry(nlarn->log, "Aborted.");
943 return FALSE;
944 }
945
946 switch (s->id)
947 {
948 case SP_CKL:
949 radius = 3;
950 type = LT_CLOUD;
951 amount = (10 * s->knowledge) + p->level;
952 break;
953
954 case SP_FLO:
955 radius = 4;
956 type = LT_WATER;
957 amount = (25 * s->knowledge) + p->level;
958 break;
959
960 case SP_MFI:
961 radius = 4;
962 type = LT_FIRE;
963 amount = (15 * s->knowledge) + p->level;
964 break;
965
966 default:
967 /* this shouldn't happen */
968 break;
969 }
970
971 area *obstacles = map_get_obstacles(game_map(nlarn, Z(pos)), pos, radius, FALSE);
972 area *range = area_new_circle_flooded(pos, radius, obstacles);
973
974 if (area_pos_get(range, p->pos)
975 && !display_get_yesno("The spell is going to hit you. " \
976 "Cast anyway?", NULL, NULL, NULL))
977 {
978 log_add_entry(nlarn->log, "Aborted.");
979 area_destroy(range);
980 return FALSE;
981 }
982
983 map_set_tiletype(game_map(nlarn, Z(pos)), range, type, amount);
984 area_destroy(range);
985
986 return TRUE;
987 }
988
spell_type_blast(spell * s,struct player * p)989 static gboolean spell_type_blast(spell *s, struct player *p)
990 {
991 g_assert(s != NULL && p != NULL && (spell_type(s) == SC_BLAST));
992
993 area *ball;
994 position pos;
995 char buffer[61];
996 int amount = 0;
997 int radius = 0;
998 damage_originator damo = { DAMO_PLAYER, p };
999 map *cmap = game_map(nlarn, Z(p->pos));
1000
1001 switch (s->id)
1002 {
1003 /* currently there is only the fireball */
1004 case SP_BAL:
1005 default:
1006 radius = 2;
1007 amount = (3 + rand_1n(15)) * s->knowledge + p->level;
1008 break;
1009 }
1010
1011 g_snprintf(buffer, 60, "Point to the center of the %s.", spell_name(s));
1012 /* Allow non-visible positions if the player is blinded. */
1013 pos = display_get_position(p, buffer, FALSE, TRUE, radius, FALSE,
1014 !player_effect(p, ET_BLINDNESS));
1015
1016 /* player pressed ESC */
1017 if (!pos_valid(pos))
1018 {
1019 log_add_entry(nlarn->log, "Aborted.");
1020 return FALSE;
1021 }
1022
1023 /* get the affected area to determine if the player would be hit */
1024 ball = area_new_circle_flooded(pos, radius, map_get_obstacles(cmap, pos,
1025 radius, TRUE));
1026
1027 gboolean player_affected = area_pos_get(ball, p->pos);
1028 area_destroy(ball);
1029
1030 if (player_affected
1031 && !display_get_yesno("The spell is going to hit you. Cast anyway?", NULL, NULL, NULL))
1032 {
1033 log_add_entry(nlarn->log, "Aborted.");
1034 return FALSE;
1035 }
1036
1037 damage *dam = damage_new(spells[s->id].damage_type, ATT_MAGIC,
1038 amount, DAMO_PLAYER, p);
1039 area_blast(pos, radius, &damo, spell_area_pos_hit, s, dam, '*', spell_colour(s));
1040
1041 /* destroy the damage as the callbacks deliver a copy */
1042 damage_free(dam);
1043
1044 return TRUE;
1045 }
1046
spell_alter_reality(spell * s,player * p)1047 static gboolean spell_alter_reality(spell *s, player *p)
1048 {
1049 map *nlevel;
1050 position pos = { { 0, 0, Z(p->pos) } };
1051
1052 if (Z(p->pos) == 0)
1053 {
1054 log_add_entry(nlarn->log, spell_msg_fail(s));
1055 return FALSE;
1056 }
1057
1058 /* reset the player's memory of the current map */
1059 memset(&player_memory_of(p, pos), 0,
1060 MAP_MAX_Y * MAP_MAX_X * sizeof(player_tile_memory));
1061
1062 map_destroy(game_map(nlarn, Z(p->pos)));
1063
1064 /* create new map */
1065 nlevel = nlarn->maps[Z(p->pos)] = map_new(Z(p->pos), nlarn_mazefile);
1066
1067 /* reposition player (if needed) */
1068 if (!map_pos_passable(nlevel, p->pos))
1069 {
1070 p->pos = map_find_space(nlevel, LE_MONSTER, FALSE);
1071 }
1072
1073 return TRUE;
1074 }
1075
spell_create_monster(spell * s,struct player * p)1076 gboolean spell_create_monster(spell *s __attribute__((unused)), struct player *p)
1077 {
1078 position mpos;
1079
1080 /* this spell doesn't work in town */
1081 if (Z(p->pos) == 0)
1082 {
1083 log_add_entry(nlarn->log, "Nothing happens.");
1084 return FALSE;
1085 }
1086
1087 /* try to find a space for the monster near the player */
1088 mpos = map_find_space_in(game_map(nlarn, Z(p->pos)),
1089 rect_new_sized(p->pos, 2), LE_MONSTER, FALSE);
1090
1091 if (pos_valid(mpos))
1092 {
1093 monster_new_by_level(mpos);
1094 return TRUE;
1095 }
1096 else
1097 {
1098 log_add_entry(nlarn->log, "You feel failure.");
1099 return FALSE;
1100 }
1101 }
1102
spell_create_sphere(spell * s,struct player * p)1103 static gboolean spell_create_sphere(spell *s, struct player *p)
1104 {
1105 g_assert(p != NULL);
1106
1107 position pos = display_get_new_position(p, p->pos,
1108 "Where do you want to place the sphere?",
1109 FALSE, FALSE, FALSE, 0, TRUE, TRUE);
1110
1111 if (pos_valid(pos))
1112 {
1113 sphere *sph = sphere_new(pos, p, p->level * 10 * s->knowledge);
1114 g_ptr_array_add(nlarn->spheres, sph);
1115
1116 return TRUE;
1117 }
1118 else
1119 {
1120 log_add_entry(nlarn->log, "Huh?");
1121
1122 return FALSE;
1123 }
1124 }
1125
spell_cure_poison(spell * s,struct player * p)1126 static gboolean spell_cure_poison(spell *s __attribute__((unused)), struct player *p)
1127 {
1128 effect *eff = NULL;
1129
1130 g_assert(p != NULL);
1131
1132 if ((eff = player_effect_get(p, ET_POISON)))
1133 {
1134 player_effect_del(p, eff);
1135 return TRUE;
1136 }
1137 else
1138 {
1139 log_add_entry(nlarn->log, "You weren't even poisoned!");
1140 return FALSE;
1141 }
1142 }
1143
spell_cure_blindness(spell * s,struct player * p)1144 static gboolean spell_cure_blindness(spell *s __attribute__((unused)), struct player *p)
1145 {
1146 effect *eff = NULL;
1147
1148 g_assert(p != NULL);
1149
1150 if ((eff = player_effect_get(p, ET_BLINDNESS)))
1151 {
1152 player_effect_del(p, eff);
1153 return TRUE;
1154 }
1155 else
1156 {
1157 log_add_entry(nlarn->log, "You weren't even blinded!");
1158 return FALSE;
1159 }
1160 }
1161
spell_phantasmal_forces(spell * s,struct player * p)1162 static gboolean spell_phantasmal_forces(spell *s, struct player *p)
1163 {
1164 position mpos;
1165 monster *m = NULL;
1166
1167 mpos = display_get_position(p, "Choose a target for phantasmal forces.",
1168 FALSE, FALSE, 0, TRUE, TRUE);
1169
1170 if (!pos_valid(mpos))
1171 {
1172 return FALSE;
1173 }
1174
1175 m = map_get_monster_at(game_map(nlarn, Z(mpos)), mpos);
1176
1177 if (m == NULL)
1178 {
1179 return FALSE;
1180 }
1181
1182 if ((player_get_int(p) + s->knowledge) > monster_int(m))
1183 {
1184 if (monster_in_sight(m))
1185 {
1186 log_add_entry(nlarn->log, spell_msg_succ(s), monster_name(m));
1187 }
1188
1189 monster_effect_add(m, effect_new(ET_SCARED));
1190 return TRUE;
1191 }
1192 else
1193 {
1194 if (monster_in_sight(m))
1195 {
1196 log_add_entry(nlarn->log, spell_msg_fail(s), monster_name(m));
1197 }
1198 return FALSE;
1199 }
1200 }
1201
spell_scare_monsters(spell * s,struct player * p)1202 static gboolean spell_scare_monsters(spell *s, struct player *p)
1203 {
1204 monster *m;
1205 int count = 0;
1206 position pos = pos_invalid;
1207 map *cmap = game_map(nlarn, Z(p->pos));
1208 Z(pos) = Z(p->pos);
1209
1210 /* the radius of this spell is determined by the player's level of
1211 spell knowledge */
1212 area *a = area_new_circle(p->pos, 1 + s->knowledge, FALSE);
1213
1214 for (int y = a->start_y; y < a->start_y + a->size_y; y++)
1215 {
1216 Y(pos) = y;
1217
1218 for (int x = a->start_x; x < a->start_x + a->size_x; x++)
1219 {
1220 X(pos) = x;
1221
1222 if (!pos_valid(pos))
1223 {
1224 /* possibly reached the level boundary */
1225 continue;
1226 }
1227
1228 m = map_get_monster_at(cmap, pos);
1229
1230 /* no monster at position? */
1231 if (m == NULL) continue;
1232
1233 /* there is a monster, check if it is affected */
1234 if (player_get_int(p) > (int)monster_int(m))
1235 {
1236 monster_effect_add(m, effect_new(ET_SCARED));
1237 count++;
1238 }
1239 }
1240 }
1241
1242 /* free allocated memory */
1243 area_destroy(a);
1244
1245 return (count > 0);
1246 }
1247
spell_summon_demon(spell * s,struct player * p)1248 static gboolean spell_summon_demon(spell *s, struct player *p)
1249 {
1250 monster *demon;
1251 position pos;
1252
1253 /* find a place near the player for the demon servant */
1254 pos = map_find_space_in(game_map(nlarn, Z(p->pos)),
1255 rect_new_sized(p->pos, 2),
1256 LE_MONSTER, FALSE);
1257
1258 if (!pos_valid(pos))
1259 return FALSE;
1260
1261 /* generate a demon */
1262 demon = monster_new(min(MT_DEMONLORD_I + (s->knowledge - 1),
1263 MT_DEMONLORD_VII), pos, NULL);
1264
1265 /* turn the demon into a servant */
1266 monster_update_action(demon, MA_SERVE);
1267
1268 return TRUE;
1269 }
1270
spell_make_wall(spell * s,player * p)1271 static gboolean spell_make_wall(spell *s __attribute__((unused)), player *p)
1272 {
1273 position pos;
1274
1275 pos = display_get_new_position(p, p->pos,
1276 "Select a position where you want to place a wall.",
1277 FALSE, FALSE, FALSE, 0, FALSE, TRUE);
1278
1279 if (pos_identical(pos, p->pos))
1280 {
1281 log_add_entry(nlarn->log, "You are actually standing there.");
1282 return FALSE;
1283 }
1284 else if (!pos_valid(pos))
1285 {
1286 log_add_entry(nlarn->log, "No wall today.");
1287 return FALSE;
1288 }
1289
1290 map *pmap = game_map(nlarn, Z(p->pos));
1291 if (map_tiletype_at(pmap, pos) != LT_WALL)
1292 {
1293 map_tile *tile = map_tile_at(pmap, pos);
1294
1295 /* destroy all items at that position */
1296 if (tile->ilist != NULL)
1297 {
1298 inv_destroy(tile->ilist, TRUE);
1299 tile->ilist = NULL;
1300 }
1301
1302 sobject_destroy_at(p, pmap, pos);
1303
1304 log_add_entry(nlarn->log, "You have created a wall.");
1305
1306 tile->type = tile->base_type = LT_WALL;
1307
1308 monster *m;
1309 if ((m = map_get_monster_at(pmap, pos)))
1310 {
1311 if (monster_type(m) != MT_XORN)
1312 {
1313 if (monster_in_sight(m))
1314 {
1315 /* briefly display the new monster before it dies */
1316 display_paint_screen(nlarn->p);
1317 g_usleep(250000);
1318
1319 log_add_entry(nlarn->log, "The %s is trapped in the wall!",
1320 monster_get_name(m));
1321 }
1322
1323 monster_die(m, nlarn->p);
1324 }
1325 }
1326
1327 return TRUE;
1328 }
1329 else
1330 {
1331 log_add_entry(nlarn->log, "There was a wall already..");
1332 return FALSE;
1333 }
1334 }
1335
spell_vaporize_rock(spell * sp,player * p)1336 gboolean spell_vaporize_rock(spell *sp __attribute__((unused)), player *p)
1337 {
1338 monster *m;
1339 position pos;
1340 map *pmap = game_map(nlarn, Z(p->pos));
1341
1342 pos = display_get_new_position(p, p->pos,
1343 "What do you want to vaporize?",
1344 FALSE, FALSE, FALSE, 0, FALSE, TRUE);
1345
1346 if (!pos_valid(pos))
1347 {
1348 log_add_entry(nlarn->log, "So you chose not to vaporize anything.");
1349 return FALSE;
1350 }
1351
1352 if (map_tiletype_at(pmap, pos) == LT_WALL)
1353 {
1354 map_tiletype_set(pmap, pos, LT_FLOOR);
1355 p->stats.vandalism++;
1356 }
1357
1358 if ((m = map_get_monster_at(pmap, pos)))
1359 {
1360 /* xorns take damage from vpr */
1361 if (monster_type(m) == MT_XORN)
1362 {
1363 monster_damage_take(m, damage_new(DAM_PHYSICAL, ATT_NONE,
1364 divert(200, 10), DAMO_PLAYER, p));
1365 }
1366 else if (monster_in_sight(m))
1367 {
1368 log_add_entry(nlarn->log, "The %s can't be vaporized.",
1369 monster_get_name(m));
1370 }
1371 }
1372
1373 sobject_destroy_at(p, pmap, pos);
1374
1375 return TRUE;
1376 }
1377
1378
book_desc(spell_id book_id)1379 char *book_desc(spell_id book_id)
1380 {
1381 g_assert(book_id < SP_MAX);
1382 return (char *)book_obfuscation[nlarn->book_desc_mapping[book_id]].desc;
1383 }
1384
book_weight(item * book)1385 int book_weight(item *book)
1386 {
1387 g_assert (book != NULL && book->type == IT_BOOK);
1388 return book_obfuscation[nlarn->book_desc_mapping[book->id]].weight;
1389 }
1390
book_colour(item * book)1391 int book_colour(item *book)
1392 {
1393 g_assert (book != NULL && book->type == IT_BOOK);
1394 return book_obfuscation[nlarn->book_desc_mapping[book->id]].colour;
1395 }
1396
book_read(struct player * p,item * book)1397 item_usage_result book_read(struct player *p, item *book)
1398 {
1399 item_usage_result result = { FALSE, FALSE };
1400 gchar *desc = item_describe(book, player_item_known(p, book),
1401 TRUE, TRUE);
1402
1403 if (player_effect(p, ET_BLINDNESS))
1404 {
1405 log_add_entry(nlarn->log, "As you are blind you can't read %s.",
1406 desc);
1407
1408 g_free(desc);
1409 return result;
1410 }
1411
1412 if (book->cursed && book->blessed_known)
1413 {
1414 log_add_entry(nlarn->log, "You'd rather not read this cursed book.");
1415 g_free(desc);
1416 return result;
1417 }
1418
1419 log_add_entry(nlarn->log, "You start reading %s.", desc);
1420
1421 /*
1422 * Try to complete reading the book.
1423 * Reading a book takes ten turns per spell level.
1424 */
1425 if (!player_make_move(p, 10 * spell_level_by_id(book->id),
1426 TRUE, "reading %s", desc))
1427 {
1428 /* the action has been aborted */
1429 g_free(desc);
1430 return result;
1431 }
1432
1433 g_free(desc);
1434
1435 /* the book has successfully been read - increase number of books read */
1436 p->stats.books_read++;
1437
1438 /* cursed spell books have nasty effects */
1439 if (book->cursed)
1440 {
1441 log_add_entry(nlarn->log, "There was something wrong with this book! " \
1442 "It crumbles to dust.");
1443
1444 player_mp_lose(p, rand_0n(p->mp));
1445 result.used_up = TRUE;
1446 }
1447 else
1448 {
1449 switch (spell_learn(p, book->id))
1450 {
1451 case 0:
1452 log_add_entry(nlarn->log, "You cannot understand the content of this book.");
1453 // Bad pun.
1454 if (strcmp(book_desc(book->id), "dull") == 0)
1455 log_add_entry(nlarn->log, "It seems really boring, though.");
1456 break;
1457
1458 case 1:
1459 /* learnt spell */
1460 log_add_entry(nlarn->log, "You master the spell %s.", book_name(book));
1461
1462 result.used_up = TRUE;
1463 result.identified = TRUE;
1464 break;
1465
1466 default:
1467 /* improved knowledge of spell */
1468 log_add_entry(nlarn->log, "You improved your knowledge of the spell %s.",
1469 book_name(book));
1470
1471 result.used_up = TRUE;
1472 result.identified = TRUE;
1473 break;
1474 }
1475
1476 /* five percent chance to increase intelligence */
1477 if (result.used_up && chance(2))
1478 {
1479 log_add_entry(nlarn->log, "Reading makes you ingenious.");
1480 p->intelligence++;
1481 }
1482 }
1483
1484 return result;
1485 }
1486
spell_cast(player * p,spell * s)1487 static int spell_cast(player *p, spell *s)
1488 {
1489 int turns = 0;
1490 gboolean well_done = FALSE;
1491
1492 /* insufficient mana */
1493 if (p->mp < spell_level(s))
1494 {
1495 log_add_entry(nlarn->log, "You lack the power to cast %s.",
1496 spell_name(s));
1497
1498 return 0;
1499 }
1500 else if (spell_success_value(p, s) < 1)
1501 {
1502 log_add_entry(nlarn->log, "This spell is too difficult for you.");
1503 return 0;
1504 }
1505
1506 log_add_entry(nlarn->log, "You cast %s.", spell_name(s));
1507
1508 /* time usage */
1509 turns = 1;
1510
1511 /* bad luck, low intelligence */
1512 if (chance(1) || spell_success_value(p, s) < (int)rand_1n(16))
1513 {
1514 log_add_entry(nlarn->log, "It didn't work!");
1515 player_mp_lose(p, spell_level(s));
1516
1517 return turns;
1518 }
1519
1520 /* call the spell's function */
1521 well_done = spells[s->id].function(s, p);
1522
1523 if (!well_done)
1524 return 0;
1525
1526 /* spell has been cast successfully, set mp usage accordingly */
1527 player_mp_lose(p, spell_level(s));
1528
1529 /* increase number of spells cast */
1530 p->stats.spells_cast++;
1531
1532 /* increase usage counter for this specific spell */
1533 s->used++;
1534
1535 return turns;
1536 }
1537
spell_print_success_message(spell * s,monster * m)1538 static void spell_print_success_message(spell *s, monster *m)
1539 {
1540 g_assert(s != NULL && m != NULL);
1541
1542 /* invisible monster -> no message */
1543 if (!monster_in_sight(m))
1544 {
1545 log_add_entry(nlarn->log, "You think you've hit something.");
1546 return;
1547 }
1548
1549 /* no message defined */
1550 if (spell_msg_succ(s) == NULL)
1551 return;
1552
1553 log_add_entry(nlarn->log, spell_msg_succ(s), monster_get_name(m));
1554 }
1555
spell_print_failure_message(spell * s,monster * m)1556 static void spell_print_failure_message(spell *s, monster *m)
1557 {
1558 g_assert(s != NULL && m != NULL);
1559
1560 /* invisible monster -> no message */
1561 if (!monster_in_sight(m))
1562 return;
1563
1564 /* no message defined */
1565 if (spell_msg_fail(s) == NULL)
1566 return;
1567
1568 log_add_entry(nlarn->log, spell_msg_fail(s), monster_get_name(m));
1569 }
1570
count_adjacent_water_squares(position pos)1571 static int count_adjacent_water_squares(position pos)
1572 {
1573 position p = pos_invalid;
1574 Z(p) = Z(pos);
1575
1576 int count = 0;
1577 for (X(p) = X(pos) - 1; X(p) <= X(pos) + 1; X(p)++)
1578 for (Y(p) = Y(pos) - 1; Y(p) <= Y(pos) + 1; Y(p)++)
1579 {
1580 if (!pos_valid(p))
1581 continue;
1582
1583 if (pos_identical(p, pos))
1584 continue;
1585
1586 const map_tile *tile = map_tile_at(game_map(nlarn, Z(pos)), p);
1587 if (tile->type == LT_WATER || tile->type == LT_DEEPWATER)
1588 count++;
1589 }
1590
1591 return count;
1592 }
1593
try_drying_ground(position pos)1594 static int try_drying_ground(position pos)
1595 {
1596 map_tile *tile = map_tile_at(game_map(nlarn, Z(pos)), pos);
1597 if (tile->type == LT_DEEPWATER)
1598 {
1599 /* success chance depends on number of adjacent water squares */
1600 const int adj_water = count_adjacent_water_squares(pos);
1601 if ((int)rand_1n(9) <= adj_water)
1602 {
1603 log_add_entry(nlarn->log, "Nothing happens.");
1604 return FALSE;
1605 }
1606
1607 tile->type = LT_WATER;
1608 log_add_entry(nlarn->log, "The water is more shallow now.");
1609 return TRUE;
1610 }
1611 else if (tile->type == LT_WATER)
1612 {
1613 /* success chance depends on number of adjacent water squares */
1614 const int adj_water = count_adjacent_water_squares(pos);
1615 if ((int)rand_1n(9) <= adj_water)
1616 {
1617 log_add_entry(nlarn->log, "Nothing happens.");
1618 return FALSE;
1619 }
1620
1621 if (tile->base_type == LT_NONE)
1622 tile->type = LT_DIRT;
1623 else
1624 tile->type = tile->base_type;
1625
1626 if (tile->timer)
1627 tile->timer = 0;
1628
1629 log_add_entry(nlarn->log, "The water evaporates!");
1630 return TRUE;
1631 }
1632 return FALSE;
1633 }
1634
spell_traj_pos_hit(const GList * traj,const damage_originator * damo,gpointer data1,gpointer data2)1635 static gboolean spell_traj_pos_hit(const GList *traj,
1636 const damage_originator *damo,
1637 gpointer data1, gpointer data2)
1638 {
1639 position pos;
1640 pos_val(pos) = GPOINTER_TO_UINT(traj->data);
1641
1642 return spell_area_pos_hit(pos, damo, data1, data2);
1643 }
1644
spell_area_pos_hit(position pos,const damage_originator * damo,gpointer data1,gpointer data2)1645 static gboolean spell_area_pos_hit(position pos,
1646 const damage_originator *damo,
1647 gpointer data1, gpointer data2)
1648 {
1649 spell *sp = (spell *)data1;
1650 damage *dam = (damage *)data2;
1651 map *cmap = game_map(nlarn, Z(pos));
1652 sobject_t mst = map_sobject_at(cmap, pos);
1653 monster *m = map_get_monster_at(cmap, pos);
1654 item_erosion_type iet;
1655 gboolean terminated = FALSE;
1656
1657 /* determine if the spell causes item erosion */
1658 switch (sp->id)
1659 {
1660 case SP_BAL:
1661 iet = IET_BURN;
1662 break;
1663
1664 default:
1665 iet = IET_NONE;
1666 break;
1667 }
1668
1669 /* The spell hit a sobject. */
1670 if (mst > LS_NONE)
1671 {
1672 if (mst == LS_MIRROR && fov_get(nlarn->p->fv, pos))
1673 {
1674 /* reflection is handled in map_trajectory, but we need
1675 to generate a message here if the mirror is visible */
1676 log_add_entry(nlarn->log, "The mirror reflects the %s!",spell_name(sp));
1677 return terminated;
1678 }
1679
1680 if (mst == LS_STATUE
1681 && (sp->id == SP_BAL || sp->id == SP_LIT)
1682 && (game_difficulty(nlarn) <= 2))
1683 {
1684 /* fireball and lightning destroy statues up to diff. level 2 */
1685 sobject_destroy_at(damo->originator, cmap, pos);
1686 terminated = TRUE;
1687 }
1688
1689 if (mst == LS_CLOSEDDOOR && (spell_level(sp) > 2))
1690 {
1691 /* Blast the door away */
1692 sobject_destroy_at(damo->originator, cmap, pos);
1693 terminated = TRUE;
1694 }
1695 }
1696
1697 /* The spell hit a monster */
1698 if (m != NULL)
1699 {
1700 spell_print_success_message(sp, m);
1701
1702 /* erode the monster's inventory */
1703 if (iet > IET_NONE)
1704 inv_erode(monster_inv(m), iet, FALSE, NULL);
1705
1706 monster_damage_take(m, damage_copy(dam));
1707
1708 /*
1709 * If the monster is at least of human size, the spell stops at
1710 * the monster, otherwise it passes and may hit other monsters
1711 */
1712 if (monster_size(m) >= MEDIUM)
1713 terminated = TRUE;
1714 }
1715
1716 /* The spell hit the player */
1717 if (pos_identical(nlarn->p->pos, pos))
1718 {
1719 if (player_effect(nlarn->p, ET_REFLECTION))
1720 {
1721 /* The player reflects the spell. Actual handling of the reflection
1722 is done in map_trajectory, just give a message here. */
1723 log_add_entry(nlarn->log, "Your amulet reflects the %s!", spell_name(sp));
1724 }
1725 else
1726 {
1727 int evasion = nlarn->p->level/(2+game_difficulty(nlarn)/2)
1728 + player_get_dex(nlarn->p)
1729 - 10
1730 - game_difficulty(nlarn);
1731
1732 // Automatic hit if paralysed or overstrained.
1733 if (player_effect(nlarn->p, ET_PARALYSIS)
1734 || player_effect(nlarn->p, ET_OVERSTRAINED))
1735 evasion = 0;
1736 else
1737 {
1738 if (player_effect(nlarn->p, ET_BLINDNESS))
1739 evasion /= 4;
1740 if (player_effect(nlarn->p, ET_CONFUSION))
1741 evasion /= 2;
1742 if (player_effect(nlarn->p, ET_BURDENED))
1743 evasion /= 2;
1744 }
1745
1746 if (evasion >= (int)rand_1n(21))
1747 {
1748 if (!player_effect(nlarn->p, ET_BLINDNESS))
1749 {
1750 log_add_entry(nlarn->log, "The %s whizzes by you!", spell_name(sp));
1751 }
1752
1753 /* missed */
1754 terminated = FALSE;
1755 }
1756 else
1757 {
1758 log_add_entry(nlarn->log, "The %s hits you!", spell_name(sp));
1759 player_damage_take(nlarn->p, damage_copy(dam), PD_SPELL, sp->id);
1760
1761 /* erode the player's inventory */
1762 if (iet > IET_NONE)
1763 {
1764 /*
1765 * Filter equipped and exposed items, e.g.
1766 * a body armour will not be affected by erosion
1767 * when the player wears a cloak over it.
1768 */
1769 inv_erode(&(nlarn->p->inventory), iet, TRUE,
1770 player_item_filter_unequippable);
1771 }
1772
1773 /* hit */
1774 terminated = TRUE;
1775 }
1776 } /* The spell wasn't reflected */
1777 } /* The spell hit the player's position */
1778
1779 if (iet > IET_NONE && map_ilist_at(cmap, pos))
1780 {
1781 /* there are items at the given map position, erode them */
1782 inv_erode(map_ilist_at(cmap, pos), iet,
1783 fov_get(nlarn->p->fv, pos), NULL);
1784 }
1785
1786 return terminated;
1787 }
1788