ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/pets.C
Revision: 1.30
Committed: Mon Apr 30 04:25:30 2007 UTC (17 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.29: +5 -6 lines
Log Message:
This is the first rough cut of the skill use system (use the STABLE tag).

Details will likely change, and combat skills do not work very well, but
it works quite well.

Players no longer have a shoottype or range slots, instead, each player
has these members:

   combat_skill/combat_ob  the currently selected skill (and weapon)
                           for direct attacks.
   ranged_skill/ranged_ob  the currently selected ranged skill (and
                           bow/spell/item)
   golem                   the currently-controlled golem, if any.

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)
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 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 remove_friendly_object (pet);
65 pet->attack_movement &= ~PETMOVE;
66 return 0;
67 }
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 0;
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))
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 = 0;
81 else
82 return tmp;
83 }
84
85 get_search_arr (search_arr);
86
87 if (owner->type == PLAYER && owner->contr->petmode > pet_normal)
88 {
89 if (owner->contr->petmode == pet_sad)
90 {
91 tmp = find_nearest_living_creature (pet);
92 if (tmp != 0)
93 {
94 get_rangevector (pet, tmp, rv, 0);
95 if (check_enemy (pet, rv) != 0)
96 return tmp;
97 else
98 pet->enemy = 0;
99 }
100 /* if we got here we have no enemy */
101 /* we return 0 to avoid heading back to the owner */
102 pet->enemy = 0;
103 return 0;
104 }
105 }
106
107 /* Since the pet has no existing enemy, look for anything nasty
108 * around the owner that it should go and attack. (if the owner is
109 * still on a map)
110 */
111 if (!owner->flag [FLAG_REMOVED])
112 {
113 tmp3 = 0;
114
115 for (i = 0; i < SIZEOFFREE; i++)
116 {
117 x = owner->x + freearr_x[search_arr[i]];
118 y = owner->y + freearr_y[search_arr[i]];
119 nm = owner->map;
120 /* Only look on the space if there is something alive there. */
121 mflags = get_map_flags (nm, &nm, x, y, &x, &y);
122
123 if (!(mflags & P_OUT_OF_MAP) && mflags & P_IS_ALIVE)
124 {
125 for (tmp = GET_MAP_OB (nm, x, y); tmp != 0; tmp = tmp->above)
126 {
127 object *tmp2 = tmp->head == 0 ? tmp : tmp->head;
128
129 if (QUERY_FLAG (tmp2, FLAG_ALIVE) && ((!QUERY_FLAG (tmp2, FLAG_FRIENDLY) &&
130 (tmp2->type != PLAYER)) ||
131 should_arena_attack (pet, owner, tmp2))
132 && !QUERY_FLAG (tmp2, FLAG_UNAGGRESSIVE) && tmp2 != pet && tmp2 != owner && can_detect_enemy (pet, tmp2, rv))
133 {
134
135 if (!can_see_enemy (pet, tmp2))
136 {
137 if (tmp3 != 0)
138 tmp3 = tmp2;
139 }
140 else
141 {
142 pet->enemy = tmp2;
143 if (check_enemy (pet, rv) != 0)
144 return tmp2;
145 else
146 pet->enemy = 0;
147 }
148 } /* if this is a valid enemy */
149 } /* for objects on this space */
150 } /* if there is something living on this space */
151 } /* for loop of spaces around the owner */
152
153 /* fine, we went through the whole loop and didn't find one we could
154 see, take what we have */
155 if (tmp3 != 0)
156 {
157 pet->enemy = tmp3;
158 if (check_enemy (pet, rv) != 0)
159 return tmp3;
160 else
161 pet->enemy = 0;
162 }
163 }
164
165 /* No threat to owner, check to see if the pet has an attacker */
166 if (attacker)
167 {
168 /* also need to check to make sure it is not freindly */
169 /* or otherwise non-hostile, and is an appropriate target */
170 if (!QUERY_FLAG (attacker, FLAG_FRIENDLY) && on_same_map (pet, attacker))
171 {
172 pet->enemy = attacker;
173
174 if (check_enemy (pet, rv) != 0)
175 return attacker;
176 else
177 pet->enemy = 0;
178 }
179 }
180
181 /* Don't have an attacker or legal enemy, so look for a new one!.
182 * This looks for one around where the pet is. Thus, you could lead
183 * a pet to danger, then take a few steps back. This code is basically
184 * the same as the code that looks around the owner.
185 */
186 if (owner->type == PLAYER && owner->contr->petmode != pet_defend)
187 {
188 tmp3 = 0;
189 for (i = 0; i < SIZEOFFREE; i++)
190 {
191 x = pet->x + freearr_x[search_arr[i]];
192 y = pet->y + freearr_y[search_arr[i]];
193 nm = pet->map;
194 /* Only look on the space if there is something alive there. */
195 mflags = get_map_flags (nm, &nm, x, y, &x, &y);
196 if (!(mflags & P_OUT_OF_MAP) && mflags & P_IS_ALIVE)
197 {
198 for (tmp = GET_MAP_OB (nm, x, y); tmp != 0; tmp = tmp->above)
199 {
200 object *tmp2 = tmp->head == 0 ? tmp : tmp->head;
201
202 if (QUERY_FLAG (tmp2, FLAG_ALIVE) && ((!QUERY_FLAG (tmp2, FLAG_FRIENDLY) &&
203 (tmp2->type != PLAYER)) ||
204 should_arena_attack (pet, owner, tmp2))
205 && !QUERY_FLAG (tmp2, FLAG_UNAGGRESSIVE) && tmp2 != pet && tmp2 != owner && can_detect_enemy (pet, tmp2, rv))
206 {
207
208 if (!can_see_enemy (pet, tmp2))
209 {
210 if (tmp3 != 0)
211 tmp3 = tmp2;
212 }
213 else
214 {
215 pet->enemy = tmp2;
216 if (check_enemy (pet, rv) != 0)
217 return tmp2;
218 else
219 pet->enemy = 0;
220 }
221 } /* make sure we can get to the bugger */
222 } /* for objects on this space */
223 } /* if there is something living on this space */
224 } /* for loop of spaces around the pet */
225
226 /* fine, we went through the whole loop and didn't find one we could
227 see, take what we have */
228 if (tmp3 != 0)
229 {
230 pet->enemy = tmp3;
231 if (check_enemy (pet, rv) != 0)
232 return tmp3;
233 else
234 pet->enemy = 0;
235 }
236 } /* pet in defence mode */
237
238 object *enemy = check_enemy (pet, rv);
239 // we have a summoned pet here and the owners enemy isn't set or can't
240 // be reached => search for a player around us and set it as our new enemy!!
241 if (!enemy && pet->owner && pet->owner->type != PLAYER)
242 enemy = get_nearest_player (pet);
243
244 /* Didn't find anything - return the owner's enemy or 0 */
245 return enemy;
246 }
247
248 void
249 terminate_all_pets (object *owner)
250 {
251 objectlink *obl, *next;
252
253 for (obl = first_friendly_object; obl; obl = next)
254 {
255 object *ob = obl->ob;
256 next = obl->next;
257
258 if (ob->owner == owner)
259 ob->destroy ();
260 }
261 }
262
263 /*
264 * Unfortunately, sometimes, the owner of a pet is in the
265 * process of entering a new map when this is called.
266 * Thus the map isn't loaded yet, and we have to remove
267 * the pet...
268 * Interesting enough, we don't use the passed map structure in
269 * this function.
270 */
271 void
272 remove_all_pets (maptile *map)
273 {
274 objectlink *obl, *next;
275 object *owner;
276
277 for (obl = first_friendly_object; obl; obl = next)
278 {
279 next = obl->next;
280
281 if (obl->ob->type != PLAYER
282 && QUERY_FLAG (obl->ob, FLAG_FRIENDLY)
283 && (owner = obl->ob->owner) != 0
284 && !on_same_map (owner, obl->ob))
285 {
286 /* follow owner checks map status for us */
287 follow_owner (obl->ob, owner);
288 }
289 }
290 }
291
292 int
293 follow_owner (object *ob, object *owner)
294 {
295 if (owner->flag [FLAG_REMOVED])
296 return 0; // do nothing if the owner is removed
297 else if (owner->map->in_memory != MAP_IN_MEMORY)
298 LOG (llevError, "Owner of the pet not on a map in memory!?\n");
299 else
300 {
301 int dir = find_free_spot (ob, owner->map, owner->x, owner->y, 1, SIZEOFFREE);
302
303 if (dir >= 0)
304 {
305 owner->map->insert (ob, owner->x + freearr_x[dir], owner->y + freearr_y[dir]);
306
307 if (owner->type == PLAYER) /* Uh, I hope this is always true... */
308 new_draw_info (NDI_UNIQUE, 0, owner, "Your pet magically appears next to you");
309
310 return 0;
311 }
312 }
313
314 ob->destroy ();
315 return 1;
316 }
317
318 void
319 pet_move (object *ob)
320 {
321 int dir = 0, i;
322 sint16 dx, dy;
323 object *ob2, *owner;
324 maptile *m;
325
326 /* Check to see if player pulled out */
327 if ((owner = ob->owner) == NULL)
328 {
329 ob->destroy ();
330 LOG (llevMonster, "Pet: no owner, leaving.\n");
331 return;
332 }
333
334 /* move monster into the owners map if not in the same map
335 * except when the owner is removed.
336 */
337 if (!on_same_map (ob, owner) && !owner->flag [FLAG_REMOVED])
338 {
339 follow_owner (ob, owner);
340 return;
341 }
342 /* Calculate Direction */
343 if (owner->type == PLAYER && owner->contr->petmode == pet_sad)
344 {
345 /* in S&D mode, if we have no enemy, run randomly about. */
346 for (i = 0; i < 15; i++)
347 {
348 dir = rndm (1, 8);
349 dx = ob->x + freearr_x[dir];
350 dy = ob->y + freearr_y[dir];
351 m = ob->map;
352 if (get_map_flags (ob->map, &m, dx, dy, &dx, &dy) & P_OUT_OF_MAP)
353 continue;
354 else if (OB_TYPE_MOVE_BLOCK (ob, GET_MAP_MOVE_BLOCK (m, dx, dy)))
355 continue;
356 else
357 break;
358 }
359 }
360 else if (!owner->flag [FLAG_REMOVED]) // only get vector to owner when owner not removed
361 {
362 struct rv_vector rv;
363
364 get_rangevector (ob, ob->owner, &rv, 0);
365 dir = rv.direction;
366 }
367 ob->direction = dir;
368
369 /* move_ob returns 0 if the object couldn't move. If that is the
370 * case, lets do some other work.
371 */
372 if (!(move_ob (ob, dir, ob)))
373 {
374 object *part;
375
376 /* the failed move_ob above may destroy the pet, so check here */
377 if (ob->destroyed ())
378 return;
379
380 for (part = ob; part != NULL; part = part->more)
381 {
382 dx = part->x + freearr_x[dir];
383 dy = part->y + freearr_y[dir];
384 m = get_map_from_coord (part->map, &dx, &dy);
385 if (!m)
386 continue;
387
388 for (ob2 = GET_MAP_OB (m, dx, dy); ob2 != NULL; ob2 = ob2->above)
389 {
390 object *new_ob;
391
392 new_ob = ob2->head ? ob2->head : ob2;
393 if (new_ob == ob)
394 break;
395 if (new_ob == ob->owner)
396 return;
397 if (new_ob->owner == ob->owner)
398 break;
399
400 /* Hmm. Did we try to move into an enemy monster? If so,
401 * make it our enemy.
402 */
403 if (QUERY_FLAG (new_ob, FLAG_ALIVE) && !QUERY_FLAG (ob, FLAG_UNAGGRESSIVE)
404 && !QUERY_FLAG (new_ob, FLAG_UNAGGRESSIVE) && !QUERY_FLAG (new_ob, FLAG_FRIENDLY))
405 {
406
407 ob->enemy = new_ob;
408 if (new_ob->enemy == NULL)
409 new_ob->enemy = ob;
410 return;
411 }
412 else if (new_ob->type == PLAYER)
413 {
414 new_draw_info (NDI_UNIQUE, 0, new_ob, "You stand in the way of someones pet.");
415 return;
416 }
417 }
418 }
419 /* Try a different course */
420 dir = absdir (dir + 4 - (rndm (5)) - (rndm (5)));
421 (void) move_ob (ob, dir, ob);
422 }
423 return;
424 }
425
426 /****************************************************************************
427 *
428 * GOLEM SPELL CODE
429 *
430 ****************************************************************************/
431
432 /* fix_summon_pet() - this makes multisquare/single square monsters
433 * proper for map insertion.
434 * at is the archetype, op is the caster of the spell, dir is the
435 * direction the monster should be placed in.
436 * is_golem is to note that this is a golem spell.
437 */
438 object *
439 fix_summon_pet (archetype *at, object *op, int dir, int is_golem)
440 {
441 archetype *atmp;
442 object *tmp = NULL, *prev = NULL, *head = NULL;
443
444 for (atmp = at; atmp; atmp = atmp->more)
445 {
446 tmp = arch_to_object (atmp);
447
448 if (atmp == at)
449 {
450 if (!is_golem)
451 SET_FLAG (tmp, FLAG_MONSTER);
452
453 tmp->set_owner (op);
454 if (op->type == PLAYER)
455 {
456 tmp->stats.exp = 0;
457 add_friendly_object (tmp);
458 if (is_golem)
459 CLEAR_FLAG (tmp, FLAG_MONSTER);
460 }
461 else
462 {
463 if (QUERY_FLAG (op, FLAG_FRIENDLY))
464 {
465 object *owner = op->owner;
466
467 if (owner)
468 { /* For now, we transfer ownership */
469 tmp->set_owner (owner);
470 tmp->attack_movement = PETMOVE;
471 add_friendly_object (tmp);
472 }
473 }
474 }
475
476 if (op->type != PLAYER || !is_golem)
477 {
478 tmp->attack_movement = PETMOVE;
479 tmp->speed_left = -1;
480 tmp->type = 0;
481 tmp->enemy = op->enemy;
482 }
483 else
484 tmp->type = GOLEM;
485
486 }
487
488 if (!head)
489 head = tmp;
490
491 tmp->x = op->x + freearr_x[dir] + tmp->arch->clone.x;
492 tmp->y = op->y + freearr_y[dir] + tmp->arch->clone.y;
493 tmp->map = op->map;
494
495 if (tmp->invisible)
496 tmp->invisible = 0;
497
498 if (head != tmp)
499 tmp->head = head, prev->more = tmp;
500
501 prev = tmp;
502 }
503
504 head->direction = dir;
505
506 /* need to change some monster attr to prevent problems/crashing */
507 head->last_heal = 0;
508 head->last_eat = 0;
509 head->last_grace = 0;
510 head->last_sp = 0;
511 head->other_arch = NULL;
512 head->stats.exp = 0;
513 CLEAR_FLAG (head, FLAG_CHANGING);
514 CLEAR_FLAG (head, FLAG_STAND_STILL);
515 CLEAR_FLAG (head, FLAG_GENERATOR);
516 CLEAR_FLAG (head, FLAG_SPLITTING);
517 if (head->attacktype & AT_GHOSTHIT)
518 head->attacktype = (AT_PHYSICAL | AT_DRAIN);
519
520 return head;
521 }
522
523 /* updated this to allow more than the golem 'head' to attack */
524 /* op is the golem to be moved. */
525 void
526 move_golem (object *op)
527 {
528 int made_attack = 0;
529 object *tmp;
530
531 if (QUERY_FLAG (op, FLAG_MONSTER))
532 return; /* Has already been moved */
533
534 if (!op->owner)
535 {
536 LOG (llevDebug, "Golem without owner destructed.\n");
537 op->remove ();
538 op->destroy ();
539 return;
540 }
541
542 /* It would be nice to have a cleaner way of what message to print
543 * when the golem expires than these hard coded entries.
544 * Note it is intentional that a golems duration is based on its
545 * hp, and not duration
546 */
547 if (--op->stats.hp < 0)
548 {
549 if (op->msg)
550 new_draw_info (NDI_UNIQUE, 0, op->owner, op->msg);
551
552 op->destroy ();
553 return;
554 }
555
556 /* Do golem attacks/movement for single & multisq golems.
557 * Assuming here that op is the 'head' object. Pass only op to
558 * move_ob (makes recursive calls to other parts)
559 * move_ob returns 0 if the creature was not able to move.
560 */
561 if (move_ob (op, op->direction, op))
562 return;
563
564 if (op->destroyed ())
565 return;
566
567 for (tmp = op; tmp; tmp = tmp->more)
568 {
569 sint16 x = tmp->x + freearr_x[op->direction], y = tmp->y + freearr_y[op->direction];
570 object *victim;
571 maptile *m;
572 int mflags;
573
574 m = op->map;
575 mflags = get_map_flags (m, &m, x, y, &x, &y);
576
577 if (mflags & P_OUT_OF_MAP)
578 continue;
579
580 for (victim = GET_MAP_OB (op->map, x, y); victim; victim = victim->above)
581 if (QUERY_FLAG (victim, FLAG_ALIVE))
582 break;
583
584 /* We used to call will_hit_self to make sure we don't
585 * hit ourselves, but that didn't work, and I don't really
586 * know if that was more efficient anyways than this.
587 * This at least works. Note that victim->head can be NULL,
588 * but since we are not trying to dereferance that pointer,
589 * that isn't a problem.
590 */
591 if (victim && victim != op && victim->head != op)
592 {
593 /* for golems with race fields, we don't attack
594 * aligned races
595 */
596
597 if (victim->race && op->race && strstr (op->race, victim->race))
598 {
599 if (op->owner)
600 new_draw_info_format (NDI_UNIQUE, 0, op->owner, "%s avoids damaging %s.", &op->name, &victim->name);
601 }
602 else if (victim == op->owner)
603 {
604 if (op->owner)
605 new_draw_info_format (NDI_UNIQUE, 0, op->owner, "%s avoids damaging you.", &op->name);
606 }
607 else
608 {
609 attack_ob (victim, op);
610 made_attack = 1;
611 }
612 } /* If victim */
613 }
614
615 if (made_attack)
616 update_object (op, UP_OBJ_FACE);
617 }
618
619 /* this is a really stupid function when you get down and
620 * look at it. Keep it here for the time being - makes life
621 * easier if we ever decide to do more interesting thing with
622 * controlled golems.
623 */
624 void
625 control_golem (object *op, int dir)
626 {
627 op->direction = dir;
628 }
629
630 /* summon golem: summons a monster for 'op'. caster is the object
631 * casting the spell, dir is the direction to place the monster,
632 * at is the archetype of the monster, and spob is the spell
633 * object. At this stage, all spob is really used for is to
634 * adjust some values in the monster.
635 */
636 int
637 summon_golem (object *op, object *caster, int dir, object *spob)
638 {
639 object *tmp, *god = NULL;
640 archetype *at;
641 char buf[MAX_BUF];
642
643 /* Because there can be different golem spells, player may want to
644 * 'lose' their old golem.
645 */
646 if (op->type == PLAYER && op->contr->golem)
647 {
648 new_draw_info (NDI_UNIQUE, 0, op, "You dismiss your existing golem.");
649 op->contr->golem->remove ();
650 op->contr->golem->destroy ();
651 op->contr->golem = 0;
652 }
653
654 if (spob->other_arch)
655 at = spob->other_arch;
656 else if (spob->race)
657 {
658 god = find_god (determine_god (caster));
659
660 if (!god)
661 {
662 new_draw_info_format (NDI_UNIQUE, 0, op, "You must worship a god to cast %s.", &spob->name);
663 return 0;
664 }
665
666 at = determine_holy_arch (god, spob->race);
667
668 if (!at)
669 {
670 new_draw_info_format (NDI_UNIQUE, 0, op, "%s has no %s for you to call.", &god->name, &spob->race);
671 return 0;
672 }
673 }
674 else
675 {
676 LOG (llevError, "Spell %s lacks other_arch\n", &spob->name);
677 return 0;
678 }
679
680 if (!dir)
681 dir = find_free_spot (&at->clone, op->map, op->x, op->y, 1, SIZEOFFREE1 + 1);
682
683 if (dir < 0 || ob_blocked (&at->clone, op->map, op->x + freearr_x[dir], op->y + freearr_y[dir]))
684 {
685 new_draw_info (NDI_UNIQUE, 0, op, "There is something in the way.");
686 return 0;
687 }
688
689 /* basically want to get proper map/coordinates for this object */
690
691 if (!(tmp = fix_summon_pet (at, op, dir, GOLEM)))
692 {
693 new_draw_info (NDI_UNIQUE, 0, op, "Your spell fails.");
694 return 0;
695 }
696
697 if (op->type == PLAYER)
698 {
699 tmp->type = GOLEM;
700 tmp->set_owner (op);
701 op->contr->golem = tmp;
702 /* give the player control of the golem */
703 set_spell_skill (op, caster, spob, tmp);
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 towner = target->type == PLAYER ? 0 : get_real_owner (target);
1109
1110 /* if the pet has now owner, exit with error */
1111 if (!rowner)
1112 {
1113 LOG (llevError, "Pet has no owner.\n");
1114 return 0;
1115 }
1116
1117 /* if the target is not a player, and has no owner, we shouldn't be here
1118 */
1119 if ((towner == NULL) && (target->type != PLAYER))
1120 {
1121 LOG (llevError, "Target is not a player but has no owner. We should not be here.\n");
1122 return 0;
1123 }
1124
1125 /* make sure that the owner is a player */
1126 if (rowner->type != PLAYER)
1127 return 0;
1128
1129 /* abort if the petmode is not arena */
1130 if (rowner->contr->petmode != pet_arena)
1131 return 0;
1132
1133 /* abort if the pet, it's owner, or the target is not on battleground */
1134 if (!(op_on_battleground (pet, NULL, NULL) && op_on_battleground (owner, NULL, NULL) && op_on_battleground (target, NULL, NULL)))
1135 return 0;
1136
1137 /* if the target is a monster, make sure it's owner is not the same */
1138 if ((target->type != PLAYER) && (rowner == towner))
1139 return 0;
1140
1141 /* check if the target is a player which affects how it will handle
1142 parties */
1143 if (target->type != PLAYER)
1144 {
1145 /* if the target is owned by a player make sure than make sure
1146 it's not in the same party */
1147 if ((towner->type == PLAYER) && (rowner->contr->party != NULL))
1148 {
1149 if (rowner->contr->party == towner->contr->party)
1150 return 0;
1151 }
1152 }
1153 else
1154 {
1155 /* if the target is a player make sure than make sure it's not
1156 in the same party */
1157 if (rowner->contr->party != NULL)
1158 {
1159 if (rowner->contr->party == target->contr->party)
1160 return 0;
1161 }
1162 }
1163
1164 return 1;
1165 }