#include <money.h>
#include <skills.h>
#include <tune.h>
#include <config.h>
private int cost_div;
private int cost_mult;
private string our_guild;
private string start_pos;
private mapping spells;
private mapping commands;
private object teaching_person;
void check_spells_commands(string *sk, int lvl);
int do_cost_primaries();
int do_cost_all(int brief);
int do_cost_to(string skill, int level);
int do_cost_by(string skill, int level);
int do_cost(string skill);
int do_advance(string skill);
int do_advance_to(string skill, int num);
int do_advance_by(string skill, int num);
void create()
{
cost_div = COST_DIV;
cost_mult = 100;
spells = ([ ]);
commands = ([ ]);
this_object()->add_help_file("guild");
} /* create() */
void init()
{
this_player()->add_command("advance", this_object(),
"<string>", (: do_advance($4[0]) :));
this_player()->add_command("advance", this_object(),
"<string> to <number>",
(: do_advance_to($4[0], $4[1]) :));
this_player()->add_command("advance", this_object(),
"<string> by <number>",
(: do_advance_by($4[0], $4[1]) :));
this_player()->add_command("join", this_object(), "");
this_player()->add_command("info", this_object(), "");
this_player()->add_command("cost", this_object(),
"primaries", (: do_cost_primaries() :));
this_player()->add_command("cost", this_object(),
"all", (: do_cost_all(0) :));
this_player()->add_command("cost", this_object(),
"all brief", (: do_cost_all(1) :));
this_player()->add_command("cost", this_object(),
"<string>", (: do_cost($4[0]) :));
this_player()->add_command("cost", this_object(),
"<string> to <number>",
(: do_cost_to($4[0], $4[1]) :));
this_player()->add_command("cost", this_object(), "<string> by <number>",
(: do_cost_by($4[0], $4[1]) :));
if ((string) this_player()->query_guild_ob() == our_guild) {
if (stringp(start_pos) && !clonep(this_object())) {
this_player()->add_start(file_name(this_object()), start_pos);
}
}
} /* init() */
varargs void set_guild(string word1, string word2)
{
our_guild = word1;
start_pos = word2;
} /* set_guild() */
void set_cost_div(int number)
{
cost_div = number;
/*
* Points of interest:
* cost_div -> zero gives cost_mult = 10
* cost_div = COST_DIV gives cost_mult = 100
* cost_div -> infinity gives cost_mult = 1000
*/
if (!cost_div) /* stands in for infinity */
cost_mult = 1000;
else
cost_mult = 10 + (990 * cost_div) / (10 * COST_DIV + cost_div);
} /* set_cost_div() */
int query_skill_cost(string skill, int offset)
{
int i;
int total;
string *next;
next = (string *) SKILL_OB->query_immediate_children(skill);
if (!sizeof(next)) {
if (!this_player()->query_guild_ob())
total = DEFAULT_COST;
else
total = (int) our_guild->query_skill_cost(skill);
total *= (STD_COST * cost_mult) / 500;
total *= ((int) this_player()->query_skill(skill) + offset) /
LEVEL_DIV + 1;
// this is the new, increased skill cost.
total = to_int(total * exp((this_player()->query_skill(skill) +
offset) / 150.0));
return total;
}
for (i = 0; i < sizeof(next); i++)
total += query_skill_cost(next[i], offset);
return total;
} /* query_skill_cost() */
int do_advance_internal(string skill, int to, int by)
{
string *bits;
string skill_name;
string outer_name;
string place;
int lvl;
int max_lvl;
int tmp;
int total_xp;
int total_cost;
int tmp_cost;
int p_val;
int i, j;
int round_value;
if ((string) this_player()->query_guild_ob() != our_guild) {
add_failed_mess("You cannot advance here! Go to your own guild.\n");
return 0;
}
place = this_object()->query_property("place");
if (!place)
place = "default";
bits = explode(implode(explode(skill, " "), "."), ".");
if (!(skill_name = (string) SKILL_OB->query_skill(bits))) {
add_failed_mess("The skill " + skill + " does not exist.\n");
return 0;
}
bits = SKILL_OB->query_inactive_skills();
for(j=0; j<sizeof(bits); j++)
if(strsrch(skill, bits[j]) != -1)
return add_failed_mess("The skill " + skill + " is inactive at "
"present and cannot be taught.\n");
lvl = (int) this_player()->query_skill(skill_name);
if (!by && to)
by = to - lvl;
if (by <= 0) {
if (by == 0) {
add_failed_mess("You are already at level " + lvl + " at " +
skill_name + ".\n");
} else {
add_failed_mess("You cannot regress below your current "
"skill level!\n");
}
return 0;
}
if (SKILL_OB->query_only_leaf(skill_name)) {
if (sizeof((mixed *)SKILL_OB->query_immediate_children(skill_name))) {
add_failed_mess("You can only advance the outer skills "
"(eg: other.movement.riding.horse), "
"not the internal "
"skills (eg: other.movement.riding, or other).\n");
return 0;
}
} else {
bits = explode(skill_name, ".");
if (sizeof(bits) > 1) {
outer_name = implode(bits[0..<2], ".");
if (((sizeof(bits) - 1) * 5) >
(int) this_player()->query_skill(outer_name)) {
add_failed_mess("You are not at a high enough level in " +
outer_name +
" to advance " + skill_name + " yet.\n");
return 0;
}
}
}
if (!SKILL_OB->query_allowed_to_teach(skill_name)) {
add_failed_mess("You cannot learn '" + skill_name +
"' from your guild.\n");
return 0;
}
max_lvl = (int) our_guild->query_skill_max_level(skill_name);
if (lvl >= max_lvl) {
add_failed_mess("You cannot advance in " + skill_name +
" any further here.\n");
return 0;
}
p_val = (int) this_player()->query_value_in(place);
if (place != "default") {
p_val += (int) this_player()->query_value_in("default");
}
round_value = MONEY_HAND->query_smallest_value_in(place);
total_cost = 0;
for (i = 0; i < by; i++) {
tmp = query_skill_cost(skill_name, i);
if ((int) this_player()->query_xp() < tmp + total_xp) {
if (!i) {
add_failed_mess("Your lack of experience prevents you improving "
+ skill_name + ".\n");
return 0;
} else {
write("Your lack of experience prevents you taking all the "
"advancement you requested in " + skill_name + ". You are "
"only trained up to level " + (i + lvl) + ".\n");
}
break;
}
if ((lvl + i) >= max_lvl) {
write("Your guild hasn't the resources to train you as far as " +
"you requested in " + skill_name +
". It trains you to level " + max_lvl + ".\n");
break;
}
if (cost_div) {
tmp_cost = ( total_xp + tmp ) / cost_div;
if ( tmp_cost + ( tmp_cost % round_value ) > p_val ) {
if (!i) {
add_failed_mess
("Due to your lack of money, the guild cannot train " +
"you in " + skill_name + ": it would have cost you " +
MONEY_HAND->money_value_string( tmp_cost +
( tmp_cost % round_value ), place ) + ".\n" );
return 0;
} else {
write("Due to your sad lack of money the guild cannot train " +
"you in " + skill_name + " to level " + (by + lvl) +
". It trains you to level " + (i + lvl) + " instead.\n");
}
break;
}
}
total_xp += tmp;
total_cost = tmp_cost;
}
total_cost += total_cost % round_value;
if (!total_xp) {
return add_failed_mess("Oops, something has gone wrong.\n");
}
this_player()->adjust_xp(-total_xp);
if (total_cost) {
this_player()->pay_money((mixed *) MONEY_HAND->
create_money_array(total_cost, place), place);
}
this_player()->add_skill_level(skill_name, i, total_xp);
this_player()->reset_all();
this_player()->dest_hide_shadow();
event(this_object(), "guild_advance", bits, lvl, lvl + i);
check_spells_commands(bits, lvl + i);
write("You advance your skill in " + skill_name + " from " + lvl + " to " +
(lvl + i) + " for " + total_xp + " xp" + (total_cost ? " and " +
(string) MONEY_HAND->
money_value_string
(total_cost,
place) : "") + ".\n");
say((string) this_player()->one_short() + " $V$0=advances,advance$V$ " +
"$V$0=" + this_player()->query_possessive() + ",their$V$ skills.\n");
return 1;
} /* do_advance_internal() */
int do_advance(string skill)
{
return do_advance_internal(skill, 0, 1);
} /* do_advance() */
int do_advance_by(string skill,
int num)
{
return do_advance_internal(skill, 0, num);
} /* do_advance_by() */
int do_advance_to(string skill,
int num)
{
return do_advance_internal(skill, num, 0);
} /* do_advance_to() */
int do_join(string str)
{
if ((string) this_player()->query_guild_ob() == our_guild) {
add_failed_mess("You're already in this guild.\n");
return 0;
}
if (this_player()->query_guild_ob()) {
add_failed_mess("You cannot join a guild while a member of another.\n");
return 0;
}
write("You will only ever get to join one guild. Are you sure? ");
input_to("join2");
return 1;
} /* do_join() */
int join2(string str)
{
str = lower_case(str);
if (str[0] != 'n' && str[0] != 'y') {
write("I do not understand. Yes or no? ");
input_to("join2");
return 1;
}
if (str[0] == 'n') {
write("Ok, not joining the " + our_guild->query_name() + " guild.\n");
return 1;
}
this_player()->set_guild_ob(our_guild);
if (stringp(start_pos) && !clonep(environment(this_player())))
this_player()->add_start(file_name(environment(this_player())),
start_pos);
this_player()->race_guild_commands();
write("You are now a member of the " + our_guild->query_name() +
" guild.\n");
say(this_player()->one_short() + " joins the " + our_guild->query_name() +
" guild.\n");
event(this_object(), "guild_join");
event(users(), "inform",
this_player()->query_cap_name() + " is now a member " + "of " +
our_guild->query_name(), "guild");
return 1;
} /* join2() */
int do_leave(string str)
{
if (str != "guild") {
add_failed_mess("Syntax : leave guild\n");
return 0;
}
if ((string) this_player()->query_guild_ob() != our_guild) {
add_failed_mess("How can you leave a guild if you aren't in it.\n");
return 0;
}
if ((int) this_player()->query_level() > 5) {
add_failed_mess("You are too high a level to leave this guild.\n");
return 0;
}
write("Are you sure you want to leave the guild (Y/N) : ");
input_to("confirm_leave");
return 1;
} /* do_leave() */
void confirm_leave(string str)
{
str = lower_case(str);
if (str != "y") {
write("You are still in the guild.\n");
return;
}
write("Goodbye.\n");
call_other(our_guild, "leaving_guild");
this_player()->set_start_pos(CONFIG_START_LOCATION);
this_player()->set_guild_ob(0);
this_player()->set_guild_data(0);
this_player()->guild_commands();
this_player()->save_me();
} /* confirm_leave() */
int check_primaries(string lpath, string g_o)
{
int i;
string dummy,
*primaries;
primaries = (string *) g_o->query_skills();
if (member_array(lpath, primaries) != -1)
return 1;
for (i = 0; i < sizeof(primaries); i++)
if (sscanf(primaries[i], lpath + ".%s", dummy))
return 1;
return 0;
} /* check_primaries() */
/*
* The purpose of this function is to build an array in the same
* format as what you get from SKILL_OB->query_skills, but only
* containing the data for the primaries in this guild.
*/
mixed *filter_primaries(mixed *tmp_skills, string skill_path,
string *primaries)
{
int i;
string test_skill;
for (i = 0; i < sizeof(tmp_skills); i += SKILL_ARR_SIZE) {
test_skill = skill_path + tmp_skills[i];
if (sizeof(tmp_skills[i + SKILL_BIT]) != 0) /* not a leaf skill */
tmp_skills[i + SKILL_BIT] =
filter_primaries(tmp_skills[i + SKILL_BIT], test_skill + ".",
primaries);
if (member_array(test_skill, primaries) == -1 &&
sizeof(tmp_skills[i + SKILL_BIT]) == 0)
{
tmp_skills = tmp_skills[0 .. (i - 1)] +
tmp_skills[(i + SKILL_ARR_SIZE) .. ];
i -= SKILL_ARR_SIZE;
}
}
return tmp_skills;
} /* filter_primaries() */
private string rec_cost(mixed *arr, string path, int depth, string guild,
int brief)
{
/* use lots of variables...so we only work things out minimum times */
int i;
int depth_gap;
int ndots;
int lvl;
int max_lvl;
int only_leaf;
int tmp;
string str;
string sub_str;
string lpath;
str = "";
depth_gap = (depth - 1) * 2;
ndots = 19 - depth_gap;
for (i = 0; i < sizeof(arr); i += SKILL_ARR_SIZE) {
lpath = path + arr[i];
lvl = this_player()->query_skill(lpath);
if (!guild) {
max_lvl = 5;
} else {
max_lvl = guild->query_skill_max_level(lpath);
}
only_leaf = SKILL_OB->query_only_leaf(lpath);
/* If this is a "lead-only" skill and it's not bottom level... */
if (only_leaf && sizeof(arr[i + SKILL_BIT]) > 0) {
sub_str = rec_cost(arr[i + SKILL_BIT], lpath + ".", depth + 1,
guild, brief);
/* And if there are subskills to print under this skill... */
if (sub_str != "") {
/*
* Print a row of dots for this skill, then print the
* subskill info and skip to the next skill
*/
str += sprintf("%*'| 's%-*'.'s\n", depth_gap, "",
ndots + 18, arr[i]) + sub_str;
continue;
}
}
/* If we're over the guild max in this skill... */
else if (lvl >= max_lvl) {
/* And we're in brief mode... */
if (brief) {
sub_str = rec_cost(arr[i + SKILL_BIT], lpath + ".", depth + 1,
guild, brief);
/* And if there are subskills to print under this skill... */
if (sub_str != "") {
/*
* Print a row of dots for this skill, then print the
* subskill info and skip to the next skill
*/
str += sprintf("%*'| 's%-*'.'s\n", depth_gap, "",
ndots + 18, arr[i]) + sub_str;
continue;
}
}
/* But if we're not in brief mode, print the 'mastered' thingy */
else str += sprintf("%*'| 's%-*'.'s%4d/%3d mastered\n", depth_gap,
"", ndots, arr[i], lvl, max_lvl);
}
/*
* Otherwise, we're not over the max level, AND it is actually a
* learnable skill, so print the cost info
*/
else {
tmp = query_skill_cost(lpath, 0);
str += sprintf("%*'| 's%-*'.'s%4d/%3d %6d xp\n", depth_gap, "",
ndots, arr[i], lvl, max_lvl, tmp);
}
/*
*If we're capable of learning subskills for this skill, or if this
* is a leaf-only skill, then go down one more level
*/
if (lvl >= depth * 5 || only_leaf) {
reset_eval_cost();
str += rec_cost(arr[i + SKILL_BIT], lpath + ".", depth + 1, guild,
brief);
}
}
return str;
} /* rec_cost() */
int do_cost_all(int brief)
{
mixed *arr;
string list;
if ((string) this_player()->query_guild_ob() != our_guild) {
add_failed_mess("You are not a member of this guild.\n");
return 0;
}
arr = (mixed *) SKILL_OB->query_skills();
if (!arr) {
add_failed_mess("Hmmm, we appear to have an error here.\n");
return 0;
}
list = sprintf("%-*'='s\n", (int) this_player()->query_cols(),
"======SKILLS=======Cur/Max==For Next");
list += sprintf("%#*-s\n", (int) this_player()->query_cols() + 1,
rec_cost(arr, "", 1, our_guild, brief));
list += sprintf("%*'='|s\n", (int) this_player()->query_cols(),
"> You have " + (int) this_player()->query_xp() +
" points to spend <");
this_player()->more_string(list, "All", 1);
return 1;
} /* do_cost_all() */
int do_cost_primaries()
{
mixed *arr;
string list;
string *primaries;
if ((string) this_player()->query_guild_ob() != our_guild) {
add_failed_mess("You are not a member of this guild.\n");
return 0;
}
arr = copy((mixed *)SKILL_OB->query_skills());
if (!arr) {
add_failed_mess("Hmmm, we appear to have an error here.\n");
return 0;
}
primaries = our_guild->query_skills();
arr = filter_primaries(arr, "", primaries);
list = sprintf("%-*'='s\n", (int) this_player()->query_cols() - 1,
"======SKILLS=======Cur/Max==For Next");
list += sprintf("%#*-s\n", (int) this_player()->query_cols(),
rec_cost(arr, "", 1, our_guild, 0));
list += sprintf("%*'='|s\n", (int) this_player()->query_cols() - 1,
"> You have " + (int) this_player()->query_xp() +
" points to spend <");
this_player()->more_string(list, "Primaries", 1);
return 1;
} /* do_cost_primaries() */
int do_cost_internal(string skill, int to, int by) {
string *bits;
string skill_name;
string outer_name;
string place;
string cost_str;
int lvl;
int max_lvl;
int i;
int tmp;
int cost;
if ((string) this_player()->query_guild_ob() != our_guild) {
add_failed_mess("You are not a member of this guild.\n");
return 0;
}
bits = explode(implode(explode(skill, " "), "."), ".");
if (!(skill_name = (string) SKILL_OB->query_skill(bits))) {
add_failed_mess("There is no such skill as " + skill + ".\n");
return 0;
}
lvl = (int) this_player()->query_skill(skill_name);
if (!by && to)
by = to - lvl;
if (by == 0) {
add_failed_mess
("It won't cost you anything to stay at the same level!\n");
return 0;
}
if (by < 0) {
add_failed_mess("We can't give refund xp for regressing skills!\n");
return 0;
}
bits = explode(skill_name, ".");
if (sizeof(bits) > 1 && !SKILL_OB->query_only_leaf(skill_name)) {
outer_name = implode(bits[0 .. <2], ".");
if (((sizeof(bits) - 1) * 5) >
this_player()->query_skill(outer_name))
{
add_failed_mess("You are not at a high enough level in " +
outer_name + " to advance " + skill_name +
" yet.\n");
return 0;
}
}
if (!SKILL_OB->query_allowed_to_teach(skill_name)) {
add_failed_mess("You cannot learn '" + skill_name +
"' from your guild.\n");
return 0;
}
max_lvl = (int) our_guild->query_skill_max_level(skill_name);
if (lvl >= max_lvl) {
add_failed_mess("Your guild prevents you even considering advancing " +
skill_name + " above level " + max_lvl + ".\n");
return 0;
}
for (i = 0; i < by; i++) {
if ((lvl + i) >= max_lvl) {
write("Your guild prevents you considering all the advancement you "
+ "requested. You can only advance up to level " + max_lvl +
" in " + skill_name + " here.\n");
break;
}
tmp += query_skill_cost(skill_name, i);
}
// Now work out the monetary cost if any.
place = this_object()->query_property("place");
if (!place)
place = "default";
cost_str = "";
if (cost_div) {
cost = tmp / cost_div;
cost += cost % MONEY_HAND->query_smallest_value_in(place);
if (cost)
cost_str = " and " + MONEY_HAND->money_value_string(cost, place);
}
printf("It would cost you %d xp%s to raise %s from level %d to %d.\n",
tmp, cost_str, skill_name, lvl, (lvl + i));
return 1;
} /* do_cost_internal() */
int do_cost(string skill)
{
mixed *arr;
int i;
int depth;
string list;
string exp_skill;
string *sub_skills;
int only_leaf;
if ((string) this_player()->query_guild_ob() != our_guild) {
add_failed_mess("You are not a member of this guild.\n");
return 0;
}
arr = (mixed *) SKILL_OB->query_skills();
if (!arr) {
add_failed_mess("Hmmm, we appear to have an error here.\n");
return 0;
}
exp_skill =
SKILL_OB->query_skill(explode(implode(explode(skill, " "), "."), "."));
if (!exp_skill) {
add_failed_mess("There is no such skill as " + skill + ".\n");
return 0;
}
i = member_array(exp_skill, arr);
only_leaf = SKILL_OB->query_only_leaf(exp_skill);
if (only_leaf) {
sub_skills = explode(exp_skill, ".");
foreach (skill in sub_skills[0 .. < 2]) {
i = member_array(skill, arr);
arr = arr[i + 3];
}
i = member_array(sub_skills[<1], arr);
depth = sizeof(sub_skills) + 1;
}
else depth = 2;
if (sizeof(arr[i + 3]) > 0) {
list = sprintf("%-*'='s\n", (int) this_player()->query_cols() - 1,
"======SKILLS=======Cur/Max==For Next") +
sprintf("%#*-s\n", (int) this_player()->query_cols(),
rec_cost(arr[i + SKILL_BIT], exp_skill + ".", depth,
our_guild, 0)) +
sprintf("%*'='|s\n", (int) this_player()->query_cols() - 1,
"> You have " + (int) this_player()->query_xp() +
" points to spend <");
this_player()->more_string(list, capitalize(exp_skill), 1);
return 1;
}
return do_cost_internal(exp_skill, 0, 1);
} /* do_cost() */
int do_cost_to(string skill, int num)
{
return do_cost_internal(skill, num, 0);
} /* do_cost_to() */
int do_cost_by(string skill, int num)
{
return do_cost_internal(skill, 0, num);
} /* do_cost_to() */
int do_info()
{
mixed *arr;
string *primaries;
write(our_guild->long() +
"\nThis guild's primary skills and costs are:\n");
arr = copy((mixed *)SKILL_OB->query_skills());
if (!arr) {
add_failed_mess("Hmmm, we appear to have an error here.\n");
return 0;
}
primaries = our_guild->query_skills();
arr = filter_primaries(arr, "", primaries);
printf("%-*'='s\n", this_player()->query_cols() - 1,
"======SKILLS=======Cur/Max==For Next");
printf("%#*-s\n", this_player()->query_cols(),
rec_cost(arr, "", 1, our_guild, 0));
printf("%*'='|s\n", this_player()->query_cols() - 1, "> You have " +
this_player()->query_xp() + " points to spend <");
return 1;
} /* do_info() */
void set_teaching_person(object ob)
{
teaching_person = ob;
}
object query_teaching_person()
{
return teaching_person;
}
void add_spell(string name, string *blue, int lvl)
{
string path;
path = implode(blue, ".");
if (!spells[path])
spells[path] = ({ name, lvl });
else
spells[path] += ({ name, lvl });
} /* add_spell() */
void add_command(string name, string *blue, int lvl)
{
string path;
path = implode(blue, ".");
if (!commands)
commands = ([ ]);
if (!commands[path])
commands[path] = ({ name, lvl });
else
commands[path] += ({ name, lvl });
} /* add_command() */
void check_spells_commands(string *sk, int lvl)
{
string *bits;
int val;
int i;
int j;
if (!teaching_person)
return;
bits = keys(spells);
for (i = 0; i < sizeof(bits); i++) {
val = this_player()->query_skill(bits[i]);
for (j = 0; j < sizeof(spells[bits[i]]); j += 2)
if ((spells[bits[i]][j + 1] < val) &&
!(this_player()->query_spell(spells[bits[i]][j])))
teaching_person->init_command("teach " + spells[bits[i]][j] +
" to " +
this_player()->query_name());
}
bits = keys(commands);
for (i = 0; i < sizeof(bits); i++) {
val = this_player()->query_skill(bits[i]);
for (j = 0; j < sizeof(commands[bits[i]]); j += 2)
if ((commands[bits[i]][j + 1] < val) &&
!(this_player()->query_known_command(commands[bits[i]][j])))
teaching_person->init_command("teach " + commands[bits[i]][j] +
" to " +
this_player()->query_name());
}
} /* check_spell_commands() */
mapping query_spells()
{
return spells + ([ ]);
}
mapping query_commands()
{
return commands + ([ ]);
}
string query_start_pos()
{
return start_pos;
}
string query_our_guild()
{
return our_guild;
}