/***************************************************************************\ [*] ___ ____ ____ __ __ ____ [*] ROGUE: ROM With Attitude [*] [*] /#/ ) /#/ ) /#/ ) /#/ /#/ /#/ [*] All rights reserved [*] [*] /#/ < /#/ / /#/ _ /#/ /#/ /#/-- [*] Copyright(C) 2000-2003 [*] [*] /#/ \(#(__/ (#(__/ (#(__/#/ (#(___ [*] Kenneth Conley (Mendanbar) [*] [*] Expression of Digital Creativity.. [*] roguemud@yahoogroups.com [*] [-]---------------------------------------+-+-----------------------------[-] [*] File: feats.cpp [*] [*] Usage: Character feat structures and handlers [*] \***************************************************************************/ #include "merc.h" #include "utils.h" #include "feats.h" #include "interp.h" #include "buffer.h" // externals extern char *ability_type[]; #define MAX_REQ 4 #define MAX_FEATS 3 int find_skill(char *name) { return 0; } char *ReqNames[] = { "None", "Ability", "Feat", "Skill", "Class", "Bonus", "\n" }; char *FeatNames[] = { "General", "Special", "Metamagic", "Item Creation", "\n" }; char *ApplyNames[] = { "None", "Ability", "Skill Check", "\n" }; // Externals extern int count_hash_records(FILE *fl); // Global Data UInt32 top_of_feats = 0; LList<TempReq *>temp_reqs; FeatData *feat_index = NULL; TempReq::TempReq(void) { this->featName = NULL; } TempReq::~TempReq(void) { if (this->featName) free(this->featName); } void TempReq::Apply(void) { TempReq *req; FeatReq *newReq; LListIterator<TempReq *> iter(temp_reqs); while ((req = iter.Next())) { newReq = new FeatReq(); if ((newReq->type = req->type) == ReqFeat) { if ((newReq->where = find_feat_by_name(req->featName)) == -1) continue; } else newReq->where = req->where; newReq->requirement = req->value; if (real_feat(req->vnum) == -1) { delete newReq; continue; } feat_index[real_feat(req->vnum)].reqs.Append(newReq); } iter.Reset(); while ((req = iter.Next())) { temp_reqs.Remove(req); delete req; } START_ITER(blahiter, req, TempReq *, temp_reqs) log(" - For some reason a temp_req didn't get deleted."); } FeatReq::FeatReq(void) { } FeatReq::~FeatReq(void) { } FeatAffect::FeatAffect(void) { } FeatAffect::~FeatAffect(void) { } FeatData::FeatData(void) { this->name = NULL; this->description = NULL; } FeatData::~FeatData(void) { if (this->name) free(this->name); if (this->description) free(this->description); } void feats_save_to_disk(void) { FILE *fl; RNum feat; FeatReq *req; FeatAffect *affect; if (!(fl = fopen(FEAT_FILE, "w"))) { mudlogf(NRM, LVL_STAFF, TRUE, "SYSERR: Cannot write feats to disk!"); return; } for (feat = 0; feat <= top_of_feats; feat++) { fprintf(fl, "#%d\n" "%s~\n" "%s~\n" "%d\n", feat, feat_index[feat].name, feat_index[feat].description, feat_index[feat].type); START_ITER(fiter, req, FeatReq *, feat_index[feat].reqs) fprintf(fl, "%d %d\n", req->type, req->requirement); fprintf(fl, "~\n"); START_ITER(aiter, affect, FeatAffect *, feat_index[feat].affects) fprintf(fl, "%d %d %d\n", affect->type, affect->location, affect->modifier); fprintf(fl, "~\n"); } fputs("$~\n", fl); fclose(fl); // olc_remove_from_save_list(FEDIT_PERMISSION, OLC_SAVE_FEATS); } void parse_feat(FILE *fl, int virtual_nr, char *filename) { SInt32 t[3]; char *argument; static SInt32 i = 0; FeatAffect *newAffect; char *arg1 = get_buffer(MAX_INPUT_LENGTH); char *arg2 = get_buffer(MAX_INPUT_LENGTH); feat_index[i].vnum = virtual_nr; feat_index[i].name = fread_string(fl); feat_index[i].description = fread_string(fl); feat_index[i].type = fread_number(fl); for (;;) { if (*(argument = fread_string_eol(fl)) == '~') break; else { TempReq *newReq = new TempReq(); argument = one_argument(argument, arg1); switch ((newReq->type = (ReqType)atoi(arg1))) { case ReqAttr: if (sscanf(argument, "%d %d", t, t + 1) != 2) { log("Format error in feat #%d ReqAttr", virtual_nr); exit(1); } if (t[0] < 0 || t[0] > (MAX_STATS - 1)) log("Location error for ApplyAttr '%s'", feat_index[i].name); newReq->value = t[1]; newReq->where = URANGE(0, t[0], MAX_STATS - 1); break; case ReqFeat: case ReqSkill: case ReqBonus: default: break; } newReq->vnum = virtual_nr; temp_reqs.Append(newReq); } } for (;;) { if (*(argument = fread_string_eol(fl)) == '~') break; else { FeatAffect *newAffect = new FeatAffect(); argument = one_argument(argument, arg1); switch ((newAffect->type = (ApplyType)atoi(arg1))) { case ApplyAttr: if (sscanf(argument, "%d %d", t, t + 1) != 2) { log("Format error in feat #%d ApplyAttr", virtual_nr); exit(1); } if (t[0] < 0 || t[0] > (MAX_STATS - 1)) log("Location error for ApplyAttr '%s'", feat_index[i].name); newAffect->modifier = t[1]; newAffect->location = URANGE(0, t[0], MAX_STATS - 1); break; case ApplySkill: argument = one_argument(argument, arg2); if ((newAffect->location = find_skill(arg2)) == -1) { log("Location error for ApplySkill '%s' '%s'", arg2, feat_index[i].name); exit(1); } newAffect->modifier = atoi(argument); break; default: break; } feat_index[i].affects.Append(newAffect); } } top_of_feats = i++; release_buffer(arg1); release_buffer(arg2); } void load_feats(void) { FILE *fl; char *filename = FEATS_FILE; char *line = get_buffer(MAX_INPUT_LENGTH); SInt32 feat_count, nr = -1, last = 0; if (!(fl = fopen(filename, "r"))) { log("Feat file %s not found.", filename); exit(1); } feat_count = count_hash_records(fl); rewind(fl); feat_count++; CREATE(feat_index, FeatData, feat_count); for (;;) { if (!get_line(fl, line)) { log("Format error after feat #%d", nr); exit(1); } if (*line == '$') break; else if (*line == '#') { last = nr; if (sscanf(line, "#%d", &nr) != 1) { log("Format error after feat #%d", nr); exit(1); } parse_feat(fl, nr, filename); } else { log("Format error after feat #%d", nr); log("Offending line: '%s'", line); exit(1); } } fclose(fl); TempReq::Apply(); release_buffer(line); } FeatData *get_feat_data(char *name) { int i; for (i = 0; i <= top_of_feats; i++) { if (!str_cmp(feat_index[i].name, name)) return &feat_index[i]; } return NULL; } RNum find_feat_by_name(char *name) { int i, nr = -1; char *arg = get_buffer(MAX_INPUT_LENGTH); one_argument(name, arg); for (i = 0; i <= top_of_feats; i++) { if (!str_cmp(feat_index[i].name, arg)) { nr = i; break; } } release_buffer(arg); return (nr); } RNum real_feat(VNum vnum) { int bot, top, mid, nr, found = -1; bot = 0; top = top_of_feats; for (;;) { mid = (bot + top) >> 1; if (feat_index[mid].vnum == vnum) return mid; if (bot >= top) break; if (feat_index[mid].vnum > vnum) top = mid - 1; else bot = mid + 1; } for (nr = 0; nr <= top_of_feats; nr++) { if (feat_index[nr].vnum == vnum) { found = nr; break; } } return (found); } struct feat_struct { char * name; FeatTypes type; // Prerequisites UInt16 level; UInt16 xpCost; char * description; UInt8 feat[MAX_REQ]; SInt8 stat[MAX_STATS]; SInt8 classes[MAX_CLASS]; // Affect SInt32 modifier; }; const struct feat_struct feat_table[MAX_FEATS + 1] = { { "reserved", GeneralFeat, 999, 0, "This is reserved for nothing!", { 0, 0, 0, 0 }, // feats { 0, 0, 0, 0, 0, 0 }, // stats { 0, 0, 0, 0, 0, 0, 0, 0 } // classes }, { "alertness", GeneralFeat, 0, 0, "You have finely tuned senses.", { 0, 0, 0, 0 }, // feats { 0, 0, 0, 0, 0, 0 }, // str, int, wis, dex, con, cha { 0, 0, 0, 0, 0, 0, 0, 0 } // classes }, { "ambidexterity", GeneralFeat, 0, 0, "You are equally adept at using either hand.", { 0, 0, 0, 0 }, { 0, 0, 0, 15, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 } }, { "\n", GeneralFeat, 0, 0, "\n", { 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 } } }; ACMD(do_feats) { int i; for (i = 0; i <= top_of_feats; i++) { FeatReq *req; FeatAffect *aff; ch->Send("%s%s [%s]: %s\n\r", i > 0 ? " " : "", capitalize(feat_index[i].name), FeatNames[feat_index[i].type], feat_index[i].description); START_ITER(iter1, req, FeatReq *, feat_index[i].reqs) { ch->Send("%s Requirement: ", ReqNames[req->type]); switch (req->type) { case ReqAttr: ch->Send("%d+ %s\n\r", req->requirement, capitalize(ability_type[req->where])); break; default: break; } } START_ITER(iter2, aff, FeatAffect *, feat_index[i].affects) { ch->Send("%s Bonus: ", ApplyNames[aff->type]); switch (aff->type) { case ApplyAttr: ch->Send("%s%d %s\n\r", plusminus(aff->modifier), abs(aff->modifier), capitalize(ability_type[aff->location])); break; default: ch->Send("%d %d\n\r", aff->location, aff->modifier); } } } /* int i, c; bool found = false; char *buf = get_buffer(MAX_STRING_LENGTH); for (i = 1, c = 0; i < MAX_FEATS; i++) { if (*feat_table[i].name == '\n') break; ch->Send("%s [%s]\n\r", feat_table[i].name, FeatNames[feat_table[i].type]); ch->Send(" %s\n\r", feat_table[i].description); } release_buffer(buf); */ } /* * We need to make this dynamic. We need to centralize all affects, or applies * that will ever be added or removed from a character. Whether it is a plus * to armor, or a plus to dexterity, or a plus to a certain skill. * * The first idea is to tag on an Affects class to the feats structure. | AffectType type = { None, Skill, Spell, Feat, Stat }; | AffectLoc location = { None, Str, Dex, AC, Spell#, Skill# }; | SInt32 modifier; // +2 to location | Flags bitvector; // any bits having this feat would give the ch * * The second idea is that the first one would be too complicated. * * What would be the best way to apply these bonuses? There are over 75 feats * not including those feats that are open ended like exotic weapon prof that * states you need to pick a weapon (dire flail, shurikan, etc). * 1) Search the entire table every time a check is needed? This is stupid. * 2) Apply the feat's affect directly to the skills or attributes? How does * this work for skills? * 3) Create a Modifiers template for CharData to store all the bonuses to * all the skills, attributes, etc. Would only add those modifiers that * the character has: ch->modifiers->skills.Append(SkillModData); * * On further thought, skills don't seem to be the big issue. In the skill * functions you could test to see if the character has a feat that would * modify the skill in some way. * * The main issue is with the feat_table itself. How do we represent certain * modifier types and still keep the casts correct. Would generating the table * from a file be easier? Have a linked list of modifiers, so that in the file * you simply have to have "Aff: Type Loc Mod" as a line, and it will read it * then add it to the feat's list of applies. * * We should still create the modifiers class in CharData. We will keep those * modifiers like initiative, attack bonus, damage bonus, etc. This would be * somewhat kin to real_abils, and aff_abils, except there is no real_abils. * * Perhaps just a "get_mod(ch, MOD_TOHIT)", like get_abil_mod()? */ /** #1 // Feat# alertness~ // Name You have finely tuned senses.~ // Description 0 0 0 // Type, Level, xpCost 0 0 0 0 0 0 // Str, Int, Wis, Dex, Con, Cha 1 1 2 // AffectType, AffectLoc, Mod ~ #2 ambidexterity~ You are equally adept at using either hand.~ 0 0 0 0 0 0 15 0 0 ~ ***/