LPMUD/
LPMUD/BIN/
LPMUD/DOC/
LPMUD/MUDLIB/
LPMUD/MUDLIB/BANISH/
LPMUD/MUDLIB/D/
LPMUD/MUDLIB/DOC/
LPMUD/MUDLIB/DOC/DOMAINS/
LPMUD/MUDLIB/DOC/EFUN/
LPMUD/MUDLIB/DOC/EXAMPLES/
LPMUD/MUDLIB/DOC/EXAMPLES/ARMOUR/
LPMUD/MUDLIB/DOC/EXAMPLES/CONTAIN/
LPMUD/MUDLIB/DOC/EXAMPLES/FOOD/
LPMUD/MUDLIB/DOC/EXAMPLES/MAGIC/
LPMUD/MUDLIB/DOC/EXAMPLES/MONSTER/
LPMUD/MUDLIB/DOC/EXAMPLES/ROOM/
LPMUD/MUDLIB/DOC/EXAMPLES/WEAPONS/
LPMUD/MUDLIB/FUNCTION/
LPMUD/MUDLIB/INCLUDE/
LPMUD/MUDLIB/INCLUDE/FN_SPECS/
LPMUD/MUDLIB/INCLUDE/SKILLS/
LPMUD/MUDLIB/INFO/
LPMUD/MUDLIB/INHERIT/BASE/
LPMUD/MUDLIB/LOG/
LPMUD/MUDLIB/MANUALS/312/
LPMUD/MUDLIB/NEWS/
LPMUD/MUDLIB/OBJ/PARTY/
LPMUD/MUDLIB/OBJ/SHADOWS/
LPMUD/MUDLIB/OBJECTS/COMPONEN/
LPMUD/MUDLIB/OPEN/
LPMUD/MUDLIB/OPEN/LIBRARY/
LPMUD/MUDLIB/OPEN/PARTY/
LPMUD/MUDLIB/PLAYERS/
LPMUD/MUDLIB/PLAYERS/ZIL/
LPMUD/MUDLIB/ROOM/
LPMUD/MUDLIB/ROOM/CITY/ARENA/
LPMUD/MUDLIB/ROOM/CITY/CREATOR/
LPMUD/MUDLIB/ROOM/CITY/GARDEN/MONST/
LPMUD/MUDLIB/ROOM/CITY/OBJ/
LPMUD/MUDLIB/ROOM/CITY/PUB/
LPMUD/MUDLIB/ROOM/CITY/SHOP/
LPMUD/MUDLIB/ROOM/DEATH/
LPMUD/MUDLIB/ROOM/REGISTRY/
LPMUD/MUDLIB/SECURE/
LPMUD/MUDLIB/SECURE/UDP_CMD_/
LPMUD/MUDLIB/SKILLS/
LPMUD/MUDLIB/SKILLS/FIGHTER/
LPMUD/MUDLIB/SKILLS/THIEF/
LPMUD/MUDLIB/USR/
LPMUD/MUDLIB/USR/CREATORS/
LPMUD/MUDLIB/USR/PLAYERS/
/* configurable guild room */
/* allows creator to easily add a guild room to use standard skills */

#include <mudlib.h>
inherit ROOM;

#define LEVEL          (int)this_player()->query_level()
#define NAME           (string)this_player()->query_name()
#define TEXP           (int)this_player()->query_total_exp()
#define EXP	       (int)this_player()->query_exp()
#define GUILD_STR      lower_case((string)this_player()->query_guild_string())
#define COST_FOR_JOIN  1000   /* coins to join a new class */
#define CHARISMA       (int)this_player()->query_charisma()
#define MASTER_CHA     16


#define QPS            (int)this_player()->query_qpoints()

#define DRAINED        (int)this_player()->query_level_drained()
#define NO_ADVANCE     present("no advance",this_player())
#define PLAY_MONEY     (int)this_player()->query_money()
#define G_NAME         (string)guild_master->query_name()


mixed  *classes;        /* class-skill array       */
mixed  *titles;         /* level titles            */
mixed  *pretitles;      /* pretitles for 30+ level */
int    *exp;            /* exp per level           */
int    *qp;             /* quest points for next level */
int    *skill_exp;      /* cost for skills         */
object guild_master;    /* guild master            */
string guild_name;      /* guild title             */
int    weapon_rate;     /* rate which get weapons proficiencies */
int    exp_multiplier;
int    skill_exp_multiplier;

object sign;

void set_classes(mixed *arg)      { classes = arg;      }
void set_titles(mixed *arg)       { titles  = arg;      }
void set_pretitles(mixed *arg)    { pretitles  = arg;   }
void set_exp(int *arg)            { exp = arg;          }
void set_skill_exp(int *arg)      { skill_exp = arg;    }
void set_guild_master(object arg) { guild_master = arg; }
void set_guild_name(string arg)   { guild_name = arg;   }
void set_weapon_rate(int arg)     { weapon_rate = arg;  }


mixed *query_classes()      { return classes;      }
mixed *query_titles()       { return titles;       }
mixed *query_pretitle()     { return pretitles;    }
int *query_exp()            { return exp;          }
int *query_skill_exp()      { return skill_exp;    }
object query_guild_master() { return guild_master; }
string query_guild_name()   { return guild_name;   }
int query_weapon_rate()     { return weapon_rate;  }


void init(){
  ::init();
  add_action("train_weapon", "train");
  add_action("advance","raise");
  add_action("advance","advance");
  add_action("cost_for_level","cost");
  add_action("join","join");
  add_action("leave","leave");
  if(!sign) {
    sign = clone_object(SIGN);
#ifdef NATIVE_MODE
    sign->move(this_object());
#else
    move_object(sign, this_object());
#endif /* NATIVE_MODE */
    sign->set_message(
  "      Commands                         Comments\n"+
  "  raise, raise <level> ....... Advance level by 1\n"+
  "  raise <skill> .............. Advances skill by 1\n"+
  "  cost <level> ............... Exp or coins needed to advance\n"+
  "  cost <skill> ............... (Not)|Possible to raise skill\n"+
  "  cost <class> ............... (Not)|Possible to raise skills in class\n"+
  "  leave <class> .............. Leave a class, set all stats to 0\n"+
  "  join <class> ............... Join the fighter class only\n"+
  "  train <weapon> ............. Train in a weapon and become proficient\n"
    );
  }
}


int get_next_qp(int stat) {
  int *qp;
 

  qp = ({
            0,    0,    0,    0,    0,    0,   0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,   0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,   0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,   0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,   0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,   0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,   0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,   0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,   0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,   0,    0,    0,    0,
            12
         });
 
   if(stat < 0) return qp[0];
   if(stat > 100) return ((stat - 100) *10) + 2;  /* worry when close */
   return qp[stat];
}

/* exp for next level */

#ifndef AMYLAAR

int get_next_exp(int stat) {
  if(!exp) {
    exp = ({
                  0,    1014,    1522,    2283,    3425,    5138,
               7707,   11561,   17341,   26012,   39018,   58527,
              87791,  131687,  197530,  296296,  444444,  666666,
            1000000, 1500000, 2000000, 2500000, 3000000, 3500000,
            4000000, 4500000, 5000000, 5500000, 6000000, 6500000,
    });
  }
  if(stat < 0) return exp[0];
  if(stat >= sizeof(exp)) {
    if(!exp_multiplier) exp_multiplier = 500000;
    return exp[sizeof(exp)-1] + (stat-(sizeof(exp)-1))*exp_multiplier;
  }
  return exp[stat];
}

#else /* amylaar version */

int get_next_exp(int stat) {
	int tmp;
  if(stat < 1) return 1014;
  if(stat < 20)
    return 1014 + to_int(1014*exp(stat * log(1.4683496)));
/* ok so it looks silly, but it makes xp for level 20 about 1.5 mill */
  else
/*
    return to_int(get_next_exp(20) * exp((stat-20) * log(1.17)));
*/
{
	tmp = get_next_exp(19) + (500000
		* (stat - 19)) + 20000 * (stat - 20) * (stat  -19) /2;
	if(tmp > 0) return tmp;
		else return 500000000000;
	}
}
/* The one above is a geometric series. lets try a algebraic one: */
/*
int get_next_exp(int stat) {
	return 1014 * (stat + 1) + 10000 * stat * (stat + 1) /2;
}
*/

#endif /* 312MASTER */


/* exp for next stat */

int get_skill_exp(int stat) {
  if(stat < 0) return skill_exp[0];
  if(stat >= sizeof(skill_exp)) {
    if(!skill_exp_multiplier) skill_exp_multiplier = 25000;
    return skill_exp[sizeof(skill_exp)-1] 
         + (stat-(sizeof(skill_exp)-1))*skill_exp_multiplier;
  }
  return skill_exp[stat];
}


/* if the player is within 10% of their next level, they advance at a
 * cost of 3 times coins of the exp difference. */

int close_enuff(status costing_only){
  int required_money, required_exp;
 
  required_exp = get_next_exp(LEVEL);
  required_money = (required_exp - EXP) * 3;

  if(EXP < required_exp && (required_exp-EXP) < required_exp/10) {
    if(PLAY_MONEY > required_money) {
      if(!costing_only){
        this_player()->add_money(required_money*(-1));
	this_player()->set_exp( required_exp);
        write(G_NAME +" takes "+ required_money +
              " coins from you for extra training.\n");
      }
      return required_money;
    }
  }
  return 0;
}



/*** exp required ***/

string get_new_title(int next_level){
  string *weird_titles;
  int gender;

  gender = (int)this_player()->query_gender();
  while(!sizeof(titles[gender])) gender++;

  if(next_level > sizeof((string *)titles[gender]) - 1) {
    while(!sizeof((string *)pretitles[gender])) if(++gender > 2) return "";
    next_level = (next_level-30)/4;
    if(next_level > sizeof((string *)pretitles[gender])) {
      next_level = sizeof((string *)pretitles[gender])-1;
    }
    if(this_player()->query_level() > 29)
    this_player() -> set_pretitle((string)pretitles[gender][next_level]);
     
    weird_titles = ({
                      "the Stranger",
                      "the Insignificant",
                      "the Saviour",
                      "the Blood Stained",
                      "the Dragon Slayer",
                      "Orcbane",
                      "Ironfist",
                      "the Faithful",
                      "the Invincible",
                      "the Meek!!!",
                      "the Black",
                      "the White",
                      "the Incorrigible",
                    });
                      
     return weird_titles[random(sizeof(weird_titles))];
  }
  while(!sizeof((string *)titles[gender])) if(++gender > 2) return "";
  return (string)titles[gender][next_level];
}



/*** advance levels or stats, and costing ***/

status advance(string str, status costing){
  int i, j, k;
  string stat;
  int total, cost, current_stat;
  string tmp1;

  if(!guild_master) {
    write("There is no one here to train you.\n");
    return 1;
  } 
  if(this_player()->query_npc()) {
    tell_room(this_object(),G_NAME +" laughs heartily!\n");
    return 1;
  }
  if(!this_player()->query_guild_string()) {
    this_player()->set_guild_string("no");
  }
  if(!(lower_case(guild_name) == "no"
  &&  (GUILD_STR == "banned" || GUILD_STR == "no"))
    || GUILD_STR != lower_case(guild_name)) {
    write(G_NAME +" says: Sorry, you can't train here.\n");
    return 1;
  }
  if(DRAINED) {
   write(G_NAME +" says: You have been drained by undead,\n"+
                 "   Perhaps you should see a priest before training.\n");
    return 1;
  }

  if(NO_ADVANCE){
    write(G_NAME +" says: You are currently under the affect of"+
          " magic, I cannot train you\n   until it wears off!\n");
    return 1;
  }

  /*** advance a players level ***/
  
  if(!str || str == "level") {
    if(costing) {
      write("Next Level: "+ get_next_exp(LEVEL) +" experience points ");
      if(EXP > get_next_exp(LEVEL)) {
        write("(POSSIBLE)\n");
      }
      else if(close_enuff(1)) {
        write("(POSSIBLE: "+ close_enuff(1) +" coins)\n");
      }
      else {
        write("(NOT POSSIBLE)\n");
      }
      return 1;
    }

   if(QPS < get_next_qp(LEVEL)) {
     notify_fail(G_NAME+" says: You need more quest points to advance\n");
     return 0;
     }
 
    if(EXP > get_next_exp(LEVEL) || close_enuff(0)) {
      this_player()->set_title(get_new_title(LEVEL));
      this_player()->set_level(LEVEL + 1);
      this_player()->save_character();
      say(G_NAME +" trains "+ NAME +" to level "+ LEVEL +".\n");
      write(G_NAME +" trains you. You are now level "+ LEVEL +", "+
            (string)this_player()->short()+"\n");
      return 1;
    }
    else {
      write(G_NAME+" says: You need to train more before advancing.\n");
       return 1;
    } 
    return 1;
  }

  if(costing) { /* recursively cost a class */
    if((i = member_array(str,classes)) != -1) {
      for(k = 0; k < sizeof(classes[i+1]); k++) {
        advance(classes[i+1][k],1);
      }
      return 1;
    }
  }

  for(i = 1; i < sizeof(classes); i += 2) {
    if((j = member_array(str, classes[i])) != -1) {
      if(!this_player()->query_class(classes[i-1]) 
      && classes[i-1] != "general" && classes[i-1] != "primary") {
        write(G_NAME +" says: You are not a "+ classes[i-1] +".\n");
      }
      else {
        stat = str;
        while(sscanf(stat,"%s %s",stat,tmp1))  stat += "_"+ tmp1;
        stat = lower_case(stat);
        current_stat = (int)call_other(this_player(),"query_"+stat);
        cost = get_skill_exp(current_stat);
        if(member_array(stat, ({ "strength", "intelligence", "wisdom",
                                 "dexterity", "constitution", "charisma",
			         "combat", })) != -1) {
           if(current_stat >= (int)call_other(this_player(), "query_max_"+
              stat)) {
              write(G_NAME+" says: You are more powerful than I am! I can no "+
		    "longer train you in "+stat+".\n");
              return 1;
            }
         }  

        if(!(classes[i-1] == "general" || classes[i-1] == "primary")) {
          /* Multi-class multiple exp setting cost; was 3/2 */
         if(sizeof((string *)this_player()->query_classes()) == 1)
           cost *= 1;
         else
           cost *= (sizeof((string *)this_player()->query_classes())*3)/2;
        }
        for(k = 0; k < sizeof(classes[i]); k++) {
          stat = classes[i][k];
          while(sscanf(stat,"%s %s", stat, tmp1)) stat += "_"+ tmp1;
          stat = lower_case(stat);
          total += (int)call_other(this_player(),"query_"+ stat);
        }
        if(total > (LEVEL + 1) * sizeof(classes[i])) {
          if(costing) {
            write("Your level needs to be higher, before you can advance "+
                  "your "+ str +".\n");
          }
          else {          
            write(G_NAME+" says: You need to be of greater Status.\n");
          }
          return 1;
        }
	if(EXP<cost) {
          if(costing) {
            str = extract(str+"                         ",0,18);
            write(capitalize(str) +"Need "+
	(cost-EXP) +" extra experience points.\n");
          }
          else {
            write(G_NAME +" says: You need to be more experienced.\n");
          }
          return 1;
        }
        if(costing) {
          str = extract(str+"                                ",0,18);
          write(capitalize(str) + cost +" experience points.\n");
          return 1;
        }
        stat = str;
        while(sscanf(stat,"%s %s",stat,tmp1))  stat += "_"+ tmp1;
        call_other(this_player(),"set_"+ lower_case(stat), current_stat+1);
	this_player()->set_exp(EXP-cost);
        this_player()->save_character();
        if(classes[i-1] == "general" || classes[i-1] == "primary") {
          write(G_NAME+" helps you to raise your "+ capitalize(str) +".\n");
        }
        else {
          write(G_NAME+" says: I have trained you in the skill of "+
                capitalize(str)+"\nused from "+capitalize(classes[i][j])+".\n");
        }
        say(NAME +" raises "+ this_player()->query_possessive() +" "+
            str +".\n"); 
      }
      return 1;
    }
  }
  write(G_NAME + " looks at you in a confused manner.\n");
  return 1;
}


/*** Cost for raising skill levels ***/

status cost_for_level(string str){
  int i;
  
  if(!guild_master) {
    write("There is no one here for you to get a costing opinion.\n");
    return 1;
  }
  if(!str) {
    write("cost <class|skill|general|primary|level>\n");
    return 1;
  }
  advance(str,1);
  return 1;
}


/*** Leave class ***/

status leave(string str) {
  if(!guild_master) {
    write("There is no one here to remove your class.\n");
    return 1;
  }
  if(!str) {
    write(G_NAME +" says: Leave what class?\n");
    return 1;
  }
  if(member_array(str, classes) == -1) {
    write(G_NAME +" says: You can't do that here.\n");
    return 1;
  }
  if(this_player() -> query_class(str)) {
    write("You are no longer in the class "+str+".\n");
    this_player() -> remove_class(str);
    call_other(this_player(),"clear_"+ str);
    this_player() -> save_character();
  }
  else
    write("You were not in the class "+ str +".\n");
  return 1;
}
  

/*** Join Class ***/

status join(string str) {
  int i;
  int cost;
  string tmp;
  status flag;

  cost = (int)this_player()->query_level();
  cost *= COST_FOR_JOIN;
  if(random(CHARISMA) > random(MASTER_CHA))
    cost -= random(cost/4);
  else
    cost += random(cost/3);


  if(!guild_master) {
    write("There is no one here to join you up.\n");
    return 1;
  }
  if(!str) {
    write(G_NAME +" says: What was that? Join what class?\n");
    return 1;
    write(G_NAME+" says: It will cost you about "+cost+" for new training.\n");
  }
  if(member_array(str, classes) == -1 || str == "general" || str == "primary"){
    write(G_NAME +" says: I can only train you in ");
    for(i = 0; i < sizeof(classes); i += 2) {
      if(classes[i] == "general" || classes[i] == "primary") continue;
      if(flag && i+2 >= sizeof(classes))
        write("and ");
      else if(flag)
        write(", ");
      write(classes[i]);
      flag = 1;
    }
    write(".\n");
    return 1;
  }
  if(this_player()->query_class(str)) {
    write(G_NAME+" says: You are already in the class "+ str +".\n");
    return 1;
  }

  write(G_NAME+" says: Taking into account your set ways at your level.\n"+
  "\t it will be more difficult to start new training.\n");

  tmp = (string)call_other("obj/money", "convert", cost);
  write(G_NAME+" haggles with you over the cost of new training.\n");
  write(G_NAME+" says: "+
  "I will have to charge you "+tmp+" for extra tuition.\n");
  if((int)this_player()->query_money() < cost) {
    write(G_NAME+" says: Without the money, I can't train you in these "+
    "new skills.\n");
    return 1;
  }
  write(G_NAME+" takes "+tmp+" for your new training.\n");
  this_player()->add_money(-cost);
	
  write("You are now a member of the class "+str+".\n");
  this_player()->add_class(str);
  this_player() -> save_character();
  return 1;
}


status train_weapon(string str) {
  string *weap_list;
  object obj;
  string weapon;
  int prof_rate;


  if(!guild_master) {
    write("There is no one here to train you.\n");
    return 1;
  }
  if(!(lower_case(guild_name) == "no" 
  &&  (GUILD_STR == "banned" || GUILD_STR == "no"))
    || GUILD_STR != lower_case(guild_name)) {
    write(G_NAME +" says: Sorry, you can't train here.\n");
    return 1;
  }
  if(!str) {
    write(G_NAME +" says: What weapon do you want to train with?\n");
    return 1;
  }
  obj = present(lower_case(str), this_player());
  if(!obj) {
    write(G_NAME +" says: You dont have that weapon! \n"+
     "\t\tHow can I train you in that!!\n");
    return 1;
  }
  weapon = lower_case((string)obj->query_name());
  if(this_player()->query_weapon_proficiency(weapon)) {
    write(G_NAME +" says: You already know how to use a "+ weapon +"\n");
    return 1;
  }
  if(!obj->query_wielded()) {
    write(G_NAME +" says: You must first be able to wield your "+ weapon +
    "before I can teach\n\tyou how to use it properly.\n");
    return 1;
  }
  weap_list = (string *)this_player()->query_weapon_prof();
  if(!weap_list) {  /* there are still some people with it defaulting to 0 */
    weap_list = ({});
    this_player()->set_weapon_prof(({}));
  }
  if(weapon_rate)
    prof_rate = weapon_rate; /* guild specific weapon rate */
  else if(this_player()->query_class("mage")) 
    prof_rate = 8;
  else if(this_player()->query_class("cleric"))
    prof_rate = 6; 
  else if(this_player()->query_class("thief"))
    prof_rate = 4;
  else /* fighter */
    prof_rate = 2;
  if(sizeof(weap_list) > ((LEVEL+2)*4)/prof_rate) {
    write(G_NAME +" says: One such as you cannot learn so many weapons "+
    " at once time\n\tWait a little longer and return later.\n");
    return 1;
  }
  write(G_NAME +" trains you in the use of the "+ weapon +".\n"+
        "You feel more proficient in its use.\n");
  say(NAME +" trains with "+ G_NAME +" in use of the "+ weapon +".\n");
  this_player()->add_weapon_proficiency(weapon);
  return 1;
}

int *get_exp_arr() {
  int *tmp;
  int n;
  
  tmp = ({});
  for(n = 0 ; n < 200; n++) tmp += ({ get_next_exp(n) });
  return tmp;
}

int query_no_fight() { return 1; }