Crossfire Server, Branch 1.12  R12190
pets.c
Go to the documentation of this file.
00001 /*
00002  * static char *rcsid_pets_c =
00003  *    "$Id: pets.c 11578 2009-02-23 22:02:27Z lalo $";
00004  */
00005 
00006 /*
00007     CrossFire, A Multiplayer game for X-windows
00008 
00009     Copyright (C) 2006 Mark Wedel & Crossfire Development Team
00010     Copyright (C) 1992 Frank Tore Johansen
00011 
00012     This program is free software; you can redistribute it and/or modify
00013     it under the terms of the GNU General Public License as published by
00014     the Free Software Foundation; either version 2 of the License, or
00015     (at your option) any later version.
00016 
00017     This program is distributed in the hope that it will be useful,
00018     but WITHOUT ANY WARRANTY; without even the implied warranty of
00019     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020     GNU General Public License for more details.
00021 
00022     You should have received a copy of the GNU General Public License
00023     along with this program; if not, write to the Free Software
00024     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00025 
00026     The authors can be reached via e-mail at crossfire-devel@real-time.com
00027 */
00028 
00034 #include <global.h>
00035 #ifndef __CEXTRACT__
00036 #include <sproto.h>
00037 #endif
00038 
00045 static void mark_inventory_as_no_drop(object *ob) {
00046     object *tmp;
00047 
00048     for (tmp = ob->inv; tmp != NULL; tmp = tmp->below) {
00049         SET_FLAG(tmp, FLAG_NO_DROP);
00050     }
00051 }
00052 
00067 object *get_pet_enemy(object *pet, rv_vector *rv) {
00068     object *owner, *tmp, *attacker, *tmp3;
00069     int i;
00070     sint16 x, y;
00071     mapstruct *nm;
00072     int search_arr[SIZEOFFREE];
00073     int mflags;
00074 
00075     attacker = pet->attacked_by; /*pointer to attacking enemy*/
00076     pet->attacked_by = NULL;     /*clear this, since we are dealing with it*/
00077 
00078     if ((owner = get_owner(pet)) != NULL) {
00079         /* If the owner has turned on the pet, make the pet
00080          * unfriendly.
00081          */
00082         if ((check_enemy(owner, rv)) == pet) {
00083             CLEAR_FLAG(pet, FLAG_FRIENDLY);
00084             remove_friendly_object(pet);
00085             pet->attack_movement &= ~PETMOVE;
00086             return owner;
00087         }
00088     } else {
00089         /* else the owner is no longer around, so the
00090          * pet no longer needs to be friendly.
00091          */
00092         CLEAR_FLAG(pet, FLAG_FRIENDLY);
00093         remove_friendly_object(pet);
00094         pet->attack_movement &= ~PETMOVE;
00095         return NULL;
00096     }
00097     /* If they are not on the same map, the pet won't be agressive */
00098     if (!on_same_map(pet, owner))
00099         return NULL;
00100 
00101     /* See if the pet has an existing enemy. If so, don't start a new one*/
00102     if ((tmp = check_enemy(pet, rv)) != NULL) {
00103         if (tmp == owner && !QUERY_FLAG(pet, FLAG_CONFUSED)
00104             && QUERY_FLAG(pet, FLAG_FRIENDLY))
00105             /* without this check, you can actually get pets with
00106              * enemy set to owner!
00107              */
00108             pet->enemy = NULL;
00109         else
00110             return tmp;
00111     }
00112     get_search_arr(search_arr);
00113 
00114     if (owner->type == PLAYER && owner->contr->petmode > pet_normal) {
00115         if (owner->contr->petmode == pet_sad) {
00116             tmp = find_nearest_living_creature(pet);
00117             if (tmp != NULL) {
00118                 get_rangevector(pet, tmp, rv, 0);
00119                 if (check_enemy(pet, rv) != NULL)
00120                     return tmp;
00121                 else
00122                     pet->enemy = NULL;
00123             }
00124             /* if we got here we have no enemy */
00125             /* we return NULL to avoid heading back to the owner */
00126             pet->enemy = NULL;
00127             return NULL;
00128         }
00129     }
00130 
00131     /* Since the pet has no existing enemy, look for anything nasty
00132      * around the owner that it should go and attack.
00133      */
00134     tmp3 = NULL;
00135     for (i = 0; i < SIZEOFFREE; i++) {
00136         x = owner->x+freearr_x[search_arr[i]];
00137         y = owner->y+freearr_y[search_arr[i]];
00138         nm = owner->map;
00139         /* Only look on the space if there is something alive there. */
00140         mflags = get_map_flags(nm, &nm, x, y, &x, &y);
00141         if (!(mflags&P_OUT_OF_MAP) && mflags&P_IS_ALIVE) {
00142             for (tmp = GET_MAP_OB(nm, x, y); tmp != NULL; tmp = tmp->above) {
00143                 object *tmp2 = tmp->head == NULL ? tmp : tmp->head;
00144 
00145                 if (QUERY_FLAG(tmp2, FLAG_ALIVE)
00146                 && ((!QUERY_FLAG(tmp2, FLAG_FRIENDLY) && tmp2->type != PLAYER) || should_arena_attack(pet, owner, tmp2))
00147                 && !QUERY_FLAG(tmp2, FLAG_UNAGGRESSIVE)
00148                 && tmp2 != pet
00149                 && tmp2 != owner
00150                 && can_detect_enemy(pet, tmp2, rv)) {
00151                     if (!can_see_enemy(pet, tmp2)) {
00152                         if (tmp3 != NULL)
00153                             tmp3 = tmp2;
00154                     } else {
00155                         pet->enemy = tmp2;
00156                         if (check_enemy(pet, rv) != NULL)
00157                             return tmp2;
00158                         else
00159                             pet->enemy = NULL;
00160                     }
00161                 }/* if this is a valid enemy */
00162             }/* for objects on this space */
00163         }/* if there is something living on this space */
00164     } /* for loop of spaces around the owner */
00165 
00166     /* fine, we went through the whole loop and didn't find one we could
00167        see, take what we have */
00168     if (tmp3 != NULL) {
00169         pet->enemy = tmp3;
00170         if (check_enemy(pet, rv) != NULL)
00171             return tmp3;
00172         else
00173             pet->enemy = NULL;
00174     }
00175 
00176     /* No threat to owner, check to see if the pet has an attacker*/
00177     if (attacker) {
00178         /* need to be sure this is the right one! */
00179         if (attacker->count == pet->attacked_by_count) {
00180             /* also need to check to make sure it is not freindly */
00181             /* or otherwise non-hostile, and is an appropriate target */
00182             if (!QUERY_FLAG(attacker, FLAG_FRIENDLY) && on_same_map(pet, attacker)) {
00183                 pet->enemy = attacker;
00184                 if (check_enemy(pet, rv) != NULL)
00185                     return attacker;
00186                 else
00187                     pet->enemy = NULL;
00188             }
00189         }
00190     }
00191 
00192     /* Don't have an attacker or legal enemy, so look for a new one!.
00193      * This looks for one around where the pet is.  Thus, you could lead
00194      * a pet to danger, then take a few steps back.  This code is basically
00195      * the same as the code that looks around the owner.
00196      */
00197     if (owner->type == PLAYER && owner->contr->petmode != pet_defend) {
00198         tmp3 = NULL;
00199         for (i = 0; i < SIZEOFFREE; i++) {
00200             x = pet->x+freearr_x[search_arr[i]];
00201             y = pet->y+freearr_y[search_arr[i]];
00202             nm = pet->map;
00203             /* Only look on the space if there is something alive there. */
00204             mflags = get_map_flags(nm, &nm, x, y, &x, &y);
00205             if (!(mflags&P_OUT_OF_MAP) && mflags&P_IS_ALIVE) {
00206                 for (tmp = GET_MAP_OB(nm, x, y); tmp != NULL; tmp = tmp->above) {
00207                     object *tmp2 = tmp->head == NULL ? tmp : tmp->head;
00208                     if (QUERY_FLAG(tmp2, FLAG_ALIVE)
00209                     && ((!QUERY_FLAG(tmp2, FLAG_FRIENDLY) && tmp2->type != PLAYER) || should_arena_attack(pet, owner, tmp2))
00210                     && !QUERY_FLAG(tmp2, FLAG_UNAGGRESSIVE)
00211                     && tmp2 != pet
00212                     && tmp2 != owner
00213                     && can_detect_enemy(pet, tmp2, rv)) {
00214                         if (!can_see_enemy(pet, tmp2)) {
00215                             if (tmp3 != NULL)
00216                                 tmp3 = tmp2;
00217                         } else {
00218                             pet->enemy = tmp2;
00219                             if (check_enemy(pet, rv) != NULL)
00220                                 return tmp2;
00221                             else
00222                                 pet->enemy = NULL;
00223                         }
00224                     } /* make sure we can get to the bugger */
00225                 }/* for objects on this space */
00226             } /* if there is something living on this space */
00227         } /* for loop of spaces around the pet */
00228     } /* pet in defence mode */
00229 
00230     /* fine, we went through the whole loop and didn't find one we could
00231        see, take what we have */
00232     if (tmp3 != NULL) {
00233         pet->enemy = tmp3;
00234         if (check_enemy(pet, rv) != NULL)
00235             return tmp3;
00236         else
00237             pet->enemy = NULL;
00238     }
00239 
00240     /* Didn't find anything - return the owner's enemy or NULL */
00241     return check_enemy(pet, rv);
00242 }
00243 
00250 void terminate_all_pets(object *owner) {
00251     objectlink *obl, *next;
00252 
00253     for (obl = first_friendly_object; obl != NULL; obl = next) {
00254         object *ob = obl->ob;
00255         next = obl->next;
00256         if (get_owner(ob) == owner) {
00257             if (!QUERY_FLAG(ob, FLAG_REMOVED))
00258                 remove_ob(ob);
00259             remove_friendly_object(ob);
00260             free_object(ob);
00261         }
00262     }
00263 }
00264 
00273 void remove_all_pets(void) {
00274     objectlink *obl, *next;
00275     object *owner;
00276 
00277     for (obl = first_friendly_object; obl != NULL; obl = next) {
00278         next = obl->next;
00279         if (obl->ob->type != PLAYER
00280         && QUERY_FLAG(obl->ob, FLAG_FRIENDLY)
00281         && (owner = get_owner(obl->ob)) != NULL
00282         && !on_same_map(owner, obl->ob)) {
00283             /* follow owner checks map status for us.  Note that pet can
00284              * die in follow_owner, so check for obl->ob existence
00285              */
00286             follow_owner(obl->ob, owner);
00287             if (obl->ob && QUERY_FLAG(obl->ob, FLAG_REMOVED) && FABS(obl->ob->speed) > MIN_ACTIVE_SPEED) {
00288                 object *ob = obl->ob;
00289 
00290                 LOG(llevMonster, "(pet failed to follow)\n");
00291                 remove_friendly_object(ob);
00292                 free_object(ob);
00293             }
00294         }
00295     }
00296 }
00297 
00306 void follow_owner(object *ob, object *owner) {
00307     object *tmp;
00308     int dir;
00309 
00310     if (!QUERY_FLAG(ob, FLAG_REMOVED))
00311         remove_ob(ob);
00312 
00313     if (owner->map == NULL) {
00314         LOG(llevError, "Can't follow owner: no map.\n");
00315         return;
00316     }
00317     if (owner->map->in_memory != MAP_IN_MEMORY) {
00318         LOG(llevError, "Owner of the pet not on a map in memory!?\n");
00319         return;
00320     }
00321     dir = find_free_spot(ob, owner->map, owner->x, owner->y, 1, SIZEOFFREE);
00322 
00323     if (dir == -1) {
00324         LOG(llevMonster, "No space for pet to follow, freeing %s.\n", ob->name);
00325         return; /* Will be freed since it's removed */
00326     }
00327     for (tmp = ob; tmp != NULL; tmp = tmp->more) {
00328         tmp->x = owner->x+freearr_x[dir]+(tmp->arch == NULL ? 0 : tmp->arch->clone.x);
00329         tmp->y = owner->y+freearr_y[dir]+(tmp->arch == NULL ? 0 : tmp->arch->clone.y);
00330         tmp->map = owner->map;
00331         if (OUT_OF_REAL_MAP(tmp->map, tmp->x, tmp->y)) {
00332             tmp->map = get_map_from_coord(tmp->map, &tmp->x, &tmp->y);
00333         }
00334     }
00335     insert_ob_in_map(ob, ob->map, NULL, 0);
00336     if (owner->type == PLAYER) /* Uh, I hope this is always true... */
00337         draw_ext_info(NDI_UNIQUE, 0, owner, MSG_TYPE_SPELL, MSG_TYPE_SPELL_PET,
00338                       "Your pet magically appears next to you", NULL);
00339     return;
00340 }
00341 
00348 void pet_move(object *ob) {
00349     int dir, i;
00350     tag_t tag;
00351     sint16 dx, dy;
00352     object *ob2, *owner;
00353     mapstruct *m;
00354 
00355     /* Check to see if player pulled out */
00356     if ((owner = get_owner(ob)) == NULL) {
00357         remove_ob(ob); /* Will be freed when returning */
00358         remove_friendly_object(ob);
00359         free_object(ob);
00360         LOG(llevMonster, "Pet: no owner, leaving.\n");
00361         return;
00362     }
00363 
00364     /* move monster into the owners map if not in the same map */
00365     if (!on_same_map(ob, owner)) {
00366         follow_owner(ob, owner);
00367         return;
00368     }
00369     /* Calculate Direction */
00370     if (owner->type == PLAYER && owner->contr->petmode == pet_sad) {
00371         /* in S&D mode, if we have no enemy, run randomly about. */
00372         for (i = 0; i < 15; i++) {
00373             dir = rndm(1, 8);
00374             dx = ob->x+freearr_x[dir];
00375             dy = ob->y+freearr_y[dir];
00376             m = ob->map;
00377             if (get_map_flags(ob->map, &m, dx, dy, &dx, &dy)&P_OUT_OF_MAP)
00378                 continue;
00379             else if (OB_TYPE_MOVE_BLOCK(ob, GET_MAP_MOVE_BLOCK(m, dx, dy)))
00380                 continue;
00381             else
00382                 break;
00383         }
00384     } else {
00385         dir = find_dir_2(ob->x-ob->owner->x, ob->y-ob->owner->y);
00386     }
00387     ob->direction = dir;
00388 
00389     tag = ob->count;
00390     /* move_ob returns 0 if the object couldn't move.  If that is the
00391      * case, lets do some other work.
00392      */
00393     if (!(move_ob(ob, dir, ob))) {
00394         object *part;
00395 
00396         /* the failed move_ob above may destroy the pet, so check here */
00397         if (was_destroyed(ob, tag))
00398             return;
00399 
00400         for (part = ob; part != NULL; part = part->more) {
00401             dx = part->x+freearr_x[dir];
00402             dy = part->y+freearr_y[dir];
00403             m = get_map_from_coord(part->map, &dx, &dy);
00404             if (!m)
00405                 continue;
00406 
00407             for (ob2 = GET_MAP_OB(m, dx, dy); ob2 != NULL; ob2 = ob2->above) {
00408                 object *new_ob;
00409 
00410                 new_ob = ob2->head ? ob2->head : ob2;
00411                 if (new_ob == ob)
00412                     break;
00413                 if (new_ob == ob->owner)
00414                     return;
00415                 if (get_owner(new_ob) == ob->owner)
00416                     break;
00417 
00418                 /* Hmm.  Did we try to move into an enemy monster?  If so,
00419                  * make it our enemy.
00420                  */
00421                 if (QUERY_FLAG(new_ob, FLAG_ALIVE)
00422                 && !QUERY_FLAG(ob, FLAG_UNAGGRESSIVE)
00423                 && !QUERY_FLAG(new_ob, FLAG_UNAGGRESSIVE)
00424                 && !QUERY_FLAG(new_ob, FLAG_FRIENDLY)) {
00425                     ob->enemy = new_ob;
00426                     if (new_ob->enemy == NULL)
00427                         new_ob->enemy = ob;
00428                     return;
00429                 } else if (new_ob->type == PLAYER) {
00430                     draw_ext_info(NDI_UNIQUE, 0, new_ob,
00431                                   MSG_TYPE_MISC, MSG_SUBTYPE_NONE,
00432                                   "You stand in the way of someones pet.", NULL);
00433                     return;
00434                 }
00435             }
00436         }
00437         /* Try a different course */
00438         dir = absdir(dir+4-(RANDOM()%5)-(RANDOM()%5));
00439         (void)move_ob(ob, dir, ob);
00440     }
00441     return;
00442 }
00443 
00444 /****************************************************************************
00445  *
00446  * GOLEM SPELL CODE
00447  *
00448  ****************************************************************************/
00449 
00464 static object *fix_summon_pet(archetype *at, object *op, int dir, int is_golem) {
00465     archetype *atmp;
00466     object *tmp = NULL, *prev = NULL, *head = NULL;
00467 
00468     for (atmp = at; atmp != NULL; atmp = atmp->more) {
00469         tmp = arch_to_object(atmp);
00470         if (atmp == at) {
00471             if (!is_golem)
00472                 SET_FLAG(tmp, FLAG_MONSTER);
00473             set_owner(tmp, op);
00474             if (op->type == PLAYER) {
00475                 tmp->stats.exp = 0;
00476                 add_friendly_object(tmp);
00477                 SET_FLAG(tmp, FLAG_FRIENDLY);
00478                 if (is_golem)
00479                     CLEAR_FLAG(tmp, FLAG_MONSTER);
00480             } else if (QUERY_FLAG(op, FLAG_FRIENDLY)) {
00481                 object *owner = get_owner(op);
00482 
00483                 if (owner != NULL) {/* For now, we transfer ownership */
00484                     set_owner(tmp, owner);
00485                     tmp->attack_movement = PETMOVE;
00486                     add_friendly_object(tmp);
00487                     SET_FLAG(tmp, FLAG_FRIENDLY);
00488                 }
00489             }
00490             if (op->type != PLAYER || !is_golem) {
00491                 tmp->attack_movement = PETMOVE;
00492                 tmp->speed_left = -1;
00493                 tmp->type = 0;
00494                 tmp->enemy = op->enemy;
00495             } else
00496                 tmp->type = GOLEM;
00497         }
00498         if (head == NULL)
00499             head = tmp;
00500         tmp->x = op->x+freearr_x[dir]+tmp->arch->clone.x;
00501         tmp->y = op->y+freearr_y[dir]+tmp->arch->clone.y;
00502         tmp->map = op->map;
00503         if (tmp->invisible)
00504             tmp->invisible = 0;
00505         if (head != tmp)
00506             tmp->head = head,
00507             prev->more = tmp;
00508         prev = tmp;
00509     }
00510     head->direction = dir;
00511 
00512     if (head->randomitems) {
00513         create_treasure(head->randomitems, head, GT_APPLY|GT_STARTEQUIP, 6, 0);
00514     }
00515     mark_inventory_as_no_drop(head);
00516 
00517     /* need to change some monster attr to prevent problems/crashing */
00518     head->last_heal = 0;
00519     head->last_eat = 0;
00520     head->last_grace = 0;
00521     head->last_sp = 0;
00522     head->other_arch = NULL;
00523     head->stats.exp = 0;
00524     CLEAR_FLAG(head, FLAG_CHANGING);
00525     CLEAR_FLAG(head, FLAG_STAND_STILL);
00526     CLEAR_FLAG(head, FLAG_GENERATOR);
00527     CLEAR_FLAG(head, FLAG_SPLITTING);
00528     if (head->attacktype&AT_GHOSTHIT)
00529         head->attacktype = (AT_PHYSICAL|AT_DRAIN);
00530 
00531     return head;
00532 }
00533 
00541 void move_golem(object *op) {
00542     int made_attack = 0;
00543     object *tmp;
00544     tag_t tag;
00545 
00546     if (QUERY_FLAG(op, FLAG_MONSTER))
00547         return; /* Has already been moved */
00548 
00549     if (get_owner(op) == NULL) {
00550         LOG(llevDebug, "Golem without owner destructed.\n");
00551         remove_ob(op);
00552         free_object(op);
00553         return;
00554     }
00555     /* It would be nice to have a cleaner way of what message to print
00556      * when the golem expires than these hard coded entries.
00557      * Note it is intentional that a golems duration is based on its
00558      * hp, and not duration
00559      */
00560     if (--op->stats.hp < 0) {
00561         if (op->msg)
00562             draw_ext_info(NDI_UNIQUE, 0, op->owner, MSG_TYPE_SPELL, MSG_TYPE_SPELL_PET,
00563                           op->msg, op->msg);
00564         op->owner->contr->ranges[range_golem] = NULL;
00565         op->owner->contr->golem_count = 0;
00566         remove_friendly_object(op);
00567         remove_ob(op);
00568         free_object(op);
00569         return;
00570     }
00571 
00572     /* Do golem attacks/movement for single & multisq golems.
00573      * Assuming here that op is the 'head' object. Pass only op to
00574      * move_ob (makes recursive calls to other parts)
00575      * move_ob returns 0 if the creature was not able to move.
00576      */
00577     tag = op->count;
00578     if (move_ob(op, op->direction, op))
00579         return;
00580     if (was_destroyed(op, tag))
00581         return;
00582 
00583     for (tmp = op; tmp; tmp = tmp->more) {
00584         sint16 x = tmp->x+freearr_x[op->direction], y = tmp->y+freearr_y[op->direction];
00585         object *victim;
00586         mapstruct *m;
00587         int mflags;
00588 
00589         m = op->map;
00590         mflags = get_map_flags(m, &m, x, y, &x, &y);
00591 
00592         if (mflags&P_OUT_OF_MAP)
00593             continue;
00594 
00595         for (victim = GET_MAP_OB(op->map, x, y); victim; victim = victim->above)
00596             if (QUERY_FLAG(victim, FLAG_ALIVE))
00597                 break;
00598 
00599         /* We used to call will_hit_self to make sure we don't
00600          * hit ourselves, but that didn't work, and I don't really
00601          * know if that was more efficient anyways than this.
00602          * This at least works.  Note that victim->head can be NULL,
00603          * but since we are not trying to dereferance that pointer,
00604          * that isn't a problem.
00605          */
00606         if (victim && victim != op && victim->head != op) {
00607             /* for golems with race fields, we don't attack
00608              * aligned races
00609              */
00610 
00611             if (victim->race && op->race && strstr(op->race, victim->race)) {
00612                 if (op->owner)
00613                     draw_ext_info_format(NDI_UNIQUE, 0, op->owner,
00614                                          MSG_TYPE_SPELL, MSG_TYPE_SPELL_PET,
00615                                          "%s avoids damaging %s.",
00616                                          "%s avoids damaging %s.",
00617                                          op->name, victim->name);
00618             } else if (victim == op->owner) {
00619                 if (op->owner)
00620                     draw_ext_info_format(NDI_UNIQUE, 0, op->owner,
00621                                          MSG_TYPE_SPELL, MSG_TYPE_SPELL_PET,
00622                                          "%s avoids damaging you.",
00623                                          "%s avoids damaging you.",
00624                                          op->name);
00625             } else {
00626                 attack_ob(victim, op);
00627                 made_attack = 1;
00628             }
00629         } /* If victim */
00630     }
00631     if (made_attack)
00632         update_object(op, UP_OBJ_FACE);
00633 }
00634 
00648 void control_golem(object *op, int dir) {
00649     op->direction = dir;
00650 }
00651 
00669 int summon_golem(object *op, object *caster, int dir, object *spob) {
00670     object *tmp;
00671     const object *god = NULL;
00672     archetype *at;
00673     char buf[MAX_BUF];
00674 
00675     /* Because there can be different golem spells, player may want to
00676      * 'lose' their old golem.
00677      */
00678     if (op->type == PLAYER
00679     && op->contr->ranges[range_golem] != NULL
00680     && op->contr->golem_count == op->contr->ranges[range_golem]->count) {
00681         draw_ext_info(NDI_UNIQUE, 0, op,
00682                       MSG_TYPE_SPELL, MSG_TYPE_SPELL_PET,
00683                       "You dismiss your existing golem.", NULL);
00684         remove_ob(op->contr->ranges[range_golem]);
00685         free_object(op->contr->ranges[range_golem]);
00686         op->contr->ranges[range_golem] = NULL;
00687         op->contr->golem_count = -1;
00688     }
00689 
00690     if (spob->other_arch)
00691         at = spob->other_arch;
00692     else if (spob->race) {
00693         god = find_god(determine_god(caster));
00694         if (!god) {
00695             draw_ext_info_format(NDI_UNIQUE, 0, op,
00696                                  MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
00697                                  "You must worship a god to cast %s.",
00698                                  "You must worship a god to cast %s.",
00699                                  spob->name);
00700             return 0;
00701         }
00702 
00703         at = determine_holy_arch(god, spob->race);
00704         if (!at) {
00705             draw_ext_info_format(NDI_UNIQUE, 0, op,
00706                                  MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
00707                                  "%s has no %s for you to call.",
00708                                  "%s has no %s for you to call.",
00709                                  god->name, spob->race);
00710             return 0;
00711         }
00712     } else {
00713         LOG(llevError, "Spell %s lacks other_arch\n", spob->name);
00714         return 0;
00715     }
00716 
00717     if (!dir)
00718         dir = find_free_spot(NULL, op->map, op->x, op->y, 1, SIZEOFFREE1+1);
00719 
00720     if ((dir == -1)
00721     || ob_blocked(&at->clone, op->map, op->x+freearr_x[dir], op->y+freearr_y[dir])) {
00722         draw_ext_info(NDI_UNIQUE, 0, op,
00723                       MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
00724                       "There is something in the way.", NULL);
00725         return 0;
00726     }
00727     /* basically want to get proper map/coordinates for this object */
00728 
00729     if (!(tmp = fix_summon_pet(at, op, dir, GOLEM))) {
00730         draw_ext_info(NDI_UNIQUE, 0, op,
00731                       MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
00732                       "Your spell fails.", NULL);
00733         return 0;
00734     }
00735 
00736     if (op->type == PLAYER) {
00737         tmp->type = GOLEM;
00738         set_owner(tmp, op);
00739         set_spell_skill(op, caster, spob, tmp);
00740         op->contr->ranges[range_golem] = tmp;
00741         op->contr->golem_count = tmp->count;
00742         /* give the player control of the golem */
00743         op->contr->shoottype = range_golem;
00744     } else {
00745         if (QUERY_FLAG(op, FLAG_FRIENDLY)) {
00746             object *owner = get_owner(op);
00747 
00748             if (owner != NULL) { /* For now, we transfer ownership */
00749                 set_owner(tmp, owner);
00750                 tmp->attack_movement = PETMOVE;
00751                 add_friendly_object(tmp);
00752                 SET_FLAG(tmp, FLAG_FRIENDLY);
00753             }
00754         }
00755         SET_FLAG(tmp, FLAG_MONSTER);
00756     }
00757 
00758     /* make the speed positive.*/
00759     tmp->speed = FABS(tmp->speed);
00760 
00761     /*  This sets the level dependencies on dam and hp for monsters */
00762     /* players can't cope with too strong summonings. */
00763     /* but monsters can.  reserve these for players. */
00764     if (op->type == PLAYER) {
00765         tmp->stats.hp += spob->duration+SP_level_duration_adjust(caster, spob);
00766         if (!spob->stats.dam)
00767             tmp->stats.dam += SP_level_dam_adjust(caster, spob);
00768         else
00769             tmp->stats.dam = spob->stats.dam+SP_level_dam_adjust(caster, spob);
00770         tmp->speed += .02*SP_level_range_adjust(caster, spob);
00771         tmp->speed = MIN(tmp->speed, 1.0);
00772         if (spob->attacktype)
00773             tmp->attacktype = spob->attacktype;
00774     }
00775     tmp->stats.wc -= SP_level_range_adjust(caster, spob);
00776 
00777     /* limit the speed to 0.3 for non-players, 1 for players. */
00778 
00779     /* make experience increase in proportion to the strength.
00780      * this is a bit simplistic - we are basically just looking at how
00781      * often the sp doubles and use that as the ratio.
00782      */
00783     tmp->stats.exp *= 1+(MAX(spob->stats.maxgrace, spob->stats.sp)/caster_level(caster, spob));
00784     tmp->speed_left = 0;
00785     tmp->direction = dir;
00786 
00787     /* Holy spell - some additional tailoring */
00788     if (god) {
00789         object *tmp2;
00790 
00791         snprintf(buf, sizeof(buf), "%s of %s", spob->name, god->name);
00792         buf[0] = toupper(buf[0]);
00793         for (tmp2 = tmp; tmp2; tmp2 = tmp2->more) {
00794             if (tmp2->name)
00795                 free_string(tmp2->name);
00796             tmp2->name = add_string(buf);
00797         }
00798         tmp->attacktype |= god->attacktype;
00799         memcpy(tmp->resist, god->resist, sizeof(tmp->resist));
00800         if (tmp->race)
00801             FREE_AND_CLEAR_STR(tmp->race);
00802         if (god->race)
00803             tmp->race = add_string(god->race);
00804         if (tmp->slaying)
00805             FREE_AND_CLEAR_STR(tmp->slaying);
00806         if (god->slaying)
00807             tmp->slaying = add_string(god->slaying);
00808         /* safety, we must allow a god's servants some reasonable attack */
00809         if (!(tmp->attacktype&AT_PHYSICAL))
00810             tmp->attacktype |= AT_PHYSICAL;
00811     }
00812 
00813     insert_ob_in_map(tmp, tmp->map, op, 0);
00814     return 1;
00815 }
00816 
00817 /***************************************************************************
00818  *
00819  * Summon monster/pet/other object code
00820  *
00821  ***************************************************************************/
00822 
00837 static object *choose_cult_monster(object *pl, const object *god, int summon_level) {
00838     char buf[MAX_BUF];
00839     const char *race;
00840     int racenr, mon_nr, i;
00841     racelink *list;
00842     objectlink *tobl;
00843     object *otmp;
00844 
00845     /* Determine the number of races available */
00846     racenr = 0;
00847     strcpy(buf, god->race);
00848     race = strtok(buf, ",");
00849     while (race) {
00850         racenr++;
00851         race = strtok(NULL, ",");
00852     }
00853 
00854     /* next, randomly select a race from the aligned_races string */
00855     if (racenr > 1) {
00856         racenr = rndm(0, racenr-1);
00857         strcpy(buf, god->race);
00858         race = strtok(buf, ",");
00859         for (i = 0; i < racenr; i++)
00860             race = strtok(NULL, ",");
00861     } else
00862         race = god->race;
00863 
00864 
00865     /* see if a we can match a race list of monsters.  This should not
00866      * happen, so graceful recovery isn't really needed, but this sanity
00867      * checking is good for cases where the god archetypes mismatch the
00868      * race file
00869      */
00870     if ((list = find_racelink(race)) == NULL) {
00871         draw_ext_info_format(NDI_UNIQUE, 0, pl,
00872                              MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
00873                              "The spell fails! %s's creatures are beyond the range of your summons",
00874                              "The spell fails! %s's creatures are beyond the range of your summons",
00875                              god->name);
00876         LOG(llevDebug, "choose_cult_monster() requested non-existent aligned race!\n");
00877         return NULL;
00878     }
00879 
00880     /* search for an apprplritate monster on this race list */
00881     mon_nr = 0;
00882     for (tobl = list->member; tobl; tobl = tobl->next) {
00883         otmp = tobl->ob;
00884         if (!otmp || !QUERY_FLAG(otmp, FLAG_MONSTER))
00885             continue;
00886         if (otmp->level <= summon_level)
00887             mon_nr++;
00888     }
00889 
00890     /* If this god has multiple race entries, we should really choose another.
00891      * But then we either need to track which ones we have tried, or just
00892      * make so many calls to this function, and if we get so many without
00893      * a valid entry, assuming nothing is available and quit.
00894      */
00895     if (!mon_nr)
00896         return NULL;
00897 
00898     mon_nr = rndm(0, mon_nr-1);
00899     for (tobl = list->member; tobl; tobl = tobl->next) {
00900         otmp = tobl->ob;
00901         if (!otmp || !QUERY_FLAG(otmp, FLAG_MONSTER))
00902             continue;
00903         if (otmp->level <= summon_level && !mon_nr--)
00904             return otmp;
00905     }
00906     /* This should not happen */
00907     LOG(llevDebug, "choose_cult_monster() mon_nr was set, but did not find a monster\n");
00908     return NULL;
00909 }
00910 
00929 int summon_object(object *op, object *caster, object *spell_ob, int dir, const char *stringarg) {
00930     sint16 x, y, nrof = 1, i;
00931     archetype *summon_arch;
00932     int ndir, mult;
00933 
00934     if (spell_ob->other_arch) {
00935         summon_arch = spell_ob->other_arch;
00936     } else if (spell_ob->randomitems) {
00937         int level = caster_level(caster, spell_ob);
00938         treasure *tr, *lasttr = NULL;
00939 
00940         /* In old code, this was a very convuluted for statement,
00941          * with all the checks in the 'for' portion itself.  Much
00942          * more readable to break some of the conditions out.
00943          */
00944         for (tr = spell_ob->randomitems->items; tr; tr = tr->next) {
00945             if (level < tr->magic)
00946                 break;
00947             lasttr = tr;
00948             if (stringarg && !strcmp(tr->item->name, stringarg))
00949                 break;
00950             if (tr->next == NULL || tr->next->item == NULL)
00951                 break;
00952         }
00953         if (!lasttr) {
00954             LOG(llevError, "Treasurelist %s did not generate a valid entry in summon_object\n", spell_ob->randomitems->name);
00955             draw_ext_info(NDI_UNIQUE, 0, op,
00956                           MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
00957                           "The spell fails to summon any monsters.", NULL);
00958             return 0;
00959         }
00960         summon_arch = lasttr->item;
00961         nrof = lasttr->nrof;
00962 
00963     } else if (spell_ob->race && !strcmp(spell_ob->race, "GODCULTMON")) {
00964         const object *god = find_god(determine_god(op));
00965         object *mon, *owner;
00966         int summon_level, tries;
00967 
00968         if (!god && ((owner = get_owner(op)) != NULL)) {
00969             god = find_god(determine_god(owner));
00970         }
00971         /* If we can't find a god, can't get what monster to summon */
00972         if (!god)
00973             return 0;
00974 
00975         if (!god->race) {
00976             draw_ext_info_format(NDI_UNIQUE, 0, op,
00977                                  MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
00978                                  "%s has no creatures that you may summon!",
00979                                  "%s has no creatures that you may summon!",
00980                                  god->name);
00981             return 0;
00982         }
00983         /* the summon level */
00984         summon_level = caster_level(caster, spell_ob);
00985         if (summon_level == 0)
00986             summon_level = 1;
00987         tries = 0;
00988         do {
00989             mon = choose_cult_monster(op, god, summon_level);
00990             if (!mon) {
00991                 draw_ext_info_format(NDI_UNIQUE, 0, op,
00992                                      MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
00993                                      "%s fails to send anything.",
00994                                      "%s fails to send anything.",
00995                                      god->name);
00996                 return 0;
00997             }
00998             ndir = dir;
00999             if (!ndir)
01000                 ndir = find_free_spot(mon, op->map, op->x, op->y, 1, SIZEOFFREE);
01001             if (ndir == -1
01002             || ob_blocked(mon, op->map, op->x+freearr_x[ndir], op->y+freearr_y[ndir])) {
01003                 ndir = -1;
01004                 if (++tries == 5) {
01005                     draw_ext_info(NDI_UNIQUE, 0, op,
01006                                   MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01007                                   "There is something in the way.", NULL);
01008                     return 0;
01009                 }
01010             }
01011         } while (ndir == -1);
01012         if (mon->level > (summon_level/2))
01013             nrof = random_roll(1, 2, op, PREFER_HIGH);
01014         else
01015             nrof = die_roll(2, 2, op, PREFER_HIGH);
01016         summon_arch = mon->arch;
01017     } else {
01018         summon_arch = NULL;
01019     }
01020 
01021     if (spell_ob->stats.dam)
01022         nrof += spell_ob->stats.dam+SP_level_dam_adjust(caster, spell_ob);
01023 
01024     if (!summon_arch) {
01025         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01026                       "There is no monsters available for summoning.", NULL);
01027         return 0;
01028     }
01029 
01030     if (dir) {
01031         /* Only fail if caster specified a blocked direction. */
01032         x = freearr_x[dir];
01033         y = freearr_y[dir];
01034         if (ob_blocked(&summon_arch->clone, op->map, op->x+x, op->y+y)) {
01035             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01036                           "There is something in the way.", NULL);
01037             return 0;
01038         }
01039     }
01040 
01041     mult = (RANDOM()%2 ? -1 : 1);
01042 
01043     for (i = 1; i <= nrof; i++) {
01044         archetype *atmp;
01045         object *prev = NULL, *head = NULL, *tmp;
01046 
01047         if (dir) {
01048             ndir = absdir(dir+(i/2)*mult);
01049             mult = -mult;
01050         } else
01051             ndir = find_free_spot(&summon_arch->clone, op->map, op->x, op->y, 1, SIZEOFFREE);
01052 
01053         if (ndir > 0) {
01054             x = freearr_x[ndir];
01055             y = freearr_y[ndir];
01056         }
01057 
01058         if (ndir == -1 || ob_blocked(&summon_arch->clone, op->map, op->x+x, op->y+y))
01059             continue;
01060 
01061         for (atmp = summon_arch; atmp != NULL; atmp = atmp->more) {
01062             tmp = arch_to_object(atmp);
01063             if (atmp == summon_arch) {
01064                 if (QUERY_FLAG(tmp, FLAG_MONSTER)) {
01065                     set_owner(tmp, op);
01066                     set_spell_skill(op, caster, spell_ob, tmp);
01067                     tmp->enemy = op->enemy;
01068                     tmp->type = 0;
01069                     CLEAR_FLAG(tmp, FLAG_SLEEP);
01070                     if (op->type == PLAYER || QUERY_FLAG(op, FLAG_FRIENDLY)) {
01071                         /* If this is not set, we make it friendly */
01072                         if (!QUERY_FLAG(spell_ob, FLAG_MONSTER)) {
01073                             SET_FLAG(tmp, FLAG_FRIENDLY);
01074                             add_friendly_object(tmp);
01075                             tmp->stats.exp = 0;
01076                             if (spell_ob->attack_movement)
01077                                 tmp->attack_movement = spell_ob->attack_movement;
01078                             if (get_owner(op))
01079                                 set_owner(tmp, get_owner(op));
01080                         }
01081                     }
01082                 }
01083                 if (tmp->speed > MIN_ACTIVE_SPEED)
01084                     tmp->speed_left = -1;
01085             }
01086             if (head == NULL)
01087                 head = tmp;
01088             else {
01089                 tmp->head = head;
01090                 prev->more = tmp;
01091             }
01092             prev = tmp;
01093             tmp->x = op->x+x+tmp->arch->clone.x;
01094             tmp->y = op->y+y+tmp->arch->clone.y;
01095             tmp->map = get_map_from_coord(op->map, &tmp->x, &tmp->y);
01096         }
01097         head->direction = freedir[ndir];
01098         head->stats.exp = 0;
01099         head = insert_ob_in_map(head, head->map, op, 0);
01100         if (head && head->randomitems) {
01101             create_treasure(head->randomitems, head, GT_APPLY|GT_STARTEQUIP, 6, 0);
01102         }
01103         if (head != NULL) {
01104             mark_inventory_as_no_drop(head);
01105         }
01106     } /* for i < nrof */
01107     return 1;
01108 }
01109 
01119 static object *get_real_owner(object *ob) {
01120     object *realowner = ob;
01121 
01122     if (realowner == NULL)
01123         return NULL;
01124 
01125     while (get_owner(realowner) != NULL) {
01126         realowner = get_owner(realowner);
01127     }
01128     return realowner;
01129 }
01130 
01146 int should_arena_attack(object *pet, object *owner, object *target) {
01147     object *rowner, *towner;
01148 
01149     /* exit if the target, pet, or owner is null. */
01150     if ((target == NULL) || (pet == NULL) || (owner == NULL))
01151         return 0;
01152 
01153     /* get the owners of itself and the target, this is to deal with pets of
01154     pets */
01155     rowner = get_real_owner(owner);
01156     if (target->type != PLAYER) {
01157         towner = get_real_owner(target);
01158     } else {
01159         towner = NULL;
01160     }
01161 
01162     /* if the pet has no owner, exit with error */
01163     if (rowner == NULL) {
01164         LOG(llevError, "Pet has no owner.\n");
01165         return 0;
01166     }
01167 
01168     /* if the target is not a player, and has no owner, we shouldn't be here
01169     */
01170     if (towner == NULL && target->type != PLAYER) {
01171         LOG(llevError, "Target is not a player but has no owner. We should not be here.\n");
01172         return 0;
01173     }
01174 
01175     /* make sure that the owner is a player */
01176     if (rowner->type != PLAYER)
01177         return 0;
01178 
01179     /* abort if the petmode is not arena */
01180     if (rowner->contr->petmode != pet_arena)
01181         return 0;
01182 
01183     /* abort if the pet, it's owner, or the target is not on battleground*/
01184     if (!(op_on_battleground(pet, NULL, NULL, NULL)
01185         && op_on_battleground(owner, NULL, NULL, NULL)
01186         && op_on_battleground(target, NULL, NULL, NULL)))
01187         return 0;
01188 
01189     /* if the target is a monster, make sure it's owner is not the same */
01190     if (target->type != PLAYER && rowner == towner)
01191         return 0;
01192 
01193     /* check if the target is a player which affects how it will handle
01194     parties */
01195     if (target->type != PLAYER) {
01196         /* if the target is owned by a player make sure than make sure
01197         it's not in the same party */
01198         if (towner->type == PLAYER && rowner->contr->party != NULL) {
01199             if (rowner->contr->party == towner->contr->party)
01200                 return 0;
01201         }
01202     } else {
01203         /* if the target is a player make sure than make sure it's not
01204         in the same party */
01205         if (rowner->contr->party != NULL) {
01206             if (rowner->contr->party == target->contr->party)
01207                 return 0;
01208         }
01209     }
01210 
01211     return 1;
01212 }