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

# Content
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 The authors can be reached via e-mail at <crossfire@schmorp.de>
22 */
23
24 #include <global.h>
25 #include <sproto.h>
26
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 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 maptile *nm;
40 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 if ((owner = pet->owner) != NULL)
47 {
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 */
80 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 for (tmp = GET_MAP_OB (nm, x, y); tmp != NULL; tmp = tmp->above)
120 {
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 /* 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 {
165 pet->enemy = attacker;
166
167 if (check_enemy (pet, rv) != NULL)
168 return attacker;
169 else
170 pet->enemy = NULL;
171 }
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 for (tmp = GET_MAP_OB (nm, x, y); tmp != NULL; tmp = tmp->above)
192 {
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 }
214 } /* 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 }
230
231 /* Didn't find anything - return the owner's enemy or NULL */
232 return check_enemy (pet, rv);
233 }
234
235 void
236 terminate_all_pets (object *owner)
237 {
238 objectlink *obl, *next;
239
240 for (obl = first_friendly_object; obl != NULL; obl = next)
241 {
242 object *ob = obl->ob;
243
244 next = obl->next;
245 if (ob->owner == owner)
246 {
247 if (!QUERY_FLAG (ob, FLAG_REMOVED))
248 ob->remove ();
249 remove_friendly_object (ob);
250 ob->destroy ();
251 }
252 }
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 void
265 remove_all_pets (maptile *map)
266 {
267 objectlink *obl, *next;
268 object *owner;
269
270 for (obl = first_friendly_object; obl != NULL; obl = next)
271 {
272 next = obl->next;
273 if (obl->ob->type != PLAYER && QUERY_FLAG (obl->ob, FLAG_FRIENDLY) &&
274 (owner = obl->ob->owner) != NULL && !on_same_map (owner, obl->ob))
275 {
276 /* follow owner checks map status for us */
277 follow_owner (obl->ob, owner);
278 }
279 }
280 }
281
282 int
283 follow_owner (object *ob, object *owner)
284 {
285 object *tmp;
286 int dir;
287
288 if (!QUERY_FLAG (ob, FLAG_REMOVED))
289 ob->remove ();
290
291 if (owner->map == NULL)
292 {
293 LOG (llevError, "Can't follow owner (%d): no map.\n", &owner->name);
294 goto fail;
295 }
296 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 }
301
302 dir = find_free_spot (ob, owner->map, owner->x, owner->y, 1, SIZEOFFREE);
303
304 if (dir == -1)
305 {
306 LOG (llevMonster, "No space for pet to follow, freeing %s.\n", &ob->name);
307 goto fail;
308 }
309 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 }
318 }
319 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
323 return 0;
324
325 fail:
326 remove_friendly_object (ob);
327 ob->destroy ();
328
329 return 1;
330 }
331
332 void
333 pet_move (object *ob)
334 {
335 int dir, i;
336 sint16 dx, dy;
337 object *ob2, *owner;
338 maptile *m;
339
340 /* Check to see if player pulled out */
341 if ((owner = ob->owner) == NULL)
342 {
343 ob->remove (); /* Will be freed when returning */
344 remove_friendly_object (ob);
345 ob->destroy ();
346 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
378 get_rangevector (ob, ob->owner, &rv, 0);
379 dir = rv.direction;
380 }
381 ob->direction = dir;
382
383 /* 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 if (ob->destroyed ())
392 return;
393
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 for (ob2 = GET_MAP_OB (m, dx, dy); ob2 != NULL; ob2 = ob2->above)
403 {
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 if (new_ob->owner == ob->owner)
412 break;
413
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 }
431 }
432 }
433 /* Try a different course */
434 dir = absdir (dir + 4 - (RANDOM () % 5) - (RANDOM () % 5));
435 (void) move_ob (ob, dir, ob);
436 }
437 return;
438 }
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 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 tmp->set_owner (op);
466 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 object *owner = op->owner;
479
480 if (owner != NULL)
481 { /* For now, we transfer ownership */
482 tmp->set_owner (owner);
483 tmp->attack_movement = PETMOVE;
484 add_friendly_object (tmp);
485 SET_FLAG (tmp, FLAG_FRIENDLY);
486 }
487 }
488 }
489 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 }
500 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
527 return head;
528 }
529
530 /* updated this to allow more than the golem 'head' to attack */
531
532 /* op is the golem to be moved. */
533
534 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 if (op->owner == NULL)
544 {
545 LOG (llevDebug, "Golem without owner destructed.\n");
546 op->remove ();
547 op->destroy ();
548 return;
549 }
550
551 /* 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
561 op->owner->contr->ranges[range_golem] = 0;
562 remove_friendly_object (op);
563 op->remove ();
564 op->destroy ();
565 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
576 if (op->destroyed ())
577 return;
578
579 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 maptile *m;
584 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 for (victim = GET_MAP_OB (op->map, x, y); victim; victim = victim->above)
593 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
606 /* for golems with race fields, we don't attack
607 * aligned races
608 */
609
610 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 }
620 else
621 {
622 attack_ob (victim, op);
623 made_attack = 1;
624 }
625 } /* If victim */
626 }
627 if (made_attack)
628 update_object (op, UP_OBJ_FACE);
629 }
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 void
637 control_golem (object *op, int dir)
638 {
639 op->direction = dir;
640 }
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 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 if (op->type == PLAYER && op->contr->ranges[range_golem])
659 {
660 new_draw_info (NDI_UNIQUE, 0, op, "You dismiss your existing golem.");
661 op->contr->ranges[range_golem]->remove ();
662 op->contr->ranges[range_golem]->destroy ();
663 op->contr->ranges[range_golem] = 0;
664 }
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
672 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 }
677
678 at = determine_holy_arch (god, spob->race);
679
680 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 }
685 }
686 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 if (dir == -1 || ob_blocked (&at->clone, op->map, op->x + freearr_x[dir], op->y + freearr_y[dir]))
696 {
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
702 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
708 if (op->type == PLAYER)
709 {
710 tmp->type = GOLEM;
711 tmp->set_owner (op);
712 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 }
717 else
718 {
719 if (QUERY_FLAG (op, FLAG_FRIENDLY))
720 {
721 object *owner = op->owner;
722
723 if (owner != NULL)
724 { /* For now, we transfer ownership */
725 tmp->set_owner (owner);
726 tmp->attack_movement = PETMOVE;
727 add_friendly_object (tmp);
728 SET_FLAG (tmp, FLAG_FRIENDLY);
729 }
730 }
731
732 SET_FLAG (tmp, FLAG_MONSTER);
733 }
734
735 /* 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
745 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
750 tmp->speed += .02 * SP_level_range_adjust (caster, spob);
751 tmp->speed = MIN (tmp->speed, 1.0);
752
753 if (spob->attacktype)
754 tmp->attacktype = spob->attacktype;
755 }
756
757 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
785 /* safety, we must allow a god's servants some reasonable attack */
786 if (!(tmp->attacktype & AT_PHYSICAL))
787 tmp->attacktype |= AT_PHYSICAL;
788 }
789
790 insert_ob_in_map (tmp, tmp->map, op, 0);
791 return 1;
792 }
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 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 }
862
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 return NULL;
870
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 }
884
885 int
886 summon_object (object *op, object *caster, object *spell_ob, int dir, const char *stringarg)
887 {
888 sint16 x, y, nrof = 1, i;
889 archetype *summon_arch;
890 int ndir;
891
892 if (spell_ob->other_arch)
893 summon_arch = spell_ob->other_arch;
894 else if (spell_ob->randomitems)
895 {
896 int level = caster_level (caster, spell_ob);
897 treasure *tr, *lasttr = NULL;
898
899 shstr_cmp sparam (stringarg);
900
901 /* In old code, this was a very convoluted for statement,
902 * 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 if (!tr->item)
908 continue;
909
910 if (level < tr->magic)
911 break;
912
913 lasttr = tr;
914
915 if (tr->item->name == sparam)
916 break;
917 }
918
919 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
926 summon_arch = lasttr->item;
927 nrof = lasttr->nrof;
928 }
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 if (!god && ((owner = op->owner) != NULL))
935 god = find_god (determine_god (owner));
936
937 /* If we can't find a god, can't get what monster to summon */
938 if (!god)
939 return 0;
940
941 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 }
946
947 /* the summon level */
948 summon_level = caster_level (caster, spell_ob);
949 if (summon_level == 0)
950 summon_level = 1;
951
952 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
962 ndir = dir;
963
964 if (!ndir)
965 ndir = find_free_spot (mon, op->map, op->x, op->y, 1, SIZEOFFREE);
966
967 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 }
975 }
976 }
977 while (ndir == -1);
978
979 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
984 summon_arch = mon->arch;
985 }
986 else
987 summon_arch = 0;
988
989 if (spell_ob->stats.dam)
990 nrof += spell_ob->stats.dam + SP_level_dam_adjust (caster, spell_ob);
991
992 if (!summon_arch)
993 {
994 new_draw_info (NDI_UNIQUE, 0, op, "There is no monsters available for summoning.");
995 return 0;
996 }
997
998 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 tmp->set_owner (op);
1034 set_spell_skill (op, caster, spell_ob, tmp);
1035 tmp->enemy = op->enemy;
1036 tmp->type = 0;
1037 CLEAR_FLAG (tmp, FLAG_SLEEP);
1038
1039 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
1048 if (spell_ob->attack_movement)
1049 tmp->attack_movement = spell_ob->attack_movement;
1050
1051 if (op->owner)
1052 tmp->set_owner (op->owner);
1053 }
1054 }
1055 }
1056
1057 if (tmp->speed > MIN_ACTIVE_SPEED)
1058 tmp->speed_left = -1;
1059 }
1060
1061 if (head == NULL)
1062 head = tmp;
1063 else
1064 {
1065 tmp->head = head;
1066 prev->more = tmp;
1067 }
1068
1069 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
1075 head->direction = freedir[ndir];
1076 head->stats.exp = 0;
1077 head = insert_ob_in_map (head, head->map, op, 0);
1078
1079 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 }
1088 } /* for i < nrof */
1089
1090 return 1;
1091 }
1092
1093 /* recursively look through the owner property of objects until the real owner
1094 is found */
1095 object *
1096 get_real_owner (object *ob)
1097 {
1098 object *realowner = ob;
1099
1100 if (realowner == NULL)
1101 return NULL;
1102
1103 while (realowner->owner != NULL)
1104 {
1105 realowner = realowner->owner;
1106 }
1107 return realowner;
1108 }
1109
1110 /* determines if checks so pets don't attack players or other pets should be
1111 overruled by the arena petmode */
1112 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 }
1185 }
1186
1187 return 1;
1188 }