ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/pets.C
Revision: 1.18
Committed: Wed Jan 3 02:30:52 2007 UTC (17 years, 5 months ago) by elmex
Content type: text/plain
Branch: MAIN
Changes since 1.17: +4 -4 lines
Log Message:
implemented proper support for empty treasures, which
sadly occur in empty treasure lists. fixing treasurelists
to have no entries at all would be even more complicated,
but even when this is fixed, the current changes only make the
server more crash robust to bad treasures.
Also removed the 'NONE' specialcase for treasure lists. Developers
should use 'none' instead now.

File Contents

# User Rev Content
1 elmex 1.1 /*
2     CrossFire, A Multiplayer game for X-windows
3    
4     Copyright (C) 2002 Mark Wedel & Crossfire Development Team
5     Copyright (C) 1992 Frank Tore Johansen
6    
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11    
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15     GNU General Public License for more details.
16    
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20    
21 root 1.9 The authors can be reached via e-mail at <crossfire@schmorp.de>
22 elmex 1.1 */
23    
24     #include <global.h>
25 root 1.17 #include <sproto.h>
26 elmex 1.1
27     /* given that 'pet' is a friendly object, this function returns a
28     * monster the pet should attack, NULL if nothing appropriate is
29     * found. it basically looks for nasty things around the owner
30     * of the pet to attack.
31     * this is now tilemap aware.
32     */
33 root 1.4 object *
34     get_pet_enemy (object *pet, rv_vector * rv)
35     {
36     object *owner, *tmp, *attacker, *tmp3;
37     int i;
38     sint16 x, y;
39 root 1.11 maptile *nm;
40 root 1.4 int search_arr[SIZEOFFREE];
41     int mflags;
42    
43     attacker = pet->attacked_by; /*pointer to attacking enemy */
44     pet->attacked_by = NULL; /*clear this, since we are dealing with it */
45    
46 root 1.15 if ((owner = pet->owner) != NULL)
47 root 1.4 {
48     /* If the owner has turned on the pet, make the pet
49     * unfriendly.
50     */
51     if ((check_enemy (owner, rv)) == pet)
52     {
53     CLEAR_FLAG (pet, FLAG_FRIENDLY);
54     remove_friendly_object (pet);
55     pet->attack_movement &= ~PETMOVE;
56     return owner;
57     }
58     }
59     else
60     {
61     /* else the owner is no longer around, so the
62     * pet no longer needs to be friendly.
63     */
64     CLEAR_FLAG (pet, FLAG_FRIENDLY);
65     remove_friendly_object (pet);
66     pet->attack_movement &= ~PETMOVE;
67     return NULL;
68     }
69     /* If they are not on the same map, the pet won't be agressive */
70     if (!on_same_map (pet, owner))
71     return NULL;
72    
73     /* See if the pet has an existing enemy. If so, don't start a new one */
74     if ((tmp = check_enemy (pet, rv)) != NULL)
75     {
76     if (tmp == owner && !QUERY_FLAG (pet, FLAG_CONFUSED) && QUERY_FLAG (pet, FLAG_FRIENDLY))
77     /* without this check, you can actually get pets with
78     * enemy set to owner!
79 root 1.2 */
80 root 1.4 pet->enemy = NULL;
81     else
82     return tmp;
83     }
84     get_search_arr (search_arr);
85    
86     if (owner->type == PLAYER && owner->contr->petmode > pet_normal)
87     {
88     if (owner->contr->petmode == pet_sad)
89     {
90     tmp = find_nearest_living_creature (pet);
91     if (tmp != NULL)
92     {
93     get_rangevector (pet, tmp, rv, 0);
94     if (check_enemy (pet, rv) != NULL)
95     return tmp;
96     else
97     pet->enemy = NULL;
98     }
99     /* if we got here we have no enemy */
100     /* we return NULL to avoid heading back to the owner */
101     pet->enemy = NULL;
102     return NULL;
103     }
104     }
105    
106     /* Since the pet has no existing enemy, look for anything nasty
107     * around the owner that it should go and attack.
108     */
109     tmp3 = NULL;
110     for (i = 0; i < SIZEOFFREE; i++)
111     {
112     x = owner->x + freearr_x[search_arr[i]];
113     y = owner->y + freearr_y[search_arr[i]];
114     nm = owner->map;
115     /* Only look on the space if there is something alive there. */
116     mflags = get_map_flags (nm, &nm, x, y, &x, &y);
117     if (!(mflags & P_OUT_OF_MAP) && mflags & P_IS_ALIVE)
118     {
119 root 1.16 for (tmp = GET_MAP_OB (nm, x, y); tmp != NULL; tmp = tmp->above)
120 root 1.4 {
121     object *tmp2 = tmp->head == NULL ? tmp : tmp->head;
122    
123     if (QUERY_FLAG (tmp2, FLAG_ALIVE) && ((!QUERY_FLAG (tmp2, FLAG_FRIENDLY) &&
124     (tmp2->type != PLAYER)) ||
125     should_arena_attack (pet, owner, tmp2))
126     && !QUERY_FLAG (tmp2, FLAG_UNAGGRESSIVE) && tmp2 != pet && tmp2 != owner && can_detect_enemy (pet, tmp2, rv))
127     {
128    
129     if (!can_see_enemy (pet, tmp2))
130     {
131     if (tmp3 != NULL)
132     tmp3 = tmp2;
133     }
134     else
135     {
136     pet->enemy = tmp2;
137     if (check_enemy (pet, rv) != NULL)
138     return tmp2;
139     else
140     pet->enemy = NULL;
141     }
142     } /* if this is a valid enemy */
143     } /* for objects on this space */
144     } /* if there is something living on this space */
145     } /* for loop of spaces around the owner */
146    
147     /* fine, we went through the whole loop and didn't find one we could
148     see, take what we have */
149     if (tmp3 != NULL)
150     {
151     pet->enemy = tmp3;
152     if (check_enemy (pet, rv) != NULL)
153     return tmp3;
154     else
155     pet->enemy = NULL;
156     }
157    
158     /* No threat to owner, check to see if the pet has an attacker */
159     if (attacker)
160     {
161 root 1.8 /* also need to check to make sure it is not freindly */
162     /* or otherwise non-hostile, and is an appropriate target */
163     if (!QUERY_FLAG (attacker, FLAG_FRIENDLY) && on_same_map (pet, attacker))
164 root 1.4 {
165 root 1.8 pet->enemy = attacker;
166    
167     if (check_enemy (pet, rv) != NULL)
168     return attacker;
169     else
170     pet->enemy = NULL;
171 root 1.4 }
172     }
173    
174     /* Don't have an attacker or legal enemy, so look for a new one!.
175     * This looks for one around where the pet is. Thus, you could lead
176     * a pet to danger, then take a few steps back. This code is basically
177     * the same as the code that looks around the owner.
178     */
179     if (owner->type == PLAYER && owner->contr->petmode != pet_defend)
180     {
181     tmp3 = NULL;
182     for (i = 0; i < SIZEOFFREE; i++)
183     {
184     x = pet->x + freearr_x[search_arr[i]];
185     y = pet->y + freearr_y[search_arr[i]];
186     nm = pet->map;
187     /* Only look on the space if there is something alive there. */
188     mflags = get_map_flags (nm, &nm, x, y, &x, &y);
189     if (!(mflags & P_OUT_OF_MAP) && mflags & P_IS_ALIVE)
190     {
191 root 1.16 for (tmp = GET_MAP_OB (nm, x, y); tmp != NULL; tmp = tmp->above)
192 root 1.4 {
193     object *tmp2 = tmp->head == NULL ? tmp : tmp->head;
194    
195     if (QUERY_FLAG (tmp2, FLAG_ALIVE) && ((!QUERY_FLAG (tmp2, FLAG_FRIENDLY) &&
196     (tmp2->type != PLAYER)) ||
197     should_arena_attack (pet, owner, tmp2))
198     && !QUERY_FLAG (tmp2, FLAG_UNAGGRESSIVE) && tmp2 != pet && tmp2 != owner && can_detect_enemy (pet, tmp2, rv))
199     {
200    
201     if (!can_see_enemy (pet, tmp2))
202     {
203     if (tmp3 != NULL)
204     tmp3 = tmp2;
205     }
206     else
207     {
208     pet->enemy = tmp2;
209     if (check_enemy (pet, rv) != NULL)
210     return tmp2;
211     else
212     pet->enemy = NULL;
213 root 1.2 }
214 root 1.4 } /* make sure we can get to the bugger */
215     } /* for objects on this space */
216     } /* if there is something living on this space */
217     } /* for loop of spaces around the pet */
218     } /* pet in defence mode */
219    
220     /* fine, we went through the whole loop and didn't find one we could
221     see, take what we have */
222     if (tmp3 != NULL)
223     {
224     pet->enemy = tmp3;
225     if (check_enemy (pet, rv) != NULL)
226     return tmp3;
227     else
228     pet->enemy = NULL;
229 elmex 1.1 }
230    
231 root 1.4 /* Didn't find anything - return the owner's enemy or NULL */
232     return check_enemy (pet, rv);
233 elmex 1.1 }
234    
235 root 1.4 void
236     terminate_all_pets (object *owner)
237     {
238 elmex 1.1 objectlink *obl, *next;
239 root 1.4
240     for (obl = first_friendly_object; obl != NULL; obl = next)
241     {
242     object *ob = obl->ob;
243    
244     next = obl->next;
245 root 1.15 if (ob->owner == owner)
246 root 1.4 {
247     if (!QUERY_FLAG (ob, FLAG_REMOVED))
248 root 1.13 ob->remove ();
249 root 1.4 remove_friendly_object (ob);
250 root 1.14 ob->destroy ();
251 root 1.4 }
252 elmex 1.1 }
253     }
254    
255     /*
256     * Unfortunately, sometimes, the owner of a pet is in the
257     * process of entering a new map when this is called.
258     * Thus the map isn't loaded yet, and we have to remove
259     * the pet...
260     * Interesting enough, we don't use the passed map structure in
261     * this function.
262     */
263    
264 root 1.4 void
265 root 1.11 remove_all_pets (maptile *map)
266 root 1.4 {
267 elmex 1.1 objectlink *obl, *next;
268     object *owner;
269    
270 root 1.4 for (obl = first_friendly_object; obl != NULL; obl = next)
271 elmex 1.1 {
272 root 1.4 next = obl->next;
273     if (obl->ob->type != PLAYER && QUERY_FLAG (obl->ob, FLAG_FRIENDLY) &&
274 root 1.15 (owner = obl->ob->owner) != NULL && !on_same_map (owner, obl->ob))
275 root 1.4 {
276     /* follow owner checks map status for us */
277     follow_owner (obl->ob, owner);
278     }
279 elmex 1.1 }
280     }
281    
282 root 1.4 int
283     follow_owner (object *ob, object *owner)
284     {
285     object *tmp;
286     int dir;
287 elmex 1.1
288 root 1.4 if (!QUERY_FLAG (ob, FLAG_REMOVED))
289 root 1.13 ob->remove ();
290 elmex 1.1
291 root 1.4 if (owner->map == NULL)
292     {
293     LOG (llevError, "Can't follow owner (%d): no map.\n", &owner->name);
294     goto fail;
295 elmex 1.1 }
296 root 1.4 if (owner->map->in_memory != MAP_IN_MEMORY)
297     {
298     LOG (llevError, "Owner of the pet not on a map in memory!?\n");
299     goto fail;
300 elmex 1.1 }
301    
302 root 1.4 dir = find_free_spot (ob, owner->map, owner->x, owner->y, 1, SIZEOFFREE);
303 elmex 1.1
304 root 1.4 if (dir == -1)
305     {
306     LOG (llevMonster, "No space for pet to follow, freeing %s.\n", &ob->name);
307     goto fail;
308 elmex 1.1 }
309 root 1.4 for (tmp = ob; tmp != NULL; tmp = tmp->more)
310     {
311     tmp->x = owner->x + freearr_x[dir] + (tmp->arch == NULL ? 0 : tmp->arch->clone.x);
312     tmp->y = owner->y + freearr_y[dir] + (tmp->arch == NULL ? 0 : tmp->arch->clone.y);
313     tmp->map = owner->map;
314     if (OUT_OF_REAL_MAP (tmp->map, tmp->x, tmp->y))
315     {
316     tmp->map = get_map_from_coord (tmp->map, &tmp->x, &tmp->y);
317 root 1.2 }
318 elmex 1.1 }
319 root 1.4 insert_ob_in_map (ob, ob->map, NULL, 0);
320     if (owner->type == PLAYER) /* Uh, I hope this is always true... */
321     new_draw_info (NDI_UNIQUE, 0, owner, "Your pet magically appears next to you");
322 elmex 1.1
323 root 1.4 return 0;
324 elmex 1.1
325     fail:
326 root 1.4 remove_friendly_object (ob);
327 root 1.14 ob->destroy ();
328 elmex 1.1
329 root 1.4 return 1;
330 elmex 1.1 }
331    
332 root 1.4 void
333     pet_move (object *ob)
334 elmex 1.1 {
335 root 1.10 int dir, i;
336 root 1.4 sint16 dx, dy;
337     object *ob2, *owner;
338 root 1.11 maptile *m;
339 root 1.4
340     /* Check to see if player pulled out */
341 root 1.15 if ((owner = ob->owner) == NULL)
342 root 1.4 {
343 root 1.13 ob->remove (); /* Will be freed when returning */
344 root 1.4 remove_friendly_object (ob);
345 root 1.14 ob->destroy ();
346 root 1.4 LOG (llevMonster, "Pet: no owner, leaving.\n");
347     return;
348     }
349    
350     /* move monster into the owners map if not in the same map */
351     if (!on_same_map (ob, owner))
352     {
353     follow_owner (ob, owner);
354     return;
355     }
356     /* Calculate Direction */
357     if (owner->type == PLAYER && owner->contr->petmode == pet_sad)
358     {
359     /* in S&D mode, if we have no enemy, run randomly about. */
360     for (i = 0; i < 15; i++)
361     {
362     dir = rndm (1, 8);
363     dx = ob->x + freearr_x[dir];
364     dy = ob->y + freearr_y[dir];
365     m = ob->map;
366     if (get_map_flags (ob->map, &m, dx, dy, &dx, &dy) & P_OUT_OF_MAP)
367     continue;
368     else if (OB_TYPE_MOVE_BLOCK (ob, GET_MAP_MOVE_BLOCK (m, dx, dy)))
369     continue;
370     else
371     break;
372     }
373     }
374     else
375     {
376     struct rv_vector rv;
377 elmex 1.1
378 root 1.4 get_rangevector (ob, ob->owner, &rv, 0);
379     dir = rv.direction;
380 elmex 1.1 }
381 root 1.4 ob->direction = dir;
382 elmex 1.1
383 root 1.4 /* move_ob returns 0 if the object couldn't move. If that is the
384     * case, lets do some other work.
385     */
386     if (!(move_ob (ob, dir, ob)))
387     {
388     object *part;
389    
390     /* the failed move_ob above may destroy the pet, so check here */
391 root 1.10 if (ob->destroyed ())
392 root 1.2 return;
393 root 1.4
394     for (part = ob; part != NULL; part = part->more)
395     {
396     dx = part->x + freearr_x[dir];
397     dy = part->y + freearr_y[dir];
398     m = get_map_from_coord (part->map, &dx, &dy);
399     if (!m)
400     continue;
401    
402 root 1.16 for (ob2 = GET_MAP_OB (m, dx, dy); ob2 != NULL; ob2 = ob2->above)
403 root 1.4 {
404     object *new_ob;
405    
406     new_ob = ob2->head ? ob2->head : ob2;
407     if (new_ob == ob)
408     break;
409     if (new_ob == ob->owner)
410     return;
411 root 1.15 if (new_ob->owner == ob->owner)
412 root 1.2 break;
413 root 1.4
414     /* Hmm. Did we try to move into an enemy monster? If so,
415     * make it our enemy.
416     */
417     if (QUERY_FLAG (new_ob, FLAG_ALIVE) && !QUERY_FLAG (ob, FLAG_UNAGGRESSIVE)
418     && !QUERY_FLAG (new_ob, FLAG_UNAGGRESSIVE) && !QUERY_FLAG (new_ob, FLAG_FRIENDLY))
419     {
420    
421     ob->enemy = new_ob;
422     if (new_ob->enemy == NULL)
423     new_ob->enemy = ob;
424     return;
425     }
426     else if (new_ob->type == PLAYER)
427     {
428     new_draw_info (NDI_UNIQUE, 0, new_ob, "You stand in the way of someones pet.");
429     return;
430 root 1.2 }
431     }
432     }
433 root 1.4 /* Try a different course */
434     dir = absdir (dir + 4 - (RANDOM () % 5) - (RANDOM () % 5));
435     (void) move_ob (ob, dir, ob);
436 elmex 1.1 }
437 root 1.4 return;
438 elmex 1.1 }
439    
440     /****************************************************************************
441     *
442     * GOLEM SPELL CODE
443     *
444     ****************************************************************************/
445    
446     /* fix_summon_pet() - this makes multisquare/single square monsters
447     * proper for map insertion.
448     * at is the archetype, op is the caster of the spell, dir is the
449     * direction the monster should be placed in.
450     * is_golem is to note that this is a golem spell.
451     */
452 root 1.4 object *
453     fix_summon_pet (archetype *at, object *op, int dir, int is_golem)
454     {
455     archetype *atmp;
456     object *tmp = NULL, *prev = NULL, *head = NULL;
457    
458     for (atmp = at; atmp != NULL; atmp = atmp->more)
459     {
460     tmp = arch_to_object (atmp);
461     if (atmp == at)
462     {
463     if (!is_golem)
464     SET_FLAG (tmp, FLAG_MONSTER);
465 root 1.15 tmp->set_owner (op);
466 root 1.4 if (op->type == PLAYER)
467     {
468     tmp->stats.exp = 0;
469     add_friendly_object (tmp);
470     SET_FLAG (tmp, FLAG_FRIENDLY);
471     if (is_golem)
472     CLEAR_FLAG (tmp, FLAG_MONSTER);
473     }
474     else
475     {
476     if (QUERY_FLAG (op, FLAG_FRIENDLY))
477     {
478 root 1.15 object *owner = op->owner;
479 root 1.4
480     if (owner != NULL)
481     { /* For now, we transfer ownership */
482 root 1.15 tmp->set_owner (owner);
483 root 1.4 tmp->attack_movement = PETMOVE;
484     add_friendly_object (tmp);
485     SET_FLAG (tmp, FLAG_FRIENDLY);
486 root 1.2 }
487     }
488     }
489 root 1.4 if (op->type != PLAYER || !is_golem)
490     {
491     tmp->attack_movement = PETMOVE;
492     tmp->speed_left = -1;
493     tmp->type = 0;
494     tmp->enemy = op->enemy;
495     }
496     else
497     tmp->type = GOLEM;
498    
499 root 1.2 }
500 root 1.4 if (head == NULL)
501     head = tmp;
502     tmp->x = op->x + freearr_x[dir] + tmp->arch->clone.x;
503     tmp->y = op->y + freearr_y[dir] + tmp->arch->clone.y;
504     tmp->map = op->map;
505     if (tmp->invisible)
506     tmp->invisible = 0;
507     if (head != tmp)
508     tmp->head = head, prev->more = tmp;
509     prev = tmp;
510     }
511     head->direction = dir;
512    
513     /* need to change some monster attr to prevent problems/crashing */
514     head->last_heal = 0;
515     head->last_eat = 0;
516     head->last_grace = 0;
517     head->last_sp = 0;
518     head->other_arch = NULL;
519     head->stats.exp = 0;
520     CLEAR_FLAG (head, FLAG_CHANGING);
521     CLEAR_FLAG (head, FLAG_STAND_STILL);
522     CLEAR_FLAG (head, FLAG_GENERATOR);
523     CLEAR_FLAG (head, FLAG_SPLITTING);
524     if (head->attacktype & AT_GHOSTHIT)
525     head->attacktype = (AT_PHYSICAL | AT_DRAIN);
526 elmex 1.1
527 root 1.4 return head;
528 elmex 1.1 }
529    
530     /* updated this to allow more than the golem 'head' to attack */
531 root 1.4
532 elmex 1.1 /* op is the golem to be moved. */
533    
534 root 1.4 void
535     move_golem (object *op)
536     {
537     int made_attack = 0;
538     object *tmp;
539    
540     if (QUERY_FLAG (op, FLAG_MONSTER))
541     return; /* Has already been moved */
542    
543 root 1.15 if (op->owner == NULL)
544 root 1.4 {
545     LOG (llevDebug, "Golem without owner destructed.\n");
546 root 1.13 op->remove ();
547 root 1.14 op->destroy ();
548 root 1.4 return;
549     }
550 root 1.10
551 root 1.4 /* It would be nice to have a cleaner way of what message to print
552     * when the golem expires than these hard coded entries.
553     * Note it is intentional that a golems duration is based on its
554     * hp, and not duration
555     */
556     if (--op->stats.hp < 0)
557     {
558     if (op->msg)
559     new_draw_info (NDI_UNIQUE, 0, op->owner, op->msg);
560 root 1.12
561     op->owner->contr->ranges[range_golem] = 0;
562 root 1.4 remove_friendly_object (op);
563 root 1.13 op->remove ();
564 root 1.14 op->destroy ();
565 root 1.4 return;
566     }
567    
568     /* Do golem attacks/movement for single & multisq golems.
569     * Assuming here that op is the 'head' object. Pass only op to
570     * move_ob (makes recursive calls to other parts)
571     * move_ob returns 0 if the creature was not able to move.
572     */
573     if (move_ob (op, op->direction, op))
574     return;
575 root 1.10
576     if (op->destroyed ())
577 root 1.4 return;
578 elmex 1.1
579 root 1.4 for (tmp = op; tmp; tmp = tmp->more)
580     {
581     sint16 x = tmp->x + freearr_x[op->direction], y = tmp->y + freearr_y[op->direction];
582     object *victim;
583 root 1.11 maptile *m;
584 root 1.4 int mflags;
585    
586     m = op->map;
587     mflags = get_map_flags (m, &m, x, y, &x, &y);
588    
589     if (mflags & P_OUT_OF_MAP)
590     continue;
591    
592 root 1.16 for (victim = GET_MAP_OB (op->map, x, y); victim; victim = victim->above)
593 root 1.4 if (QUERY_FLAG (victim, FLAG_ALIVE))
594     break;
595    
596     /* We used to call will_hit_self to make sure we don't
597     * hit ourselves, but that didn't work, and I don't really
598     * know if that was more efficient anyways than this.
599     * This at least works. Note that victim->head can be NULL,
600     * but since we are not trying to dereferance that pointer,
601     * that isn't a problem.
602     */
603     if (victim && victim != op && victim->head != op)
604     {
605 elmex 1.1
606 root 1.4 /* for golems with race fields, we don't attack
607     * aligned races
608     */
609 root 1.2
610 root 1.4 if (victim->race && op->race && strstr (op->race, victim->race))
611     {
612     if (op->owner)
613     new_draw_info_format (NDI_UNIQUE, 0, op->owner, "%s avoids damaging %s.", &op->name, &victim->name);
614     }
615     else if (victim == op->owner)
616     {
617     if (op->owner)
618     new_draw_info_format (NDI_UNIQUE, 0, op->owner, "%s avoids damaging you.", &op->name);
619 root 1.2 }
620 root 1.4 else
621     {
622     attack_ob (victim, op);
623     made_attack = 1;
624     }
625     } /* If victim */
626 elmex 1.1 }
627 root 1.4 if (made_attack)
628     update_object (op, UP_OBJ_FACE);
629 elmex 1.1 }
630    
631     /* this is a really stupid function when you get down and
632     * look at it. Keep it here for the time being - makes life
633     * easier if we ever decide to do more interesting thing with
634     * controlled golems.
635     */
636 root 1.4 void
637     control_golem (object *op, int dir)
638     {
639     op->direction = dir;
640 elmex 1.1 }
641    
642     /* summon golem: summons a monster for 'op'. caster is the object
643     * casting the spell, dir is the direction to place the monster,
644     * at is the archetype of the monster, and spob is the spell
645     * object. At this stage, all spob is really used for is to
646     * adjust some values in the monster.
647     */
648 root 1.4 int
649     summon_golem (object *op, object *caster, int dir, object *spob)
650     {
651     object *tmp, *god = NULL;
652     archetype *at;
653     char buf[MAX_BUF];
654    
655     /* Because there can be different golem spells, player may want to
656     * 'lose' their old golem.
657     */
658 root 1.12 if (op->type == PLAYER && op->contr->ranges[range_golem])
659 root 1.4 {
660     new_draw_info (NDI_UNIQUE, 0, op, "You dismiss your existing golem.");
661 root 1.13 op->contr->ranges[range_golem]->remove ();
662 root 1.14 op->contr->ranges[range_golem]->destroy ();
663 root 1.12 op->contr->ranges[range_golem] = 0;
664 root 1.4 }
665    
666     if (spob->other_arch)
667     at = spob->other_arch;
668     else if (spob->race)
669     {
670     god = find_god (determine_god (caster));
671 elmex 1.1
672 root 1.4 if (!god)
673     {
674     new_draw_info_format (NDI_UNIQUE, 0, op, "You must worship a god to cast %s.", &spob->name);
675     return 0;
676 root 1.2 }
677 root 1.6
678 root 1.4 at = determine_holy_arch (god, spob->race);
679 root 1.6
680 root 1.4 if (!at)
681     {
682     new_draw_info_format (NDI_UNIQUE, 0, op, "%s has no %s for you to call.", &god->name, &spob->race);
683     return 0;
684 root 1.2 }
685 elmex 1.1 }
686 root 1.4 else
687     {
688     LOG (llevError, "Spell %s lacks other_arch\n", &spob->name);
689     return 0;
690     }
691    
692     if (!dir)
693     dir = find_free_spot (NULL, op->map, op->x, op->y, 1, SIZEOFFREE1 + 1);
694    
695 root 1.6 if (dir == -1 || ob_blocked (&at->clone, op->map, op->x + freearr_x[dir], op->y + freearr_y[dir]))
696 root 1.4 {
697     new_draw_info (NDI_UNIQUE, 0, op, "There is something in the way.");
698     return 0;
699     }
700     /* basically want to get proper map/coordinates for this object */
701 elmex 1.1
702 root 1.4 if (!(tmp = fix_summon_pet (at, op, dir, GOLEM)))
703     {
704     new_draw_info (NDI_UNIQUE, 0, op, "Your spell fails.");
705     return 0;
706     }
707 elmex 1.1
708 root 1.4 if (op->type == PLAYER)
709     {
710     tmp->type = GOLEM;
711 root 1.15 tmp->set_owner (op);
712 root 1.4 set_spell_skill (op, caster, spob, tmp);
713     op->contr->ranges[range_golem] = tmp;
714     /* give the player control of the golem */
715     op->contr->shoottype = range_golem;
716 elmex 1.1 }
717 root 1.4 else
718     {
719     if (QUERY_FLAG (op, FLAG_FRIENDLY))
720     {
721 root 1.15 object *owner = op->owner;
722 elmex 1.1
723 root 1.4 if (owner != NULL)
724     { /* For now, we transfer ownership */
725 root 1.15 tmp->set_owner (owner);
726 root 1.4 tmp->attack_movement = PETMOVE;
727     add_friendly_object (tmp);
728     SET_FLAG (tmp, FLAG_FRIENDLY);
729     }
730     }
731 root 1.6
732 root 1.4 SET_FLAG (tmp, FLAG_MONSTER);
733 elmex 1.1 }
734    
735 root 1.4 /* make the speed positive. */
736     tmp->speed = FABS (tmp->speed);
737    
738     /* This sets the level dependencies on dam and hp for monsters */
739     /* players can't cope with too strong summonings. */
740     /* but monsters can. reserve these for players. */
741     if (op->type == PLAYER)
742     {
743     tmp->stats.hp += spob->duration + SP_level_duration_adjust (caster, spob);
744 root 1.6
745 root 1.4 if (!spob->stats.dam)
746     tmp->stats.dam += SP_level_dam_adjust (caster, spob);
747     else
748     tmp->stats.dam = spob->stats.dam + SP_level_dam_adjust (caster, spob);
749 root 1.6
750 root 1.4 tmp->speed += .02 * SP_level_range_adjust (caster, spob);
751     tmp->speed = MIN (tmp->speed, 1.0);
752 root 1.6
753 root 1.4 if (spob->attacktype)
754     tmp->attacktype = spob->attacktype;
755     }
756 root 1.6
757 root 1.4 tmp->stats.wc -= SP_level_range_adjust (caster, spob);
758    
759     /* limit the speed to 0.3 for non-players, 1 for players. */
760    
761     /* make experience increase in proportion to the strength.
762     * this is a bit simplistic - we are basically just looking at how
763     * often the sp doubles and use that as the ratio.
764     */
765     tmp->stats.exp *= 1 + (MAX (spob->stats.maxgrace, spob->stats.sp) / caster_level (caster, spob));
766     tmp->speed_left = 0;
767     tmp->direction = dir;
768    
769     /* Holy spell - some additional tailoring */
770     if (god)
771     {
772     object *tmp2;
773    
774     sprintf (buf, "%s of %s", &spob->name, &god->name);
775     buf[0] = toupper (buf[0]);
776    
777     for (tmp2 = tmp; tmp2; tmp2 = tmp2->more)
778     tmp2->name = buf;
779    
780     tmp->attacktype |= god->attacktype;
781     memcpy (tmp->resist, god->resist, sizeof (tmp->resist));
782     tmp->race = god->race;
783     tmp->slaying = god->slaying;
784 root 1.6
785 root 1.4 /* safety, we must allow a god's servants some reasonable attack */
786     if (!(tmp->attacktype & AT_PHYSICAL))
787     tmp->attacktype |= AT_PHYSICAL;
788 elmex 1.1 }
789    
790 root 1.4 insert_ob_in_map (tmp, tmp->map, op, 0);
791     return 1;
792 elmex 1.1 }
793    
794    
795     /***************************************************************************
796     *
797     * Summon monster/pet/other object code
798     *
799     ***************************************************************************/
800    
801     /* Returns a monster (chosen at random) that this particular player (and his
802     * god) find acceptable. This checks level, races allowed by god, etc
803     * to determine what is acceptable.
804     * This returns NULL if no match was found.
805     */
806    
807 root 1.4 object *
808     choose_cult_monster (object *pl, object *god, int summon_level)
809     {
810     char buf[MAX_BUF];
811     const char *race;
812     int racenr, mon_nr, i;
813     racelink *list;
814     objectlink *tobl;
815     object *otmp;
816    
817     /* Determine the number of races available */
818     racenr = 0;
819     strcpy (buf, god->race);
820     race = strtok (buf, ",");
821     while (race)
822     {
823     racenr++;
824     race = strtok (NULL, ",");
825     }
826    
827     /* next, randomly select a race from the aligned_races string */
828     if (racenr > 1)
829     {
830     racenr = rndm (0, racenr - 1);
831     strcpy (buf, god->race);
832     race = strtok (buf, ",");
833     for (i = 0; i < racenr; i++)
834     race = strtok (NULL, ",");
835     }
836     else
837     race = god->race;
838    
839    
840     /* see if a we can match a race list of monsters. This should not
841     * happen, so graceful recovery isn't really needed, but this sanity
842     * checking is good for cases where the god archetypes mismatch the
843     * race file
844     */
845     if ((list = find_racelink (race)) == NULL)
846     {
847     new_draw_info_format (NDI_UNIQUE, 0, pl, "The spell fails! %s's creatures are beyond the range of your summons", &god->name);
848     LOG (llevDebug, "choose_cult_monster() requested non-existent aligned race!\n");
849     return 0;
850     }
851    
852     /* search for an apprplritate monster on this race list */
853     mon_nr = 0;
854     for (tobl = list->member; tobl; tobl = tobl->next)
855     {
856     otmp = tobl->ob;
857     if (!otmp || !QUERY_FLAG (otmp, FLAG_MONSTER))
858     continue;
859     if (otmp->level <= summon_level)
860     mon_nr++;
861 elmex 1.1 }
862 root 1.4
863     /* If this god has multiple race entries, we should really choose another.
864     * But then we either need to track which ones we have tried, or just
865     * make so many calls to this function, and if we get so many without
866     * a valid entry, assuming nothing is available and quit.
867     */
868     if (!mon_nr)
869 elmex 1.1 return NULL;
870 root 1.4
871     mon_nr = rndm (0, mon_nr - 1);
872     for (tobl = list->member; tobl; tobl = tobl->next)
873     {
874     otmp = tobl->ob;
875     if (!otmp || !QUERY_FLAG (otmp, FLAG_MONSTER))
876     continue;
877     if (otmp->level <= summon_level && !mon_nr--)
878     return otmp;
879     }
880     /* This should not happen */
881     LOG (llevDebug, "choose_cult_monster() mon_nr was set, but did not find a monster\n");
882     return NULL;
883 elmex 1.1 }
884    
885 root 1.4 int
886     summon_object (object *op, object *caster, object *spell_ob, int dir, const char *stringarg)
887 elmex 1.1 {
888 root 1.4 sint16 x, y, nrof = 1, i;
889     archetype *summon_arch;
890     int ndir;
891    
892     if (spell_ob->other_arch)
893 root 1.5 summon_arch = spell_ob->other_arch;
894 root 1.4 else if (spell_ob->randomitems)
895     {
896     int level = caster_level (caster, spell_ob);
897 root 1.5 treasure *tr, *lasttr = NULL;
898 root 1.4
899 root 1.5 shstr_cmp sparam (stringarg);
900    
901     /* In old code, this was a very convoluted for statement,
902 root 1.4 * with all the checks in the 'for' portion itself. Much
903     * more readable to break some of the conditions out.
904     */
905     for (tr = spell_ob->randomitems->items; tr; tr = tr->next)
906     {
907 elmex 1.18 if (!tr->item)
908     continue;
909    
910 root 1.4 if (level < tr->magic)
911     break;
912 root 1.5
913 root 1.4 lasttr = tr;
914 root 1.5
915 root 1.7 if (tr->item->name == sparam)
916 root 1.4 break;
917     }
918 root 1.5
919 root 1.4 if (!lasttr)
920     {
921     LOG (llevError, "Treasurelist %s did not generate a valid entry in summon_object\n", &spell_ob->randomitems->name);
922     new_draw_info (NDI_UNIQUE, 0, op, "The spell fails to summon any monsters.");
923     return 0;
924     }
925 root 1.5
926 root 1.4 summon_arch = lasttr->item;
927 elmex 1.18 nrof = lasttr->nrof;
928 root 1.4 }
929     else if (spell_ob->race && !strcmp (spell_ob->race, "GODCULTMON"))
930     {
931     object *god = find_god (determine_god (op)), *mon, *owner;
932     int summon_level, tries;
933    
934 root 1.15 if (!god && ((owner = op->owner) != NULL))
935 root 1.5 god = find_god (determine_god (owner));
936    
937 root 1.4 /* If we can't find a god, can't get what monster to summon */
938     if (!god)
939     return 0;
940 elmex 1.1
941 root 1.4 if (!god->race)
942     {
943     new_draw_info_format (NDI_UNIQUE, 0, op, "%s has no creatures that you may summon!", &god->name);
944     return 0;
945 root 1.2 }
946 root 1.5
947 root 1.4 /* the summon level */
948     summon_level = caster_level (caster, spell_ob);
949     if (summon_level == 0)
950     summon_level = 1;
951 root 1.5
952 root 1.4 tries = 0;
953     do
954     {
955     mon = choose_cult_monster (op, god, summon_level);
956     if (!mon)
957     {
958     new_draw_info_format (NDI_UNIQUE, 0, op, "%s fails to send anything.", &god->name);
959     return 0;
960     }
961 root 1.5
962 root 1.4 ndir = dir;
963 root 1.5
964 root 1.4 if (!ndir)
965     ndir = find_free_spot (mon, op->map, op->x, op->y, 1, SIZEOFFREE);
966 root 1.5
967 root 1.4 if (ndir == -1 || ob_blocked (mon, op->map, op->x + freearr_x[ndir], op->y + freearr_y[ndir]))
968     {
969     ndir = -1;
970     if (++tries == 5)
971     {
972     new_draw_info (NDI_UNIQUE, 0, op, "There is something in the way.");
973     return 0;
974 root 1.2 }
975     }
976 root 1.4 }
977     while (ndir == -1);
978 root 1.5
979 root 1.4 if (mon->level > (summon_level / 2))
980     nrof = random_roll (1, 2, op, PREFER_HIGH);
981     else
982     nrof = die_roll (2, 2, op, PREFER_HIGH);
983 root 1.5
984 root 1.4 summon_arch = mon->arch;
985     }
986     else
987 root 1.5 summon_arch = 0;
988 elmex 1.1
989 root 1.4 if (spell_ob->stats.dam)
990     nrof += spell_ob->stats.dam + SP_level_dam_adjust (caster, spell_ob);
991 elmex 1.1
992 root 1.4 if (!summon_arch)
993     {
994     new_draw_info (NDI_UNIQUE, 0, op, "There is no monsters available for summoning.");
995     return 0;
996 elmex 1.1 }
997    
998 root 1.4 for (i = 1; i <= nrof; i++)
999     {
1000     archetype *atmp;
1001     object *prev = NULL, *head = NULL, *tmp;
1002    
1003     if (dir)
1004     {
1005     ndir = dir;
1006     dir = absdir (dir + 1);
1007     }
1008     else
1009     ndir = find_free_spot (&summon_arch->clone, op->map, op->x, op->y, 1, SIZEOFFREE);
1010    
1011     if (ndir > 0)
1012     {
1013     x = freearr_x[ndir];
1014     y = freearr_y[ndir];
1015     }
1016    
1017     if (ndir == -1 || ob_blocked (&summon_arch->clone, op->map, op->x + x, op->y + y))
1018     {
1019     new_draw_info (NDI_UNIQUE, 0, op, "There is something in the way.");
1020     if (nrof > 1)
1021     new_draw_info (NDI_UNIQUE, 0, op, "No more pets for this casting.");
1022    
1023     return nrof > 1;
1024     }
1025    
1026     for (atmp = summon_arch; atmp != NULL; atmp = atmp->more)
1027     {
1028     tmp = arch_to_object (atmp);
1029     if (atmp == summon_arch)
1030     {
1031     if (QUERY_FLAG (tmp, FLAG_MONSTER))
1032     {
1033 root 1.15 tmp->set_owner (op);
1034 root 1.4 set_spell_skill (op, caster, spell_ob, tmp);
1035     tmp->enemy = op->enemy;
1036     tmp->type = 0;
1037     CLEAR_FLAG (tmp, FLAG_SLEEP);
1038 root 1.5
1039 root 1.4 if (op->type == PLAYER || QUERY_FLAG (op, FLAG_FRIENDLY))
1040     {
1041     /* If this is not set, we make it friendly */
1042     if (!QUERY_FLAG (spell_ob, FLAG_MONSTER))
1043     {
1044     SET_FLAG (tmp, FLAG_FRIENDLY);
1045     add_friendly_object (tmp);
1046     tmp->stats.exp = 0;
1047 root 1.5
1048 root 1.4 if (spell_ob->attack_movement)
1049     tmp->attack_movement = spell_ob->attack_movement;
1050 root 1.5
1051 root 1.15 if (op->owner)
1052     tmp->set_owner (op->owner);
1053 root 1.2 }
1054     }
1055     }
1056 root 1.5
1057 root 1.4 if (tmp->speed > MIN_ACTIVE_SPEED)
1058     tmp->speed_left = -1;
1059     }
1060 root 1.5
1061 root 1.4 if (head == NULL)
1062     head = tmp;
1063     else
1064     {
1065     tmp->head = head;
1066     prev->more = tmp;
1067 root 1.2 }
1068 root 1.5
1069 root 1.4 prev = tmp;
1070     tmp->x = op->x + x + tmp->arch->clone.x;
1071     tmp->y = op->y + y + tmp->arch->clone.y;
1072     tmp->map = op->map;
1073     }
1074 root 1.5
1075 root 1.4 head->direction = freedir[ndir];
1076     head->stats.exp = 0;
1077     head = insert_ob_in_map (head, head->map, op, 0);
1078 root 1.5
1079 root 1.4 if (head && head->randomitems)
1080     {
1081     object *tmp;
1082    
1083     create_treasure (head->randomitems, head, GT_APPLY | GT_STARTEQUIP, 6, 0);
1084     for (tmp = head->inv; tmp; tmp = tmp->below)
1085     if (!tmp->nrof)
1086     SET_FLAG (tmp, FLAG_NO_DROP);
1087 root 1.2 }
1088 root 1.4 } /* for i < nrof */
1089 root 1.5
1090 root 1.4 return 1;
1091 elmex 1.1 }
1092    
1093     /* recursively look through the owner property of objects until the real owner
1094     is found */
1095 root 1.4 object *
1096     get_real_owner (object *ob)
1097     {
1098     object *realowner = ob;
1099    
1100     if (realowner == NULL)
1101     return NULL;
1102    
1103 root 1.15 while (realowner->owner != NULL)
1104 root 1.4 {
1105 root 1.15 realowner = realowner->owner;
1106 root 1.4 }
1107     return realowner;
1108 elmex 1.1 }
1109 root 1.4
1110 elmex 1.1 /* determines if checks so pets don't attack players or other pets should be
1111     overruled by the arena petmode */
1112 root 1.4 int
1113     should_arena_attack (object *pet, object *owner, object *target)
1114     {
1115     object *rowner, *towner;
1116    
1117     /* exit if the target, pet, or owner is null. */
1118     if ((target == NULL) || (pet == NULL) || (owner == NULL))
1119     return 0;
1120    
1121     /* get the owners of itself and the target, this is to deal with pets of
1122     pets */
1123     rowner = get_real_owner (owner);
1124     if (target->type != PLAYER)
1125     {
1126     towner = get_real_owner (target);
1127     }
1128     else
1129     {
1130     towner = 0;
1131     }
1132    
1133     /* if the pet has now owner, exit with error */
1134     if (rowner == NULL)
1135     {
1136     LOG (llevError, "Pet has no owner.\n");
1137     return 0;
1138     }
1139    
1140     /* if the target is not a player, and has no owner, we shouldn't be here
1141     */
1142     if ((towner == NULL) && (target->type != PLAYER))
1143     {
1144     LOG (llevError, "Target is not a player but has no owner. We should not be here.\n");
1145     return 0;
1146     }
1147    
1148     /* make sure that the owner is a player */
1149     if (rowner->type != PLAYER)
1150     return 0;
1151    
1152     /* abort if the petmode is not arena */
1153     if (rowner->contr->petmode != pet_arena)
1154     return 0;
1155    
1156     /* abort if the pet, it's owner, or the target is not on battleground */
1157     if (!(op_on_battleground (pet, NULL, NULL) && op_on_battleground (owner, NULL, NULL) && op_on_battleground (target, NULL, NULL)))
1158     return 0;
1159    
1160     /* if the target is a monster, make sure it's owner is not the same */
1161     if ((target->type != PLAYER) && (rowner == towner))
1162     return 0;
1163    
1164     /* check if the target is a player which affects how it will handle
1165     parties */
1166     if (target->type != PLAYER)
1167     {
1168     /* if the target is owned by a player make sure than make sure
1169     it's not in the same party */
1170     if ((towner->type == PLAYER) && (rowner->contr->party != NULL))
1171     {
1172     if (rowner->contr->party == towner->contr->party)
1173     return 0;
1174     }
1175     }
1176     else
1177     {
1178     /* if the target is a player make sure than make sure it's not
1179     in the same party */
1180     if (rowner->contr->party != NULL)
1181     {
1182     if (rowner->contr->party == target->contr->party)
1183     return 0;
1184 root 1.2 }
1185 root 1.4 }
1186    
1187     return 1;
1188 elmex 1.1 }