ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/pets.c
Revision: 1.1
Committed: Fri Feb 3 07:14:34 2006 UTC (18 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Branch point for: UPSTREAM
Log Message:
Initial revision

File Contents

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