/* * CREATURE.C: * * Routines that act on creatures. * * Copyright (C) 1991, 1992, 1993 Brett J. Vickers * */ #include "mstruct.h" #include "mextern.h" /**********************************************************************/ /* find_crt */ /**********************************************************************/ /* This function will attempt to locate a given creature within a given */ /* list. The first parameter contains a pointer to the creature doing */ /* the search, the second contains a tag pointer to the first tag in */ /* the list. The third contains the match string, and the fourth */ /* contains the number of times to match the string. */ creature *find_crt(ply_ptr, first_ct, str, val) creature *ply_ptr; ctag *first_ct; char *str; int val; { ctag *cp; int match=0, found=0; cp = first_ct; while(cp) { if(cp->crt->class >= CARETAKER && F_ISSET(cp->crt, PDMINV)) { cp = cp->next_tag; continue; } if(EQUAL(cp->crt, str) && (F_ISSET(ply_ptr, PDINVI) ? 1:!F_ISSET(cp->crt, MINVIS))) { match++; if(match == val) { found = 1; break; } } cp = cp->next_tag; } if(found) return(cp->crt); else return(0); } /**********************************************************************/ /* add_enm_crt */ /**********************************************************************/ /* This function adds a new enemy's name to the front a creature's enemy */ /* list. The closer to the front of the list, the higher the likelihood */ /* that you will be attacked. */ int add_enm_crt(enemy, crt_ptr) char *enemy; creature *crt_ptr; { etag *ep; int n; n = del_enm_crt(enemy, crt_ptr); ep = (etag *)malloc(sizeof(etag)); if(!ep) merror("add_enm_crt", FATAL); strcpy(ep->enemy, enemy); ep->next_tag = 0; ep->damage = (n > -1) ? n : 0; if(!crt_ptr->first_enm) { crt_ptr->first_enm = ep; return(n); } ep->next_tag = crt_ptr->first_enm; crt_ptr->first_enm = ep; if(n < 0) crt_ptr->NUMHITS = 0; return(n); } /**********************************************************************/ /* del_enm_crt */ /**********************************************************************/ /* This function removes an enemy's name from his enemy list. */ int del_enm_crt(enemy, crt_ptr) char *enemy; creature *crt_ptr; { etag *ep, *prev; int dmg; ep = crt_ptr->first_enm; if(!ep) return(-1); if(!strcmp(ep->enemy, enemy)) { crt_ptr->first_enm = ep->next_tag; dmg = ep->damage; free(ep); return(dmg); } while(ep) { if(!strcmp(ep->enemy, enemy)) { prev->next_tag = ep->next_tag; dmg = ep->damage; free(ep); return(dmg); } prev = ep; ep = ep->next_tag; } return(-1); } /**********************************************************************/ /* end_enm_crt */ /**********************************************************************/ /* This function moves an enemy within a monster's enemy list to the */ /* very end of the list. */ void end_enm_crt(enemy, crt_ptr) char *enemy; creature *crt_ptr; { etag *ep, *move; move = (etag *)malloc(sizeof(etag)); if(!move) merror("end_enm_crt", FATAL); strcpy(move->enemy, enemy); move->next_tag = 0; move->damage = del_enm_crt(enemy, crt_ptr); ep = crt_ptr->first_enm; if(!ep) { crt_ptr->first_enm = move; return; } while(ep->next_tag) ep = ep->next_tag; ep->next_tag = move; } /************************************************************************/ /* add_enm_dmg */ /************************************************************************/ /* This function adds the amount of damage indicated by the third */ /* argument to the enemy total for the creature pointed to by the */ /* second argument. The first argument contains the name of the player */ /* who hit the creature. */ void add_enm_dmg(enemy, crt_ptr, dmg) char *enemy; creature *crt_ptr; int dmg; { etag *ep; ep = crt_ptr->first_enm; while(ep) { if(!strcmp(enemy, ep->enemy)) ep->damage += dmg; ep = ep->next_tag; } } /**********************************************************************/ /* is_enm_crt */ /**********************************************************************/ /* This function returns true if the name passed in the first parameter */ /* is in the enemy list of the creature pointed to by the second. */ int is_enm_crt(enemy, crt_ptr) char *enemy; creature *crt_ptr; { etag *ep; ep = crt_ptr->first_enm; while(ep) { if(!strcmp(ep->enemy, enemy)) return(1); ep = ep->next_tag; } return(0); } /**********************************************************************/ /* die */ /**********************************************************************/ /* This function is called whenever a player or a monster dies. The */ /* first parameter contains a pointer to the creature that has died. */ /* The second contains a pointer to the creature who did the dirty */ /* deed. Different chains of events are taken depending on whether */ /* the deceased was a player or a monster. */ void die(crt_ptr, att_ptr) creature *crt_ptr; creature *att_ptr; { otag *op, *temp; etag *ep; creature *ply_ptr; room *rom_ptr; object *obj_ptr; char str[2048]; long i, t, profloss, total, xpv; int n, levels = 0, expdiv, lessthanzero=0; str[0] =0; if(crt_ptr->type == MONSTER) { ep = crt_ptr->first_enm; while(ep) { ply_ptr = find_who(ep->enemy); if(ply_ptr) levels += ply_ptr->level; ep = ep->next_tag; } expdiv = crt_ptr->experience / levels; ep = crt_ptr->first_enm; while(ep) { ply_ptr = find_who(ep->enemy); if(ply_ptr) { expdiv = (crt_ptr->experience * ep->damage) / MAX(crt_ptr->hpmax,1); expdiv = MIN(expdiv, crt_ptr->experience); print(ply_ptr->fd, "You gained %d experience for the death of %m.\n", expdiv, crt_ptr); ply_ptr->experience += expdiv; ply_ptr->alignment -= crt_ptr->alignment/5; if(ply_ptr->alignment > 1000) ply_ptr->alignment = 1000; if(ply_ptr->alignment < -1000) ply_ptr->alignment = -1000; } ep = ep->next_tag; } if(!F_ISSET(crt_ptr, MTRADE)){ sprintf(str, "%s was carrying: ", crt_str(crt_ptr, 0,CAP|INV)); n = strlen(str); i = list_obj(&str[n], att_ptr, crt_ptr->first_obj); } if(F_ISSET(crt_ptr, MPERMT)) die_perm_crt(crt_ptr); op = crt_ptr->first_obj; while(op) { temp = op->next_tag; obj_ptr = op->obj; del_obj_crt(obj_ptr, crt_ptr); if(!F_ISSET(crt_ptr, MTRADE)){ add_obj_rom(obj_ptr, crt_ptr->parent_rom); } op = temp; } if(crt_ptr->gold) { load_obj(0, &obj_ptr); sprintf(obj_ptr->name, "%d gold coins", crt_ptr->gold); if(i) strcat(str, ", "); strcat(str, obj_ptr->name); obj_ptr->value = crt_ptr->gold; add_obj_rom(obj_ptr, crt_ptr->parent_rom); } del_active(crt_ptr); del_crt_rom(crt_ptr, crt_ptr->parent_rom); free_crt(crt_ptr); if(strlen(str) > n) print(att_ptr->fd, "%s.\n", str); } else if(crt_ptr->type == PLAYER) { rom_ptr = crt_ptr->parent_rom; i = LT(crt_ptr, LT_PLYKL); t = time(0); if(att_ptr->type != PLAYER || att_ptr == crt_ptr) { if(crt_ptr->level < 4) crt_ptr->experience -= crt_ptr->experience/10; else { crt_ptr->experience -= crt_ptr->experience/4; n = crt_ptr->level - exp_to_lev(crt_ptr->experience); if (n > 1) if ( crt_ptr->level < (MAXALVL+2)) crt_ptr->experience = needed_exp[crt_ptr->level-3]; else crt_ptr->experience = (long)((needed_exp[MAXALVL-1]*(crt_ptr->level-2))); } } crt_ptr->lasttime[LT_PLYKL].ltime = 0L; crt_ptr->lasttime[LT_PLYKL].interval = 0L; if(att_ptr->type == PLAYER) { att_ptr->lasttime[LT_PLYKL].ltime = t; att_ptr->lasttime[LT_PLYKL].interval = (long)mrand(7,14) * 86400L; if(F_ISSET(att_ptr,PPLDGK) && F_ISSET(crt_ptr,PPLDGK) && (crt_ptr->name != att_ptr->name)) if(F_ISSET(att_ptr,PKNGDM) != F_ISSET(crt_ptr,PKNGDM)) { xpv = (crt_ptr->level*crt_ptr->level)*10; print(crt_ptr->fd, "You have been bested.\nYou lose %d experience.\n",xpv); crt_ptr->experience -= xpv; if (crt_ptr->experience <=0) crt_ptr->experience=0; xpv /= 2; print(att_ptr->fd, "You have vanquished %m\n",crt_ptr); print(att_ptr->fd, "You gain %d for your heroic deed.\n",xpv); att_ptr->experience += xpv; add_prof(att_ptr,xpv); } else{ xpv = (crt_ptr->level*crt_ptr->level)*5; xpv = MIN(att_ptr->experience,xpv); crt_ptr->experience -= xpv; if(crt_ptr->experience<=0) crt_ptr->experience=0; print(crt_ptr->fd, "You are killed by a fellow member of your organization!\n"); print(crt_ptr->fd, "You lose %d experience!\n",xpv); xpv = (crt_ptr->level*crt_ptr->level)*10; xpv = MIN(att_ptr->experience,xpv); print(att_ptr->fd, "You killed a fellow member of your organization!\n"); print(att_ptr->fd, "You lose %d experience!\n",xpv); att_ptr->experience -= xpv; if(att_ptr->experience<=0) att_ptr->experience=0; lower_prof(att_ptr,xpv); } } if(att_ptr->type == PLAYER && (att_ptr->level-4>crt_ptr->level)){ xpv = ((att_ptr->level-crt_ptr->level)*25); xpv = MIN(att_ptr->experience,xpv); print(att_ptr->fd, "Try fighting a worthy foe.\n"); print(att_ptr->fd, "You lose %d experience for your cowardly act!\n", xpv); att_ptr->experience -= xpv; if(att_ptr->experience<=0) att_ptr->experience=0; } else del_enm_crt(crt_ptr->name, att_ptr); for(n=0,total=0L; n<5; n++) total += crt_ptr->proficiency[n]; for(n=0; n<4; n++) total += crt_ptr->realm[n]; profloss = MAX(0L, total - crt_ptr->experience - 1024L); while(profloss > 9L && lessthanzero < 9) { lessthanzero = 0; for(n=0; n<9; n++) { if(n < 5) { crt_ptr->proficiency[n] -= profloss/(9-n); profloss -= profloss/(9-n); if(crt_ptr->proficiency[n] < 0L) { lessthanzero++; profloss -= crt_ptr->proficiency[n]; crt_ptr->proficiency[n] = 0L; } } else { crt_ptr->realm[n-5] -= profloss/(9-n); profloss -= profloss/(9-n); if(crt_ptr->realm[n-5] < 0L) { lessthanzero++; profloss -= crt_ptr->realm[n-5]; crt_ptr->realm[n-5] = 0L; } } } } for(n=1,total=0; n<5; n++) if(crt_ptr->proficiency[n] > crt_ptr->proficiency[total]) total = n; if(crt_ptr->proficiency[total] < 1024L) crt_ptr->proficiency[total] = 1024L; n = exp_to_lev(crt_ptr->experience); while(crt_ptr->level > n) down_level(crt_ptr); if (att_ptr->type == PLAYER) { crt_ptr->hpcur = crt_ptr->hpmax; crt_ptr->mpcur = MAX(crt_ptr->mpcur,(crt_ptr->mpmax)/10); } else { crt_ptr->hpcur = crt_ptr->hpmax; crt_ptr->mpcur = crt_ptr->mpmax; } F_CLR(crt_ptr, PPOISN); F_CLR(crt_ptr, PDISEA); if(crt_ptr->ready[WIELD-1] && !F_ISSET(crt_ptr->ready[WIELD-1], OCURSE)) { add_obj_rom(crt_ptr->ready[WIELD-1], crt_ptr->parent_rom); temp_perm(crt_ptr->ready[WIELD-1]); crt_ptr->ready[WIELD-1] = 0; } for(i=0; i<MAXWEAR; i++) { if(crt_ptr->ready[i] && !F_ISSET(crt_ptr->ready[i], OCURSE)) { if(att_ptr->type == PLAYER) add_obj_rom(crt_ptr->ready[i], crt_ptr->parent_rom); else add_obj_crt(crt_ptr->ready[i], crt_ptr); crt_ptr->ready[i] = 0; } } compute_ac(crt_ptr); compute_thaco(crt_ptr); if(crt_ptr == att_ptr) broadcast("### Sadly, %s died.", crt_ptr->name); else broadcast("### Sadly, %s was killed by %1m.", crt_ptr->name, att_ptr); del_ply_rom(crt_ptr, rom_ptr); load_rom(50, &rom_ptr); add_ply_rom(crt_ptr, rom_ptr); savegame(crt_ptr, 0); print(crt_ptr->fd, "If you had a weapon, it was dropped where you died.\n"); } } void temp_perm(obj_ptr) object *obj_ptr; { otag *op; F_SET(obj_ptr, OPERM2); F_SET(obj_ptr, OTEMPP); op = obj_ptr->first_obj; while(op) { temp_perm(op->obj); op = op->next_tag; } } /**********************************************************************/ /* die_perm_crt */ /**********************************************************************/ /* This function is called whenever a permanent monster is killed. The */ /* room the monster was in has its permanent monster list checked to */ /* if the monster was loaded from that room. If it was, then the last- */ /* time field for that permanent monster is updated. */ void die_perm_crt(crt_ptr) creature *crt_ptr; { creature *temp_crt; room *rom_ptr; long t; int i; t = time(0); rom_ptr = crt_ptr->parent_rom; for(i=0; i<10; i++) { if(!rom_ptr->perm_mon[i].misc) continue; if(rom_ptr->perm_mon[i].ltime + rom_ptr->perm_mon[i].interval > t) continue; if(load_crt(rom_ptr->perm_mon[i].misc, &temp_crt) < 0) continue; if(!strcmp(temp_crt->name, crt_ptr->name)) { rom_ptr->perm_mon[i].ltime = t; free_crt(temp_crt); break; } free_crt(temp_crt); } if(F_ISSET(crt_ptr,MDEATH)){ int fd,n; char tmp[2048], file[80],name[80]; strcpy(name, crt_ptr->name); for(i=0; name[i]; i++) if(name[i] == ' ') name[i] = '_'; sprintf(file,"%s/ddesc/%s_%d",OBJPATH,name,crt_ptr->level); fd = open(file,O_RDONLY,0); if (fd){ n = read(fd,tmp,2048); tmp[n] = 0; broadcast_rom(-1, crt_ptr->rom_num,"\n%s",tmp); } close(fd); } } /**********************************************************************/ /* check_for_flee */ /**********************************************************************/ /* This function will determine if a monster is about to flee to another */ /* room. Only fleeing monsters will flee, and then only after they have */ /* less than 10% of their hit points. Plus, they can only flee into */ /* rooms that have already been loaded into memory. */ void check_for_flee(crt_ptr) creature *crt_ptr; { room *rom_ptr; xtag *xp; if(!F_ISSET(crt_ptr, MFLEER)) return; if(crt_ptr->hpcur > crt_ptr->hpmax/5) return; rom_ptr = crt_ptr->parent_rom; xp = rom_ptr->first_ext; while(xp) { if(!F_ISSET(xp->ext, XSECRT) && !F_ISSET(xp->ext, XCLOSD) && is_rom_loaded(xp->ext->room) && mrand(1,100) < 75) { broadcast_rom(-1, rom_ptr->rom_num, "%M flees to the %s.", crt_ptr, xp->ext->name); die_perm_crt(crt_ptr); del_crt_rom(crt_ptr, rom_ptr); load_rom(xp->ext->room, &rom_ptr); add_crt_rom(crt_ptr, rom_ptr, 1); if(!rom_ptr->first_ply) del_active(crt_ptr); return; } xp = xp->next_tag; } } /**********************************************************************/ /* consider */ /**********************************************************************/ /* This function allows the player pointed to by the first argument to */ /* consider how difficult it would be to kill the creature in the */ /* second parameter. */ void consider(ply_ptr, crt_ptr) creature *ply_ptr; creature *crt_ptr; { char he[5], him[5]; int fd, diff; fd = ply_ptr->fd; diff = ply_ptr->level - crt_ptr->level; diff = MAX(-4, diff); diff = MIN(4, diff); sprintf(he, "%s", F_ISSET(crt_ptr, MMALES) ? "He":"She"); sprintf(him, "%s", F_ISSET(crt_ptr, MMALES) ? "him":"her"); switch(diff) { case 0: print(fd, "%s is a perfect match for you!\n", he); break; case 1: print(fd, "%s is not quite as good as you.\n", he); break; case -1: print(fd, "%s is a little better than you.\n", he); break; case 2: print(fd, "%s shouldn't be too tough to kill.\n", he); break; case -2: print(fd, "%s might be tough to kill.\n", he); break; case 3: print(fd, "%s should be easy to kill.\n", he); break; case -3: print(fd, "%s should be really hard to kill.\n", he); break; case 4: print(fd, "You could kill %s with a needle.\n", him); break; case -4: print(fd, "%s could kill you with a needle.\n", he); break; } }