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, 2 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

# User Rev Content
1 elmex 1.1 /*
2 pippijn 1.23 * 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 elmex 1.1
25     #include <global.h>
26 root 1.17 #include <sproto.h>
27 elmex 1.1
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 root 1.4 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 root 1.11 maptile *nm;
41 root 1.4 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 root 1.15 if ((owner = pet->owner) != NULL)
48 root 1.4 {
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 root 1.2 */
81 root 1.4 pet->enemy = NULL;
82     else
83     return tmp;
84     }
85 root 1.19
86 root 1.4 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 root 1.19
120 root 1.4 if (!(mflags & P_OUT_OF_MAP) && mflags & P_IS_ALIVE)
121     {
122 root 1.16 for (tmp = GET_MAP_OB (nm, x, y); tmp != NULL; tmp = tmp->above)
123 root 1.4 {
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 root 1.8 /* 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 root 1.4 {
168 root 1.8 pet->enemy = attacker;
169    
170     if (check_enemy (pet, rv) != NULL)
171     return attacker;
172     else
173     pet->enemy = NULL;
174 root 1.4 }
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 root 1.16 for (tmp = GET_MAP_OB (nm, x, y); tmp != NULL; tmp = tmp->above)
195 root 1.4 {
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 root 1.2 }
217 root 1.4 } /* 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 elmex 1.1 }
233    
234 root 1.4 /* Didn't find anything - return the owner's enemy or NULL */
235     return check_enemy (pet, rv);
236 elmex 1.1 }
237    
238 root 1.4 void
239     terminate_all_pets (object *owner)
240     {
241 elmex 1.1 objectlink *obl, *next;
242 root 1.4
243     for (obl = first_friendly_object; obl != NULL; obl = next)
244     {
245     object *ob = obl->ob;
246    
247     next = obl->next;
248 root 1.15 if (ob->owner == owner)
249 root 1.4 {
250     if (!QUERY_FLAG (ob, FLAG_REMOVED))
251 root 1.13 ob->remove ();
252 root 1.4 remove_friendly_object (ob);
253 root 1.14 ob->destroy ();
254 root 1.4 }
255 elmex 1.1 }
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 root 1.4 void
267 root 1.11 remove_all_pets (maptile *map)
268 root 1.4 {
269 elmex 1.1 objectlink *obl, *next;
270     object *owner;
271    
272 root 1.24 for (obl = first_friendly_object; obl; obl = next)
273 elmex 1.1 {
274 root 1.4 next = obl->next;
275 root 1.24
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 root 1.4 {
281     /* follow owner checks map status for us */
282     follow_owner (obl->ob, owner);
283     }
284 elmex 1.1 }
285     }
286    
287 root 1.4 int
288     follow_owner (object *ob, object *owner)
289     {
290 root 1.24 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 elmex 1.1
298 root 1.24 if (dir >= 0)
299     {
300     owner->map->insert (ob, owner->x + freearr_x[dir], owner->y + freearr_y[dir]);
301 elmex 1.1
302 root 1.24 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 elmex 1.1
305 root 1.24 return 0;
306 root 1.2 }
307 elmex 1.1 }
308    
309 root 1.14 ob->destroy ();
310 root 1.4 return 1;
311 elmex 1.1 }
312    
313 root 1.4 void
314     pet_move (object *ob)
315 elmex 1.1 {
316 root 1.10 int dir, i;
317 root 1.4 sint16 dx, dy;
318     object *ob2, *owner;
319 root 1.11 maptile *m;
320 root 1.4
321     /* Check to see if player pulled out */
322 root 1.15 if ((owner = ob->owner) == NULL)
323 root 1.4 {
324 root 1.13 ob->remove (); /* Will be freed when returning */
325 root 1.4 remove_friendly_object (ob);
326 root 1.14 ob->destroy ();
327 root 1.4 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 elmex 1.1
359 root 1.4 get_rangevector (ob, ob->owner, &rv, 0);
360     dir = rv.direction;
361 elmex 1.1 }
362 root 1.4 ob->direction = dir;
363 elmex 1.1
364 root 1.4 /* 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 root 1.10 if (ob->destroyed ())
373 root 1.2 return;
374 root 1.4
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 root 1.16 for (ob2 = GET_MAP_OB (m, dx, dy); ob2 != NULL; ob2 = ob2->above)
384 root 1.4 {
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 root 1.15 if (new_ob->owner == ob->owner)
393 root 1.2 break;
394 root 1.4
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 root 1.2 }
412     }
413     }
414 root 1.4 /* Try a different course */
415 root 1.25 dir = absdir (dir + 4 - (rndm (5)) - (rndm (5)));
416 root 1.4 (void) move_ob (ob, dir, ob);
417 elmex 1.1 }
418 root 1.4 return;
419 elmex 1.1 }
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 root 1.4 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 root 1.19 for (atmp = at; atmp; atmp = atmp->more)
440 root 1.4 {
441     tmp = arch_to_object (atmp);
442 root 1.19
443 root 1.4 if (atmp == at)
444     {
445     if (!is_golem)
446     SET_FLAG (tmp, FLAG_MONSTER);
447 root 1.19
448 root 1.15 tmp->set_owner (op);
449 root 1.4 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 root 1.15 object *owner = op->owner;
461 root 1.4
462 root 1.19 if (owner)
463 root 1.4 { /* For now, we transfer ownership */
464 root 1.15 tmp->set_owner (owner);
465 root 1.4 tmp->attack_movement = PETMOVE;
466     add_friendly_object (tmp);
467 root 1.2 }
468     }
469     }
470 root 1.19
471 root 1.4 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 root 1.2 }
482 root 1.19
483     if (!head)
484 root 1.4 head = tmp;
485 root 1.19
486 root 1.4 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 root 1.19
490 root 1.4 if (tmp->invisible)
491     tmp->invisible = 0;
492 root 1.19
493 root 1.4 if (head != tmp)
494     tmp->head = head, prev->more = tmp;
495 root 1.19
496 root 1.4 prev = tmp;
497     }
498 root 1.19
499 root 1.4 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 elmex 1.1
515 root 1.4 return head;
516 elmex 1.1 }
517    
518     /* updated this to allow more than the golem 'head' to attack */
519     /* op is the golem to be moved. */
520 root 1.4 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 root 1.15 if (op->owner == NULL)
530 root 1.4 {
531     LOG (llevDebug, "Golem without owner destructed.\n");
532 root 1.13 op->remove ();
533 root 1.14 op->destroy ();
534 root 1.4 return;
535     }
536 root 1.10
537 root 1.4 /* 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 root 1.12
547     op->owner->contr->ranges[range_golem] = 0;
548 root 1.4 remove_friendly_object (op);
549 root 1.13 op->remove ();
550 root 1.14 op->destroy ();
551 root 1.4 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 root 1.10
562     if (op->destroyed ())
563 root 1.4 return;
564 elmex 1.1
565 root 1.4 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 root 1.11 maptile *m;
570 root 1.4 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 root 1.16 for (victim = GET_MAP_OB (op->map, x, y); victim; victim = victim->above)
579 root 1.4 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 elmex 1.1
592 root 1.4 /* for golems with race fields, we don't attack
593     * aligned races
594     */
595 root 1.2
596 root 1.4 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 root 1.2 }
606 root 1.4 else
607     {
608     attack_ob (victim, op);
609     made_attack = 1;
610     }
611     } /* If victim */
612 elmex 1.1 }
613 root 1.26
614 root 1.4 if (made_attack)
615     update_object (op, UP_OBJ_FACE);
616 elmex 1.1 }
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 root 1.4 void
624     control_golem (object *op, int dir)
625     {
626     op->direction = dir;
627 elmex 1.1 }
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 root 1.4 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 root 1.12 if (op->type == PLAYER && op->contr->ranges[range_golem])
646 root 1.4 {
647     new_draw_info (NDI_UNIQUE, 0, op, "You dismiss your existing golem.");
648 root 1.13 op->contr->ranges[range_golem]->remove ();
649 root 1.14 op->contr->ranges[range_golem]->destroy ();
650 root 1.12 op->contr->ranges[range_golem] = 0;
651 root 1.4 }
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 elmex 1.1
659 root 1.4 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 root 1.2 }
664 root 1.6
665 root 1.4 at = determine_holy_arch (god, spob->race);
666 root 1.6
667 root 1.4 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 root 1.2 }
672 elmex 1.1 }
673 root 1.4 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 root 1.24 if (dir < 0 || ob_blocked (&at->clone, op->map, op->x + freearr_x[dir], op->y + freearr_y[dir]))
683 root 1.4 {
684     new_draw_info (NDI_UNIQUE, 0, op, "There is something in the way.");
685     return 0;
686     }
687 root 1.19
688 root 1.4 /* basically want to get proper map/coordinates for this object */
689 elmex 1.1
690 root 1.4 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 elmex 1.1
696 root 1.4 if (op->type == PLAYER)
697     {
698     tmp->type = GOLEM;
699 root 1.15 tmp->set_owner (op);
700 root 1.4 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 elmex 1.1 }
705 root 1.4 else
706     {
707     if (QUERY_FLAG (op, FLAG_FRIENDLY))
708     {
709 root 1.15 object *owner = op->owner;
710 elmex 1.1
711 root 1.19 if (owner)
712 root 1.4 { /* For now, we transfer ownership */
713 root 1.15 tmp->set_owner (owner);
714 root 1.4 tmp->attack_movement = PETMOVE;
715     add_friendly_object (tmp);
716     }
717     }
718 root 1.6
719 root 1.4 SET_FLAG (tmp, FLAG_MONSTER);
720 elmex 1.1 }
721    
722 root 1.4 /* 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 root 1.6
732 root 1.4 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 root 1.6
737 root 1.4 tmp->speed += .02 * SP_level_range_adjust (caster, spob);
738     tmp->speed = MIN (tmp->speed, 1.0);
739 root 1.6
740 root 1.4 if (spob->attacktype)
741     tmp->attacktype = spob->attacktype;
742     }
743 root 1.6
744 root 1.4 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 root 1.6
772 root 1.4 /* safety, we must allow a god's servants some reasonable attack */
773     if (!(tmp->attacktype & AT_PHYSICAL))
774     tmp->attacktype |= AT_PHYSICAL;
775 elmex 1.1 }
776    
777 root 1.4 insert_ob_in_map (tmp, tmp->map, op, 0);
778     return 1;
779 elmex 1.1 }
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 root 1.4 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 elmex 1.1 }
849 root 1.4
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 elmex 1.1 return NULL;
857 root 1.4
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 elmex 1.1 }
871    
872 root 1.4 int
873     summon_object (object *op, object *caster, object *spell_ob, int dir, const char *stringarg)
874 elmex 1.1 {
875 root 1.4 sint16 x, y, nrof = 1, i;
876     archetype *summon_arch;
877     int ndir;
878    
879     if (spell_ob->other_arch)
880 root 1.5 summon_arch = spell_ob->other_arch;
881 root 1.4 else if (spell_ob->randomitems)
882     {
883     int level = caster_level (caster, spell_ob);
884 root 1.5 treasure *tr, *lasttr = NULL;
885 root 1.4
886 root 1.5 shstr_cmp sparam (stringarg);
887    
888     /* In old code, this was a very convoluted for statement,
889 root 1.4 * 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 elmex 1.18 if (!tr->item)
895     continue;
896    
897 root 1.4 if (level < tr->magic)
898     break;
899 root 1.5
900 root 1.4 lasttr = tr;
901 root 1.5
902 root 1.7 if (tr->item->name == sparam)
903 root 1.4 break;
904     }
905 root 1.5
906 root 1.4 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 root 1.5
913 root 1.4 summon_arch = lasttr->item;
914 elmex 1.18 nrof = lasttr->nrof;
915 root 1.4 }
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 root 1.15 if (!god && ((owner = op->owner) != NULL))
922 root 1.5 god = find_god (determine_god (owner));
923    
924 root 1.4 /* If we can't find a god, can't get what monster to summon */
925     if (!god)
926     return 0;
927 elmex 1.1
928 root 1.4 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 root 1.2 }
933 root 1.5
934 root 1.4 /* the summon level */
935     summon_level = caster_level (caster, spell_ob);
936     if (summon_level == 0)
937     summon_level = 1;
938 root 1.5
939 root 1.4 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 root 1.5
949 root 1.4 ndir = dir;
950 root 1.5
951 root 1.4 if (!ndir)
952     ndir = find_free_spot (mon, op->map, op->x, op->y, 1, SIZEOFFREE);
953 root 1.5
954 root 1.24 if (ndir < 0 || ob_blocked (mon, op->map, op->x + freearr_x[ndir], op->y + freearr_y[ndir]))
955 root 1.4 {
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 root 1.2 }
962     }
963 root 1.4 }
964     while (ndir == -1);
965 root 1.5
966 root 1.4 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 root 1.5
971 root 1.4 summon_arch = mon->arch;
972     }
973     else
974 root 1.5 summon_arch = 0;
975 elmex 1.1
976 root 1.4 if (spell_ob->stats.dam)
977     nrof += spell_ob->stats.dam + SP_level_dam_adjust (caster, spell_ob);
978 elmex 1.1
979 root 1.4 if (!summon_arch)
980     {
981     new_draw_info (NDI_UNIQUE, 0, op, "There is no monsters available for summoning.");
982     return 0;
983 elmex 1.1 }
984    
985 root 1.4 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 root 1.24 if (ndir < 0 || ob_blocked (&summon_arch->clone, op->map, op->x + x, op->y + y))
1005 root 1.4 {
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 root 1.15 tmp->set_owner (op);
1021 root 1.4 set_spell_skill (op, caster, spell_ob, tmp);
1022     tmp->enemy = op->enemy;
1023     tmp->type = 0;
1024     CLEAR_FLAG (tmp, FLAG_SLEEP);
1025 root 1.5
1026 root 1.4 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 root 1.5
1034 root 1.4 if (spell_ob->attack_movement)
1035     tmp->attack_movement = spell_ob->attack_movement;
1036 root 1.5
1037 root 1.15 if (op->owner)
1038     tmp->set_owner (op->owner);
1039 root 1.2 }
1040     }
1041     }
1042 root 1.5
1043 root 1.4 if (tmp->speed > MIN_ACTIVE_SPEED)
1044     tmp->speed_left = -1;
1045     }
1046 root 1.5
1047 root 1.4 if (head == NULL)
1048     head = tmp;
1049     else
1050     {
1051     tmp->head = head;
1052     prev->more = tmp;
1053 root 1.2 }
1054 root 1.5
1055 root 1.4 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 root 1.5
1061 root 1.4 head->direction = freedir[ndir];
1062     head->stats.exp = 0;
1063     head = insert_ob_in_map (head, head->map, op, 0);
1064 root 1.5
1065 root 1.4 if (head && head->randomitems)
1066     {
1067 root 1.19 create_treasure (head->randomitems, head, GT_APPLY | GT_STARTEQUIP, 6, 0);
1068 root 1.4
1069 root 1.19 for (object *tmp = head->inv; tmp; tmp = tmp->below)
1070 root 1.21 SET_FLAG (tmp, FLAG_DESTROY_ON_DEATH);
1071 root 1.2 }
1072 root 1.4 } /* for i < nrof */
1073 root 1.5
1074 root 1.4 return 1;
1075 elmex 1.1 }
1076    
1077     /* recursively look through the owner property of objects until the real owner
1078     is found */
1079 root 1.4 object *
1080     get_real_owner (object *ob)
1081     {
1082     object *realowner = ob;
1083    
1084     if (realowner == NULL)
1085     return NULL;
1086    
1087 root 1.15 while (realowner->owner != NULL)
1088 root 1.4 {
1089 root 1.15 realowner = realowner->owner;
1090 root 1.4 }
1091     return realowner;
1092 elmex 1.1 }
1093 root 1.4
1094 elmex 1.1 /* determines if checks so pets don't attack players or other pets should be
1095     overruled by the arena petmode */
1096 root 1.4 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 root 1.2 }
1169 root 1.4 }
1170    
1171     return 1;
1172 elmex 1.1 }