/* Skills.c
* Something different from the usual DW skills.c
* the basic ideas are the same, but hopefully a bit smarter and faster..
* also more in the style we want FR to work.
* It will not include ways to teach/learn yet.. the code may be there
* but will be commented out.
* Baldrick, May'95.
*/
/*
* sigh, this will be interesting wont it?
* quick summary of routines
* skill_bonus(string) - gives the bonus...
* skill_lvl(string) - gives the raw level, with out stat bonus etc
* modify_skill(string, int) - modifies the skills level by int.
* calc_bonus(int, string) - given the skill lvl, stat bonus str cals bonus
* calc_lvl(string *) - calculate number of lvls in the path
* add_skill_lvl(...) - horror recursive skill adder. arghh
* teach_skill(objct *,string) - Used to teach skills to other people.
skill_commands() - all the skill add_actioned commands.
*/
#include "skills.h"
inherit "/std/skills.c";
#include "tune.h"
int calc_bonus(int lvl, string skill);
mixed recursive_skill_add(mixed skil, string *path, int avr, int lvl, int exp,
mixed standard);
mixed *skills;
private static int delta;
static mapping bonus_cache,
level_cache,
teach_offer;
void skill_commands() {
// add_action("do_learn", "learn");
} /* skill_command() */
mixed *query_skills() { return skills; }
void set_skills(mixed *bing) { skills = bing; }
int calc_level(string *path);
void create() {
::create();
bonus_cache = ([ ]);
level_cache = ([ ]);
teach_offer = ([ ]);
skills = ({ });
} /* create() */
mapping query_bonus_cache() { return bonus_cache; }
mapping query_level_cache() { return level_cache; }
/*
* This is most probably the code you wish to call when doing skil
* checks. It returns the skill + all its bonsues for stats/whatever.
* It first checks to see if the skill is in it's cache.
*/
int query_skill_bonus(string skill)
{
string *path;
object guild,
race;
int tmp, lvl;
if (!stringp(skill) || !strlen(skill))
return 0;
if (!skills)
skills = ({ });
if (skill[0] == '.')
skill = skill[1..strlen(skill)];
if (bonus_cache[skill])
return bonus_cache[skill];
path = (string *)query_skill_path(skill);
if (!path)
return 0;
if(level_cache[skill])
lvl = level_cache[skill];
else
level_cache[skill] = lvl = calc_level(path);
guild = (object)this_object()->query_guild_ob();
race = (object)this_object()->query_race_ob();
if (race)
tmp = (int)race->query_skill_bonus(lvl, skill);
if (guild)
tmp += (int)guild->query_skill_bonus(lvl, skill);
return bonus_cache[skill] = calc_bonus(lvl + tmp, skill);
} /* query_skill_bonus() */
/*
* This returns jus the skill level. Used a lot to determine if you
* can use/teach/whatever a skill.
* This also uses a cache.
*/
mixed query_skill(string skill)
{
string *path;
if (level_cache[skill])
return level_cache[skill];
if (!skills)
skills = ({ });
if (skill[0] == '.')
skill = skill[1..strlen(skill)];
path = (string *)query_skill_path(skill);
if (!path)
return 0;
return (level_cache[skill] = calc_level(path));
} /* query_skill() */
int add_skill_level(string skill, int lvl, int exp)
{
string *path;
if (!skills)
skills = STD_SKILLS;
if (!lvl)
return 0;
delta = 0;
path = (string *)query_skill_path(skill);
if (!path)
return 0;
bonus_cache = ([ ]);
level_cache = ([ ]);
skills = recursive_skill_add(skills, path, 0, lvl, exp,
query_skills_tree());
return 1;
} /* add_skill_level() */
int calc_level(string *path)
{
mixed cur;
int i, j;
int lvl;
lvl = 0;
cur = skills;
for (i=0;i<sizeof(path);i++)
if ((j=member_array(path[i], cur)) == -1)
return lvl;
else {
lvl = cur[j+SKILL_LVL];
cur = cur[j+SKILL_BIT];
}
return lvl;
} /* calc_level() */
mixed add_to_all(mixed skil, int lvl)
{
int i;
if (!sizeof(skil))
return ({ });
for (i=0;i<sizeof(skil);i+=SKILL_ARR_SIZE) {
skil[i+SKILL_LVL] += lvl;
skil[i+SKILL_BIT] = add_to_all(skil[i+SKILL_BIT], lvl);
}
return skil;
} /* add_to_all() */
mixed recursive_skill_add(mixed skil, string *path, int avr, int lvl, int exp,
mixed standard)
{
int i, j, tmp;
mixed bit;
if ((j=member_array(path[0], skil)) == -1) {
j = sizeof(skil);
skil += ({ path[0], avr, 0, ({ }) });
for (i=0;i<sizeof(standard);i+=SKILL_ARR_SIZE)
if (member_array(standard[i], skil) == -1)
skil += ({ standard[i], avr, 0, ({ }) });
}
if ((i=member_array(path[0], standard)) == -1) {
standard = ({ path[0], 0, 0, ({ }) });
i = 0;
}
path = path[1..sizeof(path)];
if (!sizeof(path)) {
skil[j+SKILL_LVL] += lvl;
skil[j+SKILL_BIT] = add_to_all(skil[j+SKILL_BIT], lvl);
skil[j+SKILL_EXP] += exp;
return skil;
}
avr = skil[j+SKILL_LVL];
skil[j+SKILL_BIT] = recursive_skill_add(skil[j+SKILL_BIT], path, avr, lvl,
exp, standard[i+SKILL_BIT]);
bit = skil[j+SKILL_BIT];
if (sizeof(bit)) {
for (i=0;i<sizeof(bit);i+=SKILL_ARR_SIZE)
tmp += bit[i+SKILL_LVL];
skil[j+SKILL_LVL] = (tmp*SKILL_ARR_SIZE)/sizeof(bit);
if (tmp%sizeof(bit))
skil[j+SKILL_LVL] += 1;
}
return skil;
} /* recursive_skill_add() */
int calc_bonus(int lvl, string skill) {
int bonus, stat, i;
string stat_bonus;
stat_bonus = (string)query_skill_stat(skill);
for (i = strlen(stat_bonus); i--; ) {
switch(stat_bonus[i]) {
case 'C' : stat = (int)this_object()->query_con();
break;
case 'S' : stat = (int)this_object()->query_str();
break;
case 'D' : stat = (int)this_object()->query_dex();
break;
case 'W' : stat = (int)this_object()->query_wis();
break;
case 'I' : stat = (int)this_object()->query_int();
break;
}
bonus += (stat - 13) * 3;
}
if (lvl > 60)
lvl = 170 + ((lvl-60) >> 1);
else if (lvl > 40)
lvl = 150 + (lvl-40);
else if (lvl > 20)
lvl = 100 + ( ((lvl-20)*5) >> 1);
else
lvl = lvl * 5;
if (strlen(stat_bonus))
return lvl + ( (lvl * bonus) / (strlen(stat_bonus) * 60) );
return lvl;
} /* calc_bonus() */
int query_skill_successful(string str, int mod) {
return (query_skill_bonus(str) + mod >= random(200));
} /* query_skill_successful */
/* This should dissapear.. easy to abuse and very complicated.
* Baldrick.
*/
int teach_skill(object *obs, string str) {
int num, lvl, my_lvl;
object *ok, *cannot, *too_little, *too_low;
string skill;
mixed *bits;
int i, j, cost, total;
if (sscanf(str, "%d levels of %s", num, skill) != 2)
if (sscanf(str, "%d %s", num, skill) != 2) {
num = 1;
skill = str;
}
if (num > 50) {
write("You can only teach a maximun of 50 levels at one time.\n");
return 1;
}
/* Make sure its a valid skill */
bits = explode(implode(explode(skill, " "), "."), ".");
if (!(skill = (string)query_skill_tree(bits)))
return 0;
/*
* We don't do the teaching here. Figure out how much xp it will cost
* to go up the levels, and inform the person we are teaching...
*/
my_lvl = query_skill(skill);
too_low = ok = cannot = too_little = ({ });
for (i=0;i<sizeof(obs);i++) {
lvl = (int)obs[i]->query_skill(skill);
if (lvl+j >= my_lvl && obs[i] != this_object()) {
cannot += ({ obs[i] });
continue;
}
if ((sizeof(explode(skill, "."))-1)*5 > lvl) {
too_low += ({ obs[i] });
continue;
}
cost = DEFAULT_COST;
cost *= (int)query_skill_cost(skill);
cost *= STD_COST/5;
total = 0;
for (j=0;j<num;j++) {
int k;
if (lvl+j <= my_lvl/2)
total += cost*(((lvl+j)/LEVEL_DIV)+1);
else
total += cost*(((lvl+j)/LEVEL_DIV)*(k=((2*(lvl+j))/(my_lvl))))*k;
}
if (total > (int)obs[i]->query_xp()) {
write("Would have cost "+total+" xp to teach "+num+" levels of "+
skill+" to "+obs[i]->query_cap_name()+".\n");
too_little += ({ obs[i] });
continue;
}
if (obs[i] != this_object()) {
tell_object(obs[i], this_object()->query_cap_name()+" offers to teach "
"you "+num+" level"+(num>1?"s":"")+" of "+skill+
" for "+total+" xp.\nUse 'learn' to learn the "
"skill.\n");
ok += ({ obs[i] });
} else
write("You can teach yourself "+num+" level"+(num>1?"s":"")+" of "+
skill+" for "+total+" xp.\nUse 'learn' to leanr the skill.\n");
obs[i]->add_teach_offer(this_object(), skill, num, lvl, total);
}
if (sizeof(cannot))
write("You are too low a level to teach "+query_multiple_short(cannot)+
" "+num+" levels of "+skill+".\n");
if (sizeof(too_low))
write(capitalize(query_multiple_short(too_low))+" is not high enough "
"level in the outer skills to learn "+num+" levels of "+skill+".\n");
/*
if (sizeof(too_little))
write(capitalize(query_multiple_short(too_little))+" does not have "
"enough xp to learn "+num+" levels of "+skill+".\n");
*/
if (sizeof(ok))
write("You offer teach "+query_multiple_short(ok)+" "+num+" levels of "+
skill+".\n");
return 1;
} /* teach_skill() */
int add_teach_offer(object ob, string skill, int num, int lvl, int xp) {
teach_offer[ob] = ({ skill, num, lvl, xp });
} /* add_teach_offer() */
mapping query_teach_offer() { return teach_offer + ([ ]); }
int do_learn(string str) {
object *obs, *diff_lvl, *no_xp, *not_offer, *ok;
int i, lvl;
string skill, *bits;
mixed *bing;
notify_fail("Syntax: learn <skill> from <person>\nIf they have to have "
"offered to teach you to use this.\n");
if (!str)
return 0;
if (sscanf(str, "%s from %s", skill, str) != 2)
return 0;
obs = find_match(str, environment());
if (!sizeof(obs)) {
notify_fail("I am sorry "+str+" is not here.\n");
return 0;
}
bits = explode(implode(explode(skill, " "), "."), ".");
if (!(skill = (string)query_skill_tree(bits))) {
notify_fail("The skill '"+implode(bits, ".")+"' is invalid.\n");
return 0;
}
ok = not_offer = no_xp = diff_lvl = ({ });
for (i=0;i<sizeof(obs);i++) {
if (!(bing = teach_offer[obs[i]])) {
not_offer += ({ obs[i] });
continue;
}
if (skill != bing[O_SKILL]) {
not_offer += ({ obs[i] });
continue;
}
if ((int)this_object()->query_xp() < bing[O_XP]) {
no_xp += ({ obs[i] });
continue;
}
if (query_skill(bing[O_SKILL]) != bing[O_LVL]) {
diff_lvl += ({ obs[i] });
continue;
}
obs[i]->adjust_xp(bing[O_XP]/10);
if (obs[i] != this_object()) {
write("You let "+obs[i]->query_cap_name()+" teach you "+bing[O_NUM]+
" levels of "+bing[O_SKILL]+" for "+bing[O_XP]+" xp.\n");
tell_object(obs[i], "You teach "+this_player()->query_cap_name()+
" "+bing[O_NUM]+" levels in "+bing[O_SKILL]+
" and gain yourself "+(bing[O_XP]/10)+" xp.\n");
ok += ({ obs[i] });
} else {
write("You teach yourself "+bing[O_NUM]+" levels in "+bing[O_SKILL]+
"for "+bing[O_XP]+".\n");
say(this_player()->query_cap_name()+" teaches themselves some skills.\n");
}
this_object()->adjust_xp(-bing[O_XP]);
add_skill_level(bing[O_SKILL], bing[O_NUM], bing[O_XP]);
teach_offer = m_delete(teach_offer, obs[i]);
}
if (sizeof(not_offer))
write(capitalize(query_multiple_short(not_offer))+" is not offering to "
"teach you "+skill+" at your current level in it.\n");
if (sizeof(no_xp))
write("You do not have enough xp to learn "+skill+" from "+
query_multiple_short(no_xp)+".\n");
if (sizeof(diff_lvl))
write("You are a different level in "+skill+" when "+
query_multiple_short(diff_lvl)+" offered to teach you.\n");
if (sizeof(ok))
say(this_player()->query_cap_name()+" learns some skills from "+
query_multiple_short(ok)+".\n", ok);
return 1;
} /* do_learn() */