Crossfire Server, Trunk  R21670
pets.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
19 #include "global.h"
20 
21 #include <assert.h>
22 #include <ctype.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include "sproto.h"
27 
34 static void mark_inventory_as_no_drop(object *ob) {
35  FOR_INV_PREPARE(ob, tmp)
36  SET_FLAG(tmp, FLAG_NO_DROP);
38 }
39 
54 object *pets_get_enemy(object *pet, rv_vector *rv) {
55  object *owner, *tmp, *attacker, *tmp3;
56  int i;
57  int16_t x, y;
58  mapstruct *nm;
59  int search_arr[SIZEOFFREE];
60  int mflags;
61 
62  attacker = pet->attacked_by; /*pointer to attacking enemy*/
63  pet->attacked_by = NULL; /*clear this, since we are dealing with it*/
64 
65  owner = object_get_owner(pet);
66  if (owner != NULL) {
67  /* If the owner has turned on the pet, make the pet
68  * unfriendly.
69  */
70  if (monster_check_enemy(owner, rv) == pet) {
73  pet->attack_movement &= ~PETMOVE;
74  return owner;
75  }
76  } else {
77  /* else the owner is no longer around, so the
78  * pet no longer needs to be friendly.
79  */
82  pet->attack_movement &= ~PETMOVE;
83  return NULL;
84  }
85  /* If they are not on the same map, the pet won't be agressive */
86  if (!on_same_map(pet, owner))
87  return NULL;
88 
89  /* See if the pet has an existing enemy. If so, don't start a new one*/
90  tmp = monster_check_enemy(pet, rv);
91  if (tmp != NULL) {
92  if (tmp != owner || QUERY_FLAG(pet, FLAG_CONFUSED) || !QUERY_FLAG(pet, FLAG_FRIENDLY))
93  return tmp;
94 
95  /* without this check, you can actually get pets with
96  * enemy set to owner!
97  */
98  object_set_enemy(pet, NULL);
99  }
100  get_search_arr(search_arr);
101 
102  if (owner->type == PLAYER && owner->contr->petmode == pet_sad) {
104  if (tmp != NULL && get_rangevector(pet, tmp, rv, 0) && monster_check_enemy(pet, rv) != NULL)
105  return tmp;
106  /* if we got here we have no enemy */
107  /* we return NULL to avoid heading back to the owner */
108  object_set_enemy(pet, NULL);
109  return NULL;
110  }
111 
112  /* Since the pet has no existing enemy, look for anything nasty
113  * around the owner that it should go and attack.
114  */
115  tmp3 = NULL;
116  for (i = 0; i < SIZEOFFREE; i++) {
117  x = owner->x+freearr_x[search_arr[i]];
118  y = owner->y+freearr_y[search_arr[i]];
119  nm = owner->map;
120  /* Only look on the space if there is something alive there. */
121  mflags = get_map_flags(nm, &nm, x, y, &x, &y);
122  if (!(mflags&P_OUT_OF_MAP) && mflags&P_IS_ALIVE) {
123  FOR_MAP_PREPARE(nm, x, y, tmp) {
124  object *tmp2 = HEAD(tmp);
125 
126  if (QUERY_FLAG(tmp2, FLAG_ALIVE)
127  && ((!QUERY_FLAG(tmp2, FLAG_FRIENDLY) && tmp2->type != PLAYER) || pets_should_arena_attack(pet, owner, tmp2))
128  && !QUERY_FLAG(tmp2, FLAG_UNAGGRESSIVE)
129  && tmp2 != pet
130  && tmp2 != owner
131  && monster_can_detect_enemy(pet, tmp2, rv)) {
132  if (!monster_can_see_enemy(pet, tmp2)) {
133  if (tmp3 == NULL)
134  tmp3 = tmp2;
135  } else {
136  object_set_enemy(pet, tmp2);
137  if (monster_check_enemy(pet, rv) != NULL)
138  return tmp2;
139  object_set_enemy(pet, NULL);
140  }
141  }/* if this is a valid enemy */
142  } FOR_MAP_FINISH();/* for objects on this space */
143  }/* if there is something living on this space */
144  } /* for loop of spaces around the owner */
145 
146  /* fine, we went through the whole loop and didn't find one we could
147  see, take what we have */
148  if (tmp3 != NULL) {
149  object_set_enemy(pet, tmp3);
150  if (monster_check_enemy(pet, rv) != NULL)
151  return tmp3;
152  object_set_enemy(pet, NULL);
153  }
154 
155  /* No threat to owner, check to see if the pet has an attacker*/
156  if (attacker != NULL) {
157  /* need to be sure this is the right one! */
158  if (attacker->count == pet->attacked_by_count) {
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  object_set_enemy(pet, attacker);
163  if (monster_check_enemy(pet, rv) != NULL)
164  return attacker;
165  object_set_enemy(pet, NULL);
166  }
167  }
168  }
169 
170  /* Don't have an attacker or legal enemy, so look for a new one!.
171  * This looks for one around where the pet is. Thus, you could lead
172  * a pet to danger, then take a few steps back. This code is basically
173  * the same as the code that looks around the owner.
174  */
175  if (owner->type == PLAYER && owner->contr->petmode != pet_defend) {
176  tmp3 = NULL;
177  for (i = 0; i < SIZEOFFREE; i++) {
178  x = pet->x+freearr_x[search_arr[i]];
179  y = pet->y+freearr_y[search_arr[i]];
180  nm = pet->map;
181  /* Only look on the space if there is something alive there. */
182  mflags = get_map_flags(nm, &nm, x, y, &x, &y);
183  if (!(mflags&P_OUT_OF_MAP) && mflags&P_IS_ALIVE) {
184  FOR_MAP_PREPARE(nm, x, y, tmp) {
185  object *tmp2 = HEAD(tmp);
186  if (QUERY_FLAG(tmp2, FLAG_ALIVE)
187  && ((!QUERY_FLAG(tmp2, FLAG_FRIENDLY) && tmp2->type != PLAYER) || pets_should_arena_attack(pet, owner, tmp2))
188  && !QUERY_FLAG(tmp2, FLAG_UNAGGRESSIVE)
189  && tmp2 != pet
190  && tmp2 != owner
191  && monster_can_detect_enemy(pet, tmp2, rv)) {
192  if (!monster_can_see_enemy(pet, tmp2)) {
193  assert(tmp3 == NULL);
194  } else {
195  object_set_enemy(pet, tmp2);
196  if (monster_check_enemy(pet, rv) != NULL)
197  return tmp2;
198  object_set_enemy(pet, NULL);
199  }
200  } /* make sure we can get to the bugger */
201  } FOR_MAP_FINISH();/* for objects on this space */
202  } /* if there is something living on this space */
203  } /* for loop of spaces around the pet */
204  } /* pet in defence mode */
205 
206  /* fine, we went through the whole loop and didn't find one we could
207  see, take what we have */
208  if (tmp3 != NULL) {
209  object_set_enemy(pet, tmp3);
210  if (monster_check_enemy(pet, rv) != NULL)
211  return tmp3;
212  object_set_enemy(pet, NULL);
213  }
214 
215  /* Didn't find anything - return the owner's enemy or NULL */
216  return monster_check_enemy(pet, rv);
217 }
218 
225 void pets_terminate_all(object *owner) {
226  objectlink *obl, *next;
227 
228  for (obl = first_friendly_object; obl != NULL; obl = next) {
229  object *ob = obl->ob;
230  next = obl->next;
231  if (object_get_owner(ob) == owner) {
232  if (!QUERY_FLAG(ob, FLAG_REMOVED))
233  object_remove(ob);
236  }
237  }
238 }
239 
248 void pets_remove_all(void) {
249  objectlink *obl, *next;
250  object *owner;
251 
252  for (obl = first_friendly_object; obl != NULL; obl = next) {
253  next = obl->next;
254  if (obl->ob->type != PLAYER
255  && QUERY_FLAG(obl->ob, FLAG_FRIENDLY)
256  && (owner = object_get_owner(obl->ob)) != NULL
257  && !on_same_map(owner, obl->ob)) {
258  /* follow owner checks map status for us. Note that pet can
259  * die in pets_follow_owner(), so check for obl->ob existence
260  */
261  pets_follow_owner(obl->ob, owner);
262  if (obl->ob && QUERY_FLAG(obl->ob, FLAG_REMOVED) && FABS(obl->ob->speed) > MIN_ACTIVE_SPEED) {
263  object *ob = obl->ob;
264 
265  LOG(llevMonster, "(pet failed to follow)\n");
268  }
269  }
270  }
271 }
272 
281 void pets_follow_owner(object *ob, object *owner) {
282  int dir;
283 
284  if (!QUERY_FLAG(ob, FLAG_REMOVED))
285  object_remove(ob);
286 
287  if (owner->map == NULL) {
288  LOG(llevError, "Can't follow owner: no map.\n");
289  return;
290  }
291  if (owner->map->in_memory != MAP_IN_MEMORY) {
292  LOG(llevError, "Owner of the pet not on a map in memory!?\n");
293  return;
294  }
295  dir = object_find_free_spot(ob, owner->map, owner->x, owner->y, 1, SIZEOFFREE);
296 
297  if (dir == -1) {
298  LOG(llevMonster, "No space for pet to follow, freeing %s.\n", ob->name);
299  return; /* Will be freed since it's removed */
300  }
301  object_insert_in_map_at(ob, owner->map, NULL, 0, owner->x+freearr_x[dir], owner->y+freearr_y[dir]);
302  if (owner->type == PLAYER) /* Uh, I hope this is always true... */
304  "Your pet magically appears next to you");
305  return;
306 }
307 
314 void pets_move(object *ob) {
315  int dir, i;
316  tag_t tag;
317  int16_t dx, dy;
318  object *owner;
319  mapstruct *m;
320 
321  /* Check to see if player pulled out */
322  owner = object_get_owner(ob);
323  if (owner == NULL) {
324  object_remove(ob); /* Will be freed when returning */
327  LOG(llevMonster, "Pet: no owner, leaving.\n");
328  return;
329  }
330 
331  /* move monster into the owners map if not in the same map */
332  if (!on_same_map(ob, owner)) {
333  pets_follow_owner(ob, owner);
334  return;
335  }
336  /* Calculate Direction */
337  if (owner->type == PLAYER && owner->contr->petmode == pet_sad) {
338  /* in S&D mode, if we have no enemy, run randomly about. */
339  for (i = 0; i < 15; i++) {
340  dir = get_random_dir();
341  dx = ob->x+freearr_x[dir];
342  dy = ob->y+freearr_y[dir];
343  m = ob->map;
344  if (!(get_map_flags(ob->map, &m, dx, dy, &dx, &dy)&P_OUT_OF_MAP)
345  && !OB_TYPE_MOVE_BLOCK(ob, GET_MAP_MOVE_BLOCK(m, dx, dy)))
346  break;
347  }
348  } else {
349  rv_vector rv;
350 
351  if (get_rangevector(ob, owner, &rv, 0))
352  dir = rv.direction;
353  else
354  dir = get_random_dir();
355  }
356  ob->direction = dir;
357 
358  tag = ob->count;
359  /* move_ob returns 0 if the object couldn't move. If that is the
360  * case, lets do some other work.
361  */
362  if (!move_ob(ob, dir, ob)) {
363  object *part;
364 
365  /* the failed move_ob above may destroy the pet, so check here */
366  if (object_was_destroyed(ob, tag))
367  return;
368 
369  for (part = ob; part != NULL; part = part->more) {
370  dx = part->x+freearr_x[dir];
371  dy = part->y+freearr_y[dir];
372  m = get_map_from_coord(part->map, &dx, &dy);
373  if (m == NULL)
374  continue;
375 
376  FOR_MAP_PREPARE(m, dx, dy, ob2) {
377  object *new_ob;
378 
379  new_ob = HEAD(ob2);
380  if (new_ob == ob)
381  break;
382  if (new_ob == owner)
383  return;
384  if (object_get_owner(new_ob) == owner)
385  break;
386 
387  /* Hmm. Did we try to move into an enemy monster? If so,
388  * make it our enemy.
389  */
390  if (QUERY_FLAG(new_ob, FLAG_ALIVE)
392  && !QUERY_FLAG(new_ob, FLAG_UNAGGRESSIVE)
393  && !QUERY_FLAG(new_ob, FLAG_FRIENDLY)) {
394  object_set_enemy(ob, new_ob);
395  if (new_ob->enemy == NULL)
396  object_set_enemy(new_ob, ob);
397  return;
398  } else if (new_ob->type == PLAYER) {
399  draw_ext_info(NDI_UNIQUE, 0, new_ob,
401  "You stand in the way of someones pet.");
402  return;
403  }
404  } FOR_MAP_FINISH();
405  }
406  /* Try a different course */
407  dir = absdir(dir+4-(RANDOM()%5)-(RANDOM()%5));
408  (void)move_ob(ob, dir, ob);
409  }
410  return;
411 }
412 
413 /****************************************************************************
414  *
415  * GOLEM SPELL CODE
416  *
417  ****************************************************************************/
418 
431 static object *fix_summon_pet(archetype *at, object *op, int dir) {
432  archetype *atmp;
433  object *tmp = NULL, *prev = NULL, *head = NULL;
434 
435  for (atmp = at; atmp != NULL; atmp = atmp->more) {
436  tmp = arch_to_object(atmp);
437  if (atmp == at) {
438 
439  /* Ensure the golem can actually move if no move_type defined.
440  * This check is redundant since this is checked at server startup. */
441  if (tmp->move_type == 0) {
442  LOG(llevError, "summoned %s [%s] is without move_type!\n", tmp->name, atmp->name);
443  tmp->move_type = MOVE_WALK;
444  }
445 
446  object_set_owner(tmp, op);
447  if (op->type == PLAYER) {
448  tmp->stats.exp = 0;
449  add_friendly_object(tmp);
450  SET_FLAG(tmp, FLAG_FRIENDLY);
451  CLEAR_FLAG(tmp, FLAG_MONSTER);
452  } else if (QUERY_FLAG(op, FLAG_FRIENDLY)) {
453  object *owner = object_get_owner(op);
454 
455  if (owner != NULL) {/* For now, we transfer ownership */
456  object_set_owner(tmp, owner);
457  tmp->attack_movement = PETMOVE;
458  add_friendly_object(tmp);
459  SET_FLAG(tmp, FLAG_FRIENDLY);
460  }
461  }
462  if (op->type != PLAYER) {
463  tmp->attack_movement = PETMOVE;
464  tmp->speed_left = -1;
465  tmp->type = 0;
466  object_set_enemy(tmp, op->enemy);
467  } else
468  tmp->type = GOLEM;
469  }
470  if (head == NULL)
471  head = tmp;
472  tmp->x = op->x+freearr_x[dir]+tmp->arch->clone.x;
473  tmp->y = op->y+freearr_y[dir]+tmp->arch->clone.y;
474  tmp->map = op->map;
475  if (tmp->invisible)
476  tmp->invisible = 0;
477  if (head != tmp)
478  tmp->head = head,
479  prev->more = tmp;
480  prev = tmp;
481  }
482  head->direction = dir;
483 
484  if (head->randomitems) {
485  create_treasure(head->randomitems, head, GT_STARTEQUIP, 6, 0);
486  if (QUERY_FLAG(head, FLAG_MONSTER)) {
488  }
489  }
491 
492  /* need to change some monster attr to prevent problems/crashing */
493  head->last_heal = 0;
494  head->last_eat = 0;
495  head->last_grace = 0;
496  head->last_sp = 0;
497  head->other_arch = NULL;
498  head->stats.exp = 0;
499  CLEAR_FLAG(head, FLAG_CHANGING);
501  CLEAR_FLAG(head, FLAG_GENERATOR);
502  CLEAR_FLAG(head, FLAG_SPLITTING);
503  if (head->attacktype&AT_GHOSTHIT)
504  head->attacktype = (AT_PHYSICAL|AT_DRAIN);
505 
506  return head;
507 }
508 
516 void pets_move_golem(object *op) {
517  int made_attack = 0;
518  object *tmp;
519  tag_t tag;
520  object *owner;
521 
522  if (QUERY_FLAG(op, FLAG_MONSTER))
523  return; /* Has already been moved */
524 
525  owner = object_get_owner(op);
526  if (owner == NULL) {
527  LOG(llevDebug, "Golem without owner destructed.\n");
528  object_remove(op);
530  return;
531  }
532  /* It would be nice to have a cleaner way of what message to print
533  * when the golem expires than these hard coded entries.
534  * Note it is intentional that a golems duration is based on its
535  * hp, and not duration
536  */
537  if (--op->stats.hp < 0) {
538  if (op->msg != NULL)
540  op->msg);
541  owner->contr->ranges[range_golem] = NULL;
542  owner->contr->golem_count = 0;
544  object_remove(op);
546  return;
547  }
548 
549  /* Do golem attacks/movement for single & multisq golems.
550  * Assuming here that op is the 'head' object. Pass only op to
551  * move_ob (makes recursive calls to other parts)
552  * move_ob returns 0 if the creature was not able to move.
553  */
554  tag = op->count;
555  if (move_ob(op, op->direction, op))
556  return;
557  if (object_was_destroyed(op, tag))
558  return;
559 
560  for (tmp = op; tmp; tmp = tmp->more) {
561  int16_t x = tmp->x+DIRX(op), y = tmp->y+DIRY(op);
562  object *victim;
563  mapstruct *m;
564  int mflags;
565 
566  m = op->map;
567  mflags = get_map_flags(m, &m, x, y, &x, &y);
568 
569  if (mflags&P_OUT_OF_MAP)
570  continue;
571 
572  victim = NULL;
573  FOR_MAP_PREPARE(op->map, x, y, tmp)
574  if (QUERY_FLAG(tmp, FLAG_ALIVE)) {
575  victim = tmp;
576  break;
577  }
578  FOR_MAP_FINISH();
579 
580  /* We used to call will_hit_self to make sure we don't
581  * hit ourselves, but that didn't work, and I don't really
582  * know if that was more efficient anyways than this.
583  * This at least works. Note that victim->head can be NULL,
584  * but since we are not trying to dereferance that pointer,
585  * that isn't a problem.
586  */
587  if (victim != NULL && victim != op && victim->head != op) {
588  /* for golems with race fields, we don't attack
589  * aligned races
590  */
591 
592  if (victim->race != NULL && op->race != NULL && strstr(op->race, victim->race)) {
593  if (owner != NULL)
596  "%s avoids damaging %s.",
597  op->name, victim->name);
598  } else if (victim == owner) {
599  if (owner != NULL)
602  "%s avoids damaging you.",
603  op->name);
604  } else {
605  attack_ob(victim, op);
606  made_attack = 1;
607  }
608  } /* If victim */
609  }
610  if (made_attack)
612 }
613 
627 void pets_control_golem(object *op, int dir) {
628  op->direction = dir;
629 }
630 
648 int pets_summon_golem(object *op, object *caster, int dir, object *spob) {
649  object *tmp;
650  const object *god = NULL;
651  archetype *at;
652  char buf[MAX_BUF];
653 
654  /* Because there can be different golem spells, player may want to
655  * 'lose' their old golem.
656  */
657  if (op->type == PLAYER
658  && op->contr->ranges[range_golem] != NULL
659  && op->contr->golem_count == op->contr->ranges[range_golem]->count) {
660  draw_ext_info(NDI_UNIQUE, 0, op,
662  "You dismiss your existing golem.");
665  op->contr->ranges[range_golem] = NULL;
666  op->contr->golem_count = -1;
667  }
668 
669  if (spob->other_arch != NULL)
670  at = spob->other_arch;
671  else if (spob->race != NULL) {
672  god = find_god(determine_god(caster));
673  if (god == NULL) {
676  "You must worship a god to cast %s.",
677  spob->name);
678  return 0;
679  }
680 
681  at = determine_holy_arch(god, spob->race);
682  if (at == NULL) {
685  "%s has no %s for you to call.",
686  god->name, spob->race);
687  return 0;
688  }
689  } else {
690  LOG(llevError, "Spell %s lacks other_arch\n", spob->name);
691  return 0;
692  }
693 
694  if (!dir)
695  dir = object_find_free_spot(NULL, op->map, op->x, op->y, 1, SIZEOFFREE1+1);
696 
697  if (dir == -1
698  || ob_blocked(&at->clone, op->map, op->x+freearr_x[dir], op->y+freearr_y[dir])) {
699  draw_ext_info(NDI_UNIQUE, 0, op,
701  "There is something in the way.");
702  return 0;
703  }
704  /* basically want to get proper map/coordinates for this object */
705  tmp = fix_summon_pet(at, op, dir);
706  if (tmp == NULL) {
707  draw_ext_info(NDI_UNIQUE, 0, op,
709  "Your spell fails.");
710  return 0;
711  }
712 
713  if (op->type == PLAYER) {
714  tmp->type = GOLEM;
715  object_set_owner(tmp, op);
716  set_spell_skill(op, caster, spob, tmp);
717  op->contr->ranges[range_golem] = tmp;
718  op->contr->golem_count = tmp->count;
719  /* give the player control of the golem */
720  op->contr->shoottype = range_golem;
721  } else {
722  if (QUERY_FLAG(op, FLAG_FRIENDLY)) {
723  object *owner = object_get_owner(op);
724 
725  if (owner != NULL) { /* For now, we transfer ownership */
726  object_set_owner(tmp, owner);
727  tmp->attack_movement = PETMOVE;
728  add_friendly_object(tmp);
729  set_spell_skill(op, caster, spob, tmp);
730  SET_FLAG(tmp, FLAG_FRIENDLY);
731  }
732  }
733  SET_FLAG(tmp, FLAG_MONSTER);
734  }
735 
736  /* make the speed positive.*/
737  tmp->speed = FABS(tmp->speed);
738 
739  /* This sets the level dependencies on dam and hp for monsters */
740  /* players can't cope with too strong summonings. */
741  /* but monsters can. reserve these for players. */
742  if (op->type == PLAYER) {
743  tmp->stats.hp += spob->duration+SP_level_duration_adjust(caster, spob);
744  tmp->stats.maxhp = tmp->stats.hp;
745  if (!spob->stats.dam)
746  tmp->stats.dam += SP_level_dam_adjust(caster, spob);
747  else
748  tmp->stats.dam = spob->stats.dam+SP_level_dam_adjust(caster, spob);
749  tmp->speed += .02*SP_level_range_adjust(caster, spob);
750  tmp->speed = MIN(tmp->speed, 1.0);
751  if (spob->attacktype)
752  tmp->attacktype = spob->attacktype;
753  }
754  tmp->stats.wc -= SP_level_wc_adjust(caster, spob);
755 
756  /* limit the speed to 0.3 for non-players, 1 for players. */
757 
758  /* make experience increase in proportion to the strength.
759  * this is a bit simplistic - we are basically just looking at how
760  * often the sp doubles and use that as the ratio.
761  */
762  tmp->stats.exp *= 1+(MAX(spob->stats.maxgrace, spob->stats.sp)/caster_level(caster, spob));
763  tmp->speed_left = 0;
764  tmp->direction = dir;
765 
766  /* Holy spell - some additional tailoring */
767  if (god != NULL) {
768  object *tmp2;
769 
770  snprintf(buf, sizeof(buf), "%s of %s", spob->name, god->name);
771  buf[0] = toupper(buf[0]);
772  for (tmp2 = tmp; tmp2; tmp2 = tmp2->more) {
773  if (tmp2->name != NULL)
774  free_string(tmp2->name);
775  tmp2->name = add_string(buf);
776  }
777  tmp->attacktype |= god->attacktype;
778  memcpy(tmp->resist, god->resist, sizeof(tmp->resist));
779  if (tmp->race != NULL)
780  FREE_AND_CLEAR_STR(tmp->race);
781  if (god->race != NULL)
782  tmp->race = add_string(god->race);
783  if (tmp->slaying != NULL)
785  if (god->slaying != NULL)
786  tmp->slaying = add_string(god->slaying);
787  /* safety, we must allow a god's servants some reasonable attack */
788  if (!(tmp->attacktype&AT_PHYSICAL))
789  tmp->attacktype |= AT_PHYSICAL;
790  }
791 
792  object_insert_in_map_at(tmp, tmp->map, op, 0, tmp->x, tmp->y);
793  return 1;
794 }
795 
796 /***************************************************************************
797  *
798  * Summon monster/pet/other object code
799  *
800  ***************************************************************************/
801 
816 static object *choose_cult_monster(object *pl, const object *god, int summon_level) {
817  char buf[MAX_BUF];
818  const char *race;
819  int racenr, mon_nr, i;
820  racelink *list;
821  objectlink *tobl;
822  object *otmp;
823 
824  /* Determine the number of races available */
825  racenr = 0;
826  safe_strncpy(buf, god->race, sizeof(buf));
827  race = strtok(buf, ",");
828  while (race) {
829  racenr++;
830  race = strtok(NULL, ",");
831  }
832 
833  /* next, randomly select a race from the aligned_races string */
834  if (racenr > 1) {
835  racenr = rndm(0, racenr-1);
836  safe_strncpy(buf, god->race, sizeof(buf));
837  race = strtok(buf, ",");
838  for (i = 0; i < racenr; i++)
839  race = strtok(NULL, ",");
840  } else
841  race = god->race;
842 
843 
844  /* see if a we can match a race list of monsters. This should not
845  * happen, so graceful recovery isn't really needed, but this sanity
846  * checking is good for cases where the god archetypes mismatch the
847  * race file
848  */
849  list = find_racelink(race);
850  if (list == NULL) {
853  "The spell fails! %s's creatures are beyond the range of your summons",
854  god->name);
855  LOG(llevDebug, "choose_cult_monster() requested non-existent aligned race!\n");
856  return NULL;
857  }
858 
859  /* search for an apprplritate monster on this race list */
860  mon_nr = 0;
861  for (tobl = list->member; tobl; tobl = tobl->next) {
862  otmp = tobl->ob;
863  if (otmp == NULL || !QUERY_FLAG(otmp, FLAG_MONSTER))
864  continue;
865  if (otmp->level <= summon_level)
866  mon_nr++;
867  }
868 
869  /* If this god has multiple race entries, we should really choose another.
870  * But then we either need to track which ones we have tried, or just
871  * make so many calls to this function, and if we get so many without
872  * a valid entry, assuming nothing is available and quit.
873  */
874  if (!mon_nr)
875  return NULL;
876 
877  mon_nr = rndm(0, mon_nr-1);
878  for (tobl = list->member; tobl; tobl = tobl->next) {
879  otmp = tobl->ob;
880  if (otmp == NULL || !QUERY_FLAG(otmp, FLAG_MONSTER))
881  continue;
882  if (otmp->level <= summon_level && !mon_nr--)
883  return otmp;
884  }
885  /* This should not happen */
886  LOG(llevDebug, "choose_cult_monster() mon_nr was set, but did not find a monster\n");
887  return NULL;
888 }
889 
908 int pets_summon_object(object *op, object *caster, object *spell_ob, int dir, const char *stringarg) {
909  int16_t x, y, nrof = 1, i;
910  archetype *summon_arch;
911  int ndir, mult;
912 
913  if (spell_ob->other_arch != NULL) {
914  summon_arch = spell_ob->other_arch;
915  } else if (spell_ob->randomitems != NULL) {
916  int level = caster_level(caster, spell_ob);
917  treasure *tr, *lasttr = NULL;
918 
919  /* In old code, this was a very convuluted for statement,
920  * with all the checks in the 'for' portion itself. Much
921  * more readable to break some of the conditions out.
922  */
923  for (tr = spell_ob->randomitems->items; tr; tr = tr->next) {
924  if (level < tr->magic)
925  break;
926  lasttr = tr;
927  if (stringarg != NULL && !strcmp(tr->item->name, stringarg))
928  break;
929  if (tr->next == NULL || tr->next->item == NULL)
930  break;
931  }
932  if (lasttr == NULL) {
933  LOG(llevError, "Treasurelist %s did not generate a valid entry in pets_summon_object\n", spell_ob->randomitems->name);
934  draw_ext_info(NDI_UNIQUE, 0, op,
936  "The spell fails to summon any monsters.");
937  return 0;
938  }
939  summon_arch = lasttr->item;
940  nrof = lasttr->nrof;
941  } else if (spell_ob->race != NULL && !strcmp(spell_ob->race, "GODCULTMON")) {
942  const object *god = find_god(determine_god(op));
943  object *mon, *owner;
944  int summon_level, tries;
945 
946  if (god == NULL) {
947  owner = object_get_owner(op);
948  if (owner != NULL) {
949  god = find_god(determine_god(owner));
950  }
951  }
952  /* If we can't find a god, can't get what monster to summon */
953  if (god == NULL)
954  return 0;
955 
956  if (god->race == NULL) {
959  "%s has no creatures that you may summon!",
960  god->name);
961  return 0;
962  }
963  /* the summon level */
964  summon_level = caster_level(caster, spell_ob);
965  if (summon_level == 0)
966  summon_level = 1;
967  tries = 0;
968  do {
969  mon = choose_cult_monster(op, god, summon_level);
970  if (mon == NULL) {
973  "%s fails to send anything.",
974  god->name);
975  return 0;
976  }
977  ndir = dir;
978  if (!ndir)
979  ndir = object_find_free_spot(mon, op->map, op->x, op->y, 1, SIZEOFFREE);
980  if (ndir == -1
981  || ob_blocked(mon, op->map, op->x+freearr_x[ndir], op->y+freearr_y[ndir])) {
982  ndir = -1;
983  if (++tries == 5) {
984  draw_ext_info(NDI_UNIQUE, 0, op,
986  "There is something in the way.");
987  return 0;
988  }
989  }
990  } while (ndir == -1);
991  if (mon->level > summon_level/2)
992  nrof = random_roll(1, 2, op, PREFER_HIGH);
993  else
994  nrof = die_roll(2, 2, op, PREFER_HIGH);
995  summon_arch = mon->arch;
996  } else {
997  summon_arch = NULL;
998  }
999 
1000  if (spell_ob->stats.dam)
1001  nrof += spell_ob->stats.dam+SP_level_dam_adjust(caster, spell_ob);
1002 
1003  if (summon_arch == NULL) {
1005  "There is no monsters available for summoning.");
1006  return 0;
1007  }
1008 
1009  if (dir) {
1010  /* Only fail if caster specified a blocked direction. */
1011  x = freearr_x[dir];
1012  y = freearr_y[dir];
1013  if (ob_blocked(&summon_arch->clone, op->map, op->x+x, op->y+y)) {
1015  "There is something in the way.");
1016  return 0;
1017  }
1018  }
1019 
1020  mult = (RANDOM()%2 ? -1 : 1);
1021 
1022  for (i = 1; i <= nrof; i++) {
1023  archetype *atmp;
1024  object *prev = NULL, *head = NULL, *tmp;
1025 
1026  if (dir) {
1027  ndir = absdir(dir+(i/2)*mult);
1028  mult = -mult;
1029  } else
1030  ndir = object_find_free_spot(&summon_arch->clone, op->map, op->x, op->y, 1, SIZEOFFREE);
1031 
1032  x = ndir > 0 ? freearr_x[ndir] : 0;
1033  y = ndir > 0 ? freearr_y[ndir] : 0;
1034  if (ndir == -1 || ob_blocked(&summon_arch->clone, op->map, op->x+x, op->y+y))
1035  continue;
1036 
1037  for (atmp = summon_arch; atmp != NULL; atmp = atmp->more) {
1038  tmp = arch_to_object(atmp);
1039  if (atmp == summon_arch) {
1040  if (QUERY_FLAG(tmp, FLAG_MONSTER)) {
1041  object_set_owner(tmp, op);
1042  set_spell_skill(op, caster, spell_ob, tmp);
1043  object_set_enemy(tmp, op->enemy);
1044  tmp->type = 0;
1045  CLEAR_FLAG(tmp, FLAG_SLEEP);
1046  if (op->type == PLAYER || QUERY_FLAG(op, FLAG_FRIENDLY)) {
1047  /* If this is not set, we make it friendly */
1048  if (!QUERY_FLAG(spell_ob, FLAG_MONSTER)) {
1049  SET_FLAG(tmp, FLAG_FRIENDLY);
1050  add_friendly_object(tmp);
1051  tmp->stats.exp = 0;
1052  if (spell_ob->attack_movement)
1053  tmp->attack_movement = spell_ob->attack_movement;
1054  if (object_get_owner(op) != NULL)
1056  }
1057  }
1058  }
1059  if (tmp->speed > MIN_ACTIVE_SPEED)
1060  tmp->speed_left = -1;
1061  }
1062  if (head == NULL)
1063  head = tmp;
1064  else {
1065  tmp->head = head;
1066  prev->more = tmp;
1067  }
1068  prev = tmp;
1069  }
1070  head->direction = freedir[ndir];
1071  head->stats.exp = 0;
1072  head = object_insert_in_map_at(head, op->map, op, 0, op->x+x, op->y+y);
1073  if (head != NULL && head->randomitems) {
1074  create_treasure(head->randomitems, head, GT_STARTEQUIP, 6, 0);
1075  if (QUERY_FLAG(head, FLAG_MONSTER)) {
1077  }
1078  }
1079  if (head != NULL) {
1081  }
1082  } /* for i < nrof */
1083  return 1;
1084 }
1085 
1095 static object *get_real_owner(object *ob) {
1096  object *realowner = ob;
1097 
1098  if (realowner == NULL)
1099  return NULL;
1100 
1101  while (object_get_owner(realowner) != NULL) {
1102  realowner = object_get_owner(realowner);
1103  }
1104  return realowner;
1105 }
1106 
1122 int pets_should_arena_attack(object *pet, object *owner, object *target) {
1123  object *rowner, *towner;
1124 
1125  /* exit if the target, pet, or owner is null. */
1126  if (target == NULL || pet == NULL || owner == NULL)
1127  return 0;
1128 
1129  /* get the owners of itself and the target, this is to deal with pets of
1130  pets */
1131  rowner = get_real_owner(owner);
1132  if (target->type != PLAYER) {
1133  towner = get_real_owner(target);
1134  } else {
1135  towner = NULL;
1136  }
1137 
1138  /* if the pet has no owner, exit with error */
1139  if (rowner == NULL) {
1140  LOG(llevError, "Pet has no owner.\n");
1141  return 0;
1142  }
1143 
1144  /* if the target is not a player, and has no owner, we shouldn't be here
1145  */
1146  if (towner == NULL && target->type != PLAYER) {
1147  LOG(llevError, "Target is not a player but has no owner. We should not be here.\n");
1148  return 0;
1149  }
1150 
1151  /* make sure that the owner is a player */
1152  if (rowner->type != PLAYER)
1153  return 0;
1154 
1155  /* abort if the petmode is not arena */
1156  if (rowner->contr->petmode != pet_arena)
1157  return 0;
1158 
1159  /* abort if the pet, it's owner, or the target is not on battleground*/
1160  if (!(op_on_battleground(pet, NULL, NULL, NULL)
1161  && op_on_battleground(owner, NULL, NULL, NULL)
1162  && op_on_battleground(target, NULL, NULL, NULL)))
1163  return 0;
1164 
1165  /* if the target is a monster, make sure it's owner is not the same */
1166  if (target->type != PLAYER && rowner == towner)
1167  return 0;
1168 
1169  /* check if the target is a player which affects how it will handle
1170  parties */
1171  if (target->type != PLAYER) {
1172  /* if the target is owned by a player make sure than make sure
1173  it's not in the same party */
1174  if (towner->type == PLAYER && rowner->contr->party != NULL) {
1175  if (rowner->contr->party == towner->contr->party)
1176  return 0;
1177  }
1178  } else {
1179  /* if the target is a player make sure than make sure it's not
1180  in the same party */
1181  if (rowner->contr->party != NULL) {
1182  if (rowner->contr->party == target->contr->party)
1183  return 0;
1184  }
1185  }
1186 
1187  return 1;
1188 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Definition: main.c:316
#define MSG_TYPE_MISC
Definition: newclient.h:389
#define FLAG_NO_DROP
Definition: define.h:289
Definition: player.h:92
const char * determine_god(object *op)
Definition: gods.c:106
#define MOVE_WALK
Definition: define.h:407
#define UP_OBJ_FACE
Definition: object.h:519
static void mark_inventory_as_no_drop(object *ob)
Definition: pets.c:34
MoveType move_type
Definition: object.h:427
Definition: map.h:380
#define AT_GHOSTHIT
Definition: attack.h:85
#define FLAG_SLEEP
Definition: define.h:308
Definition: object.h:444
const char * race
Definition: object.h:318
int caster_level(const object *caster, const object *spell)
Definition: spell_util.c:233
uint16_t attack_movement
Definition: object.h:394
tag_t attacked_by_count
Definition: object.h:386
#define SET_FLAG(xyz, p)
Definition: define.h:223
#define FABS(x)
Definition: define.h:22
int monster_can_see_enemy(object *op, object *enemy)
Definition: monster.c:2575
void pets_move(object *ob)
Definition: pets.c:314
void pets_remove_all(void)
Definition: pets.c:248
Definition: race.h:12
#define FLAG_STAND_STILL
Definition: define.h:309
int pets_summon_object(object *op, object *caster, object *spell_ob, int dir, const char *stringarg)
Definition: pets.c:908
EXTERN objectlink * first_friendly_object
Definition: global.h:121
int pets_summon_golem(object *op, object *caster, int dir, object *spob)
Definition: pets.c:648
int attack_ob(object *op, object *hitter)
Definition: attack.c:915
#define FLAG_FRIENDLY
Definition: define.h:246
object * mon
Definition: comet_perf.c:74
#define MSG_TYPE_SPELL
Definition: newclient.h:387
uint32_t in_memory
Definition: map.h:345
void get_search_arr(int *search_arr)
Definition: object.c:3363
#define MSG_TYPE_SPELL_FAILURE
Definition: newclient.h:628
int16_t maxgrace
Definition: living.h:45
void free_string(sstring str)
Definition: shstr.c:280
mapstruct * get_map_from_coord(mapstruct *m, int16_t *x, int16_t *y)
Definition: map.c:2379
struct treasureliststruct * randomitems
Definition: object.h:388
void pets_terminate_all(object *owner)
Definition: pets.c:225
object clone
Definition: object.h:472
int16_t duration
Definition: object.h:406
int16_t invisible
Definition: object.h:362
short freearr_x[SIZEOFFREE]
Definition: object.c:65
object * monster_check_enemy(object *npc, rv_vector *rv)
Definition: monster.c:71
rangetype shoottype
Definition: player.h:99
#define DIRX(xyz)
Definition: define.h:478
const char * slaying
Definition: object.h:319
#define FLAG_CONFUSED
Definition: define.h:312
object * ranges[range_size]
Definition: player.h:103
int SP_level_dam_adjust(const object *caster, const object *spob)
Definition: spell_util.c:326
int64_t exp
Definition: living.h:47
#define object_was_destroyed(op, old_tag)
Definition: object.h:68
int freedir[SIZEOFFREE]
Definition: object.c:83
void remove_friendly_object(object *op)
Definition: friend.c:56
#define MAX(x, y)
Definition: compat.h:20
void object_update(object *op, int action)
Definition: object.c:1190
int16_t sp
Definition: living.h:42
const object * find_god(const char *name)
Definition: gods.c:80
#define PETMOVE
Definition: define.h:518
#define safe_strncpy
Definition: compat.h:23
struct obj * enemy
Definition: object.h:384
struct archt * other_arch
Definition: object.h:416
int absdir(int d)
Definition: object.c:3493
int pets_should_arena_attack(object *pet, object *owner, object *target)
Definition: pets.c:1122
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Definition: main.c:311
Definition: object.h:467
struct archt * item
Definition: treasure.h:64
#define MIN(x, y)
Definition: compat.h:17
#define FLAG_REMOVED
Definition: define.h:232
int16_t hp
Definition: living.h:40
#define DIRY(xyz)
Definition: define.h:479
short freearr_y[SIZEOFFREE]
Definition: object.c:71
partylist * party
Definition: player.h:186
void create_treasure(treasurelist *t, object *op, int flag, int difficulty, int tries)
Definition: treasure.c:488
object * ob
Definition: object.h:445
int rndm(int min, int max)
Definition: utils.c:162
void object_set_owner(object *op, object *owner)
Definition: object.c:604
#define MAP_IN_MEMORY
Definition: map.h:130
struct oblnk * member
Definition: race.h:15
void object_free_drop_inventory(object *ob)
Definition: object.c:1316
int16_t y
Definition: object.h:327
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Definition: object.c:1838
int16_t maxhp
Definition: living.h:41
#define FLAG_ALIVE
Definition: define.h:230
int8_t direction
Definition: object.h:336
float speed_left
Definition: object.h:330
signed short int16_t
Definition: win32.h:160
void pets_move_golem(object *op)
Definition: pets.c:516
racelink * find_racelink(const char *name)
Definition: init.c:1406
int SP_level_wc_adjust(const object *caster, const object *spob)
Definition: spell_util.c:401
#define FLAG_UNAGGRESSIVE
Definition: define.h:272
struct mapdef * map
Definition: object.h:297
void monster_check_apply_all(object *monster)
Definition: monster.c:1827
#define snprintf
Definition: win32.h:46
int ob_blocked(const object *ob, mapstruct *m, int16_t x, int16_t y)
Definition: map.c:497
#define FOR_INV_FINISH()
Definition: define.h:714
int SP_level_duration_adjust(const object *caster, const object *spob)
Definition: spell_util.c:351
int16_t dam
Definition: living.h:46
int die_roll(int num, int size, const object *op, int goodbad)
Definition: utils.c:122
void add_friendly_object(object *op)
Definition: friend.c:30
Definition: object.h:145
const char * name
Definition: object.h:311
#define FLAG_CHANGING
Definition: define.h:263
struct archt * more
Definition: object.h:471
int monster_can_detect_enemy(object *op, object *enemy, rv_vector *rv)
Definition: monster.c:2392
#define OB_TYPE_MOVE_BLOCK(ob1, type)
Definition: define.h:447
#define SIZEOFFREE
Definition: define.h:154
#define P_OUT_OF_MAP
Definition: map.h:251
#define GET_MAP_MOVE_BLOCK(M, X, Y)
Definition: map.h:192
int move_ob(object *op, int dir, object *originator)
Definition: move.c:58
struct pl * contr
Definition: object.h:276
int get_random_dir(void)
Definition: utils.c:427
int op_on_battleground(object *op, int *x, int *y, archetype **trophy)
Definition: player.c:4306
#define FREE_AND_CLEAR_STR(xyz)
Definition: global.h:205
uint32_t tag_t
Definition: object.h:12
#define AT_PHYSICAL
Definition: attack.h:76
float speed
Definition: object.h:329
int on_same_map(const object *op1, const object *op2)
Definition: map.c:2651
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
#define CLEAR_FLAG(xyz, p)
Definition: define.h:224
#define HEAD(op)
Definition: object.h:594
uint32_t golem_count
Definition: player.h:106
#define MAX_BUF
Definition: define.h:35
uint16_t nrof
Definition: treasure.h:76
int16_t x
Definition: object.h:327
struct treasurestruct * items
Definition: treasure.h:90
int8_t wc
Definition: living.h:37
#define AT_DRAIN
Definition: attack.h:83
#define FOR_MAP_FINISH()
Definition: define.h:767
int16_t resist[NROFATTACKS]
Definition: object.h:343
Definition: object.h:107
const char * name
Definition: treasure.h:83
#define PREFER_HIGH
Definition: define.h:600
uint32_t attacktype
Definition: object.h:344
#define FLAG_GENERATOR
Definition: define.h:248
#define RANDOM()
Definition: define.h:681
archetype * determine_holy_arch(const object *god, const char *type)
Definition: gods.c:741
int SP_level_range_adjust(const object *caster, const object *spob)
Definition: spell_util.c:377
#define MSG_TYPE_SPELL_PET
Definition: newclient.h:627
tag_t count
Definition: object.h:299
living stats
Definition: object.h:370
struct archt * arch
Definition: object.h:415
void set_spell_skill(object *op, object *caster, object *spob, object *dest)
Definition: spell_util.c:94
struct oblnk * next
Definition: object.h:446
uint8_t type
Definition: object.h:340
void object_set_enemy(object *op, object *enemy)
Definition: object.c:679
int direction
Definition: map.h:384
#define FLAG_SPLITTING
Definition: define.h:266
static object * get_real_owner(object *ob)
Definition: pets.c:1095
const char * msg
Definition: object.h:322
void pets_follow_owner(object *ob, object *owner)
Definition: pets.c:281
sstring add_string(const char *str)
Definition: shstr.c:124
#define MIN_ACTIVE_SPEED
Definition: define.h:676
#define FLAG_MONSTER
Definition: define.h:245
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
Definition: map.c:310
petmode_t petmode
Definition: player.h:102
#define NDI_UNIQUE
Definition: newclient.h:245
#define SIZEOFFREE1
Definition: define.h:152
struct obj * head
Definition: object.h:296
#define MSG_SUBTYPE_NONE
Definition: newclient.h:398
Definition: player.h:46
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
void pets_control_golem(object *op, int dir)
Definition: pets.c:627
static object * choose_cult_monster(object *pl, const object *god, int summon_level)
Definition: pets.c:816
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:760
int object_find_free_spot(const object *ob, mapstruct *m, int x, int y, int start, int stop)
Definition: object.c:3280
Definition: map.h:325
#define P_IS_ALIVE
Definition: map.h:237
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:42
static object * fix_summon_pet(archetype *at, object *op, int dir)
Definition: pets.c:431
int get_rangevector(object *op1, const object *op2, rv_vector *retval, int flags)
Definition: map.c:2539
struct treasurestruct * next
Definition: treasure.h:66
object * monster_find_nearest_living_creature(object *npc)
Definition: monster.c:152
int16_t level
Definition: object.h:353
struct obj * more
Definition: object.h:295
object * arch_to_object(archetype *at)
Definition: arch.c:571
object * object_get_owner(object *op)
Definition: object.c:568
const char * name
Definition: object.h:468
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:707
void object_remove(object *op)
Definition: object.c:1577
struct obj * attacked_by
Definition: object.h:385
object * pets_get_enemy(object *pet, rv_vector *rv)
Definition: pets.c:54