rogue25b2/gods/
rogue25b2/player/
rogue25b2/space/planets/
rogue25b2/space/prototypes/
rogue25b2/space/ships/
rogue25b2/src/
/***************************************************************************\
[*]    ___    ____   ____   __   __  ____ [*]   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
~

***/