ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/pets.C
Revision: 1.26
Committed: Wed Mar 14 04:12:29 2007 UTC (17 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.25: +1 -0 lines
Log Message:
- rewrote more face handling code
- automatically send smooth faces, as the client will need them anyways
  and it makes little sense to wait for the client to axk for it. of course,
  gcfclient suffers from weird ordering problems again.
- UP_OBJ_FACE was often abused in situations where other things changed,
  updated lots of spaces, probably more to be done.
- update_smooth became so small that inlining it actually clarified
  the code. similar for update_space, which is not inlined for other reasons.
- faces were not initialised properly
- add versioncheck for face data
- rewrite invisibility handling a bit: god finger etc. now makes you blink,
  blinking routine has changed to be less annoying and more useful while
  still indicating invisibleness.

File Contents

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