/* -*- LPC -*- */
/*
* $Locker: $
* $Id: change_skills.c,v 1.9 2002/08/05 17:14:46 ceres Exp $
*
*/
/**
* Handles updating the skill systems to new levels when the skill tree
* is changed.
* @author Deutha
* @see /std/living/skills.c
* @change 1/2/97 Pinkfish
* Updated to include the new language skill tree
*/
#include <login_handler.h>
#include <language.h>
#include <tune.h>
#include <skills.h>
#include <combat.h>
#define ORIGINAL "alpha"
#define VERSION "beta"
#define VERSION_2 "gamma"
#define VERSION_3 "delta"
#define VERSION_4 "epsilon"
#define VERSION_5 "zeta"
#define VERSION_6 "eta"
#define OLD_SKILLS ({ \
"magic.spells.offensive.area", \
"magic.spells.offensive.target", \
"magic.spells.defensive.area", \
"magic.spells.defensive.self", \
"magic.spells.defensive.target", \
"magic.spells.misc.area", \
"magic.spells.misc.self", \
"magic.spells.misc.target", \
"magic.spells.special", \
"magic.items.held.wand", \
"magic.items.held.rod", \
"magic.items.held.staff", \
"magic.items.held.broom", \
"magic.items.worn.amulet", \
"magic.items.worn.ring", \
"magic.items.scroll", \
"magic.points", \
"occult.methods.brewing", \
"occult.methods.dancing", \
"occult.methods.chanting", \
"occult.methods.flying", \
"occult.spells.cursing", \
"occult.spells.summoning", \
"occult.spells.enchanting", \
"occult.spells.charming", \
"occult.spells.healing", \
})
#define OTHER_SKILLS ({ \
"magic", \
"magic.spells", \
"magic.spells.offensive", \
"magic.spells.defensive", \
"magic.spells.misc", \
"magic.items", \
"magic.items.held", \
"magic.items.worn", \
"occult", \
"occult.methods", \
"occult.spells", \
})
#define ETA_CHANGES (["other.tracking" : "crafts.hunting.tracking", \
"other.swimming" : "other.movement.swimming", \
"other.riding" : "other.movement.riding", \
"other.riding.horse" : "other.movement.riding.horse", \
"other.riding.camel" : "other.movement.riding.camel", \
"other.climbing" : "other.movement.climbing", \
"other.climbing.tree" : "other.movement.climbing.tree", \
"other.climbing.rock" : "other.movement.climbing.rock", \
"other.climbing.rope" : "other.movement.climbing.rope", \
"other.valueing" : "other.trading.valueing", \
"other.valueing.gems" : "other.trading.valueing.gems", \
"other.valueing.jewellery" : "other.trading.valueing.jewellery", \
"other.valueing.weapons" : "other.trading.valueing.weapons", \
"other.valueing.armour" : "other.trading.valueing.armour"])
#define EFFECTS ({"magic.defensive.floating", "magic.shield.impact", \
"magic.defensive.skin.troll", "priest.shield" })
/** @ignore yes */
int query_level_in( mapping skills, string skill ) {
string *bits;
if ( !undefinedp( skills[ skill ] ) )
return skills[ skill ];
bits = explode( skill, "." );
if ( sizeof( bits ) == 1 )
return 0;
bits = delete( bits, sizeof( bits ) - 1, 1 );
skill = implode( bits, "." );
return query_level_in( skills, skill );
} /* query_level_in() */
/**
* Update the skills from the original skills system to the
* beta skills system.
* @param thing the person to update
*/
void change_skills( object thing ) {
int i, total;
mapping skills;
skills = (mapping)thing->query_skills();
if ( !m_sizeof( skills ) )
return;
for ( i = 0; i < sizeof( OLD_SKILLS ); i++ ) {
total += query_level_in( skills, OLD_SKILLS[ i ] );
if ( !undefinedp( skills[ OLD_SKILLS[ i ] ] ) )
skills = m_delete( skills, OLD_SKILLS[ i ] );
}
for ( i = 0; i < sizeof( OTHER_SKILLS ); i++ )
if ( !undefinedp( skills[ OTHER_SKILLS[ i ] ] ) )
skills = m_delete( skills, OTHER_SKILLS[ i ] );
if ( !total )
return;
thing->add_property( "magic levels", total );
thing->set_skills( skills );
tell_object( thing, "\n\nAn imp that only you can see flashes into "+
"existence.\n" );
tell_object( thing, "The imp tells you: The magic and occult skill "+
"categories are being amalgamated and restructured.\n" );
tell_object( thing, "The imp tells you: You have a total of "+ total +
" levels that you can spend on the new magic skills.\n" );
tell_object( thing, "The imp tells you: Go and find the black "+
"monolith in the middle of Sator Square which you can use to "+
"to spend them.\n" );
tell_object( thing, "The imp flashes back to wherever it came from.\n\n" );
} /* change_skills() */
/**
* Figure out thge minimum amount of xp that could have been used to
* get the skill level. This assumes a leaf skill
* This appears to be the xp equation. Using it anyway...
* @param lvl the level to get to
* @return the skill cost in xp
*/
int query_minimum_skill_xp(int lvl) {
int i;
int xp;
xp = 0;
for (i = 0; i < lvl; i++) {
xp += ( DEFAULT_COST * STD_COST * 100 ) / (( LEVEL_DIV + 1) * 500);
}
return xp;
} /* query_minimum_skill_xp() */
/**
* Changes the skill array up to the new tree involving the
* languages.
* @param thing the object to upgrade
* @param lev the level we are changing up to.
*/
void change_language_skills( object thing, int lev ) {
int i;
int total_xp;
mapping skills;
string *bits;
string start;
string *langs;
string health;
skills = thing->query_skills();
if (!sizeof(skills))
return ;
bits = keys(skills);
health = skills["other.health"];
/*
* Instead of changing the skill levels directly... We will just
* call the appropriate functions on the player object to fiddle
* them.
*/
start = "other.language.";
total_xp = 0;
for (i = 0; i < sizeof(bits); i++) {
if (bits[i][0..strlen(start)-1] == start) {
/* Ok, now figure out how much xp to give back... */
total_xp += query_minimum_skill_xp(skills[bits[i]]);
map_delete(skills, bits[i]);
}
}
langs = thing->query_languages();
for (i = 0; i < sizeof(langs); i++) {
thing->add_language(langs[i]);
thing->remove_language( langs[i] );
}
if (skills["other.language"]) {
total_xp += query_minimum_skill_xp(skills["other.language"]);
map_delete(skills, "other.language");
}
/* Everyone gets common... */
thing->add_language("common");
if(thing->query_guild_ob() == "/std/guilds/wizard") {
thing->add_language("wizard spells");
}
if(thing->query_guild_ob() == "/std/guilds/thief") {
thing->add_language("thieves' cant");
}
if(skills["other.health"] != health) {
tell_object(thing, "A fluffy cabbage tells you: Something has gone wrong "
"with your language adjustment, please tell Ceres or "
"Pinkfish\n");
return;
}
/* thing->set_skills( skills ); */
thing->adjust_xp( total_xp );
tell_object(thing,
"A fluffy cabbage turns up and stares straight into your eyes.\n"
"You feel that the fluffy cabbage has shuffled around some bits of your mind.\n"
);
if (total_xp > 0 && lev < 2) {
tell_object(thing,
"Your languages have been shifted, you appear to have gained "+total_xp+" xp.\n"
);
}
} /* change_language_skills() */
mapping recalc_parents(mapping skills, string skill) {
int i, j, lvl;
string *tree, *bits, this_skill;
bits = explode(skill, ".");
for(i=sizeof(bits)-1; i>=0; i--) {
this_skill = implode(bits[0..i], ".");
tree = (string *)SKILL_OB->query_immediate_children(this_skill);
lvl = 0;
for(j=0; j<sizeof(tree); j++) {
lvl += skills[tree[j]];
}
if(sizeof(tree))
skills[this_skill] = lvl / sizeof(tree);
}
return skills;
}
/**
* Update the skills from the original skills system to the
* beta skills system.
* @param thing the person to update
*/
void change_skills_epsilon( object thing ) {
int lvl, rest;
mapping skills;
string *bits;
skills = thing->query_skills();
if (!sizeof(skills))
return ;
bits = keys(skills);
tell_object(thing, "A squirrel appears and says: I'm just updating your "
"skill tree, will be just a jiffy.\n");
// Do sleight-of-hand first.
lvl = skills["covert.sleight-of-hand"];
if(lvl > 0) {
m_delete(skills, "covert.sleight-of-hand");
if(lvl > 300) { // they get skills up to 300 at 2:1
rest = lvl - 300;
lvl = 300;
if(rest > 600) { // 300-600 at 1:1
lvl += 300;
rest -= 600;
lvl += rest / 5; // >600 at 4:10
} else
lvl += rest / 2;
}
skills["covert.manipulation.stealing"] = lvl;
skills["covert.manipulation.palming"] = lvl;
skills["covert.manipulation.passing"] = lvl;
skills["covert.manipulation.sleight-of-hand"] = lvl;
skills["covert.manipulation"] = lvl;
recalc_parents(skills, "covert");
}
// then sharp
lvl = skills["fighting.combat.melee.sharp"];
if(lvl > 0) {
if(lvl > 300) { // they get skills up to 300 at 2:1
rest = lvl - 300;
lvl = 300;
if(rest > 600) { // 300-600 at 1:1
lvl += 300;
rest -= 600;
lvl += rest / 5; // >600 at 4:10
} else
lvl += rest / 2;
}
skills["fighting.combat.melee.sharp"] = lvl;
skills["fighting.combat.melee.pierce"] = lvl;
skills = recalc_parents(skills, "fighting.combat.melee");
}
// then parry
lvl = skills["fighting.combat.parry.thrown"];
rest = skills["fighting.combat.parry.fired"];
if(lvl > 0 || rest > 0) {
if(rest > lvl)
lvl = rest + (lvl / 5);
else
lvl += (rest / 5);
m_delete(skills, "fighting.combat.parry.thrown");
m_delete(skills, "fighting.combat.parry.fired");
skills["fighting.combat.parry.range"] = lvl;
lvl = skills["fighting.combat.parry.held"];
m_delete(skills, "fighting.combat.parry.held");
skills["fighting.combat.parry.melee"] = lvl;
skills = recalc_parents(skills, "fighting.combat.parry");
}
// then special
lvl = skills["fighting.combat.special"];
if(lvl > 0) {
if(lvl > 300) { // they get skills up to 300 at 2:1
rest = lvl - 300;
lvl = 300;
if(rest > 600) { // 300-600 at 1:1
lvl += 300;
rest -= 600;
lvl += rest / 5; // >600 at 4:10
} else
lvl += rest / 2;
}
skills["fighting.combat.special.weapon"] = lvl;
skills["fighting.combat.special.unarmed"] = lvl;
skills["fighting.combat.special.tactics"] = lvl - lvl / 4;
skills = recalc_parents(skills, "fighting.combat.special");
}
// do tracking
lvl = skills["other.tracking.woodland"];
m_delete(skills, "other.tracking.woodland");
lvl += skills["other.tracking.desert"];
m_delete(skills, "other.tracking.desert");
lvl += skills["other.tracking.mountain"];
m_delete(skills, "other.tracking.mountain");
lvl += skills["other.tracking.road"];
m_delete(skills, "other.tracking.road");
lvl += skills["other.tracking.field"];
m_delete(skills, "other.tracking.field");
if(lvl > 0)
skills["other.tracking"] = lvl;
thing->set_skills( skills );
tell_object(thing, "The squirrel says: Ok, all done now.\n");
}
/**
* Calulate the new skill value for the skills version zeta
*/
int new_skill_value(int sk) {
float f = 1000.0;
float k = 0.3;
if(sk == 0)
return 0;
return to_int(f * log( 1.0 + sk/(f + sk * k) ) + 0.5);
}
/**
* Change everyones skill levels (downwards).
* @param thing the person to update
*/
void change_skills_zeta( object thing ) {
mapping skills;
string *bits, bit;
skills = thing->query_skills();
if (!sizeof(skills))
return ;
bits = keys(skills);
tell_object(thing, "A small blue frog appears and says: I'm just updating "
"your skill levels, this won't take a second.\n");
// go through all the skills one by one
foreach(bit in bits) {
// if it's not a language skill & it's a leaf skill recalculate it and
// then recalculate it's parents.
if(bit[0..13] != "other.language" &&
SKILL_OB->query_immediate_children(bit) == ({ })) {
skills[bit] = new_skill_value(skills[bit]);
skills = recalc_parents(skills, bit);
}
}
// set these skills as their new skills and we're done.
thing->set_skills( skills );
tell_object(thing, "The frog says: Ok, all done now.\n");
}
void fixup_stats(object thing) {
mapping values;
mapping changes;
int stat_total;
string str;
changes = ([ ]);
values = ([
"con" : thing->query_real_con(),
"dex" : thing->query_real_dex(),
"int" : thing->query_real_int(),
"wis" : thing->query_real_wis(),
"str" : thing->query_real_str()
]);
stat_total = values["con"] + values["dex"] + values["wis"] + values["str"] +
values["int"];
if (stat_total > 65) {
if (thing->query_real_con() > 8) {
thing->adjust_con(-1);
stat_total--;
values["con"]--;
changes["con"]++;
}
if (stat_total > 65 && thing->query_real_dex() > 8) {
thing->adjust_dex(-1);
stat_total--;
values["dex"]--;
changes["dex"]++;
}
// Now find highest stat and adjust until correct.
while (stat_total > 65) {
string highest;
int value;
string stat;
int v;
value = 0;
foreach (stat, v in values) {
if (v > value) {
value = v;
highest = stat;
}
}
call_other(thing, "adjust_" + highest, -1);
values[highest]--;
changes[highest]++;
stat_total--;
}
}
str = query_multiple_short(map(keys(changes), (: $1 + " changed by " + $2[$1] :), changes));
tell_object(thing, "The armadillo says: Stats changed: " + str + "\n");
}
void change_skills_eta(object thing) {
mapping skills;
string bit, effect;
class tactics tactics;
int eff;
// Initialise tactics since we have some new fields.
tactics = new(class tactics,
attitude : "neutral",
response : "neutral",
parry : "both",
parry_unarmed : 0,
mercy : "ask",
focus_zone : 0,
ideal_distance : 0);
thing->set_tactics(tactics);
skills = thing->query_skills();
if(!sizeof(skills))
return;
tell_object(thing, "An armadillo appears out of nowhere and says: "
"I'm just updating your skills, this won't take a second.\n");
foreach(bit in keys(ETA_CHANGES)) {
tell_object(thing, "Mapping " + bit + " to " + ETA_CHANGES[bit] + ".\n");
skills[ETA_CHANGES[bit]] = skills[bit];
map_delete(skills, bit);
}
tell_object(thing, "Adding new skills.\n");
foreach(bit in
"/std/skills"->query_immediate_children("crafts")) {
if(!skills[bit]) {
skills[bit] = 0;
}
}
recalc_parents(skills, "crafts.hunting");
recalc_parents(skills, "crafts");
recalc_parents(skills, "other.movement.riding");
recalc_parents(skills, "other.movement.climbing");
recalc_parents(skills, "other.movement");
recalc_parents(skills, "other.trading.valueing");
recalc_parents(skills, "other.trading");
recalc_parents(skills, "other");
thing->set_skills( skills );
foreach(effect in EFFECTS)
foreach(eff in thing->effects_matching(effect))
catch(thing->delete_effect(eff));
tell_object(thing, "Fixing stats.\n");
fixup_stats(thing);
tell_object(thing, "Granting rearrange.\n");
thing->add_known_command("rearrange");
if ((thing->query_guild_ob() == "/std/guilds/assassin" &&
thing->query_property("assassin") > 1) ||
(thing->query_guild_ob() == "/std/guilds/wizard" &&
"/obj/handlers/guild_things/wizard_orders"->query_order() == "last")) {
tell_object(thing, "Returning PK flag.\n");
thing->set_player_killer(1);
}
thing->remove_property(PKER);
thing->remove_property(PKER_MISSPELL);
thing->add_property("not real newbie", 1);
tell_object(thing, "Removing old combat commands.\n");
if(thing->query_known_command("strike"))
thing->remove_known_command("strike");
if(thing->query_guild_ob() != "/std/guilds/warrior") {
thing->remove_known_command("impale");
thing->remove_known_command("crush");
thing->remove_known_command("behead");
}
thing->remove_known_command("punch");
thing->remove_known_command("kick");
tell_object(thing, "The armadillo says: Ok, all done now.\n");
"/d/liaison/NEWBIE/choose_land"->fixup_player(this_player());
}
/**
* Called by the login handler, checks to see if an upgrade
* is neede.
* @param name the name of the player logging in
* @param type the type of the login
* @see /obj/handlers/login_handler.c
*/
void entering( string name, string type ) {
object thing;
int lvl;
if ( type != LOGIN )
return;
if ( !( thing = find_player( name ) ) )
return;
switch((string)thing->query_property( "skills version" )) {
case VERSION:
change_skills( thing );
case VERSION_2:
change_language_skills( thing, lvl );
case VERSION_3:
change_skills_epsilon( thing );
case VERSION_4:
change_skills_zeta(thing);
case VERSION_5:
change_skills_eta(thing);
default:
// If they don't have a skill version then they're a new player and
// don't need any modification to their skill tree.
thing->add_property("skills version", VERSION_6);
return;
}
} /* entering() */
/**
* The current version of the skill tree.
* @return the version of the skill system
*/
string query_version() { return VERSION_5; }