623 lines · 10.5 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <thread.h>#include <draw.h>#include <bio.h>#include <ndb.h> #include "map.h"#include "bt.h" #include "dat.h" enum{	MONNAME		= 0,	MONTILE,	MONXPL,	MONALIGN,	MONMVRATE,	MONHP,	MONAC,	MONATK,	MONEQUIP,	MONEQPROB,}; static char *monstdbcmd[] = {[MONNAME]	"monster",[MONTILE]	"tile",[MONXPL]	"xpl",[MONALIGN]	"align",[MONMVRATE]	"mvr",[MONHP]		"hp",[MONAC]		"ac",[MONATK]	"atk",[MONEQUIP]	"equip",[MONEQPROB]	"eqprob",}; static Ndb *monstdb; intmonstdbopen(char *file){	monstdb = ndbopen(file);	if(monstdb == nil)		return 0;	return 1;} typedef struct mdcache mdcache;struct mdcache{	MonsterData *md;	mdcache *next;}; static mdcache *monstcache = nil; /* TODO: sanity checks */static intmdaddattr(MonsterData *md, int type, Ndbtuple *t){	Ndbtuple *line;	EquipData *equip; 	switch(type){	case MONNAME:		strncpy(md->name, t->val, sizeof(md->name)-1);		md->name[sizeof(md->name)-1] = 0;		break;	case MONTILE:		md->tile = atol(t->val);	case MONXPL:		md->basexpl = atol(t->val);		break;	case MONALIGN:		md->align = (char)atol(t->val);		break;	case MONMVRATE:		md->mvr = atol(t->val);		break;	case MONHP:		md->maxhp = atol(t->val);		break;	case MONAC:		md->def = atol(t->val);		break;	case MONATK:		if(!parseroll(t->val, &md->rolls, &md->atk)){			werrstr("bad roll '%s'", t->val);			return 0;		}		break;	case MONEQUIP:		if(md->nequip > sizeof(md->equip)-1)			break; 		equip = &md->equip[md->nequip++];		snprint(equip->name, SZNAME, "%s", t->val);		equip->prob = 1.0; 		for(line = t->line; line != t; line = line->line){			if(strcmp(line->attr, monstdbcmd[MONEQPROB]) == 0){				equip->prob = atof(line->val);				break;			}		}		break;	case MONEQPROB:		/* handled by MONEQUIP */		break;	default:		return 0;	} 	return 1;} MonsterData*mdbyname(char *monster){	int i;	Ndbtuple *t, *nt;	Ndbs search;	MonsterData *md;	mdcache *mdc; 	/* cached? */	for(mdc = monstcache; mdc != nil; mdc = mdc->next){		if(strcmp(monster, mdc->md->name) == 0){			return mdc->md;		}	} 	t = ndbsearch(monstdb, &search, "monster", monster);	if(t == nil)		return nil; 	md = mallocz(sizeof(MonsterData), 1);	assert(md != nil); 	for(nt = t; nt != nil; nt = nt->entry){		for(i = 0; i < nelem(monstdbcmd); i++){			if(strcmp(nt->attr, monstdbcmd[i]) == 0){				if(!mdaddattr(md, i, nt))					sysfatal("bad monster value '%s=%s': %r", nt->attr, nt->val);				goto okattr;			}		}		sysfatal("unknown monster attribute '%s'", nt->attr);okattr:		continue;	} 	ndbfree(t); 	mdc = mallocz(sizeof(mdcache), 1);	assert(mdc != nil); 	if(monstcache == nil){		monstcache = mdc;		monstcache->md = md;	} else {		mdc->md = md;		mdc->next = monstcache;		monstcache = mdc;	} 	return md;} Monster*mbyname(char *monster){	MonsterData *md;	Monster *m; 	md = mdbyname(monster);	if(md == nil)		goto missing; 	m = mallocz(sizeof(Monster), 1);	assert(m != nil); 	m->xpl = md->basexpl;	if(m->xpl == 0)		m->xp = 0;	else		m->xp = xpcalc(m->xpl-1);	m->align = md->align;	m->mvr = md->mvr;	m->mvp = nrand(m->mvr);	m->maxhp = md->maxhp;	m->maxhp += roll(m->xpl, 8);	m->hp = m->maxhp;	m->ac = md->def; 	m->md = md; 	incref(&m->ref); 	return m;missing:	werrstr("missing data for monster '%s'", monster);	return nil;} voidmfree(Monster *m){	int n; 	if(decref(&m->ref) != 0)		return; 	btstatefree(m->bs, m);	mapfree(m->bb); 	for(n = 0; n < NEQUIP; n++){		if(m->armor[n] != nil){			munwield(m, n);		}	} 	ilfree(&m->inv); 	memset(m, 0, sizeof(Monster));	free(m);} intmupdate(Monster *m){	m->mvp += m->mvr; 	while(m->mvp >= 12){		bttick(m->bt, m->bs, m); 		m->mvp -= 12;		m->turns++; 		/* gain some hp if you aren't dead. */		if((m->flags & Mdead) == 0 && m->hp < m->maxhp && m->turns % ((42/(m->xpl+2))+1) == 0){			m->hp+=1.0;			if(m->hp > m->maxhp)				m->hp = m->maxhp;		}	} 	return 0;} /* determines what happens when a monster gets some xp */static voidmaddxp(Monster *m, int xp){	int hpgain, last; 	dbg("%s get %ld xp total %ld next %ld", m->md->name, xp, m->xp, xpcalc(m->xpl)); 	last = m->xpl;	m->xp += xp;	while(m->xp >= xpcalc(m->xpl)){		m->xpl++;		hpgain = 1 + roll(1, 8);		m->maxhp += hpgain;		m->hp += hpgain*2;	}		if(m->xpl > last)		if(nearyou(m->pt))			good("the %s becomes level %d!", m->md->name, m->xpl);} static intmattack(Monster *m, Monster *mt){	int n, hit, ac, crit, dmg, xp;	Item *i, *weap;	Tile *t; 	hit = roll(1, 20);	ac = mt->ac >= 0 ? mt->ac : -(1+nrand(mt->ac)); 	if(hit > 10 + ac + m->xpl){		if(nearyou(m->pt))			warn("the %s misses the %s!", m->md->name, mt->md->name);		return 0;	} 	dmg = roll(m->md->rolls, m->md->atk); 	/* factor in weapon damage */	weap = m->armor[IWEAPON];	if(weap != nil)		dmg += roll(weap->id->rolls, weap->id->atk); 	if(dmg && mt->ac < 0){		dmg -= 1+nrand(-mt->ac);		if(dmg < 1)			dmg = 1;	} 	crit = roll(1, 5);	if(crit == 1){		dmg *= 2;		if(nearyou(m->pt))			warn("the %s critically strikes the %s for %d!", m->md->name, mt->md->name, dmg);	} else {		if(nearyou(m->pt))			msg("the %s hits the %s for %d.", m->md->name, mt->md->name, dmg);	} 	mt->hp -= dmg; 	if(mt->hp <= 0){		/* poor guy is dead. */		mt->hp = 0;		mt->flags = Mdead; 		/* take off all armor */		for(n = 0; n < NEQUIP; n++){			if(mt->armor[n] != nil){				munwield(mt, n);			}		} 		/* drop dead guy's stuff */		while(mt->inv.count > 0){			i = iltakenth(&mt->inv, 0);			t = tileat(mt->l, mt->pt);			setflagat(mt->l, mt->pt, Fhasitem);			iladd(&t->items, i);		} 		xp = 10 + pow(mt->xpl, 2.0);		if(nearyou(m->pt))			bad("the %s kills the %s, and gains %ld xp.", m->md->name, mt->md->name, xp); 		/* gain some xp */		maddxp(m, xp); 		m->kills++; 		/* gain 1/4 your target's max hp */		if(m->hp < m->maxhp){			m->hp += (double)mt->maxhp/4.0;			if(m->hp > m->maxhp)				m->hp = m->maxhp;		} 		return 1;	} 	return 0;} static char*portalname(int tile){	switch(tile){	case TSQUARE:		return "walkway";	case TUPSTAIR:		return "staircase going up";	case TDOWNSTAIR:		return "staircase going down";	case TPORTAL:		return "magic portal";	default:		sysfatal("no portal name for tile %d\n", tile);	} 	return nil;} /* -1 bad move, 0 didn't move, 1 moved */intmaction(Monster *m, int what, Point where){	int n;	Tile *cur, *targ;	Item *i; 	if(!ptinrect(where, m->l->r))		return -1; 	switch(what){	default:		fprint(2, "unknown move %d\n", what);	case MNONE:		/* do nothing */		return 0;		break;	case MMOVE:		targ = tileat(m->l, where);		if(hasflagat(m->l, where, Fhasmonster) && targ->monst != nil && (targ->monst->flags & Mdead) == 0){			/* monster; attack it */			if(mattack(m, targ->monst) > 0){				/* dead. */				clrflagat(m->l, where, Fhasmonster);				mfree(targ->monst);				targ->monst = nil;				targ->unit = 0;			}			return 0;		} else if(!hasflagat(m->l, where, Fblocked|Fhasmonster)){			/* move */			cur = tileat(m->l, m->pt);			setflagat(m->l, where, (Fhasmonster));			clrflagat(m->l, m->pt, (Fhasmonster));			targ->unit = cur->unit;			cur->unit = 0;			targ->monst = cur->monst;			cur->monst = nil; 			m->pt = where; 			if(isyou(m) && hasflagat(m->l, where, Fhasitem)){				n = 0;				while((i = ilnth(&targ->items, n++)) != nil)					warn("you see %i here.", i);			} 			if(isyou(m) && hasflagat(m->l, where, Fportal)){				warn("you see a %s to %s here.", portalname(targ->feat), targ->portal->to->name);			} 			return 1;		}		break;	case MUSE:		/* portal */		cur = tileat(m->l, m->pt);		what = cur->unit;		if(cur->portal != nil){			clrflagat(m->l, m->pt, (Fhasmonster));			m = cur->monst;			cur->monst = nil;			if(cur->portal->to == nil){				if((cur->portal->to = lgenerate(nrand(20)+50, nrand(20)+20, nrand(3)+1)) == nil)					sysfatal("genlevel: %r"); 				cur->portal->pt = cur->portal->to->up;				targ = tileat(cur->portal->to, cur->portal->pt);				if(targ->portal != nil){					targ->portal->to = m->l;					targ->portal->pt = m->pt;				}			} 			targ = tileat(cur->portal->to, cur->portal->pt);			targ->unit = what;			targ->monst = m;			m->l = cur->portal->to;			m->pt = cur->portal->pt;			setflagat(cur->portal->to, cur->portal->pt, Fhasmonster);			return 1;		}		break;	case MPICKUP:		if(!hasflagat(m->l, where, Fhasitem))			return -1;		targ = tileat(m->l, where);		i = iltakenth(&targ->items, 0);		if(i == nil)			return -1;		if(isyou(m))			good("you take the %i.", i);		maddinv(m, i);		if(targ->items.count == 0)			clrflagat(m->l, where, Fhasitem);		return 0;		break;	case MSPECIAL:		bad("the special move is broken. please fix me.");		break;	} 	/* bad move. */	return -1;} /* wield/wear n'th item in inventory. */intmwield(Monster *m, int n){	int type;	Item **ptr, *old, *new; 	old = nil;	new = ilnth(&m->inv, n);	if(new == nil)		return 0; 	type = new->id->type;	if(type < IWEAPON || type >= NEQUIP)		return 0; 

Lines 1–500 of 623.