ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/spell_effect.C
Revision: 1.3
Committed: Sat Aug 26 23:36:34 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.2: +2 -297 lines
Log Message:
intermediate check-in, per-object events work

File Contents

# User Rev Content
1 elmex 1.1 /*
2     * static char *rcsid_spell_effect_c =
3 root 1.3 * "$Id: spell_effect.C,v 1.2 2006-08-17 20:23:32 root Exp $";
4 elmex 1.1 */
5    
6    
7     /*
8     CrossFire, A Multiplayer game for X-windows
9    
10     Copyright (C) 2002 Mark Wedel & Crossfire Development Team
11     Copyright (C) 1992 Frank Tore Johansen
12    
13     This program is free software; you can redistribute it and/or modify
14     it under the terms of the GNU General Public License as published by
15     the Free Software Foundation; either version 2 of the License, or
16     (at your option) any later version.
17    
18     This program is distributed in the hope that it will be useful,
19     but WITHOUT ANY WARRANTY; without even the implied warranty of
20     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21     GNU General Public License for more details.
22    
23     You should have received a copy of the GNU General Public License
24     along with this program; if not, write to the Free Software
25     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26    
27     The authors can be reached via e-mail at crossfire-devel@real-time.com
28     */
29    
30     #include <global.h>
31     #include <object.h>
32     #include <living.h>
33     #ifndef __CEXTRACT__
34     #include <sproto.h>
35     #endif
36     #include <spells.h>
37     #include <sounds.h>
38    
39     /* cast_magic_storm: This is really used mostly for spell
40     * fumbles at the like. tmp is the object to propogate.
41     * op is what is casting this.
42     */
43     void cast_magic_storm(object *op, object *tmp, int lvl)
44     {
45     if (!tmp) return; /* error */
46     tmp->level=op->level;
47     tmp->x=op->x;
48     tmp->y=op->y;
49     tmp->range+=lvl/5; /* increase the area of destruction */
50     tmp->duration+=lvl/5;
51    
52     /* Put a cap on duration for this - if the player fails in their
53     * apartment, don't want it to go on so long that it kills them
54     * multiple times. Also, damge already increases with level,
55     * so don't really need to increase the duration as much either.
56     */
57     if (tmp->duration>=40) tmp->duration=40;
58     tmp->stats.dam=lvl; /* nasty recoils! */
59     tmp->stats.maxhp=tmp->count; /* tract single parent */
60     insert_ob_in_map(tmp,op->map,op,0);
61    
62     }
63    
64    
65     int recharge(object *op, object *caster, object *spell_ob) {
66     object *wand, *tmp;
67     int ncharges;
68    
69     wand = find_marked_object(op);
70     if(wand == NULL || wand->type != WAND) {
71     new_draw_info(NDI_UNIQUE, 0, op, "You need to mark the wand you want to recharge.");
72     return 0;
73     }
74     if(!(random_roll(0, 3, op, PREFER_HIGH))) {
75     new_draw_info_format(NDI_UNIQUE, 0, op,
76     "The %s vibrates violently, then explodes!",query_name(wand));
77     play_sound_map(op->map, op->x, op->y, SOUND_OB_EXPLODE);
78     esrv_del_item(op->contr, wand->count);
79     remove_ob(wand);
80     free_object(wand);
81     tmp = get_archetype("fireball");
82     tmp->stats.dam = (spell_ob->stats.dam + SP_level_dam_adjust(caster, spell_ob)) / 10;
83     if (!tmp->stats.dam) tmp->stats.dam = 1;
84     tmp->stats.hp = tmp->stats.dam / 2;
85     if (tmp->stats.hp < 2) tmp->stats.hp = 2;
86     tmp->x = op->x;
87     tmp->y = op->y;
88     insert_ob_in_map(tmp, op->map, NULL, 0);
89     return 1;
90     }
91    
92     ncharges = (spell_ob->stats.dam + SP_level_dam_adjust(caster, spell_ob));
93     if (wand->inv && wand->inv->level)
94     ncharges /= wand->inv->level;
95     else {
96     new_draw_info_format(NDI_UNIQUE, 0, op, "Your %s is broken.",
97     query_name(wand));
98     return 0;
99     }
100     if (!ncharges) ncharges = 1;
101    
102     wand->stats.food += ncharges;
103     new_draw_info_format(NDI_UNIQUE, 0, op,
104     "The %s glows with power.",query_name(wand));
105     if(wand->arch && QUERY_FLAG(&wand->arch->clone, FLAG_ANIMATE)) {
106     SET_FLAG(wand, FLAG_ANIMATE);
107     wand->speed = wand->arch->clone.speed;
108     update_ob_speed(wand);
109     }
110     return 1;
111     }
112    
113     /* Create a missile (nonmagic - magic +4). Will either create bolts or arrows
114     * based on whether a crossbow or bow is equiped. If neither, it defaults to
115     * arrows.
116     * Sets the plus based on the casters level. It is also settable with the
117     * invoke command. If the caster attempts to create missiles with too
118     * great a plus, the default is used.
119     * The # of arrows created also goes up with level, so if a 30th level mage
120     * wants LOTS of arrows, and doesn't care what the plus is he could
121     * create nonnmagic arrows, or even -1, etc...
122     */
123    
124     int cast_create_missile(object *op, object *caster,object *spell, int dir, const char *stringarg)
125     {
126     int missile_plus=0, bonus_plus=0;
127     const char *missile_name;
128     object *tmp, *missile;
129     tag_t tag;
130    
131     missile_name = "arrow";
132    
133     for (tmp=op->inv; tmp != NULL; tmp=tmp->below)
134     if (tmp->type == BOW && QUERY_FLAG(tmp, FLAG_APPLIED)) {
135     missile_name=tmp->race;
136     }
137    
138     missile_plus = spell->stats.dam + SP_level_dam_adjust(caster, spell);
139    
140     if (find_archetype(missile_name)==NULL) {
141     LOG(llevDebug, "Cast create_missile: could not find archetype %s\n",
142     missile_name);
143     return 0;
144     }
145     missile = get_archetype(missile_name);
146    
147     if (stringarg) {
148     /* If it starts with a letter, presume it is a description */
149     if (isalpha(*stringarg)) {
150     artifact *al = find_artifactlist(missile->type)->items;
151    
152     for ( ; al != NULL; al=al->next)
153     if (!strcasecmp(al->item->name, stringarg)) break;
154    
155     if (!al) {
156     free_object(missile);
157     new_draw_info_format(NDI_UNIQUE, 0, op ,"No such object %ss of %s", missile_name,
158     stringarg);
159     return 0;
160     }
161     if (al->item->slaying) {
162     free_object(missile);
163     new_draw_info_format(NDI_UNIQUE, 0, op ,"You are not allowed to create %ss of %s",
164     missile_name, stringarg);
165     return 0;
166     }
167     give_artifact_abilities(missile, al->item);
168     /* These special arrows cost something extra. Don't have them also be magical -
169     * otherwise, in most cases, not enough will be created. I don't want to get into
170     * the parsing of having to do both plus and type.
171     */
172     bonus_plus = 1 + (al->item->value / 5);
173     missile_plus=0;
174     } else
175     if (atoi(stringarg) < missile_plus)
176     missile_plus = atoi(stringarg);
177     }
178     if (missile_plus > 4)
179     missile_plus = 4;
180     else if (missile_plus < -4)
181     missile_plus = -4;
182    
183     missile->nrof = spell->duration + SP_level_duration_adjust(caster, spell);
184     missile->nrof -= 3 * (missile_plus + bonus_plus);
185     if (missile->nrof < 1)
186     missile->nrof=1;
187    
188     missile->magic = missile_plus;
189     /* Can't get any money for these objects */
190     missile->value=0;
191    
192     SET_FLAG(missile, FLAG_IDENTIFIED);
193     tag = missile->count;
194    
195     if ( ! cast_create_obj (op, caster, missile, dir) && op->type == PLAYER
196     && ! was_destroyed (missile, tag))
197     {
198     pick_up(op, missile);
199     }
200     return 1;
201     }
202    
203    
204     /* allows the choice of what sort of food object to make.
205     * If stringarg is NULL, it will create food dependent on level --PeterM*/
206     int cast_create_food(object *op,object *caster, object *spell_ob, int dir, const char *stringarg)
207     {
208     int food_value;
209     archetype *at=NULL;
210     object *new_op;
211    
212     food_value=spell_ob->stats.food +
213     + 50 * SP_level_duration_adjust(caster,spell_ob);
214    
215     if(stringarg) {
216     at = find_archetype_by_object_type_name(FOOD, stringarg);
217     if (at == NULL)
218     at = find_archetype_by_object_type_name(DRINK, stringarg);
219     if (at == NULL || at->clone.stats.food > food_value)
220     stringarg = NULL;
221     }
222    
223     if(!stringarg) {
224     archetype *at_tmp;
225    
226     /* We try to find the archetype with the maximum food value.
227     * This removes the dependancy of hard coded food values in this
228     * function, and addition of new food types is automatically added.
229     * We don't use flesh types because the weight values of those need
230     * to be altered from the donor.
231     */
232    
233     /* We assume the food items don't have multiple parts */
234     for (at_tmp=first_archetype; at_tmp!=NULL; at_tmp=at_tmp->next) {
235     if (at_tmp->clone.type==FOOD || at_tmp->clone.type==DRINK) {
236     /* Basically, if the food value is something that is creatable
237     * under the limits of the spell and it is higher than
238     * the item we have now, take it instead.
239     */
240     if (at_tmp->clone.stats.food<=food_value &&
241     (!at || at_tmp->clone.stats.food>at->clone.stats.food))
242     at=at_tmp;
243     }
244     }
245     }
246     /* Pretty unlikely (there are some very low food items), but you never
247     * know
248     */
249     if (!at) {
250     new_draw_info(NDI_UNIQUE, 0, op, "You don't have enough experience to create any food.");
251     return 0;
252     }
253    
254     food_value/=at->clone.stats.food;
255 root 1.3 new_op = arch_to_object (at);
256 elmex 1.1 new_op->nrof = food_value;
257    
258     new_op->value = 0;
259     if (new_op->nrof<1) new_op->nrof = 1;
260    
261     cast_create_obj(op, caster,new_op, dir);
262     return 1;
263     }
264    
265     int probe(object *op, object *caster, object *spell_ob, int dir) {
266     int r, mflags, maxrange;
267     object *tmp;
268     mapstruct *m;
269    
270    
271     if(!dir) {
272     examine_monster(op,op);
273     return 1;
274     }
275     maxrange = spell_ob->range + SP_level_range_adjust(caster, spell_ob);
276     for(r=1;r < maxrange; r++) {
277     sint16 x=op->x+r*freearr_x[dir],y=op->y+r*freearr_y[dir];
278    
279     m = op->map;
280     mflags = get_map_flags(m, &m, x, y, &x, &y);
281    
282     if (mflags & P_OUT_OF_MAP) break;
283    
284     if (!QUERY_FLAG(op, FLAG_WIZCAST) && (mflags & P_NO_MAGIC)) {
285     new_draw_info(NDI_UNIQUE, 0,op,"Something blocks your magic.");
286     return 0;
287     }
288     if (mflags & P_IS_ALIVE) {
289     for(tmp=get_map_ob(m,x,y);tmp!=NULL;tmp=tmp->above)
290     if(QUERY_FLAG(tmp, FLAG_ALIVE)&&(tmp->type==PLAYER||QUERY_FLAG(tmp, FLAG_MONSTER))) {
291     new_draw_info(NDI_UNIQUE, 0,op,"You detect something.");
292     if(tmp->head!=NULL)
293     tmp=tmp->head;
294     examine_monster(op,tmp);
295     return 1;
296     }
297     }
298     }
299     new_draw_info(NDI_UNIQUE, 0,op,"You detect nothing.");
300     return 1;
301     }
302    
303    
304     /* This checks to see if 'pl' is invisible to 'mon'.
305     * does race check, undead check, etc
306     * Returns TRUE if mon can't see pl, false
307     * otherwise. This doesn't check range, walls, etc. It
308     * only checks the racial adjustments, and in fact that
309     * pl is invisible.
310     */
311     int makes_invisible_to(object *pl, object *mon)
312     {
313    
314     if (!pl->invisible) return 0;
315     if (pl->type == PLAYER ) {
316     /* If race isn't set, then invisible unless it is undead */
317     if (!pl->contr->invis_race) {
318     if (QUERY_FLAG(mon, FLAG_UNDEAD)) return 0;
319     return 1;
320     }
321     /* invis_race is set if we get here */
322     if (!strcmp(pl->contr->invis_race, "undead") && is_true_undead(mon))
323     return 1;
324     /* No race, can't be invisible to it */
325     if (!mon->race) return 0;
326     if (strstr(mon->race, pl->contr->invis_race)) return 1;
327     /* Nothing matched above, return 0 */
328     return 0;
329     } else {
330     /* monsters are invisible to everything */
331     return 1;
332     }
333     }
334    
335     /* Makes the player or character invisible.
336     * Note the spells to 'stack', but perhaps in odd ways.
337     * the duration for all is cumulative.
338     * In terms of invis undead/normal invis, it is the last one cast that
339     * will determine if you are invisible to undead or normal monsters.
340     * For improved invis, if you cast it with a one of the others, you
341     * lose the improved part of it, and the above statement about undead/
342     * normal applies.
343     */
344     int cast_invisible(object *op, object *caster, object *spell_ob) {
345     object *tmp;
346    
347     if(op->invisible>1000) {
348     new_draw_info(NDI_UNIQUE, 0,op,"You can not extend the duration of your invisibility any further");
349     return 0;
350     }
351    
352     /* Remove the switch with 90% duplicate code - just handle the differences with
353     * and if statement or two.
354     */
355     op->invisible += spell_ob->duration + SP_level_duration_adjust(caster, spell_ob);
356     /* max duration */
357     if(op->invisible>1000) op->invisible = 1000;
358    
359     if (op->type == PLAYER) {
360     if (op->contr->invis_race) FREE_AND_CLEAR_STR(op->contr->invis_race);
361     if (spell_ob->race)
362     op->contr->invis_race = add_refcount(spell_ob->race);
363     if (QUERY_FLAG(spell_ob, FLAG_MAKE_INVIS))
364     op->contr->tmp_invis=0;
365     else
366     op->contr->tmp_invis=1;
367    
368     op->contr->hidden = 0;
369     }
370     if (makes_invisible_to(op, op))
371     new_draw_info(NDI_UNIQUE, 0,op,"You can't see your hands!");
372     else
373     new_draw_info(NDI_UNIQUE, 0,op,"You feel more transparent!");
374    
375     update_object(op,UP_OBJ_FACE);
376    
377     /* Only search the active objects - only these should actually do
378     * harm to the player.
379     */
380     for (tmp = active_objects; tmp != NULL; tmp = tmp->active_next)
381     if (tmp->enemy == op)
382     tmp->enemy = NULL;
383     return 1;
384     }
385    
386     /* earth to dust spell. Basically destroys earthwalls in the area.
387     */
388     int cast_earth_to_dust(object *op,object *caster, object *spell_ob) {
389     object *tmp, *next;
390     int range,i,j, mflags;
391     sint16 sx, sy;
392     mapstruct *m;
393    
394     if(op->type!=PLAYER)
395     return 0;
396    
397     range=spell_ob->range + SP_level_range_adjust(caster, spell_ob);
398    
399     for(i= -range;i<=range;i++)
400     for(j= -range;j<=range;j++) {
401     sx = op->x + i;
402     sy = op->y + j;
403     m = op->map;
404     mflags = get_map_flags(m, &m, sx, sy, &sx, &sy);
405    
406     if (mflags & P_OUT_OF_MAP) continue;
407    
408     // earth to dust tears down everything that can be teared down
409     for (tmp = get_map_ob(m, sx, sy); tmp != NULL; tmp = next)
410     {
411     next = tmp->above;
412     if (QUERY_FLAG (tmp, FLAG_TEAR_DOWN))
413     hit_player (tmp, 9998, op, AT_PHYSICAL, 0);
414     }
415     }
416     return 1;
417     }
418    
419    
420     void execute_word_of_recall(object *op) {
421     object *wor=op;
422     while(op!=NULL && op->type!=PLAYER)
423     op=op->env;
424    
425     if(op!=NULL && op->map) {
426     if ((get_map_flags(op->map, NULL, op->x, op->y, NULL, NULL) & P_NO_CLERIC) && (!QUERY_FLAG(op,FLAG_WIZCAST)))
427     new_draw_info(NDI_UNIQUE, 0,op,"You feel something fizzle inside you.");
428     else
429     enter_exit(op,wor);
430     }
431     remove_ob(wor);
432     free_object(wor);
433     }
434    
435     /* Word of recall causes the player to return 'home'.
436     * we put a force into the player object, so that there is a
437     * time delay effect.
438     */
439     int cast_word_of_recall(object *op, object *caster, object *spell_ob) {
440     object *dummy;
441     int time;
442    
443     if(op->type!=PLAYER)
444     return 0;
445    
446     if (find_obj_by_type_subtype(op,SPELL_EFFECT, SP_WORD_OF_RECALL))
447     {
448     new_draw_info(NDI_UNIQUE, 0, op, "You feel a force starting to build up inside you." );
449     return 1;
450     }
451    
452     dummy=get_archetype(FORCE_NAME);
453     if(dummy == NULL){
454     new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!");
455     LOG(llevError,"cast_word_of_recall: get_archetype(force) failed!\n");
456     return 0;
457     }
458     time = spell_ob->duration - SP_level_duration_adjust(caster, spell_ob);
459     if (time <1 ) time=1;
460    
461     /* value of speed really doesn't make much difference, as long as it is
462     * positive. Lower value may be useful so that the problem doesn't
463     * do anything really odd if it say a -1000 or something.
464     */
465     dummy->speed = 0.002;
466     update_ob_speed(dummy);
467     dummy->speed_left = -dummy->speed * time;
468     dummy->type=SPELL_EFFECT;
469     dummy->subtype = SP_WORD_OF_RECALL;
470    
471     /* If we could take advantage of enter_player_savebed() here, it would be
472     * nice, but until the map load fails, we can't.
473     */
474     EXIT_PATH(dummy) = add_string(op->contr->savebed_map);
475     EXIT_X(dummy) = op->contr->bed_x;
476     EXIT_Y(dummy) = op->contr->bed_y;
477    
478     (void) insert_ob_in_ob(dummy,op);
479     new_draw_info(NDI_UNIQUE, 0,op,"You feel a force starting to build up inside you.");
480     return 1;
481     }
482    
483     /* cast_wonder
484     * wonder is really just a spell that will likely cast another
485     * spell.
486     */
487     int cast_wonder(object *op, object *caster, int dir, object *spell_ob) {
488     object *newspell;
489    
490     if(!rndm(0, 3))
491     return cast_cone(op,caster,dir, spell_ob);
492    
493     if (spell_ob->randomitems) {
494     newspell = generate_treasure(spell_ob->randomitems, caster->level);
495     if (!newspell) {
496     LOG(llevError,"cast_wonder: Unable to get a spell!\n");
497     return 0;
498     }
499     if (newspell->type != SPELL) {
500     LOG(llevError,"cast_wonder: spell returned is not a spell (%d, %s)!\n",
501     newspell->type, newspell->name);
502     return 0;
503     }
504     /* Prevent inifinit recursion */
505     if (newspell->subtype == SP_WONDER) {
506     LOG(llevError,"cast_wonder: spell returned is another wonder spell!\n");
507     return 0;
508     }
509     return cast_spell(op,caster,dir,newspell, NULL);
510     }
511     return 1;
512     }
513    
514    
515     int perceive_self(object *op) {
516     char *cp=describe_item(op, op), buf[MAX_BUF];
517     archetype *at=find_archetype(ARCH_DEPLETION);
518     object *tmp;
519     int i;
520    
521     tmp=find_god(determine_god(op));
522     if (tmp)
523     new_draw_info_format(NDI_UNIQUE, 0, op, "You worship %s", tmp->name);
524     else
525     new_draw_info(NDI_UNIQUE, 0,op,"You worship no god");
526    
527     tmp=present_arch_in_ob(at,op);
528    
529     if(*cp=='\0' && tmp==NULL)
530     new_draw_info(NDI_UNIQUE, 0,op,"You feel very mundane");
531     else {
532     new_draw_info(NDI_UNIQUE, 0,op,"You have:");
533     new_draw_info(NDI_UNIQUE, 0,op,cp);
534     if (tmp!=NULL) {
535     for (i=0; i<NUM_STATS; i++) {
536     if (get_attr_value(&tmp->stats, i)<0) {
537     new_draw_info_format(NDI_UNIQUE, 0,op,
538     "Your %s is depleted by %d", statname[i],
539     -(get_attr_value(&tmp->stats,i)));
540     }
541     }
542     }
543     }
544    
545     if (is_dragon_pl(op)) {
546     /* now grab the 'dragon_ability'-force from the player's inventory */
547     for (tmp = op->inv; tmp != NULL; tmp = tmp->below) {
548     if (tmp->type == FORCE && !strcmp(tmp->arch->name, "dragon_ability_force")) {
549     if(tmp->stats.exp == 0) {
550     sprintf(buf, "Your metabolism isn't focused on anything.");
551     } else {
552     sprintf(buf, "Your metabolism is focused on %s.", change_resist_msg[tmp->stats.exp]);
553     }
554     new_draw_info(NDI_UNIQUE, 0,op, buf);
555     break;
556     }
557     }
558     }
559     return 1;
560     }
561    
562     /* int cast_create_town_portal (object *op, object *caster, int dir)
563     *
564     * This function cast the spell of town portal for op
565     *
566     * The spell operates in two passes. During the first one a place
567     * is marked as a destination for the portal. During the second one,
568     * 2 portals are created, one in the position the player cast it and
569     * one in the destination place. The portal are synchronized and 2 forces
570     * are inserted in the player to destruct the portal next time player
571     * creates a new portal pair.
572     * This spell has a side effect that it allows people to meet each other
573     * in a permanent, private, appartements by making a town portal from it
574     * to the town or another public place. So, check if the map is unique and if
575     * so return an error
576     *
577     * Code by Tchize (david.delbecq@usa.net)
578     */
579     int cast_create_town_portal (object *op, object *caster, object *spell, int dir)
580     {
581     object *dummy, *force, *old_force, *tmp;
582     archetype *perm_portal;
583     char portal_name [1024], portal_message [1024];
584     sint16 exitx, exity;
585     mapstruct *exitmap;
586     int op_level;
587    
588    
589     /* Check to see if the map the player is currently on is a per player unique
590     * map. This can be determined in that per player unique maps have the
591     * full pathname listed.
592     */
593     if (!strncmp(op->map->path, settings.localdir, strlen(settings.localdir)) &&
594     settings.create_home_portals != TRUE )
595     {
596     new_draw_info(NDI_UNIQUE | NDI_NAVY, 0,op,"You can't cast that here.\n");
597     return 0;
598     }
599    
600     /* Check to see if the player is on a transport */
601     if (op->contr && op->contr->transport) {
602     new_draw_info(NDI_UNIQUE | NDI_NAVY, 0,op,"You need to exit the transport to cast that.\n");
603     return 0;
604     }
605    
606     /* The first thing to do is to check if we have a marked destination
607     * dummy is used to make a check inventory for the force
608     */
609     dummy=arch_to_object(spell->other_arch);
610     if(dummy == NULL){
611     new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!");
612     LOG(llevError,"get_object failed (force in cast_create_town_portal for %s!\n",op->name);
613     return 0;
614     }
615     force=check_inv_recursive (op,dummy);
616    
617     if (force==NULL) {
618     /* Here we know there is no destination marked up.
619     * We have 2 things to do:
620     * 1. Mark the destination in the player inventory.
621     * 2. Let the player know it worked.
622     */
623     free_string (dummy->name);
624     dummy->name = add_string (op->map->path);
625     EXIT_X(dummy)= op->x;
626     EXIT_Y(dummy)= op->y;
627     insert_ob_in_ob (dummy,op);
628     new_draw_info(NDI_UNIQUE | NDI_NAVY, 0,op,"You fix this place in your mind.\nYou feel you are able to come here from anywhere.");
629     return 1;
630     }
631     free_object (dummy);
632    
633     /* Here we know where the town portal should go to
634     * We should kill any existing portal associated with the player.
635     * Than we should create the 2 portals.
636     * For each of them, we need:
637     * - To create the portal with the name of the player+destination map
638     * - set the owner of the town portal
639     * - To mark the position of the portal in the player's inventory
640     * for easier destruction.
641     *
642     * The mark works has follow:
643     * slaying: Existing town portal
644     * hp, sp : x & y of the associated portal
645     * name : name of the portal
646     * race : map the portal is in
647     */
648    
649     /* First step: killing existing town portals */
650     dummy=get_archetype(spell->race);
651     if(dummy == NULL){
652     new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!");
653     LOG(llevError,"get_object failed (force) in cast_create_town_portal for %s!\n",op->name);
654     return 0;
655     }
656     perm_portal = find_archetype (spell->slaying);
657    
658     /* To kill a town portal, we go trough the player's inventory,
659     * for each marked portal in player's inventory,
660     * -We try load the associated map (if impossible, consider the portal destructed)
661     * -We find any portal in the specified location.
662     * If it has the good name, we destruct it.
663     * -We destruct the force indicating that portal.
664     */
665     while ( (old_force=check_inv_recursive (op,dummy))) {
666     exitx=EXIT_X(old_force);
667     exity=EXIT_Y(old_force);
668     LOG (llevDebug,"Trying to kill a portal in %s (%d,%d)\n",old_force->race,exitx,exity);
669    
670     if (!strncmp(old_force->race, settings.localdir, strlen(settings.localdir)))
671     exitmap = ready_map_name(old_force->race, MAP_PLAYER_UNIQUE);
672     else exitmap = ready_map_name(old_force->race, 0);
673    
674     if (exitmap) {
675     tmp=present_arch (perm_portal,exitmap,exitx,exity);
676     while (tmp) {
677     if (tmp->name == old_force->name) {
678     remove_ob (tmp);
679     free_object (tmp);
680     break;
681     } else {
682     tmp = tmp->above;
683     }
684     }
685     }
686     remove_ob (old_force);
687     free_object (old_force);
688     LOG (llevDebug,"\n");
689     }
690     free_object (dummy);
691    
692     /* Creating the portals.
693     * The very first thing to do is to ensure
694     * access to the destination map.
695     * If we can't, don't fizzle. Simply warn player.
696     * This ensure player pays his mana for the spell
697     * because HE is responsible of forgotting.
698     * 'force' is the destination of the town portal, which we got
699     * from the players inventory above.
700     */
701    
702     /* Ensure exit map is loaded*/
703     if (!strncmp(force->name, settings.localdir, strlen(settings.localdir)))
704     exitmap = ready_map_name(force->name, MAP_PLAYER_UNIQUE);
705     else
706     exitmap = ready_map_name(force->name, 0);
707    
708     /* If we were unable to load (ex. random map deleted), warn player*/
709     if (exitmap==NULL) {
710     new_draw_info(NDI_UNIQUE | NDI_NAVY, 0,op,"Something strange happens.\nYou can't remember where to go!?");
711     remove_ob(force);
712     free_object(force);
713     return 1;
714     }
715    
716     op_level = caster_level(caster, spell);
717     if (op_level<15)
718     snprintf (portal_message,1024,"\nThe air moves around you and\na huge smell of ammonia\nsurounds you as you pass\nthrough %s's tiny portal\nPouah!\n",op->name);
719     else if (op_level<30)
720     snprintf (portal_message,1024,"\n%s's portal smells of ozone.\nYou do a lot of movements and finally pass\nthrough the small hole in the air\n",op->name);
721     else if (op_level<60)
722     snprintf (portal_message,1024,"\nA shining door opens in the air in front of you,\nshowing you the path to another place.\n");
723     else snprintf (portal_message,1024,"\nAs you walk through %s's portal, flowers come out\nfrom the ground around you.\nYou feel awed.\n",op->name);
724    
725     /* Create a portal in front of player
726     * dummy contain the portal and
727     * force contain the track to kill it later
728     */
729    
730     snprintf (portal_name,1024,"%s's portal to %s",op->name,force->name);
731     dummy=get_archetype(spell->slaying); /*The portal*/
732     if(dummy == NULL) {
733     new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!");
734     LOG(llevError,"get_object failed (perm_magic_portal) in cast_create_town_portal for %s!\n",op->name);
735     return 0;
736     }
737     EXIT_PATH(dummy) = add_string (force->name);
738     EXIT_X(dummy)=EXIT_X(force);
739     EXIT_Y(dummy)=EXIT_Y(force);
740     FREE_AND_COPY(dummy->name, portal_name);
741     FREE_AND_COPY(dummy->name_pl, portal_name);
742     dummy->msg=add_string (portal_message);
743     dummy->race=add_string (op->name); /*Save the owner of the portal*/
744     cast_create_obj (op, caster, dummy, 0);
745    
746     /* Now we need to to create a town portal marker inside the player
747     * object, so on future castings, we can know that he has an active
748     * town portal.
749     */
750     tmp=get_archetype(spell->race);
751     if(tmp == NULL){
752     new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!");
753     LOG(llevError,"get_object failed (force) in cast_create_town_portal for %s!\n",op->name);
754     return 0;
755     }
756     tmp->race=add_string (op->map->path);
757     FREE_AND_COPY(tmp->name, portal_name);
758     EXIT_X(tmp)=dummy->x;
759     EXIT_Y(tmp)=dummy->y;
760     insert_ob_in_ob (tmp,op);
761    
762     /* Create a portal in the destination map
763     * dummy contain the portal and
764     * force the track to kill it later
765     * the 'force' variable still contains the 'reminder' of
766     * where this portal goes to.
767     */
768     snprintf (portal_name,1024,"%s's portal to %s",op->name,op->map->path);
769     dummy=get_archetype (spell->slaying); /*The portal*/
770     if(dummy == NULL) {
771     new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!");
772     LOG(llevError,"get_object failed (perm_magic_portal) in cast_create_town_portal for %s!\n",op->name);
773     return 0;
774     }
775     EXIT_PATH(dummy) = add_string (op->map->path);
776     EXIT_X(dummy)=op->x;
777     EXIT_Y(dummy)=op->y;
778     FREE_AND_COPY(dummy->name, portal_name);
779     FREE_AND_COPY(dummy->name_pl, portal_name);
780     dummy->msg=add_string (portal_message);
781     dummy->x=EXIT_X(force);
782     dummy->y=EXIT_Y(force);
783     dummy->race=add_string (op->name); /*Save the owner of the portal*/
784     insert_ob_in_map(dummy,exitmap,op,0);
785    
786     /* Now we create another town portal marker that
787     * points back to the one we just made
788     */
789     tmp=get_archetype(spell->race);
790     if(tmp == NULL){
791     new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!");
792     LOG(llevError,"get_object failed (force) in cast_create_town_portal for %s!\n",op->name);
793     return 0;
794     }
795     tmp->race=add_string(force->name);
796     FREE_AND_COPY(tmp->name, portal_name);
797     EXIT_X(tmp)=dummy->x;
798     EXIT_Y(tmp)=dummy->y;
799     insert_ob_in_ob (tmp,op);
800    
801     /* Describe the player what happened
802     */
803     new_draw_info(NDI_UNIQUE | NDI_NAVY, 0,op,"You see air moving and showing you the way home.");
804     remove_ob(force); /* Delete the force inside the player*/
805     free_object(force);
806     return 1;
807     }
808    
809    
810     /* This creates magic walls. Really, it can create most any object,
811     * within some reason.
812     */
813    
814     int magic_wall(object *op,object *caster,int dir,object *spell_ob) {
815     object *tmp, *tmp2;
816     int i,posblocked,negblocked, maxrange;
817     sint16 x, y;
818     mapstruct *m;
819     const char *name;
820     archetype *at;
821    
822     if(!dir) {
823     dir=op->facing;
824     x = op->x;
825     y = op->y;
826     } else {
827     x = op->x+freearr_x[dir];
828     y = op->y+freearr_y[dir];
829     }
830     m = op->map;
831    
832     if ((spell_ob->move_block || x != op->x || y != op->y) &&
833     (get_map_flags(m, &m, x, y, &x, &y) & (P_OUT_OF_MAP|P_IS_ALIVE) ||
834     ((spell_ob->move_block & GET_MAP_MOVE_BLOCK(m, x, y)) == spell_ob->move_block))) {
835     new_draw_info(NDI_UNIQUE, 0,op,"Something is in the way.");
836     return 0;
837     }
838     if (spell_ob->other_arch) {
839     tmp = arch_to_object(spell_ob->other_arch);
840     } else if (spell_ob->race) {
841     char buf1[MAX_BUF];
842    
843     sprintf(buf1,spell_ob->race,dir);
844     at = find_archetype(buf1);
845     if (!at) {
846     LOG(llevError, "summon_wall: Unable to find archetype %s\n", buf1);
847     new_draw_info(NDI_UNIQUE, 0,op,"This spell is broken.");
848     return 0;
849     }
850     tmp = arch_to_object(at);
851     } else {
852     LOG(llevError,"magic_wall: spell %s lacks other_arch\n",
853     spell_ob->name);
854     return 0;
855     }
856    
857     if (tmp->type == SPELL_EFFECT) {
858     tmp->attacktype = spell_ob->attacktype;
859     tmp->duration = spell_ob->duration +
860     SP_level_duration_adjust(caster, spell_ob);
861     tmp->stats.dam = spell_ob->stats.dam +
862     SP_level_dam_adjust(caster, spell_ob);
863     tmp->range = 0;
864     } else if (QUERY_FLAG(tmp, FLAG_ALIVE)) {
865     tmp->stats.hp = spell_ob->duration +
866     SP_level_duration_adjust(caster, spell_ob);
867     tmp->stats.maxhp = tmp->stats.hp;
868     set_owner(tmp,op);
869     set_spell_skill(op, caster, spell_ob, tmp);
870     }
871     if (QUERY_FLAG(spell_ob, FLAG_IS_USED_UP) || QUERY_FLAG(tmp, FLAG_IS_USED_UP)) {
872     tmp->stats.food = spell_ob->duration +
873     SP_level_duration_adjust(caster, spell_ob);
874     SET_FLAG(tmp, FLAG_IS_USED_UP);
875     }
876     if (QUERY_FLAG(spell_ob, FLAG_TEAR_DOWN)) {
877     tmp->stats.hp = spell_ob->stats.dam + SP_level_dam_adjust(caster, spell_ob);
878     tmp->stats.maxhp = tmp->stats.hp;
879     SET_FLAG(tmp, FLAG_TEAR_DOWN);
880     SET_FLAG(tmp, FLAG_ALIVE);
881     }
882    
883     /* This can't really hurt - if the object doesn't kill anything,
884     * these fields just won't be used.
885     */
886     set_owner(tmp,op);
887     set_spell_skill(op, caster, spell_ob, tmp);
888     tmp->x = x;
889     tmp->y = y;
890     tmp->level = caster_level(caster, spell_ob) / 2;
891    
892     name = tmp->name;
893     if ((tmp = insert_ob_in_map (tmp, m, op,0)) == NULL) {
894     new_draw_info_format(NDI_UNIQUE, 0,op,"Something destroys your %s", name);
895     return 0;
896     }
897     /* If this is a spellcasting wall, need to insert the spell object */
898     if (tmp->other_arch && tmp->other_arch->clone.type == SPELL)
899     insert_ob_in_ob(arch_to_object(tmp->other_arch), tmp);
900    
901     /* This code causes the wall to extend some distance in
902     * each direction, or until an obstruction is encountered.
903     * posblocked and negblocked help determine how far the
904     * created wall can extend, it won't go extend through
905     * blocked spaces.
906     */
907     maxrange = spell_ob->range + SP_level_range_adjust(caster, spell_ob);
908     posblocked=0;
909     negblocked=0;
910    
911     for(i=1; i<=maxrange; i++) {
912     int dir2;
913    
914     dir2 = (dir<4)?(dir+2):dir-2;
915    
916     x = tmp->x+i*freearr_x[dir2];
917     y = tmp->y+i*freearr_y[dir2];
918     m = tmp->map;
919    
920     if(!(get_map_flags(m, &m, x, y, &x, &y) & (P_OUT_OF_MAP|P_IS_ALIVE)) &&
921     ((spell_ob->move_block & GET_MAP_MOVE_BLOCK(m, x, y)) != spell_ob->move_block) &&
922     !posblocked) {
923     tmp2 = get_object();
924     copy_object(tmp,tmp2);
925     tmp2->x = x;
926     tmp2->y = y;
927     insert_ob_in_map(tmp2,m,op,0);
928     /* If this is a spellcasting wall, need to insert the spell object */
929     if (tmp2->other_arch && tmp2->other_arch->clone.type == SPELL)
930     insert_ob_in_ob(arch_to_object(tmp2->other_arch), tmp2);
931    
932     } else posblocked=1;
933    
934     x = tmp->x-i*freearr_x[dir2];
935     y = tmp->y-i*freearr_y[dir2];
936     m = tmp->map;
937    
938     if(!(get_map_flags(m, &m, x, y, &x, &y) & (P_OUT_OF_MAP|P_IS_ALIVE)) &&
939     ((spell_ob->move_block & GET_MAP_MOVE_BLOCK(m, x, y)) != spell_ob->move_block) &&
940     !negblocked) {
941     tmp2 = get_object();
942     copy_object(tmp,tmp2);
943     tmp2->x = x;
944     tmp2->y = y;
945     insert_ob_in_map(tmp2,m,op,0);
946     if (tmp2->other_arch && tmp2->other_arch->clone.type == SPELL)
947     insert_ob_in_ob(arch_to_object(tmp2->other_arch), tmp2);
948     } else negblocked=1;
949     }
950    
951     if(QUERY_FLAG(tmp, FLAG_BLOCKSVIEW))
952     update_all_los(op->map, op->x, op->y);
953    
954     return 1;
955     }
956    
957     int dimension_door(object *op,object *caster, object *spob, int dir) {
958     uint32 dist, maxdist;
959     int mflags;
960     mapstruct *m;
961     sint16 sx, sy;
962    
963     if(op->type!=PLAYER)
964     return 0;
965    
966     if(!dir) {
967     new_draw_info(NDI_UNIQUE, 0,op,"In what direction?");
968     return 0;
969     }
970    
971     /* Given the new outdoor maps, can't let players dimension door for
972     * ever, so put limits in.
973     */
974     maxdist = spob->range +
975     SP_level_range_adjust(caster, spob);
976    
977     if(op->contr->count) {
978     if (op->contr->count > maxdist) {
979     new_draw_info(NDI_UNIQUE, 0, op, "You can't dimension door that far!");
980     return 0;
981     }
982    
983     for(dist=0;dist<op->contr->count; dist++) {
984     mflags = get_map_flags(op->map, &m,
985     op->x+freearr_x[dir]*(dist+1), op->y+freearr_y[dir]*(dist+1),
986     &sx, &sy);
987    
988     if (mflags & (P_NO_MAGIC | P_OUT_OF_MAP)) break;
989    
990     if ((mflags & P_BLOCKSVIEW) &&
991     OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, sx, sy))) break;
992     }
993    
994     if(dist<op->contr->count) {
995     new_draw_info(NDI_UNIQUE, 0,op,"Something blocks the magic of the spell.\n");
996     op->contr->count=0;
997     return 0;
998     }
999     op->contr->count=0;
1000    
1001     /* Remove code that puts player on random space on maps. IMO,
1002     * a lot of maps probably have areas the player should not get to,
1003     * but may not be marked as NO_MAGIC (as they may be bounded
1004     * by such squares). Also, there are probably treasure rooms and
1005     * lots of other maps that protect areas with no magic, but the
1006     * areas themselves don't contain no magic spaces.
1007     */
1008     /* This call here is really just to normalize the coordinates */
1009     mflags = get_map_flags(op->map, &m,op->x+freearr_x[dir]*dist, op->y+freearr_y[dir]*dist,
1010     &sx, &sy);
1011     if (mflags&P_IS_ALIVE || OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, sx, sy))) {
1012     new_draw_info(NDI_UNIQUE, 0,op,"You cast your spell, but nothing happens.\n");
1013     return 1; /* Maybe the penalty should be more severe... */
1014     }
1015     } else {
1016     /* Player didn't specify a distance, so lets see how far
1017     * we can move the player. Don't know why this stopped on
1018     * spaces that blocked the players view.
1019     */
1020    
1021     for(dist=0; dist < maxdist; dist++) {
1022     mflags = get_map_flags(op->map, &m,
1023     op->x+freearr_x[dir] * (dist+1),
1024     op->y+freearr_y[dir] * (dist+1),
1025     &sx, &sy);
1026    
1027     if (mflags & (P_NO_MAGIC | P_OUT_OF_MAP)) break;
1028    
1029     if ((mflags & P_BLOCKSVIEW) &&
1030     OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, sx, sy))) break;
1031    
1032     }
1033    
1034     /* If the destination is blocked, keep backing up until we
1035     * find a place for the player.
1036     */
1037     for(;dist>0; dist--) {
1038     if (get_map_flags(op->map, &m,op->x+freearr_x[dir]*dist, op->y+freearr_y[dir]*dist,
1039     &sx, &sy) & (P_OUT_OF_MAP|P_IS_ALIVE)) continue;
1040    
1041    
1042     if (!OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, sx, sy))) break;
1043    
1044     }
1045     if(!dist) {
1046     new_draw_info(NDI_UNIQUE, 0,op,"Your spell failed!\n");
1047     return 0;
1048     }
1049     }
1050    
1051     /* Actually move the player now */
1052     remove_ob(op);
1053     op->x+=freearr_x[dir]*dist;
1054     op->y+=freearr_y[dir]*dist;
1055     if ((op = insert_ob_in_map(op,op->map,op,0)) == NULL)
1056     return 1;
1057    
1058     op->speed_left= -FABS(op->speed)*5; /* Freeze them for a short while */
1059     return 1;
1060     }
1061    
1062    
1063     /* cast_heal: Heals something.
1064     * op is the caster.
1065     * dir is the direction he is casting it in.
1066     * spell is the spell object.
1067     */
1068     int cast_heal(object *op,object *caster, object *spell, int dir) {
1069     object *tmp;
1070     archetype *at;
1071     object *poison;
1072     int heal = 0, success = 0;
1073    
1074     tmp = find_target_for_friendly_spell(op,dir);
1075    
1076     if (tmp==NULL) return 0;
1077    
1078     /* Figure out how many hp this spell might cure.
1079     * could be zero if this spell heals effects, not damage.
1080     */
1081     heal = spell->stats.dam;
1082     if (spell->stats.hp)
1083     heal += random_roll(spell->stats.hp, 6, op, PREFER_HIGH) +
1084     spell->stats.hp;
1085    
1086     if (heal) {
1087     if (tmp->stats.hp >= tmp->stats.maxhp) {
1088     new_draw_info(NDI_UNIQUE, 0,tmp, "You are already fully healed.");
1089     }
1090     else {
1091     /* See how many points we actually heal. Instead of messages
1092     * based on type of spell, we instead do messages based
1093     * on amount of damage healed.
1094     */
1095     if (heal > (tmp->stats.maxhp - tmp->stats.hp))
1096     heal = tmp->stats.maxhp - tmp->stats.hp;
1097     tmp->stats.hp += heal;
1098    
1099     if (tmp->stats.hp >= tmp->stats.maxhp) {
1100     new_draw_info(NDI_UNIQUE, 0,tmp, "You feel just fine!");
1101     } else if (heal > 50) {
1102     new_draw_info(NDI_UNIQUE, 0,tmp, "Your wounds close!");
1103     } else if (heal > 25) {
1104     new_draw_info(NDI_UNIQUE, 0,tmp, "Your wounds mostly close.");
1105     } else if (heal > 10) {
1106     new_draw_info(NDI_UNIQUE, 0,tmp, "Your wounds start to fade.");
1107     } else {
1108     new_draw_info(NDI_UNIQUE, 0,tmp, "Your wounds start to close.");
1109     }
1110     success=1;
1111     }
1112     }
1113     if (spell->attacktype & AT_DISEASE)
1114     if (cure_disease (tmp, op))
1115     success = 1;
1116    
1117     if (spell->attacktype & AT_POISON) {
1118     at = find_archetype("poisoning");
1119     poison=present_arch_in_ob(at,tmp);
1120     if (poison) {
1121     success = 1;
1122     new_draw_info(NDI_UNIQUE, 0,tmp, "Your body feels cleansed");
1123     poison->stats.food = 1;
1124     }
1125     }
1126     if (spell->attacktype & AT_CONFUSION) {
1127     poison=present_in_ob_by_name(FORCE,"confusion", tmp);
1128     if (poison) {
1129     success = 1;
1130     new_draw_info(NDI_UNIQUE, 0,tmp, "Your mind feels clearer");
1131     poison->duration = 1;
1132     }
1133     }
1134     if (spell->attacktype & AT_BLIND) {
1135     at=find_archetype("blindness");
1136     poison=present_arch_in_ob(at,tmp);
1137     if (poison) {
1138     success = 1;
1139     new_draw_info(NDI_UNIQUE, 0,tmp,"Your vision begins to return.");
1140     poison->stats.food = 1;
1141     }
1142     }
1143     if (spell->last_sp && tmp->stats.sp < tmp->stats.maxsp) {
1144     tmp->stats.sp += spell->last_sp;
1145     if (tmp->stats.sp > tmp->stats.maxsp) tmp->stats.sp = tmp->stats.maxsp;
1146     success = 1;
1147     new_draw_info(NDI_UNIQUE, 0,tmp,"Magical energy surges through your body!");
1148     }
1149     if (spell->last_grace && tmp->stats.grace < tmp->stats.maxgrace) {
1150     tmp->stats.grace += spell->last_grace;
1151     if (tmp->stats.grace > tmp->stats.maxgrace) tmp->stats.grace = tmp->stats.maxgrace;
1152     success = 1;
1153     new_draw_info(NDI_UNIQUE, 0,tmp,"You feel redeemed with your god!");
1154     }
1155     if (spell->stats.food && tmp->stats.food < 999) {
1156     tmp->stats.food += spell->stats.food;
1157     if (tmp->stats.food > 999) tmp->stats.food=999;
1158     success = 1;
1159     /* We could do something a bit better like the messages for healing above */
1160     new_draw_info(NDI_UNIQUE, 0,tmp,"You feel your belly fill with food");
1161     }
1162     return success;
1163     }
1164    
1165    
1166     /* This is used for the spells that gain stats. There are no spells
1167     * right now that icnrease wis/int/pow on a temp basis, so no
1168     * good comments for those.
1169     */
1170     static const char* const no_gain_msgs[NUM_STATS] = {
1171     "You grow no stronger.",
1172     "You grow no more agile.",
1173     "You don't feel any healthier.",
1174     "no wis",
1175     "You are no easier to look at.",
1176     "no int",
1177     "no pow"
1178     };
1179    
1180     int cast_change_ability(object *op,object *caster,object *spell_ob, int dir, int silent) {
1181     object *tmp, *tmp2=NULL;
1182     object *force=NULL;
1183     int i;
1184    
1185     /* if dir = 99 op defaults to tmp, eat_special_food() requires this. */
1186     if(dir!=0) {
1187     tmp=find_target_for_friendly_spell(op,dir);
1188     } else {
1189     tmp = op;
1190     }
1191    
1192     if(tmp==NULL) return 0;
1193    
1194     /* If we've already got a force of this type, don't add a new one. */
1195     for(tmp2=tmp->inv; tmp2!=NULL; tmp2=tmp2->below) {
1196     if (tmp2->type==FORCE && tmp2->subtype == FORCE_CHANGE_ABILITY) {
1197     if (tmp2->name == spell_ob->name) {
1198     force=tmp2; /* the old effect will be "refreshed" */
1199     break;
1200     }
1201     else if (spell_ob->race && spell_ob->race == tmp2->name) {
1202     if ( !silent )
1203     new_draw_info_format(NDI_UNIQUE, 0, op,
1204     "You can not cast %s while %s is in effect",
1205     spell_ob->name, tmp2->name_pl);
1206     return 0;
1207     }
1208     }
1209     }
1210     if(force==NULL) {
1211     force=get_archetype(FORCE_NAME);
1212     force->subtype = FORCE_CHANGE_ABILITY;
1213     free_string(force->name);
1214     if (spell_ob->race)
1215     force->name = add_refcount(spell_ob->race);
1216     else
1217     force->name = add_refcount(spell_ob->name);
1218     free_string(force->name_pl);
1219     force->name_pl = add_refcount(spell_ob->name);
1220     new_draw_info(NDI_UNIQUE, 0, op, "You create an aura of magical force.");
1221    
1222     } else {
1223     int duration;
1224    
1225     duration = spell_ob->duration + SP_level_duration_adjust(caster, spell_ob) * 50;
1226     if (duration > force->duration) {
1227     force->duration = duration;
1228     new_draw_info(NDI_UNIQUE, 0, op, "You recast the spell while in effect.");
1229     } else {
1230     new_draw_info(NDI_UNIQUE, 0, op, "Recasting the spell had no effect.");
1231     }
1232     return 1;
1233     }
1234     force->duration = spell_ob->duration + SP_level_duration_adjust(caster, spell_ob) * 50;
1235     force->speed = 1.0;
1236     force->speed_left = -1.0;
1237     SET_FLAG(force, FLAG_APPLIED);
1238    
1239     /* Now start processing the effects. First, protections */
1240     for (i=0; i < NROFATTACKS; i++) {
1241     if (spell_ob->resist[i]) {
1242     force->resist[i] = spell_ob->resist[i] + SP_level_dam_adjust(caster, spell_ob);
1243     if (force->resist[i] > 100) force->resist[i] = 100;
1244     }
1245     }
1246     if (spell_ob->stats.hp)
1247     force->stats.hp = spell_ob->stats.hp + SP_level_dam_adjust(caster,spell_ob);
1248    
1249     if (tmp->type == PLAYER) {
1250     /* Stat adjustment spells */
1251     for (i=0; i < NUM_STATS; i++) {
1252     sint8 stat = get_attr_value(&spell_ob->stats, i), k, sm;
1253     if (stat) {
1254     sm=0;
1255     for (k=0; k<stat; k++)
1256     sm += rndm(1, 3);
1257    
1258     if ((get_attr_value(&tmp->stats, i) + sm) > (15 + 5 * stat)) {
1259     sm = (15 + 5 * stat) - get_attr_value(&tmp->stats, i);
1260     if (sm<0) sm = 0;
1261     }
1262     set_attr_value(&force->stats, i, sm);
1263     if (!sm)
1264     new_draw_info(NDI_UNIQUE, 0,op,no_gain_msgs[i]);
1265     }
1266     }
1267     }
1268    
1269     force->move_type = spell_ob->move_type;
1270    
1271     if (QUERY_FLAG(spell_ob, FLAG_SEE_IN_DARK))
1272     SET_FLAG(force, FLAG_SEE_IN_DARK);
1273    
1274     if (QUERY_FLAG(spell_ob, FLAG_XRAYS))
1275     SET_FLAG(force, FLAG_XRAYS);
1276    
1277     /* Haste/bonus speed */
1278     if (spell_ob->stats.exp) {
1279     if (op->speed > 0.5f) force->stats.exp = (sint64) ((float) spell_ob->stats.exp / (op->speed + 0.5f));
1280     else
1281     force->stats.exp = spell_ob->stats.exp;
1282     }
1283    
1284     force->stats.wc = spell_ob->stats.wc;
1285     force->stats.ac = spell_ob->stats.ac;
1286     force->attacktype = spell_ob->attacktype;
1287    
1288     insert_ob_in_ob(force,tmp);
1289     change_abil(tmp,force); /* Mostly to display any messages */
1290     fix_player(tmp);
1291     return 1;
1292     }
1293    
1294     /* This used to be part of cast_change_ability, but it really didn't make
1295     * a lot of sense, since most of the values it derives are from the god
1296     * of the caster.
1297     */
1298    
1299     int cast_bless(object *op,object *caster,object *spell_ob, int dir) {
1300     int i;
1301     object *god = find_god(determine_god(op)), *tmp2, *force=NULL, *tmp;
1302    
1303     /* if dir = 99 op defaults to tmp, eat_special_food() requires this. */
1304     if(dir!=0) {
1305     tmp=find_target_for_friendly_spell(op,dir);
1306     } else {
1307     tmp = op;
1308     }
1309    
1310     /* If we've already got a force of this type, don't add a new one. */
1311     for(tmp2=tmp->inv; tmp2!=NULL; tmp2=tmp2->below) {
1312     if (tmp2->type==FORCE && tmp2->subtype == FORCE_CHANGE_ABILITY) {
1313     if (tmp2->name == spell_ob->name) {
1314     force=tmp2; /* the old effect will be "refreshed" */
1315     break;
1316     }
1317     else if (spell_ob->race && spell_ob->race == tmp2->name) {
1318     new_draw_info_format(NDI_UNIQUE, 0, op,
1319     "You can not cast %s while %s is in effect",
1320     spell_ob->name, tmp2->name_pl);
1321     return 0;
1322     }
1323     }
1324     }
1325     if(force==NULL) {
1326     force=get_archetype(FORCE_NAME);
1327     force->subtype = FORCE_CHANGE_ABILITY;
1328     free_string(force->name);
1329     if (spell_ob->race)
1330     force->name = add_refcount(spell_ob->race);
1331     else
1332     force->name = add_refcount(spell_ob->name);
1333     free_string(force->name_pl);
1334     force->name_pl = add_refcount(spell_ob->name);
1335     new_draw_info(NDI_UNIQUE, 0, op, "You create an aura of magical force.");
1336     } else {
1337     int duration;
1338    
1339     duration = spell_ob->duration + SP_level_duration_adjust(caster, spell_ob) * 50;
1340     if (duration > force->duration) {
1341     force->duration = duration;
1342     new_draw_info(NDI_UNIQUE, 0, op, "You recast the spell while in effect.");
1343     } else {
1344     new_draw_info(NDI_UNIQUE, 0, op, "Recasting the spell had no effect.");
1345     }
1346     return 0;
1347     }
1348     force->duration = spell_ob->duration + SP_level_duration_adjust(caster, spell_ob) * 50;
1349     force->speed = 1.0;
1350     force->speed_left = -1.0;
1351     SET_FLAG(force, FLAG_APPLIED);
1352    
1353     if(!god) {
1354     new_draw_info(NDI_UNIQUE, 0,op,"Your blessing seems empty.");
1355     } else {
1356     /* Only give out good benefits, and put a max on it */
1357     for (i=0; i<NROFATTACKS; i++) {
1358     if (god->resist[i]>0) {
1359     force->resist[i] = MIN(god->resist[i], spell_ob->resist[ATNR_GODPOWER]);
1360     }
1361     }
1362     force->path_attuned|=god->path_attuned;
1363     if (spell_ob->attacktype) {
1364     force->attacktype|=god->attacktype | AT_PHYSICAL;
1365     if(god->slaying) force->slaying = add_string(god->slaying);
1366     }
1367     if (tmp != op) {
1368     new_draw_info_format(NDI_UNIQUE, 0, op, "You bless %s.", tmp->name);
1369     new_draw_info_format(NDI_UNIQUE, 0, tmp, "%s blessed you.", op->name);
1370     } else {
1371     new_draw_info_format(NDI_UNIQUE, 0,tmp,
1372     "You are blessed by %s!",god->name);
1373     }
1374    
1375     }
1376     force->stats.wc = spell_ob->stats.wc;
1377     force->stats.ac = spell_ob->stats.ac;
1378    
1379     change_abil(tmp,force); /* Mostly to display any messages */
1380     insert_ob_in_ob(force,tmp);
1381     fix_player(tmp);
1382     return 1;
1383     }
1384    
1385    
1386    
1387     /* Alchemy code by Mark Wedel
1388     *
1389     * This code adds a new spell, called alchemy. Alchemy will turn
1390     * objects to gold nuggets, the value of the gold nuggets being
1391     * about 90% of that of the item itself. It uses the value of the
1392     * object before charisma adjustments, because the nuggets themselves
1393     * will be will be adjusted by charisma when sold.
1394     *
1395     * Large nuggets are worth 25 gp each (base). You will always get
1396     * the maximum number of large nuggets you could get.
1397     * Small nuggets are worth 1 gp each (base). You will get from 0
1398     * to the max amount of small nuggets as you could get.
1399     *
1400     * For example, if an item is worth 110 gold, you will get
1401     * 4 large nuggets, and from 0-10 small nuggets.
1402     *
1403     * There is also a chance (1:30) that you will get nothing at all
1404     * for the object. There is also a maximum weight that will be
1405     * alchemied.
1406     */
1407    
1408     /* I didn't feel like passing these as arguements to the
1409     * two functions that need them. Real values are put in them
1410     * when the spell is cast, and these are freed when the spell
1411     * is finished.
1412     */
1413     static object *small, *large;
1414    
1415     static void alchemy_object(object *obj, int *small_nuggets,
1416     int *large_nuggets, int *weight)
1417     {
1418     uint64 value=query_cost(obj, NULL, F_TRUE);
1419    
1420     /* Give third price when we alchemy money (This should hopefully
1421     * make it so that it isn't worth it to alchemy money, sell
1422     * the nuggets, alchemy the gold from that, etc.
1423     * Otherwise, give 9 silver on the gold for other objects,
1424     * so that it would still be more affordable to haul
1425     * the stuff back to town.
1426     */
1427    
1428     if (QUERY_FLAG(obj, FLAG_UNPAID))
1429     value=0;
1430     else if (obj->type==MONEY || obj->type==GEM)
1431     value /=3;
1432     else
1433     value = (value*9)/10;
1434    
1435     value /= 4; // fix by GHJ, don't understand, pcg
1436    
1437     if ((obj->value>0) && rndm(0, 29)) {
1438     int count;
1439    
1440     count = value / large->value;
1441     *large_nuggets += count;
1442     value -= (uint64)count * (uint64)large->value;
1443     count = value / small->value;
1444     *small_nuggets += count;
1445     }
1446    
1447     /* Turn 25 small nuggets into 1 large nugget. If the value
1448     * of large nuggets is not evenly divisable by the small nugget
1449     * value, take off an extra small_nugget (Assuming small_nuggets!=0)
1450     */
1451     if (*small_nuggets * small->value >= large->value) {
1452     (*large_nuggets)++;
1453     *small_nuggets -= large->value / small->value;
1454     if (*small_nuggets && large->value % small->value)
1455     (*small_nuggets)--;
1456     }
1457     weight += obj->weight;
1458     remove_ob(obj);
1459     free_object(obj);
1460     }
1461    
1462     static void update_map(object *op, mapstruct *m, int small_nuggets, int large_nuggets,
1463     int x, int y)
1464     {
1465     object *tmp;
1466     int flag=0;
1467    
1468     /* Put any nuggets below the player, but we can only pass this
1469     * flag if we are on the same space as the player
1470     */
1471     if (x == op->x && y == op->y && op->map == m) flag = INS_BELOW_ORIGINATOR;
1472    
1473     if (small_nuggets) {
1474     tmp = get_object();
1475     copy_object(small, tmp);
1476     tmp-> nrof = small_nuggets;
1477     tmp->x = x;
1478     tmp->y = y;
1479     insert_ob_in_map(tmp, m, op, flag);
1480     }
1481     if (large_nuggets) {
1482     tmp = get_object();
1483     copy_object(large, tmp);
1484     tmp-> nrof = large_nuggets;
1485     tmp->x = x;
1486     tmp->y = y;
1487     insert_ob_in_map(tmp, m, op, flag);
1488     }
1489     }
1490    
1491     int alchemy(object *op, object *caster, object *spell_ob)
1492     {
1493     int x,y,weight=0,weight_max,large_nuggets,small_nuggets, mflags;
1494     sint16 nx, ny;
1495     object *next,*tmp;
1496     mapstruct *mp;
1497    
1498     if(op->type!=PLAYER)
1499     return 0;
1500    
1501     /* Put a maximum weight of items that can be alchemied. Limits the power
1502     * some, and also prevents people from alcheming every table/chair/clock
1503     * in sight
1504     */
1505     weight_max = spell_ob->duration + +SP_level_duration_adjust(caster,spell_ob);
1506     weight_max *= 1000;
1507     small=get_archetype("smallnugget"),
1508     large=get_archetype("largenugget");
1509    
1510     for(y= op->y-1;y<=op->y+1;y++) {
1511     for(x= op->x-1;x<=op->x+1;x++) {
1512     nx = x;
1513     ny = y;
1514    
1515     mp = op->map;
1516    
1517     mflags = get_map_flags(mp, &mp, nx, ny, &nx, &ny);
1518    
1519     if(mflags & (P_OUT_OF_MAP | P_NO_MAGIC))
1520     continue;
1521    
1522     /* Treat alchemy a little differently - most spell effects
1523     * use fly as the movement type - for alchemy, consider it
1524     * ground level effect.
1525     */
1526     if (GET_MAP_MOVE_BLOCK(mp, nx, ny) & MOVE_WALK)
1527     continue;
1528    
1529     small_nuggets=0;
1530     large_nuggets=0;
1531    
1532     for(tmp=get_map_ob(mp,nx,ny);tmp!=NULL;tmp=next) {
1533     next=tmp->above;
1534     if (tmp->weight>0 && !QUERY_FLAG(tmp, FLAG_NO_PICK) &&
1535     !QUERY_FLAG(tmp, FLAG_ALIVE) &&
1536     !QUERY_FLAG(tmp, FLAG_IS_CAULDRON)) {
1537    
1538     if (tmp->inv) {
1539     object *next1, *tmp1;
1540     for (tmp1 = tmp->inv; tmp1!=NULL; tmp1=next1) {
1541     next1 = tmp1->below;
1542     if (tmp1->weight>0 && !QUERY_FLAG(tmp1, FLAG_NO_PICK) &&
1543     !QUERY_FLAG(tmp1, FLAG_ALIVE) &&
1544     !QUERY_FLAG(tmp1, FLAG_IS_CAULDRON))
1545     alchemy_object(tmp1, &small_nuggets, &large_nuggets,
1546     &weight);
1547     }
1548     }
1549     alchemy_object(tmp, &small_nuggets, &large_nuggets, &weight);
1550    
1551     if (weight>weight_max) {
1552     update_map(op, mp, small_nuggets, large_nuggets, nx, ny);
1553     free_object(large);
1554     free_object(small);
1555     return 1;
1556     }
1557     } /* is alchemable object */
1558     } /* process all objects on this space */
1559    
1560     /* Insert all the nuggets at one time. This probably saves time, but
1561     * it also prevents us from alcheming nuggets that were just created
1562     * with this spell.
1563     */
1564     update_map(op, mp, small_nuggets, large_nuggets, nx, ny);
1565     }
1566     }
1567     free_object(large);
1568     free_object(small);
1569     /* reset this so that if player standing on a big pile of stuff,
1570     * it is redrawn properly.
1571     */
1572     op->contr->socket.look_position = 0;
1573     return 1;
1574     }
1575    
1576    
1577     /* This function removes the cursed/damned status on equipped
1578     * items.
1579     */
1580     int remove_curse(object *op, object *caster, object *spell) {
1581     object *tmp;
1582     int success = 0, was_one = 0;
1583    
1584     for (tmp = op->inv; tmp; tmp = tmp->below)
1585     if (QUERY_FLAG(tmp, FLAG_APPLIED) &&
1586     ((QUERY_FLAG(tmp, FLAG_CURSED) && QUERY_FLAG(spell, FLAG_CURSED)) ||
1587     (QUERY_FLAG(tmp, FLAG_DAMNED) && QUERY_FLAG(spell, FLAG_DAMNED)))) {
1588    
1589     was_one++;
1590     if (tmp->level <= caster_level(caster, spell)) {
1591     success++;
1592     if (QUERY_FLAG(spell, FLAG_DAMNED))
1593     CLEAR_FLAG(tmp, FLAG_DAMNED);
1594    
1595     CLEAR_FLAG(tmp, FLAG_CURSED);
1596     CLEAR_FLAG(tmp, FLAG_KNOWN_CURSED);
1597     tmp->value = 0; /* Still can't sell it */
1598     if (op->type == PLAYER)
1599     esrv_send_item(op, tmp);
1600     }
1601     }
1602    
1603     if (op->type==PLAYER) {
1604     if (success) {
1605     new_draw_info(NDI_UNIQUE, 0,op, "You feel like some of your items are looser now.");
1606     } else {
1607     if (was_one)
1608     new_draw_info(NDI_UNIQUE, 0,op, "You failed to remove the curse.");
1609     else
1610     new_draw_info(NDI_UNIQUE, 0,op, "You are not using any cursed items.");
1611     }
1612     }
1613     return success;
1614     }
1615    
1616     /* Identifies objects in the players inventory/on the ground */
1617    
1618     int cast_identify(object *op, object *caster, object *spell) {
1619     object *tmp;
1620     int success = 0, num_ident;
1621    
1622     num_ident = spell->stats.dam + SP_level_dam_adjust(caster, spell);
1623    
1624     if (num_ident < 1) num_ident=1;
1625    
1626    
1627     for (tmp = op->inv; tmp ; tmp = tmp->below) {
1628     if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED) && !tmp->invisible && need_identify(tmp)) {
1629     identify(tmp);
1630     if (op->type==PLAYER) {
1631     new_draw_info_format(NDI_UNIQUE, 0, op,
1632     "You have %s.", long_desc(tmp, op));
1633     if (tmp->msg) {
1634     new_draw_info(NDI_UNIQUE, 0,op, "The item has a story:");
1635     new_draw_info(NDI_UNIQUE, 0,op, tmp->msg);
1636     }
1637     }
1638     num_ident--;
1639     success=1;
1640     if (!num_ident) break;
1641     }
1642     }
1643     /* If all the power of the spell has been used up, don't go and identify
1644     * stuff on the floor. Only identify stuff on the floor if the spell
1645     * was not fully used.
1646     */
1647     if (num_ident) {
1648     for(tmp = get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above)
1649     if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED) && !tmp->invisible &&
1650     need_identify(tmp)) {
1651    
1652     identify(tmp);
1653     if (op->type==PLAYER) {
1654     new_draw_info_format(NDI_UNIQUE, 0,op,
1655     "On the ground is %s.", long_desc(tmp, op));
1656     if (tmp->msg) {
1657     new_draw_info(NDI_UNIQUE, 0,op, "The item has a story:");
1658     new_draw_info(NDI_UNIQUE, 0,op, tmp->msg);
1659     }
1660     esrv_send_item(op, tmp);
1661     }
1662     num_ident--;
1663     success=1;
1664     if (!num_ident) break;
1665     }
1666     }
1667     if (!success)
1668     new_draw_info(NDI_UNIQUE, 0,op, "You can't reach anything unidentified.");
1669     else {
1670     spell_effect(spell, op->x, op->y, op->map, op);
1671     }
1672     return success;
1673     }
1674    
1675    
1676     int cast_detection(object *op, object *caster, object *spell, object *skill) {
1677     object *tmp, *last, *god, *detect;
1678     int done_one, range, mflags, floor, level;
1679     sint16 x, y, nx, ny;
1680     mapstruct *m;
1681    
1682     /* We precompute some values here so that we don't have to keep
1683     * doing it over and over again.
1684     */
1685     god=find_god(determine_god(op));
1686     level=caster_level(caster, spell);
1687     range = spell->range + SP_level_range_adjust(caster, spell);
1688    
1689     if (!skill) skill=caster;
1690    
1691     for (x = op->x - range; x <= op->x + range; x++)
1692     for (y = op->y - range; y <= op->y + range; y++) {
1693    
1694     m = op->map;
1695     mflags = get_map_flags(m, &m, x, y, &nx, &ny);
1696     if (mflags & P_OUT_OF_MAP) continue;
1697    
1698     /* For most of the detections, we only detect objects above the
1699     * floor. But this is not true for show invisible.
1700     * Basically, we just go and find the top object and work
1701     * down - that is easier than working up.
1702     */
1703    
1704     for (last=NULL, tmp=get_map_ob(m, nx, ny); tmp; tmp=tmp->above) last=tmp;
1705     /* Shouldn't happen, but if there are no objects on a space, this
1706     * would happen.
1707     */
1708     if (!last) continue;
1709    
1710     done_one=0;
1711     floor=0;
1712     detect = NULL;
1713     for (tmp=last; tmp; tmp=tmp->below) {
1714    
1715     /* show invisible */
1716     if (QUERY_FLAG(spell, FLAG_MAKE_INVIS) &&
1717     /* Might there be other objects that we can make visibile? */
1718     (tmp->invisible && (QUERY_FLAG(tmp, FLAG_MONSTER) ||
1719     (tmp->type==PLAYER && !QUERY_FLAG(tmp, FLAG_WIZ)) ||
1720     tmp->type==CF_HANDLE ||
1721     tmp->type==TRAPDOOR || tmp->type==EXIT || tmp->type==HOLE ||
1722     tmp->type==BUTTON || tmp->type==TELEPORTER ||
1723     tmp->type==GATE || tmp->type==LOCKED_DOOR ||
1724     tmp->type==WEAPON || tmp->type==ALTAR || tmp->type==SIGN ||
1725     tmp->type==TRIGGER_PEDESTAL || tmp->type==SPECIAL_KEY ||
1726     tmp->type==TREASURE || tmp->type==BOOK ||
1727     tmp->type==HOLY_ALTAR))) {
1728     if(random_roll(0, skill->level-1, op, PREFER_HIGH) > level/4) {
1729     tmp->invisible=0;
1730     done_one = 1;
1731     }
1732     }
1733     if (QUERY_FLAG(tmp, FLAG_IS_FLOOR)) floor=1;
1734    
1735     /* All detections below this point don't descend beneath the floor,
1736     * so just continue on. We could be clever and look at the type of
1737     * detection to completely break out if we don't care about objects beneath
1738     * the floor, but once we get to the floor, not likely a very big issue anyways.
1739     */
1740     if (floor) continue;
1741    
1742     /* I had thought about making detect magic and detect curse
1743     * show the flash the magic item like it does for detect monster.
1744     * however, if the object is within sight, this would then make it
1745     * difficult to see what object is magical/cursed, so the
1746     * effect wouldn't be as apparant.
1747     */
1748    
1749     /* detect magic */
1750     if (QUERY_FLAG(spell, FLAG_KNOWN_MAGICAL) &&
1751     !QUERY_FLAG(tmp,FLAG_KNOWN_MAGICAL) &&
1752     !QUERY_FLAG(tmp, FLAG_IDENTIFIED) &&
1753     is_magical(tmp)) {
1754     SET_FLAG(tmp,FLAG_KNOWN_MAGICAL);
1755     /* make runes more visibile */
1756     if(tmp->type==RUNE && tmp->attacktype&AT_MAGIC)
1757     tmp->stats.Cha/=4;
1758     done_one = 1;
1759     }
1760     /* detect monster */
1761     if (QUERY_FLAG(spell, FLAG_MONSTER) &&
1762     (QUERY_FLAG(tmp, FLAG_MONSTER) || tmp->type==PLAYER)) {
1763     done_one = 2;
1764     if (!detect) detect=tmp;
1765     }
1766     /* Basically, if race is set in the spell, then the creatures race must
1767     * match that. if the spell race is set to GOD, then the gods opposing
1768     * race must match.
1769     */
1770     if (spell->race && QUERY_FLAG(tmp,FLAG_MONSTER) && tmp->race &&
1771     ((!strcmp(spell->race, "GOD") && god && god->slaying && strstr(god->slaying,tmp->race)) ||
1772     (strstr(spell->race, tmp->race)))) {
1773     done_one = 2;
1774     if (!detect) detect=tmp;
1775     }
1776     if (QUERY_FLAG(spell, FLAG_KNOWN_CURSED) && !QUERY_FLAG(tmp, FLAG_KNOWN_CURSED) &&
1777     (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))) {
1778     SET_FLAG(tmp, FLAG_KNOWN_CURSED);
1779     done_one = 1;
1780     }
1781     } /* for stack of objects on this space */
1782    
1783     /* Code here puts an effect of the spell on the space, so you can see
1784     * where the magic is.
1785     */
1786     if (done_one) {
1787     object *detect_ob = arch_to_object(spell->other_arch);
1788     detect_ob->x = nx;
1789     detect_ob->y = ny;
1790     /* if this is set, we want to copy the face */
1791     if (done_one == 2 && detect) {
1792     detect_ob->face = detect->face;
1793     detect_ob->animation_id = detect->animation_id;
1794     detect_ob->anim_speed = detect->anim_speed;
1795     detect_ob->last_anim=0;
1796     /* by default, the detect_ob is already animated */
1797     if (!QUERY_FLAG(detect, FLAG_ANIMATE)) CLEAR_FLAG(detect_ob, FLAG_ANIMATE);
1798     }
1799     insert_ob_in_map(detect_ob, m, op,0);
1800     }
1801     } /* for processing the surrounding spaces */
1802    
1803    
1804     /* Now process objects in the players inventory if detect curse or magic */
1805     if (QUERY_FLAG(spell, FLAG_KNOWN_CURSED) || QUERY_FLAG(spell, FLAG_KNOWN_MAGICAL)) {
1806     done_one = 0;
1807     for (tmp = op->inv; tmp; tmp = tmp->below) {
1808     if (!tmp->invisible && !QUERY_FLAG(tmp, FLAG_IDENTIFIED)) {
1809     if (QUERY_FLAG(spell, FLAG_KNOWN_MAGICAL) &&
1810     is_magical(tmp) && !QUERY_FLAG(tmp,FLAG_KNOWN_MAGICAL)) {
1811     SET_FLAG(tmp,FLAG_KNOWN_MAGICAL);
1812     if (op->type==PLAYER)
1813     esrv_send_item (op, tmp);
1814     }
1815     if (QUERY_FLAG(spell, FLAG_KNOWN_CURSED) && !QUERY_FLAG(tmp, FLAG_KNOWN_CURSED) &&
1816     (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))) {
1817     SET_FLAG(tmp, FLAG_KNOWN_CURSED);
1818     if (op->type==PLAYER)
1819     esrv_send_item (op, tmp);
1820     }
1821     } /* if item is not identified */
1822     } /* for the players inventory */
1823     } /* if detect magic/curse and object is a player */
1824     return 1;
1825     }
1826    
1827    
1828     /**
1829     * Checks if victim has overcharged mana. caster_level is the caster's (skill)
1830     * level whos spell did cause the overcharge.
1831     */
1832     static void charge_mana_effect(object *victim, int caster_level)
1833     {
1834    
1835     /* Prevent explosions for objects without mana. Without this check, doors
1836     * will explode, too.
1837     */
1838     if (victim->stats.maxsp <= 0)
1839     return;
1840    
1841     new_draw_info(NDI_UNIQUE, 0, victim, "You feel energy course through you.");
1842    
1843     if (victim->stats.sp >= victim->stats.maxsp*2) {
1844     object *tmp;
1845    
1846     new_draw_info(NDI_UNIQUE, 0, victim, "Your head explodes!");
1847    
1848     /* Explodes a fireball centered at player */
1849     tmp = get_archetype(EXPLODING_FIREBALL);
1850     tmp->dam_modifier = random_roll(1, caster_level, victim, PREFER_LOW)/5+1;
1851     tmp->stats.maxhp = random_roll(1, caster_level, victim, PREFER_LOW)/10+2;
1852     tmp->x = victim->x;
1853     tmp->y = victim->y;
1854     insert_ob_in_map(tmp, victim->map, NULL, 0);
1855     victim->stats.sp = 2*victim->stats.maxsp;
1856     }
1857     else if (victim->stats.sp >= victim->stats.maxsp*1.88) {
1858     new_draw_info(NDI_UNIQUE, NDI_ORANGE, victim, "You feel like your head is going to explode.");
1859     }
1860     else if (victim->stats.sp >= victim->stats.maxsp*1.66) {
1861     new_draw_info(NDI_UNIQUE, 0, victim, "You get a splitting headache!");
1862     }
1863     else if (victim->stats.sp >= victim->stats.maxsp*1.5) {
1864     new_draw_info(NDI_UNIQUE, 0, victim, "Chaos fills your world.");
1865     confuse_player(victim, victim, 99);
1866     }
1867     else if (victim->stats.sp >= victim->stats.maxsp*1.25) {
1868     new_draw_info(NDI_UNIQUE, 0, victim, "You start hearing voices.");
1869     }
1870     }
1871    
1872     /* cast_transfer
1873     * This spell transfers sp from the player to another person.
1874     * We let the target go above their normal maximum SP.
1875     */
1876    
1877     int cast_transfer(object *op,object *caster, object *spell, int dir) {
1878     object *plyr=NULL;
1879     sint16 x, y;
1880     mapstruct *m;
1881     int mflags;
1882    
1883     m = op->map;
1884     x = op->x+freearr_x[dir];
1885     y = op->y+freearr_y[dir];
1886    
1887     mflags = get_map_flags(m, &m, x, y, &x, &y);
1888    
1889     if (!(mflags & P_OUT_OF_MAP) && mflags & P_IS_ALIVE) {
1890     for(plyr=get_map_ob(m, x, y); plyr!=NULL; plyr=plyr->above)
1891     if (plyr != op && QUERY_FLAG(plyr, FLAG_ALIVE))
1892     break;
1893     }
1894    
1895    
1896     /* If we did not find a player in the specified direction, transfer
1897     * to anyone on top of us. This is used for the rune of transference mostly.
1898     */
1899     if(plyr==NULL)
1900     for(plyr=get_map_ob(op->map,op->x,op->y); plyr!=NULL; plyr=plyr->above)
1901     if (plyr != op && QUERY_FLAG(plyr, FLAG_ALIVE))
1902     break;
1903    
1904     if (!plyr) {
1905     new_draw_info(NDI_BLACK, 0, op, "There is no one there.");
1906     return 0;
1907     }
1908     /* give sp */
1909     if(spell->stats.dam > 0) {
1910     plyr->stats.sp += spell->stats.dam + SP_level_dam_adjust(caster, spell);
1911     charge_mana_effect(plyr, caster_level(caster, spell));
1912     return 1;
1913     }
1914     /* suck sp away. Can't suck sp from yourself */
1915     else if (op != plyr) {
1916     /* old dragin magic used floats. easier to just use ints and divide by 100 */
1917    
1918     int rate = -spell->stats.dam + SP_level_dam_adjust(caster, spell), sucked;
1919    
1920     if (rate > 95) rate=95;
1921    
1922     sucked = (plyr->stats.sp * rate) / 100;
1923     plyr->stats.sp -= sucked;
1924     if (QUERY_FLAG(op, FLAG_ALIVE)) {
1925     /* Player doesn't get full credit */
1926     sucked = (sucked * rate) / 100;
1927     op->stats.sp += sucked;
1928     if (sucked > 0) {
1929     charge_mana_effect(op, caster_level(caster, spell));
1930     }
1931     }
1932     return 1;
1933     }
1934     return 0;
1935     }
1936    
1937    
1938     /* counterspell: nullifies spell effects.
1939     * op is the counterspell object, dir is the direction
1940     * it was cast in.
1941     * Basically, if the object has a magic attacktype,
1942     * this may nullify it.
1943     */
1944     void counterspell(object *op,int dir)
1945     {
1946     object *tmp, *head, *next;
1947     int mflags;
1948     mapstruct *m;
1949     sint16 sx,sy;
1950    
1951     sx = op->x + freearr_x[dir];
1952     sy = op->y + freearr_y[dir];
1953     m = op->map;
1954     mflags = get_map_flags(m, &m, sx, sy, &sx, &sy);
1955     if (mflags & P_OUT_OF_MAP) return;
1956    
1957     for(tmp=get_map_ob(m,sx,sy); tmp!=NULL; tmp=next) {
1958     next = tmp->above;
1959    
1960     /* Need to look at the head object - otherwise, if tmp
1961     * points to a monster, we don't have all the necessary
1962     * info for it.
1963     */
1964     if (tmp->head) head = tmp->head;
1965     else head = tmp;
1966    
1967     /* don't attack our own spells */
1968     if(tmp->owner && tmp->owner == op->owner) continue;
1969    
1970     /* Basically, if the object is magical and not counterspell,
1971     * we will more or less remove the object. Don't counterspell
1972     * monsters either.
1973     */
1974    
1975     if (head->attacktype & AT_MAGIC &&
1976     !(head->attacktype & AT_COUNTERSPELL) &&
1977     !QUERY_FLAG(head,FLAG_MONSTER) &&
1978     (op->level > head->level)) {
1979     remove_ob(head);
1980     free_object(head);
1981     } else switch(head->type) {
1982     case SPELL_EFFECT:
1983     if(op->level > head->level) {
1984     remove_ob(head);
1985     free_object(head);
1986     }
1987     break;
1988    
1989     /* I really don't get this rune code that much - that
1990     * random chance seems really low.
1991     */
1992     case RUNE:
1993     if(rndm(0, 149) == 0) {
1994     head->stats.hp--; /* weaken the rune */
1995     if(!head->stats.hp) {
1996     remove_ob(head);
1997     free_object(head);
1998     }
1999     }
2000     break;
2001     }
2002     }
2003     }
2004    
2005    
2006    
2007     /* cast_consecrate() - a spell to make an altar your god's */
2008     int cast_consecrate(object *op, object *caster, object *spell) {
2009     char buf[MAX_BUF];
2010    
2011     object *tmp, *god=find_god(determine_god(op));
2012    
2013     if(!god) {
2014     new_draw_info(NDI_UNIQUE, 0,op,
2015     "You can't consecrate anything if you don't worship a god!");
2016     return 0;
2017     }
2018    
2019     for(tmp=op->below;tmp;tmp=tmp->below) {
2020     if(QUERY_FLAG(tmp,FLAG_IS_FLOOR)) break;
2021     if(tmp->type==HOLY_ALTAR) {
2022    
2023     if(tmp->level > caster_level(caster, spell)) {
2024     new_draw_info_format(NDI_UNIQUE, 0,op,
2025     "You are not powerful enough to reconsecrate the %s", tmp->name);
2026     return 0;
2027     } else {
2028     /* If we got here, we are consecrating an altar */
2029     if(tmp->name) free_string(tmp->name);
2030     sprintf(buf,"Altar of %s",god->name);
2031     tmp->name = add_string(buf);
2032     tmp->level = caster_level(caster, spell);
2033     tmp->other_arch = god->arch;
2034     if(op->type==PLAYER) esrv_update_item(UPD_NAME, op, tmp);
2035     new_draw_info_format(NDI_UNIQUE,0, op,
2036     "You consecrated the altar to %s!",god->name);
2037     return 1;
2038     }
2039     }
2040     }
2041     new_draw_info(NDI_UNIQUE, 0,op,"You are not standing over an altar!");
2042     return 0;
2043     }
2044    
2045     /* animate_weapon -
2046     * Generalization of staff_to_snake. Makes a golem out of the caster's weapon.
2047     * The golem is based on the archetype specified, modified by the caster's level
2048     * and the attributes of the weapon. The weapon is inserted in the golem's
2049     * inventory so that it falls to the ground when the golem dies.
2050     * This code was very odd - code early on would only let players use the spell,
2051     * yet the code wass full of player checks. I've presumed that the code
2052     * that only let players use it was correct, and removed all the other
2053     * player checks. MSW 2003-01-06
2054     */
2055    
2056     int animate_weapon(object *op,object *caster,object *spell, int dir) {
2057     object *weapon, *tmp;
2058     char buf[MAX_BUF];
2059     int a, i;
2060     sint16 x, y;
2061     mapstruct *m;
2062     materialtype_t *mt;
2063    
2064     if(!spell->other_arch){
2065     new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!");
2066     LOG(llevError,"animate_weapon failed: spell %s missing other_arch!\n", spell->name);
2067     return 0;
2068     }
2069     /* exit if it's not a player using this spell. */
2070     if(op->type!=PLAYER) return 0;
2071    
2072     /* if player already has a golem, abort */
2073     if(op->contr->ranges[range_golem]!=NULL && op->contr->golem_count == op->contr->ranges[range_golem]->count) {
2074     control_golem(op->contr->ranges[range_golem],dir);
2075     return 0;
2076     }
2077    
2078     /* if no direction specified, pick one */
2079     if(!dir)
2080     dir=find_free_spot(NULL,op->map,op->x,op->y,1,9);
2081    
2082     m = op->map;
2083     x = op->x+freearr_x[dir];
2084     y = op->y+freearr_y[dir];
2085    
2086     /* if there's no place to put the golem, abort */
2087     if((dir==-1) || (get_map_flags(m, &m, x, y, &x, &y) & P_OUT_OF_MAP) ||
2088     ((spell->other_arch->clone.move_type & GET_MAP_MOVE_BLOCK(m, x, y)) == spell->other_arch->clone.move_type)) {
2089     new_draw_info(NDI_UNIQUE, 0,op,"There is something in the way.");
2090     return 0;
2091     }
2092    
2093     /* Use the weapon marked by the player. */
2094     weapon = find_marked_object(op);
2095    
2096     if (!weapon) {
2097     new_draw_info(NDI_BLACK, 0, op, "You must mark a weapon to use with this spell!");
2098     return 0;
2099     }
2100     if (spell->race && strcmp(weapon->arch->name, spell->race)) {
2101     new_draw_info(NDI_UNIQUE, 0,op,"The spell fails to transform your weapon.");
2102     return 0;
2103     }
2104     if (weapon->type != WEAPON) {
2105     new_draw_info(NDI_UNIQUE, 0,op,"You need to wield a weapon to animate it.");
2106     return 0;
2107     }
2108     if (QUERY_FLAG(weapon, FLAG_APPLIED)) {
2109     new_draw_info_format(NDI_BLACK, 0, op, "You need to unequip %s before using it in this spell",
2110     query_name(weapon));
2111     return 0;
2112     }
2113    
2114     if (weapon->nrof > 1) {
2115     tmp = get_split_ob(weapon, 1);
2116     esrv_send_item(op, weapon);
2117     weapon = tmp;
2118     }
2119    
2120     /* create the golem object */
2121     tmp=arch_to_object(spell->other_arch);
2122    
2123     /* if animated by a player, give the player control of the golem */
2124     CLEAR_FLAG(tmp, FLAG_MONSTER);
2125     SET_FLAG(tmp, FLAG_FRIENDLY);
2126     tmp->stats.exp=0;
2127     add_friendly_object(tmp);
2128     tmp->type=GOLEM;
2129     set_owner(tmp,op);
2130     set_spell_skill(op, caster, spell, tmp);
2131     op->contr->ranges[range_golem]=tmp;
2132     op->contr->shoottype=range_golem;
2133     op->contr->golem_count = tmp->count;
2134    
2135     /* Give the weapon to the golem now. A bit of a hack to check the
2136     * removed flag - it should only be set if get_split_object was
2137     * used above.
2138     */
2139     if (!QUERY_FLAG(weapon, FLAG_REMOVED))
2140     remove_ob (weapon);
2141     insert_ob_in_ob (weapon, tmp);
2142     esrv_send_item(op, weapon);
2143     /* To do everything necessary to let a golem use the weapon is a pain,
2144     * so instead, just set it as equipped (otherwise, we need to update
2145     * body_info, skills, etc)
2146     */
2147     SET_FLAG (tmp, FLAG_USE_WEAPON);
2148     SET_FLAG(weapon, FLAG_APPLIED);
2149     fix_player(tmp);
2150    
2151     /* There used to be 'odd' code that basically seemed to take the absolute
2152     * value of the weapon->magic an use that. IMO, that doesn't make sense -
2153     * if you're using a crappy weapon, it shouldn't be as good.
2154     */
2155    
2156     /* modify weapon's animated wc */
2157     tmp->stats.wc = tmp->stats.wc - SP_level_range_adjust(caster,spell)
2158     - 5 * weapon->stats.Dex - 2 * weapon->stats.Str - weapon->magic;
2159     if(tmp->stats.wc<-127) tmp->stats.wc = -127;
2160    
2161     /* Modify hit points for weapon */
2162     tmp->stats.maxhp = tmp->stats.maxhp + spell->duration +
2163     SP_level_duration_adjust(caster, spell) +
2164     + 8 * weapon->magic + 12 * weapon->stats.Con;
2165     if(tmp->stats.maxhp<0) tmp->stats.maxhp=10;
2166     tmp->stats.hp = tmp->stats.maxhp;
2167    
2168     /* Modify weapon's damage */
2169     tmp->stats.dam = spell->stats.dam + SP_level_dam_adjust(caster, spell)
2170     + weapon->stats.dam
2171     + weapon->magic
2172     + 5 * weapon->stats.Str;
2173     if(tmp->stats.dam<0) tmp->stats.dam=127;
2174    
2175    
2176     /* attacktype */
2177     if ( ! tmp->attacktype)
2178     tmp->attacktype = AT_PHYSICAL;
2179    
2180     mt = NULL;
2181     if (op->materialname != NULL)
2182     mt = name_to_material(op->materialname);
2183     if (mt != NULL) {
2184     for (i=0; i < NROFATTACKS; i++)
2185     tmp->resist[i] = 50 - (mt->save[i] * 5);
2186     a = mt->save[0];
2187     } else {
2188     for (i=0; i < NROFATTACKS; i++)
2189     tmp->resist[i] = 5;
2190     a = 10;
2191     }
2192     /* Set weapon's immunity */
2193     tmp->resist[ATNR_CONFUSION] = 100;
2194     tmp->resist[ATNR_POISON] = 100;
2195     tmp->resist[ATNR_SLOW] = 100;
2196     tmp->resist[ATNR_PARALYZE] = 100;
2197     tmp->resist[ATNR_TURN_UNDEAD] = 100;
2198     tmp->resist[ATNR_FEAR] = 100;
2199     tmp->resist[ATNR_DEPLETE] = 100;
2200     tmp->resist[ATNR_DEATH] = 100;
2201     tmp->resist[ATNR_BLIND] = 100;
2202    
2203     /* Improve weapon's armour value according to best save vs. physical of its material */
2204    
2205     if (a > 14) a = 14;
2206     tmp->resist[ATNR_PHYSICAL] = 100 - (int)((100.0-(float)tmp->resist[ATNR_PHYSICAL])/(30.0-2.0*a));
2207    
2208     /* Determine golem's speed */
2209     tmp->speed = 0.4 + 0.1 * SP_level_range_adjust(caster,spell);
2210    
2211     if(tmp->speed > 3.33) tmp->speed = 3.33;
2212    
2213     if (!spell->race) {
2214     sprintf(buf, "animated %s", weapon->name);
2215     if(tmp->name) free_string(tmp->name);
2216     tmp->name = add_string(buf);
2217    
2218     tmp->face = weapon->face;
2219     tmp->animation_id = weapon->animation_id;
2220     tmp->anim_speed = weapon->anim_speed;
2221     tmp->last_anim = weapon->last_anim;
2222     tmp->state = weapon->state;
2223     if(QUERY_FLAG(weapon, FLAG_ANIMATE)) {
2224     SET_FLAG(tmp,FLAG_ANIMATE);
2225     } else {
2226     CLEAR_FLAG(tmp,FLAG_ANIMATE);
2227     }
2228     update_ob_speed(tmp);
2229     }
2230    
2231     /* make experience increase in proportion to the strength of the summoned creature. */
2232     tmp->stats.exp *= 1 + (MAX(spell->stats.maxgrace, spell->stats.sp) / caster_level(caster, spell));
2233    
2234     tmp->speed_left= -1;
2235     tmp->x=x;
2236     tmp->y=y;
2237     tmp->direction=dir;
2238     insert_ob_in_map(tmp,m,op,0);
2239     return 1;
2240     }
2241    
2242     /* cast_daylight() - changes the map darkness level *lower* */
2243    
2244     /* cast_change_map_lightlevel: Was cast_daylight/nightfall.
2245     * This changes the light level for the entire map.
2246     */
2247    
2248     int cast_change_map_lightlevel( object *op, object *caster, object *spell ) {
2249     int success;
2250    
2251     if(!op->map) return 0; /* shouldnt happen */
2252    
2253     success=change_map_light(op->map,spell->stats.dam);
2254     if(!success) {
2255     if (spell->stats.dam < 0)
2256     new_draw_info(NDI_UNIQUE,0,op,"It can be no brighter here.");
2257     else
2258     new_draw_info(NDI_UNIQUE,0,op,"It can be no darker here.");
2259     }
2260     return success;
2261     }
2262    
2263    
2264    
2265    
2266    
2267     /* create an aura spell object and put it in the player's inventory.
2268     * as usual, op is player, caster is the object casting the spell,
2269     * spell is the spell object itself.
2270     */
2271     int create_aura(object *op, object *caster, object *spell)
2272     {
2273     int refresh=0;
2274     object *new_aura;
2275    
2276     new_aura = present_arch_in_ob(spell->other_arch, op);
2277     if (new_aura) refresh=1;
2278     else new_aura = arch_to_object(spell->other_arch);
2279    
2280     new_aura->duration = spell->duration +
2281     10* SP_level_duration_adjust(caster,spell);
2282    
2283     new_aura->stats.dam = spell->stats.dam
2284     +SP_level_dam_adjust(caster,spell);
2285    
2286     set_owner(new_aura,op);
2287     set_spell_skill(op, caster, spell, new_aura);
2288     new_aura->attacktype= spell->attacktype;
2289    
2290     new_aura->level = caster_level(caster, spell);
2291     if (refresh)
2292     new_draw_info(NDI_UNIQUE, 0, op, "You recast the spell while in effect.");
2293     else
2294     new_draw_info(NDI_UNIQUE, 0, op, "You create an aura of magical force.");
2295     insert_ob_in_ob(new_aura, op);
2296     return 1;
2297     }
2298    
2299    
2300     /* move aura function. An aura is a part of someone's inventory,
2301     * which he carries with him, but which acts on the map immediately
2302     * around him.
2303     * Aura parameters:
2304     * duration: duration counter.
2305     * attacktype: aura's attacktype
2306     * other_arch: archetype to drop where we attack
2307     */
2308    
2309     void move_aura(object *aura) {
2310     int i, mflags;
2311     object *env;
2312     mapstruct *m;
2313    
2314     /* auras belong in inventories */
2315     env = aura->env;
2316    
2317     /* no matter what we've gotta remove the aura...
2318     * we'll put it back if its time isn't up.
2319     */
2320     remove_ob(aura);
2321    
2322     /* exit if we're out of gas */
2323     if(aura->duration--< 0) {
2324     free_object(aura);
2325     return;
2326     }
2327    
2328     /* auras only exist in inventories */
2329     if(env == NULL || env->map==NULL) {
2330     free_object(aura);
2331     return;
2332     }
2333     aura->x = env->x;
2334     aura->y = env->y;
2335    
2336     /* we need to jump out of the inventory for a bit
2337     * in order to hit the map conveniently.
2338     */
2339     insert_ob_in_map(aura,env->map,aura,0);
2340    
2341     for(i=1;i<9;i++) {
2342     sint16 nx, ny;
2343     nx = aura->x + freearr_x[i];
2344     ny = aura->y + freearr_y[i];
2345     mflags = get_map_flags(env->map, &m, nx, ny, &nx, &ny);
2346    
2347     /* Consider the movement tyep of the person with the aura as
2348     * movement type of the aura. Eg, if the player is flying, the aura
2349     * is flying also, if player is walking, it is on the ground, etc.
2350     */
2351     if (!(mflags & P_OUT_OF_MAP) && !(OB_TYPE_MOVE_BLOCK(env, GET_MAP_MOVE_BLOCK(m, nx, ny)))) {
2352     hit_map(aura,i,aura->attacktype,0);
2353    
2354     if(aura->other_arch) {
2355     object *new_ob;
2356    
2357     new_ob = arch_to_object(aura->other_arch);
2358     new_ob->x = nx;
2359     new_ob->y = ny;
2360     insert_ob_in_map(new_ob,m,aura,0);
2361     }
2362     }
2363     }
2364     /* put the aura back in the player's inventory */
2365     remove_ob(aura);
2366     insert_ob_in_ob(aura, env);
2367     }
2368    
2369     /* moves the peacemaker spell.
2370     * op is the piece object.
2371     */
2372    
2373     void move_peacemaker(object *op) {
2374     object *tmp;
2375    
2376     for(tmp=get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above) {
2377     int atk_lev, def_lev;
2378     object *victim=tmp;
2379    
2380     if (tmp->head) victim=tmp->head;
2381     if (!QUERY_FLAG(victim,FLAG_MONSTER)) continue;
2382     if (QUERY_FLAG(victim,FLAG_UNAGGRESSIVE)) continue;
2383     if (victim->stats.exp == 0) continue;
2384    
2385     def_lev = MAX(1,victim->level);
2386     atk_lev = MAX(1,op->level);
2387    
2388     if (rndm(0, atk_lev-1) > def_lev) {
2389     /* make this sucker peaceful. */
2390    
2391     change_exp(get_owner(op),victim->stats.exp, op->skill, 0);
2392     victim->stats.exp=0;
2393     #if 0
2394     /* No idea why these were all set to zero - if something
2395     * makes this creature agressive, he should still do damage.
2396     */
2397     victim->stats.dam = 0;
2398     victim->stats.sp = 0;
2399     victim->stats.grace = 0;
2400     victim->stats.Pow = 0;
2401     #endif
2402     victim->attack_movement = RANDO2;
2403     SET_FLAG(victim,FLAG_UNAGGRESSIVE);
2404     SET_FLAG(victim,FLAG_RUN_AWAY);
2405     SET_FLAG(victim,FLAG_RANDOM_MOVE);
2406     CLEAR_FLAG(victim,FLAG_MONSTER);
2407     if(victim->name) {
2408     new_draw_info_format(NDI_UNIQUE,0,op->owner,"%s no longer feels like fighting.",victim->name);
2409     }
2410     }
2411     }
2412     }
2413    
2414    
2415     /* This writes a rune that contains the appropriate message.
2416     * There really isn't any adjustments we make.
2417     */
2418    
2419     int write_mark(object *op, object *spell, const char *msg) {
2420     char rune[HUGE_BUF];
2421     object *tmp;
2422    
2423     if (!msg || msg[0] == 0) {
2424     new_draw_info(NDI_UNIQUE, 0, op, "Write what?");
2425     return 0;
2426     }
2427    
2428     if (strcasestr_local(msg, "endmsg")) {
2429     new_draw_info(NDI_UNIQUE, 0, op, "Trying to cheat are we?");
2430     LOG(llevInfo,"write_rune: player %s tried to write bogus rune %s\n", op->name, msg);
2431     return 0;
2432     }
2433     if (!spell->other_arch) return 0;
2434     tmp = arch_to_object(spell->other_arch);
2435     strncpy(rune, msg, HUGE_BUF-2);
2436     rune[HUGE_BUF-2] = 0;
2437     strcat(rune, "\n");
2438     tmp->race = add_string (op->name); /*Save the owner of the rune*/
2439     tmp->msg = add_string(rune);
2440     tmp->x = op->x;
2441     tmp->y = op->y;
2442     insert_ob_in_map(tmp, op->map, op, INS_BELOW_ORIGINATOR);
2443     return 1;
2444     }