/* Copyright (C)1991, Marcus J. Ranum. All rights reserved. */ #ifndef lint static char RCSid[] = "$Header: /home/mjr/hacks/umud/RCS/combat.c,v 1.10 92/05/17 23:32:37 mjr Exp $"; #endif #include "config.h" #ifdef NOSYSTYPES_H #include <types.h> #else #include <sys/types.h> #endif #include "mud.h" #include "vars.h" #include "sbuf.h" #include "match.h" #include "combat.h" #ifndef COMBAT edit combat.o out of the makefile, you klunch. #endif static struct smap { char *lnam; char *mval; char *cval; } map [] = { "strength", var_Strength, var_strength, "endurance", var_Endurance, var_endurance, "willpower", var_Willpower, var_willpower, "agility", var_Agility, var_agility, "magic", var_Magic, var_magic, "action", var_Action, var_action, 0, 0, 0 }; /* map stat name to attribute name */ static char * stat2att(nam,m) char *nam; int m; { int len; struct smap *mp; len = strlen(nam); for(mp = map; mp->lnam != (char *)0; mp++) if(!strncmp(nam,mp->lnam,len)) return(m ? mp->mval : mp->cval); return((char *)0); } /* map attribute name to stat name */ static char * att2stat(nam,m) char *nam; int m; { struct smap *mp; for(mp = map; mp->lnam != (char *)0; mp++) { if(m && !strcmp(nam,mp->mval)) return(mp->lnam); if(!m && !strcmp(nam,mp->cval)) return(mp->lnam); } return((char *)0); } /* this is a function just to ease porting if needed */ static void update_time(u,tim) char *u; time_t *tim; { ut_setnum(u,u,var_lastupd,*tim); } static time_t get_time(u) char *u; { char *lupt; lupt = ut_getatt(u,0,typ_int,var_lastupd,(char *)0); if(lupt == (char *)0) return((time_t)-1); return((time_t)atol(lupt)); } static void regenerate(u,elapsed,nowval,maxval,nowatt) char *u; time_t elapsed; int nowval; int maxval; char *nowatt; { int i; int d; int mc = maxval * 100; int nc = nowval * 100; /* basically what we do here is a quick form of compounding interest, with values shifted to make them slip into a pleasant bracket with respect to rounding errors in integer division. */ for(i = elapsed / TIMEUNIT; i > 0 ; i--) { d = ((nc * PERCENT) / 100) > 1 ? ((nc * PERCENT) / 100) : 10; if(nc >= mc - d) { nc = mc; break; } nc += d; } nowval = (nc / 100); (void)ut_setnum(u,u,nowatt,nowval); } static void update_stats(u) char *u; { time_t now; time_t then; time_t delta; int pow; int nowstr; int maxstr; int nowend; int maxend; int nowwil; int maxwil; int nowagi; int maxagi; int nowmag; int maxmag; int nowact; int maxact; (void)time(&now); if((then = get_time(u)) == (time_t)-1) update_time(u,&now); /* noncombatant or recently updated */ if(ut_getnum(u,var_power,&pow) < 0 || now - then < UPDATE_QUANT) return; delta = now - then; /* if these values aren't set for some reason, they default zero */ (void)ut_getnum(u,var_strength,&nowstr); (void)ut_getnum(u,var_Strength,&maxstr); (void)ut_getnum(u,var_endurance,&nowend); (void)ut_getnum(u,var_Endurance,&maxend); (void)ut_getnum(u,var_willpower,&nowwil); (void)ut_getnum(u,var_Willpower,&maxwil); (void)ut_getnum(u,var_agility,&nowagi); (void)ut_getnum(u,var_Agility,&maxagi); (void)ut_getnum(u,var_magic,&nowmag); (void)ut_getnum(u,var_Magic,&maxmag); (void)ut_getnum(u,var_action,&nowact); (void)ut_getnum(u,var_Action,&maxact); regenerate(u,delta,nowstr,maxstr,var_strength); regenerate(u,delta,nowend,maxend,var_endurance); regenerate(u,delta,nowwil,maxwil,var_willpower); regenerate(u,delta,nowagi,maxagi,var_agility); regenerate(u,delta,nowmag,maxmag,var_magic); /* give more action points - by scaling delta */ regenerate(u,delta * ACTIONSCALE ,nowact,maxact,var_action); update_time(u,&now); /* if the guy's no longer incapacitated, unmark him */ if(nowstr && nowend && nowwil && nowagi && nowmag) ut_unset(u,u,var_isdead); } cmd__combat(ac,av,who,aswho) int ac; char *av[]; char *who; char *aswho; { if(ac < 2) { say(who,"please provide an option (try ",av[0]," help).\n",(char *)0); return(UERR_ARGCNT); } /* update a player's stats */ if(!strncmp(av[1],"update",strlen(av[1]))) { update_stats(who); return(UERR_NONE); } /* register a player as a combatant - loads of checks... */ if(!strcmp(av[1],"register")) { int pow; if(ut_getnum(who,var_power,&pow) == 0) { say(who,"You are already registered as a combatant.\n",(char *)0); return(UERR_PERM); } if(strcmp(who,aswho)) { say(who,"You cannot register another object.\n",(char *)0); return(UERR_PERM); } if(!ut_flagged(who,var_isplay)) { say(who,"Only player objects may fight.\n",(char *)0); return(UERR_PERM); } if(ut_setnum(who,aswho,var_power,INITIAL_POWER)) { say(who,"Cannot register combatant status.\n",(char *)0); return(UERR_FATAL); } update_stats(who); say(who,"Registered. You may now allocate power.\n",(char *)0); return(UERR_NONE); } /* allocate power points. */ if(!strcmp(av[1],"allocate")) { int pow; int pts; int aval; char *aptr; if(ac != 4) { say(who,"usage: allocate attribute points\n",(char *)0); return(UERR_BADPARM); } if(strcmp(who,aswho)) { say(who,"You cannot allocate for another object.\n",(char *)0); return(UERR_PERM); } if(ut_getnum(who,var_power,&pow) < 0) { say(who,"You must first register as a combatant.\n",(char *)0); return(UERR_PERM); } if((pts = atoi(av[3])) <= 0 || pts > INITIAL_POWER) { say(who,"Cannot allocate ",av[3]," points.\n",(char *)0); return(UERR_BADPARM); } if(pts > pow) { say(who,"You don't have enough points.\n",(char *)0); return(UERR_BADPARM); } if((aptr = stat2att(av[2],1)) == (char *)0) { say(who,"Unknown combat attribute: ",av[2],".\n",(char *)0); return(UERR_BADPARM); } /* is gut. */ if(ut_getnum(who,aptr,&aval) < 0) aval = 0; aval += pts; if(ut_setnum(who,aswho,aptr,aval)) { say(who,"Cannot set attribute (internal error)\n",(char *)0); return(UERR_FATAL); } pow -= pts; /* f***it - if this doesn't work they get free points */ (void)ut_setnum(who,aswho,var_power,pow); say(who,"Allocated points to ",aptr,".\n",(char *)0); /* give the guy some points */ if((aptr = stat2att(av[2],0)) == (char *)0) return(UERR_NONE); if(ut_getnum(who,aptr,&pow) < 0) pow = 0; (void)ut_setnum(who,aswho,aptr,(aval + pow) / 2); return(UERR_NONE); } /* stats */ if(!strcmp(av[1],"stats")) { int nowstr; int maxstr; int nowend; int maxend; int nowwil; int maxwil; int nowagi; int maxagi; int nowmag; int maxmag; int nowact; int maxact; char xbuf[MAXOID]; update_stats(who); (void)ut_getnum(who,var_strength,&nowstr); (void)ut_getnum(who,var_Strength,&maxstr); (void)ut_getnum(who,var_endurance,&nowend); (void)ut_getnum(who,var_Endurance,&maxend); (void)ut_getnum(who,var_willpower,&nowwil); (void)ut_getnum(who,var_Willpower,&maxwil); (void)ut_getnum(who,var_agility,&nowagi); (void)ut_getnum(who,var_Agility,&maxagi); (void)ut_getnum(who,var_magic,&nowmag); (void)ut_getnum(who,var_Magic,&maxmag); (void)ut_getnum(who,var_action,&nowact); (void)ut_getnum(who,var_Action,&maxact); say(who,"Str:",itoa(nowstr,xbuf),(char *)0); say(who,"/",itoa(maxstr,xbuf)," ",(char *)0); say(who,"End:",itoa(nowend,xbuf),(char *)0); say(who,"/",itoa(maxend,xbuf)," ",(char *)0); say(who,"Will:",itoa(nowwil,xbuf),(char *)0); say(who,"/",itoa(maxwil,xbuf)," ",(char *)0); say(who,"Agil:",itoa(nowagi,xbuf),(char *)0); say(who,"/",itoa(maxagi,xbuf)," ",(char *)0); say(who,"Magic:",itoa(nowmag,xbuf),(char *)0); say(who,"/",itoa(maxmag,xbuf)," ",(char *)0); say(who,"Action Points:",itoa(nowact,xbuf),(char *)0); say(who,"/",itoa(maxact,xbuf),"\n",(char *)0); return(UERR_NONE); } /* help */ if(!strcmp(av[1],"help")) { say(who,av[0]," update\n",(char *)0); say(who,av[0]," register\n",(char *)0); say(who,av[0]," stats\n",(char *)0); say(who,av[0]," allocate combat-attribute #points\n",(char *)0); return(UERR_NONE); } say(who,av[0]," Unknown option (try \"help\")\n",(char *)0); return(UERR_BADPARM); } static int do_attack(att,def,attrib,arisk,abet,dhas,ahas,wmod,amod,smes,fmes) char *att; char *def; char *attrib; int arisk; int abet; int dhas; int ahas; int wmod; int amod; char *smes; char *fmes; { int p; float risk; int rdam; char xuf[64]; risk = arisk + wmod; p = (int)((risk * risk * 1000.0)/ ((risk * risk * 10.0)+((float)abet * (float)abet * 14.0))); #ifdef COMBAT_DEBUG printf("prob of %s hitting %s %s: %d%\n",att,def,attrib,p); #endif /* a HIT ! */ if(p > get_random(100)) { if(abet <= 0) { ut_roombcast(ut_loc(att),(char *)0,ut_name(att), " launches a futile attack at ",ut_name(def),"\n",(char *)0); return(0); } rdam = abet - amod; if(rdam <= 0) { ut_roombcast(ut_loc(att),(char *)0,ut_name(att), "'s attack glances off ",ut_name(def),"'s armor!\n",(char *)0); return(0); } if(smes != (char *)0) ut_roombcast(ut_loc(att),(char *)0,ut_name(att), " ",smes,"\n",(char *)0); /* a kill */ if(dhas - rdam <= 0) { ut_setnum(def,def,attrib,0); ut_roombcast(ut_loc(att),(char *)0,ut_name(att), " killed ",ut_name(def),"!!\n",(char *)0); ut_home_player(def,def,ut_loc(att)); ut_set(def,def,typ_flag,var_isdead,""); return(1); } say(att,"You hit ",ut_name(def)," for ",itoa(rdam,xuf), " point", rdam > 1 ? "s" : "", " of ", att2stat(attrib,0),"!\n",(char *)0); say(def,ut_name(att)," hit you, for ",xuf," point", rdam > 1 ? "s" : "" ," of ", att2stat(attrib,0),"!\n",(char *)0); ut_setnum(def,def,attrib,dhas - rdam); return(0); } /* failure message */ if(fmes != (char *)0) ut_roombcast(ut_loc(att),(char *)0,ut_name(att)," ", fmes,"\n",(char *)0); /* did the attacker wipe himself? */ if(ahas - arisk <= 0) { ut_setnum(att,att,attrib,0); ut_roombcast(ut_loc(att),(char *)0,ut_name(def), " killed ",ut_name(att),"!!\n",(char *)0); ut_home_player(att,att,ut_loc(def)); ut_set(att,def,typ_flag,var_isdead,""); return(1); } /* hurt him */ ut_setnum(att,att,attrib,ahas - arisk); say(att,"You missed ",ut_name(def)," and lose ",itoa(arisk,xuf), " point", arisk > 1 ? "s" : "", " of ", att2stat(attrib,0),"!\n",(char *)0); say(def,ut_name(att)," missed you, and lost ",xuf," point", arisk > 1 ? "s" : "" ," of ", att2stat(attrib,0),"!\n",(char *)0); return(0); } /* generate weapon modifier */ static int modify_weapon(who,att) char *who; char *att; { char *weap; char *ap; int iac; if((weap = ut_getatt(who,0,typ_obj,var_weapon,(char *)0)) == (char *)0) return(0); if((ap = ut_getatt(weap,0,typ_int,att,(char *)0)) == (char *)0) return(0); #ifdef COMBAT_DEBUG printf("%s weapon modifier: %d%\n",who,atoi(ap)); #endif return(atoi(ap)); } /* generate armor modifier */ static int modify_armor(who,att) char *who; char *att; { char *lp; char arm[MAXOID]; char *ap; int ret = 0; if((lp = ut_getatt(who,0,typ_list,var_wearing,(char *)0)) == (char *)0) return(0); while((lp = lstnext(lp,arm,sizeof(arm))) != (char *)0) { if(!ut_flagged(arm,var_isarmor)) continue; if((ap = ut_getatt(arm,0,typ_int,att,(char *)0)) != (char *)0) ret += atoi(ap); } #ifdef COMBAT_DEBUG printf("%s total armor modifiers: %d%\n",who,ret); #endif return(ret); } /* basic combat driver */ cmd__attack(ac,av,who,aswho) int ac; char *av[]; char *who; char *aswho; { char vict[MAXOID]; char *aptr; int abet; int arisk; char *smes = (char *)0; char *fmes = (char *)0; int aval; int dval; int wmod = 0; int amod = 0; char *cp; int c; char cbuf[512]; char *cav[12]; Sbuf suf; if(matchplayers(who,av[1],ut_loc(who),MTCH_UNIQ|MTCH_MEOK,vict)) return(UERR_NOMATCH); if(!strcmp(who,vict)) { say(who,"Don't kill yourself over a game.\n",(char *)0); return(UERR_PERM); } if(strcmp(who,aswho)) { char *weap; weap = ut_getatt(who,0,typ_obj,var_weapon,(char *)0); if(weap == (char *)0 || strcmp(aswho,weap)) { say(who,"Only players and weapons can attack.\n",(char *)0); return(UERR_PERM); } } if(ut_getnum(who,var_power,&aval) < 0) { say(who,"Sorry. You're a noncombatant.\n",(char *)0); return(UERR_PERM); } if(ut_getnum(vict,var_power,&aval) < 0) { say(who,"Sorry. ",ut_name(vict)," is a noncombatant.\n",(char *)0); return(UERR_PERM); } if((aptr = stat2att(av[2],0)) == (char *)0) { say(who,"Unknown combat attribute: ",av[2],".\n",(char *)0); return(UERR_BADPARM); } if((abet = atoi(av[3])) <= 0) { say(who,"Cannot attack ",av[3]," points.\n",(char *)0); return(UERR_BADPARM); } if((arisk = atoi(av[4])) <= 0) { say(who,"Cannot gamble ",av[3]," points in attack.\n",(char *)0); return(UERR_BADPARM); } /* messages? */ if(ac > 5) smes = av[5]; if(ac > 6) fmes = av[6]; /* see if the guy has the cojones to attack with */ update_stats(who); if(ut_flagged(who,var_isdead)) { say(who,"You're too wounded to fight!\n",(char *)0); return(UERR_PERM); } /* now see if they have the wherewithal to attack */ if(ut_getnum(who,aptr,&aval) < 0 || aval == 0) { say(who,"You have no points with which to attack.\n",(char *)0); return(UERR_BADPARM); } /* now charge the guy action points */ if(ut_getnum(who,var_action,&dval) < 0 || dval < ATTACK_COST) { say(who,"You have insufficient action points.\n",(char *)0); return(UERR_BADPARM); } dval -= ATTACK_COST; ut_setnum(who,who,var_action,dval); /* get victim's attribute values */ update_stats(vict); if(ut_getnum(vict,aptr,&dval) < 0) dval = 0; /* add weapons modifiers for attacker */ wmod = modify_weapon(who,aptr); /* add armor modifiers for defender */ amod = modify_armor(vict,aptr); /* adjust values as best we can */ if(arisk > aval) arisk = aval - 1; if(arisk < 0) arisk = 0; if(abet > dval + amod) abet = (dval + amod) > 0 ? (dval + amod) : 1; /* set last attacker */ (void)ut_set(who,vict,typ_obj,var_lastatt,who); /* if someone wins in initial attack, stop the action. */ if(do_attack(who,vict,aptr,arisk,abet,dval,aval,wmod,amod,smes,fmes)) { /* set last killer */ (void)ut_set(who,vict,typ_obj,var_lastatt,who); return(UERR_NONE); } if(ut_flagged(vict,var_isdead)) { say(vict,"You're too wounded to counterattack!\n",(char *)0); return(UERR_PERM); } /* initial attack wasn't a knockout, so the victim gets to hit back */ cp = ut_getatt(vict,0,typ_list,var_counters,(char *)0); if(cp == (char *)0 || (c = lstcnt(cp)) == 0) return(UERR_NONE); /* pick one at random from list of counterattacks */ c = get_random(c); sbuf_initstatic(&suf); cp = lstnextsbuf(cp,&suf); while(c > 0) { cp = lstnextsbuf(cp,&suf); c--; } /* tokenize it */ c = enargv(sbuf_buf(&suf),cav,12,cbuf,sizeof(cbuf),(char **)0,who,aswho,0,(char **)0); sbuf_freestatic(&suf); if(c < 3) { say(vict,"Counterattack is missing parameters!\n",(char *)0); return(UERR_NONE); } /* messages */ if(c > 3) smes = cav[3]; if(c > 4) fmes = cav[4]; /* lookup aptr */ if((aptr = stat2att(cav[0],0)) == (char *)0) { say(vict,"Counterattack attribute ",cav[0]," is unknown!\n",(char *)0); return(UERR_NONE); } if((abet = atoi(cav[1])) <= 0) { say(vict,"Cannot counterattack ",cav[1]," points.\n",(char *)0); return(UERR_BADPARM); } if((arisk = atoi(cav[2])) <= 0) { say(vict,"Cannot gamble ",cav[2]," points in counterattack.\n",(char *)0); return(UERR_BADPARM); } if(ut_getnum(vict,aptr,&aval) < 0 || aval == 0) { say(vict,"You have no points with which to counterattack.\n",(char *)0); return(UERR_BADPARM); } /* now charge the guy action points */ if(ut_getnum(vict,var_action,&dval) < 0 || dval < ATTACK_COST) { say(vict,"You have no action points to counterattack.\n",(char *)0); return(UERR_BADPARM); } dval -= ATTACK_COST; ut_setnum(vict,vict,var_action,dval); if(ut_getnum(who,aptr,&dval) < 0) dval = 0; wmod = modify_weapon(vict,aptr); amod = modify_armor(who,aptr); if(arisk > aval) arisk = aval; if(abet > dval + amod) abet = dval + amod; (void)ut_set(vict,who,typ_obj,var_lastatt,vict); if(do_attack(vict,who,aptr,arisk,abet,dval,aval,wmod,amod,smes,fmes)) { (void)ut_set(vict,who,typ_obj,var_lastatt,vict); return(UERR_NONE); } return(UERR_NONE); } static int stop_wielding(who,ud) char *who; char *ud; { if(ut_listadd(who,who,var_cont,ud)) return(UERR_FATAL); if(ut_unset(who,who,var_weapon)) return(UERR_FATAL); say(who,"You are now empty-handed.\n",(char *)0); return(UERR_NONE); } /* ARGSUSED */ cmd_wield(ac,av,who,aswho) int ac; char *av[]; char *who; char *aswho; { char *ud; ud = ut_getatt(who,0,typ_obj,var_weapon,(char *)0); if(ac <= 1) { if(ud == (char *)0) { say(who,"You are empty-handed.\n",(char *)0); return(UERR_NONE); } return(stop_wielding(who,ud)); } else if(ac == 2) { char ob[MAXOID]; int xx; if(ud != (char *)0 && (xx = stop_wielding(who,ud)) != UERR_NONE) return(xx); if(matchinv(who,av[1],0,MTCH_UNIQ|MTCH_QUIET,ob)) return(UERR_NOMATCH); if(!ut_flagged(ob,var_isweapon)) { say(who,ut_name(ob)," is not a weapon.\n",(char *)0); return(UERR_PERM); } if(ut_listdel(who,who,var_cont,ob)) return(UERR_FATAL); if(ut_set(who,who,typ_obj,var_weapon,ob)) return(UERR_FATAL); say(who,"You are now wielding ",ut_name(ob),".\n",(char *)0); return(UERR_NONE); } say(who,"You can only use one weapon at a time.\n",(char *)0); return(UERR_NONE); }