Difference for server/pets.c from version 1.17 to 1.18


version 1.17 version 1.18
Line 1
 
Line 1
 /*  /*
  * static char *rcsid_pets_c =   * static char *rcsid_pets_c =
  *    "$Id: pets.c,v 1.17 2003/01/08 08:39:20 mwedel Exp $";   *    "$Id: pets.c,v 1.18 2003/09/13 05:02:11 mwedel Exp $";
  */   */
   
 /*  /*
Line 378
 
Line 378
     }      }
     return;      return;
 }  }
   
   /****************************************************************************
    *
    * GOLEM SPELL CODE
    *
    ****************************************************************************/
   
   /* fix_summon_pet() - this makes multisquare/single square monsters
    * proper for map insertion.
    * at is the archetype, op is the caster of the spell, dir is the
    * direction the monster should be placed in.
    * is_golem is to note that this is a golem spell.
    */
   object *fix_summon_pet(archetype *at, object *op, int dir, int is_golem ) {
       archetype *atmp;
       object *tmp=NULL, *prev=NULL, *head=NULL;
   
       for(atmp = at; atmp!=NULL; atmp = atmp->more) {
    tmp = arch_to_object(atmp);
    if (atmp == at) {
        if(!is_golem)
    SET_FLAG(tmp, FLAG_MONSTER);
        set_owner(tmp, op);
        if (op->type == PLAYER) {
    tmp->stats.exp = 0;
    add_friendly_object(tmp);
    SET_FLAG(tmp, FLAG_FRIENDLY);
    if(is_golem) CLEAR_FLAG(tmp, FLAG_MONSTER);
        } else {
    if(QUERY_FLAG(op, FLAG_FRIENDLY)) {
        object *owner = get_owner(op);
        if(owner != NULL) {/* For now, we transfer ownership */
    set_owner(tmp,owner);
    tmp->move_type = PETMOVE;
    add_friendly_object(tmp);
    SET_FLAG(tmp, FLAG_FRIENDLY);
        }
    }
        }
        if(op->type!=PLAYER || !is_golem) {
    tmp->move_type = PETMOVE;
    tmp->speed_left = -1;
    tmp->type = 0;
    tmp->enemy = op->enemy;
        } else
    tmp->type = GOLEM;
         
    }
    if(head == NULL)
        head = tmp;
    tmp->x = op->x + freearr_x[dir] + tmp->arch->clone.x;
    tmp->y = op->y + freearr_y[dir] + tmp->arch->clone.y;
    tmp->map = op->map;
    if(tmp->invisible) tmp->invisible=0;
    if(head != tmp)
        tmp->head = head, prev->more = tmp;
    prev = tmp;
       }
       head->direction = dir;
   
       /* need to change some monster attr to prevent problems/crashing */
       if(head->last_heal) head->last_heal=0;
       if(head->last_eat) head->last_eat=0;
       if(head->last_grace) head->last_grace=0;
       if(head->last_sp) head->last_sp=0;
       if(head->attacktype&AT_GHOSTHIT) head->attacktype=(AT_PHYSICAL|AT_DRAIN);
       if(head->other_arch) head->other_arch=NULL;
       if(QUERY_FLAG(head,FLAG_CHANGING)) CLEAR_FLAG(head,FLAG_CHANGING);
       if(QUERY_FLAG(head,FLAG_STAND_STILL)) CLEAR_FLAG(head,FLAG_STAND_STILL);
       if(QUERY_FLAG(head,FLAG_GENERATOR)) CLEAR_FLAG(head,FLAG_GENERATOR);
       if(QUERY_FLAG(head,FLAG_SPLITTING)) CLEAR_FLAG(head,FLAG_SPLITTING);
   
       return head;
   }
   
   /* updated this to allow more than the golem 'head' to attack */
   /* op is the golem to be moved. */
   
   void move_golem(object *op) {
       int made_attack=0;
       object *tmp;
       tag_t tag;
   
       if(QUERY_FLAG(op, FLAG_MONSTER))
    return; /* Has already been moved */
   
       if(get_owner(op)==NULL) {
    LOG(llevDebug,"Golem without owner destructed.\n");
    remove_ob(op);
    free_object(op);
    return;
       }
       /* It would be nice to have a cleaner way of what message to print
        * when the golem expires than these hard coded entries.
        * Note it is intentional that a golems duration is based on its
        * hp, and not duration
        */
       if(--op->stats.hp<0) {
    if (op->msg)
        new_draw_info(NDI_UNIQUE, 0,op->owner,op->msg);
    op->owner->contr->ranges[range_golem]=NULL;
    op->owner->contr->golem_count = 0;
    remove_friendly_object(op);
    remove_ob(op);
    free_object(op);
    return;
       }
   
       /* Do golem attacks/movement for single & multisq golems.
        * Assuming here that op is the 'head' object. Pass only op to
        * move_ob (makes recursive calls to other parts)
        * move_ob returns 0 if the creature was not able to move.
        */
       tag = op->count;
       if(move_ob(op,op->direction,op)) return;
       if (was_destroyed (op, tag))
           return;
   
       for(tmp=op;tmp;tmp=tmp->more) {
    sint16 x=tmp->x+freearr_x[op->direction],y=tmp->y+freearr_y[op->direction];
    object *victim;
    mapstruct *m;
    int mflags;
   
    m = op->map;
    mflags = get_map_flags(m, &m, x, y, &x, &y);
   
    if (mflags & P_OUT_OF_MAP) continue;
   
    for(victim=get_map_ob(op->map,x,y);victim;victim=victim->above)
        if(QUERY_FLAG(victim,FLAG_ALIVE)) break;
   
    /* We used to call will_hit_self to make sure we don't
    * hit ourselves, but that didn't work, and I don't really
    * know if that was more efficient anyways than this.
    * This at least works.  Note that victim->head can be NULL,
    * but since we are not trying to dereferance that pointer,
    * that isn't a problem.
    */
    if(victim && victim!=op && victim->head!=op) {
   
        /* for golems with race fields, we don't attack
         * aligned races
         */
   
        if(victim->race && op->race && strstr(op->race,victim->race)) {
    if(op->owner) new_draw_info_format(NDI_UNIQUE, 0,op->owner,
    "%s avoids damaging %s.",op->name,victim->name);
        } else if (victim == op->owner) {
    if(op->owner) new_draw_info_format(NDI_UNIQUE, 0,op->owner,
    "%s avoids damaging you.",op->name);
        } else {
    attack_ob(victim,op);
    made_attack=1;
        }
    } /* If victim */
       }
       if(made_attack) update_object(op,UP_OBJ_FACE);
   }
   
   /* this is a really stupid function when you get down and
    * look at it.  Keep it here for the time being - makes life
    * easier if we ever decide to do more interesting thing with
    * controlled golems.
    */
   void control_golem(object *op,int dir) {
       op->direction=dir;
   }
   
   /* summon golem: summons a monster for 'op'.  caster is the object
    * casting the spell, dir is the direction to place the monster,
    * at is the archetype of the monster, and spob is the spell
    * object.  At this stage, all spob is really used for is to
    * adjust some values in the monster.
    */
   int summon_golem(object *op,object *caster,int dir,object *spob) {
       object *tmp, *god=NULL;
       archetype *at;
       char buf[MAX_BUF];
   
       /* Because there can be different golem spells, player may want to
        * 'lose' their old golem.
        */
       if(op->type==PLAYER &&
          op->contr->ranges[range_golem]!=NULL &&
          op->contr->golem_count == op->contr->ranges[range_golem]->count) {
        new_draw_info(NDI_UNIQUE, 0, op, "You dismiss your existing golem.");
        remove_ob(op->contr->ranges[range_golem]);
        free_object(op->contr->ranges[range_golem]);
        op->contr->ranges[range_golem]=NULL;
        op->contr->golem_count=-1;
       }
   
       if (spob->other_arch)
    at = spob->other_arch;
       else if (spob->race) {
    god = find_god(determine_god(caster));
   
    if (!god) {
        new_draw_info_format(NDI_UNIQUE, 0,op,"You must worship a god to cast %s.", spob->name);
        return 0;
    }
    at = determine_holy_arch (god, spob->race);
    if (!at) {
        new_draw_info_format(NDI_UNIQUE, 0,op,"%s has no %s for you to call.",
    god->name,spob->race);
        return 0;
    }
       } else {
    LOG(llevError,"Spell %s lacks other_arch\n", spob->name);
    return 0;
       }
   
       if(!dir)
    dir=find_free_spot(NULL,op->map,op->x,op->y,1,9);
   
       if ((dir==-1) || arch_blocked(at, op->map, op->x + freearr_x[dir], op->y + freearr_y[dir])) {
    new_draw_info(NDI_UNIQUE, 0,op,"There is something in the way.");
    return 0;
       }
       /* basically want to get proper map/coordinates for this object */
   
       if(!(tmp=fix_summon_pet(at,op,dir,GOLEM))) {
    new_draw_info(NDI_UNIQUE, 0,op,"Your spell fails.");
    return 0;
       }
   
       if(op->type==PLAYER) {
    tmp->type=GOLEM;
    set_owner(tmp,op);
    set_spell_skill(op, caster, spob, tmp);
    op->contr->ranges[range_golem]=tmp;
    op->contr->golem_count = tmp->count;
    /* give the player control of the golem */
    op->contr->shoottype=range_golem;
       } else {
    if(QUERY_FLAG(op, FLAG_FRIENDLY)) {
        object *owner = get_owner(op);
        if (owner != NULL) { /* For now, we transfer ownership */
    set_owner (tmp, owner);
    tmp->move_type = PETMOVE;
    add_friendly_object (tmp);
    SET_FLAG (tmp, FLAG_FRIENDLY);
        }
    }
    SET_FLAG(tmp, FLAG_MONSTER);
       }
   
       /* make the speed positive.*/
       tmp->speed = FABS(tmp->speed);
   
       /*  This sets the level dependencies on dam and hp for monsters */
       /* players can't cope with too strong summonings. */
       /* but monsters can.  reserve these for players. */
       if(op->type==PLAYER) {
    tmp->stats.hp += spob->duration +
                        SP_level_duration_adjust(caster,spob);
    if (!spob->stats.dam)
        tmp->stats.dam += SP_level_dam_adjust(caster,spob);
    else
        tmp->stats.dam= spob->stats.dam +
                       SP_level_dam_adjust(caster,spob);
    tmp->speed += .02 * SP_level_range_adjust(caster,spob);
    tmp->speed = MIN(tmp->speed, 1.0);
    if (spob->attacktype) tmp->attacktype = spob->attacktype;
       }
       tmp->stats.wc -= SP_level_range_adjust(caster,spob);
   
       /* limit the speed to 0.3 for non-players, 1 for players. */
   
       /* make experience increase in proportion to the strength.
        * this is a bit simplistic - we are basically just looking at how
        * often the sp doubles and use that as the ratio.
        */
       tmp->stats.exp *= 1 + (MAX(spob->stats.maxgrace, spob->stats.sp) /
       caster_level(caster, spob));
       tmp->speed_left= 0;
       tmp->direction=dir;
   
       /* Holy spell - some additional tailoring */
       if (god) {
    object *tmp2;
   
    sprintf(buf,"%s of %s",spob->name,god->name);
    buf[0] = toupper(buf[0]);
    for (tmp2=tmp; tmp2; tmp2=tmp2->more) {
        if (tmp2->name) free_string(tmp2->name);
        tmp2->name = add_string(buf);
    }
    tmp->attacktype |= god->attacktype;
    memcpy(tmp->resist, god->resist, sizeof(tmp->resist));
    if (tmp->race) FREE_AND_CLEAR_STR(tmp->race);
    if (god->race) tmp->race = add_string(god->race);
    if (tmp->slaying) FREE_AND_CLEAR_STR(tmp->slaying);
    if (god->slaying) tmp->slaying = add_string(god->slaying);
    /* safety, we must allow a god's servants some reasonable attack */
    if(!(tmp->attacktype&AT_PHYSICAL)) tmp->attacktype|=AT_PHYSICAL;
       }
   
       insert_ob_in_map(tmp,tmp->map,op,0);
       return 1;
   }
   
   
   /***************************************************************************
    *
    * Summon monster/pet/other object code
    *
    ***************************************************************************/
   
   /* Returns a monster (chosen at random) that this particular player (and his
    * god) find acceptable.  This checks level, races allowed by god, etc
    * to determine what is acceptable.
    * This returns NULL if no match was found.
    */
   
   object *choose_cult_monster(object *pl, object *god, int summon_level) {
       char buf[MAX_BUF],*race;
       int racenr, mon_nr,i;
       racelink *list;
       objectlink *tobl;
       object *otmp;
   
       /* Determine the number of races available */
       racenr=0;
       strcpy(buf,god->race);
       race = strtok(buf,",");
       while(race) {
    racenr++;
    race = strtok(NULL,","); 
        }
   
       /* next, randomly select a race from the aligned_races string */
       if(racenr>1) {
    racenr = rndm(0, racenr-1);
    strcpy(buf,god->race);
           race = strtok(buf,",");
           for(i=0;i<racenr;i++)
         race = strtok(NULL,",");
       } else
           race = god->race;
   
   
       /* see if a we can match a race list of monsters.  This should not
        * happen, so graceful recovery isn't really needed, but this sanity
        * checking is good for cases where the god archetypes mismatch the
        * race file
        */
       if((list=find_racelink(race))==NULL) {
    new_draw_info_format(NDI_UNIQUE, 0,pl,
        "The spell fails! %s's creatures are beyond the range of your summons",
         god->name);
    LOG(llevDebug,"choose_cult_monster() requested non-existant aligned race!\n");
    return 0;
       }
   
       /* search for an apprplritate monster on this race list */
       mon_nr=0;
       for(tobl=list->member;tobl;tobl=tobl->next) {
    otmp=tobl->ob;
    if(!otmp||!QUERY_FLAG(otmp,FLAG_MONSTER)) continue;
    if(otmp->level<=summon_level) mon_nr++;
       }
   
       /* If this god has multiple race entries, we should really choose another.
        * But then we either need to track which ones we have tried, or just
        * make so many calls to this function, and if we get so many without
        * a valid entry, assuming nothing is available and quit.
        */
       if (!mon_nr) return NULL;
   
       mon_nr = rndm(0, mon_nr-1);
       for(tobl=list->member;tobl;tobl=tobl->next) {
    otmp=tobl->ob;
    if(!otmp||!QUERY_FLAG(otmp,FLAG_MONSTER)) continue;
    if(otmp->level<=summon_level && !mon_nr--) return otmp;
       }
       /* This should not happen */
       LOG(llevDebug,"choose_cult_monster() mon_nr was set, but did not find a monster\n");
       return NULL;
   }
   
   
   
   int summon_object(object *op, object *caster, object *spell_ob, int dir)
   {
       sint16 x,y, nrof=1, i;
       archetype *summon_arch;
   
       if (spell_ob->other_arch) {
    summon_arch = spell_ob->other_arch;
       } else if (spell_ob->randomitems) {
    int level = caster_level(caster, spell_ob);
    treasure *tr, *lasttr=NULL;;
   
    /* In old code, this was a very convuluted for statement,   
    * with all the checks in the 'for' portion itself.  Much
    * more readable to break some of the conditions out.
    */
    for (tr=spell_ob->randomitems->items; tr; tr=tr->next) {
        if (level < tr->magic) break;
        if (tr->next == NULL || tr->next->item == NULL) break;
        lasttr = tr;
    }
    if (!lasttr) {
        LOG(llevError,"Treasurelist %s did not generate a valid entry in summon_object\n",
    spell_ob->randomitems->name);
        new_draw_info(NDI_UNIQUE, 0, op, "The spell fails to summon any monsters.");
        return 0;
    }
    summon_arch = lasttr->item;
    nrof = lasttr->nrof;
   
       } else if (spell_ob->race && !strcmp(spell_ob->race,"GODCULTMON")) {
    object *god=find_god(determine_god(op)), *mon;
    int summon_level, tries, ndir;
   
    if (!god->race) {
        new_draw_info_format(NDI_UNIQUE, 0,op,
    "%s has no creatures that you may summon!",god->name);
        return 0;
    }
    /* the summon level */
    summon_level=caster_level(caster, spell_ob);
    if (summon_level==0) summon_level=1;
    tries = 0;
    do {
        mon = choose_cult_monster(op, god,summon_level);
        if (!mon) {
    new_draw_info_format(NDI_UNIQUE, 0,op,
         "%s fails to send anything.",god->name);
    return 0;
        }
        ndir = dir;
        if (!ndir)
    ndir = find_free_spot(mon->arch, op->map, op->x, op->y, 1, SIZEOFFREE);
        if (ndir == -1 || arch_blocked(mon->arch,op->map, op->x + freearr_x[ndir], op->y+freearr_y[ndir])) {
    ndir=-1;
    if (++tries == 5) {
        new_draw_info(NDI_UNIQUE, 0,op, "There is something in the way.");
        return 0;
    }
        }
    } while (ndir == -1);
    if (mon->level > (summon_level/2))
        nrof = random_roll(1, 2, op, PREFER_HIGH);
    else
    nrof =  die_roll(2, 2, op, PREFER_HIGH);
    summon_arch = mon->arch;
       }
   
       if (spell_ob->stats.dam)
    nrof += spell_ob->stats.dam + SP_level_dam_adjust(caster, spell_ob);
   
       if (!summon_arch) {
    new_draw_info(NDI_UNIQUE, 0, op, "There is no monsters available for summoning.");
    return 0;
       }
   
       if (!dir)
    dir = find_free_spot(summon_arch, op->map, op->x, op->y, 1, SIZEOFFREE);
   
       if (dir > 0) {
    x = op->x + freearr_x[dir];
    y = op->y + freearr_y[dir];
       }
   
       if (dir == -1 || arch_blocked(summon_arch, op->map, x, y)){
    new_draw_info(NDI_UNIQUE, 0, op, "There is something in the way.");
    return 0;
       }
   
       for (i=1; i <= nrof; i++) {
    archetype *atmp;
    object *prev=NULL, *head=NULL, *tmp;
   
    for (atmp = summon_arch; atmp!=NULL; atmp=atmp->more) {
        tmp = arch_to_object(atmp);
        if (atmp == summon_arch) {
    if (QUERY_FLAG(tmp, FLAG_MONSTER)) {
        set_owner(tmp, op);
        set_spell_skill(op, caster, spell_ob, tmp);
        tmp->enemy = op->enemy;
        tmp->type = 0;
        CLEAR_FLAG(tmp, FLAG_SLEEP);
        if (op->type == PLAYER || QUERY_FLAG(op, FLAG_FRIENDLY)) {
    /* If this is not set, we make it friendly */
    if (!QUERY_FLAG(spell_ob, FLAG_MONSTER)) {
        SET_FLAG(tmp, FLAG_FRIENDLY);
        add_friendly_object(tmp);
        tmp->stats.exp = 0;
        if (spell_ob->move_type)
    tmp->move_type = spell_ob->move_type;
        if (get_owner(op))
    set_owner(tmp, get_owner(op));
    }
        }
    }
    if (tmp->speed > MIN_ACTIVE_SPEED) tmp->speed_left = -1;
        }
        if (head == NULL) head = tmp;
        else {
    tmp->head = head;
    prev->more = tmp;
        }
        prev = tmp;
        tmp->x = op->x + freearr_x[dir] + tmp->arch->clone.x;
        tmp->y = op->y + freearr_y[dir] + tmp->arch->clone.y;
        tmp->map = op->map;
    }
    head->direction = dir;
    head = insert_ob_in_map(head, head->map, op, 0);
    if (head && head->randomitems) {
        object *tmp;
        create_treasure(head->randomitems, head, GT_APPLY, 6, 0);
        for (tmp=head->inv; tmp; tmp=tmp->below)
    if (!tmp->nrof) SET_FLAG(tmp, FLAG_NO_DROP);
    }
    dir = absdir(dir + 1);
    if (arch_blocked(summon_arch, op->map, op->x + freearr_x[dir], op->y + freearr_y[dir])) {
        if (i < nrof) {
    new_draw_info(NDI_UNIQUE, 0,op, "There is something in the way,");
    new_draw_info(NDI_UNIQUE, 0,op, "No more pets for this casting.");
    return 1;
        }
    }
       } /* for i < nrof */
       return 1;
   }
       
   


Legend:
line(s) removed in v.1.17 
line(s) changed
 line(s) added in v.1.18

File made using version 1.98 of cvs2html by leaf at 2011-07-21 17:39