#include #include #include "obj.h" #include "vars.h" #include "dice.h" #include "util.h" #include "fight.h" #include "pline.h" #include "fuses.h" #include "display.h" #include "structs.h" #include "montypes.h" #include "mon-@-you.h" #include "mon.h" static MONST *seestairmon = 0; static void mon_settick(MONST *); /* forward */ static void mon_tick(long int arg_li __attribute__((__unused__)), void *mvp) { MONST *m; m = mvp; if (m->flags & MF_DEAD) panic("ticking dead monster"); m->age ++; (*m->ops->tick)(m); mon_settick(m); } static void mon_settick(MONST *m) { m->fuseid = addfuse_rel(&mon_tick,0,m,m->speed); } MONST *newmon(int type, LEVEL *dlevel) { MONST *m; m = malloc(sizeof(MONST)); m->type = &montypes[type]; m->symbol = '\\'; m->hp = 0; m->maxhp = 0; m->heal = 0; m->flags = 0; m->speed = 1 * TIMESCALE; m->age = 0; m->created = curtime; m->fuseid = 0; m->you.x = 0; m->you.y = 0; m->private = 0; m->link = 0; m->ops = 0; inv_init(&m->invent,IT_MON,m); m->lastloc = 0; m->loc = 0; m = (*montypes[type].info->new)(m,dlevel); if (m) mon_settick(m); return(m); } int placemon(MONST *m, LOC *lc) { if (lc->monst) { pline("placemon to occupied location?"); impossible(); (*m->ops->destroy)(m); return(1); } else { m->loc = lc; lc->monst = m; lc->on->nmonst ++; (*m->ops->bemoved)(m); upddisp1(lc); m->link = mchain; mchain = m; mchain_count ++; return(0); } } static void makemon(void) { int probs[N_LEVELS]; int probv[MON__N]; int tprob; int i; LEVEL *lv; MONST *m; int dlevel; int mlevel; tprob = 0; for (i=0;incave / 10) - lv->nmonst; if (probs[i] < 0) probs[i] = 0; tprob += probs[i]; } i = you->loc->on->index + LEVEL_OFFSET; tprob -= probs[i]; probs[i] *= 10; tprob += probs[i]; if (tprob < 1) { pline("makemon: level total prob (%d) < 1",tprob); return; } tprob = rnd(tprob); for (i=0;i= N_LEVELS) { panic("can't find level for makemon (tprob = %d)",tprob); } dlevel = i; if (Z_mad) { mlevel = DUNGEON_LEVELS - 1; } else { mlevel = i; } lv = &Levels[dlevel]; tprob = 0; for (i=0;iprobs[mlevel]; tprob += probv[i]; } if (tprob < 1) { return; } tprob = rnd(tprob); for (i=0;i= MON__N) { panic("can't find monster in makemon (tprob = %d)",tprob); } m = newmon(i,lv); if (placemon(m,outofsight(lv))) { pline("makemon: can't place monster"); return; } } void makemon_fuse(long int arg_li, void *arg_vp __attribute__((__unused__))) { if (! arg_li) addfuse_rel(&makemon_fuse,0,0,roll("2d10+15")*TIMESCALE); makemon(); } int seestairs(void) { LOC *lc; lc = you->loc->to; if ( !lc->monst || (lc->monst == seestairmon) ) { return(0); } pline("There's %s at the other end of the stairs!",amonname(lc->monst)); seestairmon = lc->monst; return(1); } char *amonname(MONST *m) { static char buf[64]; const char *cp; cp = m->type->name; sprintf(buf,"%s %s",vowel(cp[0])?"an":"a",cp); return(buf); } void std_kill(MONST *m) { cancelfuse(m->fuseid); mergeinv(&m->invent,&m->loc->objs); m->loc->monst = 0; m->loc->on->nmonst --; upddisp(m->loc); m->flags |= MF_DEAD; } void std_destroy(MONST *m) { free(m); } void killmon(MONST *m) { (*m->ops->kill)(m); } void movemon(MONST *m, LOC *to) { m->loc->monst = 0; m->loc->on->nmonst --; upddisp1(m->loc); to->monst = m; m->loc = to; to->on->nmonst ++; (*m->ops->bemoved)(m); upddisp1(to); } static int takestairs(MONST *m) { LOC *lc; LOC *lc2; lc = m->loc; if ( (lc->type != LOC_CAVE) || ( (lc->cavetype != LOC_STAIRS_U) && (lc->cavetype != LOC_STAIRS_D) ) ) { panic("non-stairs in takestairs?"); } lc2 = lc->to; if (lc2 == 0) { return(0); } if (lc2->monst == 0) { movemon(m,lc2); return(1); } if ((m == you) || (lc2->monst == you)) { if (seestairs()) { return(1); } mhitm(m,lc2->monst); return(1); } return(0); } static void monnextto(MONST *m, LOC *to) { int bestd; int bestn; int tx; int ty; int x; int y; int dx; int dy; LOC *gotlc; LEVEL *lv; LOC *lc; lv = to->on; tx = to->x; ty = to->y; bestd = (LEV_X+LEV_Y+1) * (LEV_X+LEV_Y+1); bestn = 0; gotlc = 0; for (x=0;x tx) ? x - tx : tx - x; if ((dx >= LEV_X/2) && (lv->flags & LVF_WRAPAROUND)) { dx = LEV_X - dx; } dx *= dx; lc = &lv->cells[x][0]; for (y=0;y ty) ? y - ty : ty - y; if ((dy >= LEV_Y/2) && (lv->flags & LVF_WRAPAROUND)) { dy = LEV_Y - dy; } if ( (lc->type == LOC_CAVE) && (lc->monst == 0) ) { int df; df = dx + (dy * dy); if ((df > 0) && (df < bestd)) { bestd = df; bestn = 1; } else if (df == bestd) { bestn ++; } if ((df == bestd) && onein(bestn)) { gotlc = lc; } } lc ++; } } if (bestn == 0) { panic("No free space on level for monnextto?"); } if (! gotlc) { panic("monnextto: have cell but can't find it?"); } movemon(m,gotlc); } void gatemon(MONST *m, LOC *to) { monnextto(m,to); } void aggravate(void) { MONST *m; Z_mad = ! Z_mad; pline("Aggravate %s",Z_mad?"on":"off"); for (m=mchain;m;m=m->link) { (*m->type->info->monctl)(m,MCTL_AGGR,1); } } static int followtrack(MONST *m, int tracktype) { LOC *b; LOC *lc; if ((tracktype < 0) || (tracktype >= NTRACKTYPES)) panic("followtrack: bad tracktype %d",tracktype); lc = m->loc; b = lc->tracks[tracktype]; if (b) { if (b->monst == you) { mhitm(m,you); } else if (b->monst) { if (b->on == lc->on) { int x1; int x2; int y1; int y2; int x; int y; LOC *lc2; int n; x = b->x; y = b->y; x1 = x + ((x > 0) ? -1 : 0); x2 = x + ((x >= LEV_X) ? 0 : 1); y1 = y + ((y > 0) ? -1 : 0); y2 = y + ((y >= LEV_Y) ? 0 : 1); n = 0; for (x=x1;x<=x2;x++) { for (y=y1;y<=y2;y++) { lc2 = &b->on->cells[x][y]; if ( !lc2->monst && lc2->tracks[tracktype] && ( (lc2->type == LOC_CAVE) || ( (lc2->type == LOC_DOOR) && (lc2->walldoor & LOC_DOOR_OPEN) ) ) && ( onein(5) || (lc2->tracks[tracktype] == b->tracks[tracktype]) ) ) { n ++; if (onein(n)) b = lc2; } } } } } if (! b->monst) { if (b->type == LOC_GATE) { gatemon(m,b->to); } else if ( (b->type == LOC_DOOR) && !(b->walldoor & LOC_DOOR_OPEN) ) { if (tracktype == TRACK_SMART) { opendoor(b); } } else { movemon(m,b); if ((b->flags & LF_VISIBLE) && (b->type == LOC_SDOOR)) { finddoor(b); opendoor(b); resee(); } } } return(1); } else { return(0); } } static int headfor(MONST *m, int tox, int toy, int smart) { LOC *ml; LOC *target; int i; int j; int k; LOC *lc; int d2[9]; int di[9]; LOC *l[9]; int n; int bestx; int mi; ml = m->loc; if ((tox == ml->x) && (toy == ml->y)) return(0); target = &ml->on->cells[tox][toy]; mi = distancei(ml,target); if (mi < 2) { if (target->monst == you) { mhitm(m,you); } else if (target->monst) { return(0); } else { movemon(m,target); } return(1); } n = 0; bestx = -1; for (i=1;i>=-1;i--) { for (j=1;j>=-1;j--) { lc = movecell(ml,i,j,0); if ( (lc->type == LOC_CAVE) || ( (lc->type == LOC_DOOR) && (lc->walldoor && LOC_DOOR_OPEN) ) ) { if (lc->monst) continue; l[n] = lc; d2[n] = distance2(lc,target); di[n] = distancei(lc,target); if ((bestx < 0) || (di[n] < di[bestx])) bestx = n; n ++; } } } if (bestx < 0) return(0); if (smart) { if (di[bestx] == 1) { if (onein(2)) return(1); j = 0; for (i=n-1;i>=0;i--) { if (di[i] == 1) { j ++; if (onein(j)) bestx = i; } } if (j < 1) abort(); movemon(m,l[bestx]); return(1); } bestx = -1; for (i=n-1;i>=0;i--) { if ((bestx < 0) || (d2[i] < d2[bestx])) bestx = i; } if (di[bestx] > mi) return(0); if (d2[bestx] == (di[bestx]*di[bestx])) { j = 0; for (i=n-1;i>=0;i--) { if ((di[i] == di[bestx]) && (d2[i] != d2[bestx])) { if ((j > 0) && (d2[i] < d2[k])) j = 0; j ++; if (onein(j)) k = i; } } if (j > 0) { movemon(m,l[k]); return(1); } } movemon(m,l[bestx]); return(1); } else { bestx = -1; for (i=n-1;i>=0;i--) { if (di[i] > mi) continue; if ((bestx < 0) || (d2[i] < d2[bestx])) bestx = i; } if (bestx < 0) return(0); movemon(m,l[bestx]); return(1); } #if 0 bestd = distance2(ml,target); if ((bestd < 4) && target->monst) { if (target->monst == you) { mhitm(m,you); } return(0); } bestl = ml; idx = rnd(8); for (i=0;i<8;i++,idx=(idx+1)&7) { lc = movecell(ml,delta[idx][0],delta[idx][1],0); if ( (lc->type == LOC_CAVE) || ( (lc->type == LOC_DOOR) && (lc->walldoor & LOC_DOOR_OPEN) ) ) { if (! lc->monst) { int df; df = distance2(lc,target); if ((df < 4) && smart) if (df < bestd) { bestd = df; bestl = lc; } } } } if (bestl != ml) { movemon(m,bestl); } return(1); #endif } void std_animal_tick(MONST *m) { LOC *lc; int x; int y; LOC *to; m->age ++; gainhp(m,m->heal); lc = m->loc; if (lc->flags & LF_VISIBLE) { m->flags |= MF_SAWYOU; m->you.x = you->loc->x; m->you.y = you->loc->y; } if (m->age > 1000) { m->flags |= MF_TRACKING; } if (m->flags & MF_SAWYOU) { if (headfor(m,m->you.x,m->you.y,0)) return; if ((lc->cavetype == LOC_STAIRS_U) || (lc->cavetype == LOC_STAIRS_D)) { takestairs(m); } m->flags &= ~MF_SAWYOU; } else if (m->flags & MF_TRACKING) { if (followtrack(m,TRACK_DUMB)) return; m->flags &= ~MF_TRACKING; } else { if (Z_mad) { m->flags |= MF_TRACKING; } if ( ( (lc->cavetype == LOC_STAIRS_U) || (lc->cavetype == LOC_STAIRS_D) ) && onein(5) ) { takestairs(m); return; } x = lc->x + rnd(3) - 1; y = lc->y + rnd(3) - 1; if ((x < 0) || (y < 0) || (x >= LEV_X) || (y >= LEV_Y)) { if (lc->on->flags & LVF_WRAPAROUND) { x = (x + LEV_X) % LEV_X; y = (y + LEV_Y) % LEV_Y; } else { panic("Outside level"); } } to = &lc->on->cells[x][y]; if ((to->type == LOC_CAVE) && !to->monst) { movemon(m,to); } } } void std_human_tick(MONST *m) { LOC *lc; int x; int y; LOC *to; m->age ++; gainhp(m,m->heal); lc = m->loc; if (lc->flags & LF_VISIBLE) { m->flags |= MF_SAWYOU; m->you.x = you->loc->x; m->you.y = you->loc->y; } if (m->age > 1000) { m->flags |= MF_TRACKING; } if (m->flags & MF_SAWYOU) { if (headfor(m,m->you.x,m->you.y,1)) return; if ((lc->cavetype == LOC_STAIRS_U) || (lc->cavetype == LOC_STAIRS_D)) { takestairs(m); } m->flags &= ~MF_SAWYOU; return; } if (lc->objs.inv && rnd(3)) { int t; INVOBJ *picked; int foo(INVOBJ *io) { t = io->dispn; if (rnd(t) < io->dispn) picked = io; return(0); } t = 0; inv_scan(&lc->objs,&foo); if (! picked) panic("can't find an object to pick up"); if (picked->dispn > 1) picked = inventory_split_n(picked,-1,-1); inv_move_1(&lc->objs,picked,&m->invent); } if (m->flags & MF_TRACKING) { if (followtrack(m,TRACK_SMART)) return; m->flags &= ~MF_TRACKING; } else { if (Z_mad) { m->flags |= MF_TRACKING; } if ( ( (lc->cavetype == LOC_STAIRS_U) || (lc->cavetype == LOC_STAIRS_D) ) && onein(5) ) { takestairs(m); return; } x = lc->x + rnd(3) - 1; y = lc->y + rnd(3) - 1; if ((x < 0) || (y < 0) || (x >= LEV_X) || (y >= LEV_Y)) { if (lc->on->flags & LVF_WRAPAROUND) { x = (x + LEV_X) % LEV_X; y = (y + LEV_Y) % LEV_Y; } else { panic("Outside level"); } } to = &lc->on->cells[x][y]; if ((to->type == LOC_CAVE) && !to->monst) { movemon(m,to); } } } int std_takedamage(MONST *m, int dmg) { m->hp -= dmg; if (m->hp <= 0) { killmon(m); return(1); } return(0); } static void tick_monsters(long int arg_li __attribute__((__unused__)), void *arg_vp __attribute__((__unused__))) { MONST **mp; MONST *m; int count; mp = &mchain; count = 0; while (1) { m = *mp; if (! m) break; if (m->flags & MF_DEAD) { *mp = m->link; (*m->ops->destroy)(m); mchain_count --; continue; } count ++; if (count > mchain_count) panic("too many monsters on chain"); mp = &m->link; } if (count != mchain_count) panic("too few monsters on chain"); addfuse_rel(&tick_monsters,0,0,10*TIMESCALE); } static void probinit_to_probs(MTINFO *mi) { char set[N_LEVELS]; int i; PROBINIT *pi; int j; for (i=N_LEVELS-1;i>=0;i--) set[i] = 0; for <"init"> (i=0;;i++) { pi = &mi->probinit[i]; switch (pi->kind) { default: panic("bad kind %d in probinit_to_prob",(int)pi->kind); break; case PIK_END: break <"init">; break; case PIK_LEV: if ( (pi->level < -LEVEL_OFFSET) || (pi->nlevel < 1) || (pi->level > DUNGEON_LEVELS) || (pi->nlevel > N_LEVELS) || (pi->level+pi->nlevel > DUNGEON_LEVELS) ) { panic("bad PIK_LEVELS (%d %d) in probinit_to_prob",pi->level,pi->nlevel); } for (j=pi->nlevel-1;j>=0;j--) { mi->probs[LEVEL_OFFSET+pi->level+j] = pi->prob; set[LEVEL_OFFSET+pi->level+j] = 1; } break; } } for (i=N_LEVELS-1;i>=0;i--) if (! set[i]) panic("no prob for %d in probinit_to_prob",i); } void initmonst(void) { int i; MTINFO *mi; int *pv; pv = malloc(MON__N*N_LEVELS*sizeof(int)); for (i=0;iprobs = pv; pv += N_LEVELS; probinit_to_probs(mi); } mchain = 0; mchain_count = 0; tick_monsters(0,0); } void gainhp(MONST *m, int hp) { m->hp += hp; if (m->hp > m->maxhp) m->hp = m->maxhp; }