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

# 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 elmex 1.28 if (owner = pet->owner)
48 root 1.4 {
49     /* If the owner has turned on the pet, make the pet
50     * unfriendly.
51     */
52 root 1.29 if (check_enemy (owner, rv) == pet)
53 root 1.4 {
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 elmex 1.28 return 0;
67 root 1.4 }
68 root 1.29
69 root 1.4 /* If they are not on the same map, the pet won't be agressive */
70 elmex 1.28 //if (!on_same_map (pet, owner))
71     // return 0;
72 root 1.4
73     /* See if the pet has an existing enemy. If so, don't start a new one */
74 elmex 1.28 if (tmp = check_enemy (pet, rv))
75 root 1.4 {
76     if (tmp == owner && !QUERY_FLAG (pet, FLAG_CONFUSED) && QUERY_FLAG (pet, FLAG_FRIENDLY))
77     /* without this check, you can actually get pets with
78     * enemy set to owner!
79 root 1.2 */
80 elmex 1.28 pet->enemy = 0;
81 root 1.4 else
82     return tmp;
83     }
84 root 1.19
85 root 1.4 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 elmex 1.28 if (tmp != 0)
93 root 1.4 {
94     get_rangevector (pet, tmp, rv, 0);
95 elmex 1.28 if (check_enemy (pet, rv) != 0)
96 root 1.4 return tmp;
97     else
98 elmex 1.28 pet->enemy = 0;
99 root 1.4 }
100     /* if we got here we have no enemy */
101 elmex 1.28 /* we return 0 to avoid heading back to the owner */
102     pet->enemy = 0;
103     return 0;
104 root 1.4 }
105     }
106    
107     /* Since the pet has no existing enemy, look for anything nasty
108 elmex 1.28 * around the owner that it should go and attack. (if the owner is
109     * still on a map)
110 root 1.4 */
111 elmex 1.28 if (!owner->flag [FLAG_REMOVED])
112 root 1.4 {
113 elmex 1.28 tmp3 = 0;
114 root 1.19
115 elmex 1.28 for (i = 0; i < SIZEOFFREE; i++)
116 root 1.4 {
117 elmex 1.28 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 root 1.4 {
125 elmex 1.28 for (tmp = GET_MAP_OB (nm, x, y); tmp != 0; tmp = tmp->above)
126 root 1.4 {
127 elmex 1.28 object *tmp2 = tmp->head == 0 ? tmp : tmp->head;
128 root 1.4
129 elmex 1.28 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 root 1.4 {
134 elmex 1.28
135     if (!can_see_enemy (pet, tmp2))
136     {
137     if (tmp3 != 0)
138     tmp3 = tmp2;
139     }
140 root 1.4 else
141 elmex 1.28 {
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 root 1.4 }
164    
165     /* No threat to owner, check to see if the pet has an attacker */
166     if (attacker)
167     {
168 root 1.8 /* 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 root 1.4 {
172 root 1.8 pet->enemy = attacker;
173    
174 elmex 1.28 if (check_enemy (pet, rv) != 0)
175 root 1.8 return attacker;
176     else
177 elmex 1.28 pet->enemy = 0;
178 root 1.4 }
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 elmex 1.28 tmp3 = 0;
189 root 1.4 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 elmex 1.28 for (tmp = GET_MAP_OB (nm, x, y); tmp != 0; tmp = tmp->above)
199 root 1.4 {
200 elmex 1.28 object *tmp2 = tmp->head == 0 ? tmp : tmp->head;
201 root 1.4
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 elmex 1.28 if (tmp3 != 0)
211 root 1.4 tmp3 = tmp2;
212     }
213     else
214     {
215     pet->enemy = tmp2;
216 elmex 1.28 if (check_enemy (pet, rv) != 0)
217 root 1.4 return tmp2;
218     else
219 elmex 1.28 pet->enemy = 0;
220 root 1.2 }
221 root 1.4 } /* 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 elmex 1.28
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 root 1.4 } /* pet in defence mode */
237    
238 elmex 1.28 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 elmex 1.1
244 elmex 1.28 /* Didn't find anything - return the owner's enemy or 0 */
245     return enemy;
246 elmex 1.1 }
247    
248 root 1.4 void
249     terminate_all_pets (object *owner)
250     {
251 elmex 1.1 objectlink *obl, *next;
252 root 1.4
253 root 1.29 for (obl = first_friendly_object; obl; obl = next)
254 root 1.4 {
255     object *ob = obl->ob;
256 root 1.29 next = obl->next;
257 root 1.4
258 root 1.15 if (ob->owner == owner)
259 root 1.29 ob->destroy ();
260 elmex 1.1 }
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 root 1.4 void
272 root 1.11 remove_all_pets (maptile *map)
273 root 1.4 {
274 elmex 1.1 objectlink *obl, *next;
275     object *owner;
276    
277 root 1.24 for (obl = first_friendly_object; obl; obl = next)
278 elmex 1.1 {
279 root 1.4 next = obl->next;
280 root 1.24
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 root 1.4 {
286     /* follow owner checks map status for us */
287     follow_owner (obl->ob, owner);
288     }
289 elmex 1.1 }
290     }
291    
292 root 1.4 int
293     follow_owner (object *ob, object *owner)
294     {
295 elmex 1.28 if (owner->flag [FLAG_REMOVED])
296     return 0; // do nothing if the owner is removed
297 root 1.24 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 elmex 1.1
303 root 1.24 if (dir >= 0)
304     {
305     owner->map->insert (ob, owner->x + freearr_x[dir], owner->y + freearr_y[dir]);
306 elmex 1.1
307 root 1.24 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 elmex 1.1
310 root 1.24 return 0;
311 root 1.2 }
312 elmex 1.1 }
313    
314 root 1.14 ob->destroy ();
315 root 1.4 return 1;
316 elmex 1.1 }
317    
318 root 1.4 void
319     pet_move (object *ob)
320 elmex 1.1 {
321 elmex 1.28 int dir = 0, i;
322 root 1.4 sint16 dx, dy;
323     object *ob2, *owner;
324 root 1.11 maptile *m;
325 root 1.4
326     /* Check to see if player pulled out */
327 root 1.15 if ((owner = ob->owner) == NULL)
328 root 1.4 {
329 root 1.14 ob->destroy ();
330 root 1.4 LOG (llevMonster, "Pet: no owner, leaving.\n");
331     return;
332     }
333    
334 elmex 1.28 /* 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 root 1.4 {
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 elmex 1.28 else if (!owner->flag [FLAG_REMOVED]) // only get vector to owner when owner not removed
361 root 1.4 {
362     struct rv_vector rv;
363 elmex 1.1
364 root 1.4 get_rangevector (ob, ob->owner, &rv, 0);
365     dir = rv.direction;
366 elmex 1.1 }
367 root 1.4 ob->direction = dir;
368 elmex 1.1
369 root 1.4 /* 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 root 1.10 if (ob->destroyed ())
378 root 1.2 return;
379 root 1.4
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 root 1.16 for (ob2 = GET_MAP_OB (m, dx, dy); ob2 != NULL; ob2 = ob2->above)
389 root 1.4 {
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 root 1.15 if (new_ob->owner == ob->owner)
398 root 1.2 break;
399 root 1.4
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 root 1.2 }
417     }
418     }
419 root 1.4 /* Try a different course */
420 root 1.25 dir = absdir (dir + 4 - (rndm (5)) - (rndm (5)));
421 root 1.4 (void) move_ob (ob, dir, ob);
422 elmex 1.1 }
423 root 1.4 return;
424 elmex 1.1 }
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 root 1.4 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 root 1.19 for (atmp = at; atmp; atmp = atmp->more)
445 root 1.4 {
446     tmp = arch_to_object (atmp);
447 root 1.19
448 root 1.4 if (atmp == at)
449     {
450     if (!is_golem)
451     SET_FLAG (tmp, FLAG_MONSTER);
452 root 1.19
453 root 1.15 tmp->set_owner (op);
454 root 1.4 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 root 1.15 object *owner = op->owner;
466 root 1.4
467 root 1.19 if (owner)
468 root 1.4 { /* For now, we transfer ownership */
469 root 1.15 tmp->set_owner (owner);
470 root 1.4 tmp->attack_movement = PETMOVE;
471     add_friendly_object (tmp);
472 root 1.2 }
473     }
474     }
475 root 1.19
476 root 1.4 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 root 1.2 }
487 root 1.19
488     if (!head)
489 root 1.4 head = tmp;
490 root 1.19
491 root 1.4 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 root 1.19
495 root 1.4 if (tmp->invisible)
496     tmp->invisible = 0;
497 root 1.19
498 root 1.4 if (head != tmp)
499     tmp->head = head, prev->more = tmp;
500 root 1.19
501 root 1.4 prev = tmp;
502     }
503 root 1.19
504 root 1.4 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 elmex 1.1
520 root 1.4 return head;
521 elmex 1.1 }
522    
523     /* updated this to allow more than the golem 'head' to attack */
524     /* op is the golem to be moved. */
525 root 1.4 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 root 1.29 if (!op->owner)
535 root 1.4 {
536     LOG (llevDebug, "Golem without owner destructed.\n");
537 root 1.13 op->remove ();
538 root 1.14 op->destroy ();
539 root 1.4 return;
540     }
541 root 1.10
542 root 1.4 /* 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 root 1.12
552 root 1.14 op->destroy ();
553 root 1.4 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 root 1.10
564     if (op->destroyed ())
565 root 1.4 return;
566 elmex 1.1
567 root 1.4 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 root 1.11 maptile *m;
572 root 1.4 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 root 1.16 for (victim = GET_MAP_OB (op->map, x, y); victim; victim = victim->above)
581 root 1.4 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 root 1.2
597 root 1.4 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 root 1.2 }
607 root 1.4 else
608     {
609     attack_ob (victim, op);
610     made_attack = 1;
611     }
612     } /* If victim */
613 elmex 1.1 }
614 root 1.26
615 root 1.4 if (made_attack)
616     update_object (op, UP_OBJ_FACE);
617 elmex 1.1 }
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 root 1.4 void
625     control_golem (object *op, int dir)
626     {
627     op->direction = dir;
628 elmex 1.1 }
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 root 1.4 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 root 1.30 if (op->type == PLAYER && op->contr->golem)
647 root 1.4 {
648     new_draw_info (NDI_UNIQUE, 0, op, "You dismiss your existing golem.");
649 root 1.30 op->contr->golem->remove ();
650     op->contr->golem->destroy ();
651     op->contr->golem = 0;
652 root 1.4 }
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 elmex 1.1
660 root 1.4 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 root 1.2 }
665 root 1.6
666 root 1.4 at = determine_holy_arch (god, spob->race);
667 root 1.6
668 root 1.4 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 root 1.2 }
673 elmex 1.1 }
674 root 1.4 else
675     {
676     LOG (llevError, "Spell %s lacks other_arch\n", &spob->name);
677     return 0;
678     }
679    
680     if (!dir)
681 elmex 1.27 dir = find_free_spot (&at->clone, op->map, op->x, op->y, 1, SIZEOFFREE1 + 1);
682 root 1.4
683 root 1.24 if (dir < 0 || ob_blocked (&at->clone, op->map, op->x + freearr_x[dir], op->y + freearr_y[dir]))
684 root 1.4 {
685     new_draw_info (NDI_UNIQUE, 0, op, "There is something in the way.");
686     return 0;
687     }
688 root 1.19
689 root 1.4 /* basically want to get proper map/coordinates for this object */
690 elmex 1.1
691 root 1.4 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 elmex 1.1
697 root 1.4 if (op->type == PLAYER)
698     {
699     tmp->type = GOLEM;
700 root 1.15 tmp->set_owner (op);
701 root 1.30 op->contr->golem = tmp;
702 root 1.4 /* give the player control of the golem */
703 root 1.29 set_spell_skill (op, caster, spob, tmp);
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 root 1.29 towner = target->type == PLAYER ? 0 : get_real_owner (target);
1109 root 1.4
1110     /* if the pet has now owner, exit with error */
1111 root 1.29 if (!rowner)
1112 root 1.4 {
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 root 1.2 }
1162 root 1.4 }
1163    
1164     return 1;
1165 elmex 1.1 }