/* player.c */

#include <secure.h>
#include <sys.h>
#include <weapon.h>
#include <combat_def.h>
#include <flags.h>
#include <armor.h>

#define TP(X) tell_player(this_object(),(X))
#define TPE(X) tell_room_except(location(this_object()),this_object(),(X))
#define TPE2(X) tell_room_except2(location(this_object()),this_object(), \
                                  is_attacking,(X))

object me;
object is_attacking;
string name;
string low_name;
string password;
int alive,hp,max_hp,xp,level,flags,special,gold,armor_dam,armor_hit,is_healing;
object weapon,possessor;
object armor_worn[NUM_PROT];

add_gold(arg) { gold+=arg; return 1; }

static autoheal() {
  hp+=HEAL_AMOUNT;
  if (hp>=max_hp) {
    hp=max_hp;
    is_healing=0;
  } else
    alarm(HEAL_DELAY,"autoheal");
}

static do_exits(arg) {
  object curr;
  int count,pos;
  string ename;

  if (location(this_object())) curr=contents(location(this_object()));
  while (curr) {
    if (call_other(curr,"get_type")==TYPE_EXIT)
      if (!(call_other(curr,"get_flags") & F_DARK)) {
        if (!count) write("Obvious exits:");
        ename=call_other(curr,"get_name");
        pos=instr(ename,1,";");
        if (pos)
          write(" "+leftstr(ename,pos-1));
        else
          write(" "+ename);
        count++;
      }
    curr=next_object(curr);
  }
  if (!count)
    write("There are no obvious exits\n");
  else
    write("\n");
  return 1;
}

static do_wear(arg) {
  string w_name;

  if (!alive) {
    write("You're too dead.\n");
    return 1;
  }
  arg=present(arg,this_object());
  if (!arg) {
    write("You don't have that.\n");
    return 1;
  }
  if (call_other(arg,"get_type")!=TYPE_ARMOR) {
    write("You can't wear that.\n");
    return 1;
  }
  if (armor_worn[call_other(arg,"get_protection")]) {
    write("You're already wearing something on your "+
          make_prot(call_other(arg,"get_protection"))+".\n");
    return 1;
  }
  w_name=call_other(arg,"get_short");
  if (!call_other(arg,"wear")) {
    write("You can't wear that.\n");
    return 1;
  }
  write("You wear "+w_name+".\n");
  TPE(get_short()+" wears "+w_name+".\n");
  armor_worn[call_other(arg,"get_protection")]=arg;
  recalc_armor();
  return 1;
}

recalc_armor() {
  int loop;

  armor_hit=0;
  armor_dam=0;
  loop=0;
  while (loop<NUM_PROT) {
    if (armor_worn[loop]) {
      armor_hit+=call_other(armor_worn[loop],"get_hitreduce");
      armor_dam+=call_other(armor_worn[loop],"get_damreduce");
    }
    loop++;
  }
}

force_unwear(p) {
  if (armor_worn[p]!=caller_object()) return;
  armor_worn[p]=0;
  recalc_armor();
}

static do_unwear(arg) {
  if (!alive) {
    write("You're too dead.\n");
    return 1;
  }
  arg=present(arg,this_object());
  if (call_other(arg,"get_type")!=TYPE_ARMOR) {
    write("You're not wearing that.\n");
    return 1;
  }
  if (armor_worn[call_other(arg,"get_protection")]!=arg) {
    write("You're not wearing that.\n");
    return 1;
  }
  if (call_other(arg,"unwear")) {
    write("You can't unwear that.\n");
    return 1;
  }
  armor_worn[call_other(arg,"get_protection")]=0;
  recalc_armor();
  write("You unwear "+call_other(arg,"get_short")+".\n");
  TPE(get_short()+" unwears "+call_other(arg,"get_short")+".\n");
  return 1;
}

stat() {
  tell_player(this_player(),"Object Type: PLAYER\n");
  if (flags) tell_player(this_player(),"Flags: "+make_flags(flags)+"\n");
  if (special) tell_player(this_player(),"Special: "+itoa(special)+"\n");
  if (name)
    tell_player(this_player(),"Name: "+name+"\n");
  if (alive)
    tell_player(this_player(),"HP: "+itoa(hp)+"/"+itoa(max_hp)+
                "   XP: "+itoa(xp)+"   Level: "+itoa(level)+"\n");
  else
    tell_player(this_player(),"HP: DEAD/"+itoa(max_hp)+"   XP: "+
                itoa(xp)+"   Level: "+itoa(level)+"\n");
  tell_player(this_player(),"HitReduce: "+itoa(2*level-2)+" (With Armor: "+
              itoa(armor_hit+2*level-2)+")\n");
  tell_player(this_player(),"DamReduce: 0 (With Armor: "+itoa(armor_dam)+
              ")\n");
  tell_player(this_player(),"Damage: 1d2+0 (With Weapon: "+(weapon?
              call_other(weapon,"get_damage"):"1d2+0")+")\n");
  tell_player(this_player(),"HitBonus: "+itoa(2*level-2)+" (With Weapon: "+
              itoa(2*level-2+(weapon?call_other(weapon,"get_hit_bonus"):0))+
              ")\n");
  if (weapon)
    tell_player(this_player(),"Wielding: "+make_num(weapon)+"\n");
  if (is_attacking)
    tell_player(this_player(),"Is Attacking: "+make_num(is_attacking)+"\n");
  if (possessor && priv(this_player()))
    tell_player(this_player(),"Possessed By: "+make_num(possessor)+"\n");
  return 1;
}


is_player() { return 1; }

id(arg) { return arg==name || arg==low_name; }

get_uid() { return low_name; }

set_possessor(arg) {
  if (!priv(caller_object())) return 0;
  if (!arg && possessor==caller_object()) {
    possessor=0;
    return 1;
  }
  if (arg!=caller_object()) return 0;
  if (possessor && arg) return 0;
  possessor=arg;
  return 1;
}

force_disconnect() {
  if (!priv(caller_object())) return 0;
  disconnect_device();
  disconnect();
  return 1;
}

force(arg) {
  command(arg);
  return 1;
}

set_name(arg) {
  if (prototype(this_object())) return;
  if (name) return;
  name=arg;
  low_name=downcase(arg);
  return 1;
}

set_password(arg) {
  if (!arg) return;
  if (prototype(this_object())) return;
  if (!priv(caller_object())) return;
  password=arg;
  return 1;
}

set_flags(f) { flags=f; return 1; }
set_special(s) { special=s; return 1; }
get_flags() { return flags; }
get_special() { return special; }

static do_password(arg) {
  string p1,p2;
  int pos;

  pos=instr(arg,1," ");
  p1=leftstr(arg,pos-1);
  p2=rightstr(arg,strlen(arg)-pos);
  if (!p1 || !p2) {
    write("usage: password oldpass newpass\n");
    return 1;
  }
  if (p1!=password) {
    write("Wrong password.\n");
    return 1;
  }
  password=p2;
  write("Password changed.\n");
  return 1;
}

do_say(arg) {
  write("You say \""+arg+"\"\n");
  TPE(get_short()+" says \""+arg+"\"\n");
  return 1;
}

do_pose(arg) {
  write(get_short()+" "+arg+"\n");
  TPE(get_short()+" "+arg+"\n");
  return 1;
}

check_password(arg) {
  if (!priv(caller_object())) return 0;
  return password==arg;
}

add_xp(arg) {
  int new_level,new_xp;

  xp+=arg;
  if (xp<(new_xp=1000)) new_level=1;
  else if (xp<(new_xp=2000)) new_level=2;
  else if (xp<(new_xp=3500)) new_level=3;
  else if (xp<(new_xp=7000)) new_level=4;
  else if (xp<(new_xp=10000)) new_level=5;
  else if (xp<(new_xp=14000)) new_level=6;
  else if (xp<(new_xp=20000)) new_level=7;
  else if (xp<(new_xp=30000)) new_level=8;
  else if (xp<(new_xp=60000)) new_level=9;
  else if (xp<(new_xp=100000)) new_level=10;
  else if (xp<(new_xp=160000)) new_level=11;
  else if (xp<(new_xp=250000)) new_level=12;
  else if (xp<(new_xp=380000)) new_level=13;
  else if (xp<(new_xp=600000)) new_level=14;
  else new_level=15;
  if (arg) write("You gain "+itoa(arg)+" XP.\n");
  if (new_level!=level) {
    level=new_level;
    recalc_max_hp();
    write("You are now level "+itoa(level)+". ");
    if (new_level<15) write("Next level at "+itoa(new_xp)+" XP.\n");
    else write("\n");
  }
}

recalc_max_hp() {
  max_hp=10+4*level;
  if (max_hp>hp && !is_healing) {
    is_healing=1;
    alarm(HEAL_DELAY,"autoheal");
  } else
    hp=max_hp;
}

impending_attack() {
  if (is_attacking) return (xp/20);
  is_attacking=caller_object();
  attack(0);
  return (xp/20);
}

get_name() {
  return name;
}

get_hit_msg1(intensity) {
  string s;

  if (weapon) s=call_other(weapon,"get_hit_msg1",intensity);
  if (!s) s=get_def_hit_msg1(intensity);
  return s;
}

get_hit_msg2(intensity) {
  string s;

  if (weapon) s=call_other(weapon,"get_hit_msg2",intensity);
  if (!s) s=get_def_hit_msg2(intensity);
  return s;
}

get_hit_msg3(intensity) {
  string s;

  if (weapon) s=call_other(weapon,"get_hit_msg3",intensity);
  if (!s) s=get_def_hit_msg3(intensity);
  return s;
}

get_hit_msg4(intensity) {
  string s;

  if (weapon) s=call_other(weapon,"get_hit_msg4",intensity);
  if (!s) s=get_def_hit_msg4(intensity);
  return s;
}

get_hit_msg5(intensity) {
  string s;

  if (weapon) s=call_other(weapon,"get_hit_msg5",intensity);
  if (!s) s=get_def_hit_msg5(intensity);
  return s;
}

get_long() {
  if (alive)
    return "This is the player "+name;
  else
    return "This is the insubstantial ghost of the player "+name;
}

move_player(dest,direction) {
  if (location(this_object()))
    TPE(get_short()+" goes "+direction+".\n");
  move_object(this_object(),dest);
  look(0);
  if (dest)
    TPE(get_short()+" has arrived.\n");
  return 1;
}

look(arg) {
  int contents_printed,curr;
  string oname,t;

  contents_printed=0;
  t=arg;
  if (arg) {
    arg=present(arg,location(this_object()));
    if (!arg) arg=present(t,this_object());
    if (!arg && t=="me") arg=this_object();
    if (!arg && t=="here") arg=this_object();
  } else
    if (!location(this_object())) {
      write("You are in the void.\n");
      return 1;
    } else
      arg=location(this_object());
  if (!arg) {
    write("You don't see that here.\n");
    return 1;
  }
  if (arg==location(this_object())) {
    oname=call_other(arg,"get_short");
    if (oname) write(oname+"\n");
  }
  oname=call_other(arg,"get_long");
  if (oname) write(oname+"\n");
  curr=contents(arg);
  if (!(call_other(arg,"get_flags") & F_DARK))
    while (curr) {
      if (curr!=this_object()) {
        oname=call_other(curr,"get_short");
        if (oname) {
          if (!contents_printed) {
            write("Contents:\n");
            contents_printed=1;
          }
          write(oname+"\n");
        }
      }
      curr=next_object(curr);
    }
  return 1;
}

get_type() { return TYPE_PLAYER; }

find_enemy() { is_attacking=0; }

is_living() { return alive; }

get_short() { return alive ? name:"Ghost of "+name; }

to_hit_bonus() {
  int bonus;

  if (weapon)
    bonus=call_other(weapon,"get_hit_bonus",is_attacking);
  else
    bonus=0;
  return bonus+2*level-2;
}

damage_bonus() {
  int bonus;

  if (weapon)
    bonus=call_other(weapon,"get_damage_bonus",is_attacking);
  else
    bonus=random(2)+1;
  return bonus;
}

static do_kill(arg) {
  if (!alive) {
    write("You're too dead.\n");
    return 1;
  }
  arg=present(arg,location(this_object()));
  if (!arg) {
    write("You don't see that here.\n");
    return 1;
  }
  if (!call_other(arg,"is_living")) {
     write("You can't kill that, you bonehead.\n");
     return 1;
  }
  if (arg==this_object()) {
    write("PLEASE try to control your suicidal impulses.\n");
    return 1;
  }
  if (arg==is_attacking) {
    write("You're already attacking that. Chill.\n");
    return 1;
  }
  write("You start attacking "+call_other(arg,"get_short")+".\n");
  tell_player(arg,name+" starts attacking you.\n");
  tell_room_except2(location(this_object()),arg,this_object(),name+
                    " starts attacking "+call_other(arg,"get_short")+".\n");
  attack(arg);
  return 1;
}

do_damage(amount) {
  int retval,intensity;
  object curr,next;

  amount-=armor_dam;
  if (amount<0) amount=0;
  if (!is_healing) {
    is_healing=1;
    alarm(HEAL_DELAY,"autoheal");
  }
  retval=(amount>hp)?hp:amount;
  hp-=amount;
  if (amount<I_PUNY_AMT)
    intensity=I_PUNY;
  else if (amount<I_WEAK_AMT)
    intensity=I_WEAK;
  else if (amount<I_POOR_AMT)
    intensity=I_POOR;
  else if (amount<I_AVERAGE_AMT)
    intensity=I_AVERAGE;
  else if (amount<I_GOOD_AMT)
    intensity=I_GOOD;
  else if (amount<I_VERYGOOD_AMT)
    intensity=I_VERYGOOD;
  else intensity=I_MASSACRE;
  write(call_other(caller_object(),"get_short")+" "+
        call_other(caller_object(),"get_hit_msg3",intensity)+"\n");
  write("You have "+itoa((hp>0)?hp:0)+" hit points left.\n");
  if (hp<1) {
    write("You have died. Your ghost now hovers around uselessly.\n");
    hp=0;
    alive=0;
    is_attacking=0;
    curr=clone_object("/obj/corpse");
    call_other(curr,"set_name",name);
    move_object(curr,location(this_object()));
    if (gold>0) {
      curr=clone_object("/obj/coins");
      call_other(curr,"set_value",gold);
      move_object(curr,location(this_object()));
      gold=0;
    }
    curr=contents(this_object());
    while (curr) {
      next=next_object(curr);
      if (call_other(curr,"drop")) {
        if (!call_other(curr,"dead_drop"))
          move_object(curr,location(this_object()));
      } else
        move_object(curr,location(this_object()));
      curr=next;
    }
  }
  return retval;
}

static do_reincarnate(arg) {
  if (alive)
    write("Idiot, you haven't DIED yet.\n");
  else {
    if (!location(this_object())) {
      write("You're not in a temple, you're in the hideous void.\n");
      return 1;
    }
    if (!(call_other(location(this_object()),"get_flags") & F_TEMPLE)) {
      write("You're not in a temple.\n");
      return 1;
    }
    hp=1;
    alive=1;
    is_healing=1;
    xp=xp/2;
    add_xp(0);
    alarm(HEAL_DELAY,"autoheal");
    write("You come back to life, you hot ticket.\n");
    TPE(name+" stops being a ghost, regains flesh, etc.\n");
  }
  return 1;
}

attempt_hit(chance) {
  return chance+52-armor_hit-(2*level)>random(100);
}

get_hitreduce() { return armor_hit; }
get_damreduce() { return armor_dam; }

attack(enemy) {
  int enemy_xp,intensity,dmg,tmp;
  string ename;

  if (enemy==this_object()) return;
  if (is_attacking==this_object()) {
    is_attacking=0;
    return;
  }
  if (!alive) {
    is_attacking=0;
    return;
  }
  if (enemy)
    if (is_attacking) {
      is_attacking=enemy;
      return;
    } else
      is_attacking=enemy;
  if (!is_attacking)
    find_enemy();
  if (!is_attacking) return;
  if (!call_other(is_attacking,"is_living")) find_enemy();
  if (!is_attacking) return;
  if (location(is_attacking)!=location(this_object()))
    find_enemy();
  if (!is_attacking) return;
  ename=call_other(is_attacking,"get_short");
  enemy_xp=call_other(is_attacking,"impending_attack");
  if (!alive) return;
  if (call_other(is_attacking,"attempt_hit",to_hit_bonus())) {
    dmg=damage_bonus();
    tmp=call_other(is_attacking,"do_damage",dmg);
    if (dmg<I_PUNY_AMT) {
      intensity=I_PUNY;
    } else if (dmg<I_WEAK_AMT) {
      intensity=I_WEAK;
    } else if (dmg<I_POOR_AMT) {
      intensity=I_POOR;
    } else if (dmg<I_AVERAGE_AMT) {
      intensity=I_AVERAGE;
    } else if (dmg<I_GOOD_AMT) {
      intensity=I_GOOD;
    } else if (dmg<I_VERYGOOD_AMT) {
      intensity=I_VERYGOOD;
    } else {
      intensity=I_MASSACRE;
    }
    TP(get_hit_msg1(intensity)+ename+get_hit_msg2(intensity)+"\n");
    TPE2(get_short()+" "+get_hit_msg4(intensity)+ename+get_hit_msg5(intensity)
         +"\n");
    if (!call_other(is_attacking,"is_living")) {
      TP("You have killed "+ename+"!\n");
      TPE2(get_short()+" has killed "+ename+"!\n");
      add_xp(enemy_xp);
      find_enemy();
    }
  } else {
    TP("You missed "+ename+".\n");
    tell_player(is_attacking,get_short()+" misses you.\n");
    TPE2(get_short()+" misses "+ename+".\n");
  }
  alarm(3,"attack");
}

do_quit(arg) {
  TPE(get_short()+" has disconnected.\n");
  disconnect_device();
  return 1;
}

static do_inventory(arg) {
  int count;
  object curr;
  string oname;

  count=0;
  curr=contents(this_object());
  while (curr) {
    oname=call_other(curr,"get_short");
    if (oname) {
      if (!count) {
        write("You are carrying:\n");
      }
      count++;
      write(oname+"\n");
    }
    curr=next_object(curr);
  }
  if (gold) write("You have "+itoa(gold)+" gold coins.\n");
  if (!count && !gold)
    write("You are empty-handed.\n");
  return 1;
}

get_gold() { return gold; }

static do_score(arg) {
  if (alive) {
    write("HP: "+itoa(hp)+"/"+itoa(max_hp)+"       Level: "+itoa(level)+
          "       XP: "+itoa(xp)+"     Gold: "+itoa(gold)+"\n");
  } else
    write("You are dead. This implies your score kinda SUCKS.\n");
  return 1;
}

static do_help(arg) {
  object curr,found;
  string s;

  if (arg) {
    curr=next_child(atoo("/sys/help1"));
    while (curr && !found) {
      if (call_other(curr,"get_topic")==arg) found=curr;
      curr=next_child(curr);
    }
    if (!found) {
      write("There is no help available on that topic.\n");
      return 1;
    }
    s=(call_other(found,"get_text"));
    if (s) write("\n"+s+"\n");
    else write("Help has not been entered for that topic.\n");
    return 1;
  }
  write("\nHelp available for the following:\n");
  write("---------------------------------\n");
  curr=next_child(atoo("/sys/help1"));
  while (curr) {
    write(call_other(curr,"get_topic")+" ");
    curr=next_child(curr);
  }
  write("\n\n");
  return 1;
}

static banner() {
  cat("/motd");
  look(0);
}

connect() {
  TPE(get_short()+" has connected.\n");
  alarm(0,"banner");
}

static do_huh(arg) {
  write(HUH_STRING);
}

static do_quit(arg) {
  write("Bye!\n");
  disconnect_device();
  disconnect();
  return 1;
}

static do_who(arg) {
  who_list();
  return 1;
}

static do_get(arg) {
  if (!alive) {
    write("You're too dead.\n");
    return 1;
  }
  arg=present(arg,location(this_object()));
  if (!arg) {
    write("You don't see that here.\n");
    return 1;
  }
  if (!call_other(arg,"get")) {
    write("You can't get "+call_other(arg,"get_short")+".\n");
    return 1;
  }
  move_object(arg,this_object());
  write("You get "+call_other(arg,"get_short")+".\n");
  TPE(get_short()+" gets "+call_other(arg,"get_short")+"\n");
  move_object(arg,this_object());
  return 1;
}

static do_drop(arg) {
  if (!alive) {
    write("You're too dead.\n");
    return 1;
  }
  arg=present(arg,(this_object()));
  if (!arg) {
    write("You don't have that.\n");
    return 1;
  }
  if (call_other(arg,"drop")) {
    write("You can't drop "+call_other(arg,"get_short")+".\n");
    return 1;
  }
  write("You drop "+call_other(arg,"get_short")+".\n");
  TPE(get_short()+" drops "+call_other(arg,"get_short")+"\n");
  move_object(arg,location(this_object()));
  return 1;
}

static do_wield(arg) {
  string w_name;

  if (!alive) {
    write("You're too dead.\n");
    return 1;
  }
  arg=present(arg,this_object());
  if (!arg) {
    write("You don't have that.\n");
    return 1;
  }
  if (weapon) {
    write("You are already wielding a weapon.\n");
    return 1;
  }
  w_name=call_other(arg,"get_short");
  if (!call_other(arg,"wield")) {
    write("You can't wield that.\n");
    return 1;
  }
  write("You wield "+w_name+".\n");
  TPE(get_short()+" wields "+w_name+".\n");
  weapon=arg;
  return 1;
}

force_unwield() {
  if (weapon!=caller_object()) return;
  weapon=0;
}

static do_unwield(arg) {
  if (!alive) {
    write("You're too dead.\n");
    return 1;
  }
  arg=present(arg,this_object());
  if (arg!=weapon) {
    write("You aren't wielding that.\n");
    return 1;
  }
  if (call_other(arg,"unwield")) {
    write("You can't unwield "+call_other(arg,"get_short")+".\n");
    return 1;
  }
  weapon=0;
  write("You unwield "+call_other(arg,"get_short")+".\n");
  TPE(get_short()+" unwields "+call_other(arg,"get_short")+".\n");
  return 1;
}

disconnect() {
  TPE(get_short()+" has disconnected.\n");
}

static init() {
  check_secure(SECURE_PRIV,"");
  alive=1;
  set_interactive();
  max_hp=14;
  hp=max_hp;
  xp=0;
  level=1;
  me=this_object();
  if (!prototype(me)) return;
  add_xverb("","do_huh");
  add_verb("say","do_say");
  add_verb("pose","do_pose");
  add_xverb(":","do_pose");
  add_xverb("\"","do_say");
  add_verb("help","do_help");
  add_verb("?","do_help");
  add_verb("i","do_inventory");
  add_verb("inventory","do_inventory");
  add_verb("l","look");
  add_verb("look","look");
  add_verb("drop","do_drop");
  add_verb("get","do_get");
  add_verb("take","do_take");
  add_verb("wield","do_wield");
  add_verb("unwield","do_unwield");
  add_verb("who","do_who");
  add_verb("quit","do_quit");
  add_verb("reincarnate","do_reincarnate");
  add_verb("kill","do_kill");
  add_verb("score","do_score");
  add_verb("QUIT","do_quit");
  add_verb("WHO","do_who");
  add_verb("password","do_password");
  add_verb("exits","do_exits");
  add_verb("wear","do_wear");
  add_verb("unwear","do_unwear");
}

listen(arg) {
  send_device(arg);
  if (possessor) call_other(possessor,"possession_report",arg);
}